Skip to content

Commit 8a0fd9e

Browse files
mondalacicursoragentert78gb
authored
feat: show macro key assignments on the macro page (#3002)
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> Co-authored-by: Kiss Róbert <ert78gb@gmail.com>
1 parent ae17655 commit 8a0fd9e

27 files changed

Lines changed: 496 additions & 53 deletions

.cursor/rules/initialize-state-and-variables.mdc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
---
2-
description: Initialize variables and NgRx state with concrete defaults
3-
globs: **/*.{ts,tsx}
4-
alwaysApply: false
2+
description: Initialize variables and NgRx state with concrete defaults; avoid optional chaining smell
3+
alwaysApply: true
54
---
65

76
# Initialize variables and NgRx state
87

98
Initialize primitives and objects at declaration whenever a sensible default exists. The goal is typed, predictable values—not defensive `?.` and `??` scattered through selectors, templates, and components.
109

10+
Treat widespread optional chaining (`?.`) and nullish coalescing (`??`) as a code smell: they often hide missing initialization or the wrong selector. Prefer concrete defaults and explicit control flow (`if` / early return) so optionality is intentional, not lazy.
11+
1112
## General TypeScript
1213

1314
- Give local variables an initial value when one is known at declaration time.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
description: Prefer options objects when a function needs more than three parameters
3+
alwaysApply: true
4+
---
5+
6+
# Function parameters
7+
8+
Functions and methods with more than three parameters are hard to maintain. Prefer an options/payload object once the call needs four or more values.
9+
10+
```typescript
11+
// ❌ BAD
12+
createKeymapWithMacroAssignment(abbr, name, layerId, moduleId, keyId, macroId)
13+
14+
// ✅ GOOD
15+
createKeymapWithMacroAssignment({ abbreviation, name, layerId, moduleId, keyId, macroId })
16+
```
17+
18+
Three-or-fewer scalar params are fine when the meaning stays obvious at the call site.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description: Prefer Bootstrap and existing styles over new custom CSS
3+
alwaysApply: true
4+
---
5+
6+
# Prefer existing styles
7+
8+
Do not add custom CSS when Bootstrap utilities or existing shared styles already cover spacing, typography, and layout.
9+
10+
## Prefer
11+
12+
- Bootstrap utilities: `my-2`, `mb-3`, `me-1`, `small`, `text-nowrap`, `float-end`, …
13+
- Patterns already used nearby (e.g. `back-to` uses `class="my-2"`)
14+
15+
## Avoid
16+
17+
- New BEM blocks for one-off spacing/font-size/weight that utilities already express
18+
- Growing component SCSS with unexplained overrides that later confuse deletions
19+
20+
```html
21+
<!-- ❌ BAD -->
22+
<div class="macro__assignments">…</div>
23+
<!-- plus .macro__assignments { font-size: 0.875rem; padding-top: 1.25em; } -->
24+
25+
<!-- ✅ GOOD -->
26+
<div class="my-2 small">…</div>
27+
```
28+
29+
Only add custom CSS when no existing utility/shared rule fits, and keep it minimal.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
description: Prefer smart containers / dumb leaf components with store-side calculations
3+
alwaysApply: true
4+
---
5+
6+
# Smart / dumb components
7+
8+
Follow the smart/dumb (container/presentational) pattern already used in this codebase.
9+
10+
## Responsibilities
11+
12+
- **Store / selectors / smart parents**: derive view models, sort/filter/map domain data, combine streams.
13+
- **Leaf components**: receive `@Input()`s, render them, emit `@Output()`s / dispatch obvious user intents. Avoid reading the store just to compute display data.
14+
15+
```typescript
16+
// ❌ BAD — deep leaf component selects and calculates
17+
class MacroHeaderComponent {
18+
constructor(store: Store) {
19+
store.select(getKeymaps).subscribe(keymaps => this.assignments = map(keymaps));
20+
}
21+
}
22+
23+
// ✅ GOOD — smart parent selects; leaf renders
24+
// macro-edit (smart)
25+
assignments$ = this.store.select(getSelectedMacroKeyAssignments);
26+
27+
// template
28+
<macro-header [macro]="macro" [assignments]="assignments$ | async"></macro-header>
29+
30+
// macro-header (dumb regarding this data)
31+
@Input() assignments: MacroKeyAssignmentViewModel[] = [];
32+
```
33+
34+
Existing leaf components that already inject `Store` only to `dispatch` actions are acceptable when inherited; do not expand them with new selectors or calculation logic.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
description: Keep UI and presentation code out of uhk-common
3+
alwaysApply: true
4+
---
5+
6+
# Package boundaries: uhk-common vs uhk-web
7+
8+
`uhk-common` is for shared, UI-agnostic domain logic (config serializers, USB helpers, pure data transforms). Do **not** put presentation-oriented helpers there.
9+
10+
## Belong in `uhk-web` (or uhk-agent), not `uhk-common`
11+
12+
- Display labels, breadcrumbs, menu grouping, view-model mappers
13+
- Anything that formats data for Angular templates
14+
- Code that only Agent UI consumers need, even if it looks "pure"
15+
16+
## Belong in `uhk-common`
17+
18+
- Config/device models and serialization
19+
- Algorithms that both Electron main and renderer need without UI assumptions
20+
21+
```typescript
22+
// ❌ BAD — lives in uhk-common only because tests were convenient there
23+
export function findMacroKeyAssignments(...): MacroKeyAssignment[] { /* for macro page UI */ }
24+
25+
// ✅ GOOD — presentation helper next to the UI that uses it
26+
// packages/uhk-web/src/app/util/find-macro-key-assignments.ts
27+
```
28+
29+
If you want unit tests for UI helpers and `uhk-web` has no runner yet, still put the code in `uhk-web` and add tests there; do not grow UI surface area in `uhk-common` for convenience.

packages/uhk-web/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
"server:renderer": "ng build --project=uhk-renderer --watch",
1212
"lint": "run-p -snc lint:*",
1313
"lint:ng": "ng lint",
14-
"lint:style": "stylelint \"src/**/*.scss\""
14+
"lint:style": "stylelint \"src/**/*.scss\"",
15+
"test": "node --loader=ts-node/esm --test \"**/*.test.ts\""
1516
},
1617
"private": true,
18+
"type": "module",
1719
"license": "See in LICENSE",
1820
"devDependencies": {
1921
"@angular-devkit/build-angular": "20.3.28",

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 as string, 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.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
[macro]="macro"
1111
[isNew]="isNew$ | async"
1212
[maxMacroCountReached]="maxMacroCountReached$ | async"
13+
[assignments]="assignments"
1314
></macro-header>
1415
<macro-list
1516
[macro]="macro"

0 commit comments

Comments
 (0)