8 Commits

Author SHA1 Message Date
László Monda
123cab5724 Bump Agent version to 1.2.9 2018-09-13 20:42:07 +02:00
Róbert Kiss
c16365a0e5 fix: Alt+Tab triggers "Remap on all layers" (#773) 2018-09-11 22:29:30 +02:00
Róbert Kiss
a21d278c0c fix: modifier button layout (#772) 2018-09-11 21:56:30 +02:00
Róbert Kiss
0466916be1 fix: key modifier reordering (#771) 2018-09-11 00:29:22 +02:00
László Monda
9a845d8f6a Fix eeprom.js 2018-09-10 19:29:58 +02:00
Róbert Kiss
9ae1673499 feat: secondary role visualisation (#767)
* feat: secondary role visualisation

* fix: recalculate the text position of the secondary role if changes

* fix: recalculate the text position of SvgKeystrokeKeyComponent

* fix: recalculate the text position when changes anything

* fix: two line text key position calculation

* fix: fix space positioning

* fix: visualize second character of complex key

* style: remove extra line
2018-09-08 12:09:16 +02:00
László Monda
2d5a5e7aef Exchange Alt and Super modifiers in the key action popover. 2018-09-03 03:58:03 +02:00
Róbert Kiss
3e4d439852 feat: display OS-specific modifiers (#764)
* chore: git ignore "out-tsc/" folder in uhk-web package

* feat: add OperationSystem calculation to the app reducer

* feat: create os specific key modifier

* feat: Os specific texts

* revert: KeyModifierValues and getKeyModifiers selector

* refactor: remove unnecessary return

* refactor: rename OperationSystem => OperatingSystem
2018-09-03 00:21:55 +02:00
43 changed files with 692 additions and 194 deletions

View File

@@ -6,6 +6,14 @@ 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). Every Agent version includes the most recent firmware version. See the [firmware changelog](https://github.com/UltimateHackingKeyboard/firmware/blob/master/CHANGELOG.md).
## [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 ## [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 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

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "uhk-agent", "name": "uhk-agent",
"version": "1.2.7", "version": "1.2.9",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -3,7 +3,7 @@
"private": true, "private": true,
"author": "Ultimate Gadget Laboratories", "author": "Ultimate Gadget Laboratories",
"main": "electron/dist/electron-main.js", "main": "electron/dist/electron-main.js",
"version": "1.2.8", "version": "1.2.9",
"firmwareVersion": "8.2.5", "firmwareVersion": "8.2.5",
"deviceProtocolVersion": "4.4.0", "deviceProtocolVersion": "4.4.0",
"userConfigVersion": "4.0.1", "userConfigVersion": "4.0.1",

1
packages/uhk-web/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
out-tsc/

View File

@@ -47,7 +47,7 @@
</ul> </ul>
</div> </div>
<div [ngSwitch]="activeTab"> <div [ngSwitch]="activeTab">
<keypress-tab #tab *ngSwitchCase="tabName.Keypress" class="popover-content" <keypress-tab #tab *ngSwitchCase="tabName.Keypress" class="popover-content pr-10"
[defaultKeyAction]="defaultKeyAction" [defaultKeyAction]="defaultKeyAction"
[secondaryRoleEnabled]="true" [secondaryRoleEnabled]="true"
(validAction)="keyActionValid=$event" (validAction)="keyActionValid=$event"

View File

@@ -99,6 +99,10 @@
padding: 10px 24px; padding: 10px 24px;
} }
.pr-10 {
padding-right: 10px;
}
.popover-overlay { .popover-overlay {
position: fixed; position: fixed;
width: 100%; width: 100%;

View File

@@ -36,20 +36,20 @@
<div class="btn-toolbar modifiers"> <div class="btn-toolbar modifiers">
<div class="btn-group btn-group-sm modifiers__left"> <div class="btn-group btn-group-sm modifiers__left">
<button type="button" class="btn btn-default" <button type="button" class="btn btn-default"
*ngFor="let modifier of leftModifiers; let index = index" *ngFor="let modifier of leftModifiers; trackBy:modifiersTrackBy"
[class.btn-primary]="leftModifierSelects[index]" [class.btn-primary]="modifier.checked"
(click)="toggleModifier(false, index)" (click)="toggleModifier(modifier)"
> >
{{modifier}} {{ modifier.text }}
</button> </button>
</div> </div>
<div class="btn-group btn-group-sm modifiers__right"> <div class="btn-group btn-group-sm modifiers__right">
<button type="button" class="btn btn-default" <button type="button" class="btn btn-default"
*ngFor="let modifier of rightModifiers; let index = index" *ngFor="let modifier of rightModifiers; trackBy:modifiersTrackBy"
[class.btn-primary]="rightModifierSelects[index]" [class.btn-primary]="modifier.checked"
(click)="toggleModifier(true, index)" (click)="toggleModifier(modifier)"
> >
{{modifier}} {{ modifier.text }}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,8 @@ import { KeyAction, KeystrokeAction, KeystrokeType, SCANCODES, SECONDARY_ROLES }
import { Tab } from '../tab'; import { Tab } from '../tab';
import { MapperService } from '../../../../services/mapper.service'; import { MapperService } from '../../../../services/mapper.service';
import { SelectOptionData } from '../../../../models/select-option-data'; import { SelectOptionData } from '../../../../models/select-option-data';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
import { mapLeftRigthModifierToKeyActionModifier } from '../../../../util';
@Component({ @Component({
selector: 'keypress-tab', selector: 'keypress-tab',
@@ -15,11 +17,8 @@ export class KeypressTabComponent extends Tab implements OnChanges {
@Input() defaultKeyAction: KeyAction; @Input() defaultKeyAction: KeyAction;
@Input() secondaryRoleEnabled: boolean; @Input() secondaryRoleEnabled: boolean;
leftModifiers: string[]; leftModifiers: KeyModifierModel[];
rightModifiers: string[]; rightModifiers: KeyModifierModel[];
leftModifierSelects: boolean[];
rightModifierSelects: boolean[];
scanCodeGroups: Array<SelectOptionData>; scanCodeGroups: Array<SelectOptionData>;
secondaryRoleGroups: Array<SelectOptionData>; secondaryRoleGroups: Array<SelectOptionData>;
@@ -29,16 +28,15 @@ export class KeypressTabComponent extends Tab implements OnChanges {
constructor(private mapper: MapperService) { constructor(private mapper: MapperService) {
super(); super();
this.leftModifiers = ['LShift', 'LCtrl', 'LSuper', 'LAlt']; this.leftModifiers = mapper.getLeftKeyModifiers();
this.rightModifiers = ['RShift', 'RCtrl', 'RSuper', 'RAlt']; this.rightModifiers = mapper.getRightKeyModifiers();
this.scanCodeGroups = [{ this.scanCodeGroups = [{
id: '0', id: '0',
text: 'None' text: 'None'
}]; }];
this.scanCodeGroups = this.scanCodeGroups.concat(SCANCODES); this.scanCodeGroups = this.scanCodeGroups.concat(SCANCODES);
this.secondaryRoleGroups = SECONDARY_ROLES; this.secondaryRoleGroups = SECONDARY_ROLES;
this.leftModifierSelects = Array(this.leftModifiers.length).fill(false);
this.rightModifierSelects = Array(this.rightModifiers.length).fill(false);
this.selectedScancodeOption = this.scanCodeGroups[0]; this.selectedScancodeOption = this.scanCodeGroups[0];
this.selectedSecondaryRoleIndex = -1; this.selectedSecondaryRoleIndex = -1;
} }
@@ -56,15 +54,15 @@ export class KeypressTabComponent extends Tab implements OnChanges {
return (keystrokeAction) ? (keystrokeAction.scancode > 0 || keystrokeAction.modifierMask > 0) : false; return (keystrokeAction) ? (keystrokeAction.scancode > 0 || keystrokeAction.modifierMask > 0) : false;
} }
onKeysCapture(event: { code: number, left: boolean[], right: boolean[] }) { onKeysCapture(event: { code: number, left: KeyModifierModel[], right: KeyModifierModel[] }) {
if (event.code) { if (event.code) {
this.selectedScancodeOption = this.findScancodeOptionByScancode(event.code, KeystrokeType.basic); this.selectedScancodeOption = this.findScancodeOptionByScancode(event.code, KeystrokeType.basic);
} else { } else {
this.selectedScancodeOption = this.scanCodeGroups[0]; this.selectedScancodeOption = this.scanCodeGroups[0];
} }
this.leftModifierSelects = event.left; this.leftModifiers = event.left;
this.rightModifierSelects = event.right; this.rightModifiers = event.right;
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
@@ -76,16 +74,12 @@ export class KeypressTabComponent extends Tab implements OnChanges {
// Restore selectedScancodeOption // Restore selectedScancodeOption
this.selectedScancodeOption = this.findScancodeOptionByScancode(keystrokeAction.scancode || 0, keystrokeAction.type); this.selectedScancodeOption = this.findScancodeOptionByScancode(keystrokeAction.scancode || 0, keystrokeAction.type);
const leftModifiersLength: number = this.leftModifiers.length; for (const modifier of this.leftModifiers) {
modifier.checked = (keystrokeAction.modifierMask & modifier.value) > 0;
// Restore modifiers
for (let i = 0; i < leftModifiersLength; ++i) {
this.leftModifierSelects[this.mapper.modifierMapper(i)] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
} }
for (let i = leftModifiersLength; i < leftModifiersLength + this.rightModifierSelects.length; ++i) { for (const modifier of this.rightModifiers) {
const index: number = this.mapper.modifierMapper(i) - leftModifiersLength; modifier.checked = (keystrokeAction.modifierMask & modifier.value) > 0;
this.rightModifierSelects[index] = ((keystrokeAction.modifierMask >> i) & 1) === 1;
} }
// Restore secondaryRoleAction // Restore secondaryRoleAction
@@ -107,11 +101,7 @@ export class KeypressTabComponent extends Tab implements OnChanges {
} else { } else {
keystrokeAction.type = KeystrokeType[scTypePair[1]]; keystrokeAction.type = KeystrokeType[scTypePair[1]];
} }
keystrokeAction.modifierMask = 0; keystrokeAction.modifierMask = mapLeftRigthModifierToKeyActionModifier(this.leftModifiers, this.rightModifiers);
const modifiers = this.leftModifierSelects.concat(this.rightModifierSelects).map(x => x ? 1 : 0);
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
keystrokeAction.secondaryRoleAction = this.selectedSecondaryRoleIndex === -1 keystrokeAction.secondaryRoleAction = this.selectedSecondaryRoleIndex === -1
? undefined ? undefined
@@ -122,9 +112,8 @@ export class KeypressTabComponent extends Tab implements OnChanges {
} }
} }
toggleModifier(right: boolean, index: number) { toggleModifier(modifier: KeyModifierModel): void {
const modifierSelects: boolean[] = right ? this.rightModifierSelects : this.leftModifierSelects; modifier.checked = !modifier.checked;
modifierSelects[index] = !modifierSelects[index];
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
@@ -139,6 +128,10 @@ export class KeypressTabComponent extends Tab implements OnChanges {
this.validAction.emit(this.keyActionValid()); this.validAction.emit(this.keyActionValid());
} }
modifiersTrackBy(index: number, modifier: KeyModifierModel): string {
return `${modifier.value}${modifier.checked}`;
}
private findScancodeOptionBy(predicate: (option: SelectOptionData) => boolean): SelectOptionData { private findScancodeOptionBy(predicate: (option: SelectOptionData) => boolean): SelectOptionData {
let selectedOption: SelectOptionData; let selectedOption: SelectOptionData;

View File

@@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { CaptureService } from '../../../../services/capture.service'; import { CaptureService } from '../../../../services/capture.service';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
@Component({ @Component({
selector: 'capture-keystroke-button', selector: 'capture-keystroke-button',
@@ -68,8 +69,8 @@ export class CaptureKeystrokeButtonComponent {
private saveScanCode(code?: number) { private saveScanCode(code?: number) {
this.record = false; this.record = false;
const left: boolean[] = this.captureService.getModifiers(true); const left: KeyModifierModel[] = this.captureService.getModifiers(true);
const right: boolean[] = this.captureService.getModifiers(false); const right: KeyModifierModel[] = this.captureService.getModifiers(false);
this.capture.emit({ this.capture.emit({
code, code,

View File

@@ -0,0 +1 @@
export const SECONDARY_ROLE_BOTTOM_MARGIN = 1;

View File

@@ -11,3 +11,4 @@ export { SvgSingleIconKeyComponent } from './svg-single-icon-key';
export { SvgSwitchKeymapKeyComponent } from './svg-switch-keymap-key'; export { SvgSwitchKeymapKeyComponent } from './svg-switch-keymap-key';
export { SvgTextIconKeyComponent } from './svg-text-icon-key'; export { SvgTextIconKeyComponent } from './svg-text-icon-key';
export { SvgTwoLineTextKeyComponent } from './svg-two-line-text-key'; export { SvgTwoLineTextKeyComponent } from './svg-two-line-text-key';
export { SvgSecondaryRoleComponent } from './svg-secondary-role';

View File

@@ -11,3 +11,10 @@
[attr.font-size]="11"> [attr.font-size]="11">
<tspan [attr.x]="spanX">{{ text }}</tspan> <tspan [attr.x]="spanX">{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 499 B

View File

@@ -1,15 +1,19 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-icon-text-key]', selector: 'g[svg-icon-text-key]',
templateUrl: './svg-icon-text-key.component.html', templateUrl: './svg-icon-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgIconTextKeyComponent implements OnInit { export class SvgIconTextKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() icon: string; @Input() icon: string;
@Input() text: string; @Input() text: string;
@Input() secondaryText: string;
useWidth: number; useWidth: number;
useHeight: number; useHeight: number;
@@ -17,16 +21,33 @@ export class SvgIconTextKeyComponent implements OnInit {
useY: number; useY: number;
textY: number; textY: number;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { constructor() {
} }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
private calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.useWidth = this.width / 3; this.useWidth = this.width / 3;
this.useHeight = this.height / 3; this.useHeight = this.height / 3;
this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3; this.useX = (this.width > 2 * this.height) ? 0 : this.width / 3;
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10; this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 10;
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6; this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height * 0.6;
this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2; this.spanX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -25,34 +25,40 @@
<svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey" <svg:g svg-keystroke-key *ngSwitchCase="enumLabelTypes.KeystrokeKey"
[height]="height" [height]="height"
[width]="width" [width]="width"
[keystrokeAction]="labelSource"> [keystrokeAction]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText" <svg:g svg-one-line-text-key *ngSwitchCase="enumLabelTypes.OneLineText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[text]="labelSource"> [text]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText" <svg:g svg-two-line-text-key *ngSwitchCase="enumLabelTypes.TwoLineText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[texts]="labelSource"> [texts]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon" <svg:g svg-text-icon-key *ngSwitchCase="enumLabelTypes.TextIcon"
[height]="height" [height]="height"
[width]="width" [width]="width"
[text]="labelSource.text" [text]="labelSource.text"
[icon]="labelSource.icon"> [icon]="labelSource.icon"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText" <svg:g svg-icon-text-key *ngSwitchCase="enumLabelTypes.IconText"
[height]="height" [height]="height"
[width]="width" [width]="width"
[icon]="labelSource.icon" [icon]="labelSource.icon"
[text]="labelSource.text"> [text]="labelSource.text"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon" <svg:g svg-single-icon-key *ngSwitchCase="enumLabelTypes.SingleIcon"
[height]="height" [height]="height"
[width]="width" [width]="width"
[icon]="labelSource"> [icon]="labelSource"
[secondaryText]="secondaryText">
</svg:g> </svg:g>
<svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap" <svg:g svg-switch-keymap-key *ngSwitchCase="enumLabelTypes.SwitchKeymap"
[height]="height" [height]="height"

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -27,6 +27,8 @@ import { MapperService } from '../../../../services/mapper.service';
import { AppState } from '../../../../store'; import { AppState } from '../../../../store';
import { getMacros } from '../../../../store/reducers/user-configuration'; import { getMacros } from '../../../../store/reducers/user-configuration';
import { SvgKeyCaptureEvent, SvgKeyClickEvent } from '../../../../models/svg-key-events'; import { SvgKeyCaptureEvent, SvgKeyClickEvent } from '../../../../models/svg-key-events';
import { OperatingSystem } from '../../../../models/operating-system';
import { KeyModifierModel } from '../../../../models/key-modifier-model';
enum LabelTypes { enum LabelTypes {
KeystrokeKey, KeystrokeKey,
@@ -94,6 +96,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
labelType: LabelTypes; labelType: LabelTypes;
labelSource: any; labelSource: any;
secondaryText: string;
macros: Macro[]; macros: Macro[];
private subscription: Subscription; private subscription: Subscription;
private scanCodePressed: boolean; private scanCodePressed: boolean;
@@ -117,13 +120,13 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
this.scanCodePressed = false; this.scanCodePressed = false;
} }
@HostListener('click') @HostListener('click', ['$event'])
onClick() { onClick(e: MouseEvent) {
this.reset(); this.reset();
this.keyClick.emit({ this.keyClick.emit({
keyTarget: this.element.nativeElement, keyTarget: this.element.nativeElement,
shiftPressed: this.pressedShiftLocation > -1, shiftPressed: e.shiftKey,
altPressed: this.pressedAltLocation > -1 altPressed: e.altKey
}); });
this.pressedShiftLocation = -1; this.pressedShiftLocation = -1;
this.pressedAltLocation = -1; this.pressedAltLocation = -1;
@@ -140,14 +143,8 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} else { } else {
this.recording = true; this.recording = true;
this.recordAnimation = 'active'; this.recordAnimation = 'active';
this.shiftPressed = e.shiftKey;
if (this.pressedShiftLocation > -1) { this.altPressed = e.altKey;
this.shiftPressed = true;
}
if (this.pressedAltLocation > -1) {
this.altPressed = true;
}
} }
} }
} }
@@ -248,8 +245,8 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} }
private saveScanCode(code = 0) { private saveScanCode(code = 0) {
const left: boolean[] = this.captureService.getModifiers(true); const left: KeyModifierModel[] = this.captureService.getModifiers(true);
const right: boolean[] = this.captureService.getModifiers(false); const right: KeyModifierModel[] = this.captureService.getModifiers(false);
this.capture.emit({ this.capture.emit({
captured: { captured: {
@@ -265,17 +262,18 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} }
private setLabels(): void { private setLabels(): void {
this.labelType = LabelTypes.OneLineText;
this.labelSource = undefined;
this.secondaryText = undefined;
if (!this.keyAction) { if (!this.keyAction) {
this.labelSource = undefined;
this.labelType = LabelTypes.OneLineText;
return; return;
} }
this.labelType = LabelTypes.OneLineText;
if (this.keyAction instanceof KeystrokeAction) { if (this.keyAction instanceof KeystrokeAction) {
const keyAction: KeystrokeAction = this.keyAction as KeystrokeAction; const keyAction: KeystrokeAction = this.keyAction as KeystrokeAction;
let newLabelSource: string[]; let newLabelSource: string[];
this.secondaryText = this.mapper.getSecondaryRoleText(keyAction.secondaryRoleAction);
if (!keyAction.hasActiveModifier() && keyAction.hasScancode()) { if (!keyAction.hasActiveModifier() && keyAction.hasScancode()) {
const scancode: number = keyAction.scancode; const scancode: number = keyAction.scancode;
@@ -293,29 +291,32 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges, OnDestroy {
} }
} }
} else if (keyAction.hasOnlyOneActiveModifier() && !keyAction.hasScancode()) { } else if (keyAction.hasOnlyOneActiveModifier() && !keyAction.hasScancode()) {
newLabelSource = [];
switch (keyAction.modifierMask) { switch (keyAction.modifierMask) {
case KeyModifiers.leftCtrl: case KeyModifiers.leftCtrl:
case KeyModifiers.rightCtrl: case KeyModifiers.rightCtrl:
newLabelSource.push('Ctrl'); this.labelSource = ['Ctrl'];
break; break;
case KeyModifiers.leftShift: case KeyModifiers.leftShift:
case KeyModifiers.rightShift: case KeyModifiers.rightShift:
newLabelSource.push('Shift'); this.labelSource = ['Shift'];
break; break;
case KeyModifiers.leftAlt: case KeyModifiers.leftAlt:
case KeyModifiers.rightAlt: case KeyModifiers.rightAlt:
newLabelSource.push('Alt'); this.labelSource = [this.mapper.getOsSpecificText('Alt')];
break; break;
case KeyModifiers.leftGui: case KeyModifiers.leftGui:
case KeyModifiers.rightGui: case KeyModifiers.rightGui:
newLabelSource.push('Super'); if (this.mapper.getOperatingSystem() === OperatingSystem.Windows) {
this.labelSource = this.mapper.getIcon('command');
this.labelType = LabelTypes.SingleIcon;
} else {
this.labelSource = [this.mapper.getOsSpecificText('Super')];
}
break; break;
default: default:
newLabelSource.push('Undefined'); this.labelSource = ['Undefined'];
break; break;
} }
this.labelSource = newLabelSource;
} else { } else {
this.labelType = LabelTypes.KeystrokeKey; this.labelType = LabelTypes.KeystrokeKey;
this.labelSource = this.keyAction; this.labelSource = this.keyAction;

View File

@@ -13,7 +13,8 @@
<svg:g svg-two-line-text-key *ngSwitchCase="'two-line'" <svg:g svg-two-line-text-key *ngSwitchCase="'two-line'"
[height]="height" [height]="height"
[width]="width" [width]="width"
[texts]="labelSource"> [texts]="labelSource"
[secondaryText]="subComponentSecondaryRoleText">
</svg:g> </svg:g>
</svg> </svg>
<svg [attr.viewBox]="viewBox" [attr.width]="modifierContainer.width" [attr.height]="modifierContainer.height" [attr.x]="modifierContainer.x" <svg [attr.viewBox]="viewBox" [attr.width]="modifierContainer.width" [attr.height]="modifierContainer.height" [attr.x]="modifierContainer.x"
@@ -28,10 +29,19 @@
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y" <svg viewBox="0 0 100 100" [attr.width]="option.width" [attr.height]="option.height" [attr.x]="option.x" [attr.y]="option.y"
preserveAspectRatio="none" [class.disabled]="option.disabled"> preserveAspectRatio="none" [class.disabled]="option.disabled">
<svg:use [attr.xlink:href]="modifierIconNames.option" /> <svg:use *ngIf="modifierIconNames.option" [attr.xlink:href]="modifierIconNames.option" />
<svg:text *ngIf="!modifierIconNames.option" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">A</svg:text>
</svg> </svg>
<svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y" <svg viewBox="0 0 100 100" [attr.width]="command.width" [attr.height]="command.height" [attr.x]="command.x" [attr.y]="command.y"
preserveAspectRatio="none" [class.disabled]="command.disabled"> preserveAspectRatio="none" [class.disabled]="command.disabled">
<svg:use [attr.xlink:href]="modifierIconNames.command" /> <svg:use *ngIf="modifierIconNames.command" [attr.xlink:href]="modifierIconNames.command" />
<svg:text *ngIf="!modifierIconNames.command" [attr.text-anchor]="'middle'" [attr.x]="50" [attr.y]="50">S</svg:text>
</svg> </svg>
</svg> </svg>
<svg:g svg-secondary-role
*ngIf="thisSecondaryRoleText"
[height]="20"
[width]="secondaryTextWidth"
[y]="secondaryTextY"
[text]="thisSecondaryRoleText">
</svg:g>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,7 +1,9 @@
import { Component, Input, OnChanges, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { KeyModifiers, KeystrokeAction } from 'uhk-common'; import { KeyModifiers, KeystrokeAction } from 'uhk-common';
import { MapperService } from '../../../../services/mapper.service'; import { MapperService } from '../../../../services/mapper.service';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
class SvgAttributes { class SvgAttributes {
width: number; width: number;
@@ -25,10 +27,11 @@ class SvgAttributes {
styleUrls: ['./svg-keystroke-key.component.scss'], styleUrls: ['./svg-keystroke-key.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgKeystrokeKeyComponent implements OnInit, OnChanges { export class SvgKeystrokeKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() keystrokeAction: KeystrokeAction; @Input() keystrokeAction: KeystrokeAction;
@Input() secondaryText: string;
viewBox: string; viewBox: string;
textContainer: SvgAttributes; textContainer: SvgAttributes;
@@ -46,6 +49,11 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
option?: string, option?: string,
command?: string command?: string
}; };
secondaryTextY: number;
secondaryTextWidth: number;
secondaryHeight: number;
thisSecondaryRoleText: string;
subComponentSecondaryRoleText: string;
constructor(private mapper: MapperService) { constructor(private mapper: MapperService) {
this.modifierIconNames = {}; this.modifierIconNames = {};
@@ -57,15 +65,75 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command = new SvgAttributes(); this.command = new SvgAttributes();
} }
ngOnInit() { ngOnChanges() {
this.calculatePositions();
}
private calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
this.thisSecondaryRoleText = this.secondaryText;
this.subComponentSecondaryRoleText = null;
const bottomSideMode: boolean = this.width < this.height * 1.8;
const isRectangleAsSecondaryRole = isRectangleAsSecondaryRoleKey(this.width, this.height);
if (this.secondaryText && isRectangleAsSecondaryRole) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
if (this.keystrokeAction.hasScancode()) {
const scancode: number = this.keystrokeAction.scancode;
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode, this.keystrokeAction.type);
if (this.labelSource) {
this.labelType = 'icon';
} else {
let newLabelSource: string[];
newLabelSource = this.mapper.scanCodeToText(scancode, this.keystrokeAction.type);
if (newLabelSource) {
if (this.secondaryText && newLabelSource.length === 2) {
if (isRectangleAsSecondaryRole || bottomSideMode) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
this.thisSecondaryRoleText = null;
this.subComponentSecondaryRoleText = this.secondaryText;
}
}
else {
if (newLabelSource.length === 1) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
}
}
}
}
} else {
this.labelType = 'empty';
}
this.shift.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftShift | KeyModifiers.rightShift);
this.control.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftCtrl | KeyModifiers.rightCtrl);
this.option.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftAlt | KeyModifiers.rightAlt);
this.command.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftGui | KeyModifiers.rightGui);
this.secondaryHeight = this.secondaryText ? this.height / 4 : 0;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
this.viewBox = [0, 0, this.width, this.height].join(' '); this.viewBox = [0, 0, this.width, this.height].join(' ');
this.modifierIconNames.shift = this.mapper.getIcon('shift'); this.modifierIconNames.shift = this.mapper.getIcon('shift');
this.modifierIconNames.option = this.mapper.getIcon('option'); this.modifierIconNames.option = this.mapper.getIcon('option');
this.modifierIconNames.command = this.mapper.getIcon('command'); this.modifierIconNames.command = this.mapper.getIcon('command');
this.textContainer.y = 0;
const bottomSideMode: boolean = this.width < this.height * 1.8;
const heightWidthRatio = this.height / this.width; const heightWidthRatio = this.height / this.width;
this.secondaryTextWidth = this.width;
if (bottomSideMode) { if (bottomSideMode) {
const maxIconWidth = this.width / 4; const maxIconWidth = this.width / 4;
@@ -75,7 +143,7 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
const iconHeight = iconScalingFactor * maxIconHeight; const iconHeight = iconScalingFactor * maxIconHeight;
this.modifierContainer.width = this.width; this.modifierContainer.width = this.width;
this.modifierContainer.height = this.height / 5; this.modifierContainer.height = this.height / 5;
this.modifierContainer.y = this.height - this.modifierContainer.height; this.modifierContainer.y = this.height - this.modifierContainer.height - this.secondaryHeight;
this.shift.width = iconWidth; this.shift.width = iconWidth;
this.shift.height = iconHeight; this.shift.height = iconHeight;
this.shift.x = (maxIconWidth - iconWidth) / 2; this.shift.x = (maxIconWidth - iconWidth) / 2;
@@ -92,7 +160,7 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command.height = iconHeight; this.command.height = iconHeight;
this.command.x = this.option.x + maxIconWidth; this.command.x = this.option.x + maxIconWidth;
this.command.y = this.shift.y; this.command.y = this.shift.y;
this.textContainer.y = -this.modifierContainer.height / 2; this.textContainer.y = -this.modifierContainer.height / 2 - this.secondaryHeight / 2;
} else { } else {
this.modifierContainer.width = this.width / 4; this.modifierContainer.width = this.width / 4;
this.modifierContainer.height = this.height; this.modifierContainer.height = this.height;
@@ -120,40 +188,11 @@ export class SvgKeystrokeKeyComponent implements OnInit, OnChanges {
this.command.x = this.option.x + this.width / 2; this.command.x = this.option.x + this.width / 2;
this.command.y = this.option.y; this.command.y = this.option.y;
this.textContainer.x = -this.modifierContainer.width / 2; this.textContainer.x = -this.modifierContainer.width / 2;
this.secondaryTextWidth = this.width - this.modifierContainer.width;
} }
this.textContainer.y -= textYModifier;
this.textContainer.width = this.width; this.textContainer.width = this.width;
this.textContainer.height = this.height; this.textContainer.height = this.height;
} }
ngOnChanges() {
if (this.keystrokeAction.hasScancode()) {
const scancode: number = this.keystrokeAction.scancode;
this.labelSource = this.mapper.scanCodeToSvgImagePath(scancode, this.keystrokeAction.type);
if (this.labelSource) {
this.labelType = 'icon';
} else {
let newLabelSource: string[];
newLabelSource = this.mapper.scanCodeToText(scancode, this.keystrokeAction.type);
if (newLabelSource) {
if (newLabelSource.length === 1) {
this.labelSource = newLabelSource[0];
this.labelType = 'one-line';
} else {
this.labelSource = newLabelSource;
this.labelType = 'two-line';
}
}
}
} else {
this.labelType = 'empty';
}
this.shift.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftShift | KeyModifiers.rightShift);
this.control.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftCtrl | KeyModifiers.rightCtrl);
this.option.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftAlt | KeyModifiers.rightAlt);
this.command.disabled = !this.keystrokeAction.isActive(KeyModifiers.leftGui | KeyModifiers.rightGui);
}
} }

View File

@@ -1,6 +1,13 @@
<svg:text <svg:text
[attr.x]="0" [attr.x]="0"
[attr.y]="textY" [attr.y]="textY"
[attr.text-anchor]="'middle'"> [attr.text-anchor]="'middle'">
<tspan [attr.x]="spanX" dy="0">{{ text }}</tspan> <tspan [attr.x]="spanX" dy="0">{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -1,22 +1,43 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-one-line-text-key]', selector: 'g[svg-one-line-text-key]',
templateUrl: './svg-one-line-text-key.component.html', templateUrl: './svg-one-line-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgOneLineTextKeyComponent implements OnInit { export class SvgOneLineTextKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() text: string; @Input() text: string;
@Input() secondaryText: string;
textY: number; textY: number;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.textY = this.height / 2; this.calculatePositions();
}
calculatePositions() {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.textY = this.height / 2 - textYModifier;
this.spanX = this.width / 2; this.spanX = this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -0,0 +1 @@
export * from './svg-secondary-role.component';

View File

@@ -0,0 +1,15 @@
<svg [attr.viewBox]="viewBox" [attr.width]="width" [attr.height]="height" [attr.y]="y">
<g id="secondaryContent" [attr.transform]="transform">
<svg viewBox="0 0 14 14" width="12" height="12" x="2" [attr.y]="textY / 3.5">
<ellipse stroke="#fff" rx="6.5" ry="6.5" cy="7" cx="7" stroke-width="1" fill-opacity="0"/>
<text text-anchor="start" font-family="Helvetica" font-size="12" y="7.8" x="4" stroke-width="0">2
</text>
</svg>
<text [attr.y]="textY"
[attr.x]="textIndent"
font-size="12"
text-anchor="start">
{{ text }}
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 667 B

View File

@@ -0,0 +1,52 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Input,
OnChanges,
OnInit,
SimpleChanges,
ViewChild
} from '@angular/core';
import { getContentWidth } from '../../../../util';
const SECONDARY_STYLE: CSSStyleDeclaration = {
font: '12px Helvetica'
} as any;
@Component({
selector: 'g[svg-secondary-role]',
templateUrl: './svg-secondary-role.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SvgSecondaryRoleComponent implements OnInit, OnChanges {
@Input() height: number;
@Input() width: number;
@Input() y: number;
@Input() text: string;
@ViewChild('secondary') svgElement: ElementRef;
viewBox: string;
textY: number;
transform: string;
textIndent = 16;
ngOnInit(): void {
this.viewBox = [0, 0, this.width, this.height].join(' ');
this.textY = this.height / 2 - 2;
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.text) {
this.calculateTextPosition();
}
}
private calculateTextPosition(): void {
const textWidth = getContentWidth(SECONDARY_STYLE, this.text) + this.textIndent;
const translateValue = Math.max(0, (this.width - textWidth) / 2);
this.transform = `translate(${ translateValue },0)`;
}
}

View File

@@ -4,3 +4,10 @@
[attr.x]="svgWidth" [attr.x]="svgWidth"
[attr.y]="svgHeight"> [attr.y]="svgHeight">
</svg:use> </svg:use>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -1,22 +1,42 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-single-icon-key]', selector: 'g[svg-single-icon-key]',
templateUrl: './svg-single-icon-key.component.html', templateUrl: './svg-single-icon-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgSingleIconKeyComponent implements OnInit { export class SvgSingleIconKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() icon: string; @Input() icon: string;
@Input() secondaryText: string;
svgHeight: number; svgHeight: number;
svgWidth: number; svgWidth: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.svgWidth = this.width / 3; this.svgWidth = this.width / 3;
this.svgHeight = this.height / 3; this.svgHeight = this.height / 3;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -10,3 +10,10 @@
[attr.x]="useX" [attr.x]="useX"
[attr.y]="useY"> [attr.y]="useY">
</svg:use> </svg:use>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -1,15 +1,19 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, OnChanges, SimpleChanges } from '@angular/core';
import { isRectangleAsSecondaryRoleKey } from '../util';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-text-icon-key]', selector: 'g[svg-text-icon-key]',
templateUrl: './svg-text-icon-key.component.html', templateUrl: './svg-text-icon-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgTextIconKeyComponent implements OnInit { export class SvgTextIconKeyComponent implements OnChanges {
@Input() width: number; @Input() width: number;
@Input() height: number; @Input() height: number;
@Input() text: string; @Input() text: string;
@Input() icon: string; @Input() icon: string;
@Input() secondaryText: string;
useWidth: number; useWidth: number;
useHeight: number; useHeight: number;
@@ -18,16 +22,33 @@ export class SvgTextIconKeyComponent implements OnInit {
textY: number; textY: number;
textAnchor: string; textAnchor: string;
spanX: number; spanX: number;
secondaryTextY: number;
secondaryHeight: number;
constructor() { } constructor() { }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.calculatePositions();
}
calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
if (this.secondaryText && isRectangleAsSecondaryRoleKey(this.width, this.height)) {
textYModifier = this.height / 5;
secondaryYModifier = 5;
}
this.useWidth = this.width / 3; this.useWidth = this.width / 3;
this.useHeight = this.height / 3; this.useHeight = this.height / 3;
this.useX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 3; this.useX = (this.width > 2 * this.height) ? this.width * 0.6 : this.width / 3;
this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2; this.useY = (this.width > 2 * this.height) ? this.height / 3 : this.height / 2;
this.textY = (this.width > 2 * this.height) ? this.height / 2 : this.height / 3; this.textY = ((this.width > 2 * this.height) ? this.height / 2 : this.height / 3) - textYModifier;
this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle'; this.textAnchor = (this.width > 2 * this.height) ? 'end' : 'middle';
this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2; this.spanX = (this.width > 2 * this.height) ? 0.6 * this.width : this.width / 2;
this.secondaryHeight = this.height / 4;
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -9,3 +9,10 @@
dy="0" dy="0"
>{{ text }}</tspan> >{{ text }}</tspan>
</svg:text> </svg:text>
<svg:g svg-secondary-role
*ngIf="secondaryText"
[height]="20"
[width]="width"
[y]="secondaryTextY"
[text]="secondaryText">
</svg:g>

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -1,28 +1,51 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Component, Input, ChangeDetectionStrategy, SimpleChanges, OnChanges } from '@angular/core';
import { SECONDARY_ROLE_BOTTOM_MARGIN } from '../../constants';
@Component({ @Component({
selector: 'g[svg-two-line-text-key]', selector: 'g[svg-two-line-text-key]',
templateUrl: './svg-two-line-text-key.component.html', templateUrl: './svg-two-line-text-key.component.html',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SvgTwoLineTextKeyComponent implements OnInit { export class SvgTwoLineTextKeyComponent implements OnChanges {
@Input() height: number; @Input() height: number;
@Input() width: number; @Input() width: number;
@Input() texts: string[]; @Input() texts: string[];
@Input() secondaryText: string;
textY: number; textY: number;
spanX: number; spanX: number;
spanYs: number[]; spanYs: number[];
secondaryTextY: number;
secondaryHeight: number;
constructor() { constructor() {
this.spanYs = []; this.spanYs = [];
} }
ngOnInit() { ngOnChanges(changes: SimpleChanges): void {
this.textY = this.height / 2; this.calculatePositions();
this.spanX = this.width / 2; }
for (let i = 0; i < this.texts.length; ++i) {
this.spanYs.push((0.75 - i * 0.5) * this.height); calculatePositions(): void {
let textYModifier = 0;
let secondaryYModifier = 0;
this.secondaryHeight = 0;
let textContainerHeight = this.height;
if (this.secondaryText) {
textYModifier = this.height / 5;
secondaryYModifier = 0;
this.secondaryHeight = this.height / 4;
textContainerHeight -= this.secondaryHeight;
} }
this.textY = textContainerHeight / 2;
this.spanX = this.width / 2;
this.spanYs = [];
for (let i = 0; i < this.texts.length; ++i) {
this.spanYs.push((0.75 - i * 0.5) * textContainerHeight);
}
this.secondaryTextY = this.height - this.secondaryHeight - SECONDARY_ROLE_BOTTOM_MARGIN - secondaryYModifier;
} }
} }

View File

@@ -0,0 +1,3 @@
export const isRectangleAsSecondaryRoleKey = (width: number, height: number): boolean => {
return width > height * 2.4;
};

View File

@@ -49,6 +49,7 @@ import {
SvgKeyHoverEvent SvgKeyHoverEvent
} from '../../../models/svg-key-events'; } from '../../../models/svg-key-events';
import { RemapInfo } from '../../../models/remap-info'; import { RemapInfo } from '../../../models/remap-info';
import { mapLeftRigthModifierToKeyActionModifier } from '../../../util';
interface NameValuePair { interface NameValuePair {
name: string; name: string;
@@ -181,14 +182,9 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
onCapture(event: SvgKeyboardCaptureEvent): void { onCapture(event: SvgKeyboardCaptureEvent): void {
const keystrokeAction: KeystrokeAction = new KeystrokeAction(); const keystrokeAction: KeystrokeAction = new KeystrokeAction();
const modifiers = event.captured.left.concat(event.captured.right).map(x => x ? 1 : 0);
keystrokeAction.scancode = event.captured.code; keystrokeAction.scancode = event.captured.code;
keystrokeAction.modifierMask = 0; keystrokeAction.modifierMask = mapLeftRigthModifierToKeyActionModifier(event.captured.left, event.captured.right);
for (let i = 0; i < modifiers.length; ++i) {
keystrokeAction.modifierMask |= modifiers[i] << this.mapper.modifierMapper(i);
}
this.store.dispatch( this.store.dispatch(
KeymapActions.saveKey( KeymapActions.saveKey(

View File

@@ -0,0 +1,7 @@
import { KeyModifiers } from 'uhk-common';
export interface KeyModifierModel {
text: string;
value: KeyModifiers;
checked: boolean;
}

View File

@@ -0,0 +1,5 @@
export enum OperatingSystem {
Linux,
Mac,
Windows
}

View File

@@ -1,3 +1,5 @@
import { KeyModifierModel } from './key-modifier-model';
export interface SvgKeyClickEvent { export interface SvgKeyClickEvent {
keyTarget: HTMLElement; keyTarget: HTMLElement;
shiftPressed?: boolean; shiftPressed?: boolean;
@@ -14,8 +16,8 @@ export interface SvgKeyboardKeyClickEvent extends SvgModuleKeyClickEvent {
export interface KeyCaptureData { export interface KeyCaptureData {
code: number; code: number;
left: boolean[]; left: KeyModifierModel[];
right: boolean[]; right: KeyModifierModel[];
} }
export interface SvgKeyCaptureEvent { export interface SvgKeyCaptureEvent {

View File

@@ -1,14 +1,18 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MapperService } from './mapper.service';
import { KeyModifiers } from 'uhk-common';
import { KeyModifierModel } from '../models/key-modifier-model';
@Injectable() @Injectable()
export class CaptureService { export class CaptureService {
private mapping: Map<number, number>; private mapping: Map<number, number>;
private leftModifiers: Map<number, boolean>; private readonly leftModifiers: Map<number, KeyModifierModel>;
private rightModifiers: Map<number, boolean>; private readonly rightModifiers: Map<number, KeyModifierModel>;
constructor() { constructor(private mapper: MapperService) {
this.leftModifiers = new Map<number, boolean>(); this.leftModifiers = new Map<number, KeyModifierModel>();
this.rightModifiers = new Map<number, boolean>(); this.rightModifiers = new Map<number, KeyModifierModel>();
this.mapping = new Map<number, number>(); this.mapping = new Map<number, number>();
} }
@@ -21,26 +25,61 @@ export class CaptureService {
} }
public setModifier(left: boolean, code: number) { public setModifier(left: boolean, code: number) {
return left ? this.leftModifiers.set(code, true) : this.rightModifiers.set(code, true); const map = left ? this.leftModifiers : this.rightModifiers;
map.get(code).checked = true;
} }
public getModifiers(left: boolean) { public getModifiers(left: boolean) {
return left ? this.reMap(this.leftModifiers) : this.reMap(this.rightModifiers); const map = left ? this.leftModifiers : this.rightModifiers;
return Array.from(map.values());
} }
public initModifiers() { public initModifiers() {
this.leftModifiers.set(16, false); // Shift this.leftModifiers.set(16, {
this.leftModifiers.set(17, false); // Ctrl text: 'LShift',
this.leftModifiers.set(18, false); // Alt value: KeyModifiers.leftShift,
this.leftModifiers.set(91, false); // Super checked: false
});
this.leftModifiers.set(17, {
text: 'LCtrl',
value: KeyModifiers.leftCtrl,
checked: false
});
this.leftModifiers.set(18, {
text: this.mapper.getOsSpecificText('LAlt'),
value: KeyModifiers.leftAlt,
checked: false
});
this.leftModifiers.set(91, {
text: this.mapper.getOsSpecificText('LSuper'),
value: KeyModifiers.leftGui,
checked: false
});
this.rightModifiers.set(16, false); // Shift this.rightModifiers.set(16, {
this.rightModifiers.set(17, false); // Ctrl text: 'RShift',
this.rightModifiers.set(18, false); // Alt value: KeyModifiers.rightShift,
this.rightModifiers.set(91, false); // Super checked: false
});
this.rightModifiers.set(17, {
text: 'RCtrl',
value: KeyModifiers.rightCtrl,
checked: false
});
this.rightModifiers.set(18, {
text: this.mapper.getOsSpecificText('RAlt'),
value: KeyModifiers.rightAlt,
checked: false
});
this.rightModifiers.set(91, {
text: this.mapper.getOsSpecificText('RSuper'),
value: KeyModifiers.rightGui,
checked: false
});
} }
public populateMapping () { public populateMapping() {
this.mapping.set(8, 42); // Backspace this.mapping.set(8, 42); // Backspace
this.mapping.set(9, 43); // Tab this.mapping.set(9, 43); // Tab
this.mapping.set(13, 40); // Enter this.mapping.set(13, 40); // Enter
@@ -136,8 +175,4 @@ export class CaptureService {
this.mapping.set(221, 48); // Close bracket this.mapping.set(221, 48); // Close bracket
this.mapping.set(222, 52); // Single quote this.mapping.set(222, 52); // Single quote
} }
private reMap(value: Map<number, boolean>): boolean[] {
return [value.get(16), value.get(17), value.get(91), value.get(18)];
}
} }

View File

@@ -1,22 +1,40 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { KeystrokeType } from 'uhk-common'; import { Store } from '@ngrx/store';
import { KeyModifiers, KeystrokeType, SecondaryRoleAction } from 'uhk-common';
import { Subscription } from 'rxjs/Subscription';
import { AppState, getOperatingSystem } from '../store';
import { OperatingSystem } from '../models/operating-system';
import { KeyModifierModel } from '../models/key-modifier-model';
@Injectable() @Injectable()
export class MapperService { export class MapperService {
private basicScanCodeTextMap: Map<number, string[]>; private basicScanCodeTextMap: Map<number, string[]>;
private mediaScanCodeTextMap: Map<number, string[]>; private mediaScanCodeTextMap: Map<number, string[]>;
private sytemScanCodeTextMap: Map<number, string[]>; private systemScanCodeTextMap: Map<number, string[]>;
private basicScancodeIcons: Map<number, string>; private basicScancodeIcons: Map<number, string>;
private mediaScancodeIcons: Map<number, string>; private mediaScancodeIcons: Map<number, string>;
private systemScancodeIcons: Map<number, string>; private systemScancodeIcons: Map<number, string>;
private nameToFileName: Map<string, string>; private nameToFileName: Map<string, string>;
private osSpecificTexts: Map<string, string>;
private secondaryRoleTexts: Map<number, string>;
constructor() { private operatingSystem: OperatingSystem;
this.initScanCodeTextMap(); private osSubscription: Subscription;
this.initScancodeIcons();
this.initNameToFileNames(); constructor(private store: Store<AppState>) {
this.osSubscription = store
.select(getOperatingSystem)
.subscribe(os => {
this.operatingSystem = os;
this.initOsSpecificText();
this.initScanCodeTextMap();
this.initScancodeIcons();
this.initNameToFileNames();
this.initSecondaryRoleTexts();
});
} }
public scanCodeToText(scanCode: number, type: KeystrokeType = KeystrokeType.basic): string[] { public scanCodeToText(scanCode: number, type: KeystrokeType = KeystrokeType.basic): string[] {
@@ -27,7 +45,7 @@ export class MapperService {
map = this.mediaScanCodeTextMap; map = this.mediaScanCodeTextMap;
break; break;
case KeystrokeType.system: case KeystrokeType.system:
map = this.sytemScanCodeTextMap; map = this.systemScanCodeTextMap;
break; break;
default: default:
map = this.basicScanCodeTextMap; map = this.basicScanCodeTextMap;
@@ -79,7 +97,10 @@ export class MapperService {
} }
public getIcon(iconName: string): string { public getIcon(iconName: string): string {
return 'assets/compiled_sprite.svg#' + this.nameToFileName.get(iconName); const mappedIconName = this.nameToFileName.get(iconName);
if (mappedIconName) {
return 'assets/compiled_sprite.svg#' + mappedIconName;
}
} }
public modifierMapper(x: number) { public modifierMapper(x: number) {
@@ -90,6 +111,87 @@ export class MapperService {
} }
} }
public getOperatingSystem(): OperatingSystem {
return this.operatingSystem;
}
public getOsSpecificText(key: string): string {
const text = this.osSpecificTexts.get(key);
return text ? text : key;
}
public getSecondaryRoleText(secondaryRoleAction: SecondaryRoleAction): string {
return this.secondaryRoleTexts.get(secondaryRoleAction);
}
public getLeftKeyModifiers(): KeyModifierModel[] {
return [
{
text: 'LShift',
value: KeyModifiers.leftShift,
checked: false
},
{
text: 'LCtrl',
value: KeyModifiers.leftCtrl,
checked: false
},
{
text: this.getOsSpecificText('LAlt'),
value: KeyModifiers.leftAlt,
checked: false
},
{
text: this.getOsSpecificText('LSuper'),
value: KeyModifiers.leftGui,
checked: false
}
];
}
public getRightKeyModifiers(): KeyModifierModel[] {
return [
{
text: 'RShift',
value: KeyModifiers.rightShift,
checked: false
},
{
text: 'RCtrl',
value: KeyModifiers.rightCtrl,
checked: false
},
{
text: this.getOsSpecificText('RAlt'),
value: KeyModifiers.rightAlt,
checked: false
},
{
text: this.getOsSpecificText('RSuper'),
value: KeyModifiers.rightGui,
checked: false
}
];
}
private initOsSpecificText(): void {
this.osSpecificTexts = new Map<string, string>();
if (this.operatingSystem === OperatingSystem.Mac) {
this.osSpecificTexts.set('Enter', 'Return');
this.osSpecificTexts.set('Alt', 'Option');
this.osSpecificTexts.set('Super', 'Cmd');
this.osSpecificTexts.set('LSuper', 'LCmd');
this.osSpecificTexts.set('RSuper', 'RCmd');
this.osSpecificTexts.set('LAlt', 'LOption');
this.osSpecificTexts.set('RAlt', 'ROption');
} else if (this.operatingSystem === OperatingSystem.Windows) {
this.osSpecificTexts.set('LSuper', 'LWindows');
this.osSpecificTexts.set('RSuper', 'RWindows');
}
}
// TODO: read the mapping from JSON // TODO: read the mapping from JSON
private initScanCodeTextMap(): void { private initScanCodeTextMap(): void {
this.basicScanCodeTextMap = new Map<number, string[]>(); this.basicScanCodeTextMap = new Map<number, string[]>();
@@ -129,7 +231,7 @@ export class MapperService {
this.basicScanCodeTextMap.set(37, ['8', '*']); this.basicScanCodeTextMap.set(37, ['8', '*']);
this.basicScanCodeTextMap.set(38, ['9', '(']); this.basicScanCodeTextMap.set(38, ['9', '(']);
this.basicScanCodeTextMap.set(39, ['0', ')']); this.basicScanCodeTextMap.set(39, ['0', ')']);
this.basicScanCodeTextMap.set(40, ['Enter']); this.basicScanCodeTextMap.set(40, [this.getOsSpecificText('Enter')]);
this.basicScanCodeTextMap.set(41, ['Esc']); this.basicScanCodeTextMap.set(41, ['Esc']);
this.basicScanCodeTextMap.set(42, ['Backspace']); this.basicScanCodeTextMap.set(42, ['Backspace']);
this.basicScanCodeTextMap.set(43, ['Tab']); this.basicScanCodeTextMap.set(43, ['Tab']);
@@ -177,7 +279,7 @@ export class MapperService {
this.basicScanCodeTextMap.set(85, ['*']); this.basicScanCodeTextMap.set(85, ['*']);
this.basicScanCodeTextMap.set(86, ['-']); this.basicScanCodeTextMap.set(86, ['-']);
this.basicScanCodeTextMap.set(87, ['+']); this.basicScanCodeTextMap.set(87, ['+']);
this.basicScanCodeTextMap.set(88, ['Enter']); this.basicScanCodeTextMap.set(88, [this.getOsSpecificText('Enter')]);
this.basicScanCodeTextMap.set(89, ['end', '1']); this.basicScanCodeTextMap.set(89, ['end', '1']);
this.basicScanCodeTextMap.set(90, ['2']); this.basicScanCodeTextMap.set(90, ['2']);
this.basicScanCodeTextMap.set(91, ['pgdn', '3']); this.basicScanCodeTextMap.set(91, ['pgdn', '3']);
@@ -224,10 +326,10 @@ export class MapperService {
this.mediaScanCodeTextMap.set(394, ['Launch Email Client']); this.mediaScanCodeTextMap.set(394, ['Launch Email Client']);
this.mediaScanCodeTextMap.set(402, ['Launch Calculator']); this.mediaScanCodeTextMap.set(402, ['Launch Calculator']);
this.sytemScanCodeTextMap = new Map<number, string[]>(); this.systemScanCodeTextMap = new Map<number, string[]>();
this.sytemScanCodeTextMap.set(129, ['Power Down']); this.systemScanCodeTextMap.set(129, ['Power Down']);
this.sytemScanCodeTextMap.set(130, ['Sleep']); this.systemScanCodeTextMap.set(130, ['Sleep']);
this.sytemScanCodeTextMap.set(131, ['Wake Up']); this.systemScanCodeTextMap.set(131, ['Wake Up']);
} }
private initScancodeIcons(): void { private initScancodeIcons(): void {
@@ -266,8 +368,12 @@ export class MapperService {
this.nameToFileName.set('switch-keymap', 'icon-kbd__mod--switch-keymap'); this.nameToFileName.set('switch-keymap', 'icon-kbd__mod--switch-keymap');
this.nameToFileName.set('macro', 'icon-icon__macro'); this.nameToFileName.set('macro', 'icon-icon__macro');
this.nameToFileName.set('shift', 'icon-kbd__default--modifier-shift'); this.nameToFileName.set('shift', 'icon-kbd__default--modifier-shift');
this.nameToFileName.set('option', 'icon-kbd__default--modifier-option'); if (this.operatingSystem === OperatingSystem.Mac) {
this.nameToFileName.set('command', 'icon-kbd__default--modifier-command'); this.nameToFileName.set('option', 'icon-kbd__default--modifier-option');
this.nameToFileName.set('command', 'icon-kbd__default--modifier-command');
} else if (this.operatingSystem === OperatingSystem.Windows) {
this.nameToFileName.set('command', 'icon-kbd__default--modifier-windows');
}
this.nameToFileName.set('mouse', 'icon-kbd__mouse'); this.nameToFileName.set('mouse', 'icon-kbd__mouse');
this.nameToFileName.set('left-arrow', 'icon-kbd__mod--arrow-left'); this.nameToFileName.set('left-arrow', 'icon-kbd__mod--arrow-left');
this.nameToFileName.set('right-arrow', 'icon-kbd__mod--arrow-right'); this.nameToFileName.set('right-arrow', 'icon-kbd__mod--arrow-right');
@@ -279,4 +385,18 @@ export class MapperService {
this.nameToFileName.set('scroll-up', 'icon-kbd__mouse--scroll-up'); this.nameToFileName.set('scroll-up', 'icon-kbd__mouse--scroll-up');
} }
private initSecondaryRoleTexts(): void {
this.secondaryRoleTexts = new Map<number, string>();
this.secondaryRoleTexts.set(0, 'LCtrl');
this.secondaryRoleTexts.set(1, 'LShift');
this.secondaryRoleTexts.set(2, 'LAlt');
this.secondaryRoleTexts.set(3, 'LSuper');
this.secondaryRoleTexts.set(4, 'RCtrl');
this.secondaryRoleTexts.set(5, 'RShift');
this.secondaryRoleTexts.set(6, 'RAlt');
this.secondaryRoleTexts.set(7, 'RSuper');
this.secondaryRoleTexts.set(8, 'Mod');
this.secondaryRoleTexts.set(9, 'Fn');
this.secondaryRoleTexts.set(10, 'Mouse');
}
} }

View File

@@ -59,6 +59,7 @@ import {
SvgMouseScrollKeyComponent, SvgMouseScrollKeyComponent,
SvgMouseSpeedKeyComponent, SvgMouseSpeedKeyComponent,
SvgOneLineTextKeyComponent, SvgOneLineTextKeyComponent,
SvgSecondaryRoleComponent,
SvgSingleIconKeyComponent, SvgSingleIconKeyComponent,
SvgSwitchKeymapKeyComponent, SvgSwitchKeymapKeyComponent,
SvgTextIconKeyComponent, SvgTextIconKeyComponent,
@@ -186,7 +187,8 @@ import { HelpPageComponent } from './components/agent/help-page/help-page.compon
FileUploadComponent, FileUploadComponent,
AutoGrowInputComponent, AutoGrowInputComponent,
HelpPageComponent, HelpPageComponent,
ExternalUrlDirective ExternalUrlDirective,
SvgSecondaryRoleComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

View File

@@ -10,6 +10,7 @@ import * as fromAppUpdate from './reducers/app-update.reducer';
import * as autoUpdateSettings from './reducers/auto-update-settings'; import * as autoUpdateSettings from './reducers/auto-update-settings';
import * as fromApp from './reducers/app.reducer'; import * as fromApp from './reducers/app.reducer';
import * as fromDevice from './reducers/device'; import * as fromDevice from './reducers/device';
import * as fromSelectors from './reducers/selectors';
import { initProgressButtonState } from './reducers/progress-button-state'; import { initProgressButtonState } from './reducers/progress-button-state';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { RouterStateUrl } from './router-util'; import { RouterStateUrl } from './router-util';
@@ -52,6 +53,7 @@ export const getKeyboardLayout = createSelector(appState, fromApp.getKeyboardLay
export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded); export const deviceConfigurationLoaded = createSelector(appState, fromApp.deviceConfigurationLoaded);
export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo); export const getAgentVersionInfo = createSelector(appState, fromApp.getAgentVersionInfo);
export const getPrivilegePageState = createSelector(appState, fromApp.getPrivilagePageState); export const getPrivilegePageState = createSelector(appState, fromApp.getPrivilagePageState);
export const getOperatingSystem = createSelector(appState, fromSelectors.getOperatingSystem);
export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows); export const runningOnNotSupportedWindows = createSelector(appState, fromApp.runningOnNotSupportedWindows);
export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs); export const firmwareUpgradeAllowed = createSelector(runningOnNotSupportedWindows, notSupportedOs => !notSupportedOs);

View File

@@ -0,0 +1,29 @@
import { OperatingSystem } from '../../models/operating-system';
import { State } from './app.reducer';
export const getOperatingSystem = (state: State): OperatingSystem => {
if (state.runningInElectron) {
switch (state.platform) {
case 'darwin':
return OperatingSystem.Mac;
case 'win32':
return OperatingSystem.Windows;
default:
return OperatingSystem.Linux;
}
}
const platform = navigator.platform.toLowerCase();
if (platform.indexOf('mac') > -1) {
return OperatingSystem.Mac;
}
if (platform.indexOf('win') > -1) {
return OperatingSystem.Windows;
}
return OperatingSystem.Linux;
};

View File

@@ -0,0 +1 @@
export * from './get-operating-system.selector';

View File

@@ -1,4 +1,5 @@
export * from './find-new-item'; export * from './find-new-item';
export * from './html-helper'; export * from './html-helper';
export * from './key-modifier-model-mapper';
export * from './validators'; export * from './validators';
export * from './version-helper'; export * from './version-helper';

View File

@@ -0,0 +1,13 @@
import { KeyModifierModel } from '../models/key-modifier-model';
export const mapLeftRigthModifierToKeyActionModifier = (left: KeyModifierModel[], right: KeyModifierModel[]): number => {
const modifiers = [...left, ...right];
let result = 0;
for (const modifier of modifiers) {
if (modifier.checked) {
result |= modifier.value;
}
}
return result;
};

View File

@@ -1,13 +1,21 @@
#!/usr/bin/env node #!/usr/bin/env node
const uhk = require('./uhk'); const uhk = require('./uhk');
const device = uhk.getUhkDevice();
const eepromTransferType = process.argv[2]; (async function() {
const eepromTransfer = uhk.eepromTransfer[eepromTransferType]; const operationArg = process.argv[2];
const operation = uhk.eepromOperations[operationArg];
if (operation === undefined) {
console.error(`Invalid operation: Gotta provide one of ${Object.keys(uhk.eepromOperations).join(', ')}`);
process.exit(1);
}
if (eepromTransfer === undefined) { const bufferIdArg = process.argv[3];
console.error(`Gotta provide one of ${Object.keys(uhk.eepromTransfer).join(', ')}`); const bufferId = uhk.configBufferIds[bufferIdArg]
process.exit(1); if (bufferId === undefined) {
} console.error(`Invalid bufferId: Gotta provide one of ${Object.keys(uhk.configBufferIds).join(', ')}`);
process.exit(1);
// const buffer = await uhk.writeDevice(device, [uhk.usbCommands.launchEepromTransfer, eepromTransfer.operation, eepromTransfer.configBuffer]); }
const buffer = await uhk.launchEepromTransfer(device, operation, bufferId);
})();