@tanstack/pacer-devtools 1.3.1 → 1.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/README.md +21 -6
- package/dist/{components-hqYwCNDx.js → components-BwcTEJfm.js} +34 -14
- package/dist/{components-DZv7GBzY.js → components-D6zc5Wjw.js} +49 -29
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1 -1
- package/dist/production/index.d.ts +2 -3
- package/dist/production/index.js +1 -1
- package/dist/production/server.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +12 -12
- package/src/PacerContextProvider.tsx +0 -172
- package/src/components/ActionButtons.tsx +0 -170
- package/src/components/DetailsPanel.tsx +0 -62
- package/src/components/Shell.tsx +0 -176
- package/src/components/StateHeader.tsx +0 -139
- package/src/components/UtilList.tsx +0 -62
- package/src/components/UtilStateJsonTree.tsx +0 -25
- package/src/components/index.tsx +0 -19
- package/src/components/util-groups.ts +0 -54
- package/src/core.tsx +0 -9
- package/src/index.ts +0 -10
- package/src/production.ts +0 -5
- package/src/styles/tokens.ts +0 -305
- package/src/styles/use-styles.ts +0 -385
- package/src/utils/read-pacer-store-state.ts +0 -33
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { emitChange } from '@tanstack/pacer'
|
|
2
|
-
import { shallow, useSelector } from '@tanstack/solid-store'
|
|
3
|
-
import { useStyles } from '../styles/use-styles'
|
|
4
|
-
import {
|
|
5
|
-
getPacerUtilStoreState,
|
|
6
|
-
isPacerUtilTanStackStore,
|
|
7
|
-
} from '../utils/read-pacer-store-state'
|
|
8
|
-
import type { PacerEventName } from '@tanstack/pacer'
|
|
9
|
-
|
|
10
|
-
type ActionButtonsProps = {
|
|
11
|
-
instance: any
|
|
12
|
-
utilName: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function ActionButtons(props: ActionButtonsProps) {
|
|
16
|
-
const styles = useStyles()
|
|
17
|
-
const utilInstance = props.instance
|
|
18
|
-
const store = utilInstance?.store
|
|
19
|
-
|
|
20
|
-
const stateAccessor = isPacerUtilTanStackStore(store)
|
|
21
|
-
? useSelector(
|
|
22
|
-
store as never,
|
|
23
|
-
(s: unknown) =>
|
|
24
|
-
s !== null && s !== undefined && typeof s === 'object'
|
|
25
|
-
? (s as Record<string, unknown>)
|
|
26
|
-
: {},
|
|
27
|
-
{ compare: shallow },
|
|
28
|
-
)
|
|
29
|
-
: () => getPacerUtilStoreState(utilInstance) as Record<string, unknown>
|
|
30
|
-
|
|
31
|
-
const getState = () => stateAccessor()
|
|
32
|
-
|
|
33
|
-
const hasPending = () => {
|
|
34
|
-
const u = getState()
|
|
35
|
-
return 'isPending' in u
|
|
36
|
-
}
|
|
37
|
-
const isPending = () => {
|
|
38
|
-
const u = getState()
|
|
39
|
-
return 'isPending' in u ? !!u.isPending : false
|
|
40
|
-
}
|
|
41
|
-
const isEmptyFlag = () => {
|
|
42
|
-
const u = getState()
|
|
43
|
-
return 'isEmpty' in u ? !!u.isEmpty : false
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const hasFlush = typeof utilInstance.flush === 'function'
|
|
47
|
-
const hasCancel = typeof utilInstance.cancel === 'function'
|
|
48
|
-
const hasReset = typeof utilInstance.reset === 'function'
|
|
49
|
-
const hasClear = typeof utilInstance.clear === 'function'
|
|
50
|
-
const hasStart = typeof utilInstance.start === 'function'
|
|
51
|
-
const hasStop = typeof utilInstance.stop === 'function'
|
|
52
|
-
const hasStartStop = hasStart && hasStop
|
|
53
|
-
|
|
54
|
-
const isRunning = () => {
|
|
55
|
-
const u = getState()
|
|
56
|
-
return 'isRunning' in u ? !!u.isRunning : true
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!hasPending() && !hasFlush && !hasCancel && !hasReset && !hasClear) {
|
|
60
|
-
return (
|
|
61
|
-
<div class={styles().sectionEmpty}>
|
|
62
|
-
No actions available for this util
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const emitName = `d-${props.utilName}` as PacerEventName
|
|
68
|
-
const utilNameLower = props.utilName.toLowerCase()
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div class={styles().actionsRow}>
|
|
72
|
-
{hasPending() && (
|
|
73
|
-
<button
|
|
74
|
-
class={styles().actionButton}
|
|
75
|
-
onMouseDown={() => {
|
|
76
|
-
const next = !isPending()
|
|
77
|
-
utilInstance.store.setState((prev: any) => ({
|
|
78
|
-
...prev,
|
|
79
|
-
isPending: next,
|
|
80
|
-
}))
|
|
81
|
-
emitChange(emitName, utilInstance)
|
|
82
|
-
}}
|
|
83
|
-
>
|
|
84
|
-
<span class={styles().actionDotBlue} />
|
|
85
|
-
{isPending() ? 'Restore Pending' : 'Trigger Pending'}
|
|
86
|
-
</button>
|
|
87
|
-
)}
|
|
88
|
-
{hasFlush && (
|
|
89
|
-
<button
|
|
90
|
-
class={styles().actionButton}
|
|
91
|
-
onMouseDown={() => {
|
|
92
|
-
utilInstance.flush()
|
|
93
|
-
emitChange(emitName, utilInstance)
|
|
94
|
-
}}
|
|
95
|
-
disabled={
|
|
96
|
-
(utilNameLower.includes('debouncer') && !isPending()) ||
|
|
97
|
-
(utilNameLower.includes('throttler') && !isPending()) ||
|
|
98
|
-
(utilNameLower.includes('batcher') && isEmptyFlag()) ||
|
|
99
|
-
(utilNameLower.includes('queuer') && isEmptyFlag())
|
|
100
|
-
}
|
|
101
|
-
>
|
|
102
|
-
<span class={styles().actionDotGreen} />
|
|
103
|
-
Flush
|
|
104
|
-
</button>
|
|
105
|
-
)}
|
|
106
|
-
{hasCancel && (
|
|
107
|
-
<button
|
|
108
|
-
class={styles().actionButton}
|
|
109
|
-
onMouseDown={() => {
|
|
110
|
-
utilInstance.cancel()
|
|
111
|
-
emitChange(emitName, utilInstance)
|
|
112
|
-
}}
|
|
113
|
-
disabled={
|
|
114
|
-
(utilNameLower.includes('debouncer') && !isPending()) ||
|
|
115
|
-
(utilNameLower.includes('throttler') && !isPending()) ||
|
|
116
|
-
(utilNameLower.includes('batcher') && isEmptyFlag()) ||
|
|
117
|
-
(utilNameLower.includes('queuer') && isEmptyFlag())
|
|
118
|
-
}
|
|
119
|
-
>
|
|
120
|
-
<span class={styles().actionDotRed} />
|
|
121
|
-
Cancel
|
|
122
|
-
</button>
|
|
123
|
-
)}
|
|
124
|
-
{hasReset && (
|
|
125
|
-
<button
|
|
126
|
-
class={styles().actionButton}
|
|
127
|
-
onMouseDown={() => {
|
|
128
|
-
utilInstance.reset()
|
|
129
|
-
emitChange(emitName, utilInstance)
|
|
130
|
-
}}
|
|
131
|
-
>
|
|
132
|
-
<span class={styles().actionDotYellow} />
|
|
133
|
-
Reset
|
|
134
|
-
</button>
|
|
135
|
-
)}
|
|
136
|
-
{hasClear && (
|
|
137
|
-
<button
|
|
138
|
-
class={styles().actionButton}
|
|
139
|
-
onMouseDown={() => {
|
|
140
|
-
utilInstance.clear()
|
|
141
|
-
emitChange(emitName, utilInstance)
|
|
142
|
-
}}
|
|
143
|
-
disabled={
|
|
144
|
-
(utilNameLower.includes('batcher') && isEmptyFlag()) ||
|
|
145
|
-
(utilNameLower.includes('queuer') && isEmptyFlag())
|
|
146
|
-
}
|
|
147
|
-
>
|
|
148
|
-
<span class={styles().actionDotOrange} />
|
|
149
|
-
Clear
|
|
150
|
-
</button>
|
|
151
|
-
)}
|
|
152
|
-
{hasStartStop && (
|
|
153
|
-
<button
|
|
154
|
-
class={styles().actionButton}
|
|
155
|
-
onMouseDown={() => {
|
|
156
|
-
if (isRunning()) {
|
|
157
|
-
utilInstance.stop()
|
|
158
|
-
} else {
|
|
159
|
-
utilInstance.start()
|
|
160
|
-
}
|
|
161
|
-
emitChange(emitName, utilInstance)
|
|
162
|
-
}}
|
|
163
|
-
>
|
|
164
|
-
<span class={styles().actionDotPurple} />
|
|
165
|
-
{isRunning() ? 'Stop' : 'Start'}
|
|
166
|
-
</button>
|
|
167
|
-
)}
|
|
168
|
-
</div>
|
|
169
|
-
)
|
|
170
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { JsonTree } from '@tanstack/devtools-ui'
|
|
2
|
-
import { useStyles } from '../styles/use-styles'
|
|
3
|
-
import { ActionButtons } from './ActionButtons'
|
|
4
|
-
import { StateHeader } from './StateHeader'
|
|
5
|
-
import { UtilStateJsonTree } from './UtilStateJsonTree'
|
|
6
|
-
|
|
7
|
-
type DetailsPanelProps = {
|
|
8
|
-
selectedInstance: () => { instance: any; type: string } | null
|
|
9
|
-
utilState: () => { lastUpdatedByKey: Record<string, number> }
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function DetailsPanel(props: DetailsPanelProps) {
|
|
13
|
-
const styles = useStyles()
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<div class={styles().stateDetails}>
|
|
17
|
-
{(() => {
|
|
18
|
-
const entry = props.selectedInstance()
|
|
19
|
-
if (!entry) {
|
|
20
|
-
return (
|
|
21
|
-
<div class={styles().noSelection}>
|
|
22
|
-
Select a util from the left panel to view its state
|
|
23
|
-
</div>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<>
|
|
29
|
-
<StateHeader
|
|
30
|
-
selectedInstance={props.selectedInstance}
|
|
31
|
-
utilState={props.utilState}
|
|
32
|
-
/>
|
|
33
|
-
|
|
34
|
-
<div class={styles().detailsGrid}>
|
|
35
|
-
<div class={styles().detailSection}>
|
|
36
|
-
<div class={styles().detailSectionHeader}>Actions</div>
|
|
37
|
-
<ActionButtons
|
|
38
|
-
instance={entry.instance}
|
|
39
|
-
utilName={entry.type}
|
|
40
|
-
/>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div class={styles().detailSection}>
|
|
44
|
-
<div class={styles().detailSectionHeader}>State</div>
|
|
45
|
-
<div class={styles().stateContent}>
|
|
46
|
-
<UtilStateJsonTree instance={entry.instance} />
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<div class={styles().detailSection}>
|
|
51
|
-
<div class={styles().detailSectionHeader}>Options</div>
|
|
52
|
-
<div class={styles().stateContent}>
|
|
53
|
-
<JsonTree value={entry.instance.options as unknown} />
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
</>
|
|
58
|
-
)
|
|
59
|
-
})()}
|
|
60
|
-
</div>
|
|
61
|
-
)
|
|
62
|
-
}
|
package/src/components/Shell.tsx
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createEffect,
|
|
3
|
-
createMemo,
|
|
4
|
-
createSignal,
|
|
5
|
-
onCleanup,
|
|
6
|
-
onMount,
|
|
7
|
-
} from 'solid-js'
|
|
8
|
-
import { Header, HeaderLogo, MainPanel } from '@tanstack/devtools-ui'
|
|
9
|
-
import { useStyles } from '../styles/use-styles'
|
|
10
|
-
import { usePacerDevtoolsState } from '../PacerContextProvider'
|
|
11
|
-
import { UTIL_GROUPS } from './util-groups'
|
|
12
|
-
import { UtilList } from './UtilList'
|
|
13
|
-
import { DetailsPanel } from './DetailsPanel'
|
|
14
|
-
import type { StateKey } from './util-groups'
|
|
15
|
-
|
|
16
|
-
function readResizeObserverBlockSize(entry: ResizeObserverEntry): number {
|
|
17
|
-
const [box] = entry.borderBoxSize
|
|
18
|
-
if (box !== undefined) return box.blockSize
|
|
19
|
-
return entry.contentRect.height
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Outer plugin slot from TanStack Devtools (not the inner React mount div, which can report a tiny height). */
|
|
23
|
-
function resolvePluginHost(el: HTMLElement): HTMLElement | null {
|
|
24
|
-
const byId = el.closest<HTMLElement>('[id^="plugin-container-"]')
|
|
25
|
-
if (byId) return byId
|
|
26
|
-
return (
|
|
27
|
-
el.parentElement?.parentElement?.parentElement ??
|
|
28
|
-
el.parentElement?.parentElement ??
|
|
29
|
-
el.parentElement
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function Shell() {
|
|
34
|
-
const styles = useStyles()
|
|
35
|
-
const state = usePacerDevtoolsState()
|
|
36
|
-
const utilState = () => state
|
|
37
|
-
const [selectedKey, setSelectedKey] = createSignal<string | null>(null)
|
|
38
|
-
const [leftPanelWidth, setLeftPanelWidth] = createSignal(300)
|
|
39
|
-
const [isDragging, setIsDragging] = createSignal(false)
|
|
40
|
-
const [shellRootEl, setShellRootEl] = createSignal<HTMLDivElement | null>(
|
|
41
|
-
null,
|
|
42
|
-
)
|
|
43
|
-
const [slotHeightPx, setSlotHeightPx] = createSignal<number | undefined>(
|
|
44
|
-
undefined,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
const selectedInstance = createMemo(() => {
|
|
48
|
-
const key = selectedKey()
|
|
49
|
-
if (!key) return null
|
|
50
|
-
for (const group of UTIL_GROUPS) {
|
|
51
|
-
const instance = (utilState() as unknown as Record<StateKey, Array<any>>)[
|
|
52
|
-
group.key
|
|
53
|
-
].find((inst) => inst.key === key)
|
|
54
|
-
if (instance) return { instance, type: group.displayName }
|
|
55
|
-
}
|
|
56
|
-
return null
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
let dragStartX = 0
|
|
60
|
-
let dragStartWidth = 0
|
|
61
|
-
|
|
62
|
-
const handleMouseDown = (e: MouseEvent) => {
|
|
63
|
-
e.preventDefault()
|
|
64
|
-
e.stopPropagation()
|
|
65
|
-
setIsDragging(true)
|
|
66
|
-
document.body.style.cursor = 'col-resize'
|
|
67
|
-
document.body.style.userSelect = 'none'
|
|
68
|
-
dragStartX = e.clientX
|
|
69
|
-
dragStartWidth = leftPanelWidth()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const handleMouseMove = (e: MouseEvent) => {
|
|
73
|
-
if (!isDragging()) return
|
|
74
|
-
|
|
75
|
-
e.preventDefault()
|
|
76
|
-
const deltaX = e.clientX - dragStartX
|
|
77
|
-
const newWidth = Math.max(150, Math.min(800, dragStartWidth + deltaX))
|
|
78
|
-
setLeftPanelWidth(newWidth)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const handleMouseUp = () => {
|
|
82
|
-
setIsDragging(false)
|
|
83
|
-
document.body.style.cursor = ''
|
|
84
|
-
document.body.style.userSelect = ''
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
onMount(() => {
|
|
88
|
-
document.addEventListener('mousemove', handleMouseMove)
|
|
89
|
-
document.addEventListener('mouseup', handleMouseUp)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
onCleanup(() => {
|
|
93
|
-
document.removeEventListener('mousemove', handleMouseMove)
|
|
94
|
-
document.removeEventListener('mouseup', handleMouseUp)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* TanStack Devtools plugin slots use height: 100% without min-height: 0 on flex
|
|
99
|
-
* ancestors, so percentage height often never constrains our tree. Observing the
|
|
100
|
-
* outer `plugin-container-*` host (not the immediate React mount wrapper) and
|
|
101
|
-
* setting an explicit pixel height makes inner overflow-y regions scroll without
|
|
102
|
-
* capping to the mount div’s collapsed height.
|
|
103
|
-
*/
|
|
104
|
-
createEffect(() => {
|
|
105
|
-
const el = shellRootEl()
|
|
106
|
-
if (!el || typeof ResizeObserver === 'undefined') return
|
|
107
|
-
const pluginSlot = resolvePluginHost(el)
|
|
108
|
-
if (!pluginSlot) return
|
|
109
|
-
|
|
110
|
-
const ro = new ResizeObserver((entries) => {
|
|
111
|
-
const entry = entries[0]
|
|
112
|
-
if (!entry) return
|
|
113
|
-
const target = entry.target as HTMLElement
|
|
114
|
-
const h =
|
|
115
|
-
target.clientHeight > 0
|
|
116
|
-
? target.clientHeight
|
|
117
|
-
: readResizeObserverBlockSize(entry)
|
|
118
|
-
if (h > 0) setSlotHeightPx(Math.round(h))
|
|
119
|
-
})
|
|
120
|
-
ro.observe(pluginSlot)
|
|
121
|
-
onCleanup(() => ro.disconnect())
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<div
|
|
126
|
-
ref={setShellRootEl}
|
|
127
|
-
class={styles().shellRoot}
|
|
128
|
-
style={{
|
|
129
|
-
...(slotHeightPx() !== undefined
|
|
130
|
-
? {
|
|
131
|
-
height: `${slotHeightPx()}px`,
|
|
132
|
-
'max-height': `${slotHeightPx()}px`,
|
|
133
|
-
}
|
|
134
|
-
: {}),
|
|
135
|
-
}}
|
|
136
|
-
>
|
|
137
|
-
<MainPanel class={styles().mainPanelShell}>
|
|
138
|
-
<Header>
|
|
139
|
-
<HeaderLogo flavor={{ light: '#84cc16', dark: '#84cc16' }}>
|
|
140
|
-
TanStack Pacer
|
|
141
|
-
</HeaderLogo>
|
|
142
|
-
</Header>
|
|
143
|
-
|
|
144
|
-
<div class={styles().mainContainer}>
|
|
145
|
-
<div
|
|
146
|
-
class={styles().leftPanel}
|
|
147
|
-
style={{
|
|
148
|
-
width: `${leftPanelWidth()}px`,
|
|
149
|
-
'min-width': '150px',
|
|
150
|
-
'max-width': '800px',
|
|
151
|
-
}}
|
|
152
|
-
>
|
|
153
|
-
<UtilList
|
|
154
|
-
selectedKey={selectedKey}
|
|
155
|
-
setSelectedKey={setSelectedKey}
|
|
156
|
-
utilState={utilState}
|
|
157
|
-
/>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div
|
|
161
|
-
class={`${styles().dragHandle} ${isDragging() ? 'dragging' : ''}`}
|
|
162
|
-
onMouseDown={handleMouseDown}
|
|
163
|
-
/>
|
|
164
|
-
|
|
165
|
-
<div class={styles().rightPanel} style={{ flex: 1 }}>
|
|
166
|
-
<div class={styles().panelHeader}>Details</div>
|
|
167
|
-
<DetailsPanel
|
|
168
|
-
selectedInstance={selectedInstance}
|
|
169
|
-
utilState={utilState}
|
|
170
|
-
/>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
</MainPanel>
|
|
174
|
-
</div>
|
|
175
|
-
)
|
|
176
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import dayjs from 'dayjs'
|
|
2
|
-
import relativeTime from 'dayjs/plugin/relativeTime.js'
|
|
3
|
-
import { Show, createSignal, onCleanup, onMount } from 'solid-js'
|
|
4
|
-
import { shallow, useSelector } from '@tanstack/solid-store'
|
|
5
|
-
import { useStyles } from '../styles/use-styles'
|
|
6
|
-
import {
|
|
7
|
-
getPacerUtilStoreState,
|
|
8
|
-
isPacerUtilTanStackStore,
|
|
9
|
-
} from '../utils/read-pacer-store-state'
|
|
10
|
-
|
|
11
|
-
dayjs.extend(relativeTime)
|
|
12
|
-
|
|
13
|
-
type StateHeaderProps = {
|
|
14
|
-
selectedInstance: () => { instance: any; type: string } | null
|
|
15
|
-
utilState: () => { lastUpdatedByKey: Record<string, number> }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function reductionFromState(
|
|
19
|
-
entry: { type: string },
|
|
20
|
-
state: Record<string, unknown> | null | undefined,
|
|
21
|
-
): number {
|
|
22
|
-
if (!state) return 0
|
|
23
|
-
|
|
24
|
-
const isAsync = entry.type.toLowerCase().includes('async')
|
|
25
|
-
const completedExecutions = isAsync
|
|
26
|
-
? Number(state.settleCount) || 0
|
|
27
|
-
: Number(state.executionCount) || 0
|
|
28
|
-
|
|
29
|
-
if (entry.type.toLowerCase().includes('batcher')) {
|
|
30
|
-
const totalItemsProcessed = Number(state.totalItemsProcessed) || 0
|
|
31
|
-
if (totalItemsProcessed === 0) return 0
|
|
32
|
-
return Math.round(
|
|
33
|
-
((totalItemsProcessed - completedExecutions) / totalItemsProcessed) * 100,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let requestCount = 0
|
|
38
|
-
|
|
39
|
-
if (state.maybeExecuteCount !== undefined) {
|
|
40
|
-
requestCount = Number(state.maybeExecuteCount) || 0
|
|
41
|
-
} else if (state.addItemCount !== undefined) {
|
|
42
|
-
requestCount = Number(state.addItemCount) || 0
|
|
43
|
-
} else {
|
|
44
|
-
return 0
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (requestCount === 0) return 0
|
|
48
|
-
|
|
49
|
-
const reduction = requestCount - completedExecutions
|
|
50
|
-
return Math.round((reduction / requestCount) * 100)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function StateHeaderInner(props: {
|
|
54
|
-
entry: { instance: any; type: string }
|
|
55
|
-
styles: ReturnType<ReturnType<typeof useStyles>>
|
|
56
|
-
now: () => number
|
|
57
|
-
utilState: () => { lastUpdatedByKey: Record<string, number> }
|
|
58
|
-
}) {
|
|
59
|
-
const key = props.entry.instance.key as string
|
|
60
|
-
const updatedAt = () => props.utilState().lastUpdatedByKey[key] ?? Date.now()
|
|
61
|
-
|
|
62
|
-
const getRelativeTime = () => {
|
|
63
|
-
const at = updatedAt()
|
|
64
|
-
const diffMs = props.now() - at
|
|
65
|
-
const diffSeconds = Math.floor(diffMs / 1000)
|
|
66
|
-
|
|
67
|
-
if (diffSeconds < 60) {
|
|
68
|
-
return `${diffSeconds} second${diffSeconds !== 1 ? 's' : ''} ago`
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return dayjs(at).fromNow()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const store = props.entry.instance?.store
|
|
75
|
-
const stateAccessor = isPacerUtilTanStackStore(store)
|
|
76
|
-
? useSelector(
|
|
77
|
-
store as never,
|
|
78
|
-
(s: unknown) =>
|
|
79
|
-
s !== null && s !== undefined && typeof s === 'object'
|
|
80
|
-
? (s as Record<string, unknown>)
|
|
81
|
-
: {},
|
|
82
|
-
{ compare: shallow },
|
|
83
|
-
)
|
|
84
|
-
: () =>
|
|
85
|
-
getPacerUtilStoreState(props.entry.instance) as Record<string, unknown>
|
|
86
|
-
|
|
87
|
-
const reductionPercentage = () =>
|
|
88
|
-
reductionFromState(props.entry, stateAccessor())
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<div class={props.styles.stateHeader}>
|
|
92
|
-
<div class={props.styles.stateTitle}>{props.entry.type}</div>
|
|
93
|
-
<div style={{ display: 'flex', 'align-items': 'center', gap: '16px' }}>
|
|
94
|
-
<div class={props.styles.infoGrid}>
|
|
95
|
-
<div class={props.styles.infoLabel}>Key</div>
|
|
96
|
-
<div class={props.styles.infoValueMono}>{key}</div>
|
|
97
|
-
<div class={props.styles.infoLabel}>Last Updated</div>
|
|
98
|
-
<div class={props.styles.infoValueMono}>
|
|
99
|
-
{new Date(updatedAt()).toLocaleTimeString()} ({getRelativeTime()})
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<div
|
|
103
|
-
class={props.styles.infoValueMono}
|
|
104
|
-
style={{ 'margin-left': 'auto', 'font-weight': 'bold' }}
|
|
105
|
-
>
|
|
106
|
-
{reductionPercentage()}% reduction
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function StateHeader(props: StateHeaderProps) {
|
|
114
|
-
const styles = useStyles()
|
|
115
|
-
const [now, setNow] = createSignal(Date.now())
|
|
116
|
-
|
|
117
|
-
onMount(() => {
|
|
118
|
-
const interval = setInterval(() => {
|
|
119
|
-
setNow(Date.now())
|
|
120
|
-
}, 1000)
|
|
121
|
-
|
|
122
|
-
onCleanup(() => {
|
|
123
|
-
clearInterval(interval)
|
|
124
|
-
})
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<Show when={props.selectedInstance()}>
|
|
129
|
-
{(entry) => (
|
|
130
|
-
<StateHeaderInner
|
|
131
|
-
entry={entry()}
|
|
132
|
-
styles={styles()}
|
|
133
|
-
now={now}
|
|
134
|
-
utilState={props.utilState}
|
|
135
|
-
/>
|
|
136
|
-
)}
|
|
137
|
-
</Show>
|
|
138
|
-
)
|
|
139
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { For } from 'solid-js'
|
|
2
|
-
import { useSelector } from '@tanstack/solid-store'
|
|
3
|
-
import clsx from 'clsx'
|
|
4
|
-
import { useStyles } from '../styles/use-styles'
|
|
5
|
-
import { UTIL_GROUPS } from './util-groups'
|
|
6
|
-
import type { StateKey } from './util-groups'
|
|
7
|
-
|
|
8
|
-
type UtilListProps = {
|
|
9
|
-
selectedKey: () => string | null
|
|
10
|
-
setSelectedKey: (key: string | null) => void
|
|
11
|
-
utilState: () => Record<StateKey, Array<any>>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function UtilList(props: UtilListProps) {
|
|
15
|
-
const styles = useStyles()
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<div class={styles().utilList}>
|
|
19
|
-
<For each={UTIL_GROUPS}>
|
|
20
|
-
{(group) => (
|
|
21
|
-
<>
|
|
22
|
-
{props.utilState()[group.key].length > 0 && (
|
|
23
|
-
<div class={styles().utilGroup}>
|
|
24
|
-
<div class={styles().utilGroupHeader}>{group.label}</div>
|
|
25
|
-
<For each={props.utilState()[group.key]}>
|
|
26
|
-
{(instance) => {
|
|
27
|
-
const status = (() => {
|
|
28
|
-
try {
|
|
29
|
-
const statusAccessor = useSelector(
|
|
30
|
-
instance.store,
|
|
31
|
-
(s: any) => s.status,
|
|
32
|
-
)
|
|
33
|
-
return () => statusAccessor() ?? 'unknown'
|
|
34
|
-
} catch {
|
|
35
|
-
return () => 'unknown'
|
|
36
|
-
}
|
|
37
|
-
})()
|
|
38
|
-
return (
|
|
39
|
-
<div
|
|
40
|
-
class={clsx(
|
|
41
|
-
styles().utilRow,
|
|
42
|
-
props.selectedKey() === instance.key &&
|
|
43
|
-
styles().utilRowSelected,
|
|
44
|
-
)}
|
|
45
|
-
onClick={() =>
|
|
46
|
-
props.setSelectedKey(instance.key ?? null)
|
|
47
|
-
}
|
|
48
|
-
>
|
|
49
|
-
<div class={styles().utilKey}>{instance.key}</div>
|
|
50
|
-
<div class={styles().utilStatus}>{status()}</div>
|
|
51
|
-
</div>
|
|
52
|
-
)
|
|
53
|
-
}}
|
|
54
|
-
</For>
|
|
55
|
-
</div>
|
|
56
|
-
)}
|
|
57
|
-
</>
|
|
58
|
-
)}
|
|
59
|
-
</For>
|
|
60
|
-
</div>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { JsonTree } from '@tanstack/devtools-ui'
|
|
2
|
-
import { useSelector } from '@tanstack/solid-store'
|
|
3
|
-
import {
|
|
4
|
-
getPacerUtilStoreState,
|
|
5
|
-
isPacerUtilTanStackStore,
|
|
6
|
-
} from '../utils/read-pacer-store-state'
|
|
7
|
-
import type { JSX } from 'solid-js'
|
|
8
|
-
|
|
9
|
-
type UtilStateJsonTreeProps = {
|
|
10
|
-
instance: any
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Subscribes to the util's TanStack Store so the tree updates live (same
|
|
15
|
-
* idea as {@link UtilList} status). A plain snapshot would only refresh when the
|
|
16
|
-
* devtools shell re-renders, which does not happen on nested store updates.
|
|
17
|
-
*/
|
|
18
|
-
export function UtilStateJsonTree(props: UtilStateJsonTreeProps): JSX.Element {
|
|
19
|
-
const store = props.instance?.store
|
|
20
|
-
if (isPacerUtilTanStackStore(store)) {
|
|
21
|
-
const snapshot = useSelector(store as never, (s: unknown) => s)
|
|
22
|
-
return <JsonTree value={snapshot()} />
|
|
23
|
-
}
|
|
24
|
-
return <JsonTree value={getPacerUtilStoreState(props.instance)} />
|
|
25
|
-
}
|
package/src/components/index.tsx
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { ThemeContextProvider } from '@tanstack/devtools-ui'
|
|
2
|
-
import { PacerContextProvider } from '../PacerContextProvider'
|
|
3
|
-
import { Shell } from './Shell'
|
|
4
|
-
|
|
5
|
-
import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui'
|
|
6
|
-
|
|
7
|
-
interface DevtoolsProps {
|
|
8
|
-
theme: TanStackDevtoolsTheme
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function PacerDevtools(props: DevtoolsProps) {
|
|
12
|
-
return (
|
|
13
|
-
<ThemeContextProvider theme={props.theme}>
|
|
14
|
-
<PacerContextProvider>
|
|
15
|
-
<Shell />
|
|
16
|
-
</PacerContextProvider>
|
|
17
|
-
</ThemeContextProvider>
|
|
18
|
-
)
|
|
19
|
-
}
|