Skip to content

Commit 30b72b3

Browse files
Show macro key assignments on the macro page.
List where each macro is used with links back to the assigned key, and avoid treating Jump to macro navigation as a config change. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a32fd88 commit 30b72b3

17 files changed

Lines changed: 344 additions & 35 deletions
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, it } from 'node:test';
2+
3+
import { Keymap, Layer, LayerName, Module, PlayMacroAction } from '../config-serializer/index.js';
4+
import { findMacroKeyAssignments } from './find-macro-key-assignments.js';
5+
6+
function createKeymapWithMacroAssignment(
7+
abbreviation: string,
8+
name: string,
9+
layerId: LayerName,
10+
moduleId: number,
11+
keyId: number,
12+
macroId: number
13+
): Keymap {
14+
const keyActions = [];
15+
keyActions[keyId] = Object.assign(new PlayMacroAction(), { macroId });
16+
17+
const keymap = new Keymap();
18+
keymap.abbreviation = abbreviation;
19+
keymap.name = name;
20+
keymap.layers = [
21+
Object.assign(new Layer(), {
22+
id: layerId,
23+
modules: [
24+
Object.assign(new Module(), {
25+
id: moduleId,
26+
keyActions,
27+
}),
28+
],
29+
}),
30+
];
31+
32+
return keymap;
33+
}
34+
35+
describe('findMacroKeyAssignments', () => {
36+
it('returns assignments for the requested macro', ({ assert }) => {
37+
const keymaps = [
38+
createKeymapWithMacroAssignment('QWR', 'QWERTY for PC', LayerName.fn, 0, 2, 5),
39+
createKeymapWithMacroAssignment('DVO', 'Dvorak', LayerName.base, 1, 0, 5),
40+
createKeymapWithMacroAssignment('QWR', 'QWERTY for PC', LayerName.mod, 0, 1, 9),
41+
];
42+
43+
const assignments = findMacroKeyAssignments(keymaps, 5);
44+
45+
assert.equal(assignments.length, 2);
46+
assert.deepEqual(assignments[0], {
47+
keymapAbbreviation: 'QWR',
48+
keymapName: 'QWERTY for PC',
49+
layerId: LayerName.fn,
50+
moduleId: 0,
51+
keyId: 2,
52+
});
53+
assert.deepEqual(assignments[1], {
54+
keymapAbbreviation: 'DVO',
55+
keymapName: 'Dvorak',
56+
layerId: LayerName.base,
57+
moduleId: 1,
58+
keyId: 0,
59+
});
60+
});
61+
62+
it('returns an empty list when the macro is not assigned', ({ assert }) => {
63+
const keymaps = [
64+
createKeymapWithMacroAssignment('QWR', 'QWERTY for PC', LayerName.fn, 0, 2, 5),
65+
];
66+
67+
assert.deepEqual(findMacroKeyAssignments(keymaps, 99), []);
68+
});
69+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Keymap, LayerName, PlayMacroAction } from '../config-serializer/index.js';
2+
3+
export interface MacroKeyAssignment {
4+
keymapAbbreviation: string;
5+
keymapName: string;
6+
layerId: LayerName;
7+
moduleId: number;
8+
keyId: number;
9+
}
10+
11+
export function findMacroKeyAssignments(keymaps: Keymap[], macroId: number): MacroKeyAssignment[] {
12+
const assignments: MacroKeyAssignment[] = [];
13+
14+
for (const keymap of keymaps) {
15+
for (const layer of keymap.layers) {
16+
for (const module of layer.modules) {
17+
for (let keyId = 0; keyId < module.keyActions.length; keyId++) {
18+
const keyAction = module.keyActions[keyId];
19+
20+
if (!(keyAction instanceof PlayMacroAction) || keyAction.macroId !== macroId) {
21+
continue;
22+
}
23+
24+
assignments.push({
25+
keymapAbbreviation: keymap.abbreviation,
26+
keymapName: keymap.name,
27+
layerId: layer.id,
28+
moduleId: module.id,
29+
keyId,
30+
});
31+
}
32+
}
33+
}
34+
}
35+
36+
return assignments;
37+
}

packages/uhk-common/src/util/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './convert-array-to-hex-string.js';
66
export * from './create-md5-hash.js';
77
export * from './date-formatter.js';
88
export * from './disable-agent-upgrade-protection.js';
9+
export * from './find-macro-key-assignments.js';
910
export * from './find-uhk-module-by-id.js';
1011
export * from './get-formatted-timestamp.js';
1112
export * from './get-md5-hash-from-file-name.js';

packages/uhk-web/src/app/components/back-to/back-to.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import { Subscription } from 'rxjs';
77
standalone: false,
88
template: `
99
<div *ngIf="backUrl" class="my-2">
10-
Back to <a [routerLink]="[backUrl]" [queryParams]="queryParams">{{backText}}</a>
10+
Back to <a [routerLink]="[backUrl]" [queryParams]="queryParams">{{ backText }}</a>{{ backSuffix }}
1111
</div>
1212
`,
1313
})
1414
export class BackToComponent implements OnDestroy {
1515
backUrl: string | undefined;
1616
backText: string;
17+
backSuffix = '';
1718
queryParams = {};
1819

1920
private routeSubscription: Subscription;
@@ -25,6 +26,7 @@ export class BackToComponent implements OnDestroy {
2526
const backUrl = new URL(params.backUrl, window.location.origin);
2627
this.backUrl = backUrl.pathname;
2728
this.backText = params.backText;
29+
this.backSuffix = params.backSuffix ?? '';
2830
this.queryParams = {};
2931
for (const key of backUrl.searchParams.keys()) {
3032
this.queryParams[key] = backUrl.searchParams.get(key);

packages/uhk-web/src/app/components/keymap/add/keymap-add.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ export class KeymapAddComponent implements OnDestroy, OnInit {
6868
navigateToModuleSettings(moduleId: number): void {
6969
this.store.dispatch(new NavigateToModuleSettings({
7070
backUrl: `/add-keymap/${encodeURIComponent(this.keymap.abbreviation)}`,
71-
backText: `new "${this.keymap.name}" keymap`,
71+
backText: `new ${this.keymap.name}`,
72+
backSuffix: ' keymap',
7273
moduleId
7374
}));
7475
}

packages/uhk-web/src/app/components/keymap/edit/keymap-edit.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ export class KeymapEditComponent implements OnDestroy {
161161
navigateToModuleSettings(moduleId: number): void {
162162
this.store.dispatch(new NavigateToModuleSettings({
163163
backUrl: `/keymap/${encodeURIComponent(this.keymap.abbreviation)}?layer=${this.selectedLayer}`,
164-
backText: `"${this.keymap.name}" keymap`,
164+
backText: this.keymap.name,
165+
backSuffix: ' keymap',
165166
moduleId,
166167
}));
167168
}

packages/uhk-web/src/app/components/macro/edit/macro-edit.component.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,6 @@ export class MacroEditComponent implements OnDestroy {
5959
left: 100,
6060
right: 0
6161
};
62-
63-
private backUrl: string;
64-
private backUrlText: string;
6562
private subscriptions = new Subscription();
6663

6764
constructor(private store: Store<AppState>,
@@ -90,9 +87,6 @@ export class MacroEditComponent implements OnDestroy {
9087
this.smartMacroDocUrl$ = store.select(selectSmartMacroDocUrl);
9188
this.smartMacroPanelVisibility$ = store.select(getSmartMacroPanelVisibility);
9289
this.subscriptions.add(this.route.queryParams.subscribe(params => {
93-
this.backUrl = params.backUrl;
94-
this.backUrlText = params.backText;
95-
9690
if (params.actionIndex) {
9791
if (params.actionIndex === 'new') {
9892
this.selectedMacroActionIdModel = {
@@ -150,8 +144,6 @@ export class MacroEditComponent implements OnDestroy {
150144
this.router.navigate([], {
151145
queryParams: {
152146
actionIndex: model?.id,
153-
backText: this.backUrlText,
154-
backUrl: this.backUrl,
155147
inlineEdit: model?.inlineEdit
156148
}
157149
});
@@ -173,10 +165,7 @@ export class MacroEditComponent implements OnDestroy {
173165
private hideActiveEditor(): void {
174166
if (!this.selectedMacroActionIdModel?.inlineEdit) {
175167
this.router.navigate([], {
176-
queryParams: {
177-
backText: this.backUrlText,
178-
backUrl: this.backUrl,
179-
},
168+
queryParams: {},
180169
});
181170
}
182171
}

packages/uhk-web/src/app/components/macro/header/macro-header.component.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,11 @@
1818
placement="bottom-right"
1919
(click)="duplicateMacro()"></fa-icon>
2020
</uhk-header>
21-
<back-to/>
21+
<div *ngIf="assignments.length" class="macro__assignments">
22+
<span class="macro__assignments-label">Used on:</span>
23+
<ng-container *ngFor="let assignment of assignments; let last = last">
24+
<a class="macro__assignment-link"
25+
[routerLink]="['/keymap', assignment.keymapAbbreviation]"
26+
[queryParams]="getKeymapQueryParams(assignment)">{{ assignment.label }}</a><span *ngIf="!last" class="macro__assignment-separator">, </span>
27+
</ng-container>
28+
</div>

packages/uhk-web/src/app/components/macro/header/macro-header.component.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1+
:host {
2+
display: block;
3+
}
14

25
.macro {
6+
&__assignments {
7+
font-size: 0.875rem;
8+
line-height: 1.5;
9+
padding-top: 1.25em;
10+
}
11+
12+
&__assignments-label {
13+
margin-right: 0.35em;
14+
white-space: nowrap;
15+
}
16+
17+
&__assignment-link {
18+
text-decoration: underline;
19+
white-space: nowrap;
20+
}
21+
22+
&__assignment-separator {
23+
white-space: pre;
24+
}
25+
326
&__remove {
427
font-size: 0.75em;
528
top: 7px;

0 commit comments

Comments
 (0)