@tanstack/devtools 0.10.13 → 0.11.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/bin/intent.js +20 -0
- package/dist/dev.js +1 -1
- package/dist/devtools/{W6LG6674.js → AKRRB3KC.js} +41 -8
- package/dist/devtools/{7Z2ESJHO.js → UZDPUP5E.js} +37 -7
- package/dist/index.d.ts +14 -6
- package/dist/index.js +1 -1
- package/dist/mount-impl/{EMNOPRXX.js → IGHTIX6Y.js} +1 -1
- package/dist/mount-impl/{Z6LKUI5N.js → YYPMJI4G.js} +1 -1
- package/dist/server.js +1 -1
- package/package.json +9 -4
- package/skills/devtools-app-setup/SKILL.md +359 -0
- package/skills/devtools-marketplace/SKILL.md +390 -0
- package/skills/devtools-plugin-panel/SKILL.md +429 -0
- package/skills/devtools-plugin-panel/references/panel-api.md +136 -0
- package/skills/devtools-production/SKILL.md +459 -0
- package/src/components/tab-content.tsx +9 -5
- package/src/context/devtools-context.tsx +7 -9
- package/src/context/devtools-store.ts +3 -2
- package/src/devtools.tsx +9 -1
- package/src/index.ts +2 -0
- package/src/styles/use-styles.ts +0 -1
- package/src/tabs/index.tsx +1 -1
- package/src/tabs/plugin-registry.ts +18 -0
- package/src/tabs/plugins-tab.tsx +9 -4
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devtools-plugin-panel
|
|
3
|
+
description: >
|
|
4
|
+
Build devtools panel components that display emitted event data. Listen via
|
|
5
|
+
EventClient.on(), handle theme (light/dark), use @tanstack/devtools-ui
|
|
6
|
+
components. Plugin registration (name, render, id, defaultOpen), lifecycle
|
|
7
|
+
(mount, activate, destroy), max 3 active plugins. Two paths: Solid.js core
|
|
8
|
+
with devtools-ui for multi-framework support, or framework-specific panels.
|
|
9
|
+
type: core
|
|
10
|
+
library: tanstack-devtools
|
|
11
|
+
library_version: '0.10.12'
|
|
12
|
+
requires:
|
|
13
|
+
- devtools-event-client
|
|
14
|
+
sources:
|
|
15
|
+
- 'TanStack/devtools:docs/building-custom-plugins.md'
|
|
16
|
+
- 'TanStack/devtools:docs/plugin-lifecycle.md'
|
|
17
|
+
- 'TanStack/devtools:docs/plugin-configuration.md'
|
|
18
|
+
- 'TanStack/devtools:packages/devtools/src/context/devtools-context.tsx'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## TanStackDevtoolsPlugin Interface
|
|
22
|
+
|
|
23
|
+
The low-level contract every plugin implements. Framework adapters wrap this automatically.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// Source: packages/devtools/src/context/devtools-context.tsx
|
|
27
|
+
interface TanStackDevtoolsPlugin {
|
|
28
|
+
id?: string
|
|
29
|
+
name: string | ((el: HTMLHeadingElement, theme: 'dark' | 'light') => void)
|
|
30
|
+
render: (el: HTMLDivElement, theme: 'dark' | 'light') => void
|
|
31
|
+
destroy?: (pluginId: string) => void
|
|
32
|
+
defaultOpen?: boolean
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **`name`** (required) -- String tab title, or function receiving `(el, theme)` for custom rendering.
|
|
37
|
+
- **`render`** (required) -- Called on activation with a `<div>` container and theme. Called again on theme change.
|
|
38
|
+
- **`id`** (optional) -- Stable identifier. If omitted: `name.toLowerCase().replace(' ', '-')-{index}`. Explicit ids persist selection across reloads.
|
|
39
|
+
- **`defaultOpen`** (optional) -- Opens panel on first load when no saved state. Max 3 open. Does not override saved preferences.
|
|
40
|
+
- **`destroy`** (optional) -- Called on deactivation or unmount. Framework adapters handle cleanup automatically.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Two Development Paths
|
|
45
|
+
|
|
46
|
+
### Path 1: Solid.js Core + Framework Adapters (Multi-Framework)
|
|
47
|
+
|
|
48
|
+
Build the panel in Solid.js using `@tanstack/devtools-ui` components. Use `constructCoreClass` for lazy loading, then `createReactPanel`/`createSolidPanel` to wrap for each framework. The devtools core is Solid, so Solid panels run natively.
|
|
49
|
+
|
|
50
|
+
### Path 2: Framework-Specific Panel (Single Framework)
|
|
51
|
+
|
|
52
|
+
Build directly in your framework and use `createReactPlugin`/`createVuePlugin`/`createSolidPlugin`/`createPreactPlugin` from `@tanstack/devtools-utils`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Path 1: Solid.js Core Panel
|
|
57
|
+
|
|
58
|
+
### Step 1: Define Event Map and Create EventClient
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// src/event-client.ts
|
|
62
|
+
import { EventClient } from '@tanstack/devtools-event-client'
|
|
63
|
+
|
|
64
|
+
type StoreEvents = {
|
|
65
|
+
'state-changed': { storeName: string; state: unknown; timestamp: number }
|
|
66
|
+
'action-dispatched': { storeName: string; action: string; payload: unknown }
|
|
67
|
+
reset: void
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class StoreInspectorClient extends EventClient<StoreEvents> {
|
|
71
|
+
constructor() {
|
|
72
|
+
super({ pluginId: 'store-inspector' })
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const storeInspector = new StoreInspectorClient()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Event names are suffixes only. The `pluginId` is prepended automatically: `'store-inspector:state-changed'`.
|
|
80
|
+
|
|
81
|
+
### Step 2: Build the Solid.js Panel Component
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
/** @jsxImportSource solid-js */
|
|
85
|
+
import { createSignal, onCleanup, For } from 'solid-js'
|
|
86
|
+
import {
|
|
87
|
+
MainPanel,
|
|
88
|
+
Header,
|
|
89
|
+
HeaderLogo,
|
|
90
|
+
Section,
|
|
91
|
+
SectionTitle,
|
|
92
|
+
JsonTree,
|
|
93
|
+
Button,
|
|
94
|
+
Tag,
|
|
95
|
+
useTheme,
|
|
96
|
+
} from '@tanstack/devtools-ui'
|
|
97
|
+
import { storeInspector } from './event-client'
|
|
98
|
+
|
|
99
|
+
export default function StoreInspectorPanel() {
|
|
100
|
+
const { theme } = useTheme()
|
|
101
|
+
const [state, setState] = createSignal<Record<string, unknown>>({})
|
|
102
|
+
const [actions, setActions] = createSignal<
|
|
103
|
+
Array<{ action: string; payload: unknown }>
|
|
104
|
+
>([])
|
|
105
|
+
|
|
106
|
+
const cleanupState = storeInspector.on('state-changed', (e) => {
|
|
107
|
+
setState((prev) => ({ ...prev, [e.payload.storeName]: e.payload.state }))
|
|
108
|
+
})
|
|
109
|
+
const cleanupActions = storeInspector.on('action-dispatched', (e) => {
|
|
110
|
+
setActions((prev) => [
|
|
111
|
+
...prev,
|
|
112
|
+
{ action: e.payload.action, payload: e.payload.payload },
|
|
113
|
+
])
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
onCleanup(() => {
|
|
117
|
+
cleanupState()
|
|
118
|
+
cleanupActions()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<MainPanel>
|
|
123
|
+
<Header>
|
|
124
|
+
<HeaderLogo flavor={{ light: '#1a1a2e', dark: '#e0e0e0' }}>
|
|
125
|
+
Store Inspector
|
|
126
|
+
</HeaderLogo>
|
|
127
|
+
</Header>
|
|
128
|
+
<Section>
|
|
129
|
+
<SectionTitle>Current State</SectionTitle>
|
|
130
|
+
<JsonTree value={state()} copyable defaultExpansionDepth={2} />
|
|
131
|
+
</Section>
|
|
132
|
+
<Section>
|
|
133
|
+
<SectionTitle>
|
|
134
|
+
Action Log
|
|
135
|
+
<Tag color="purple" label="Actions" count={actions().length} />
|
|
136
|
+
</SectionTitle>
|
|
137
|
+
<For each={actions()}>
|
|
138
|
+
{(a) => (
|
|
139
|
+
<div>
|
|
140
|
+
<strong>{a.action}</strong>
|
|
141
|
+
<JsonTree value={a.payload} copyable defaultExpansionDepth={1} />
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
</For>
|
|
145
|
+
<Button variant="danger" onClick={() => setActions([])}>
|
|
146
|
+
Clear Log
|
|
147
|
+
</Button>
|
|
148
|
+
</Section>
|
|
149
|
+
</MainPanel>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Step 3: Create Core Class and Framework Adapters
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
// src/core.ts
|
|
158
|
+
import { constructCoreClass } from '@tanstack/devtools-utils/solid/class'
|
|
159
|
+
|
|
160
|
+
export const [StoreInspectorCore, NoOpStoreInspectorCore] = constructCoreClass(
|
|
161
|
+
() => import('./panel'),
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// src/react.tsx
|
|
167
|
+
import { createReactPanel } from '@tanstack/devtools-utils/react'
|
|
168
|
+
import { StoreInspectorCore } from './core'
|
|
169
|
+
|
|
170
|
+
export const [StoreInspectorPanel, NoOpStoreInspectorPanel] =
|
|
171
|
+
createReactPanel(StoreInspectorCore)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
// src/react-plugin.tsx
|
|
176
|
+
import { createReactPlugin } from '@tanstack/devtools-utils/react'
|
|
177
|
+
import { StoreInspectorPanel } from './react'
|
|
178
|
+
|
|
179
|
+
export const [StoreInspectorPlugin, NoOpStoreInspectorPlugin] =
|
|
180
|
+
createReactPlugin({
|
|
181
|
+
name: 'Store Inspector',
|
|
182
|
+
id: 'store-inspector',
|
|
183
|
+
defaultOpen: true,
|
|
184
|
+
Component: StoreInspectorPanel,
|
|
185
|
+
})
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Step 4: Register
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { TanStackDevtools } from '@tanstack/react-devtools'
|
|
192
|
+
import { StoreInspectorPlugin } from 'your-package/react-plugin'
|
|
193
|
+
|
|
194
|
+
function App() {
|
|
195
|
+
return (
|
|
196
|
+
<>
|
|
197
|
+
<YourApp />
|
|
198
|
+
<TanStackDevtools plugins={[StoreInspectorPlugin()]} />
|
|
199
|
+
</>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Path 2: Framework-Specific Panel (React Example)
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { useState, useEffect } from 'react'
|
|
210
|
+
import { EventClient } from '@tanstack/devtools-event-client'
|
|
211
|
+
import { createReactPlugin } from '@tanstack/devtools-utils/react'
|
|
212
|
+
|
|
213
|
+
type MyEvents = {
|
|
214
|
+
'data-update': { items: Array<{ id: string; value: number }> }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class MyPluginClient extends EventClient<MyEvents> {
|
|
218
|
+
constructor() {
|
|
219
|
+
super({ pluginId: 'my-plugin' })
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const myPlugin = new MyPluginClient()
|
|
224
|
+
|
|
225
|
+
function MyPluginPanel({ theme }: { theme?: 'light' | 'dark' }) {
|
|
226
|
+
const [items, setItems] = useState<Array<{ id: string; value: number }>>([])
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
const cleanup = myPlugin.on('data-update', (e) => {
|
|
230
|
+
setItems(e.payload.items)
|
|
231
|
+
})
|
|
232
|
+
return cleanup
|
|
233
|
+
}, [])
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div style={{ color: theme === 'dark' ? '#fff' : '#000' }}>
|
|
237
|
+
<h3>My Plugin</h3>
|
|
238
|
+
<ul>
|
|
239
|
+
{items.map((item) => (
|
|
240
|
+
<li key={item.id}>
|
|
241
|
+
{item.id}: {item.value}
|
|
242
|
+
</li>
|
|
243
|
+
))}
|
|
244
|
+
</ul>
|
|
245
|
+
</div>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export const [MyPlugin, NoOpMyPlugin] = createReactPlugin({
|
|
250
|
+
name: 'My Plugin',
|
|
251
|
+
id: 'my-plugin',
|
|
252
|
+
defaultOpen: false,
|
|
253
|
+
Component: MyPluginPanel,
|
|
254
|
+
})
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Plugin Lifecycle Sequence
|
|
260
|
+
|
|
261
|
+
1. **Initialization** -- `TanStackDevtoolsCore` receives `plugins` array. Each plugin gets an `id` (explicit or generated).
|
|
262
|
+
2. **DOM containers created** -- Core creates `<div id="plugin-container-{id}">` and `<h3 id="plugin-title-container-{id}">` per plugin.
|
|
263
|
+
3. **Activation** -- On tab click or `defaultOpen`, `plugin.render(container, theme)` called.
|
|
264
|
+
4. **Framework portaling** -- React uses `createPortal`, Solid uses `<Portal>`, Vue uses `<Teleport>`.
|
|
265
|
+
5. **Theme change** -- `render` called again with new theme value.
|
|
266
|
+
6. **Deactivation/Unmount** -- `destroy(pluginId)` called if provided. Framework adapters handle cleanup.
|
|
267
|
+
|
|
268
|
+
Active plugin selection persisted in `localStorage` under key `tanstack_devtools_state`.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Common Mistakes
|
|
273
|
+
|
|
274
|
+
### CRITICAL: Not Cleaning Up Event Listeners
|
|
275
|
+
|
|
276
|
+
Each `on()` returns a cleanup function. Forgetting it causes memory leaks and duplicate handlers.
|
|
277
|
+
|
|
278
|
+
Wrong:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
client.on('state', cb)
|
|
283
|
+
}, [])
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Correct:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
const cleanup = client.on('state', cb)
|
|
291
|
+
return cleanup
|
|
292
|
+
}, [])
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
In Solid, use `onCleanup()`:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const cleanup = storeInspector.on('state-changed', handler)
|
|
299
|
+
onCleanup(cleanup)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Source: docs/building-custom-plugins.md
|
|
303
|
+
|
|
304
|
+
### HIGH: Oversubscribing to Events in Multiple Components
|
|
305
|
+
|
|
306
|
+
Do not call `on()` in multiple components for the same event. Subscribe once in a shared store/hook.
|
|
307
|
+
|
|
308
|
+
Wrong:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
function ComponentA() {
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
const c = client.on('state', cb1)
|
|
314
|
+
return c
|
|
315
|
+
}, [])
|
|
316
|
+
}
|
|
317
|
+
function ComponentB() {
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
const c = client.on('state', cb2)
|
|
320
|
+
return c
|
|
321
|
+
}, [])
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Correct:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
function useStoreState() {
|
|
329
|
+
const [state, setState] = useState(null)
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
const cleanup = client.on('state', (e) => setState(e.payload))
|
|
332
|
+
return cleanup
|
|
333
|
+
}, [])
|
|
334
|
+
return state
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Source: maintainer interview
|
|
339
|
+
|
|
340
|
+
### MEDIUM: Hardcoding Repeated Event Payload Fields
|
|
341
|
+
|
|
342
|
+
When emitting events that share common fields, create a shared base object.
|
|
343
|
+
|
|
344
|
+
Wrong:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
client.emit('state-changed', { storeName: 'main', version: '1.0', state })
|
|
348
|
+
client.emit('action-dispatched', { storeName: 'main', version: '1.0', action })
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Correct:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
const base = { storeName: 'main', version: '1.0' }
|
|
355
|
+
client.emit('state-changed', { ...base, state })
|
|
356
|
+
client.emit('action-dispatched', { ...base, action })
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Source: maintainer interview
|
|
360
|
+
|
|
361
|
+
### MEDIUM: Ignoring Theme Prop in Panel Component
|
|
362
|
+
|
|
363
|
+
Panels must adapt styling to theme. Factory-created plugins receive `props.theme`.
|
|
364
|
+
|
|
365
|
+
Wrong:
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
function MyPanel() {
|
|
369
|
+
return <div style={{ color: 'white' }}>Always white text</div>
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Correct:
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
function MyPanel({ theme }: { theme?: 'light' | 'dark' }) {
|
|
377
|
+
return (
|
|
378
|
+
<div style={{ color: theme === 'dark' ? '#e0e0e0' : '#1a1a1a' }}>
|
|
379
|
+
Theme-aware text
|
|
380
|
+
</div>
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
In Solid panels using devtools-ui, use `useTheme()` instead of prop drilling.
|
|
386
|
+
|
|
387
|
+
Source: docs/plugin-lifecycle.md
|
|
388
|
+
|
|
389
|
+
### MEDIUM: Not Knowing Max 3 Active Plugins Limit
|
|
390
|
+
|
|
391
|
+
`MAX_ACTIVE_PLUGINS = 3` (in `packages/devtools/src/utils/constants.ts`). If more than 3 set `defaultOpen: true`, only the first 3 open. Activating a 4th deactivates the earliest. Single-plugin exception: if only 1 plugin is registered, it opens automatically.
|
|
392
|
+
|
|
393
|
+
Source: packages/devtools/src/utils/get-default-active-plugins.ts
|
|
394
|
+
|
|
395
|
+
### MEDIUM: Using Raw DOM Manipulation Instead of Framework Portals
|
|
396
|
+
|
|
397
|
+
Framework adapters handle portaling. Do not manually manipulate DOM.
|
|
398
|
+
|
|
399
|
+
Wrong:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
render: (el) => {
|
|
403
|
+
const div = document.createElement('div')
|
|
404
|
+
div.textContent = 'Hello'
|
|
405
|
+
el.appendChild(div)
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Correct:
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
import { createReactPlugin } from '@tanstack/devtools-utils/react'
|
|
413
|
+
const [Plugin, NoOpPlugin] = createReactPlugin({
|
|
414
|
+
name: 'My Plugin',
|
|
415
|
+
Component: ({ theme }) => <div>Hello</div>,
|
|
416
|
+
})
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Source: docs/plugin-lifecycle.md
|
|
420
|
+
|
|
421
|
+
### MEDIUM: Not Keeping Devtools Packages at Latest Versions
|
|
422
|
+
|
|
423
|
+
All `@tanstack/devtools-*` packages should be on compatible versions. For external plugins, pin to compatible ranges.
|
|
424
|
+
|
|
425
|
+
Source: maintainer interview
|
|
426
|
+
|
|
427
|
+
## References
|
|
428
|
+
|
|
429
|
+
- [devtools-ui components and API](references/panel-api.md)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Plugin Panel API Reference
|
|
2
|
+
|
|
3
|
+
## Plugin Factory Functions
|
|
4
|
+
|
|
5
|
+
All factories return `[Plugin, NoOpPlugin]` tuples for production tree-shaking.
|
|
6
|
+
|
|
7
|
+
| Factory | Import Path | Framework |
|
|
8
|
+
| -------------------- | -------------------------------------- | ------------------------ |
|
|
9
|
+
| `createReactPlugin` | `@tanstack/devtools-utils/react` | React |
|
|
10
|
+
| `createSolidPlugin` | `@tanstack/devtools-utils/solid` | Solid.js |
|
|
11
|
+
| `createVuePlugin` | `@tanstack/devtools-utils/vue` | Vue 3 |
|
|
12
|
+
| `createPreactPlugin` | `@tanstack/devtools-utils/preact` | Preact |
|
|
13
|
+
| `createReactPanel` | `@tanstack/devtools-utils/react` | React (wraps Solid core) |
|
|
14
|
+
| `createSolidPanel` | `@tanstack/devtools-utils/solid` | Solid (wraps Solid core) |
|
|
15
|
+
| `constructCoreClass` | `@tanstack/devtools-utils/solid/class` | Core class construction |
|
|
16
|
+
|
|
17
|
+
### createReactPlugin / createSolidPlugin / createPreactPlugin
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
function createReactPlugin(config: {
|
|
21
|
+
name: string
|
|
22
|
+
id?: string
|
|
23
|
+
defaultOpen?: boolean
|
|
24
|
+
Component: (props: { theme?: 'light' | 'dark' }) => JSX.Element
|
|
25
|
+
}): readonly [() => PluginConfig, () => PluginConfig]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### createVuePlugin
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
function createVuePlugin<TComponentProps extends Record<string, any>>(
|
|
32
|
+
name: string,
|
|
33
|
+
component: DefineComponent<TComponentProps, {}, unknown>,
|
|
34
|
+
): readonly [
|
|
35
|
+
(props: TComponentProps) => {
|
|
36
|
+
name: string
|
|
37
|
+
component: DefineComponent
|
|
38
|
+
props: TComponentProps
|
|
39
|
+
},
|
|
40
|
+
(props: TComponentProps) => {
|
|
41
|
+
name: string
|
|
42
|
+
component: Fragment
|
|
43
|
+
props: TComponentProps
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Vue uses positional `(name, component)` args, not an options object.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## devtools-ui Components
|
|
53
|
+
|
|
54
|
+
All components are Solid.js. Use in Path 1 (Solid core) panels only.
|
|
55
|
+
|
|
56
|
+
| Component | Purpose |
|
|
57
|
+
| ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `MainPanel` | Root container with optional padding |
|
|
59
|
+
| `Header` | Top header bar |
|
|
60
|
+
| `HeaderLogo` | Logo section; accepts `flavor` colors |
|
|
61
|
+
| `Section` | Content section wrapper |
|
|
62
|
+
| `SectionTitle` | `<h3>` section heading |
|
|
63
|
+
| `SectionDescription` | `<p>` description text |
|
|
64
|
+
| `SectionIcon` | Icon wrapper in sections |
|
|
65
|
+
| `JsonTree` | Expandable JSON tree viewer with copy support |
|
|
66
|
+
| `Button` | Variants: primary, secondary, danger, success, info, warning; supports `outline` and `ghost` |
|
|
67
|
+
| `Tag` | Colored label tag with optional count badge |
|
|
68
|
+
| `Select` | Dropdown select with label and description |
|
|
69
|
+
| `Input` | Text input |
|
|
70
|
+
| `Checkbox` | Checkbox input |
|
|
71
|
+
| `TanStackLogo` | TanStack logo SVG |
|
|
72
|
+
| `ThemeContextProvider` | Wraps children with theme context |
|
|
73
|
+
| `useTheme` | Returns `{ theme: Accessor<Theme>, setTheme }` -- must be inside ThemeContextProvider |
|
|
74
|
+
|
|
75
|
+
### JsonTree Props
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
function JsonTree<TData>(props: {
|
|
79
|
+
value: TData
|
|
80
|
+
copyable?: boolean
|
|
81
|
+
defaultExpansionDepth?: number // default: 1
|
|
82
|
+
collapsePaths?: Array<string>
|
|
83
|
+
config?: { dateFormat?: string }
|
|
84
|
+
}): JSX.Element
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## EventClient API (Quick Reference)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
class EventClient<TEventMap extends Record<string, any>> {
|
|
93
|
+
constructor(config: {
|
|
94
|
+
pluginId: string
|
|
95
|
+
debug?: boolean // default: false
|
|
96
|
+
enabled?: boolean // default: true
|
|
97
|
+
reconnectEveryMs?: number // default: 300
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
emit<TEvent extends keyof TEventMap & string>(
|
|
101
|
+
eventSuffix: TEvent,
|
|
102
|
+
payload: TEventMap[TEvent],
|
|
103
|
+
): void
|
|
104
|
+
|
|
105
|
+
on<TEvent extends keyof TEventMap & string>(
|
|
106
|
+
eventSuffix: TEvent,
|
|
107
|
+
cb: (event: {
|
|
108
|
+
type: TEvent
|
|
109
|
+
payload: TEventMap[TEvent]
|
|
110
|
+
pluginId?: string
|
|
111
|
+
}) => void,
|
|
112
|
+
options?: { withEventTarget?: boolean },
|
|
113
|
+
): () => void
|
|
114
|
+
|
|
115
|
+
onAll(cb: (event: { type: string; payload: any }) => void): () => void
|
|
116
|
+
onAllPluginEvents(
|
|
117
|
+
cb: (event: AllDevtoolsEvents<TEventMap>) => void,
|
|
118
|
+
): () => void
|
|
119
|
+
getPluginId(): string
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Key Source Files
|
|
126
|
+
|
|
127
|
+
| File | Purpose |
|
|
128
|
+
| ----------------------------------------------------------- | -------------------------------------------------------- |
|
|
129
|
+
| `packages/devtools/src/context/devtools-context.tsx` | `TanStackDevtoolsPlugin` interface, plugin ID generation |
|
|
130
|
+
| `packages/devtools/src/core.ts` | `TanStackDevtoolsCore` class |
|
|
131
|
+
| `packages/devtools/src/utils/constants.ts` | `MAX_ACTIVE_PLUGINS = 3` |
|
|
132
|
+
| `packages/devtools/src/utils/get-default-active-plugins.ts` | defaultOpen resolution logic |
|
|
133
|
+
| `packages/event-bus-client/src/plugin.ts` | `EventClient` class |
|
|
134
|
+
| `packages/devtools-utils/src/solid/class.ts` | `constructCoreClass` |
|
|
135
|
+
| `packages/devtools-ui/src/index.ts` | All UI component exports |
|
|
136
|
+
| `packages/devtools-ui/src/components/theme.tsx` | `ThemeContextProvider`, `useTheme` |
|