use-kbd 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-kbd",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Keyboard-first UX for React: action registration, shortcuts modal, omnibar, and sequences",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -48,19 +48,17 @@
48
48
  "react": "^18.3.1",
49
49
  "tsup": "^8.3.5",
50
50
  "typescript": "^5.7.2",
51
- "typescript-eslint": "^8.50.1",
52
- "vitest": "^2.1.8"
51
+ "typescript-eslint": "^8.50.1"
53
52
  },
54
53
  "peerDependencies": {
55
- "react": ">=18.0.0"
56
- },
57
- "dependencies": {
58
- "@rdub/base": "^0.8.1"
54
+ "react": ">=18.0.0",
55
+ "react-dom": ">=18.0.0"
59
56
  },
57
+ "dependencies": {},
60
58
  "scripts": {
61
59
  "build": "tsup",
62
60
  "dev": "tsup --watch",
63
- "test": "vitest",
61
+ "test": "pnpm -C site test",
64
62
  "lint": "eslint src"
65
63
  }
66
64
  }
package/src/styles.css CHANGED
@@ -9,37 +9,44 @@
9
9
  * }
10
10
  */
11
11
 
12
- /* === CSS Custom Properties === */
13
- .kbd-modal,
14
- .kbd-omnibar {
15
- /* Colors */
16
- --kbd-bg: var(--kbd-bg, #ffffff);
17
- --kbd-bg-secondary: var(--kbd-bg-secondary, #f9fafb);
18
- --kbd-text: var(--kbd-text, #1f2937);
19
- --kbd-text-secondary: var(--kbd-text-secondary, #6b7280);
20
- --kbd-border: var(--kbd-border, #e5e7eb);
21
- --kbd-accent: var(--kbd-accent, #3b82f6);
22
- --kbd-accent-hover: var(--kbd-accent-hover, #2563eb);
12
+ /* === CSS Custom Properties (defaults) === */
13
+ :root {
14
+ /* Colors - light mode defaults */
15
+ --kbd-bg: #ffffff;
16
+ --kbd-bg-secondary: #f9fafb;
17
+ --kbd-text: #1f2937;
18
+ --kbd-text-secondary: #6b7280;
19
+ --kbd-border: #e5e7eb;
20
+ --kbd-accent: #3b82f6;
21
+ --kbd-accent-hover: #2563eb;
23
22
 
24
23
  /* Conflict/warning colors */
25
- --kbd-conflict: var(--kbd-conflict, #ef4444);
26
- --kbd-conflict-bg: var(--kbd-conflict-bg, #fef2f2);
27
- --kbd-warning: var(--kbd-warning, #f59e0b);
28
- --kbd-warning-bg: var(--kbd-warning-bg, #fef3c7);
24
+ --kbd-conflict: #ef4444;
25
+ --kbd-conflict-bg: #fef2f2;
26
+ --kbd-warning: #f59e0b;
27
+ --kbd-warning-bg: #fef3c7;
28
+
29
+ /* Timeout bar */
30
+ --kbd-timeout-bar: #10b981;
29
31
 
30
32
  /* Kbd element */
31
- --kbd-kbd-bg: var(--kbd-kbd-bg, #f3f4f6);
32
- --kbd-kbd-border: var(--kbd-kbd-border, #d1d5db);
33
- --kbd-kbd-text: var(--kbd-kbd-text, #374151);
33
+ --kbd-kbd-bg: #f3f4f6;
34
+ --kbd-kbd-border: #d1d5db;
35
+ --kbd-kbd-text: #374151;
34
36
 
35
37
  /* Spacing & sizing */
36
- --kbd-radius: var(--kbd-radius, 8px);
37
- --kbd-radius-sm: var(--kbd-radius-sm, 4px);
38
- --kbd-gap: var(--kbd-gap, 8px);
39
- --kbd-padding: var(--kbd-padding, 16px);
38
+ --kbd-radius: 8px;
39
+ --kbd-radius-sm: 4px;
40
+ --kbd-gap: 8px;
41
+ --kbd-padding: 16px;
40
42
 
41
43
  /* Animation */
42
- --kbd-transition: var(--kbd-transition, 150ms ease);
44
+ --kbd-transition: 150ms ease;
45
+
46
+ /* Sequence modal */
47
+ --kbd-sequence-max-height: 300px;
48
+ --kbd-sequence-row-padding: 8px 12px;
49
+ --kbd-sequence-row-gap: 8px;
43
50
  }
44
51
 
45
52
  /* === Backdrop === */
@@ -79,12 +86,34 @@
79
86
  font-weight: 600;
80
87
  }
81
88
 
89
+ .kbd-modal-header-buttons {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 8px;
93
+ }
94
+
95
+ .kbd-reset-btn {
96
+ background: none;
97
+ border: 1px solid var(--kbd-border);
98
+ border-radius: var(--kbd-radius-sm);
99
+ padding: 4px 10px;
100
+ font-size: 0.875rem;
101
+ cursor: pointer;
102
+ color: var(--kbd-text-secondary);
103
+ transition: all var(--kbd-transition);
104
+ }
105
+
106
+ .kbd-reset-btn:hover {
107
+ color: var(--kbd-text);
108
+ border-color: var(--kbd-text-secondary);
109
+ }
110
+
82
111
  .kbd-modal-close {
83
112
  background: none;
84
113
  border: none;
85
- font-size: 1.5rem;
114
+ font-size: 1.25rem;
86
115
  cursor: pointer;
87
- padding: 4px;
116
+ padding: 4px 8px;
88
117
  line-height: 1;
89
118
  color: var(--kbd-text-secondary);
90
119
  transition: color var(--kbd-transition);
@@ -136,6 +165,7 @@
136
165
 
137
166
  /* === Kbd element === */
138
167
  .kbd-kbd {
168
+ position: relative;
139
169
  display: inline-flex;
140
170
  align-items: center;
141
171
  gap: 2px;
@@ -202,12 +232,47 @@
202
232
  flex-shrink: 0;
203
233
  }
204
234
 
235
+ /* === Key icons (arrows, enter, etc.) === */
236
+ .kbd-key-icon {
237
+ width: 14px;
238
+ height: 14px;
239
+ flex-shrink: 0;
240
+ }
241
+
242
+ /* === Digit placeholder (for \d and \d+ patterns) === */
243
+ .kbd-placeholder {
244
+ font-family: monospace;
245
+ font-weight: 600;
246
+ color: var(--kbd-text);
247
+ background: var(--kbd-bg-secondary);
248
+ border: 1px solid var(--kbd-border);
249
+ border-radius: var(--kbd-radius-sm);
250
+ padding: 0 3px;
251
+ margin: 0 1px;
252
+ }
253
+
205
254
  /* === Sequence separator === */
206
255
  .kbd-sequence-sep {
207
256
  color: var(--kbd-text-secondary);
208
257
  margin: 0 1px;
209
258
  }
210
259
 
260
+ /* === Clickable kbd === */
261
+ .kbd-clickable {
262
+ cursor: pointer;
263
+ transition: all var(--kbd-transition);
264
+ }
265
+
266
+ .kbd-clickable:hover {
267
+ background: var(--kbd-accent);
268
+ color: white;
269
+ }
270
+
271
+ .kbd-clickable:focus-visible {
272
+ outline: 2px solid var(--kbd-accent);
273
+ outline-offset: 2px;
274
+ }
275
+
211
276
  /* === Add binding button === */
212
277
  .kbd-add-btn {
213
278
  background: none;
@@ -227,23 +292,39 @@
227
292
 
228
293
  /* === Remove binding button === */
229
294
  .kbd-remove-btn {
230
- background: none;
295
+ position: absolute;
296
+ top: -6px;
297
+ right: -6px;
298
+ display: flex;
299
+ align-items: center;
300
+ justify-content: center;
301
+ background: var(--kbd-conflict, #dc2626);
231
302
  border: none;
303
+ border-radius: 50%;
232
304
  padding: 0;
233
- margin-left: 2px;
234
- font-size: 0.875rem;
235
- color: var(--kbd-text-secondary);
305
+ width: 16px;
306
+ height: 16px;
307
+ font-size: 12px;
308
+ font-weight: bold;
309
+ line-height: 1;
310
+ color: #fff;
236
311
  cursor: pointer;
237
312
  opacity: 0;
238
- transition: opacity var(--kbd-transition);
313
+ transform: scale(0.8);
314
+ transition:
315
+ opacity var(--kbd-transition),
316
+ transform var(--kbd-transition),
317
+ background var(--kbd-transition);
239
318
  }
240
319
 
241
- .kbd-kbd:hover .kbd-remove-btn {
320
+ .kbd-kbd:hover .kbd-remove-btn,
321
+ .kbd-kbd:focus-within .kbd-remove-btn {
242
322
  opacity: 1;
323
+ transform: scale(1);
243
324
  }
244
325
 
245
326
  .kbd-remove-btn:hover {
246
- color: var(--kbd-conflict);
327
+ background: #b91c1c;
247
328
  }
248
329
 
249
330
  /* === Timeout bar === */
@@ -251,8 +332,8 @@
251
332
  position: absolute;
252
333
  bottom: 0;
253
334
  left: 0;
254
- height: 2px;
255
- background-color: var(--kbd-accent);
335
+ height: 3px;
336
+ background-color: var(--kbd-timeout-bar);
256
337
  animation: kbd-timeout linear forwards;
257
338
  }
258
339
 
@@ -362,18 +443,6 @@
362
443
  }
363
444
 
364
445
  .kbd-sequence {
365
- /* Inherit CSS custom properties */
366
- --kbd-bg: var(--kbd-bg, #ffffff);
367
- --kbd-bg-secondary: var(--kbd-bg-secondary, #f9fafb);
368
- --kbd-text: var(--kbd-text, #1f2937);
369
- --kbd-text-secondary: var(--kbd-text-secondary, #6b7280);
370
- --kbd-border: var(--kbd-border, #e5e7eb);
371
- --kbd-kbd-bg: var(--kbd-kbd-bg, #f3f4f6);
372
- --kbd-kbd-border: var(--kbd-kbd-border, #d1d5db);
373
- --kbd-kbd-text: var(--kbd-kbd-text, #374151);
374
- --kbd-radius: var(--kbd-radius, 8px);
375
- --kbd-accent: var(--kbd-accent, #3b82f6);
376
-
377
446
  background-color: var(--kbd-bg);
378
447
  border: 1px solid var(--kbd-border);
379
448
  border-radius: 12px;
@@ -447,8 +516,8 @@
447
516
  padding: 12px 16px;
448
517
  display: flex;
449
518
  flex-direction: column;
450
- gap: 8px;
451
- max-height: 300px;
519
+ gap: var(--kbd-sequence-row-gap);
520
+ max-height: var(--kbd-sequence-max-height);
452
521
  overflow-y: auto;
453
522
  }
454
523
 
@@ -456,14 +525,19 @@
456
525
  display: flex;
457
526
  align-items: center;
458
527
  gap: 8px;
459
- padding: 8px 12px;
460
- background-color: var(--kbd-bg-secondary);
528
+ padding: var(--kbd-sequence-row-padding);
461
529
  border-radius: 6px;
462
530
  transition: background-color 0.1s ease;
463
531
  }
464
532
 
465
533
  .kbd-sequence-completion:hover {
466
- background-color: var(--kbd-bg);
534
+ background-color: var(--kbd-bg-secondary);
535
+ }
536
+
537
+ .kbd-sequence-completion:first-child {
538
+ background-color: var(--kbd-bg-secondary);
539
+ outline: 2px solid var(--kbd-accent);
540
+ outline-offset: -2px;
467
541
  }
468
542
 
469
543
  .kbd-sequence-arrow {
@@ -508,19 +582,135 @@
508
582
  text-align: center;
509
583
  }
510
584
 
585
+ /* === Lookup Modal === */
586
+ .kbd-lookup-backdrop {
587
+ position: fixed;
588
+ inset: 0;
589
+ display: flex;
590
+ align-items: flex-start;
591
+ justify-content: center;
592
+ padding-top: 15vh;
593
+ z-index: 9999;
594
+ background-color: rgba(0, 0, 0, 0.5);
595
+ animation: kbd-fade-in 0.1s ease;
596
+ }
597
+
598
+ .kbd-lookup {
599
+ background-color: var(--kbd-bg);
600
+ border-radius: var(--kbd-radius);
601
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
602
+ width: 90vw;
603
+ max-width: 500px;
604
+ max-height: 70vh;
605
+ display: flex;
606
+ flex-direction: column;
607
+ overflow: hidden;
608
+ }
609
+
610
+ .kbd-lookup-header {
611
+ padding: 12px 16px;
612
+ border-bottom: 1px solid var(--kbd-border);
613
+ display: flex;
614
+ flex-direction: column;
615
+ gap: 8px;
616
+ }
617
+
618
+ .kbd-lookup-search {
619
+ display: flex;
620
+ align-items: center;
621
+ min-height: 32px;
622
+ }
623
+
624
+ .kbd-lookup-placeholder {
625
+ color: var(--kbd-text-secondary);
626
+ font-size: 0.875rem;
627
+ }
628
+
629
+ .kbd-lookup-hint {
630
+ font-size: 0.75rem;
631
+ color: var(--kbd-text-secondary);
632
+ }
633
+
634
+ .kbd-lookup-results {
635
+ flex: 1;
636
+ overflow-y: auto;
637
+ padding: 8px;
638
+ }
639
+
640
+ .kbd-lookup-result {
641
+ display: flex;
642
+ align-items: center;
643
+ gap: 12px;
644
+ padding: 8px 12px;
645
+ border-radius: 6px;
646
+ cursor: pointer;
647
+ transition: background-color 0.1s ease;
648
+ }
649
+
650
+ .kbd-lookup-result:hover,
651
+ .kbd-lookup-result.selected {
652
+ background-color: var(--kbd-bg-secondary);
653
+ }
654
+
655
+ .kbd-lookup-result.selected {
656
+ outline: 2px solid var(--kbd-accent);
657
+ outline-offset: -2px;
658
+ }
659
+
660
+ .kbd-lookup-labels {
661
+ flex: 1;
662
+ font-size: 0.875rem;
663
+ color: var(--kbd-text);
664
+ }
665
+
666
+ .kbd-lookup-empty {
667
+ padding: 24px;
668
+ text-align: center;
669
+ color: var(--kbd-text-secondary);
670
+ font-size: 0.875rem;
671
+ }
672
+
673
+ .kbd-lookup-continuations {
674
+ padding: 8px 16px;
675
+ border-top: 1px solid var(--kbd-border);
676
+ display: flex;
677
+ align-items: center;
678
+ gap: 8px;
679
+ flex-wrap: wrap;
680
+ }
681
+
682
+ .kbd-lookup-continuations-label {
683
+ font-size: 0.75rem;
684
+ color: var(--kbd-text-secondary);
685
+ }
686
+
687
+ .kbd-kbd.kbd-small {
688
+ font-size: 0.7rem;
689
+ padding: 2px 5px;
690
+ }
691
+
511
692
  /* === Dark mode preset === */
512
- [data-theme="dark"] .kbd-modal,
513
- [data-theme="dark"] .kbd-omnibar,
514
- [data-theme="dark"] .kbd-sequence,
515
- .dark .kbd-modal,
516
- .dark .kbd-omnibar,
517
- .dark .kbd-sequence {
518
- --kbd-bg: var(--kbd-bg, #1f2937);
519
- --kbd-bg-secondary: var(--kbd-bg-secondary, #374151);
520
- --kbd-text: var(--kbd-text, #f3f4f6);
521
- --kbd-text-secondary: var(--kbd-text-secondary, #9ca3af);
522
- --kbd-border: var(--kbd-border, #4b5563);
523
- --kbd-kbd-bg: var(--kbd-kbd-bg, #374151);
524
- --kbd-kbd-border: var(--kbd-kbd-border, #4b5563);
525
- --kbd-kbd-text: var(--kbd-kbd-text, #e5e7eb);
693
+ [data-theme="dark"],
694
+ .dark {
695
+ --kbd-bg: #1f2937;
696
+ --kbd-bg-secondary: #374151;
697
+ --kbd-text: #f3f4f6;
698
+ --kbd-text-secondary: #9ca3af;
699
+ --kbd-border: #4b5563;
700
+ --kbd-kbd-bg: #374151;
701
+ --kbd-kbd-border: #4b5563;
702
+ --kbd-kbd-text: #e5e7eb;
703
+ }
704
+
705
+ @media (prefers-color-scheme: dark) {
706
+ :root:not([data-theme="light"]):not(.light) {
707
+ --kbd-bg: #1f2937;
708
+ --kbd-bg-secondary: #374151;
709
+ --kbd-text: #f3f4f6;
710
+ --kbd-text-secondary: #9ca3af;
711
+ --kbd-border: #4b5563;
712
+ --kbd-kbd-bg: #374151;
713
+ --kbd-kbd-border: #4b5563;
714
+ --kbd-kbd-text: #e5e7eb;
715
+ }
526
716
  }