@tanstack/hotkeys-devtools 0.9.0 → 1.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 (42) hide show
  1. package/README.md +15 -1
  2. package/dist/HotkeysContextProvider.js +1 -2
  3. package/dist/_registrations.js +47 -0
  4. package/dist/components/ActionButtons.js +2 -3
  5. package/dist/components/DetailsPanel.js +317 -325
  6. package/dist/components/HeldKeysTopbar.js +5 -3
  7. package/dist/components/HotkeyList.js +167 -196
  8. package/dist/components/Shell.js +5 -4
  9. package/dist/components/index.js +1 -2
  10. package/dist/core.d.ts +3 -6
  11. package/dist/core.js +1 -2
  12. package/dist/index.d.ts +2 -4
  13. package/dist/index.js +1 -2
  14. package/dist/sequence-progress.js +1 -2
  15. package/dist/styles/tokens.js +1 -2
  16. package/dist/styles/use-styles.js +5 -4
  17. package/package.json +11 -11
  18. package/dist/HotkeysContextProvider.js.map +0 -1
  19. package/dist/components/ActionButtons.js.map +0 -1
  20. package/dist/components/DetailsPanel.js.map +0 -1
  21. package/dist/components/HeldKeysTopbar.js.map +0 -1
  22. package/dist/components/HotkeyList.js.map +0 -1
  23. package/dist/components/Shell.js.map +0 -1
  24. package/dist/components/index.js.map +0 -1
  25. package/dist/core.js.map +0 -1
  26. package/dist/index.js.map +0 -1
  27. package/dist/sequence-progress.js.map +0 -1
  28. package/dist/styles/tokens.js.map +0 -1
  29. package/dist/styles/use-styles.js.map +0 -1
  30. package/src/HotkeysContextProvider.tsx +0 -130
  31. package/src/components/ActionButtons.tsx +0 -38
  32. package/src/components/DetailsPanel.tsx +0 -829
  33. package/src/components/HeldKeysTopbar.tsx +0 -46
  34. package/src/components/HotkeyList.tsx +0 -565
  35. package/src/components/Shell.tsx +0 -171
  36. package/src/components/index.tsx +0 -19
  37. package/src/core.tsx +0 -11
  38. package/src/index.ts +0 -11
  39. package/src/production.ts +0 -5
  40. package/src/sequence-progress.ts +0 -29
  41. package/src/styles/tokens.ts +0 -305
  42. package/src/styles/use-styles.ts +0 -687
