@tanstack/hotkeys-devtools 0.4.4 → 0.7.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/README.md +1 -1
- package/dist/HotkeysContextProvider.js +25 -2
- package/dist/HotkeysContextProvider.js.map +1 -1
- package/dist/components/DetailsPanel.js +367 -217
- package/dist/components/DetailsPanel.js.map +1 -1
- package/dist/components/HeldKeysTopbar.js +7 -4
- package/dist/components/HeldKeysTopbar.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 +107 -2
- 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 +201 -44
- package/src/components/HeldKeysTopbar.tsx +9 -5
- 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 +109 -2
|
@@ -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
|
</>
|
package/src/components/Shell.tsx
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createEffect,
|
|
3
|
+
createMemo,
|
|
4
|
+
createSignal,
|
|
5
|
+
onCleanup,
|
|
6
|
+
onMount,
|
|
7
|
+
} from 'solid-js'
|
|
2
8
|
import { Header, HeaderLogo, MainPanel } from '@tanstack/devtools-ui'
|
|
3
9
|
import { useStyles } from '../styles/use-styles'
|
|
4
10
|
import { useHotkeysDevtoolsState } from '../HotkeysContextProvider'
|
|
@@ -6,12 +12,33 @@ import { HeldKeysBar } from './HeldKeysTopbar'
|
|
|
6
12
|
import { HotkeyList } from './HotkeyList'
|
|
7
13
|
import { DetailsPanel } from './DetailsPanel'
|
|
8
14
|
|
|
15
|
+
function readResizeObserverBlockSize(entry: ResizeObserverEntry): number {
|
|
16
|
+
const [box] = entry.borderBoxSize
|
|
17
|
+
if (box !== undefined) return box.blockSize
|
|
18
|
+
return entry.contentRect.height
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** TanStack devtools plugin pane; percentage heights break without this. */
|
|
22
|
+
function resolvePluginHost(el: HTMLElement): HTMLElement | null {
|
|
23
|
+
const found = el.closest('[id^="plugin-container-"]')
|
|
24
|
+
if (found instanceof HTMLElement) return found
|
|
25
|
+
let n: HTMLElement | null = el
|
|
26
|
+
for (let i = 0; i < 3 && n; i++) {
|
|
27
|
+
n = n.parentElement
|
|
28
|
+
}
|
|
29
|
+
return n
|
|
30
|
+
}
|
|
31
|
+
|
|
9
32
|
export function Shell() {
|
|
10
33
|
const styles = useStyles()
|
|
11
34
|
const state = useHotkeysDevtoolsState()
|
|
12
35
|
const [selectedId, setSelectedId] = createSignal<string | null>(null)
|
|
13
36
|
const [leftPanelWidth, setLeftPanelWidth] = createSignal(300)
|
|
14
37
|
const [isDragging, setIsDragging] = createSignal(false)
|
|
38
|
+
const [shellRootEl, setShellRootEl] = createSignal<HTMLElement | null>(null)
|
|
39
|
+
const [slotHeightPx, setSlotHeightPx] = createSignal<number | undefined>(
|
|
40
|
+
undefined,
|
|
41
|
+
)
|
|
15
42
|
|
|
16
43
|
const selectedRegistration = createMemo(() => {
|
|
17
44
|
const id = selectedId()
|
|
@@ -60,45 +87,85 @@ export function Shell() {
|
|
|
60
87
|
document.removeEventListener('mouseup', handleMouseUp)
|
|
61
88
|
})
|
|
62
89
|
|
|
90
|
+
createEffect(() => {
|
|
91
|
+
const el = shellRootEl()
|
|
92
|
+
if (!el || typeof ResizeObserver === 'undefined') return
|
|
93
|
+
const pluginHost = resolvePluginHost(el)
|
|
94
|
+
if (!pluginHost) return
|
|
95
|
+
|
|
96
|
+
const applyHeight = (entry?: ResizeObserverEntry) => {
|
|
97
|
+
const clientH = pluginHost.clientHeight
|
|
98
|
+
const fromObserver =
|
|
99
|
+
entry !== undefined ? readResizeObserverBlockSize(entry) : 0
|
|
100
|
+
const h = clientH > 0 ? clientH : fromObserver
|
|
101
|
+
if (h > 0) setSlotHeightPx(Math.round(h))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
applyHeight()
|
|
105
|
+
const ro = new ResizeObserver((entries) => {
|
|
106
|
+
applyHeight(entries[0])
|
|
107
|
+
})
|
|
108
|
+
ro.observe(pluginHost)
|
|
109
|
+
onCleanup(() => ro.disconnect())
|
|
110
|
+
})
|
|
111
|
+
|
|
63
112
|
return (
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
style={{
|
|
83
|
-
width: `${leftPanelWidth()}px`,
|
|
84
|
-
'min-width': '150px',
|
|
85
|
-
'max-width': '800px',
|
|
113
|
+
<div
|
|
114
|
+
ref={(el) => setShellRootEl(el)}
|
|
115
|
+
class={styles().shellRoot}
|
|
116
|
+
style={{
|
|
117
|
+
...(slotHeightPx() !== undefined
|
|
118
|
+
? {
|
|
119
|
+
height: `${slotHeightPx()}px`,
|
|
120
|
+
'max-height': `${slotHeightPx()}px`,
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
<MainPanel class={styles().mainPanelShell}>
|
|
126
|
+
<Header>
|
|
127
|
+
<HeaderLogo
|
|
128
|
+
flavor={{
|
|
129
|
+
light: 'oklch(51.4% 0.222 16.935)',
|
|
130
|
+
dark: 'oklch(58.6% 0.253 17.585)',
|
|
86
131
|
}}
|
|
87
132
|
>
|
|
88
|
-
|
|
89
|
-
</
|
|
133
|
+
TanStack Hotkeys
|
|
134
|
+
</HeaderLogo>
|
|
135
|
+
</Header>
|
|
136
|
+
|
|
137
|
+
<div class={styles().mainContainer}>
|
|
138
|
+
<HeldKeysBar />
|
|
139
|
+
|
|
140
|
+
<div class={styles().panelsContainer}>
|
|
141
|
+
<div
|
|
142
|
+
class={styles().leftPanel}
|
|
143
|
+
style={{
|
|
144
|
+
width: `${leftPanelWidth()}px`,
|
|
145
|
+
'min-width': '150px',
|
|
146
|
+
'max-width': '800px',
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
<div class={styles().leftPanelScroll}>
|
|
150
|
+
<HotkeyList
|
|
151
|
+
selectedId={selectedId}
|
|
152
|
+
setSelectedId={setSelectedId}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
90
156
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
<div
|
|
158
|
+
class={`${styles().dragHandle} ${isDragging() ? 'dragging' : ''}`}
|
|
159
|
+
onMouseDown={handleMouseDown}
|
|
160
|
+
/>
|
|
95
161
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
162
|
+
<div class={styles().rightPanel} style={{ flex: 1 }}>
|
|
163
|
+
<div class={styles().panelHeader}>Details</div>
|
|
164
|
+
<DetailsPanel selectedRegistration={selectedRegistration} />
|
|
165
|
+
</div>
|
|
99
166
|
</div>
|
|
100
167
|
</div>
|
|
101
|
-
</
|
|
102
|
-
</
|
|
168
|
+
</MainPanel>
|
|
169
|
+
</div>
|
|
103
170
|
)
|
|
104
171
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DEFAULT_SEQUENCE_TIMEOUT } from '@tanstack/hotkeys'
|
|
2
|
+
import type { SequenceRegistrationView } from '@tanstack/hotkeys'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Matched step count for UI, accounting for inter-key timeout even if the
|
|
6
|
+
* manager has not processed another event yet.
|
|
7
|
+
*/
|
|
8
|
+
export function effectiveSequenceMatchedSteps(
|
|
9
|
+
reg: SequenceRegistrationView,
|
|
10
|
+
now: number,
|
|
11
|
+
): number {
|
|
12
|
+
const timeout = reg.options.timeout ?? DEFAULT_SEQUENCE_TIMEOUT
|
|
13
|
+
if (
|
|
14
|
+
reg.matchedStepCount <= 0 ||
|
|
15
|
+
reg.partialMatchLastKeyTime <= 0 ||
|
|
16
|
+
now - reg.partialMatchLastKeyTime > timeout
|
|
17
|
+
) {
|
|
18
|
+
return 0
|
|
19
|
+
}
|
|
20
|
+
return reg.matchedStepCount
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether the devtools should run a short interval to refresh sequence progress UI. */
|
|
24
|
+
export function needsSequenceProgressClock(
|
|
25
|
+
regs: Array<SequenceRegistrationView>,
|
|
26
|
+
now: number,
|
|
27
|
+
): boolean {
|
|
28
|
+
return regs.some((reg) => effectiveSequenceMatchedSteps(reg, now) > 0)
|
|
29
|
+
}
|