@tanstack/hotkeys-devtools 0.0.1 → 0.1.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -45
  3. package/dist/HotkeysContextProvider.js +47 -0
  4. package/dist/HotkeysContextProvider.js.map +1 -0
  5. package/dist/HotkeysDevtools.js +14 -0
  6. package/dist/HotkeysDevtools.js.map +1 -0
  7. package/dist/components/ActionButtons.js +33 -0
  8. package/dist/components/ActionButtons.js.map +1 -0
  9. package/dist/components/DetailsPanel.js +268 -0
  10. package/dist/components/DetailsPanel.js.map +1 -0
  11. package/dist/components/HeldKeysTopbar.js +75 -0
  12. package/dist/components/HeldKeysTopbar.js.map +1 -0
  13. package/dist/components/HotkeyList.js +188 -0
  14. package/dist/components/HotkeyList.js.map +1 -0
  15. package/dist/components/Shell.js +98 -0
  16. package/dist/components/Shell.js.map +1 -0
  17. package/dist/core.d.ts +24 -0
  18. package/dist/core.js +9 -0
  19. package/dist/core.js.map +1 -0
  20. package/dist/index.d.ts +16 -0
  21. package/dist/index.js +10 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/production.d.ts +2 -0
  24. package/dist/production.js +5 -0
  25. package/dist/styles/tokens.js +301 -0
  26. package/dist/styles/tokens.js.map +1 -0
  27. package/dist/styles/use-styles.js +496 -0
  28. package/dist/styles/use-styles.js.map +1 -0
  29. package/package.json +59 -7
  30. package/src/HotkeysContextProvider.tsx +67 -0
  31. package/src/HotkeysDevtools.tsx +10 -0
  32. package/src/components/ActionButtons.tsx +25 -0
  33. package/src/components/DetailsPanel.tsx +298 -0
  34. package/src/components/HeldKeysTopbar.tsx +42 -0
  35. package/src/components/HotkeyList.tsx +248 -0
  36. package/src/components/Shell.tsx +101 -0
  37. package/src/core.tsx +11 -0
  38. package/src/index.ts +10 -0
  39. package/src/production.ts +5 -0
  40. package/src/styles/tokens.ts +305 -0
  41. package/src/styles/use-styles.ts +493 -0
