@tanstack/hotkeys-devtools 0.4.4 → 0.5.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/dist/HotkeysContextProvider.js +25 -2
- package/dist/HotkeysContextProvider.js.map +1 -1
- package/dist/components/DetailsPanel.js +84 -48
- package/dist/components/DetailsPanel.js.map +1 -1
- package/dist/components/HotkeyList.js +229 -195
- package/dist/components/HotkeyList.js.map +1 -1
- package/dist/components/Shell.js +100 -43
- package/dist/components/Shell.js.map +1 -1
- package/dist/sequence-progress.js +20 -0
- package/dist/sequence-progress.js.map +1 -0
- package/dist/styles/use-styles.js +47 -0
- package/dist/styles/use-styles.js.map +1 -1
- package/package.json +2 -2
- package/src/HotkeysContextProvider.tsx +35 -0
- package/src/components/DetailsPanel.tsx +65 -20
- package/src/components/HotkeyList.tsx +187 -151
- package/src/components/Shell.tsx +101 -34
- package/src/sequence-progress.ts +29 -0
- package/src/styles/use-styles.ts +49 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/hotkeys-devtools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Devtools for TanStack Hotkeys",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"src"
|
|
38
38
|
],
|
|
39
39
|
"peerDependencies": {
|
|
40
|
-
"@tanstack/hotkeys": "0.
|
|
40
|
+
"@tanstack/hotkeys": "0.5.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@tanstack/devtools-ui": "^0.5.1",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
KeyStateTracker,
|
|
11
11
|
SequenceManager,
|
|
12
12
|
} from '@tanstack/hotkeys'
|
|
13
|
+
import { needsSequenceProgressClock } from './sequence-progress'
|
|
13
14
|
import type { Accessor } from 'solid-js'
|
|
14
15
|
import type {
|
|
15
16
|
HotkeyRegistration,
|
|
@@ -21,6 +22,8 @@ interface HotkeysDevtoolsContextType {
|
|
|
21
22
|
sequenceRegistrations: Accessor<Array<SequenceRegistrationView>>
|
|
22
23
|
heldKeys: Accessor<Array<string>>
|
|
23
24
|
heldCodes: Accessor<Record<string, string>>
|
|
25
|
+
/** Monotonic clock for sequence in-progress UI (updates while a sequence match is active). */
|
|
26
|
+
sequenceProgressNow: Accessor<number>
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
const HotkeysDevtoolsContext = createContext<HotkeysDevtoolsContextType>({
|
|
@@ -28,6 +31,7 @@ const HotkeysDevtoolsContext = createContext<HotkeysDevtoolsContextType>({
|
|
|
28
31
|
sequenceRegistrations: () => [],
|
|
29
32
|
heldKeys: () => [],
|
|
30
33
|
heldCodes: () => ({}),
|
|
34
|
+
sequenceProgressNow: () => Date.now(),
|
|
31
35
|
})
|
|
32
36
|
|
|
33
37
|
export function HotkeysContextProvider(props: { children: any }) {
|
|
@@ -76,6 +80,36 @@ export function HotkeysContextProvider(props: { children: any }) {
|
|
|
76
80
|
onCleanup(() => unsubscribe())
|
|
77
81
|
})
|
|
78
82
|
|
|
83
|
+
const [sequenceProgressNow, setSequenceProgressNow] = createSignal(Date.now())
|
|
84
|
+
|
|
85
|
+
createEffect(() => {
|
|
86
|
+
const regs = sequenceRegistrations()
|
|
87
|
+
let id: ReturnType<typeof setInterval> | undefined
|
|
88
|
+
|
|
89
|
+
const tick = () => {
|
|
90
|
+
const t = Date.now()
|
|
91
|
+
setSequenceProgressNow(t)
|
|
92
|
+
if (!needsSequenceProgressClock(sequenceRegistrations(), t)) {
|
|
93
|
+
if (id !== undefined) {
|
|
94
|
+
clearInterval(id)
|
|
95
|
+
id = undefined
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (needsSequenceProgressClock(regs, Date.now())) {
|
|
101
|
+
id = setInterval(tick, 50)
|
|
102
|
+
} else {
|
|
103
|
+
setSequenceProgressNow(Date.now())
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onCleanup(() => {
|
|
107
|
+
if (id !== undefined) {
|
|
108
|
+
clearInterval(id)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
79
113
|
return (
|
|
80
114
|
<HotkeysDevtoolsContext.Provider
|
|
81
115
|
value={{
|
|
@@ -83,6 +117,7 @@ export function HotkeysContextProvider(props: { children: any }) {
|
|
|
83
117
|
sequenceRegistrations,
|
|
84
118
|
heldKeys,
|
|
85
119
|
heldCodes,
|
|
120
|
+
sequenceProgressNow,
|
|
86
121
|
}}
|
|
87
122
|
>
|
|
88
123
|
{props.children}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { For, Show, createMemo } from 'solid-js'
|
|
2
|
+
import clsx from 'clsx'
|
|
2
3
|
import { formatForDisplay, formatHotkeySequence } from '@tanstack/hotkeys'
|
|
3
4
|
import { useStyles } from '../styles/use-styles'
|
|
4
5
|
import { useHotkeysDevtoolsState } from '../HotkeysContextProvider'
|
|
6
|
+
import { effectiveSequenceMatchedSteps } from '../sequence-progress'
|
|
5
7
|
import { ActionButtons } from './ActionButtons'
|
|
6
8
|
import type {
|
|
7
9
|
ConflictBehavior,
|
|
@@ -368,15 +370,23 @@ function SequenceDetails(props: {
|
|
|
368
370
|
styles: ReturnType<typeof useStyles>
|
|
369
371
|
}) {
|
|
370
372
|
const reg = () => props.registration
|
|
373
|
+
const liveReg = createMemo(
|
|
374
|
+
() =>
|
|
375
|
+
props.state.sequenceRegistrations().find((r) => r.id === reg().id) ??
|
|
376
|
+
reg(),
|
|
377
|
+
)
|
|
378
|
+
const matchedSteps = createMemo(() =>
|
|
379
|
+
effectiveSequenceMatchedSteps(liveReg(), props.state.sequenceProgressNow()),
|
|
380
|
+
)
|
|
371
381
|
const targetConflicts = createMemo(() =>
|
|
372
382
|
props.findSequenceTargetConflicts(
|
|
373
|
-
|
|
383
|
+
liveReg(),
|
|
374
384
|
props.state.sequenceRegistrations(),
|
|
375
385
|
),
|
|
376
386
|
)
|
|
377
387
|
const scopeConflicts = createMemo(() =>
|
|
378
388
|
props.findSequenceScopeConflicts(
|
|
379
|
-
|
|
389
|
+
liveReg(),
|
|
380
390
|
props.state.sequenceRegistrations(),
|
|
381
391
|
),
|
|
382
392
|
)
|
|
@@ -385,7 +395,7 @@ function SequenceDetails(props: {
|
|
|
385
395
|
...scopeConflicts(),
|
|
386
396
|
])
|
|
387
397
|
const conflictBehavior = (): ConflictBehavior =>
|
|
388
|
-
|
|
398
|
+
liveReg().options.conflictBehavior ?? 'warn'
|
|
389
399
|
|
|
390
400
|
const styles = props.styles
|
|
391
401
|
|
|
@@ -393,18 +403,48 @@ function SequenceDetails(props: {
|
|
|
393
403
|
<>
|
|
394
404
|
<div class={styles().stateHeader}>
|
|
395
405
|
<div class={styles().stateTitle}>
|
|
396
|
-
{
|
|
406
|
+
<For each={liveReg().sequence}>
|
|
407
|
+
{(step, i) => (
|
|
408
|
+
<span>
|
|
409
|
+
<Show when={i() > 0}> </Show>
|
|
410
|
+
<span
|
|
411
|
+
class={
|
|
412
|
+
i() < matchedSteps()
|
|
413
|
+
? styles().sequenceStepMatched
|
|
414
|
+
: undefined
|
|
415
|
+
}
|
|
416
|
+
>
|
|
417
|
+
{props.formatForDisplay(step)}
|
|
418
|
+
</span>
|
|
419
|
+
</span>
|
|
420
|
+
)}
|
|
421
|
+
</For>
|
|
397
422
|
</div>
|
|
398
423
|
<div class={styles().infoGrid}>
|
|
399
424
|
<div class={styles().infoLabel}>ID</div>
|
|
400
|
-
<div class={styles().infoValueMono}>{
|
|
425
|
+
<div class={styles().infoValueMono}>{liveReg().id}</div>
|
|
401
426
|
<div class={styles().infoLabel}>Sequence</div>
|
|
402
427
|
<div class={styles().infoValueMono}>
|
|
403
|
-
{
|
|
428
|
+
<For each={liveReg().sequence}>
|
|
429
|
+
{(step, i) => (
|
|
430
|
+
<span>
|
|
431
|
+
<Show when={i() > 0}> </Show>
|
|
432
|
+
<span
|
|
433
|
+
class={
|
|
434
|
+
i() < matchedSteps()
|
|
435
|
+
? styles().sequenceStepMatched
|
|
436
|
+
: undefined
|
|
437
|
+
}
|
|
438
|
+
>
|
|
439
|
+
{props.formatForDisplay(step)}
|
|
440
|
+
</span>
|
|
441
|
+
</span>
|
|
442
|
+
)}
|
|
443
|
+
</For>
|
|
404
444
|
</div>
|
|
405
445
|
<div class={styles().infoLabel}>Target</div>
|
|
406
446
|
<div class={styles().infoValueMono}>
|
|
407
|
-
{props.getTargetDescription(
|
|
447
|
+
{props.getTargetDescription(liveReg().target)}
|
|
408
448
|
</div>
|
|
409
449
|
</div>
|
|
410
450
|
</div>
|
|
@@ -413,13 +453,18 @@ function SequenceDetails(props: {
|
|
|
413
453
|
<div class={styles().detailSection}>
|
|
414
454
|
<div class={styles().detailSectionHeader}>Key Breakdown</div>
|
|
415
455
|
<div class={styles().keyBreakdown}>
|
|
416
|
-
<For each={
|
|
456
|
+
<For each={liveReg().sequence}>
|
|
417
457
|
{(step, i) => (
|
|
418
458
|
<>
|
|
419
459
|
<Show when={i() > 0}>
|
|
420
460
|
<span class={styles().keyBreakdownPlus}>→</span>
|
|
421
461
|
</Show>
|
|
422
|
-
<span
|
|
462
|
+
<span
|
|
463
|
+
class={clsx(
|
|
464
|
+
styles().keyCapLarge,
|
|
465
|
+
i() < matchedSteps() && styles().keyCapLargeInProgress,
|
|
466
|
+
)}
|
|
467
|
+
>
|
|
423
468
|
{props.formatForDisplay(step)}
|
|
424
469
|
</span>
|
|
425
470
|
</>
|
|
@@ -430,7 +475,7 @@ function SequenceDetails(props: {
|
|
|
430
475
|
|
|
431
476
|
<div class={styles().detailSection}>
|
|
432
477
|
<div class={styles().detailSectionHeader}>Actions</div>
|
|
433
|
-
<ActionButtons registration={
|
|
478
|
+
<ActionButtons registration={liveReg()} />
|
|
434
479
|
</div>
|
|
435
480
|
|
|
436
481
|
<div class={styles().detailSection}>
|
|
@@ -440,60 +485,60 @@ function SequenceDetails(props: {
|
|
|
440
485
|
<span class={styles().optionLabel}>enabled</span>
|
|
441
486
|
<span
|
|
442
487
|
class={
|
|
443
|
-
|
|
488
|
+
liveReg().options.enabled !== false
|
|
444
489
|
? styles().optionValueTrue
|
|
445
490
|
: styles().optionValueFalse
|
|
446
491
|
}
|
|
447
492
|
>
|
|
448
|
-
{String(
|
|
493
|
+
{String(liveReg().options.enabled !== false)}
|
|
449
494
|
</span>
|
|
450
495
|
</div>
|
|
451
496
|
<div class={styles().optionRow}>
|
|
452
497
|
<span class={styles().optionLabel}>eventType</span>
|
|
453
498
|
<span class={styles().optionValue}>
|
|
454
|
-
{
|
|
499
|
+
{liveReg().options.eventType ?? 'keydown'}
|
|
455
500
|
</span>
|
|
456
501
|
</div>
|
|
457
502
|
<div class={styles().optionRow}>
|
|
458
503
|
<span class={styles().optionLabel}>timeout</span>
|
|
459
504
|
<span class={styles().optionValue}>
|
|
460
|
-
{
|
|
505
|
+
{liveReg().options.timeout ?? 1000}ms
|
|
461
506
|
</span>
|
|
462
507
|
</div>
|
|
463
508
|
<div class={styles().optionRow}>
|
|
464
509
|
<span class={styles().optionLabel}>preventDefault</span>
|
|
465
510
|
<span
|
|
466
511
|
class={
|
|
467
|
-
|
|
512
|
+
liveReg().options.preventDefault
|
|
468
513
|
? styles().optionValueTrue
|
|
469
514
|
: styles().optionValueFalse
|
|
470
515
|
}
|
|
471
516
|
>
|
|
472
|
-
{String(!!
|
|
517
|
+
{String(!!liveReg().options.preventDefault)}
|
|
473
518
|
</span>
|
|
474
519
|
</div>
|
|
475
520
|
<div class={styles().optionRow}>
|
|
476
521
|
<span class={styles().optionLabel}>stopPropagation</span>
|
|
477
522
|
<span
|
|
478
523
|
class={
|
|
479
|
-
|
|
524
|
+
liveReg().options.stopPropagation
|
|
480
525
|
? styles().optionValueTrue
|
|
481
526
|
: styles().optionValueFalse
|
|
482
527
|
}
|
|
483
528
|
>
|
|
484
|
-
{String(!!
|
|
529
|
+
{String(!!liveReg().options.stopPropagation)}
|
|
485
530
|
</span>
|
|
486
531
|
</div>
|
|
487
532
|
<div class={styles().optionRow}>
|
|
488
533
|
<span class={styles().optionLabel}>ignoreInputs</span>
|
|
489
534
|
<span
|
|
490
535
|
class={
|
|
491
|
-
|
|
536
|
+
liveReg().options.ignoreInputs !== false
|
|
492
537
|
? styles().optionValueTrue
|
|
493
538
|
: styles().optionValueFalse
|
|
494
539
|
}
|
|
495
540
|
>
|
|
496
|
-
{String(
|
|
541
|
+
{String(liveReg().options.ignoreInputs !== false)}
|
|
497
542
|
</span>
|
|
498
543
|
</div>
|
|
499
544
|
<div class={styles().optionRow}>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { For, Show, createEffect, createMemo, createSignal, on } from 'solid-js'
|
|
2
2
|
import clsx from 'clsx'
|
|
3
|
-
import { formatForDisplay
|
|
3
|
+
import { formatForDisplay } from '@tanstack/hotkeys'
|
|
4
4
|
import { useStyles } from '../styles/use-styles'
|
|
5
5
|
import { useHotkeysDevtoolsState } from '../HotkeysContextProvider'
|
|
6
|
+
import { effectiveSequenceMatchedSteps } from '../sequence-progress'
|
|
6
7
|
import type {
|
|
7
8
|
ConflictBehavior,
|
|
8
9
|
HotkeyRegistration,
|
|
@@ -112,6 +113,182 @@ function findSequenceScopeConflicts(
|
|
|
112
113
|
)
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
function SequenceListRow(props: {
|
|
117
|
+
reg: SequenceRegistrationView
|
|
118
|
+
getSequences: () => Array<SequenceRegistrationView>
|
|
119
|
+
sequenceProgressNow: () => number
|
|
120
|
+
selectedId: () => string | null
|
|
121
|
+
setSelectedId: (id: string | null) => void
|
|
122
|
+
}) {
|
|
123
|
+
const styles = useStyles()
|
|
124
|
+
|
|
125
|
+
const sequenceRegistrations = () => props.getSequences()
|
|
126
|
+
|
|
127
|
+
const targetConflicts = () =>
|
|
128
|
+
findSequenceTargetConflicts(props.reg, sequenceRegistrations())
|
|
129
|
+
const scopeConflicts = () =>
|
|
130
|
+
findSequenceScopeConflicts(props.reg, sequenceRegistrations())
|
|
131
|
+
|
|
132
|
+
const hasTargetConflict = () => targetConflicts().length > 0
|
|
133
|
+
const hasScopeConflict = () => scopeConflicts().length > 0
|
|
134
|
+
|
|
135
|
+
const conflictBehavior = (): ConflictBehavior =>
|
|
136
|
+
props.reg.options.conflictBehavior ?? 'warn'
|
|
137
|
+
|
|
138
|
+
const targetConflictBadge = () => {
|
|
139
|
+
const behavior = conflictBehavior()
|
|
140
|
+
const c = targetConflicts()
|
|
141
|
+
if (behavior === 'allow') {
|
|
142
|
+
return {
|
|
143
|
+
style: 'badgeAllow' as const,
|
|
144
|
+
label: '~',
|
|
145
|
+
tooltip: `Allowed: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: allow)`,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (behavior === 'error') {
|
|
149
|
+
return {
|
|
150
|
+
style: 'badgeError' as const,
|
|
151
|
+
label: '!',
|
|
152
|
+
tooltip: `Error: ${c.length} conflicting binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: error)`,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
style: 'badgeConflict' as const,
|
|
157
|
+
label: '!',
|
|
158
|
+
tooltip: `Warning: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target`,
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const scopeConflictTooltip = () => {
|
|
163
|
+
const c = scopeConflicts()
|
|
164
|
+
return `Info: ${c.length} binding${c.length > 1 ? 's' : ''} with same sequence on different target${c.length > 1 ? 's' : ''}`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const enabled = () => props.reg.options.enabled !== false
|
|
168
|
+
|
|
169
|
+
const liveSequenceReg = createMemo(
|
|
170
|
+
() =>
|
|
171
|
+
sequenceRegistrations().find((r) => r.id === props.reg.id) ?? props.reg,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
const triggerCount = () =>
|
|
175
|
+
sequenceRegistrations().find((r) => r.id === props.reg.id)?.triggerCount ??
|
|
176
|
+
props.reg.triggerCount
|
|
177
|
+
|
|
178
|
+
const matchedSteps = createMemo(() =>
|
|
179
|
+
effectiveSequenceMatchedSteps(
|
|
180
|
+
liveSequenceReg(),
|
|
181
|
+
props.sequenceProgressNow(),
|
|
182
|
+
),
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const [prevCount, setPrevCount] = createSignal(props.reg.triggerCount)
|
|
186
|
+
const [pulsing, setPulsing] = createSignal(false)
|
|
187
|
+
|
|
188
|
+
createEffect(
|
|
189
|
+
on(triggerCount, (current) => {
|
|
190
|
+
if (current > prevCount()) {
|
|
191
|
+
setPulsing(true)
|
|
192
|
+
setTimeout(() => setPulsing(false), 600)
|
|
193
|
+
}
|
|
194
|
+
setPrevCount(current)
|
|
195
|
+
}),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div
|
|
200
|
+
class={clsx(
|
|
201
|
+
styles().hotkeyRow,
|
|
202
|
+
props.selectedId() === props.reg.id && styles().hotkeyRowSelected,
|
|
203
|
+
pulsing() && styles().hotkeyRowTriggered,
|
|
204
|
+
)}
|
|
205
|
+
onClick={() => props.setSelectedId(props.reg.id)}
|
|
206
|
+
>
|
|
207
|
+
<span class={styles().hotkeyLabel}>
|
|
208
|
+
<For each={liveSequenceReg().sequence}>
|
|
209
|
+
{(step, i) => (
|
|
210
|
+
<span>
|
|
211
|
+
<Show when={i() > 0}> </Show>
|
|
212
|
+
<span
|
|
213
|
+
class={
|
|
214
|
+
i() < matchedSteps()
|
|
215
|
+
? styles().sequenceStepMatched
|
|
216
|
+
: undefined
|
|
217
|
+
}
|
|
218
|
+
>
|
|
219
|
+
{formatForDisplay(step)}
|
|
220
|
+
</span>
|
|
221
|
+
</span>
|
|
222
|
+
)}
|
|
223
|
+
</For>
|
|
224
|
+
</span>
|
|
225
|
+
<Show when={triggerCount() > 0}>
|
|
226
|
+
<span class={styles().triggerCount}>x{triggerCount()}</span>
|
|
227
|
+
</Show>
|
|
228
|
+
<div class={styles().hotkeyBadges}>
|
|
229
|
+
{hasTargetConflict() && (
|
|
230
|
+
<span
|
|
231
|
+
class={clsx(
|
|
232
|
+
styles().badge,
|
|
233
|
+
styles()[targetConflictBadge().style],
|
|
234
|
+
styles().tooltip,
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
{targetConflictBadge().label}
|
|
238
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
239
|
+
{targetConflictBadge().tooltip}
|
|
240
|
+
</span>
|
|
241
|
+
</span>
|
|
242
|
+
)}
|
|
243
|
+
{hasScopeConflict() && (
|
|
244
|
+
<span
|
|
245
|
+
class={clsx(styles().badge, styles().badgeInfo, styles().tooltip)}
|
|
246
|
+
>
|
|
247
|
+
i
|
|
248
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
249
|
+
{scopeConflictTooltip()}
|
|
250
|
+
</span>
|
|
251
|
+
</span>
|
|
252
|
+
)}
|
|
253
|
+
<span
|
|
254
|
+
class={clsx(
|
|
255
|
+
styles().badge,
|
|
256
|
+
enabled() ? styles().badgeEnabled : styles().badgeDisabled,
|
|
257
|
+
styles().tooltip,
|
|
258
|
+
)}
|
|
259
|
+
>
|
|
260
|
+
{enabled() ? 'on' : 'off'}
|
|
261
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
262
|
+
{enabled() ? 'Sequence is enabled' : 'Sequence is disabled'}
|
|
263
|
+
</span>
|
|
264
|
+
</span>
|
|
265
|
+
<span
|
|
266
|
+
class={clsx(
|
|
267
|
+
styles().badge,
|
|
268
|
+
(props.reg.options.eventType ?? 'keydown') === 'keydown'
|
|
269
|
+
? styles().badgeKeydown
|
|
270
|
+
: styles().badgeKeyup,
|
|
271
|
+
styles().tooltip,
|
|
272
|
+
)}
|
|
273
|
+
>
|
|
274
|
+
{props.reg.options.eventType ?? 'keydown'}
|
|
275
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
276
|
+
Fires on {props.reg.options.eventType ?? 'keydown'} event
|
|
277
|
+
</span>
|
|
278
|
+
</span>
|
|
279
|
+
<span
|
|
280
|
+
class={clsx(styles().badge, styles().badgeTarget, styles().tooltip)}
|
|
281
|
+
>
|
|
282
|
+
{getTargetLabel(props.reg.target)}
|
|
283
|
+
<span class={styles().tooltipText} data-tooltip>
|
|
284
|
+
{getTargetTooltip(props.reg.target)}
|
|
285
|
+
</span>
|
|
286
|
+
</span>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
115
292
|
export function HotkeyList(props: HotkeyListProps) {
|
|
116
293
|
const styles = useStyles()
|
|
117
294
|
const state = useHotkeysDevtoolsState()
|
|
@@ -284,156 +461,15 @@ export function HotkeyList(props: HotkeyListProps) {
|
|
|
284
461
|
</div>
|
|
285
462
|
<div class={styles().hotkeyList}>
|
|
286
463
|
<For each={sequenceRegistrations()}>
|
|
287
|
-
{(reg) =>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const conflictBehavior = (): ConflictBehavior =>
|
|
297
|
-
reg.options.conflictBehavior ?? 'warn'
|
|
298
|
-
|
|
299
|
-
const targetConflictBadge = () => {
|
|
300
|
-
const behavior = conflictBehavior()
|
|
301
|
-
const c = targetConflicts()
|
|
302
|
-
if (behavior === 'allow') {
|
|
303
|
-
return {
|
|
304
|
-
style: 'badgeAllow' as const,
|
|
305
|
-
label: '~',
|
|
306
|
-
tooltip: `Allowed: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: allow)`,
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (behavior === 'error') {
|
|
310
|
-
return {
|
|
311
|
-
style: 'badgeError' as const,
|
|
312
|
-
label: '!',
|
|
313
|
-
tooltip: `Error: ${c.length} conflicting binding${c.length > 1 ? 's' : ''} on same sequence and target (conflictBehavior: error)`,
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return {
|
|
317
|
-
style: 'badgeConflict' as const,
|
|
318
|
-
label: '!',
|
|
319
|
-
tooltip: `Warning: ${c.length} other binding${c.length > 1 ? 's' : ''} on same sequence and target`,
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const scopeConflictTooltip = () => {
|
|
324
|
-
const c = scopeConflicts()
|
|
325
|
-
return `Info: ${c.length} binding${c.length > 1 ? 's' : ''} with same sequence on different target${c.length > 1 ? 's' : ''}`
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const enabled = () => reg.options.enabled !== false
|
|
329
|
-
|
|
330
|
-
const triggerCount = () =>
|
|
331
|
-
sequenceRegistrations().find((r) => r.id === reg.id)
|
|
332
|
-
?.triggerCount ?? reg.triggerCount
|
|
333
|
-
|
|
334
|
-
const [prevCount, setPrevCount] = createSignal(reg.triggerCount)
|
|
335
|
-
const [pulsing, setPulsing] = createSignal(false)
|
|
336
|
-
|
|
337
|
-
createEffect(
|
|
338
|
-
on(triggerCount, (current) => {
|
|
339
|
-
if (current > prevCount()) {
|
|
340
|
-
setPulsing(true)
|
|
341
|
-
setTimeout(() => setPulsing(false), 600)
|
|
342
|
-
}
|
|
343
|
-
setPrevCount(current)
|
|
344
|
-
}),
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
return (
|
|
348
|
-
<div
|
|
349
|
-
class={clsx(
|
|
350
|
-
styles().hotkeyRow,
|
|
351
|
-
props.selectedId() === reg.id && styles().hotkeyRowSelected,
|
|
352
|
-
pulsing() && styles().hotkeyRowTriggered,
|
|
353
|
-
)}
|
|
354
|
-
onClick={() => props.setSelectedId(reg.id)}
|
|
355
|
-
>
|
|
356
|
-
<span class={styles().hotkeyLabel}>
|
|
357
|
-
{formatHotkeySequence(reg.sequence)}
|
|
358
|
-
</span>
|
|
359
|
-
<Show when={triggerCount() > 0}>
|
|
360
|
-
<span class={styles().triggerCount}>x{triggerCount()}</span>
|
|
361
|
-
</Show>
|
|
362
|
-
<div class={styles().hotkeyBadges}>
|
|
363
|
-
{hasTargetConflict() && (
|
|
364
|
-
<span
|
|
365
|
-
class={clsx(
|
|
366
|
-
styles().badge,
|
|
367
|
-
styles()[targetConflictBadge().style],
|
|
368
|
-
styles().tooltip,
|
|
369
|
-
)}
|
|
370
|
-
>
|
|
371
|
-
{targetConflictBadge().label}
|
|
372
|
-
<span class={styles().tooltipText} data-tooltip>
|
|
373
|
-
{targetConflictBadge().tooltip}
|
|
374
|
-
</span>
|
|
375
|
-
</span>
|
|
376
|
-
)}
|
|
377
|
-
{hasScopeConflict() && (
|
|
378
|
-
<span
|
|
379
|
-
class={clsx(
|
|
380
|
-
styles().badge,
|
|
381
|
-
styles().badgeInfo,
|
|
382
|
-
styles().tooltip,
|
|
383
|
-
)}
|
|
384
|
-
>
|
|
385
|
-
i
|
|
386
|
-
<span class={styles().tooltipText} data-tooltip>
|
|
387
|
-
{scopeConflictTooltip()}
|
|
388
|
-
</span>
|
|
389
|
-
</span>
|
|
390
|
-
)}
|
|
391
|
-
<span
|
|
392
|
-
class={clsx(
|
|
393
|
-
styles().badge,
|
|
394
|
-
enabled()
|
|
395
|
-
? styles().badgeEnabled
|
|
396
|
-
: styles().badgeDisabled,
|
|
397
|
-
styles().tooltip,
|
|
398
|
-
)}
|
|
399
|
-
>
|
|
400
|
-
{enabled() ? 'on' : 'off'}
|
|
401
|
-
<span class={styles().tooltipText} data-tooltip>
|
|
402
|
-
{enabled()
|
|
403
|
-
? 'Sequence is enabled'
|
|
404
|
-
: 'Sequence is disabled'}
|
|
405
|
-
</span>
|
|
406
|
-
</span>
|
|
407
|
-
<span
|
|
408
|
-
class={clsx(
|
|
409
|
-
styles().badge,
|
|
410
|
-
(reg.options.eventType ?? 'keydown') === 'keydown'
|
|
411
|
-
? styles().badgeKeydown
|
|
412
|
-
: styles().badgeKeyup,
|
|
413
|
-
styles().tooltip,
|
|
414
|
-
)}
|
|
415
|
-
>
|
|
416
|
-
{reg.options.eventType ?? 'keydown'}
|
|
417
|
-
<span class={styles().tooltipText} data-tooltip>
|
|
418
|
-
Fires on {reg.options.eventType ?? 'keydown'} event
|
|
419
|
-
</span>
|
|
420
|
-
</span>
|
|
421
|
-
<span
|
|
422
|
-
class={clsx(
|
|
423
|
-
styles().badge,
|
|
424
|
-
styles().badgeTarget,
|
|
425
|
-
styles().tooltip,
|
|
426
|
-
)}
|
|
427
|
-
>
|
|
428
|
-
{getTargetLabel(reg.target)}
|
|
429
|
-
<span class={styles().tooltipText} data-tooltip>
|
|
430
|
-
{getTargetTooltip(reg.target)}
|
|
431
|
-
</span>
|
|
432
|
-
</span>
|
|
433
|
-
</div>
|
|
434
|
-
</div>
|
|
435
|
-
)
|
|
436
|
-
}}
|
|
464
|
+
{(reg) => (
|
|
465
|
+
<SequenceListRow
|
|
466
|
+
reg={reg}
|
|
467
|
+
getSequences={sequenceRegistrations}
|
|
468
|
+
sequenceProgressNow={state.sequenceProgressNow}
|
|
469
|
+
selectedId={props.selectedId}
|
|
470
|
+
setSelectedId={props.setSelectedId}
|
|
471
|
+
/>
|
|
472
|
+
)}
|
|
437
473
|
</For>
|
|
438
474
|
</div>
|
|
439
475
|
</>
|