@@ -1,829 +0,0 @@
1
- import { For, Show, createMemo } from 'solid-js'
2
- import clsx from 'clsx'
3
- import {
4
- detectPlatform,
5
- formatForDisplay,
6
- formatHotkeySequence,
7
- normalizeHotkey,
8
- normalizeHotkeyFromParsed,
9
- parseHotkey,
10
- } from '@tanstack/hotkeys'
11
- import { useStyles } from '../styles/use-styles'
12
- import { useHotkeysDevtoolsState } from '../HotkeysContextProvider'
13
- import { effectiveSequenceMatchedSteps } from '../sequence-progress'
14
- import { ActionButtons } from './ActionButtons'
15
- import type {
16
- ConflictBehavior,
17
- FormatDisplayOptions,
18
- HotkeyRegistration,
19
- HotkeySequence,
20
- ParsedHotkey,
21
- RegisterableHotkey,
22
- SequenceRegistrationView,
23
- } from '@tanstack/hotkeys'
24
-
25
- const PLATFORM_ROWS = [
26
- { id: 'mac' as const, label: 'macOS' },
27
- { id: 'windows' as const, label: 'Windows' },
28
- { id: 'linux' as const, label: 'Linux' },
29
- ]
30
-
31
- function KeyBreakdownPlatformTable(props: {
32
- getParsed: () => ParsedHotkey
33
- /** Platform used when the parsed hotkey was produced (registration `options.platform`). */
34
- getCanonicalPlatform: () => 'mac' | 'windows' | 'linux'
35
- styles: ReturnType<typeof useStyles>
36
- }) {
37
- const st = () => props.styles()
38
- return (
39
- <div class={st().platformBreakdown}>
40
- <For each={PLATFORM_ROWS}>
41
- {(row) => (
42
- <div class={st().platformBreakdownRow}>
43
- <span class={st().platformBreakdownLabel}>{row.label}</span>
44
- <span class={st().platformBreakdownPretty}>
45
- {formatForDisplay(
46
- normalizeHotkeyFromParsed(
47
- props.getParsed(),
48
- props.getCanonicalPlatform(),
49
- ),
50
- {
51
- platform: row.id,
52
- },
53
- )}
54
- </span>
55
- </div>
56
- )}
57
- </For>
58
- </div>
59
- )
60
- }
61
-
62
- function sequenceKey(sequence: Array<string>): string {
63
- return sequence.join('|')
64
- }
65
-
66
- function isSequenceRegistration(
67
- reg: HotkeyRegistration | SequenceRegistrationView,
68
- ): reg is SequenceRegistrationView {
69
- return 'sequence' in reg && Array.isArray(reg.sequence)
70
- }
71
-
72
- type DetailsPanelProps = {
73
- selectedRegistration: () =>
74
- | HotkeyRegistration
75
- | SequenceRegistrationView
76
- | null
77
- }
78
-
79
- function getTargetDescription(target: HTMLElement | Document | Window): string {
80
- if (typeof document !== 'undefined' && target === document) {
81
- return 'document'
82
- }
83
- if (typeof window !== 'undefined' && target === window) {
84
- return 'window'
85
- }
86
- if (target instanceof HTMLElement) {
87
- const tag = target.tagName.toLowerCase()
88
- const id = target.id ? `#${target.id}` : ''
89
- const cls = target.className
90
- ? `.${target.className.split(' ').join('.')}`
91
- : ''
92
- return `${tag}${id}${cls}`
93
- }
94
- return 'element'
95
- }
96
-
97
- function findTargetConflicts(
98
- registration: HotkeyRegistration,
99
- all: Array<HotkeyRegistration>,
100
- ): Array<HotkeyRegistration> {
101
- return all.filter(
102
- (other) =>
103
- other.id !== registration.id &&
104
- other.hotkey === registration.hotkey &&
105
- other.options.eventType === registration.options.eventType &&
106
- other.target === registration.target,
107
- )
108
- }
109
-
110
- function findScopeConflicts(
111
- registration: HotkeyRegistration,
112
- all: Array<HotkeyRegistration>,
113
- ): Array<HotkeyRegistration> {
114
- return all.filter(
115
- (other) =>
116
- other.id !== registration.id &&
117
- other.hotkey === registration.hotkey &&
118
- other.options.eventType === registration.options.eventType &&
119
- other.target !== registration.target,
120
- )
121
- }
122
-
123
- function findSequenceTargetConflicts(
124
- registration: SequenceRegistrationView,
125
- all: Array<SequenceRegistrationView>,
126
- ): Array<SequenceRegistrationView> {
127
- return all.filter(
128
- (other) =>
129
- other.id !== registration.id &&
130
- sequenceKey(other.sequence) === sequenceKey(registration.sequence) &&
131
- other.options.eventType === registration.options.eventType &&
132
- other.target === registration.target,
133
- )
134
- }
135
-
136
- function findSequenceScopeConflicts(
137
- registration: SequenceRegistrationView,
138
- all: Array<SequenceRegistrationView>,
139
- ): Array<SequenceRegistrationView> {
140
- return all.filter(
141
- (other) =>
142
- other.id !== registration.id &&
143
- sequenceKey(other.sequence) === sequenceKey(registration.sequence) &&
144
- other.options.eventType === registration.options.eventType &&
145
- other.target !== registration.target,
146
- )
147
- }
148
-
149
- function getConflictItemStyle(
150
- behavior: ConflictBehavior,
151
- isSameTarget: boolean,
152
- ):
153
- | 'conflictItem'
154
- | 'conflictItemAllow'
155
- | 'conflictItemError'
156
- | 'conflictItemScope' {
157
- if (!isSameTarget) return 'conflictItemScope'
158
- if (behavior === 'allow') return 'conflictItemAllow'
159
- if (behavior === 'error') return 'conflictItemError'
160
- return 'conflictItem'
161
- }
162
-
163
- function getConflictLabel(
164
- behavior: ConflictBehavior,
165
- isSameTarget: boolean,
166
- ): string {
167
- if (!isSameTarget) return 'scope'
168
- if (behavior === 'allow') return 'allowed'
169
- if (behavior === 'error') return 'error'
170
- if (behavior === 'replace') return 'replaced'
171
- return 'warning'
172
- }
173
-
174
- function HotkeyDetails(props: {
175
- registration: HotkeyRegistration
176
- state: ReturnType<typeof useHotkeysDevtoolsState>
177
- getTargetDescription: (t: HTMLElement | Document | Window) => string
178
- findTargetConflicts: (
179
- r: HotkeyRegistration,
180
- all: Array<HotkeyRegistration>,
181
- ) => Array<HotkeyRegistration>
182
- findScopeConflicts: (
183
- r: HotkeyRegistration,
184
- all: Array<HotkeyRegistration>,
185
- ) => Array<HotkeyRegistration>
186
- getConflictItemStyle: (
187
- b: ConflictBehavior,
188
- same: boolean,
189
- ) =>
190
- | 'conflictItem'
191
- | 'conflictItemAllow'
192
- | 'conflictItemError'
193
- | 'conflictItemScope'
194
- getConflictLabel: (b: ConflictBehavior, same: boolean) => string
195
- formatForDisplay: (
196
- hotkey: RegisterableHotkey,
197
- options?: FormatDisplayOptions,
198
- ) => string
199
- styles: ReturnType<typeof useStyles>
200
- }) {
201
- const reg = () => props.registration
202
- const parsed = () => reg().parsedHotkey
203
- const targetConflicts = createMemo(() =>
204
- props.findTargetConflicts(reg(), props.state.registrations()),
205
- )
206
- const scopeConflicts = createMemo(() =>
207
- props.findScopeConflicts(reg(), props.state.registrations()),
208
- )
209
- const allConflicts = createMemo(() => [
210
- ...targetConflicts(),
211
- ...scopeConflicts(),
212
- ])
213
- const conflictBehavior = (): ConflictBehavior =>
214
- reg().options.conflictBehavior ?? 'warn'
215
-
216
- const registrationPlatform = () => reg().options.platform ?? detectPlatform()
217
-
218
- const rawModParts = createMemo(() =>
219
- normalizeHotkeyFromParsed(parsed(), registrationPlatform()).split('+'),
220
- )
221
-
222
- const styles = props.styles
223
-
224
- return (
225
- <>
226
- <div class={styles().stateHeader}>
227
- <div class={styles().stateTitle}>
228
- {props.formatForDisplay(reg().hotkey)}
229
- </div>
230
- <div class={styles().infoGrid}>
231
- <div class={styles().infoLabel}>ID</div>
232
- <div class={styles().infoValueMono}>{reg().id}</div>
233
- <div class={styles().infoLabel}>Raw</div>
234
- <div class={styles().infoValueMono}>{reg().hotkey}</div>
235
- <div class={styles().infoLabel}>Target</div>
236
- <div class={styles().infoValueMono}>
237
- {props.getTargetDescription(reg().target)}
238
- </div>
239
- </div>
240
- </div>
241
-
242
- <div class={styles().detailsGrid}>
243
- <div class={styles().detailSection}>
244
- <div class={styles().detailSectionHeader}>Key Breakdown</div>
245
- <div class={styles().keyBreakdownSplitRow}>
246
- <div class={styles().keyBreakdownSplitLeft}>
247
- <div class={styles().keyBreakdown}>
248
- <For each={rawModParts()}>
249
- {(part, i) => (
250
- <>
251
- <Show when={i() > 0}>
252
- <span class={styles().keyBreakdownPlus}>+</span>
253
- </Show>
254
- <span class={styles().keyCapLarge}>{part}</span>
255
- </>
256
- )}
257
- </For>
258
- </div>
259
- </div>
260
- <span class={styles().keyBreakdownSplitArrow} aria-hidden="true">
261
- {'->'}
262
- </span>
263
- <div class={styles().keyBreakdownSplitRight}>
264
- <KeyBreakdownPlatformTable
265
- getParsed={() => parsed()}
266
- getCanonicalPlatform={registrationPlatform}
267
- styles={props.styles}
268
- />
269
- </div>
270
- </div>
271
- </div>
272
-
273
- <div class={styles().detailSection}>
274
- <div class={styles().detailSectionHeader}>Actions</div>
275
- <ActionButtons registration={reg()} />
276
- </div>
277
-
278
- <div class={styles().detailSection}>
279
- <div class={styles().detailSectionHeader}>Meta</div>
280
- <div>
281
- <div class={styles().optionRow}>
282
- <span class={styles().optionLabel}>name</span>
283
- <span class={styles().optionValue}>
284
- {reg().options.meta?.name ?? '—'}
285
- </span>
286
- </div>
287
- <div class={styles().optionRow}>
288
- <span class={styles().optionLabel}>description</span>
289
- <span class={styles().optionValue}>
290
- {reg().options.meta?.description ?? '—'}
291
- </span>
292
- </div>
293
- <Show
294
- when={
295
- reg().options.meta &&
296
- Object.keys(reg().options.meta!).filter(
297
- (k) => k !== 'name' && k !== 'description',
298
- ).length > 0
299
- }
300
- >
301
- <For
302
- each={Object.entries(reg().options.meta ?? {}).filter(
303
- ([k]) => k !== 'name' && k !== 'description',
304
- )}
305
- >
306
- {([key, value]) => (
307
- <div class={styles().optionRow}>
308
- <span class={styles().optionLabel}>{key}</span>
309
- <span class={styles().optionValue}>{String(value)}</span>
310
- </div>
311
- )}
312
- </For>
313
- </Show>
314
- </div>
315
- </div>
316
-
317
- <div class={styles().detailSection}>
318
- <div class={styles().detailSectionHeader}>Options</div>
319
- <div>
320
- <div class={styles().optionRow}>
321
- <span class={styles().optionLabel}>enabled</span>
322
- <span
323
- class={
324
- reg().options.enabled !== false
325
- ? styles().optionValueTrue
326
- : styles().optionValueFalse
327
- }
328
- >
329
- {String(reg().options.enabled !== false)}
330
- </span>
331
- </div>
332
- <div class={styles().optionRow}>
333
- <span class={styles().optionLabel}>eventType</span>
334
- <span class={styles().optionValue}>
335
- {reg().options.eventType ?? 'keydown'}
336
- </span>
337
- </div>
338
- <div class={styles().optionRow}>
339
- <span class={styles().optionLabel}>preventDefault</span>
340
- <span
341
- class={
342
- reg().options.preventDefault
343
- ? styles().optionValueTrue
344
- : styles().optionValueFalse
345
- }
346
- >
347
- {String(!!reg().options.preventDefault)}
348
- </span>
349
- </div>
350
- <div class={styles().optionRow}>
351
- <span class={styles().optionLabel}>stopPropagation</span>
352
- <span
353
- class={
354
- reg().options.stopPropagation
355
- ? styles().optionValueTrue
356
- : styles().optionValueFalse
357
- }
358
- >
359
- {String(!!reg().options.stopPropagation)}
360
- </span>
361
- </div>
362
- <div class={styles().optionRow}>
363
- <span class={styles().optionLabel}>ignoreInputs</span>
364
- <span
365
- class={
366
- reg().options.ignoreInputs !== false
367
- ? styles().optionValueTrue
368
- : styles().optionValueFalse
369
- }
370
- >
371
- {String(reg().options.ignoreInputs !== false)}
372
- </span>
373
- </div>
374
- <div class={styles().optionRow}>
375
- <span class={styles().optionLabel}>requireReset</span>
376
- <span
377
- class={
378
- reg().options.requireReset
379
- ? styles().optionValueTrue
380
- : styles().optionValueFalse
381
- }
382
- >
383
- {String(!!reg().options.requireReset)}
384
- </span>
385
- </div>
386
- <div class={styles().optionRow}>
387
- <span class={styles().optionLabel}>conflictBehavior</span>
388
- <span class={styles().optionValue}>{conflictBehavior()}</span>
389
- </div>
390
- <div class={styles().optionRow}>
391
- <span class={styles().optionLabel}>hasFired</span>
392
- <span
393
- class={
394
- reg().hasFired
395
- ? styles().optionValueTrue
396
- : styles().optionValueFalse
397
- }
398
- >
399
- {String(reg().hasFired)}
400
- </span>
401
- </div>
402
- </div>
403
- </div>
404
-
405
- <Show when={allConflicts().length > 0}>
406
- <div class={styles().detailSection}>
407
- <div class={styles().detailSectionHeader}>
408
- Conflicts ({allConflicts().length})
409
- </div>
410
- <div class={styles().conflictList}>
411
- <For each={targetConflicts()}>
412
- {(conflict) => {
413
- const itemStyle = () =>
414
- props.getConflictItemStyle(conflictBehavior(), true)
415
- const label = () =>
416
- props.getConflictLabel(conflictBehavior(), true)
417
- return (
418
- <div class={styles()[itemStyle()]}>
419
- <span>{label()}</span> {conflict.id}:{' '}
420
- {props.formatForDisplay(conflict.hotkey)} (
421
- {conflict.options.eventType ?? 'keydown'}) on{' '}
422
- {props.getTargetDescription(conflict.target)}
423
- </div>
424
- )
425
- }}
426
- </For>
427
- <For each={scopeConflicts()}>
428
- {(conflict) => (
429
- <div class={styles().conflictItemScope}>
430
- <span>scope</span> {conflict.id}:{' '}
431
- {props.formatForDisplay(conflict.hotkey)} (
432
- {conflict.options.eventType ?? 'keydown'}) on{' '}
433
- {props.getTargetDescription(conflict.target)}
434
- </div>
435
- )}
436
- </For>
437
- </div>
438
- </div>
439
- </Show>
440
- </div>
441
- </>
442
- )
443
- }
444
-
445
- function SequenceDetails(props: {
446
- registration: SequenceRegistrationView
447
- state: ReturnType<typeof useHotkeysDevtoolsState>
448
- getTargetDescription: (t: HTMLElement | Document | Window) => string
449
- findSequenceTargetConflicts: (
450
- r: SequenceRegistrationView,
451
- all: Array<SequenceRegistrationView>,
452
- ) => Array<SequenceRegistrationView>
453
- findSequenceScopeConflicts: (
454
- r: SequenceRegistrationView,
455
- all: Array<SequenceRegistrationView>,
456
- ) => Array<SequenceRegistrationView>
457
- getConflictItemStyle: (
458
- b: ConflictBehavior,
459
- same: boolean,
460
- ) =>
461
- | 'conflictItem'
462
- | 'conflictItemAllow'
463
- | 'conflictItemError'
464
- | 'conflictItemScope'
465
- getConflictLabel: (b: ConflictBehavior, same: boolean) => string
466
- formatHotkeySequence: (seq: HotkeySequence) => string
467
- formatForDisplay: (
468
- hotkey: RegisterableHotkey,
469
- options?: FormatDisplayOptions,
470
- ) => string
471
- styles: ReturnType<typeof useStyles>
472
- }) {
473
- const reg = () => props.registration
474
- const liveReg = createMemo(
475
- () =>
476
- props.state.sequenceRegistrations().find((r) => r.id === reg().id) ??
477
- reg(),
478
- )
479
- const matchedSteps = createMemo(() =>
480
- effectiveSequenceMatchedSteps(liveReg(), props.state.sequenceProgressNow()),
481
- )
482
- const targetConflicts = createMemo(() =>
483
- props.findSequenceTargetConflicts(
484
- liveReg(),
485
- props.state.sequenceRegistrations(),
486
- ),
487
- )
488
- const scopeConflicts = createMemo(() =>
489
- props.findSequenceScopeConflicts(
490
- liveReg(),
491
- props.state.sequenceRegistrations(),
492
- ),
493
- )
494
- const allConflicts = createMemo(() => [
495
- ...targetConflicts(),
496
- ...scopeConflicts(),
497
- ])
498
- const conflictBehavior = (): ConflictBehavior =>
499
- liveReg().options.conflictBehavior ?? 'warn'
500
-
501
- const sequenceCanonicalPlatform = createMemo(
502
- () => liveReg().options.platform ?? detectPlatform(),
503
- )
504
-
505
- const styles = props.styles
506
-
507
- return (
508
- <>
509
- <div class={styles().stateHeader}>
510
- <div class={styles().stateTitle}>
511
- <For each={liveReg().sequence}>
512
- {(step, i) => (
513
- <span>
514
- <Show when={i() > 0}> </Show>
515
- <span
516
- class={
517
- i() < matchedSteps()
518
- ? styles().sequenceStepMatched
519
- : undefined
520
- }
521
- >
522
- {props.formatForDisplay(step)}
523
- </span>
524
- </span>
525
- )}
526
- </For>
527
- </div>
528
- <div class={styles().infoGrid}>
529
- <div class={styles().infoLabel}>ID</div>
530
- <div class={styles().infoValueMono}>{liveReg().id}</div>
531
- <div class={styles().infoLabel}>Sequence</div>
532
- <div class={styles().infoValueMono}>
533
- <For each={liveReg().sequence}>
534
- {(step, i) => (
535
- <span>
536
- <Show when={i() > 0}> </Show>
537
- <span
538
- class={
539
- i() < matchedSteps()
540
- ? styles().sequenceStepMatched
541
- : undefined
542
- }
543
- >
544
- {props.formatForDisplay(step)}
545
- </span>
546
- </span>
547
- )}
548
- </For>
549
- </div>
550
- <div class={styles().infoLabel}>Target</div>
551
- <div class={styles().infoValueMono}>
552
- {props.getTargetDescription(liveReg().target)}
553
- </div>
554
- </div>
555
- </div>
556
-
557
- <div class={styles().detailsGrid}>
558
- <div class={styles().detailSection}>
559
- <div class={styles().detailSectionHeader}>Key Breakdown</div>
560
- <div class={styles().keyBreakdown}>
561
- <For each={liveReg().sequence}>
562
- {(step, i) => (
563
- <>
564
- <Show when={i() > 0}>
565
- <span class={styles().keyBreakdownPlus}>→</span>
566
- </Show>
567
- <span
568
- class={clsx(
569
- styles().keyCapLarge,
570
- i() < matchedSteps() && styles().keyCapLargeInProgress,
571
- )}
572
- >
573
- {props.formatForDisplay(step)}
574
- </span>
575
- </>
576
- )}
577
- </For>
578
- </div>
579
-
580
- <div class={styles().keyBreakdownSubHeader}>Each chord</div>
581
- <For each={liveReg().sequence}>
582
- {(step, stepIdx) => {
583
- const stepParsed = () =>
584
- parseHotkey(step, sequenceCanonicalPlatform())
585
- const stepRawParts = () =>
586
- normalizeHotkey(step, sequenceCanonicalPlatform()).split('+')
587
- return (
588
- <div class={styles().sequenceChordDetail}>
589
- <div class={styles().keyBreakdownSubHeader}>
590
- Step {stepIdx() + 1}
591
- </div>
592
- <div class={styles().keyBreakdownSplitRow}>
593
- <div class={styles().keyBreakdownSplitLeft}>
594
- <div class={styles().keyBreakdown}>
595
- <For each={stepRawParts()}>
596
- {(part, i) => (
597
- <>
598
- <Show when={i() > 0}>
599
- <span class={styles().keyBreakdownPlus}>+</span>
600
- </Show>
601
- <span class={styles().keyCapLarge}>{part}</span>
602
- </>
603
- )}
604
- </For>
605
- </div>
606
- </div>
607
- <span
608
- class={styles().keyBreakdownSplitArrow}
609
- aria-hidden="true"
610
- >
611
- {'->'}
612
- </span>
613
- <div class={styles().keyBreakdownSplitRight}>
614
- <KeyBreakdownPlatformTable
615
- getParsed={stepParsed}
616
- getCanonicalPlatform={() => sequenceCanonicalPlatform()}
617
- styles={props.styles}
618
- />
619
- </div>
620
- </div>
621
- </div>
622
- )
623
- }}
624
- </For>
625
- </div>
626
-
627
- <div class={styles().detailSection}>
628
- <div class={styles().detailSectionHeader}>Actions</div>
629
- <ActionButtons registration={liveReg()} />
630
- </div>
631
-
632
- <div class={styles().detailSection}>
633
- <div class={styles().detailSectionHeader}>Meta</div>
634
- <div>
635
- <div class={styles().optionRow}>
636
- <span class={styles().optionLabel}>name</span>
637
- <span class={styles().optionValue}>
638
- {liveReg().options.meta?.name ?? '—'}
639
- </span>
640
- </div>
641
- <div class={styles().optionRow}>
642
- <span class={styles().optionLabel}>description</span>
643
- <span class={styles().optionValue}>
644
- {liveReg().options.meta?.description ?? '—'}
645
- </span>
646
- </div>
647
- <Show
648
- when={
649
- liveReg().options.meta &&
650
- Object.keys(liveReg().options.meta!).filter(
651
- (k) => k !== 'name' && k !== 'description',
652
- ).length > 0
653
- }
654
- >
655
- <For
656
- each={Object.entries(liveReg().options.meta ?? {}).filter(
657
- ([k]) => k !== 'name' && k !== 'description',
658
- )}
659
- >
660
- {([key, value]) => (
661
- <div class={styles().optionRow}>
662
- <span class={styles().optionLabel}>{key}</span>
663
- <span class={styles().optionValue}>{String(value)}</span>
664
- </div>
665
- )}
666
- </For>
667
- </Show>
668
- </div>
669
- </div>
670
-
671
- <div class={styles().detailSection}>
672
- <div class={styles().detailSectionHeader}>Options</div>
673
- <div>
674
- <div class={styles().optionRow}>
675
- <span class={styles().optionLabel}>enabled</span>
676
- <span
677
- class={
678
- liveReg().options.enabled !== false
679
- ? styles().optionValueTrue
680
- : styles().optionValueFalse
681
- }
682
- >
683
- {String(liveReg().options.enabled !== false)}
684
- </span>
685
- </div>
686
- <div class={styles().optionRow}>
687
- <span class={styles().optionLabel}>eventType</span>
688
- <span class={styles().optionValue}>
689
- {liveReg().options.eventType ?? 'keydown'}
690
- </span>
691
- </div>
692
- <div class={styles().optionRow}>
693
- <span class={styles().optionLabel}>timeout</span>
694
- <span class={styles().optionValue}>
695
- {liveReg().options.timeout ?? 1000}ms
696
- </span>
697
- </div>
698
- <div class={styles().optionRow}>
699
- <span class={styles().optionLabel}>preventDefault</span>
700
- <span
701
- class={
702
- liveReg().options.preventDefault
703
- ? styles().optionValueTrue
704
- : styles().optionValueFalse
705
- }
706
- >
707
- {String(!!liveReg().options.preventDefault)}
708
- </span>
709
- </div>
710
- <div class={styles().optionRow}>
711
- <span class={styles().optionLabel}>stopPropagation</span>
712
- <span
713
- class={
714
- liveReg().options.stopPropagation
715
- ? styles().optionValueTrue
716
- : styles().optionValueFalse
717
- }
718
- >
719
- {String(!!liveReg().options.stopPropagation)}
720
- </span>
721
- </div>
722
- <div class={styles().optionRow}>
723
- <span class={styles().optionLabel}>ignoreInputs</span>
724
- <span
725
- class={
726
- liveReg().options.ignoreInputs !== false
727
- ? styles().optionValueTrue
728
- : styles().optionValueFalse
729
- }
730
- >
731
- {String(liveReg().options.ignoreInputs !== false)}
732
- </span>
733
- </div>
734
- <div class={styles().optionRow}>
735
- <span class={styles().optionLabel}>conflictBehavior</span>
736
- <span class={styles().optionValue}>{conflictBehavior()}</span>
737
- </div>
738
- </div>
739
- </div>
740
-
741
- <Show when={allConflicts().length > 0}>
742
- <div class={styles().detailSection}>
743
- <div class={styles().detailSectionHeader}>
744
- Conflicts ({allConflicts().length})
745
- </div>
746
- <div class={styles().conflictList}>
747
- <For each={targetConflicts()}>
748
- {(conflict) => {
749
- const itemStyle = () =>
750
- props.getConflictItemStyle(conflictBehavior(), true)
751
- const label = () =>
752
- props.getConflictLabel(conflictBehavior(), true)
753
- return (
754
- <div class={styles()[itemStyle()]}>
755
- <span>{label()}</span> {conflict.id}:{' '}
756
- {props.formatHotkeySequence(conflict.sequence)} (
757
- {conflict.options.eventType ?? 'keydown'}) on{' '}
758
- {props.getTargetDescription(conflict.target)}
759
- </div>
760
- )
761
- }}
762
- </For>
763
- <For each={scopeConflicts()}>
764
- {(conflict) => (
765
- <div class={styles().conflictItemScope}>
766
- <span>scope</span> {conflict.id}:{' '}
767
- {props.formatHotkeySequence(conflict.sequence)} (
768
- {conflict.options.eventType ?? 'keydown'}) on{' '}
769
- {props.getTargetDescription(conflict.target)}
770
- </div>
771
- )}
772
- </For>
773
- </div>
774
- </div>
775
- </Show>
776
- </div>
777
- </>
778
- )
779
- }
780
-
781
- export function DetailsPanel(props: DetailsPanelProps) {
782
- const styles = useStyles()
783
- const state = useHotkeysDevtoolsState()
784
-
785
- return (
786
- <div class={styles().stateDetails}>
787
- <Show
788
- when={props.selectedRegistration()}
789
- fallback={
790
- <div class={styles().noSelection}>
791
- Select a hotkey or sequence from the list to view its details
792
- </div>
793
- }
794
- >
795
- {(reg) => (
796
- <Show
797
- when={isSequenceRegistration(reg())}
798
- fallback={
799
- <HotkeyDetails
800
- registration={reg() as HotkeyRegistration}
801
- state={state}
802
- getTargetDescription={getTargetDescription}
803
- findTargetConflicts={findTargetConflicts}
804
- findScopeConflicts={findScopeConflicts}
805
- getConflictItemStyle={getConflictItemStyle}
806
- getConflictLabel={getConflictLabel}
807
- formatForDisplay={formatForDisplay}
808
- styles={styles}
809
- />
810
- }
811
- >
812
- <SequenceDetails
813
- registration={reg() as SequenceRegistrationView}
814
- state={state}
815
- getTargetDescription={getTargetDescription}
816
- findSequenceTargetConflicts={findSequenceTargetConflicts}
817
- findSequenceScopeConflicts={findSequenceScopeConflicts}
818
- getConflictItemStyle={getConflictItemStyle}
819
- getConflictLabel={getConflictLabel}
820
- formatHotkeySequence={formatHotkeySequence}
821
- formatForDisplay={formatForDisplay}
822
- styles={styles}
823
- />
824
- </Show>
825
- )}
826
- </Show>
827
- </div>
828
- )
829
- }