@@ -0,0 +1,493 @@
1
+ import * as goober from 'goober'
2
+ import { createEffect, createSignal } from 'solid-js'
3
+ import { useTheme } from '@tanstack/devtools-ui'
4
+ import { tokens } from './tokens'
5
+
6
+ const stylesFactory = (theme: 'light' | 'dark') => {
7
+ const { colors, font, size, alpha, border } = tokens
8
+ const { fontFamily, size: fontSize } = font
9
+ const css = goober.css
10
+ const t = (light: string, dark: string) => (theme === 'light' ? light : dark)
11
+
12
+ return {
13
+ mainContainer: css`
14
+ display: flex;
15
+ flex-direction: column;
16
+ padding: ${size[2]};
17
+ padding-top: 0;
18
+ margin-top: ${size[2]};
19
+ `,
20
+ heldKeysBar: css`
21
+ background: ${t(colors.gray[100], colors.darkGray[800])};
22
+ border-radius: ${border.radius.lg};
23
+ border: 1px solid ${t(colors.gray[200], colors.darkGray[700])};
24
+ display: flex;
25
+ align-items: center;
26
+ gap: ${size[3]};
27
+ padding: ${size[3]} ${size[3]};
28
+ flex-shrink: 0;
29
+ min-height: 64px;
30
+ margin-bottom: ${size[2]};
31
+ `,
32
+ heldKeysBarHeader: css`
33
+ font-size: ${fontSize.sm};
34
+ font-weight: ${font.weight.bold};
35
+ color: ${t(colors.gray[600], colors.gray[400])};
36
+ text-transform: uppercase;
37
+ letter-spacing: 0.05em;
38
+ flex-shrink: 0;
39
+ `,
40
+ heldKeysBarList: css`
41
+ display: flex;
42
+ align-items: center;
43
+ gap: ${size[1.5]};
44
+ flex-wrap: wrap;
45
+ flex: 1;
46
+ min-width: 0;
47
+ `,
48
+ panelsContainer: css`
49
+ display: flex;
50
+ `,
51
+ dragHandle: css`
52
+ width: 8px;
53
+ background: ${t(colors.gray[300], colors.darkGray[600])};
54
+ cursor: col-resize;
55
+ position: relative;
56
+ transition: all 0.2s ease;
57
+ user-select: none;
58
+ pointer-events: all;
59
+ margin: 0 ${size[1]};
60
+ border-radius: 2px;
61
+
62
+ &:hover {
63
+ background: ${t(colors.blue[600], colors.blue[500])};
64
+ margin: 0 ${size[1]};
65
+ }
66
+
67
+ &.dragging {
68
+ background: ${t(colors.blue[700], colors.blue[600])};
69
+ margin: 0 ${size[1]};
70
+ }
71
+
72
+ &::after {
73
+ content: '';
74
+ position: absolute;
75
+ top: 50%;
76
+ left: 50%;
77
+ transform: translate(-50%, -50%);
78
+ width: 2px;
79
+ height: 20px;
80
+ background: ${t(colors.gray[400], colors.darkGray[400])};
81
+ border-radius: 1px;
82
+ pointer-events: none;
83
+ }
84
+
85
+ &:hover::after,
86
+ &.dragging::after {
87
+ background: ${t(colors.blue[500], colors.blue[300])};
88
+ }
89
+ `,
90
+ keyCap: css`
91
+ display: inline-flex;
92
+ flex-direction: column;
93
+ align-items: center;
94
+ justify-content: center;
95
+ min-width: 36px;
96
+ padding: ${size[1]} ${size[2]};
97
+ font-family: ${fontFamily.mono};
98
+ font-size: ${fontSize.sm};
99
+ font-weight: ${font.weight.bold};
100
+ color: ${t(colors.gray[800], colors.gray[100])};
101
+ background: ${t(colors.white, colors.darkGray[600])};
102
+ border: 1px solid ${t(colors.gray[300], colors.darkGray[400])};
103
+ border-bottom-width: 2px;
104
+ border-radius: ${border.radius.sm};
105
+ box-shadow: ${tokens.shadow.xs()};
106
+ text-transform: capitalize;
107
+ white-space: nowrap;
108
+ line-height: 1.2;
109
+ `,
110
+ keyCapCode: css`
111
+ font-size: ${fontSize['2xs']};
112
+ font-weight: ${font.weight.normal};
113
+ color: ${t(colors.gray[500], colors.gray[400])};
114
+ text-transform: none;
115
+ line-height: 1;
116
+ margin-top: 2px;
117
+ `,
118
+ noKeysHeld: css`
119
+ font-size: ${fontSize.sm};
120
+ color: ${t(colors.gray[400], colors.gray[600])};
121
+ font-style: italic;
122
+ text-align: center;
123
+ padding: ${size[1]};
124
+ `,
125
+ leftPanel: css`
126
+ background: ${t(colors.gray[100], colors.darkGray[800])};
127
+ border-radius: ${border.radius.lg};
128
+ border: 1px solid ${t(colors.gray[200], colors.darkGray[700])};
129
+ display: flex;
130
+ flex-direction: column;
131
+ flex-shrink: 0;
132
+ `,
133
+ rightPanel: css`
134
+ background: ${t(colors.gray[100], colors.darkGray[800])};
135
+ border-radius: ${border.radius.lg};
136
+ border: 1px solid ${t(colors.gray[200], colors.darkGray[700])};
137
+ display: flex;
138
+ flex-direction: column;
139
+ flex: 1;
140
+ `,
141
+ panelHeader: css`
142
+ font-size: ${fontSize.md};
143
+ font-weight: ${font.weight.bold};
144
+ color: ${t(colors.blue[700], colors.blue[400])};
145
+ padding: ${size[2]};
146
+ border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])};
147
+ background: ${t(colors.gray[100], colors.darkGray[800])};
148
+ flex-shrink: 0;
149
+ `,
150
+ hotkeyList: css`
151
+ padding: ${size[1]};
152
+ `,
153
+ hotkeyRow: css`
154
+ display: flex;
155
+ justify-content: space-between;
156
+ align-items: center;
157
+ padding: ${size[2]};
158
+ margin-bottom: ${size[1]};
159
+ background: ${t(colors.gray[200], colors.darkGray[700])};
160
+ border-radius: ${border.radius.md};
161
+ cursor: pointer;
162
+ transition: all 0.2s ease;
163
+ border: 1px solid transparent;
164
+
165
+ &:hover {
166
+ background: ${t(colors.gray[300], colors.darkGray[600])};
167
+ border-color: ${t(colors.gray[400], colors.darkGray[500])};
168
+ }
169
+ `,
170
+ hotkeyRowSelected: css`
171
+ background: ${t(colors.blue[100], colors.blue[900] + alpha[20])};
172
+ border-color: ${t(colors.blue[600], colors.blue[500])};
173
+ box-shadow: 0 0 0 1px
174
+ ${t(colors.blue[600] + alpha[30], colors.blue[500] + alpha[30])};
175
+ `,
176
+ hotkeyRowTriggered: css`
177
+ animation: hotkey-pulse 0.6s ease-out;
178
+
179
+ @keyframes hotkey-pulse {
180
+ 0% {
181
+ border-color: ${t(colors.blue[500], colors.blue[400])};
182
+ box-shadow: 0 0 0 2px
183
+ ${t(colors.blue[500] + alpha[40], colors.blue[400] + alpha[40])};
184
+ }
185
+ 100% {
186
+ border-color: transparent;
187
+ box-shadow: 0 0 0 0px
188
+ ${t(colors.blue[500] + '00', colors.blue[400] + '00')};
189
+ }
190
+ }
191
+ `,
192
+ hotkeyLabel: css`
193
+ font-family: ${fontFamily.mono};
194
+ font-size: ${fontSize.xs};
195
+ color: ${t(colors.gray[900], colors.gray[100])};
196
+ flex: 1;
197
+ overflow: hidden;
198
+ text-overflow: ellipsis;
199
+ white-space: nowrap;
200
+ `,
201
+ hotkeyBadges: css`
202
+ display: flex;
203
+ gap: ${size[1]};
204
+ margin-left: ${size[1]};
205
+ flex-shrink: 0;
206
+ overflow: visible;
207
+ `,
208
+ badge: css`
209
+ font-size: ${fontSize['2xs']};
210
+ padding: ${size[0.5]} ${size[1]};
211
+ border-radius: ${border.radius.sm};
212
+ text-transform: uppercase;
213
+ letter-spacing: 0.05em;
214
+ white-space: nowrap;
215
+ `,
216
+ badgeEnabled: css`
217
+ color: ${t(colors.green[700], colors.green[400])};
218
+ background: ${t(colors.green[100], colors.green[900] + alpha[30])};
219
+ `,
220
+ badgeDisabled: css`
221
+ color: ${t(colors.red[700], colors.red[400])};
222
+ background: ${t(colors.red[100], colors.red[900] + alpha[30])};
223
+ `,
224
+ badgeKeydown: css`
225
+ color: ${t(colors.purple[700], colors.purple[400])};
226
+ background: ${t(colors.purple[100], colors.purple[900] + alpha[30])};
227
+ `,
228
+ badgeKeyup: css`
229
+ color: ${t(colors.teal[700], colors.teal[400])};
230
+ background: ${t(colors.teal[100], colors.teal[900] + alpha[30])};
231
+ `,
232
+ badgeTarget: css`
233
+ color: ${t(colors.gray[700], colors.gray[300])};
234
+ background: ${t(colors.gray[200], colors.darkGray[600])};
235
+ `,
236
+ badgeConflict: css`
237
+ color: ${t(colors.yellow[700], colors.yellow[400])};
238
+ background: ${t(colors.yellow[100], colors.yellow[900] + alpha[30])};
239
+ `,
240
+ badgeError: css`
241
+ color: ${t(colors.red[700], colors.red[400])};
242
+ background: ${t(colors.red[100], colors.red[900] + alpha[30])};
243
+ `,
244
+ badgeAllow: css`
245
+ color: ${t(colors.gray[600], colors.gray[400])};
246
+ background: ${t(colors.gray[200], colors.darkGray[600])};
247
+ `,
248
+ badgeInfo: css`
249
+ color: ${t(colors.blue[700], colors.blue[400])};
250
+ background: ${t(colors.blue[100], colors.blue[900] + alpha[30])};
251
+ `,
252
+ stateDetails: css`
253
+ padding: ${size[2]};
254
+ `,
255
+ stateHeader: css`
256
+ margin-bottom: ${size[2]};
257
+ padding-bottom: ${size[2]};
258
+ border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[700])};
259
+ `,
260
+ stateTitle: css`
261
+ font-size: ${fontSize.md};
262
+ font-weight: ${font.weight.bold};
263
+ color: ${t(colors.blue[700], colors.blue[400])};
264
+ margin-bottom: ${size[1]};
265
+ `,
266
+ detailsGrid: css`
267
+ display: grid;
268
+ grid-template-columns: 1fr;
269
+ gap: ${size[2]};
270
+ align-items: start;
271
+ `,
272
+ detailSection: css`
273
+ background: ${t(colors.white, colors.darkGray[700])};
274
+ border: 1px solid ${t(colors.gray[300], colors.darkGray[600])};
275
+ border-radius: ${border.radius.md};
276
+ padding: ${size[2]};
277
+ `,
278
+ detailSectionHeader: css`
279
+ font-size: ${fontSize.sm};
280
+ font-weight: ${font.weight.bold};
281
+ color: ${t(colors.gray[800], colors.gray[200])};
282
+ margin-bottom: ${size[1]};
283
+ text-transform: uppercase;
284
+ letter-spacing: 0.04em;
285
+ `,
286
+ infoGrid: css`
287
+ display: grid;
288
+ grid-template-columns: auto 1fr;
289
+ gap: ${size[1]};
290
+ row-gap: ${size[1]};
291
+ align-items: center;
292
+ `,
293
+ infoLabel: css`
294
+ color: ${t(colors.gray[600], colors.gray[400])};
295
+ font-size: ${fontSize.xs};
296
+ text-transform: uppercase;
297
+ letter-spacing: 0.05em;
298
+ `,
299
+ infoValueMono: css`
300
+ font-family: ${fontFamily.mono};
301
+ font-size: ${fontSize.xs};
302
+ color: ${t(colors.gray[900], colors.gray[100])};
303
+ word-break: break-all;
304
+ `,
305
+ actionsRow: css`
306
+ display: flex;
307
+ flex-wrap: wrap;
308
+ gap: ${size[2]};
309
+ `,
310
+ actionButton: css`
311
+ display: inline-flex;
312
+ align-items: center;
313
+ gap: ${size[1]};
314
+ padding: ${size[1]} ${size[2]};
315
+ border-radius: ${border.radius.md};
316
+ border: 1px solid ${t(colors.gray[300], colors.darkGray[500])};
317
+ background: ${t(colors.gray[200], colors.darkGray[600])};
318
+ color: ${t(colors.gray[900], colors.gray[100])};
319
+ font-size: ${fontSize.xs};
320
+ cursor: pointer;
321
+ user-select: none;
322
+ transition:
323
+ background 0.15s,
324
+ border-color 0.15s;
325
+ &:hover {
326
+ background: ${t(colors.gray[300], colors.darkGray[500])};
327
+ border-color: ${t(colors.gray[400], colors.darkGray[400])};
328
+ }
329
+ &:disabled {
330
+ opacity: 0.5;
331
+ cursor: not-allowed;
332
+ &:hover {
333
+ background: ${t(colors.gray[200], colors.darkGray[600])};
334
+ border-color: ${t(colors.gray[300], colors.darkGray[500])};
335
+ }
336
+ }
337
+ `,
338
+ actionDotGreen: css`
339
+ width: 6px;
340
+ height: 6px;
341
+ border-radius: 9999px;
342
+ background: ${colors.green[400]};
343
+ `,
344
+ noSelection: css`
345
+ flex: 1;
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ color: ${t(colors.gray[500], colors.gray[500])};
350
+ font-style: italic;
351
+ text-align: center;
352
+ padding: ${size[4]};
353
+ `,
354
+ keyBreakdown: css`
355
+ display: flex;
356
+ flex-wrap: wrap;
357
+ gap: ${size[1]};
358
+ align-items: center;
359
+ `,
360
+ keyBreakdownPlus: css`
361
+ color: ${t(colors.gray[500], colors.gray[400])};
362
+ font-size: ${fontSize.sm};
363
+ `,
364
+ keyCapLarge: css`
365
+ display: inline-flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ min-width: 36px;
369
+ padding: ${size[1]} ${size[2]};
370
+ font-family: ${fontFamily.mono};
371
+ font-size: ${fontSize.sm};
372
+ font-weight: ${font.weight.bold};
373
+ color: ${t(colors.gray[800], colors.gray[100])};
374
+ background: ${t(colors.white, colors.darkGray[600])};
375
+ border: 1px solid ${t(colors.gray[300], colors.darkGray[400])};
376
+ border-bottom-width: 3px;
377
+ border-radius: ${border.radius.md};
378
+ box-shadow: ${tokens.shadow.sm()};
379
+ text-transform: capitalize;
380
+ white-space: nowrap;
381
+ `,
382
+ conflictList: css`
383
+ display: flex;
384
+ flex-direction: column;
385
+ gap: ${size[1]};
386
+ `,
387
+ conflictItem: css`
388
+ font-family: ${fontFamily.mono};
389
+ font-size: ${fontSize.xs};
390
+ color: ${t(colors.yellow[700], colors.yellow[400])};
391
+ padding: ${size[1]} ${size[2]};
392
+ background: ${t(colors.yellow[50], colors.yellow[900] + alpha[20])};
393
+ border-radius: ${border.radius.sm};
394
+ border: 1px solid ${t(colors.yellow[200], colors.yellow[800] + alpha[30])};
395
+ `,
396
+ conflictItemAllow: css`
397
+ font-family: ${fontFamily.mono};
398
+ font-size: ${fontSize.xs};
399
+ color: ${t(colors.gray[600], colors.gray[400])};
400
+ padding: ${size[1]} ${size[2]};
401
+ background: ${t(colors.gray[100], colors.darkGray[700])};
402
+ border-radius: ${border.radius.sm};
403
+ border: 1px solid ${t(colors.gray[300], colors.darkGray[600])};
404
+ `,
405
+ conflictItemError: css`
406
+ font-family: ${fontFamily.mono};
407
+ font-size: ${fontSize.xs};
408
+ color: ${t(colors.red[700], colors.red[400])};
409
+ padding: ${size[1]} ${size[2]};
410
+ background: ${t(colors.red[50], colors.red[900] + alpha[20])};
411
+ border-radius: ${border.radius.sm};
412
+ border: 1px solid ${t(colors.red[200], colors.red[900] + alpha[30])};
413
+ `,
414
+ conflictItemScope: css`
415
+ font-family: ${fontFamily.mono};
416
+ font-size: ${fontSize.xs};
417
+ color: ${t(colors.blue[700], colors.blue[400])};
418
+ padding: ${size[1]} ${size[2]};
419
+ background: ${t(colors.blue[50], colors.blue[900] + alpha[20])};
420
+ border-radius: ${border.radius.sm};
421
+ border: 1px solid ${t(colors.blue[200], colors.blue[800] + alpha[30])};
422
+ `,
423
+ optionRow: css`
424
+ display: flex;
425
+ justify-content: space-between;
426
+ align-items: center;
427
+ padding: ${size[1]} 0;
428
+ font-size: ${fontSize.xs};
429
+ border-bottom: 1px solid ${t(colors.gray[200], colors.darkGray[600])};
430
+ &:last-child {
431
+ border-bottom: none;
432
+ }
433
+ `,
434
+ optionLabel: css`
435
+ color: ${t(colors.gray[600], colors.gray[400])};
436
+ `,
437
+ optionValue: css`
438
+ font-family: ${fontFamily.mono};
439
+ color: ${t(colors.gray[900], colors.gray[100])};
440
+ `,
441
+ optionValueTrue: css`
442
+ font-family: ${fontFamily.mono};
443
+ color: ${t(colors.green[600], colors.green[400])};
444
+ `,
445
+ optionValueFalse: css`
446
+ font-family: ${fontFamily.mono};
447
+ color: ${t(colors.red[600], colors.red[400])};
448
+ `,
449
+ tooltip: css`
450
+ position: relative;
451
+ &:hover > [data-tooltip] {
452
+ opacity: 1;
453
+ visibility: visible;
454
+ }
455
+ `,
456
+ tooltipText: css`
457
+ position: absolute;
458
+ top: 100%;
459
+ left: 50%;
460
+ transform: translateX(-50%);
461
+ padding: ${size[1]} ${size[1.5]};
462
+ background: ${t(colors.gray[800], colors.darkGray[500])};
463
+ color: ${t(colors.white, colors.gray[100])};
464
+ font-size: ${fontSize['2xs']};
465
+ border-radius: ${border.radius.sm};
466
+ white-space: nowrap;
467
+ pointer-events: none;
468
+ opacity: 0;
469
+ visibility: hidden;
470
+ transition:
471
+ opacity 0.15s,
472
+ visibility 0.15s;
473
+ z-index: ${tokens.zIndices.tooltip};
474
+ margin-top: ${size[1]};
475
+ `,
476
+ triggerCount: css`
477
+ font-family: ${fontFamily.mono};
478
+ font-size: ${fontSize['2xs']};
479
+ color: ${t(colors.gray[500], colors.gray[400])};
480
+ margin-left: ${size[1]};
481
+ flex-shrink: 0;
482
+ `,
483
+ }
484
+ }
485
+
486
+ export function useStyles() {
487
+ const { theme } = useTheme()
488
+ const [styles, setStyles] = createSignal(stylesFactory(theme()))
489
+ createEffect(() => {
490
+ setStyles(stylesFactory(theme()))
491
+ })
492
+ return styles
493
+ }