Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7d625573 | ||
|
|
52a57c0e87 | ||
|
|
a52b34fc3f | ||
|
|
1a14ac020e | ||
|
|
330f7e72be | ||
|
|
1ed6669ced | ||
|
|
108d60a497 | ||
|
|
1a9bd7de83 | ||
|
|
10cd06c70b | ||
|
|
84b6c33c54 | ||
|
|
18808eae9c | ||
|
|
ca74b0a76b | ||
|
|
b45d60efb2 | ||
|
|
a4e3696078 | ||
|
|
2b963993d2 | ||
|
|
e333022043 | ||
|
|
2e2a59ccb8 | ||
|
|
6f073ad718 | ||
|
|
166834e46c | ||
|
|
0ff2364b9e | ||
|
|
2cbfc6a11e | ||
|
|
404ccc7b2b | ||
|
|
42e413ab65 | ||
|
|
d57ef66038 | ||
|
|
843b4cbf68 | ||
|
|
7e4b7c5c8b | ||
|
|
2ff65537a0 | ||
|
|
6e2b1fb18d | ||
|
|
3e621a2818 | ||
|
|
247ec4c1b2 | ||
|
|
edcff069fd | ||
|
|
8afdeac306 | ||
|
|
425f861451 | ||
|
|
5a843ed02c | ||
|
|
f3bd83af03 | ||
|
|
0b3fca63b7 | ||
|
|
cbd4460df0 | ||
|
|
b941bd9a75 | ||
|
|
439b84affc | ||
|
|
66d5302e6f | ||
|
|
9e2e2b9c5c | ||
|
|
aa243ac7b0 | ||
|
|
eb421e0681 | ||
|
|
63aae8f578 | ||
|
|
e577454a31 | ||
|
|
e802bb0052 | ||
|
|
b98e5df20a | ||
|
|
7332105edb | ||
|
|
6a4feaf18d | ||
|
|
ee637d7958 | ||
|
|
8d161ce8ff | ||
|
|
8010bd8195 | ||
|
|
059f1d5505 | ||
|
|
123cab5724 | ||
|
|
c16365a0e5 | ||
|
|
a21d278c0c | ||
|
|
0466916be1 | ||
|
|
9a845d8f6a | ||
|
|
9ae1673499 | ||
|
|
2d5a5e7aef | ||
|
|
3e4d439852 | ||
|
|
aba0b09109 | ||
|
|
af608ee17d | ||
|
|
df817e86d6 | ||
|
|
a7d3b62512 | ||
|
|
b41f14192a | ||
|
|
475ec71983 | ||
|
|
b5cff2fa93 | ||
|
|
80e8c014ec | ||
|
|
67d42f666c | ||
|
|
fa32f95438 | ||
|
|
06878dd56a | ||
|
|
374f6a3e6e | ||
|
|
0b9c804a3d | ||
|
|
365a459d61 | ||
|
|
cec891a2c0 | ||
|
|
8eb8aa3032 | ||
|
|
38184e7968 | ||
|
|
f6092ea195 | ||
|
|
ac7d66e338 | ||
|
|
b82a1da92a | ||
|
|
b8859f7b64 | ||
|
|
a04fa67446 | ||
|
|
ac89aff018 | ||
|
|
e7cf8dc966 | ||
|
|
d0102f5bdb | ||
|
|
eb0daadf98 | ||
|
|
49d6ca173d | ||
|
|
a3eb6a6b7e | ||
|
|
144ed57b20 | ||
|
|
6086ddabf0 | ||
|
|
84f378a276 | ||
|
|
648e8d5f2c | ||
|
|
15df8d7129 | ||
|
|
cfc0af9655 | ||
|
|
f02e3181a6 | ||
|
|
3d59bcf97e | ||
|
|
5e4fc983fb | ||
|
|
32d9635b34 | ||
|
|
3978011d2e | ||
|
|
cd1952a7df | ||
|
|
4251477451 | ||
|
|
873f1de1ef | ||
|
|
150f993e5f | ||
|
|
06e76e5e0f | ||
|
|
a208a264c7 | ||
|
|
114014fa13 | ||
|
|
94cfd9d2e9 | ||
|
|
0aa9c73b4b | ||
|
|
5234f85dbe | ||
|
|
bd8a2f704f | ||
|
|
439886d69f | ||
|
|
b2a37795e3 | ||
|
|
440db56080 | ||
|
|
337e6e6bb6 | ||
|
|
a1aeda3d35 | ||
|
|
c6a83f8c9b | ||
|
|
0f24427628 | ||
|
|
f52dc36a6a | ||
|
|
63a936968d | ||
|
|
cabfde7963 | ||
|
|
79628c2351 | ||
|
|
762fa6f8bf | ||
|
|
a258c097a9 | ||
|
|
41faa98fcd | ||
|
|
c4d7318686 | ||
|
|
9ef11eaa34 | ||
|
|
f34cb2df56 | ||
|
|
83b9f0d1e9 | ||
|
|
7d81cf0c6a | ||
|
|
82b76a9455 | ||
|
|
4ae577f936 | ||
|
|
81a83994ab | ||
|
|
1d3a3c7f5f | ||
|
|
8bb645125d | ||
|
|
9471b31a5d | ||
|
|
ffa52757c9 | ||
|
|
ee53a0df9b | ||
|
|
8e20c85e07 | ||
|
|
65ea786358 | ||
|
|
1035837b3b | ||
|
|
18fc2e6b3f | ||
|
|
fc728697d7 | ||
|
|
cdf3caee9e | ||
|
|
0a4d3a002e | ||
|
|
d11c532ea4 | ||
|
|
1ff51697b1 | ||
|
|
ab8ae31324 | ||
|
|
daa0e723b1 | ||
|
|
609aba856a | ||
|
|
a6678bd537 | ||
|
|
6c4f580fc2 | ||
|
|
ea41661c65 | ||
|
|
c553c7b63b | ||
|
|
e5988aa800 | ||
|
|
ae319c607f | ||
|
|
5d23ad1c9e | ||
|
|
55eef50da7 | ||
|
|
653465f0e0 | ||
|
|
2cf8044987 | ||
|
|
3c056a7255 | ||
|
|
091796d13c | ||
|
|
eb97dd844f | ||
|
|
17693ec8fe | ||
|
|
7c7ce8f50f | ||
|
|
e294727ac5 | ||
|
|
f29d64c803 | ||
|
|
0385b0ce29 | ||
|
|
b526274cd7 | ||
|
|
88c16af4a9 | ||
|
|
05ac9a6832 | ||
|
|
04aa5236c2 | ||
|
|
ec98e4e1c6 | ||
|
|
bb9ece494c | ||
|
|
217e6776ac | ||
|
|
2286218980 | ||
|
|
3d9c83f9f4 | ||
|
|
14ed163238 | ||
|
|
c815de0718 | ||
|
|
6a46556d9e | ||
|
|
f8f820529f | ||
|
|
cac11155e7 | ||
|
|
d20870f11e | ||
|
|
10ceb6c79d | ||
|
|
b38b6fa294 | ||
|
|
94c1d35429 | ||
|
|
e33cef4e89 | ||
|
|
9b815ed9c1 |
154
CHANGELOG.md
154
CHANGELOG.md
@@ -6,6 +6,158 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1
|
||||
|
||||
Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
|
||||
|
||||
## [1.2.12] - 2018-11-14
|
||||
|
||||
Firmware: 8.**5.3** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.5.3)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- When the firmware of the right keyboard half is larger or equal than 8.4.3 then display the "Lock layer when double tapping this key" checkbox and remove "... macro playback is not implemented yet..." notices.
|
||||
- Upgrade to node-hid 0.7.3 which utilizes the hidraw USB driver on Linux instead of libusb.
|
||||
- Update udev rules for the new hidraw based node-hid.
|
||||
- Improve the "Cannot find your UHK" and the privilege escalation screens to show more relevant messages when transitioning from the libusb based node-hid to the hidraw based node-hid.
|
||||
- Fix the rendering of macro actions, so that their text doesn't overlap.
|
||||
- Add "International {1,2,3}" and "Language {1,2}" keypress actions.
|
||||
- Add icon for the Play/Pause keypress action.
|
||||
- Remove the Stop/Eject keypress action.
|
||||
- Make the "Type text" macro action accept clipboard data on Mac.
|
||||
- Display "You can't change this mapping because on the base layer a layer switcher key targets this key." in the key action popover whenever it applies.
|
||||
- Fix UI bug which could be triggered by tapping Tab in the keymap abbreviation input.
|
||||
- Don't trigger Agent shortcuts when capturing keypresses.
|
||||
- Log USB device list before checking permissions.
|
||||
- Show OS-specific modifiers in the title bar of macro actions.
|
||||
- Only show the device list on Linux when the list actually changes.
|
||||
|
||||
## [1.2.11] - 2018-10-03
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Add backspace and caps lock icons which avoids the overlap of their old texts.
|
||||
- Fix right and middle mouse click macro actions which were exchanged.
|
||||
- Include Agent version to the firmware update log.
|
||||
|
||||
## [1.2.10] - 2018-09-24
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Add History Back and History Forward scancodes.
|
||||
- Save the actual decelerated scroll speed instead of using the accelerated scroll speed by accident.
|
||||
- Allow layer switcher secondary roles only on the base layer.
|
||||
- When remapping modifiers, display a warning suggesting to remap them on all layers.
|
||||
- Display more exact instructions on the permission setup screen.
|
||||
- Set the decelerated scroll speed of the default configuration from 20 to 10.
|
||||
- Map Caps Lock without Ctrl on default keymaps.
|
||||
- Rename "Scroll Lock" to "ScrLk" and "Num Lock" to "NumLk" on keys to avoid text overlap.
|
||||
- In the scancode select2, display "Print Screen SysRq" and add SysRq above PrtScn when rendering the key.
|
||||
- Fix left and right direction titles for mouse movement macro actions.
|
||||
|
||||
## [1.2.9] - 2018-09-13
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Display OS-specific modifiers.
|
||||
- Display secondary roles.
|
||||
- Don't trigger "Remap on all layers" after leaving Agent with Alt+Tab.
|
||||
|
||||
## [1.2.8] - 2018-08-26
|
||||
|
||||
Firmware: 8.**2.5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Uncheck the "Remap on all keymaps" and "Remap on all layers" checkboxes of the key action popover by default.
|
||||
- Bind left and right Shift on the Mouse layer of all keymaps in the default configuration.
|
||||
- Make ng2-select2 widgets faster.
|
||||
- Add note to the LED brightness page saying that current UHK versions are not backlit.
|
||||
- Fix the padding of the secondary role tooltip.
|
||||
- Remove the redundant scrollbar from the LED brightness page.
|
||||
|
||||
## [1.2.7] - 2018-07-26
|
||||
|
||||
Firmware: 8.4.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.4.0 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Fix Agent startup exception on Linux by upgrading Electron builder.
|
||||
- Change the shortcut which enables the USB stack test code, so that it can be triggered with the default Mac US keymap.
|
||||
|
||||
## [1.2.6] - 2018-07-26
|
||||
|
||||
Firmware: 8.**4.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.4.0)] | Device Protocol: 4.**4.0** | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Replace the Linux blhost binary with a statically compiled version that doesn't use special instructions and shouldn't segfault.
|
||||
- Keep the current layer when changing keymaps.
|
||||
- Fix the sleep key of Mac keymaps.
|
||||
- Add help page.
|
||||
- Add "save to keyboard" and "remap key" shortcuts.
|
||||
- Build only AppImages for Linux.
|
||||
- Replace ng2-select2 widgets with ngx-select-ex that always shows up in the correct position.
|
||||
- Improve the phrasing of the firmware update error message.
|
||||
- Tweak unsupported Windows firmware update notification.
|
||||
- Hide the Settings menu until auto update is implemented.
|
||||
- Don't scroll when the macro tab of the key action popover gets selected.
|
||||
- Add keyboard shortcut for enabling the USB stack test mode of the firmware. `DEVICEPROTOCOL:MINOR`
|
||||
- Tone down the color of the separator line.
|
||||
|
||||
## [1.2.5] - 2018-06-26
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- When remapping a switch keymap action on all keymaps, don't set it on its own keymap.
|
||||
- Make the key action popover always contain the action of the current key, even after cancelled.
|
||||
- Include the firmware version to be updated to the firmware update log.
|
||||
- Update the Agent icon of the side menu and the about page.
|
||||
- When remapping a key, only flash the affected key instead of all keys.
|
||||
- Fade in/out the keyboard separator line only when splitting the keyboard.
|
||||
- Only show the unsupported OS message of the firmware page on relevant Windows versions.
|
||||
- Close and reopen USB device when an error occurs.
|
||||
- Temporarily remove the export keymap feature because it's useless until import is implemented.
|
||||
|
||||
## [1.2.4] - 2018-06-21
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Replace Linux x86-64 blhost with a statically linked version which should make firmware updates work on every Linux distro.
|
||||
|
||||
## [1.2.3] - 2018-06-19
|
||||
|
||||
Firmware: 8.2.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.1 | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Add checkboxes for remapping keys on all layers and/or all keymaps.
|
||||
- Add separator line between the keyboard halves.
|
||||
- Add double tap icon for switch layer actions.
|
||||
- Improve the looks and content of the tooltips of the key action popover.
|
||||
- Make the left keyboard half less likely to timeout during firmware update.
|
||||
- Terminate the firmware update process if blhost segfaults.
|
||||
- Replace the Linux x86-64 version of the blhost binary which should not make it segfault anymore.
|
||||
- Make the firmware update log shorter by listing one device per line and not repeating the list of available USB devices.
|
||||
- Make the firmware update help text shorter.
|
||||
|
||||
## [1.2.2] - 2018-05-27
|
||||
|
||||
Firmware: 8.2.**5** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.5)] | Device Protocol: 4.3.**1** | User Config: 4.0.1 | Hardware Config: 1.0.0
|
||||
|
||||
- Offer recovery for bricked right keyboard halfs.
|
||||
- Detect when the hardware configuration of a device is invalid and display a notification. `DEVICEPROTOCOL:PATCH`
|
||||
- Check if the keyboard is in factory reset mode and if so, display a relevant instruction.
|
||||
- Only allow ASCII characters in type text macro actions.
|
||||
- Allow uploading the same file multiple times in a row.
|
||||
- Only send auto update notification when the user initiates the update.
|
||||
- Update the firmware versions on the firmware update page right after firmware updates.
|
||||
- Add a lot of useful instructions to the firmware page to help users update the firmware.
|
||||
- Add the operating system and initial device list to the firmware update log.
|
||||
- Add copy to clipboard button to the top right corner of the firmware update terminal widget.
|
||||
|
||||
## [1.2.1] - 2018-05-12
|
||||
|
||||
Firmware: 8.2.**2** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.2)] | Device Protocol: 4.3.0 | User Config: 4.0.**1** | Hardware Config: 1.0.0
|
||||
|
||||
- Match for the new USB usage page and usage number. This is critical for UHKs flashed with firmware >=8.2.2 to be recognized by Agent on OSX.
|
||||
- Make the config serializer handle long media macro actions. `USERCONFIG:PATCH`
|
||||
- Add note on the macro page explaining that the macro engine of the firmware is not ready yet.
|
||||
- Add an example to the scancode tooltip to better explain users how to invoke non-US characters.
|
||||
|
||||
## [1.2.0] - 2018-04-20
|
||||
|
||||
Firmware: 8.**2.0** [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.2.0)] | Device Protocol: 4.**3.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Tweak the default mouse speed. This was necessary because the last firmware version adjusted speed multipliers. The mouse speed can be reset via the "Reset speeds to default" button of the "Mouse speed" page.
|
||||
- Make the newly added switch-keymap.js script utilize the new UsbCommandId_SwitchKeymap, allowing for programmatic keymap switching. `DEVICEPROTOCOL:MINOR`
|
||||
|
||||
## [1.1.5] - 2018-04-10
|
||||
|
||||
Firmware: 8.1.5 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.5)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
@@ -56,7 +208,7 @@ Firmware: 8.1.**2** [[release](https://github.com/UltimateHackingKeyboard/firmwa
|
||||
|
||||
## [1.1.0] - 2018-01-15
|
||||
|
||||
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.2.0 | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
Firmware: 8.**1**.0 [[release](https://github.com/UltimateHackingKeyboard/firmware/releases/tag/v8.1.0)] | Device Protocol: 4.**2.0** | User Config: 4.0.0 | Hardware Config: 1.0.0
|
||||
|
||||
- Only accept device, keymap, and macro names upon editing if their trimmed length is non-zero.
|
||||
- Add diagnostics USB scripts, most notably /packages/usb/{get-i2c-health,set-i2c-baud-rate}.js, some utilizing new device protocol commands and properties. `DEVICEPROTOCOL:MINOR`
|
||||
|
||||
8
ISSUE_TEMPLATE
Normal file
8
ISSUE_TEMPLATE
Normal file
@@ -0,0 +1,8 @@
|
||||
Before submitting a new issue, make sure to do the following:
|
||||
|
||||
1. If you're using Karabiner Elements on your Mac, close it!
|
||||
2. Install the latest Agent:
|
||||
https://github.com/UltimateHackingKeyboard/agent/releases/latest
|
||||
3. Use Agent to update to the latest firmware:
|
||||
https://github.com/UltimateHackingKeyboard/firmware/releases/latest
|
||||
4. Try to reproduce the issue, and only report it if it still persists.
|
||||
19
README.md
19
README.md
@@ -5,17 +5,8 @@
|
||||
|
||||
Agent is the configuration application of the [Ultimate Hacking Keyboard](https://ultimatehackingkeyboard.com/).
|
||||
|
||||
[Give it a whirl!](http://ultimatehackingkeyboard.github.io/agent/)
|
||||
|
||||
## Two builds to rule them all
|
||||
|
||||
It's worth mentioning that Agent has two builds.
|
||||
|
||||
The **electron build** is the desktop application which is meant to be used if you have an actual UHK at hand. It starts with an opening screen which detects your UHK. You cannot get past this screen without connecting a UHK via USB.
|
||||
|
||||
The **web build** is meant to be used for demonstration purposes, so people who don't yet own a UHK can get a feel of Agent and its capabilities in their browser. Eventually, WebUSB support will be added to the web build, making it able to communicate with the UHK. Given the sandboxed nature of browsers, the web build will always lack features that the electron build offers, so this won't make the electron build obsolete.
|
||||
|
||||
The two builds share code as much as possible.
|
||||
* Try out the [web build of Agent](http://ultimatehackingkeyboard.github.io/agent/) in your browser. This is meant to be used for demonstration purposes.
|
||||
* Download the [desktop build of Agent](https://github.com/UltimateHackingKeyboard/agent/releases) from our releases page. Use this if you have an actual UHK at hand, or else you won't get past the opening screen!
|
||||
|
||||
## Building the electron application
|
||||
|
||||
@@ -33,9 +24,9 @@ For everyone else, use the appropriate package manager for your OS.
|
||||
```
|
||||
git clone git@github.com:UltimateHackingKeyboard/agent.git
|
||||
cd agent
|
||||
npm install # to install Node dependencies
|
||||
npm run build:electron # to build the agent
|
||||
npm run electron # to run the newly built agent
|
||||
npm install
|
||||
npm run build
|
||||
npm run electron
|
||||
```
|
||||
|
||||
At this point, Agent should be running on your machine.
|
||||
|
||||
16481
package-lock.json
generated
16481
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
62
package.json
62
package.json
@@ -3,10 +3,10 @@
|
||||
"private": true,
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"main": "electron/dist/electron-main.js",
|
||||
"version": "1.1.5",
|
||||
"firmwareVersion": "8.1.5",
|
||||
"deviceProtocolVersion": "4.2.0",
|
||||
"userConfigVersion": "4.0.0",
|
||||
"version": "1.2.12",
|
||||
"firmwareVersion": "8.5.3",
|
||||
"deviceProtocolVersion": "4.4.0",
|
||||
"userConfigVersion": "4.0.1",
|
||||
"hardwareConfigVersion": "1.0.0",
|
||||
"description": "Agent is the configuration application of the Ultimate Hacking Keyboard.",
|
||||
"repository": {
|
||||
@@ -15,55 +15,65 @@
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"engines": {
|
||||
"node": ">=8.9.1 <9.0.0",
|
||||
"npm": ">=5.6.0 <6.0.0"
|
||||
"node": ">=8.12.0 <9.0.0",
|
||||
"npm": ">=6.4.0 <7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/electron-devtools-installer": "2.0.2",
|
||||
"@types/electron-settings": "3.0.0",
|
||||
"@types/fs-extra": "5.0.1",
|
||||
"@types/jasmine": "2.6.0",
|
||||
"@types/jasmine": "2.8.8",
|
||||
"@types/jquery": "3.3.1",
|
||||
"@types/jsonfile": "4.0.1",
|
||||
"@types/lodash-es": "4.17.0",
|
||||
"@types/node": "8.0.53",
|
||||
"@types/node-hid": "0.5.2",
|
||||
"@types/node-hid": "0.7.0",
|
||||
"@types/request": "2.0.8",
|
||||
"@types/semver": "5.5.0",
|
||||
"@types/usb": "1.1.3",
|
||||
"autoprefixer": "6.5.3",
|
||||
"buffer": "5.0.6",
|
||||
"copyfiles": "^2.0.0",
|
||||
"check-node-version": "^3.2.0",
|
||||
"copy-webpack-plugin": "4.0.1",
|
||||
"copyfiles": "^2.0.0",
|
||||
"core-js": "2.4.1",
|
||||
"cross-env": "5.0.5",
|
||||
"decompress": "4.2.0",
|
||||
"decompress-tarbz2": "^4.1.1",
|
||||
"decompress-tarbz2": "4.1.1",
|
||||
"devtron": "1.4.0",
|
||||
"electron": "1.8.4",
|
||||
"electron-builder": "20.8.1",
|
||||
"electron": "1.8.7",
|
||||
"electron-builder": "20.34.0",
|
||||
"electron-debug": "1.5.0",
|
||||
"electron-devtools-installer": "2.2.3",
|
||||
"electron-log": "2.2.14",
|
||||
"electron-rebuild": "1.7.3",
|
||||
"electron-log": "2.2.16",
|
||||
"electron-rebuild": "1.8.2",
|
||||
"electron-settings": "3.1.4",
|
||||
"electron-updater": "2.21.4",
|
||||
"exports-loader": "0.6.3",
|
||||
"file-loader": "0.10.0",
|
||||
"fs-extra": "5.0.0",
|
||||
"gh-pages": "1.1.0",
|
||||
"jasmine": "2.8.0",
|
||||
"jasmine-core": "2.8.0",
|
||||
"jasmine-node": "2.0.1",
|
||||
"jasmine-ts": "0.2.1",
|
||||
"jsonfile": "4.0.0",
|
||||
"lerna": "2.9.0",
|
||||
"lerna": "3.2.0",
|
||||
"lodash-es": "4.17.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"npm-run-all": "4.0.2",
|
||||
"pre-commit": "1.2.2",
|
||||
"request": "2.83.0",
|
||||
"request": "2.88.0",
|
||||
"rimraf": "2.6.1",
|
||||
"standard-version": "4.2.0",
|
||||
"stylelint": "7.13.0",
|
||||
"svg-sprite": "1.3.7",
|
||||
"standard-version": "4.4.0",
|
||||
"stylelint": "9.6.0",
|
||||
"svg-sprite": "1.5.0",
|
||||
"ts-loader": "2.3.1",
|
||||
"ts-node": "3.0.4",
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "5.9.1",
|
||||
"typescript": "2.5.2",
|
||||
"webpack": "2.4.1"
|
||||
"typescript": "2.6.2",
|
||||
"webpack": "3.12.0"
|
||||
},
|
||||
"pre-commit": [
|
||||
"precommit-msg"
|
||||
@@ -83,6 +93,8 @@
|
||||
"lint:ts:test-serializer": "tslint --project ./packages/test-serializer/tsconfig.json",
|
||||
"lint:ts:uhk-usb": "tslint --project ./packages/uhk-usb/tsconfig.json",
|
||||
"lint:style": "stylelint \"packages/uhk-agent/src/**/*.scss\" \"packages/uhk-web/src/**/*.scss\" --syntax scss",
|
||||
"e2e": "lerna run e2e --scope uhk-web",
|
||||
"prebuild": "check-node-version --package",
|
||||
"build": "run-s build:common build:usb build:web build:electron",
|
||||
"build:web": "lerna exec --scope uhk-web npm run build",
|
||||
"build:electron": "cross-env AOT_BUILD=true run-s -sn build:electron:renderer build:electron:main",
|
||||
@@ -98,7 +110,9 @@
|
||||
"pack": "node ./scripts/release.js",
|
||||
"sprites": "node ./scripts/generate-svg-sprites",
|
||||
"release": "node ./scripts/release.js",
|
||||
"clean": "lerna exec rimraf ./node_modules ./dist"
|
||||
"clean": "lerna exec rimraf ./node_modules ./dist && rimraf ./node_modules ./dist",
|
||||
"predeploy-gh-pages": "run-s build:web",
|
||||
"deploy-gh-pages": "gh-pages -d packages/uhk-web/dist"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
146
packages/test-serializer/package-lock.json
generated
146
packages/test-serializer/package-lock.json
generated
@@ -201,11 +201,11 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"gaze": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz",
|
||||
"integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
|
||||
"integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
|
||||
"requires": {
|
||||
"globule": "0.1.0"
|
||||
"globule": "1.2.1"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
@@ -219,41 +219,37 @@
|
||||
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
|
||||
},
|
||||
"glob": {
|
||||
"version": "3.1.21",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
|
||||
"integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"requires": {
|
||||
"graceful-fs": "1.2.3",
|
||||
"inherits": "1.0.2",
|
||||
"minimatch": "0.2.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz",
|
||||
"integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js="
|
||||
}
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"globule": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
|
||||
"integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
|
||||
"integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
|
||||
"requires": {
|
||||
"glob": "3.1.21",
|
||||
"lodash": "1.0.2",
|
||||
"minimatch": "0.2.14"
|
||||
"glob": "7.1.2",
|
||||
"lodash": "4.17.10",
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
|
||||
"integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q="
|
||||
},
|
||||
"growl": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/growl/-/growl-1.7.0.tgz",
|
||||
"integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto="
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
|
||||
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
|
||||
},
|
||||
"grunt-exec": {
|
||||
"version": "0.4.7",
|
||||
"resolved": "https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.7.tgz",
|
||||
"integrity": "sha1-QAUf+k6wyWV+BTuV6I1ENSocLCU="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "2.0.0",
|
||||
@@ -362,31 +358,34 @@
|
||||
"integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4="
|
||||
},
|
||||
"jasmine-growl-reporter": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-0.2.1.tgz",
|
||||
"integrity": "sha1-1fCje5L2qD/VxkgrgJSVyQqLVf4=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-growl-reporter/-/jasmine-growl-reporter-1.0.1.tgz",
|
||||
"integrity": "sha512-dh7VjP3l0OLxL9+sw5vK6RrdH4gdHCNkTnUd9orViHDPr7Fe8LsXY+IObWauS2hX5khMFtjKRZCfTcDHKAjm/A==",
|
||||
"requires": {
|
||||
"growl": "1.7.0"
|
||||
"growl": "1.10.5"
|
||||
}
|
||||
},
|
||||
"jasmine-node": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-2.0.0.tgz",
|
||||
"integrity": "sha1-gXUacjJfVJdJCxQYGlUIfxsDcf8=",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-node/-/jasmine-node-2.0.1.tgz",
|
||||
"integrity": "sha512-1S5Z4Mof5yxwqLIApzyo2pV5WN2kRpTSgICvEo3+rJmKve9P94kolzC9eS0u5cyiT+gxBY2mwOQdxLbkhwKzoA==",
|
||||
"requires": {
|
||||
"coffee-script": "1.7.1",
|
||||
"gaze": "0.5.2",
|
||||
"jasmine-growl-reporter": "0.2.1",
|
||||
"minimist": "0.0.8",
|
||||
"gaze": "1.1.3",
|
||||
"grunt-exec": "0.4.7",
|
||||
"jasmine-growl-reporter": "1.0.1",
|
||||
"jasmine-reporters": "git://github.com/larrymyers/jasmine-reporters.git#2c7242dc11c15c2f156169bc704798568b8cb50d",
|
||||
"minimist": "0.0.10",
|
||||
"mkdirp": "0.3.5",
|
||||
"underscore": "1.6.0",
|
||||
"walkdir": "0.0.12"
|
||||
"walkdir": "0.0.12",
|
||||
"xml2js": "0.4.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.5",
|
||||
@@ -395,6 +394,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"jasmine-reporters": {
|
||||
"version": "git://github.com/larrymyers/jasmine-reporters.git#2c7242dc11c15c2f156169bc704798568b8cb50d",
|
||||
"requires": {
|
||||
"mkdirp": "0.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz",
|
||||
"integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jasmine-ts": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-ts/-/jasmine-ts-0.2.1.tgz",
|
||||
@@ -442,14 +454,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz",
|
||||
"integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "2.7.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
|
||||
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.0",
|
||||
@@ -470,12 +477,11 @@
|
||||
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
|
||||
"integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=",
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"lru-cache": "2.7.3",
|
||||
"sigmund": "1.0.1"
|
||||
"brace-expansion": "1.1.8"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
@@ -633,6 +639,11 @@
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
|
||||
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
@@ -656,11 +667,6 @@
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
|
||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
@@ -854,6 +860,20 @@
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
|
||||
"requires": {
|
||||
"sax": "1.2.4",
|
||||
"xmlbuilder": "9.0.7"
|
||||
}
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "9.0.7",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
|
||||
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
|
||||
@@ -14,16 +14,9 @@
|
||||
"npm": ">=5.1.0 <6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"uhk-common": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "2.6.0",
|
||||
"@types/node": "8.0.30",
|
||||
"jasmine": "2.8.0",
|
||||
"jasmine-core": "2.8.0",
|
||||
"jasmine-node": "2.0.0",
|
||||
"jasmine-ts": "0.2.1",
|
||||
"ts-node": "3.3.0",
|
||||
"uhk-common": "1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jasmine-ts --config=jasmine.json"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
]
|
||||
"experimentalDecorators": true
|
||||
}
|
||||
}
|
||||
|
||||
347
packages/uhk-agent/package-lock.json
generated
347
packages/uhk-agent/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@
|
||||
"command-line-args": "4.0.7",
|
||||
"decompress": "4.2.0",
|
||||
"decompress-bzip2": "4.0.0",
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"sudo-prompt": "7.0.0",
|
||||
"tmp": "0.0.33",
|
||||
"uhk-common": "^1.0.0",
|
||||
@@ -31,10 +31,10 @@
|
||||
"scripts": {
|
||||
"start": "electron ./dist/electron-main.js",
|
||||
"electron:spe": "electron ./dist/electron-main.js --spe",
|
||||
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-blhost",
|
||||
"build": "webpack && npm run install:build-deps && npm run build:usb && npm run download-firmware && npm run copy-to-tmp-folder",
|
||||
"build:usb": "electron-rebuild -w node-hid -p -m ./dist",
|
||||
"install:build-deps": "cd ./dist && npm i",
|
||||
"download-firmware": "node ../../scripts/download-firmware.js",
|
||||
"copy-blhost": "node ../../scripts/copy-blhost.js"
|
||||
"copy-to-tmp-folder": "node ../../scripts/copy-to-tmp-folder.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { AppService } from './services/app.service';
|
||||
import { SudoService } from './services/sudo.service';
|
||||
import { UhkBlhost } from '../../uhk-usb/src';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
import { setMenu } from './electron-menu';
|
||||
|
||||
const optionDefinitions = [
|
||||
{name: 'addons', type: Boolean},
|
||||
@@ -99,12 +100,12 @@ function createWindow() {
|
||||
},
|
||||
icon: path.join(__dirname, 'renderer/assets/images/agent-app-icon.png')
|
||||
});
|
||||
win.setMenuBarVisibility(false);
|
||||
setMenu(win);
|
||||
win.maximize();
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options);
|
||||
uhkHidDeviceService = new UhkHidDevice(logger, options, packagesDir);
|
||||
uhkBlhost = new UhkBlhost(logger, packagesDir);
|
||||
uhkOperations = new UhkOperations(logger, uhkBlhost, uhkHidDeviceService, packagesDir);
|
||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations);
|
||||
deviceService = new DeviceService(logger, win, uhkHidDeviceService, uhkOperations, packagesDir);
|
||||
appUpdateService = new AppUpdateService(logger, win, app);
|
||||
appService = new AppService(logger, win, deviceService, options, uhkHidDeviceService);
|
||||
sudoService = new SudoService(logger, options);
|
||||
|
||||
37
packages/uhk-agent/src/electron-menu.ts
Normal file
37
packages/uhk-agent/src/electron-menu.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { app, BrowserWindow, Menu, systemPreferences } from 'electron';
|
||||
import * as isDev from 'electron-is-dev';
|
||||
|
||||
export const setMenu = (win: BrowserWindow): void => {
|
||||
if (process.platform !== 'darwin' || isDev) {
|
||||
win.setMenuBarVisibility(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const template = [
|
||||
{
|
||||
label: app.getName(),
|
||||
submenu: [
|
||||
{role: 'quit'}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{role: 'cut'},
|
||||
{role: 'copy'},
|
||||
{role: 'paste'},
|
||||
{role: 'delete'},
|
||||
{role: 'selectall'}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// hide "Start Dictation" submenu item in Edit menu
|
||||
systemPreferences.setUserDefault('NSDisabledDictationMenuItem', 'boolean', true as any);
|
||||
// hide "Emoji & Symbols" submenu item in Edit menu
|
||||
systemPreferences.setUserDefault('NSDisabledCharacterPaletteMenuItem', 'boolean', false as any);
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
};
|
||||
@@ -3,5 +3,6 @@ import { SynchrounousResult } from 'tmp';
|
||||
export interface TmpFirmware {
|
||||
rightFirmwarePath: string;
|
||||
leftFirmwarePath: string;
|
||||
packageJsonPath: string;
|
||||
tmpDirectory: SynchrounousResult;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"npm": ">=5.1.0 <6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.7"
|
||||
"node-hid": "0.7.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import { IpcEvents, LogService } from 'uhk-common';
|
||||
import { MainServiceBase } from './main-service-base';
|
||||
|
||||
export class AppUpdateService extends MainServiceBase {
|
||||
|
||||
private sendAutoUpdateNotification = false;
|
||||
|
||||
constructor(protected logService: LogService,
|
||||
protected win: Electron.BrowserWindow,
|
||||
private app: Electron.App) {
|
||||
@@ -24,16 +27,21 @@ export class AppUpdateService extends MainServiceBase {
|
||||
|
||||
private initListeners() {
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
this.logService.debug('[AppUpdateService] checking for update');
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkingForUpdate);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', async (ev: any, info: UpdateInfo) => {
|
||||
this.logService.debug('[AppUpdateService] update available. Downloading started');
|
||||
await autoUpdater.downloadUpdate();
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.updateAvailable, info);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', (ev: any, info: UpdateInfo) => {
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
|
||||
if (this.sendAutoUpdateNotification) {
|
||||
this.logService.debug('[AppUpdateService] update not available');
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.updateNotAvailable, info);
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (ev: any, err: string) => {
|
||||
@@ -51,6 +59,7 @@ export class AppUpdateService extends MainServiceBase {
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (ev: any, info: UpdateInfo) => {
|
||||
this.logService.debug('[AppUpdateService] update downloaded');
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.autoUpdateDownloaded, info);
|
||||
});
|
||||
|
||||
@@ -61,12 +70,15 @@ export class AppUpdateService extends MainServiceBase {
|
||||
|
||||
ipcMain.on(IpcEvents.app.appStarted, () => {
|
||||
if (this.checkForUpdateAtStartup()) {
|
||||
this.sendAutoUpdateNotification = false;
|
||||
this.logService.debug('[AppUpdateService] app started. Automatically check for update.');
|
||||
this.checkForUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.autoUpdater.checkForUpdate, () => {
|
||||
this.logService.debug('[AppUpdateService] checkForUpdate request from renderer process');
|
||||
this.sendAutoUpdateNotification = true;
|
||||
this.checkForUpdate();
|
||||
});
|
||||
}
|
||||
@@ -75,14 +87,22 @@ export class AppUpdateService extends MainServiceBase {
|
||||
if (isDev) {
|
||||
const msg = '[AppUpdateService] Application update is not working in dev mode.';
|
||||
this.logService.info(msg);
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
|
||||
if (this.sendAutoUpdateNotification) {
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isFirstRun()) {
|
||||
const msg = '[AppUpdateService] Application update is skipping at first run.';
|
||||
this.logService.info(msg);
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
|
||||
if (this.sendAutoUpdateNotification) {
|
||||
this.sendIpcToWindow(IpcEvents.autoUpdater.checkForUpdateNotAvailable, msg);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BrowserWindow, ipcMain, shell } from 'electron';
|
||||
import { ipcMain, shell } from 'electron';
|
||||
import { UhkHidDevice } from 'uhk-usb';
|
||||
import * as os from 'os';
|
||||
|
||||
import { AppStartInfo, IpcEvents, LogService } from 'uhk-common';
|
||||
import { MainServiceBase } from './main-service-base';
|
||||
@@ -22,13 +23,14 @@ export class AppService extends MainServiceBase {
|
||||
|
||||
private async handleAppStartInfo(event: Electron.Event) {
|
||||
this.logService.info('[AppService] getAppStartInfo');
|
||||
|
||||
const deviceConnectionState = await this.uhkHidDeviceService.getDeviceConnectionStateAsync();
|
||||
const response: AppStartInfo = {
|
||||
deviceConnectionState,
|
||||
commandLineArgs: {
|
||||
addons: this.options.addons || false
|
||||
},
|
||||
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
|
||||
hasPermission: this.uhkHidDeviceService.hasPermission()
|
||||
platform: process.platform as string,
|
||||
osVersion: os.release()
|
||||
};
|
||||
this.logService.info('[AppService] getAppStartInfo response:', response);
|
||||
return event.sender.send(IpcEvents.app.getAppStartInfoReply, response);
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
ConfigurationReply,
|
||||
DeviceConnectionState,
|
||||
FirmwareUpgradeIpcResponse,
|
||||
getHardwareConfigFromDeviceResponse,
|
||||
HardwareModules,
|
||||
IpcEvents,
|
||||
IpcResponse,
|
||||
LogService,
|
||||
mapObjectToUserConfigBinaryBuffer,
|
||||
SaveUserConfigurationData
|
||||
SaveUserConfigurationData,
|
||||
UpdateFirmwareData
|
||||
} from 'uhk-common';
|
||||
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { emptyDir } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
import 'rxjs/add/observable/interval';
|
||||
import 'rxjs/add/observable/fromPromise';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import 'rxjs/add/operator/startWith';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/do';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
|
||||
import { saveTmpFirmware } from '../util/save-extract-firmware';
|
||||
import { TmpFirmware } from '../models/tmp-firmware';
|
||||
import { QueueManager } from './queue-manager';
|
||||
import { backupUserConfiguration, getBackupUserConfigurationContent } from '../util/backup-user-confoguration';
|
||||
import {
|
||||
backupUserConfiguration,
|
||||
getBackupUserConfigurationContent,
|
||||
getPackageJsonFromPathAsync,
|
||||
saveTmpFirmware
|
||||
} from '../util';
|
||||
|
||||
/**
|
||||
* IpcMain pair of the UHK Communication
|
||||
@@ -39,7 +48,8 @@ export class DeviceService {
|
||||
constructor(private logService: LogService,
|
||||
private win: Electron.BrowserWindow,
|
||||
private device: UhkHidDevice,
|
||||
private operations: UhkOperations) {
|
||||
private operations: UhkOperations,
|
||||
private rootDir: string) {
|
||||
this.pollUhkDevice();
|
||||
|
||||
ipcMain.on(IpcEvents.device.saveUserConfiguration, (...args: any[]) => {
|
||||
@@ -71,6 +81,24 @@ export class DeviceService {
|
||||
|
||||
ipcMain.on(IpcEvents.device.startConnectionPoller, this.pollUhkDevice.bind(this));
|
||||
|
||||
ipcMain.on(IpcEvents.device.recoveryDevice, (...args: any[]) => {
|
||||
this.queueManager.add({
|
||||
method: this.recoveryDevice,
|
||||
bind: this,
|
||||
params: args,
|
||||
asynchronous: true
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.device.enableUsbStackTest, (...args: any[]) => {
|
||||
this.queueManager.add({
|
||||
method: this.enableUsbStackTest,
|
||||
bind: this,
|
||||
params: args,
|
||||
asynchronous: true
|
||||
});
|
||||
});
|
||||
|
||||
logService.debug('[DeviceService] init success');
|
||||
}
|
||||
|
||||
@@ -84,10 +112,7 @@ export class DeviceService {
|
||||
try {
|
||||
await this.device.waitUntilKeyboardBusy();
|
||||
const result = await this.operations.loadConfigurations();
|
||||
const modules: HardwareModules = {
|
||||
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
|
||||
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
|
||||
};
|
||||
const modules: HardwareModules = await this.getHardwareModules(false);
|
||||
|
||||
const hardwareConfig = getHardwareConfigFromDeviceResponse(result.hardwareConfiguration);
|
||||
const uniqueId = hardwareConfig.uniqueId;
|
||||
@@ -110,33 +135,69 @@ export class DeviceService {
|
||||
event.sender.send(IpcEvents.device.loadConfigurationReply, JSON.stringify(response));
|
||||
}
|
||||
|
||||
public async getHardwareModules(catchError: boolean): Promise<HardwareModules> {
|
||||
try {
|
||||
await this.device.waitUntilKeyboardBusy();
|
||||
|
||||
return {
|
||||
leftModuleInfo: await this.operations.getLeftModuleVersionInfo(),
|
||||
rightModuleInfo: await this.operations.getRightModuleVersionInfo()
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (!catchError) {
|
||||
return err;
|
||||
}
|
||||
|
||||
this.logService.error('[DeviceService] Read hardware modules information failed', err);
|
||||
}
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.stopPollTimer();
|
||||
this.logService.info('[DeviceService] Device connection checker stopped.');
|
||||
}
|
||||
|
||||
public async updateFirmware(event: Electron.Event, args?: Array<string>): Promise<void> {
|
||||
const response = new IpcResponse();
|
||||
const response = new FirmwareUpgradeIpcResponse();
|
||||
const data: UpdateFirmwareData = JSON.parse(args[0]);
|
||||
|
||||
let firmwarePathData: TmpFirmware;
|
||||
|
||||
try {
|
||||
this.logService.debug('Agent version:', data.versionInformation.version);
|
||||
const hardwareModules = await this.getHardwareModules(false);
|
||||
this.logService.debug('Device right firmware version:', hardwareModules.rightModuleInfo.firmwareVersion);
|
||||
this.logService.debug('Device left firmware version:', hardwareModules.leftModuleInfo.firmwareVersion);
|
||||
|
||||
this.device.resetDeviceCache();
|
||||
this.stopPollTimer();
|
||||
|
||||
if (args && args.length > 0) {
|
||||
firmwarePathData = await saveTmpFirmware(args[0]);
|
||||
if (data.firmware) {
|
||||
firmwarePathData = await saveTmpFirmware(data.firmware);
|
||||
|
||||
const packageJson = await getPackageJsonFromPathAsync(firmwarePathData.packageJsonPath);
|
||||
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
||||
|
||||
await this.operations.updateRightFirmware(firmwarePathData.rightFirmwarePath);
|
||||
await this.operations.updateLeftModule(firmwarePathData.leftFirmwarePath);
|
||||
}
|
||||
else {
|
||||
const packageJsonPath = path.join(this.rootDir, 'packages/firmware/package.json');
|
||||
const packageJson = await getPackageJsonFromPathAsync(packageJsonPath);
|
||||
this.logService.debug('New firmware version:', packageJson.firmwareVersion);
|
||||
|
||||
await this.operations.updateRightFirmware();
|
||||
await this.operations.updateLeftModule();
|
||||
}
|
||||
|
||||
response.success = true;
|
||||
response.modules = await this.getHardwareModules(false);
|
||||
} catch (error) {
|
||||
const err = {message: error.message, stack: error.stack};
|
||||
this.logService.error('[DeviceService] updateFirmware error', err);
|
||||
|
||||
response.modules = await this.getHardwareModules(true);
|
||||
response.error = err;
|
||||
}
|
||||
|
||||
@@ -145,9 +206,42 @@ export class DeviceService {
|
||||
}
|
||||
|
||||
await snooze(500);
|
||||
|
||||
this.pollUhkDevice();
|
||||
|
||||
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||
}
|
||||
|
||||
public async recoveryDevice(event: Electron.Event): Promise<void> {
|
||||
const response = new FirmwareUpgradeIpcResponse();
|
||||
|
||||
try {
|
||||
this.stopPollTimer();
|
||||
|
||||
await this.operations.updateRightFirmware();
|
||||
|
||||
await snooze(500);
|
||||
|
||||
this.pollUhkDevice();
|
||||
|
||||
response.modules = await this.getHardwareModules(false);
|
||||
response.success = true;
|
||||
} catch (error) {
|
||||
const err = {message: error.message, stack: error.stack};
|
||||
this.logService.error('[DeviceService] updateFirmware error', err);
|
||||
|
||||
response.modules = await this.getHardwareModules(true);
|
||||
response.error = err;
|
||||
}
|
||||
|
||||
await snooze(500);
|
||||
event.sender.send(IpcEvents.device.updateFirmwareReply, response);
|
||||
}
|
||||
|
||||
public async enableUsbStackTest(event: Electron.Event) {
|
||||
await this.device.enableUsbStackTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* HID API not support device attached and detached event.
|
||||
* This method check the keyboard is attached to the computer or not.
|
||||
@@ -161,16 +255,11 @@ export class DeviceService {
|
||||
|
||||
this.pollTimer$ = Observable.interval(1000)
|
||||
.startWith(0)
|
||||
.map(() => this.device.deviceConnected())
|
||||
.distinctUntilChanged()
|
||||
.do((connected: boolean) => {
|
||||
const response: DeviceConnectionState = {
|
||||
connected,
|
||||
hasPermission: this.device.hasPermission()
|
||||
};
|
||||
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', response);
|
||||
.switchMap(() => Observable.fromPromise(this.device.getDeviceConnectionStateAsync()))
|
||||
.distinctUntilChanged<DeviceConnectionState>(isEqual)
|
||||
.do((state: DeviceConnectionState) => {
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, state);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', state);
|
||||
})
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
export const getPackageJsonFromPathAsync = async (filePath: string): Promise<any> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(JSON.parse(data));
|
||||
});
|
||||
});
|
||||
};
|
||||
3
packages/uhk-agent/src/util/index.ts
Normal file
3
packages/uhk-agent/src/util/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './backup-user-confoguration';
|
||||
export * from './get-package-json-from-path-async';
|
||||
export * from './save-extract-firmware';
|
||||
@@ -6,7 +6,7 @@ import * as decompressTarbz from 'decompress-tarbz2';
|
||||
|
||||
import { TmpFirmware } from '../models/tmp-firmware';
|
||||
|
||||
export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
|
||||
export async function saveTmpFirmware(data: Array<number>): Promise<TmpFirmware> {
|
||||
const tmpDirectory = dirSync();
|
||||
const zipFilePath = path.join(tmpDirectory.name, 'firmware.bz2');
|
||||
|
||||
@@ -16,15 +16,14 @@ export async function saveTmpFirmware(data: string): Promise<TmpFirmware> {
|
||||
return {
|
||||
tmpDirectory,
|
||||
rightFirmwarePath: path.join(tmpDirectory.name, 'devices/uhk60-right/firmware.hex'),
|
||||
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin')
|
||||
|
||||
leftFirmwarePath: path.join(tmpDirectory.name, 'modules/uhk60-left.bin'),
|
||||
packageJsonPath: path.join(tmpDirectory.name, 'package.json')
|
||||
};
|
||||
}
|
||||
|
||||
function writeDataToFile(data: string, filePath: string): Promise<void> {
|
||||
function writeDataToFile(data: Array<number>, filePath: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const array: Array<number> = JSON.parse(data);
|
||||
const buffer = new Buffer(array);
|
||||
const buffer = new Buffer(data);
|
||||
|
||||
fs.writeFile(filePath, buffer, err => {
|
||||
if (err) {
|
||||
|
||||
170
packages/uhk-common/package-lock.json
generated
170
packages/uhk-common/package-lock.json
generated
@@ -18,11 +18,11 @@
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"requires": {
|
||||
"color-convert": "1.9.0"
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"arrify": {
|
||||
@@ -36,9 +36,9 @@
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -55,13 +55,13 @@
|
||||
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
|
||||
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
|
||||
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.0",
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "4.4.0"
|
||||
"supports-color": "5.3.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
@@ -100,9 +100,9 @@
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
|
||||
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@@ -122,15 +122,15 @@
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||
"requires": {
|
||||
"lru-cache": "4.1.1",
|
||||
"lru-cache": "4.1.2",
|
||||
"shebang-command": "1.2.0",
|
||||
"which": "1.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
|
||||
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
|
||||
"integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
|
||||
"requires": {
|
||||
"pseudomap": "1.0.2",
|
||||
"yallist": "2.1.2"
|
||||
@@ -144,9 +144,9 @@
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz",
|
||||
"integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
|
||||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.1",
|
||||
@@ -271,9 +271,9 @@
|
||||
"integrity": "sha1-3i1mE20ALhErpw8/EMMc98NQsto="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
|
||||
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"homedir-polyfill": {
|
||||
"version": "1.0.1",
|
||||
@@ -284,9 +284,9 @@
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
|
||||
"integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg=="
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
|
||||
"integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
@@ -382,7 +382,7 @@
|
||||
"requires": {
|
||||
"jasmine": "2.8.0",
|
||||
"ts-node": "3.3.0",
|
||||
"typescript": "2.5.3",
|
||||
"typescript": "2.8.1",
|
||||
"yargs": "8.0.2"
|
||||
}
|
||||
},
|
||||
@@ -432,29 +432,29 @@
|
||||
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz",
|
||||
"integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y="
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz",
|
||||
"integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g=="
|
||||
},
|
||||
"mem": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
|
||||
"integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
|
||||
"requires": {
|
||||
"mimic-fn": "1.1.0"
|
||||
"mimic-fn": "1.2.0"
|
||||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
|
||||
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.8"
|
||||
"brace-expansion": "1.1.11"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
@@ -472,10 +472,10 @@
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||
"integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
|
||||
"requires": {
|
||||
"hosted-git-info": "2.5.0",
|
||||
"hosted-git-info": "2.6.0",
|
||||
"is-builtin-module": "1.0.0",
|
||||
"semver": "5.4.1",
|
||||
"validate-npm-package-license": "3.0.1"
|
||||
"semver": "5.5.0",
|
||||
"validate-npm-package-license": "3.0.3"
|
||||
}
|
||||
},
|
||||
"npm-run-path": {
|
||||
@@ -1942,18 +1942,26 @@
|
||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
|
||||
"integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
|
||||
"integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
|
||||
"requires": {
|
||||
"p-try": "1.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
|
||||
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
|
||||
"requires": {
|
||||
"p-limit": "1.1.0"
|
||||
"p-limit": "1.2.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
|
||||
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
||||
@@ -2030,9 +2038,9 @@
|
||||
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
@@ -2076,22 +2084,32 @@
|
||||
}
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
|
||||
"integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
|
||||
"integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
|
||||
"requires": {
|
||||
"spdx-license-ids": "1.2.2"
|
||||
"spdx-expression-parse": "3.0.0",
|
||||
"spdx-license-ids": "3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-exceptions": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
|
||||
"integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
|
||||
},
|
||||
"spdx-expression-parse": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
|
||||
"integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
||||
"requires": {
|
||||
"spdx-exceptions": "2.1.0",
|
||||
"spdx-license-ids": "3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-license-ids": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
|
||||
"integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
|
||||
"integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
@@ -2146,11 +2164,11 @@
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
|
||||
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
|
||||
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
|
||||
"requires": {
|
||||
"has-flag": "2.0.0"
|
||||
"has-flag": "3.0.0"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
@@ -2159,14 +2177,14 @@
|
||||
"integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=",
|
||||
"requires": {
|
||||
"arrify": "1.0.1",
|
||||
"chalk": "2.1.0",
|
||||
"diff": "3.4.0",
|
||||
"make-error": "1.3.0",
|
||||
"chalk": "2.3.2",
|
||||
"diff": "3.5.0",
|
||||
"make-error": "1.3.4",
|
||||
"minimist": "1.2.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"source-map-support": "0.4.18",
|
||||
"tsconfig": "6.0.0",
|
||||
"v8flags": "3.0.1",
|
||||
"v8flags": "3.0.2",
|
||||
"yn": "2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -2202,9 +2220,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
|
||||
"integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w=="
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz",
|
||||
"integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg=="
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
@@ -2212,20 +2230,20 @@
|
||||
"integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
|
||||
},
|
||||
"v8flags": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz",
|
||||
"integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.2.tgz",
|
||||
"integrity": "sha512-6sgSKoFw1UpUPd3cFdF7QGnrH6tDeBgW1F3v9gy8gLY0mlbiBXq8soy8aQpY6xeeCjH5K+JvC62Acp7gtl7wWA==",
|
||||
"requires": {
|
||||
"homedir-polyfill": "1.0.1"
|
||||
}
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
|
||||
"integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
|
||||
"integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
|
||||
"requires": {
|
||||
"spdx-correct": "1.0.2",
|
||||
"spdx-expression-parse": "1.0.4"
|
||||
"spdx-correct": "3.0.0",
|
||||
"spdx-expression-parse": "3.0.0"
|
||||
}
|
||||
},
|
||||
"walkdir": {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Common Library contains the common code for uhk-agent (electron-main) and web (electron-renderer) modules",
|
||||
"main": "dist/index.js",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"author": "Ultimate Gadget Laboratories",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,16 +19,6 @@
|
||||
"coverage": "nyc jasmine-ts --config=jasmine.json"
|
||||
},
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "2.6.0",
|
||||
"@types/node": "8.0.30",
|
||||
"jasmine": "2.8.0",
|
||||
"jasmine-core": "2.8.0",
|
||||
"jasmine-node": "2.0.0",
|
||||
"jasmine-ts": "0.2.1",
|
||||
"nyc": "11.2.1",
|
||||
"ts-node": "3.3.0"
|
||||
},
|
||||
"nyc": {
|
||||
"extension": [
|
||||
".ts"
|
||||
|
||||
@@ -41,16 +41,20 @@ export class HardwareConfiguration {
|
||||
}
|
||||
|
||||
fromBinary(buffer: UhkBuffer): HardwareConfiguration {
|
||||
this.signature = buffer.readString();
|
||||
this.majorVersion = buffer.readUInt8();
|
||||
this.minorVersion = buffer.readUInt8();
|
||||
this.patchVersion = buffer.readUInt8();
|
||||
this.brandId = buffer.readUInt8();
|
||||
this.deviceId = buffer.readUInt8();
|
||||
this.uniqueId = buffer.readUInt32();
|
||||
this.isVendorModeOn = buffer.readBoolean();
|
||||
this.isIso = buffer.readBoolean();
|
||||
return this;
|
||||
try {
|
||||
this.signature = buffer.readString();
|
||||
this.majorVersion = buffer.readUInt8();
|
||||
this.minorVersion = buffer.readUInt8();
|
||||
this.patchVersion = buffer.readUInt8();
|
||||
this.brandId = buffer.readUInt8();
|
||||
this.deviceId = buffer.readUInt8();
|
||||
this.uniqueId = buffer.readUInt32();
|
||||
this.isVendorModeOn = buffer.readBoolean();
|
||||
this.isIso = buffer.readBoolean();
|
||||
return this;
|
||||
} catch (e) {
|
||||
throw new Error('Please power cycle your keyboard (Invalid hardware configuration: Index out of bounds)');
|
||||
}
|
||||
}
|
||||
|
||||
toJsonObject(): any {
|
||||
|
||||
@@ -13,7 +13,7 @@ export enum KeystrokeActionFlag {
|
||||
|
||||
const KEYSTROKE_ACTION_FLAG_LENGTH = 3;
|
||||
|
||||
interface JsonObjectKeystrokeAction {
|
||||
export interface JsonObjectKeystrokeAction {
|
||||
keyActionType: string;
|
||||
scancode?: number;
|
||||
modifierMask?: number;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { binaryDefaultHelper, jsonDefaultHelper } from '../../../../test/serializer-test-helper';
|
||||
import { SwitchLayerAction } from './switch-layer-action';
|
||||
import { SwitchLayerAction, SwitchLayerMode } from './switch-layer-action';
|
||||
import { keyActionType } from './key-action';
|
||||
|
||||
// TODO: Add null, undefined, empty object, empty buffer test cases
|
||||
describe('switch-layer-action', () => {
|
||||
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, isLayerToggleable: false});
|
||||
const action = new SwitchLayerAction(<SwitchLayerAction>{layer: 0, switchLayerMode: SwitchLayerMode.hold});
|
||||
|
||||
it('should be instantiate', () => {
|
||||
expect(new SwitchLayerAction()).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('toString', () => {
|
||||
it('should return <SwitchLayerAction layer="0" toggle="false">', () => {
|
||||
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" toggle="false">');
|
||||
it('should return <SwitchLayerAction layer="0" switchLayerMode="hold">', () => {
|
||||
expect(action.toString()).toEqual('<SwitchLayerAction layer="0" switchLayerMode="hold">');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,4 +31,20 @@ describe('switch-layer-action', () => {
|
||||
binaryDefaultHelper(action);
|
||||
});
|
||||
});
|
||||
|
||||
describe('backward compatibility of the "toggle" property ', () => {
|
||||
it('should map toggle=false to SwitchLayerMode.holdAndDoubleTapToggle', () => {
|
||||
const oldAction = new SwitchLayerAction();
|
||||
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: false});
|
||||
|
||||
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.holdAndDoubleTapToggle);
|
||||
});
|
||||
|
||||
it('should map toggle=true to SwitchLayerMode.toggle', () => {
|
||||
const oldAction = new SwitchLayerAction();
|
||||
oldAction.fromJsonObject({keyActionType: keyActionType.SwitchLayerAction, layer: 0, toggle: true});
|
||||
|
||||
expect(oldAction.switchLayerMode).toEqual(SwitchLayerMode.toggle);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,9 +8,48 @@ export enum LayerName {
|
||||
mouse
|
||||
}
|
||||
|
||||
export enum SwitchLayerMode {
|
||||
holdAndDoubleTapToggle = 'holdAndDoubleTapToggle',
|
||||
toggle = 'toggle',
|
||||
hold = 'hold'
|
||||
}
|
||||
|
||||
export const mapSwitchLayerModeToNumber = (switchLayerMode: SwitchLayerMode): number => {
|
||||
switch (switchLayerMode) {
|
||||
case SwitchLayerMode.holdAndDoubleTapToggle:
|
||||
return 0;
|
||||
|
||||
case SwitchLayerMode.toggle:
|
||||
return 1;
|
||||
|
||||
case SwitchLayerMode.hold:
|
||||
return 2;
|
||||
|
||||
default:
|
||||
throw new Error(`Can not map ${switchLayerMode} to number`);
|
||||
}
|
||||
};
|
||||
|
||||
export const mapNumberToSwitchLayerMode = (value: number): SwitchLayerMode => {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return SwitchLayerMode.holdAndDoubleTapToggle;
|
||||
|
||||
case 1:
|
||||
return SwitchLayerMode.toggle;
|
||||
|
||||
case 2:
|
||||
return SwitchLayerMode.hold;
|
||||
|
||||
default:
|
||||
throw new Error(`Can not map "${value}" to SwitchLayerMode`);
|
||||
}
|
||||
};
|
||||
|
||||
export class SwitchLayerAction extends KeyAction {
|
||||
|
||||
isLayerToggleable: boolean;
|
||||
@assertEnum(SwitchLayerMode)
|
||||
switchLayerMode: SwitchLayerMode;
|
||||
|
||||
@assertEnum(LayerName)
|
||||
layer: LayerName;
|
||||
@@ -20,21 +59,29 @@ export class SwitchLayerAction extends KeyAction {
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
this.isLayerToggleable = other.isLayerToggleable;
|
||||
this.switchLayerMode = other.switchLayerMode;
|
||||
this.layer = other.layer;
|
||||
}
|
||||
|
||||
fromJsonObject(jsonObject: any): SwitchLayerAction {
|
||||
this.assertKeyActionType(jsonObject);
|
||||
this.layer = LayerName[<string>jsonObject.layer];
|
||||
this.isLayerToggleable = jsonObject.toggle;
|
||||
|
||||
// Backward compatibility when "switchLayerMode" was a boolean type as "toggle"
|
||||
if (typeof jsonObject.toggle === 'boolean') {
|
||||
this.switchLayerMode = jsonObject.toggle ? SwitchLayerMode.toggle : SwitchLayerMode.holdAndDoubleTapToggle;
|
||||
}
|
||||
else {
|
||||
this.switchLayerMode = jsonObject.switchLayerMode;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fromBinary(buffer: UhkBuffer): SwitchLayerAction {
|
||||
this.readAndAssertKeyActionId(buffer);
|
||||
this.layer = buffer.readUInt8();
|
||||
this.isLayerToggleable = buffer.readBoolean();
|
||||
this.switchLayerMode = mapNumberToSwitchLayerMode(buffer.readUInt8());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -42,18 +89,18 @@ export class SwitchLayerAction extends KeyAction {
|
||||
return {
|
||||
keyActionType: keyActionType.SwitchLayerAction,
|
||||
layer: LayerName[this.layer],
|
||||
toggle: this.isLayerToggleable
|
||||
switchLayerMode: this.switchLayerMode
|
||||
};
|
||||
}
|
||||
|
||||
toBinary(buffer: UhkBuffer) {
|
||||
buffer.writeUInt8(KeyActionId.SwitchLayerAction);
|
||||
buffer.writeUInt8(this.layer);
|
||||
buffer.writeBoolean(this.isLayerToggleable);
|
||||
buffer.writeUInt8(mapSwitchLayerModeToNumber(this.switchLayerMode));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<SwitchLayerAction layer="${this.layer}" toggle="${this.isLayerToggleable}">`;
|
||||
return `<SwitchLayerAction layer="${this.layer}" switchLayerMode="${this.switchLayerMode}">`;
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
|
||||
@@ -127,7 +127,7 @@ export class Keymap {
|
||||
if (currentLayerId - 1 === baseKeyAction.layer) {
|
||||
if (currentKeyAction instanceof SwitchLayerAction) {
|
||||
if (currentKeyAction.layer === baseKeyAction.layer &&
|
||||
currentKeyAction.isLayerToggleable === baseKeyAction.isLayerToggleable) {
|
||||
currentKeyAction.switchLayerMode === baseKeyAction.switchLayerMode) {
|
||||
continue;
|
||||
}
|
||||
// tslint:disable-next-line: max-line-length
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { assertEnum, assertUInt8 } from '../../assert';
|
||||
import { assertEnum, assertUInt8, assertUInt16 } from '../../assert';
|
||||
import { UhkBuffer } from '../../uhk-buffer';
|
||||
import { KeyModifiers } from '../key-modifiers';
|
||||
import { MacroAction, MacroActionId, MacroKeySubAction, macroActionType } from './macro-action';
|
||||
import { KeystrokeType } from '../key-action';
|
||||
|
||||
interface JsObjectKeyMacroAction {
|
||||
export interface JsObjectKeyMacroAction {
|
||||
macroActionType: string;
|
||||
action: string;
|
||||
type?: string;
|
||||
@@ -20,12 +20,24 @@ export class KeyMacroAction extends MacroAction {
|
||||
@assertEnum(KeystrokeType)
|
||||
type: KeystrokeType;
|
||||
|
||||
@assertUInt8
|
||||
scancode: number;
|
||||
|
||||
@assertUInt8
|
||||
modifierMask: number;
|
||||
|
||||
@assertUInt16
|
||||
private _scancode: number;
|
||||
|
||||
set scancode(scancode: number) {
|
||||
this._scancode = scancode;
|
||||
if (this.type !== KeystrokeType.shortMedia && this.type !== KeystrokeType.longMedia) {
|
||||
return;
|
||||
}
|
||||
this.type = scancode < 256 ? KeystrokeType.shortMedia : KeystrokeType.longMedia;
|
||||
}
|
||||
|
||||
get scancode() {
|
||||
return this._scancode;
|
||||
}
|
||||
|
||||
constructor(other?: KeyMacroAction) {
|
||||
super();
|
||||
if (!other) {
|
||||
@@ -33,7 +45,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
}
|
||||
this.action = other.action;
|
||||
this.type = other.type;
|
||||
this.scancode = other.scancode;
|
||||
this._scancode = other._scancode;
|
||||
this.modifierMask = other.modifierMask;
|
||||
}
|
||||
|
||||
@@ -45,7 +57,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
} else {
|
||||
this.type = KeystrokeType[jsObject.type];
|
||||
}
|
||||
this.scancode = jsObject.scancode;
|
||||
this._scancode = jsObject.scancode;
|
||||
this.modifierMask = jsObject.modifierMask;
|
||||
return this;
|
||||
}
|
||||
@@ -58,7 +70,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
this.type = keyMacroType & 0b11;
|
||||
keyMacroType >>= 2;
|
||||
if (keyMacroType & 0b10) {
|
||||
this.scancode = buffer.readUInt8();
|
||||
this._scancode = this.type === KeystrokeType.longMedia ? buffer.readUInt16() : buffer.readUInt8();
|
||||
}
|
||||
if (keyMacroType & 0b01) {
|
||||
this.modifierMask = buffer.readUInt8();
|
||||
@@ -78,7 +90,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
} else {
|
||||
jsObject.type = KeystrokeType[this.type];
|
||||
}
|
||||
jsObject.scancode = this.scancode;
|
||||
jsObject.scancode = this._scancode;
|
||||
}
|
||||
|
||||
if (this.hasModifiers()) {
|
||||
@@ -98,7 +110,11 @@ export class KeyMacroAction extends MacroAction {
|
||||
|
||||
buffer.writeUInt8(keyMacroType);
|
||||
if (this.hasScancode()) {
|
||||
buffer.writeUInt8(this.scancode);
|
||||
if (this.type === KeystrokeType.longMedia) {
|
||||
buffer.writeUInt16(this.scancode);
|
||||
} else {
|
||||
buffer.writeUInt8(this.scancode);
|
||||
}
|
||||
}
|
||||
if (this.hasModifiers()) {
|
||||
buffer.writeUInt8(this.modifierMask);
|
||||
@@ -106,7 +122,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<KeyMacroAction action="${this.action}" scancode="${this.scancode}" modifierMask="${this.modifierMask}">`;
|
||||
return `<KeyMacroAction action="${this.action}" scancode="${this._scancode}" modifierMask="${this.modifierMask}">`;
|
||||
}
|
||||
|
||||
isModifierActive(modifier: KeyModifiers): boolean {
|
||||
@@ -114,7 +130,7 @@ export class KeyMacroAction extends MacroAction {
|
||||
}
|
||||
|
||||
hasScancode(): boolean {
|
||||
return !!this.scancode;
|
||||
return !!this._scancode;
|
||||
}
|
||||
|
||||
hasModifiers(): boolean {
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('keymap', () => {
|
||||
{
|
||||
keyActionType: 'switchLayer',
|
||||
layer: 'mod',
|
||||
toggle: false
|
||||
switchLayerMode: 'holdAndDoubleTapToggle'
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -121,7 +121,7 @@ describe('keymap', () => {
|
||||
{
|
||||
keyActionType: 'switchLayer',
|
||||
layer: 'mod',
|
||||
toggle: false
|
||||
switchLayerMode: 'holdAndDoubleTapToggle'
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -151,7 +151,7 @@ describe('keymap', () => {
|
||||
|
||||
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
|
||||
// tslint:disable-next-line: max-line-length
|
||||
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" toggle="false">');
|
||||
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is not switch layer. <KeystrokeAction type="basic" scancode="44"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
|
||||
});
|
||||
|
||||
it('should normalize SwitchLayerAction if non base layer action is other SwitchLayerAction', () => {
|
||||
@@ -262,7 +262,7 @@ describe('keymap', () => {
|
||||
{
|
||||
keyActionType: 'switchLayer',
|
||||
layer: 'mod',
|
||||
toggle: false
|
||||
switchLayerMode: 'holdAndDoubleTapToggle'
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -274,7 +274,7 @@ describe('keymap', () => {
|
||||
{
|
||||
keyActionType: 'switchLayer',
|
||||
layer: 'mod',
|
||||
toggle: false
|
||||
switchLayerMode: 'holdAndDoubleTapToggle'
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -304,6 +304,6 @@ describe('keymap', () => {
|
||||
|
||||
expect(inputUserConfig.toJsonObject()).toEqual(expectedJsonConfig);
|
||||
// tslint:disable-next-line: max-line-length
|
||||
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" toggle="false"> will be override with <SwitchLayerAction layer="0" toggle="false">');
|
||||
expect(console.warn).toHaveBeenCalledWith('QWERTY.layers[1]modules[0].keyActions[0] is different switch layer. <SwitchLayerAction layer="1" switchLayerMode="holdAndDoubleTapToggle"> will be override with <SwitchLayerAction layer="0" switchLayerMode="holdAndDoubleTapToggle">');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,12 +3,12 @@ import { UhkBuffer } from '../../uhk-buffer';
|
||||
import { MacroAction, MacroActionId, MacroMouseSubAction, macroActionType } from './macro-action';
|
||||
|
||||
export enum MouseButtons {
|
||||
Left = 1 << 0,
|
||||
Middle = 1 << 1,
|
||||
Right = 1 << 2
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Middle = 2
|
||||
}
|
||||
|
||||
interface JsObjectMouseButtonMacroAction {
|
||||
export interface JsObjectMouseButtonMacroAction {
|
||||
macroActionType: string;
|
||||
action: string;
|
||||
mouseButtonsMask?: number;
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
},
|
||||
{
|
||||
"id": "70",
|
||||
"text": "Print Screen"
|
||||
"text": "Print Screen SysRq"
|
||||
},
|
||||
{
|
||||
"id": "72",
|
||||
@@ -454,14 +454,6 @@
|
||||
"scancode": 182
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "132",
|
||||
"text": "Stop/Eject",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 204
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "133",
|
||||
"text": "Play/Pause",
|
||||
@@ -503,11 +495,19 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "138",
|
||||
"text": "WWW",
|
||||
"id": "145",
|
||||
"text": "History Back",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 138
|
||||
"scancode": 548
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "146",
|
||||
"text": "History Forward",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 549
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -515,14 +515,6 @@
|
||||
{
|
||||
"text": "Launch application",
|
||||
"children": [
|
||||
{
|
||||
"id": "142",
|
||||
"text": "Launch Web Browser",
|
||||
"additional": {
|
||||
"type": "media",
|
||||
"scancode": 406
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "143",
|
||||
"text": "Launch Email Client",
|
||||
@@ -687,5 +679,50 @@
|
||||
"text": "."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "International",
|
||||
"children": [
|
||||
{
|
||||
"id": "235",
|
||||
"text": "International 1",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 135
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "236",
|
||||
"text": "International 2",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 136
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "237",
|
||||
"text": "International 3",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 137
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "244",
|
||||
"text": "Language 1",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 144
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "245",
|
||||
"text": "Language 2",
|
||||
"additional": {
|
||||
"type": "basic",
|
||||
"scancode": 145
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { assertUInt8, assertUInt16 } from '../assert';
|
||||
import { assertUInt16, assertUInt8 } from '../assert';
|
||||
import { UhkBuffer } from '../uhk-buffer';
|
||||
import { Keymap } from './keymap';
|
||||
import { Macro } from './macro';
|
||||
import { ModuleConfiguration } from './module-configuration';
|
||||
import { ConfigSerializer } from '../config-serializer';
|
||||
import { KeystrokeAction, NoneAction } from './key-action';
|
||||
import { SecondaryRoleAction } from './secondary-role-action';
|
||||
import { isScancodeExists } from './scancode-checker';
|
||||
|
||||
export class UserConfiguration {
|
||||
|
||||
@@ -90,7 +93,7 @@ export class UserConfiguration {
|
||||
this.mouseMoveAcceleratedSpeed = jsonObject.mouseMoveAcceleratedSpeed;
|
||||
this.mouseScrollInitialSpeed = jsonObject.mouseScrollInitialSpeed;
|
||||
this.mouseScrollAcceleration = jsonObject.mouseScrollAcceleration;
|
||||
this.mouseScrollDeceleratedSpeed = jsonObject.mouseScrollAcceleration;
|
||||
this.mouseScrollDeceleratedSpeed = jsonObject.mouseScrollDeceleratedSpeed;
|
||||
this.mouseScrollBaseSpeed = jsonObject.mouseScrollBaseSpeed;
|
||||
this.mouseScrollAcceleratedSpeed = jsonObject.mouseScrollAcceleratedSpeed;
|
||||
this.moduleConfigurations = jsonObject.moduleConfigurations.map((moduleConfiguration: any) => {
|
||||
@@ -102,7 +105,9 @@ export class UserConfiguration {
|
||||
return macro;
|
||||
});
|
||||
this.keymaps = jsonObject.keymaps.map((keymap: any) => new Keymap().fromJsonObject(keymap, this.macros));
|
||||
this.clean();
|
||||
this.recalculateConfigurationLength();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -138,6 +143,8 @@ export class UserConfiguration {
|
||||
this.keymaps = buffer.readArray<Keymap>(uhkBuffer => new Keymap().fromBinary(uhkBuffer, this.macros));
|
||||
ConfigSerializer.resolveSwitchKeymapActions(this.keymaps);
|
||||
|
||||
this.clean();
|
||||
|
||||
if (this.userConfigurationLength === 0) {
|
||||
this.recalculateConfigurationLength();
|
||||
}
|
||||
@@ -222,4 +229,34 @@ export class UserConfiguration {
|
||||
this.deviceName = 'My UHK';
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove not allowed settings/bugs
|
||||
* 1. Layer Switcher secondary roles allowed only on base layers
|
||||
*/
|
||||
private clean(): void {
|
||||
for (const keymap of this.keymaps) {
|
||||
for (let layerId = 1; layerId < keymap.layers.length; layerId++) {
|
||||
const layer = keymap.layers[layerId];
|
||||
|
||||
for (const module of layer.modules) {
|
||||
for (let keyActionId = 0; keyActionId < module.keyActions.length; keyActionId++) {
|
||||
const keyAction = module.keyActions[keyActionId];
|
||||
if (!keyAction || !(keyAction instanceof KeystrokeAction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyAction.secondaryRoleAction === SecondaryRoleAction.fn ||
|
||||
keyAction.secondaryRoleAction === SecondaryRoleAction.mod ||
|
||||
keyAction.secondaryRoleAction === SecondaryRoleAction.mouse) {
|
||||
(keyAction as any)._secondaryRoleAction = undefined;
|
||||
}
|
||||
|
||||
if (keyAction.hasScancode() && !isScancodeExists(keyAction.scancode)) {
|
||||
module.keyActions[keyActionId] = new NoneAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { CommandLineArgs } from './command-line-args';
|
||||
import { DeviceConnectionState } from './device-connection-state';
|
||||
|
||||
export interface AppStartInfo {
|
||||
commandLineArgs: CommandLineArgs;
|
||||
deviceConnected: boolean;
|
||||
hasPermission: boolean;
|
||||
deviceConnectionState: DeviceConnectionState;
|
||||
platform: string;
|
||||
osVersion: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { UdevRulesInfo } from './udev-rules-info';
|
||||
|
||||
export interface DeviceConnectionState {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
bootloaderActive: boolean;
|
||||
zeroInterfaceAvailable: boolean;
|
||||
udevRulesInfo: UdevRulesInfo;
|
||||
}
|
||||
|
||||
@@ -8,3 +8,5 @@ export * from './device-connection-state';
|
||||
export * from './hardware-modules';
|
||||
export * from './hardware-module-info';
|
||||
export * from './save-user-configuration-data';
|
||||
export * from './udev-rules-info';
|
||||
export * from './update-firmware-data';
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { HardwareModules } from './hardware-modules';
|
||||
|
||||
export class IpcResponse {
|
||||
success: boolean;
|
||||
error?: { message: string };
|
||||
}
|
||||
|
||||
export class FirmwareUpgradeIpcResponse extends IpcResponse {
|
||||
modules?: HardwareModules;
|
||||
}
|
||||
|
||||
16
packages/uhk-common/src/models/udev-rules-info.ts
Normal file
16
packages/uhk-common/src/models/udev-rules-info.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* What is the state of the udev rules.
|
||||
* Only on Linux need extra udev rules.
|
||||
*/
|
||||
export enum UdevRulesInfo {
|
||||
Unkonwn,
|
||||
Ok,
|
||||
/**
|
||||
* Udev rules not exists need to setup on Linux
|
||||
*/
|
||||
NeedToSetup,
|
||||
/**
|
||||
* Udev rules exist but different than expected on Linux
|
||||
*/
|
||||
Different
|
||||
}
|
||||
6
packages/uhk-common/src/models/update-firmware-data.ts
Normal file
6
packages/uhk-common/src/models/update-firmware-data.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { VersionInformation } from './version-information';
|
||||
|
||||
export interface UpdateFirmwareData {
|
||||
versionInformation: VersionInformation;
|
||||
firmware?: Array<number>;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export namespace Constants {
|
||||
export const AGENT_GITHUB_URL = 'https://github.com/UltimateHackingKeyboard/agent';
|
||||
export const FIRMWARE_GITHUB_ISSUE_URL = 'https://github.com/UltimateHackingKeyboard/agent/issues/new';
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { HardwareConfiguration, UhkBuffer, UserConfiguration } from '../../index';
|
||||
import { HardwareConfiguration, UhkBuffer, UserConfiguration } from '../config-serializer';
|
||||
|
||||
export const getHardwareConfigFromDeviceResponse = (json: string): HardwareConfiguration => {
|
||||
const data = JSON.parse(json);
|
||||
const hardwareConfig = new HardwareConfiguration();
|
||||
hardwareConfig.fromBinary(UhkBuffer.fromArray(data));
|
||||
|
||||
if (hardwareConfig.uniqueId > 0) {
|
||||
return hardwareConfig;
|
||||
if (hardwareConfig.signature === 'FTY') {
|
||||
throw Error('The device is in factory reset mode. Power-cycle the device to use it with Agent!');
|
||||
}
|
||||
return null;
|
||||
|
||||
if (hardwareConfig.signature !== 'UHK') {
|
||||
throw Error('Please power cycle your keyboard (Invalid hardware configuration: Invalid signature)');
|
||||
}
|
||||
|
||||
return hardwareConfig;
|
||||
};
|
||||
|
||||
export const getUserConfigFromDeviceResponse = (json: string): UserConfiguration => {
|
||||
|
||||
@@ -2,6 +2,7 @@ export { IpcEvents } from './ipcEvents';
|
||||
export * from './log';
|
||||
export * from './constants';
|
||||
export * from './helpers';
|
||||
export * from './is-equal-array';
|
||||
|
||||
// Source: http://stackoverflow.com/questions/13720256/javascript-regex-camelcase-to-sentence
|
||||
export function camelCaseToSentence(camelCasedText: string): string {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class App {
|
||||
export class App {
|
||||
public static readonly appStarted = 'app-started';
|
||||
public static readonly getAppStartInfo = 'app-get-start-info';
|
||||
public static readonly getAppStartInfoReply = 'app-get-start-info-reply';
|
||||
@@ -6,7 +6,7 @@ class App {
|
||||
public static readonly openUrl = 'open-url';
|
||||
}
|
||||
|
||||
class AutoUpdate {
|
||||
export class AutoUpdate {
|
||||
public static readonly checkingForUpdate = 'checking-for-update';
|
||||
public static readonly updateAvailable = 'update-available';
|
||||
public static readonly updateNotAvailable = 'update-not-available';
|
||||
@@ -18,7 +18,7 @@ class AutoUpdate {
|
||||
public static readonly checkForUpdateNotAvailable = 'check-for-update-not-available';
|
||||
}
|
||||
|
||||
class Device {
|
||||
export class Device {
|
||||
public static readonly setPrivilegeOnLinux = 'set-privilege-on-linux';
|
||||
public static readonly setPrivilegeOnLinuxReply = 'set-privilege-on-linux-reply';
|
||||
public static readonly deviceConnectionStateChanged = 'device-connection-state-changed';
|
||||
@@ -29,6 +29,8 @@ class Device {
|
||||
public static readonly updateFirmware = 'device-update-firmware';
|
||||
public static readonly updateFirmwareReply = 'device-update-firmware-reply';
|
||||
public static readonly startConnectionPoller = 'device-start-connection-poller';
|
||||
public static readonly recoveryDevice = 'device-recovery';
|
||||
public static readonly enableUsbStackTest = 'enable-usb-stack-test';
|
||||
}
|
||||
|
||||
export class IpcEvents {
|
||||
|
||||
15
packages/uhk-common/src/util/is-equal-array.ts
Normal file
15
packages/uhk-common/src/util/is-equal-array.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
export const isEqualArray = (arr1: Array<any>, arr2: Array<any>): boolean => {
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const a of arr1) {
|
||||
if (!arr2.some(b => isEqual(a, b))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -1,17 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"declaration": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": [
|
||||
"es2015.iterable",
|
||||
"dom",
|
||||
|
||||
289
packages/uhk-usb/package-lock.json
generated
289
packages/uhk-usb/package-lock.json
generated
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"requires": true,
|
||||
"name": "uhk-usb",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "8.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
|
||||
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
|
||||
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
@@ -18,12 +21,12 @@
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
|
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||
"requires": {
|
||||
"delegates": "1.0.0",
|
||||
"readable-stream": "2.3.6"
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"bindings": {
|
||||
@@ -33,17 +36,36 @@
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
||||
"requires": {
|
||||
"readable-stream": "2.3.6",
|
||||
"safe-buffer": "5.1.1"
|
||||
"readable-stream": "^2.3.5",
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"buffer-alloc": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
|
||||
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
|
||||
"requires": {
|
||||
"buffer-alloc-unsafe": "^1.1.0",
|
||||
"buffer-fill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"buffer-alloc-unsafe": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
|
||||
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
|
||||
},
|
||||
"buffer-fill": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
|
||||
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz",
|
||||
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
|
||||
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
@@ -65,13 +87,13 @@
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
|
||||
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
|
||||
"requires": {
|
||||
"mimic-response": "1.0.0"
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
|
||||
"integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
@@ -88,27 +110,32 @@
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
||||
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
|
||||
"requires": {
|
||||
"once": "1.4.0"
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"expand-template": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz",
|
||||
"integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
|
||||
"integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"requires": {
|
||||
"aproba": "1.2.0",
|
||||
"console-control-strings": "1.1.0",
|
||||
"has-unicode": "2.0.1",
|
||||
"object-assign": "4.1.1",
|
||||
"signal-exit": "3.0.2",
|
||||
"string-width": "1.0.2",
|
||||
"strip-ansi": "3.0.1",
|
||||
"wide-align": "1.1.2"
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"github-from-package": {
|
||||
@@ -136,7 +163,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
@@ -145,18 +172,18 @@
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
|
||||
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
@@ -164,32 +191,32 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
|
||||
},
|
||||
"node-abi": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.3.0.tgz",
|
||||
"integrity": "sha512-zwm6vU3SsVgw3e9fu48JBaRBCJGIvAgysDsqtf5+vEexFE71bEOtaMWb5zr/zODZNzTPtQlqUUpC79k68Hspow==",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.5.tgz",
|
||||
"integrity": "sha512-aa/UC6Nr3+tqhHGRsAuw/edz7/q9nnetBrKWxj6rpTtm+0X9T1qU7lIEHMS3yN9JwAbRiKUbRRFy1PLz/y3aaA==",
|
||||
"requires": {
|
||||
"semver": "5.5.0"
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-hid": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.5.7.tgz",
|
||||
"integrity": "sha512-dwwpOetL2+MGYgivbO22ML+45ieCGbueWv1rYxRgBoEc2QMp6UF6ZucEkYts1IA3YPWJNkmpGh6dqQ85n19szw==",
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-hid/-/node-hid-0.7.3.tgz",
|
||||
"integrity": "sha512-LOCqWqcOlng+Kn1Qj/54zrPVfCagg1O7RlSgMmugykBcoYvUud6BswTrJM2aXuBac+bCCm3lA2srRG8YfmyXZQ==",
|
||||
"requires": {
|
||||
"bindings": "1.3.0",
|
||||
"nan": "2.10.0",
|
||||
"prebuild-install": "2.5.1"
|
||||
"bindings": "^1.3.0",
|
||||
"nan": "^2.10.0",
|
||||
"prebuild-install": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"noop-logger": {
|
||||
@@ -202,10 +229,10 @@
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"requires": {
|
||||
"are-we-there-yet": "1.1.4",
|
||||
"console-control-strings": "1.1.0",
|
||||
"gauge": "2.7.4",
|
||||
"set-blocking": "2.0.0"
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
@@ -223,7 +250,7 @@
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
@@ -232,25 +259,25 @@
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.1.tgz",
|
||||
"integrity": "sha512-3DX9L6pzwc1m1ksMkW3Ky2WLgPQUBiySOfXVl3WZyAeJSyJb4wtoH9OmeRGcubAWsMlLiL8BTHbwfm/jPQE9Ag==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz",
|
||||
"integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==",
|
||||
"requires": {
|
||||
"detect-libc": "1.0.3",
|
||||
"expand-template": "1.1.0",
|
||||
"detect-libc": "^1.0.3",
|
||||
"expand-template": "^1.0.2",
|
||||
"github-from-package": "0.0.0",
|
||||
"minimist": "1.2.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"node-abi": "2.3.0",
|
||||
"noop-logger": "0.1.1",
|
||||
"npmlog": "4.1.2",
|
||||
"os-homedir": "1.0.2",
|
||||
"pump": "2.0.1",
|
||||
"rc": "1.2.6",
|
||||
"simple-get": "2.7.0",
|
||||
"tar-fs": "1.16.0",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"which-pm-runs": "1.0.0"
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-abi": "^2.2.0",
|
||||
"noop-logger": "^0.1.1",
|
||||
"npmlog": "^4.0.1",
|
||||
"os-homedir": "^1.0.1",
|
||||
"pump": "^2.0.1",
|
||||
"rc": "^1.1.6",
|
||||
"simple-get": "^2.7.0",
|
||||
"tar-fs": "^1.13.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"which-pm-runs": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
@@ -263,44 +290,44 @@
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
|
||||
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"once": "1.4.0"
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz",
|
||||
"integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||
"requires": {
|
||||
"deep-extend": "0.4.2",
|
||||
"ini": "1.3.5",
|
||||
"minimist": "1.2.0",
|
||||
"strip-json-comments": "2.0.1"
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "2.0.0",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.1.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
@@ -318,13 +345,13 @@
|
||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.7.0.tgz",
|
||||
"integrity": "sha512-RkE9rGPHcxYZ/baYmgJtOSM63vH0Vyq+ma5TijBcLla41SWlh8t6XYIGMR/oeZcmr+/G8k+zrClkkVrtnQ0esg==",
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"requires": {
|
||||
"decompress-response": "3.3.0",
|
||||
"once": "1.4.0",
|
||||
"simple-concat": "1.0.0"
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
@@ -332,9 +359,9 @@
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
@@ -342,15 +369,15 @@
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
@@ -359,14 +386,14 @@
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz",
|
||||
"integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==",
|
||||
"version": "1.16.3",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
|
||||
"integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
|
||||
"requires": {
|
||||
"chownr": "1.0.1",
|
||||
"mkdirp": "0.5.1",
|
||||
"pump": "1.0.3",
|
||||
"tar-stream": "1.5.5"
|
||||
"chownr": "^1.0.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pump": "^1.0.0",
|
||||
"tar-stream": "^1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"pump": {
|
||||
@@ -374,29 +401,37 @@
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
|
||||
"integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
|
||||
"requires": {
|
||||
"end-of-stream": "1.4.1",
|
||||
"once": "1.4.0"
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
|
||||
"integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
|
||||
"requires": {
|
||||
"bl": "1.2.2",
|
||||
"end-of-stream": "1.4.1",
|
||||
"readable-stream": "2.3.6",
|
||||
"xtend": "4.0.1"
|
||||
"bl": "^1.0.0",
|
||||
"buffer-alloc": "^1.2.0",
|
||||
"end-of-stream": "^1.0.0",
|
||||
"fs-constants": "^1.0.0",
|
||||
"readable-stream": "^2.3.0",
|
||||
"to-buffer": "^1.1.1",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"to-buffer": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
|
||||
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
@@ -410,11 +445,11 @@
|
||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||
"requires": {
|
||||
"string-width": "1.0.2"
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"@types/node": "8.0.28"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-hid": "0.5.7",
|
||||
"node-hid": "0.7.3",
|
||||
"uhk-common": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export namespace Constants {
|
||||
export const VENDOR_ID = 0x1D50;
|
||||
export const PRODUCT_ID = 0x6122;
|
||||
export const BOOTLOADER_ID = 0x6120;
|
||||
export const MAX_PAYLOAD_SIZE = 64;
|
||||
}
|
||||
|
||||
@@ -22,7 +23,12 @@ export enum UsbCommand {
|
||||
GetDebugBuffer = 0x0b,
|
||||
GetAdcValue = 0x0c,
|
||||
SetLedPwmBrightness = 0x0d,
|
||||
GetModuleProperty = 0x0e
|
||||
GetModuleProperty = 0x0e,
|
||||
GetSlaveI2cErrors = 0x0f,
|
||||
SetI2cBaudRate = 0x10,
|
||||
SwitchKeymap = 0x11,
|
||||
GetVariable = 0x12,
|
||||
SetVariable = 0x13
|
||||
}
|
||||
|
||||
export enum EepromOperation {
|
||||
@@ -85,3 +91,10 @@ export enum KbootCommands {
|
||||
export enum ModulePropertyId {
|
||||
protocolVersions = 0
|
||||
}
|
||||
|
||||
export enum UsbVariables {
|
||||
testSwitches = 0,
|
||||
testUsbStack = 1,
|
||||
debounceTimePress = 2,
|
||||
debounceTimeRelease = 3
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export class UhkBlhost {
|
||||
|
||||
self.logService.debug(`[blhost] FINISHED: ${code}`);
|
||||
|
||||
if (code !== null && code !== 0) {
|
||||
if (code !== 0) {
|
||||
return reject(new Error(`blhost error code:${code}`));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Device, devices, HID } from 'node-hid';
|
||||
import { CommandLineArgs, LogService } from 'uhk-common';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { platform } from 'os';
|
||||
import { CommandLineArgs, DeviceConnectionState, isEqualArray, LogService, UdevRulesInfo } from 'uhk-common';
|
||||
|
||||
import {
|
||||
ConfigBufferId,
|
||||
@@ -10,9 +13,10 @@ import {
|
||||
KbootCommands,
|
||||
ModuleSlotToI2cAddress,
|
||||
ModuleSlotToId,
|
||||
UsbCommand
|
||||
UsbCommand,
|
||||
UsbVariables
|
||||
} from './constants';
|
||||
import { bufferToString, getTransferData, retry, snooze } from './util';
|
||||
import { bufferToString, getFileContentAsync, getTransferData, isUhkDevice, isUhkZeroInterface, retry, snooze } from './util';
|
||||
|
||||
export const BOOTLOADER_TIMEOUT_MS = 5000;
|
||||
|
||||
@@ -24,11 +28,14 @@ export class UhkHidDevice {
|
||||
* Internal variable that represent the USB UHK device
|
||||
* @private
|
||||
*/
|
||||
private _prevDevices = [];
|
||||
private _device: HID;
|
||||
private _hasPermission = false;
|
||||
private _udevRulesInfo = UdevRulesInfo.Unkonwn;
|
||||
|
||||
constructor(private logService: LogService,
|
||||
private options: CommandLineArgs) {
|
||||
private options: CommandLineArgs,
|
||||
private rootDir: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,12 +55,20 @@ export class UhkHidDevice {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.deviceConnected()) {
|
||||
this.logService.debug('[UhkHidDevice] Devices before check permission:');
|
||||
const devs = devices();
|
||||
this.logDevices(devs);
|
||||
|
||||
const dev = devs.find((x: Device) => isUhkZeroInterface(x) || x.productId === Constants.BOOTLOADER_ID);
|
||||
|
||||
if (!dev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._hasPermission = this.getDevice() !== null;
|
||||
this.close();
|
||||
const device = new HID(dev.path);
|
||||
device.close();
|
||||
|
||||
this._hasPermission = true;
|
||||
|
||||
return this._hasPermission;
|
||||
} catch (err) {
|
||||
@@ -64,18 +79,33 @@ export class UhkHidDevice {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
* Return with the USB device communication sate.
|
||||
* @returns {DeviceConnectionState}
|
||||
*/
|
||||
public deviceConnected(): boolean {
|
||||
const connected = devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID);
|
||||
public async getDeviceConnectionStateAsync(): Promise<DeviceConnectionState> {
|
||||
const devs = devices();
|
||||
const result: DeviceConnectionState = {
|
||||
bootloaderActive: false,
|
||||
connected: false,
|
||||
zeroInterfaceAvailable: false,
|
||||
hasPermission: this.hasPermission(),
|
||||
udevRulesInfo: await this.getUdevInfoAsync()
|
||||
};
|
||||
|
||||
if (!connected) {
|
||||
this._hasPermission = false;
|
||||
for (const dev of devs) {
|
||||
if (isUhkDevice(dev)) {
|
||||
result.connected = true;
|
||||
}
|
||||
|
||||
if (isUhkZeroInterface(dev)) {
|
||||
result.zeroInterfaceAvailable = true;
|
||||
} else if (dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.BOOTLOADER_ID) {
|
||||
result.bootloaderActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
return connected;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +125,7 @@ export class UhkHidDevice {
|
||||
device.read((err: any, receivedData: Array<number>) => {
|
||||
if (err) {
|
||||
this.logService.error('[UhkHidDevice] Transfer error: ', err);
|
||||
this.close();
|
||||
return reject(err);
|
||||
}
|
||||
const logString = bufferToString(receivedData);
|
||||
@@ -118,6 +149,11 @@ export class UhkHidDevice {
|
||||
await this.waitUntilKeyboardBusy();
|
||||
}
|
||||
|
||||
public async enableUsbStackTest(): Promise<void> {
|
||||
await this.write(new Buffer([UsbCommand.SetVariable, UsbVariables.testUsbStack, 1]));
|
||||
await this.waitUntilKeyboardBusy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the communication chanel with UHK Device
|
||||
*/
|
||||
@@ -142,6 +178,10 @@ export class UhkHidDevice {
|
||||
}
|
||||
}
|
||||
|
||||
public resetDeviceCache(): void {
|
||||
this._prevDevices = [];
|
||||
}
|
||||
|
||||
async reenumerate(enumerationMode: EnumerationModes): Promise<void> {
|
||||
const reenumMode = EnumerationModes[enumerationMode].toString();
|
||||
this.logService.debug(`[UhkHidDevice] Start reenumeration, mode: ${reenumMode}`);
|
||||
@@ -197,7 +237,7 @@ export class UhkHidDevice {
|
||||
|
||||
async sendKbootCommandToModule(module: ModuleSlotToI2cAddress, command: KbootCommands, maxTry = 1): Promise<any> {
|
||||
let transfer;
|
||||
const moduleName = kbootKommandName(module);
|
||||
const moduleName = kbootCommandName(module);
|
||||
this.logService.debug(`[UhkHidDevice] USB[T]: Send KbootCommand ${moduleName} ${KbootCommands[command].toString()}`);
|
||||
if (command === KbootCommands.idle) {
|
||||
transfer = new Buffer([UsbCommand.SendKbootCommandToModule, command]);
|
||||
@@ -233,30 +273,73 @@ export class UhkHidDevice {
|
||||
private connectToDevice(): HID {
|
||||
try {
|
||||
const devs = devices();
|
||||
this.logService.debug('[UhkHidDevice] Available devices:', devs);
|
||||
let compareDevices = devs as any;
|
||||
|
||||
const dev = devs.find((x: Device) =>
|
||||
x.vendorId === Constants.VENDOR_ID &&
|
||||
x.productId === Constants.PRODUCT_ID &&
|
||||
((x.usagePage === 128 && x.usage === 129) || x.interface === 0));
|
||||
if (platform() === 'linux') {
|
||||
compareDevices = devs.map(x => ({
|
||||
productId: x.productId,
|
||||
vendorId: x.vendorId,
|
||||
interface: x.interface
|
||||
}));
|
||||
}
|
||||
|
||||
if (!isEqualArray(this._prevDevices, compareDevices)) {
|
||||
this.logService.debug('[UhkHidDevice] Available devices:');
|
||||
this.logDevices(devs);
|
||||
this._prevDevices = compareDevices;
|
||||
} else {
|
||||
this.logService.debug('[UhkHidDevice] Available devices unchanged');
|
||||
}
|
||||
|
||||
const dev = devs.find(isUhkZeroInterface);
|
||||
|
||||
if (!dev) {
|
||||
this.logService.debug('[UhkHidDevice] UHK Device not found:');
|
||||
return null;
|
||||
}
|
||||
const device = new HID(dev.path);
|
||||
this.logService.debug('[UhkHidDevice] Used device:', dev);
|
||||
this.logService.debug('[UhkHidDevice] Used device:', JSON.stringify(dev));
|
||||
return device;
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
this.logService.error('[UhkHidDevice] Can not create device:', err);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private logDevices(devs: Array<Device>): void {
|
||||
for (const logDevice of devs) {
|
||||
this.logService.debug(JSON.stringify(logDevice));
|
||||
}
|
||||
}
|
||||
|
||||
private async getUdevInfoAsync(): Promise<UdevRulesInfo> {
|
||||
if (this._udevRulesInfo === UdevRulesInfo.Ok) {
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32' || process.platform === 'darwin') {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
if (!(await pathExists('/etc/udev/rules.d/50-uhk60.rules'))) {
|
||||
return UdevRulesInfo.NeedToSetup;
|
||||
}
|
||||
|
||||
const expectedUdevSettings = await getFileContentAsync(path.join(this.rootDir, 'rules/50-uhk60.rules'));
|
||||
const currentUdevSettings = await getFileContentAsync('/etc/udev/rules.d/50-uhk60.rules');
|
||||
|
||||
if (isEqualArray(expectedUdevSettings, currentUdevSettings)) {
|
||||
this._udevRulesInfo = UdevRulesInfo.Ok;
|
||||
return UdevRulesInfo.Ok;
|
||||
}
|
||||
|
||||
return UdevRulesInfo.Different;
|
||||
}
|
||||
}
|
||||
|
||||
function kbootKommandName(module: ModuleSlotToI2cAddress): string {
|
||||
function kbootCommandName(module: ModuleSlotToI2cAddress): string {
|
||||
switch (module) {
|
||||
case ModuleSlotToI2cAddress.leftHalf:
|
||||
return 'leftHalf';
|
||||
@@ -267,7 +350,7 @@ function kbootKommandName(module: ModuleSlotToI2cAddress): string {
|
||||
case ModuleSlotToI2cAddress.rightAddon:
|
||||
return 'rightAddon';
|
||||
|
||||
default :
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from './constants';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { UhkBlhost } from './uhk-blhost';
|
||||
import { UhkHidDevice } from './uhk-hid-device';
|
||||
import { snooze } from './util';
|
||||
@@ -29,6 +30,7 @@ export class UhkOperations {
|
||||
}
|
||||
|
||||
public async updateRightFirmware(firmwarePath = this.getFirmwarePath()) {
|
||||
this.logService.debug(`[UhkOperations] Operating system: ${os.type()} ${os.release()} ${os.arch()}`);
|
||||
this.logService.debug('[UhkOperations] Start flashing right firmware');
|
||||
const prefix = [`--usb 0x1d50,0x${EnumerationNameToProductId.bootloader.toString(16)}`];
|
||||
|
||||
@@ -57,8 +59,9 @@ export class UhkOperations {
|
||||
|
||||
const leftModuleBricked = await this.waitForKbootIdle();
|
||||
if (!leftModuleBricked) {
|
||||
this.logService.error('[UhkOperations] Couldn\'t connect to the left keyboard half.');
|
||||
return;
|
||||
const msg = '[UhkOperations] Couldn\'t connect to the left keyboard half.';
|
||||
this.logService.error(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
await this.device.reenumerate(EnumerationModes.Buspal);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Constants, UsbCommand } from './constants';
|
||||
import { Device } from 'node-hid';
|
||||
import { readFile } from 'fs-extra';
|
||||
import { EOL } from 'os';
|
||||
import { LogService } from 'uhk-common';
|
||||
|
||||
import { Constants, UsbCommand } from './constants';
|
||||
|
||||
export const snooze = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
@@ -75,7 +79,6 @@ export async function retry(command: Function, maxTry = 3, logService?: LogServi
|
||||
try {
|
||||
// logService.debug(`[retry] try to run FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
await command();
|
||||
await snooze(100);
|
||||
// logService.debug(`[retry] success FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
return;
|
||||
} catch (err) {
|
||||
@@ -91,7 +94,31 @@ export async function retry(command: Function, maxTry = 3, logService?: LogServi
|
||||
if (logService) {
|
||||
logService.info(`[retry] failed, but try run FUNCTION:\n ${command}, \n retry: ${retryCount}`);
|
||||
}
|
||||
await snooze(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const isUhkZeroInterface = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID &&
|
||||
// hidapi can not read the interface number on Mac, so check the usage page and usage
|
||||
((dev.usagePage === 128 && dev.usage === 129) || // Old firmware
|
||||
(dev.usagePage === (0xFF00 | 0x00) && dev.usage === 0x01) || // New firmware
|
||||
dev.interface === 0);
|
||||
};
|
||||
|
||||
export const isUhkDevice = (dev: Device): boolean => {
|
||||
return dev.vendorId === Constants.VENDOR_ID &&
|
||||
(dev.productId === Constants.PRODUCT_ID || dev.productId === Constants.BOOTLOADER_ID);
|
||||
};
|
||||
|
||||
export const getFileContentAsync = async (filePath: string): Promise<Array<string>> => {
|
||||
const fileContent = await readFile(filePath, {encoding: 'utf-8'});
|
||||
|
||||
return fileContent
|
||||
.split(EOL)
|
||||
.map(x => x.trim())
|
||||
.filter(x => !x.startsWith('#') && x.length > 0);
|
||||
};
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
],
|
||||
"scripts": [
|
||||
"../node_modules/bootstrap/dist/js/bootstrap.js",
|
||||
"../node_modules/select2/dist/js/select2.full.js",
|
||||
"../node_modules/nouislider/distribute/nouislider.js"
|
||||
],
|
||||
"environmentSource": "environments/environment.ts",
|
||||
|
||||
1
packages/uhk-web/.gitignore
vendored
Normal file
1
packages/uhk-web/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
out-tsc/
|
||||
@@ -7,8 +7,8 @@ describe('web App', () => {
|
||||
page = new WebPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
it('should display default device name', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toEqual('Welcome to app!');
|
||||
expect(page.getDeviceName()).toEqual('My UHK');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,8 @@ export class WebPage {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root h1')).getText();
|
||||
getDeviceName() {
|
||||
return element(by.css('body > main-app > side-menu > ul > li:nth-child(1) > div > auto-grow-input > input'))
|
||||
.getAttribute('value');
|
||||
}
|
||||
}
|
||||
|
||||
14681
packages/uhk-web/package-lock.json
generated
14681
packages/uhk-web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,22 +10,24 @@
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"build:renderer": "webpack --config webpack.config.js",
|
||||
"server:renderer": "webpack --config webpack.config.js --watch"
|
||||
"server:renderer": "webpack --config webpack.config.js --watch",
|
||||
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@angular/animations": "4.4.5",
|
||||
"@angular/cli": "1.4.7",
|
||||
"@angular/common": "4.4.5",
|
||||
"@angular/compiler": "4.4.5",
|
||||
"@angular/compiler-cli": "4.4.5",
|
||||
"@angular/core": "4.4.5",
|
||||
"@angular/forms": "4.4.5",
|
||||
"@angular/http": "4.4.5",
|
||||
"@angular/language-service": "4.4.5",
|
||||
"@angular/platform-browser": "4.4.5",
|
||||
"@angular/platform-browser-dynamic": "4.4.5",
|
||||
"@angular/router": "4.4.5",
|
||||
"@angular/animations": "5.2.9",
|
||||
"@angular/cli": "1.7.4",
|
||||
"@angular/common": "5.2.9",
|
||||
"@angular/compiler": "5.2.9",
|
||||
"@angular/compiler-cli": "5.2.9",
|
||||
"@angular/core": "5.2.9",
|
||||
"@angular-devkit/build-optimizer": "0.3.2",
|
||||
"@angular/forms": "5.2.9",
|
||||
"@angular/http": "5.2.9",
|
||||
"@angular/language-service": "5.2.9",
|
||||
"@angular/platform-browser": "5.2.9",
|
||||
"@angular/platform-browser-dynamic": "5.2.9",
|
||||
"@angular/router": "5.2.9",
|
||||
"@ngrx/effects": "4.0.5",
|
||||
"@ngrx/router-store": "4.0.4",
|
||||
"@ngrx/store": "4.0.3",
|
||||
@@ -34,70 +36,50 @@
|
||||
"@types/electron-devtools-installer": "2.0.2",
|
||||
"@types/electron-settings": "3.0.0",
|
||||
"@types/file-saver": "0.0.1",
|
||||
"@types/jasmine": "2.5.53",
|
||||
"@types/jasminewd2": "2.0.2",
|
||||
"@types/jasmine": "2.8.8",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/jquery": "3.2.9",
|
||||
"@types/lodash-es": "4.17.0",
|
||||
"@types/usb": "1.1.3",
|
||||
"angular-confirmation-popover": "3.2.0",
|
||||
"angular-notifier": "2.0.0",
|
||||
"autoprefixer": "6.5.3",
|
||||
"autoprefixer": "^7.2.3",
|
||||
"bootstrap": "3.3.7",
|
||||
"buffer": "5.0.6",
|
||||
"circular-dependency-plugin": "3.0.0",
|
||||
"circular-dependency-plugin": "^4.2.1",
|
||||
"codelyzer": "3.0.1",
|
||||
"copy-webpack-plugin": "4.0.1",
|
||||
"css-loader": "0.28.1",
|
||||
"cssnano": "3.10.0",
|
||||
"dragula": "3.7.2",
|
||||
"exports-loader": "0.6.3",
|
||||
"file-loader": "0.10.0",
|
||||
"file-saver": "1.3.3",
|
||||
"font-awesome": "4.7.0",
|
||||
"html-webpack-plugin": "2.29.0",
|
||||
"istanbul-instrumenter-loader": "2.0.0",
|
||||
"jasmine-core": "2.6.2",
|
||||
"jasmine-spec-reporter": "4.1.0",
|
||||
"html-webpack-plugin": "^2.29.0",
|
||||
"jasmine-core": "3.2.1",
|
||||
"jasmine-spec-reporter": "4.2.1",
|
||||
"jquery": "3.2.1",
|
||||
"jsonfile": "3.0.1",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "2.1.1",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "1.2.1",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"less-loader": "4.0.5",
|
||||
"lodash-es": "4.17.4",
|
||||
"karma": "3.0.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "2.0.1",
|
||||
"karma-jasmine": "1.1.2",
|
||||
"karma-jasmine-html-reporter": "1.3.1",
|
||||
"ng2-dragula": "1.5.0",
|
||||
"ng2-nouislider": "^1.7.6",
|
||||
"ng2-select2": "1.0.0-beta.10",
|
||||
"ngx-clipboard": "8.0.0",
|
||||
"ng2-nouislider": "^1.7.7",
|
||||
"ngx-clipboard": "10.0.0",
|
||||
"@ert78gb/ngx-select-ex": "3.7.2",
|
||||
"ngrx-store-freeze": "0.1.9",
|
||||
"nouislider": "^10.1.0",
|
||||
"postcss-loader": "1.3.3",
|
||||
"postcss-url": "5.1.2",
|
||||
"protractor": "5.1.2",
|
||||
"raw-loader": "0.5.1",
|
||||
"nouislider": "^11.1.0",
|
||||
"postcss-url": "^7.1.2",
|
||||
"protractor": "5.4.0",
|
||||
"reselect": "3.0.1",
|
||||
"sass-loader": "6.0.3",
|
||||
"script-loader": "0.7.0",
|
||||
"select2": "4.0.3",
|
||||
"source-map-loader": "0.2.0",
|
||||
"style-loader": "0.13.1",
|
||||
"stylus-loader": "3.0.1",
|
||||
"sudo-prompt": "7.1.1",
|
||||
"ts-loader": "2.3.1",
|
||||
"ts-node": "3.0.4",
|
||||
"rxjs": "5.5.8",
|
||||
"typescript": "2.6.2",
|
||||
"semver": "5.6.0",
|
||||
"uhk-common": "1.0.0",
|
||||
"url-loader": "0.5.7",
|
||||
"webpack": "3.4.1",
|
||||
"webpack-dev-server": "2.5.1",
|
||||
"webpack-svgstore-plugin": "4.0.1",
|
||||
"xml-loader": "1.2.1",
|
||||
"zone.js": "0.8.14"
|
||||
"zone.js": "0.8.26",
|
||||
"@angular-devkit/core": "0.3.2",
|
||||
"@ngtools/webpack": "1.10.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"classlist.js": "1.1.20150312",
|
||||
"file-saver": "1.3.3"
|
||||
"file-saver": "1.3.3",
|
||||
"spacing-bootstrap-3": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ exports.config = {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
baseUrl: 'http://localhost:8080/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<notifier-container></notifier-container>
|
||||
<progress-button class="save-to-keyboard-button"
|
||||
*ngIf="(saveToKeyboardState$ | async).showButton"
|
||||
*ngIf="saveToKeyboardState.showButton"
|
||||
[@showSaveToKeyboardButton]
|
||||
[state]="saveToKeyboardState$ | async"
|
||||
[state]="saveToKeyboardState"
|
||||
(clicked)="clickedOnProgressButton($event)"></progress-button>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, HostListener, ViewEncapsulation, OnDestroy } from '@angular/core';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
|
||||
import 'rxjs/add/operator/last';
|
||||
|
||||
import { DoNotUpdateAppAction, UpdateAppAction } from './store/actions/app-update.action';
|
||||
import { EnableUsbStackTestAction } from './store/actions/device';
|
||||
import {
|
||||
AppState,
|
||||
getShowAppUpdateAvailable,
|
||||
deviceConfigurationLoaded,
|
||||
runningInElectron,
|
||||
saveToKeyboardState
|
||||
saveToKeyboardState,
|
||||
keypressCapturing
|
||||
} from './store';
|
||||
import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||
|
||||
@@ -34,17 +37,50 @@ import { ProgressButtonState } from './store/reducers/progress-button-state';
|
||||
])
|
||||
]
|
||||
})
|
||||
export class MainAppComponent {
|
||||
export class MainAppComponent implements OnDestroy {
|
||||
showUpdateAvailable$: Observable<boolean>;
|
||||
deviceConfigurationLoaded$: Observable<boolean>;
|
||||
runningInElectron$: Observable<boolean>;
|
||||
saveToKeyboardState$: Observable<ProgressButtonState>;
|
||||
saveToKeyboardState: ProgressButtonState;
|
||||
|
||||
private keypressCapturing: boolean;
|
||||
private saveToKeyboardStateSubscription: Subscription;
|
||||
private keypressCapturingSubscription: Subscription;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.showUpdateAvailable$ = store.select(getShowAppUpdateAvailable);
|
||||
this.deviceConfigurationLoaded$ = store.select(deviceConfigurationLoaded);
|
||||
this.runningInElectron$ = store.select(runningInElectron);
|
||||
this.saveToKeyboardState$ = store.select(saveToKeyboardState);
|
||||
this.saveToKeyboardStateSubscription = store.select(saveToKeyboardState)
|
||||
.subscribe(data => this.saveToKeyboardState = data);
|
||||
this.keypressCapturingSubscription = store.select(keypressCapturing)
|
||||
.subscribe(data => this.keypressCapturing = data);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.saveToKeyboardStateSubscription.unsubscribe();
|
||||
this.keypressCapturingSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
onKeyDown(event: KeyboardEvent) {
|
||||
if (this.saveToKeyboardState.showButton &&
|
||||
event.ctrlKey &&
|
||||
event.key === 's' &&
|
||||
!event.defaultPrevented &&
|
||||
!this.keypressCapturing) {
|
||||
this.clickedOnProgressButton(this.saveToKeyboardState.action);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (event.shiftKey &&
|
||||
event.ctrlKey &&
|
||||
event.metaKey &&
|
||||
event.key === '|' &&
|
||||
!event.defaultPrevented) {
|
||||
this.enableUsbStackTest();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
updateApp() {
|
||||
@@ -58,4 +94,8 @@ export class MainAppComponent {
|
||||
clickedOnProgressButton(action: Action) {
|
||||
return this.store.dispatch(action);
|
||||
}
|
||||
|
||||
enableUsbStackTest() {
|
||||
this.store.dispatch(new EnableUsbStackTestAction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,19 @@ import { deviceRoutes } from './components/device';
|
||||
import { addOnRoutes } from './components/add-on';
|
||||
import { keymapRoutes } from './components/keymap';
|
||||
import { macroRoutes } from './components/macro';
|
||||
import { PrivilegeCheckerComponent } from './components/privilege-checker/privilege-checker.component';
|
||||
import { MissingDeviceComponent } from './components/missing-device/missing-device.component';
|
||||
import { PrivilegeCheckerComponent } from './components/privilege-checker';
|
||||
import { MissingDeviceComponent } from './components/missing-device';
|
||||
import { UhkDeviceDisconnectedGuard } from './services/uhk-device-disconnected.guard';
|
||||
import { UhkDeviceConnectedGuard } from './services/uhk-device-connected.guard';
|
||||
import { UhkDeviceUninitializedGuard } from './services/uhk-device-uninitialized.guard';
|
||||
import { UhkDeviceInitializedGuard } from './services/uhk-device-initialized.guard';
|
||||
import { MainPage } from './pages/main-page/main.page';
|
||||
import { agentRoutes } from './components/agent/agent.routes';
|
||||
import { agentRoutes } from './components/agent';
|
||||
import { LoadingDevicePageComponent } from './pages/loading-page/loading-device.page';
|
||||
import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
||||
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
||||
import { RecoveryModeComponent } from './components/device';
|
||||
import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard';
|
||||
|
||||
const appRoutes: Routes = [
|
||||
{
|
||||
@@ -33,6 +35,11 @@ const appRoutes: Routes = [
|
||||
component: LoadingDevicePageComponent,
|
||||
canActivate: [UhkDeviceLoadedGuard]
|
||||
},
|
||||
{
|
||||
path: 'recovery-device',
|
||||
component: RecoveryModeComponent,
|
||||
canActivate: [UhkDeviceBootloaderNotActiveGuard]
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: MainPage,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="row">
|
||||
<h1 class="col-xs-12 pane-title">
|
||||
<i class="uhk-icon uhk-icon-agent-icon"></i>
|
||||
<i class="uhk-icon uhk-icon-pure-agent-icon"></i>
|
||||
<span>About</span>
|
||||
</h1>
|
||||
<div class="col-xs-12">
|
||||
<div class="agent-version">Agent version: <span class="text-bold">{{version}}</span></div>
|
||||
<div><a class="link-github" (click)="openAgentGitHubPage($event)">Agent on GitHub</a></div>
|
||||
<div class="agent-version">Agent version: <span class="text-bold">{{ version }}</span></div>
|
||||
<div><a class="link-github" [href]="agentGithubUrl" externalUrl>Agent on GitHub</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Constants } from 'uhk-common';
|
||||
|
||||
import { AppState } from '../../../store';
|
||||
import { getVersions } from '../../../util';
|
||||
import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
|
||||
|
||||
@Component({
|
||||
selector: 'about-page',
|
||||
@@ -16,12 +13,5 @@ import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
|
||||
})
|
||||
export class AboutComponent {
|
||||
version: string = getVersions().version;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
}
|
||||
|
||||
openAgentGitHubPage(event) {
|
||||
event.preventDefault();
|
||||
this.store.dispatch(new OpenUrlInNewWindowAction(Constants.AGENT_GITHUB_URL));
|
||||
}
|
||||
agentGithubUrl = Constants.AGENT_GITHUB_URL;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@ import { Routes } from '@angular/router';
|
||||
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { AboutComponent } from './about/about.component';
|
||||
import { HelpPageComponent } from './help-page/help-page.component';
|
||||
|
||||
export const agentRoutes: Routes = [
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
},
|
||||
{
|
||||
path: 'help',
|
||||
component: HelpPageComponent
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<div class="row">
|
||||
<h1 class="col-xs-12 pane-title">
|
||||
<i class="fa fa-question-circle"></i>
|
||||
<span class="macro__name pane-title__name">Help</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
Frequently asked questions
|
||||
<ul>
|
||||
<li><a href="https://ultimatehackingkeyboard.com/blog/2018/06/23/how-can-i-type-accented-characters-with-my-uhk" externalUrl>How can I type accented characters with my UHK?</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
Keyboard shortcuts
|
||||
<ul>
|
||||
<li><kbd>CTRL</kbd> + <kbd>Enter</kbd> = Remap key</li>
|
||||
<li><kbd>CTRL</kbd> + <kbd>S</kbd> = Save to keyboard</li>
|
||||
<li>Right click on a key = Capture key</li>
|
||||
<li>Hold Shift while clicking on a key = Remap on all keymaps</li>
|
||||
<li>Hold Alt while clicking on a key = Remap on all layers</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'help-page',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './help-page.component.html',
|
||||
styleUrls: ['./help-page.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class HelpPageComponent {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<input #inputControl
|
||||
cancelable
|
||||
[class]="css"
|
||||
type="text"
|
||||
[disabled]="disabled"
|
||||
[(ngModel)]="model"
|
||||
(blur)="blur()"
|
||||
(focus)="focus()"
|
||||
(keyup.enter)="keyEnter($event)"
|
||||
(keyup)="calculateTextWidth($event.target.value)">
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
forwardRef, HostListener,
|
||||
Input,
|
||||
Renderer2,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import * as util from '../../util';
|
||||
|
||||
const noop = (_: any) => {
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'auto-grow-input',
|
||||
templateUrl: './auto-grow-input.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => AutoGrowInputComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AutoGrowInputComponent implements ControlValueAccessor {
|
||||
@Input() maxParentWidthPercent = 1;
|
||||
@Input() css: string;
|
||||
|
||||
@ViewChild('inputControl') inputControl: ElementRef;
|
||||
|
||||
disabled: boolean;
|
||||
|
||||
get model(): string {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
set model(value: string) {
|
||||
if (this._model === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._model = value;
|
||||
}
|
||||
|
||||
private _model: string;
|
||||
private _originalModel: string;
|
||||
private _onChanged = noop;
|
||||
private _onTouched = noop;
|
||||
|
||||
constructor(private _cdRef: ChangeDetectorRef,
|
||||
private _renderer: Renderer2) {
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this._onChanged = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this._onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
if (this.disabled === isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.disabled = isDisabled;
|
||||
this._cdRef.markForCheck();
|
||||
}
|
||||
|
||||
@HostListener('window:resize')
|
||||
windowResize(): void {
|
||||
this.calculateTextWidth(this._model);
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
console.log('write', new Date());
|
||||
if (this.model === obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._model = obj;
|
||||
this._originalModel = obj;
|
||||
this.calculateTextWidth(this._model);
|
||||
this._cdRef.markForCheck();
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this._onTouched(this);
|
||||
}
|
||||
|
||||
blur(): void {
|
||||
if (!util.isValidName(this._model) || this._model.trim() === this._originalModel) {
|
||||
this._model = this._originalModel;
|
||||
this.calculateTextWidth(this._model);
|
||||
this._cdRef.markForCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
this._originalModel = this._model;
|
||||
this._onChanged(this._model);
|
||||
}
|
||||
|
||||
keyEnter(event): void {
|
||||
event.target.blur();
|
||||
}
|
||||
|
||||
calculateTextWidth(text: string): void {
|
||||
const htmlInput = this.inputControl.nativeElement as HTMLInputElement;
|
||||
const maxWidth = htmlInput.parentElement.parentElement.offsetWidth * this.maxParentWidthPercent;
|
||||
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
|
||||
this._renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './auto-grow-input.component';
|
||||
@@ -9,16 +9,14 @@
|
||||
|
||||
<ul class="list-unstyled btn-list">
|
||||
<li>
|
||||
<button class="btn btn-default"
|
||||
<button class="btn btn-primary"
|
||||
(click)="exportUserConfiguration($event)">Export device configuration
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<label class="btn btn-default btn-file">
|
||||
Import device configuration
|
||||
<input type="file"
|
||||
(change)="changeFile($event)">
|
||||
</label>
|
||||
<file-upload (fileChanged)="changeFile($event)"
|
||||
label="Import device configuration">
|
||||
</file-upload>
|
||||
</li>
|
||||
<li>
|
||||
<button class="btn btn-danger"
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SaveUserConfigInBinaryFileAction,
|
||||
SaveUserConfigInJsonFileAction
|
||||
} from '../../../store/actions/user-config';
|
||||
import { UploadFileData } from '../../../models/upload-file-data';
|
||||
|
||||
@Component({
|
||||
selector: 'device-settings',
|
||||
@@ -42,16 +43,7 @@ export class DeviceConfigurationComponent {
|
||||
}
|
||||
}
|
||||
|
||||
changeFile(event): void {
|
||||
const files = event.srcElement.files;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = function () {
|
||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
||||
this.store.dispatch(new LoadUserConfigurationFromFileAction({
|
||||
filename: event.srcElement.value,
|
||||
data: Array.from(arrayBuffer)
|
||||
}));
|
||||
}.bind(this);
|
||||
fileReader.readAsArrayBuffer(files[0]);
|
||||
changeFile(data: UploadFileData): void {
|
||||
this.store.dispatch(new LoadUserConfigurationFromFileAction(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { DeviceFirmwareComponent } from './firmware/device-firmware.component';
|
||||
import { MouseSpeedComponent } from './mouse-speed/mouse-speed.component';
|
||||
import { LEDBrightnessComponent } from './led-brightness/led-brightness.component';
|
||||
import { RestoreConfigurationComponent } from './restore-configuration/restore-configuration.component';
|
||||
import { RecoveryModeComponent } from './recovery-mode/recovery-mode.component';
|
||||
|
||||
export const deviceRoutes: Routes = [
|
||||
{
|
||||
@@ -34,6 +35,10 @@ export const deviceRoutes: Routes = [
|
||||
{
|
||||
path: 'restore-user-configuration',
|
||||
component: RestoreConfigurationComponent
|
||||
},
|
||||
{
|
||||
path: 'recovery-mode',
|
||||
component: RecoveryModeComponent
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,42 +12,40 @@
|
||||
Firmware {{ hardwareModules.rightModuleInfo.firmwareVersion }} is running on the right keyboard half.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<i>
|
||||
Please note that the firmware update process may sometimes fail. If if fails then
|
||||
simply retry until it succeeds. If the left half becomes unresponsive after a failed
|
||||
update then retry and follow the instructions displayed during the update to fix it.
|
||||
We'll make the firmware update process more robust.
|
||||
</i>
|
||||
</p>
|
||||
<p *ngIf="runningOnNotSupportedWindows$ | async">Firmware update doesn't work on Windows 7, Windows Vista,
|
||||
and Windows XP. Use Windows 10, Windows 8, Linux, or OSX instead.</p>
|
||||
|
||||
<p>
|
||||
<p *ngIf="firmwareUpgradeAllowed$ | async">
|
||||
<button class="btn btn-primary"
|
||||
[disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||
(click)="onUpdateFirmware()">
|
||||
Flash firmware {{ (getAgentVersionInfo$ | async).firmwareVersion }} (bundled with Agent)
|
||||
</button>
|
||||
<label class="btn btn-primary btn-file"
|
||||
[class.disabled]="flashFirmwareButtonDisbabled$ | async">
|
||||
Choose firmware file and flash it
|
||||
<input id="firmware-file-select"
|
||||
type="file"
|
||||
accept=".tar.bz2"
|
||||
[disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||
(change)="changeFile($event)">
|
||||
</label>
|
||||
<file-upload [disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||
(fileChanged)="changeFile($event)"
|
||||
accept=".tar.bz2"
|
||||
label="Choose firmware file and flash it"></file-upload>
|
||||
</p>
|
||||
|
||||
<div *ngIf="firmwareUpgradeFailed$ | async"
|
||||
class="alert alert-danger"
|
||||
role="alert">
|
||||
<p>Firmware update failed. Disconnect every USB device from your computer (including USB hubs, KVM switches, USB dongles, and everything else), then connect only your UHK and retry.</p>
|
||||
|
||||
<p>If you've tried the above and the update still keeps failing, please <a class="link-github" (click)="openFirmwareGitHubIssuePage($event)">create a GitHub issue</a>, and attach the update log.</p>
|
||||
</div>
|
||||
|
||||
<div *ngIf="firmwareUpgradeSuccess$ | async"
|
||||
class="alert alert-success"
|
||||
role="alert">
|
||||
<p>Firmware update succeeded.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow" #scrollMe>
|
||||
<div class="flex-grow" *ngIf="firmwareUpgradeAllowed$ | async">
|
||||
<xterm [logs]="xtermLog$ | async"></xterm>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button type="button"
|
||||
class="btn btn-primary ok-button"
|
||||
[disabled]="firmwareOkButtonDisabled$ | async"
|
||||
(click)="onOkButtonClick()">OK
|
||||
</button>
|
||||
<div class="flex-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,24 +6,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
background-color: black;
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.ok-button {
|
||||
min-width: 100px;
|
||||
.link-github {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { HardwareModules, VersionInformation } from 'uhk-common';
|
||||
import { Constants, HardwareModules, VersionInformation } from 'uhk-common';
|
||||
|
||||
import { OpenUrlInNewWindowAction } from '../../../store/actions/app';
|
||||
import {
|
||||
AppState,
|
||||
firmwareOkButtonDisabled,
|
||||
firmwareUpgradeAllowed,
|
||||
firmwareUpgradeFailed,
|
||||
firmwareUpgradeSuccess,
|
||||
flashFirmwareButtonDisbabled,
|
||||
getAgentVersionInfo,
|
||||
getHardwareModules,
|
||||
runningOnNotSupportedWindows,
|
||||
xtermLog
|
||||
} from '../../../store';
|
||||
import { UpdateFirmwareAction, UpdateFirmwareOkButtonAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
|
||||
import { UpdateFirmwareAction, UpdateFirmwareWithAction } from '../../../store/actions/device';
|
||||
import { XtermLog } from '../../../models/xterm-log';
|
||||
import { UploadFileData } from '../../../models/upload-file-data';
|
||||
|
||||
@Component({
|
||||
selector: 'device-firmware',
|
||||
@@ -26,33 +31,28 @@ import { XtermLog } from '../../../models/xterm-log';
|
||||
export class DeviceFirmwareComponent implements OnDestroy {
|
||||
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
||||
xtermLog$: Observable<Array<XtermLog>>;
|
||||
xtermLogSubscription: Subscription;
|
||||
getAgentVersionInfo$: Observable<VersionInformation>;
|
||||
firmwareOkButtonDisabled$: Observable<boolean>;
|
||||
hardwareModulesSubscription: Subscription;
|
||||
hardwareModules: HardwareModules;
|
||||
|
||||
@ViewChild('scrollMe') divElement: ElementRef;
|
||||
runningOnNotSupportedWindows$: Observable<boolean>;
|
||||
firmwareUpgradeAllowed$: Observable<boolean>;
|
||||
firmwareUpgradeFailed$: Observable<boolean>;
|
||||
firmwareUpgradeSuccess$: Observable<boolean>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
this.flashFirmwareButtonDisbabled$ = store.select(flashFirmwareButtonDisbabled);
|
||||
this.xtermLog$ = store.select(xtermLog);
|
||||
this.xtermLogSubscription = this.xtermLog$.subscribe(() => {
|
||||
if (this.divElement && this.divElement.nativeElement) {
|
||||
setTimeout(() => {
|
||||
this.divElement.nativeElement.scrollTop = this.divElement.nativeElement.scrollHeight;
|
||||
});
|
||||
}
|
||||
});
|
||||
this.getAgentVersionInfo$ = store.select(getAgentVersionInfo);
|
||||
this.firmwareOkButtonDisabled$ = store.select(firmwareOkButtonDisabled);
|
||||
this.hardwareModulesSubscription = store.select(getHardwareModules).subscribe(data => {
|
||||
this.hardwareModules = data;
|
||||
});
|
||||
this.runningOnNotSupportedWindows$ = store.select(runningOnNotSupportedWindows);
|
||||
this.firmwareUpgradeAllowed$ = store.select(firmwareUpgradeAllowed);
|
||||
this.firmwareUpgradeFailed$ = store.select(firmwareUpgradeFailed);
|
||||
this.firmwareUpgradeSuccess$ = store.select(firmwareUpgradeSuccess);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.xtermLogSubscription.unsubscribe();
|
||||
this.hardwareModulesSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
@@ -60,22 +60,12 @@ export class DeviceFirmwareComponent implements OnDestroy {
|
||||
this.store.dispatch(new UpdateFirmwareAction());
|
||||
}
|
||||
|
||||
onOkButtonClick(): void {
|
||||
this.store.dispatch(new UpdateFirmwareOkButtonAction());
|
||||
changeFile(data: UploadFileData): void {
|
||||
this.store.dispatch(new UpdateFirmwareWithAction(data.data));
|
||||
}
|
||||
|
||||
changeFile(event): void {
|
||||
const files = event.srcElement.files;
|
||||
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = function () {
|
||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
||||
this.store.dispatch(new UpdateFirmwareWithAction(Array.prototype.slice.call(arrayBuffer)));
|
||||
}.bind(this);
|
||||
fileReader.readAsArrayBuffer(files[0]);
|
||||
openFirmwareGitHubIssuePage(event): void {
|
||||
event.preventDefault();
|
||||
this.store.dispatch(new OpenUrlInNewWindowAction(Constants.FIRMWARE_GITHUB_ISSUE_URL));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ export * from './firmware/device-firmware.component';
|
||||
export * from './mouse-speed/mouse-speed.component';
|
||||
export * from './led-brightness/led-brightness.component';
|
||||
export * from './restore-configuration/restore-configuration.component';
|
||||
export * from './recovery-mode/recovery-mode.component';
|
||||
export * from './device.routes';
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="row led-setting">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<slider-wrapper
|
||||
label="Key backlight brightness"
|
||||
label="Key backlight brightness <span class='text-muted pl-1'>Please note that current UHK versions are not backlit.</span>"
|
||||
[min]="0"
|
||||
[max]="255"
|
||||
[step]="1"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="full-height">
|
||||
<div class="flex-container">
|
||||
<div>
|
||||
|
||||
<h1>
|
||||
<i class="fa fa-wrench"></i>
|
||||
<span>Fix device</span>
|
||||
</h1>
|
||||
<p>
|
||||
Your device seems to be broken. No worries, Agent can fix it.
|
||||
</p>
|
||||
<p>
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
[disabled]="flashFirmwareButtonDisbabled$ | async"
|
||||
(click)="onRecoveryDevice()">Fix device
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-grow">
|
||||
<xterm [logs]="xtermLog$ | async"></xterm>
|
||||
</div>
|
||||
<div class="flex-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
:host {
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
p {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { XtermLog } from '../../../models/xterm-log';
|
||||
import { AppState, flashFirmwareButtonDisbabled, xtermLog } from '../../../store';
|
||||
import { RecoveryDeviceAction } from '../../../store/actions/device';
|
||||
|
||||
@Component({
|
||||
selector: 'device-recovery-mode',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './recovery-mode.component.html',
|
||||
styleUrls: ['./recovery-mode.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class RecoveryModeComponent implements OnInit {
|
||||
flashFirmwareButtonDisbabled$: Observable<boolean>;
|
||||
|
||||
xtermLog$: Observable<Array<XtermLog>>;
|
||||
|
||||
constructor(private store: Store<AppState>) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.flashFirmwareButtonDisbabled$ = this.store.select(flashFirmwareButtonDisbabled);
|
||||
this.xtermLog$ = this.store.select(xtermLog);
|
||||
}
|
||||
|
||||
onRecoveryDevice(): void {
|
||||
this.store.dispatch(new RecoveryDeviceAction());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<label class="btn btn-primary btn-file"
|
||||
[class.disabled]="disabled">
|
||||
{{ label }}
|
||||
<input #inputControl
|
||||
type="file"
|
||||
[accept]="accept"
|
||||
[disabled]="disabled"
|
||||
(change)="changeFile($event)">
|
||||
</label>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
|
||||
import { UploadFileData } from '../../models/upload-file-data';
|
||||
|
||||
@Component({
|
||||
selector: 'file-upload',
|
||||
templateUrl: './file-upload.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FileUploadComponent {
|
||||
@Input() label = 'Select file';
|
||||
@Input() disabled: boolean;
|
||||
@Input() accept: string;
|
||||
|
||||
@Output() fileChanged = new EventEmitter<UploadFileData>();
|
||||
|
||||
changeFile(event): void {
|
||||
const files = event.srcElement.files;
|
||||
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = function () {
|
||||
const arrayBuffer = new Uint8Array(fileReader.result);
|
||||
const target = event.target || event.srcElement || event.currentTarget;
|
||||
target.value = null;
|
||||
this.fileChanged.emit({
|
||||
filename: event.srcElement.value,
|
||||
data: Array.from(arrayBuffer)
|
||||
});
|
||||
}.bind(this);
|
||||
fileReader.readAsArrayBuffer(files[0]);
|
||||
}
|
||||
}
|
||||
1
packages/uhk-web/src/app/components/file-upload/index.ts
Normal file
1
packages/uhk-web/src/app/components/file-upload/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './file-upload.component';
|
||||
@@ -9,6 +9,7 @@
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
[description]="description"
|
||||
[showDescription]="true"
|
||||
oncontextmenu="return false;"
|
||||
(keyClick)="keyClick.emit($event)"
|
||||
(keyHover)="keyHover.emit($event)"
|
||||
(capture)="capture.emit($event)"
|
||||
|
||||
|
Before Width: | Height: | Size: 809 B After Width: | Height: | Size: 853 B |
@@ -3,6 +3,11 @@ import { animate, keyframes, state, style, transition, trigger } from '@angular/
|
||||
import { Layer } from 'uhk-common';
|
||||
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import {
|
||||
SvgKeyboardCaptureEvent,
|
||||
SvgKeyboardKeyClickEvent,
|
||||
SvgKeyHoverEvent
|
||||
} from '../../../models/svg-key-events';
|
||||
|
||||
type AnimationKeyboard =
|
||||
'init' |
|
||||
@@ -82,9 +87,9 @@ export class KeyboardSliderComponent implements OnChanges {
|
||||
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Input() description: string;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@Output() keyClick = new EventEmitter<SvgKeyboardKeyClickEvent>();
|
||||
@Output() keyHover = new EventEmitter<SvgKeyHoverEvent>();
|
||||
@Output() capture = new EventEmitter<SvgKeyboardCaptureEvent>();
|
||||
@Output() descriptionChanged = new EventEmitter<string>();
|
||||
|
||||
layerAnimationState: AnimationKeyboard[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Keymap } from 'uhk-common';
|
||||
|
||||
@@ -14,6 +14,7 @@ import { KeymapActions } from '../../../store/actions';
|
||||
selector: 'keymap-add',
|
||||
templateUrl: './keymap-add.component.html',
|
||||
styleUrls: ['./keymap-add.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async"
|
||||
[halvesSplit]="keyboardSplit"
|
||||
[keyboardLayout]="keyboardLayout$ | async"
|
||||
[allowLayerDoubleTap]="allowLayerDoubleTap$ | async"
|
||||
(descriptionChanged)="descriptionChanged($event)"></svg-keyboard-wrap>
|
||||
</ng-template>
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, HostListener, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, HostListener } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Keymap } from 'uhk-common';
|
||||
@@ -14,9 +14,8 @@ import 'rxjs/add/operator/combineLatest';
|
||||
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
import { AppState, getKeyboardLayout } from '../../../store';
|
||||
import { layerDoubleTapSupported, AppState, getKeyboardLayout } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import { SvgKeyboardWrapComponent } from '../../svg/wrap';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
|
||||
@@ -25,19 +24,19 @@ import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription
|
||||
selector: 'keymap-edit',
|
||||
templateUrl: './keymap-edit.component.html',
|
||||
styleUrls: ['./keymap-edit.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class KeymapEditComponent {
|
||||
|
||||
@ViewChild(SvgKeyboardWrapComponent) wrap: SvgKeyboardWrapComponent;
|
||||
|
||||
keyboardSplit: boolean;
|
||||
|
||||
deletable$: Observable<boolean>;
|
||||
keymap$: Observable<Keymap>;
|
||||
keyboardLayout$: Observable<KeyboardLayout>;
|
||||
allowLayerDoubleTap$: Observable<boolean>;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
route: ActivatedRoute) {
|
||||
@@ -52,6 +51,7 @@ export class KeymapEditComponent {
|
||||
.map((keymaps: Keymap[]) => keymaps.length > 1);
|
||||
|
||||
this.keyboardLayout$ = store.select(getKeyboardLayout);
|
||||
this.allowLayerDoubleTap$ = store.select(layerDoubleTapSupported);
|
||||
}
|
||||
|
||||
downloadKeymap() {
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
data-placement="bottom"
|
||||
(click)="duplicateKeymap()"
|
||||
></i>
|
||||
<i class="fa fa-download keymap__download pull-right"
|
||||
<!--i class="fa fa-download keymap__download pull-right"
|
||||
title="Download keymap"
|
||||
[html]="true"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
(click)="onDownloadIconClick()"></i>
|
||||
(click)="onDownloadIconClick()"></i-->
|
||||
</h1>
|
||||
</div>
|
||||
</uhk-header>
|
||||
|
||||
@@ -43,4 +43,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +67,7 @@ export class MacroKeyTabComponent extends MacroBaseComponent implements OnInit {
|
||||
}
|
||||
|
||||
getKeyMacroAction(): KeyMacroAction {
|
||||
const keyMacroAction = Object.assign(new KeyMacroAction(), this.keypressTab.toKeyAction());
|
||||
const keyMacroAction = new KeyMacroAction(this.keypressTab.toKeyAction() as any);
|
||||
keyMacroAction.action = this.getActionType(this.activeTab);
|
||||
return keyMacroAction;
|
||||
}
|
||||
|
||||
@@ -93,10 +93,17 @@
|
||||
<h4 *ngIf="activeTab === TabName.Hold">Hold mouse button</h4>
|
||||
<h4 *ngIf="activeTab === TabName.Release">Release mouse button</h4>
|
||||
<div class="btn-group">
|
||||
<button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index"
|
||||
class="btn btn-default"
|
||||
[class.btn-primary]="hasButton(buttonIndex)"
|
||||
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}
|
||||
<button class="btn btn-default"
|
||||
[class.btn-primary]="hasButton(MouseButtons.Left)"
|
||||
(click)="setMouseClick(MouseButtons.Left)">Left
|
||||
</button>
|
||||
<button class="btn btn-default"
|
||||
[class.btn-primary]="hasButton(MouseButtons.Middle)"
|
||||
(click)="setMouseClick(MouseButtons.Middle)">Middle
|
||||
</button>
|
||||
<button class="btn btn-default"
|
||||
[class.btn-primary]="hasButton(MouseButtons.Right)"
|
||||
(click)="setMouseClick(MouseButtons.Right)">Right
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import {
|
||||
MacroMouseSubAction,
|
||||
MouseButtons,
|
||||
MouseButtonMacroAction,
|
||||
MoveMouseMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
MacroMouseSubAction
|
||||
ScrollMouseMacroAction
|
||||
} from 'uhk-common';
|
||||
import { Tab } from '../../../../popover/tab';
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
@@ -33,6 +34,7 @@ export class MacroMouseTabComponent extends MacroBaseComponent implements OnInit
|
||||
@ViewChild('tab') selectedTab: Tab;
|
||||
|
||||
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
|
||||
MouseButtons = MouseButtons;
|
||||
TabName = TabName;
|
||||
/* tslint:enable:variable-name */
|
||||
activeTab: TabName;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<div>
|
||||
<h4>Type text</h4>
|
||||
<textarea #macroTextInput name="macro-text" (change)="onTextChange()"
|
||||
(keyup)="validate()" class="macro__text-input">{{ macroAction?.text }}</textarea>
|
||||
<textarea #macroTextInput
|
||||
name="macro-text"
|
||||
(keydown)="onKeydown($event)"
|
||||
(change)="onTextChange()"
|
||||
(keyup)="validate()"
|
||||
(paste)="onPaste($event)"
|
||||
class="macro__text-input">{{ macroAction?.text }}</textarea>
|
||||
</div>
|
||||
|
||||
@@ -4,13 +4,14 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
Renderer,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { TextMacroAction } from 'uhk-common';
|
||||
|
||||
import { MacroBaseComponent } from '../macro-base.component';
|
||||
|
||||
const NON_ASCII_REGEXP = /[^\x00-\x7F]/g;
|
||||
|
||||
@Component({
|
||||
selector: 'macro-text-tab',
|
||||
templateUrl: './macro-text.component.html',
|
||||
@@ -21,14 +22,14 @@ export class MacroTextTabComponent extends MacroBaseComponent implements OnInit,
|
||||
@Input() macroAction: TextMacroAction;
|
||||
@ViewChild('macroTextInput') input: ElementRef;
|
||||
|
||||
constructor(private renderer: Renderer) { super(); }
|
||||
constructor() { super(); }
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
|
||||
this.input.nativeElement.focus();
|
||||
}
|
||||
|
||||
onTextChange() {
|
||||
@@ -36,6 +37,41 @@ export class MacroTextTabComponent extends MacroBaseComponent implements OnInit,
|
||||
this.macroAction.text = this.input.nativeElement.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not allow non ascii character
|
||||
* @param $event
|
||||
*/
|
||||
onKeydown($event: KeyboardEvent): void {
|
||||
if (new RegExp(NON_ASCII_REGEXP).test($event.key)) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove non ascii character from clipboard data
|
||||
* @param $event
|
||||
*/
|
||||
onPaste($event: ClipboardEvent): void {
|
||||
$event.preventDefault();
|
||||
|
||||
const textarea: HTMLTextAreaElement = this.input.nativeElement;
|
||||
const data = $event.clipboardData.getData('text/plain');
|
||||
const text = data && data.replace(NON_ASCII_REGEXP, '') || '';
|
||||
if (text.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = textarea.value || '';
|
||||
const prefix = value.substr(0, textarea.selectionStart);
|
||||
const end = textarea.selectionEnd;
|
||||
const suffix = value.substr(textarea.selectionEnd);
|
||||
textarea.value = prefix + text + suffix;
|
||||
const correction = end === 0 ? 0 : 1;
|
||||
textarea.selectionStart = textarea.selectionEnd = end + text.length - correction;
|
||||
this.macroAction.text = textarea.value;
|
||||
}
|
||||
|
||||
isMacroValid = () => !!this.input.nativeElement.value;
|
||||
|
||||
private init = () => {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
></macro-header>
|
||||
<macro-list
|
||||
[macro]="macro"
|
||||
[macroPlaybackSupported]="macroPlaybackSupported$ | async"
|
||||
(add)="addAction($event.macroId, $event.action)"
|
||||
(edit)="editAction($event.macroId, $event.index, $event.action)"
|
||||
(delete)="deleteAction($event.macroId, $event.index, $event.action)"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user