@vitejs/devtools-kit 0.0.0-alpha.3 → 0.0.0-alpha.31

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.
@@ -0,0 +1,7 @@
1
+ import { C as DevToolsDocksUserSettings } from "./client-script-BOHKZVPh.mjs";
2
+
3
+ //#region src/constants.d.ts
4
+ declare const DEFAULT_CATEGORIES_ORDER: Record<string, number>;
5
+ declare const DEFAULT_STATE_USER_SETTINGS: () => DevToolsDocksUserSettings;
6
+ //#endregion
7
+ export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS };
@@ -0,0 +1,20 @@
1
+ //#region src/constants.ts
2
+ const DEFAULT_CATEGORIES_ORDER = {
3
+ "~viteplus": -1e3,
4
+ "default": 0,
5
+ "app": 100,
6
+ "framework": 200,
7
+ "web": 300,
8
+ "advanced": 400,
9
+ "~builtin": 1e3
10
+ };
11
+ const DEFAULT_STATE_USER_SETTINGS = () => ({
12
+ docksHidden: [],
13
+ docksCategoriesHidden: [],
14
+ docksPinned: [],
15
+ docksCustomOrder: {},
16
+ showIframeAddressBar: false
17
+ });
18
+
19
+ //#endregion
20
+ export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS };
@@ -0,0 +1,71 @@
1
+ //#region src/types/events.d.ts
2
+ interface EventsMap {
3
+ [event: string]: any;
4
+ }
5
+ interface EventUnsubscribe {
6
+ (): void;
7
+ }
8
+ interface EventEmitter<Events extends EventsMap> {
9
+ /**
10
+ * Calls each of the listeners registered for a given event.
11
+ *
12
+ * ```js
13
+ * ee.emit('tick', tickType, tickDuration)
14
+ * ```
15
+ *
16
+ * @param event The event name.
17
+ * @param args The arguments for listeners.
18
+ */
19
+ emit: <K extends keyof Events>(event: K, ...args: Parameters<Events[K]>) => void;
20
+ /**
21
+ * Calls the listeners for a given event once and then removes the listener.
22
+ *
23
+ * @param event The event name.
24
+ * @param args The arguments for listeners.
25
+ */
26
+ emitOnce: <K extends keyof Events>(event: K, ...args: Parameters<Events[K]>) => void;
27
+ /**
28
+ * Event names in keys and arrays with listeners in values.
29
+ *
30
+ * @internal
31
+ */
32
+ _listeners: Partial<{ [E in keyof Events]: Events[E][] }>;
33
+ /**
34
+ * Add a listener for a given event.
35
+ *
36
+ * ```js
37
+ * const unbind = ee.on('tick', (tickType, tickDuration) => {
38
+ * count += 1
39
+ * })
40
+ *
41
+ * disable () {
42
+ * unbind()
43
+ * }
44
+ * ```
45
+ *
46
+ * @param event The event name.
47
+ * @param cb The listener function.
48
+ * @returns Unbind listener from event.
49
+ */
50
+ on: <K extends keyof Events>(event: K, cb: Events[K]) => EventUnsubscribe;
51
+ /**
52
+ * Add a listener for a given event once.
53
+ *
54
+ * ```js
55
+ * const unbind = ee.once('tick', (tickType, tickDuration) => {
56
+ * count += 1
57
+ * })
58
+ *
59
+ * disable () {
60
+ * unbind()
61
+ * }
62
+ * ```
63
+ *
64
+ * @param event The event name.
65
+ * @param cb The listener function.
66
+ * @returns Unbind listener from event.
67
+ */
68
+ once: <K extends keyof Events>(event: K, cb: Events[K]) => EventUnsubscribe;
69
+ }
70
+ //#endregion
71
+ export { EventUnsubscribe as n, EventsMap as r, EventEmitter as t };
@@ -0,0 +1,3 @@
1
+ import { $ as DevToolsViewAction, A as ConnectionMeta, B as DevToolsTerminalSessionBase, C as DevToolsDocksUserSettings, D as RpcFunctionsHost, E as RpcBroadcastOptions, F as DevToolsViewHost, G as DevToolsRpcSharedStates, H as DevToolsTerminalStatus, I as DevToolsChildProcessExecuteOptions, J as DevToolsDockEntryBase, K as ClientScriptEntry, L as DevToolsChildProcessTerminalSession, M as DevToolsNodeContext, N as DevToolsNodeUtils, O as RpcSharedStateGetOptions, P as DevToolsPluginOptions, Q as DevToolsDockUserEntry, R as DevToolsTerminalHost, S as Thenable, T as DevToolsNodeRpcSessionMeta, U as DevToolsRpcClientFunctions, V as DevToolsTerminalSessionStreamChunkEvent, W as DevToolsRpcServerFunctions, X as DevToolsDockEntryIcon, Y as DevToolsDockEntryCategory, Z as DevToolsDockHost, _ as RpcDefinitionsToFunctions, b as EntriesToObject, et as DevToolsViewBuiltin, g as RpcDefinitionsFilter, h as defineRpcFunction, it as DevToolsViewLauncherStatus, j as DevToolsCapabilities, k as RpcSharedStateHost, nt as DevToolsViewIframe, q as DevToolsDockEntry, rt as DevToolsViewLauncher, tt as DevToolsViewCustomRender, v as PluginWithDevTools, w as DevToolsNodeRpcSession, x as PartialWithoutId, y as ViteConfigDevtoolsOptions, z as DevToolsTerminalSession } from "./client-script-BOHKZVPh.mjs";
2
+ import { n as EventUnsubscribe, r as EventsMap, t as EventEmitter } from "./events-57bKw4ct.mjs";
3
+ export { ClientScriptEntry, ConnectionMeta, DevToolsCapabilities, DevToolsChildProcessExecuteOptions, DevToolsChildProcessTerminalSession, DevToolsDockEntry, DevToolsDockEntryBase, DevToolsDockEntryCategory, DevToolsDockEntryIcon, DevToolsDockHost, DevToolsDockUserEntry, DevToolsDocksUserSettings, DevToolsNodeContext, DevToolsNodeRpcSession, DevToolsNodeRpcSessionMeta, DevToolsNodeUtils, DevToolsPluginOptions, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, DevToolsRpcSharedStates, DevToolsTerminalHost, DevToolsTerminalSession, DevToolsTerminalSessionBase, DevToolsTerminalSessionStreamChunkEvent, DevToolsTerminalStatus, DevToolsViewAction, DevToolsViewBuiltin, DevToolsViewCustomRender, DevToolsViewHost, DevToolsViewIframe, DevToolsViewLauncher, DevToolsViewLauncherStatus, EntriesToObject, EventEmitter, EventUnsubscribe, EventsMap, PartialWithoutId, PluginWithDevTools, RpcBroadcastOptions, RpcDefinitionsFilter, RpcDefinitionsToFunctions, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost, Thenable, ViteConfigDevtoolsOptions, defineRpcFunction };
package/dist/index.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { createDefineWrapperWithContext } from "@vitejs/devtools-rpc";
2
+
3
+ //#region src/utils/define.ts
4
+ const defineRpcFunction = createDefineWrapperWithContext();
5
+
6
+ //#endregion
7
+ export { defineRpcFunction };
@@ -0,0 +1,9 @@
1
+ import { r as EventsMap, t as EventEmitter } from "../events-57bKw4ct.mjs";
2
+
3
+ //#region src/utils/events.d.ts
4
+ /**
5
+ * Create event emitter.
6
+ */
7
+ declare function createEventEmitter<Events extends EventsMap>(): EventEmitter<Events>;
8
+ //#endregion
9
+ export { createEventEmitter };
@@ -0,0 +1,41 @@
1
+ //#region src/utils/events.ts
2
+ /**
3
+ * Create event emitter.
4
+ */
5
+ function createEventEmitter() {
6
+ const _listeners = {};
7
+ function emit(event, ...args) {
8
+ const callbacks = _listeners[event] || [];
9
+ for (let i = 0, length = callbacks.length; i < length; i++) {
10
+ const callback = callbacks[i];
11
+ if (callback) callback(...args);
12
+ }
13
+ }
14
+ function emitOnce(event, ...args) {
15
+ emit(event, ...args);
16
+ delete _listeners[event];
17
+ }
18
+ function on(event, cb) {
19
+ (_listeners[event] ||= []).push(cb);
20
+ return () => {
21
+ _listeners[event] = _listeners[event]?.filter((i) => cb !== i);
22
+ };
23
+ }
24
+ function once(event, cb) {
25
+ const unsubscribe = on(event, ((...args) => {
26
+ unsubscribe();
27
+ return cb(...args);
28
+ }));
29
+ return unsubscribe;
30
+ }
31
+ return {
32
+ _listeners,
33
+ emit,
34
+ emitOnce,
35
+ on,
36
+ once
37
+ };
38
+ }
39
+
40
+ //#endregion
41
+ export { createEventEmitter };
@@ -0,0 +1,4 @@
1
+ //#region src/utils/nanoid.d.ts
2
+ declare function nanoid(size?: number): string;
3
+ //#endregion
4
+ export { nanoid };
@@ -0,0 +1,11 @@
1
+ //#region src/utils/nanoid.ts
2
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
3
+ function nanoid(size = 21) {
4
+ let id = "";
5
+ let i = size;
6
+ while (i--) id += urlAlphabet[Math.random() * 64 | 0];
7
+ return id;
8
+ }
9
+
10
+ //#endregion
11
+ export { nanoid };
@@ -0,0 +1,53 @@
1
+ import { t as EventEmitter } from "../events-57bKw4ct.mjs";
2
+ import { Objectish, Patch, Patch as SharedStatePatch } from "immer";
3
+
4
+ //#region src/utils/shared-state.d.ts
5
+ type ImmutablePrimitive = undefined | null | boolean | string | number | Function;
6
+ type Immutable<T> = T extends ImmutablePrimitive ? T : T extends Array<infer U> ? ImmutableArray<U> : T extends Map<infer K, infer V> ? ImmutableMap<K, V> : T extends Set<infer M> ? ImmutableSet<M> : ImmutableObject<T>;
7
+ type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
8
+ type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
9
+ type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
10
+ type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };
11
+ /**
12
+ * State host that is immutable by default with explicit mutate.
13
+ */
14
+ interface SharedState<T> {
15
+ /**
16
+ * Get the current state. Immutable.
17
+ */
18
+ value: () => Immutable<T>;
19
+ /**
20
+ * Subscribe to state changes.
21
+ */
22
+ on: EventEmitter<SharedStateEvents<T>>['on'];
23
+ /**
24
+ * Mutate the state.
25
+ */
26
+ mutate: (fn: (state: T) => void, syncId?: string) => void;
27
+ /**
28
+ * Apply patches to the state.
29
+ */
30
+ patch: (patches: Patch[], syncId?: string) => void;
31
+ /**
32
+ * Sync IDs that have been applied to the state.
33
+ */
34
+ syncIds: Set<string>;
35
+ }
36
+ interface SharedStateEvents<T> {
37
+ updated: (fullState: T, patches: Patch[] | undefined, syncId: string) => void;
38
+ }
39
+ interface SharedStateOptions<T> {
40
+ /**
41
+ * Initial state.
42
+ */
43
+ initialValue: T;
44
+ /**
45
+ * Enable patches.
46
+ *
47
+ * @default false
48
+ */
49
+ enablePatches?: boolean;
50
+ }
51
+ declare function createSharedState<T extends Objectish>(options: SharedStateOptions<T>): SharedState<T>;
52
+ //#endregion
53
+ export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, type SharedStatePatch, createSharedState };
@@ -0,0 +1,38 @@
1
+ import { createEventEmitter } from "./events.mjs";
2
+ import { nanoid } from "./nanoid.mjs";
3
+ import { applyPatches, enablePatches, produce, produceWithPatches } from "immer";
4
+
5
+ //#region src/utils/shared-state.ts
6
+ function createSharedState(options) {
7
+ const { enablePatches: enablePatches$1 = false } = options;
8
+ const events = createEventEmitter();
9
+ let state = options.initialValue;
10
+ const syncIds = /* @__PURE__ */ new Set();
11
+ return {
12
+ on: events.on,
13
+ value: () => state,
14
+ patch: (patches, syncId = nanoid()) => {
15
+ if (syncIds.has(syncId)) return;
16
+ enablePatches();
17
+ state = applyPatches(state, patches);
18
+ syncIds.add(syncId);
19
+ events.emit("updated", state, void 0, syncId);
20
+ },
21
+ mutate: (fn, syncId = nanoid()) => {
22
+ if (syncIds.has(syncId)) return;
23
+ syncIds.add(syncId);
24
+ if (enablePatches$1) {
25
+ const [newState, patches] = produceWithPatches(state, fn);
26
+ state = newState;
27
+ events.emit("updated", state, patches, syncId);
28
+ } else {
29
+ state = produce(state, fn);
30
+ events.emit("updated", state, void 0, syncId);
31
+ }
32
+ },
33
+ syncIds
34
+ };
35
+ }
36
+
37
+ //#endregion
38
+ export { createSharedState };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@vitejs/devtools-kit",
3
3
  "type": "module",
4
- "version": "0.0.0-alpha.3",
4
+ "version": "0.0.0-alpha.31",
5
5
  "description": "Vite DevTools Kit",
6
6
  "author": "VoidZero Inc.",
7
7
  "license": "MIT",
8
8
  "homepage": "https://github.com/vitejs/devtools#readme",
9
9
  "repository": {
10
- "directory": "packages/devtools-kit",
10
+ "directory": "packages/kit",
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/vitejs/devtools.git"
13
13
  },
@@ -19,26 +19,31 @@
19
19
  ],
20
20
  "sideEffects": false,
21
21
  "exports": {
22
- ".": "./dist/index.js",
23
- "./client": "./dist/client.js",
22
+ ".": "./dist/index.mjs",
23
+ "./client": "./dist/client.mjs",
24
+ "./constants": "./dist/constants.mjs",
25
+ "./utils/events": "./dist/utils/events.mjs",
26
+ "./utils/nanoid": "./dist/utils/nanoid.mjs",
27
+ "./utils/shared-state": "./dist/utils/shared-state.mjs",
24
28
  "./package.json": "./package.json"
25
29
  },
26
- "main": "./dist/index.js",
27
- "module": "./dist/index.js",
28
- "types": "./dist/index.d.ts",
30
+ "types": "./dist/index.d.mts",
29
31
  "files": [
30
- "dist"
32
+ "dist",
33
+ "skills"
31
34
  ],
32
35
  "peerDependencies": {
33
- "vite": "npm:rolldown-vite@^7.1.14"
36
+ "vite": "*"
34
37
  },
35
38
  "dependencies": {
36
- "birpc": "^2.6.1",
37
- "@vitejs/devtools-rpc": "0.0.0-alpha.3"
39
+ "birpc": "^4.0.0",
40
+ "immer": "^11.1.3",
41
+ "@vitejs/devtools-rpc": "0.0.0-alpha.31"
38
42
  },
39
43
  "devDependencies": {
40
- "tsdown": "^0.15.6",
41
- "vite": "npm:rolldown-vite@^7.1.14"
44
+ "my-ua-parser": "^2.0.4",
45
+ "tsdown": "^0.20.2",
46
+ "vite": "^8.0.0-beta.13"
42
47
  },
43
48
  "scripts": {
44
49
  "build": "tsdown",
@@ -0,0 +1,325 @@
1
+ ---
2
+ name: writing-vite-devtools-integrations
3
+ description: >
4
+ Creates devtools integrations for Vite using @vitejs/devtools-kit.
5
+ Use when building Vite plugins with devtools panels, RPC functions,
6
+ dock entries, shared state, or any devtools-related functionality.
7
+ Applies to files importing from @vitejs/devtools-kit or containing
8
+ devtools.setup hooks in Vite plugins.
9
+ ---
10
+
11
+ # Vite DevTools Kit
12
+
13
+ Build custom developer tools that integrate with Vite DevTools using `@vitejs/devtools-kit`.
14
+
15
+ ## Core Concepts
16
+
17
+ A DevTools plugin extends a Vite plugin with a `devtools.setup(ctx)` hook. The context provides:
18
+
19
+ | Property | Purpose |
20
+ |----------|---------|
21
+ | `ctx.docks` | Register dock entries (iframe, action, custom-render) |
22
+ | `ctx.views` | Host static files for UI |
23
+ | `ctx.rpc` | Register RPC functions, broadcast to clients |
24
+ | `ctx.rpc.sharedState` | Synchronized server-client state |
25
+ | `ctx.viteConfig` | Resolved Vite configuration |
26
+ | `ctx.viteServer` | Dev server instance (dev mode only) |
27
+ | `ctx.mode` | `'dev'` or `'build'` |
28
+
29
+ ## Quick Start: Minimal Plugin
30
+
31
+ ```ts
32
+ /// <reference types="@vitejs/devtools-kit" />
33
+ import type { Plugin } from 'vite'
34
+
35
+ export default function myPlugin(): Plugin {
36
+ return {
37
+ name: 'my-plugin',
38
+ devtools: {
39
+ setup(ctx) {
40
+ ctx.docks.register({
41
+ id: 'my-plugin',
42
+ title: 'My Plugin',
43
+ icon: 'ph:puzzle-piece-duotone',
44
+ type: 'iframe',
45
+ url: 'https://example.com/devtools',
46
+ })
47
+ },
48
+ },
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Quick Start: Full Integration
54
+
55
+ ```ts
56
+ /// <reference types="@vitejs/devtools-kit" />
57
+ import type { Plugin } from 'vite'
58
+ import { fileURLToPath } from 'node:url'
59
+ import { defineRpcFunction } from '@vitejs/devtools-kit'
60
+
61
+ export default function myAnalyzer(): Plugin {
62
+ const data = new Map<string, { size: number }>()
63
+
64
+ return {
65
+ name: 'my-analyzer',
66
+
67
+ // Collect data in Vite hooks
68
+ transform(code, id) {
69
+ data.set(id, { size: code.length })
70
+ },
71
+
72
+ devtools: {
73
+ setup(ctx) {
74
+ // 1. Host static UI
75
+ const clientPath = fileURLToPath(
76
+ new URL('../dist/client', import.meta.url)
77
+ )
78
+ ctx.views.hostStatic('/.my-analyzer/', clientPath)
79
+
80
+ // 2. Register dock entry
81
+ ctx.docks.register({
82
+ id: 'my-analyzer',
83
+ title: 'Analyzer',
84
+ icon: 'ph:chart-bar-duotone',
85
+ type: 'iframe',
86
+ url: '/.my-analyzer/',
87
+ })
88
+
89
+ // 3. Register RPC function
90
+ ctx.rpc.register(
91
+ defineRpcFunction({
92
+ name: 'my-analyzer:get-data',
93
+ type: 'query',
94
+ setup: () => ({
95
+ handler: async () => Array.from(data.entries()),
96
+ }),
97
+ })
98
+ )
99
+ },
100
+ },
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Namespacing Convention
106
+
107
+ **CRITICAL**: Always prefix RPC functions, shared state keys, and dock IDs with your plugin name:
108
+
109
+ ```ts
110
+ // Good - namespaced
111
+ 'my-plugin:get-modules'
112
+ 'my-plugin:state'
113
+
114
+ // Bad - may conflict
115
+ 'get-modules'
116
+ 'state'
117
+ ```
118
+
119
+ ## Dock Entry Types
120
+
121
+ | Type | Use Case |
122
+ |------|----------|
123
+ | `iframe` | Full UI panels, dashboards (most common) |
124
+ | `action` | Buttons that trigger client-side scripts (inspectors, toggles) |
125
+ | `custom-render` | Direct DOM access in panel (framework mounting) |
126
+
127
+ ### Iframe Entry
128
+
129
+ ```ts
130
+ ctx.docks.register({
131
+ id: 'my-plugin',
132
+ title: 'My Plugin',
133
+ icon: 'ph:house-duotone',
134
+ type: 'iframe',
135
+ url: '/.my-plugin/',
136
+ })
137
+ ```
138
+
139
+ ### Action Entry
140
+
141
+ ```ts
142
+ ctx.docks.register({
143
+ id: 'my-inspector',
144
+ title: 'Inspector',
145
+ icon: 'ph:cursor-duotone',
146
+ type: 'action',
147
+ action: {
148
+ importFrom: 'my-plugin/devtools-action',
149
+ importName: 'default',
150
+ },
151
+ })
152
+ ```
153
+
154
+ ### Custom Render Entry
155
+
156
+ ```ts
157
+ ctx.docks.register({
158
+ id: 'my-custom',
159
+ title: 'Custom View',
160
+ icon: 'ph:code-duotone',
161
+ type: 'custom-render',
162
+ renderer: {
163
+ importFrom: 'my-plugin/devtools-renderer',
164
+ importName: 'default',
165
+ },
166
+ })
167
+ ```
168
+
169
+ ## RPC Functions
170
+
171
+ ### Server-Side Definition
172
+
173
+ ```ts
174
+ import { defineRpcFunction } from '@vitejs/devtools-kit'
175
+
176
+ const getModules = defineRpcFunction({
177
+ name: 'my-plugin:get-modules',
178
+ type: 'query', // 'query' | 'action' | 'static'
179
+ setup: ctx => ({
180
+ handler: async (filter?: string) => {
181
+ // ctx has full DevToolsNodeContext
182
+ return modules.filter(m => !filter || m.includes(filter))
183
+ },
184
+ }),
185
+ })
186
+
187
+ // Register in setup
188
+ ctx.rpc.register(getModules)
189
+ ```
190
+
191
+ ### Client-Side Call (iframe)
192
+
193
+ ```ts
194
+ import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client'
195
+
196
+ const rpc = await getDevToolsRpcClient()
197
+ const modules = await rpc.call('my-plugin:get-modules', 'src/')
198
+ ```
199
+
200
+ ### Client-Side Call (action/renderer script)
201
+
202
+ ```ts
203
+ import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
204
+
205
+ export default function setup(ctx: DevToolsClientScriptContext) {
206
+ ctx.current.events.on('entry:activated', async () => {
207
+ const data = await ctx.current.rpc.call('my-plugin:get-data')
208
+ })
209
+ }
210
+ ```
211
+
212
+ ### Broadcasting to Clients
213
+
214
+ ```ts
215
+ // Server broadcasts to all clients
216
+ ctx.rpc.broadcast({
217
+ method: 'my-plugin:on-update',
218
+ args: [{ changedFile: '/src/main.ts' }],
219
+ })
220
+ ```
221
+
222
+ ## Type Safety
223
+
224
+ Extend the DevTools Kit interfaces for full type checking:
225
+
226
+ ```ts
227
+ // src/types.ts
228
+ import '@vitejs/devtools-kit'
229
+
230
+ declare module '@vitejs/devtools-kit' {
231
+ interface DevToolsRpcServerFunctions {
232
+ 'my-plugin:get-modules': (filter?: string) => Promise<Module[]>
233
+ }
234
+
235
+ interface DevToolsRpcClientFunctions {
236
+ 'my-plugin:on-update': (data: { changedFile: string }) => void
237
+ }
238
+
239
+ interface DevToolsRpcSharedStates {
240
+ 'my-plugin:state': MyPluginState
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## Shared State
246
+
247
+ ### Server-Side
248
+
249
+ ```ts
250
+ const state = await ctx.rpc.sharedState.get('my-plugin:state', {
251
+ initialValue: { count: 0, items: [] },
252
+ })
253
+
254
+ // Read
255
+ console.log(state.value())
256
+
257
+ // Mutate (auto-syncs to clients)
258
+ state.mutate((draft) => {
259
+ draft.count += 1
260
+ draft.items.push('new item')
261
+ })
262
+ ```
263
+
264
+ ### Client-Side
265
+
266
+ ```ts
267
+ const client = await getDevToolsRpcClient()
268
+ const state = await client.rpc.sharedState.get('my-plugin:state')
269
+
270
+ // Read
271
+ console.log(state.value())
272
+
273
+ // Subscribe to changes
274
+ state.on('updated', (newState) => {
275
+ console.log('State updated:', newState)
276
+ })
277
+ ```
278
+
279
+ ## Client Scripts
280
+
281
+ For action buttons and custom renderers:
282
+
283
+ ```ts
284
+ // src/devtools-action.ts
285
+ import type { DevToolsClientScriptContext } from '@vitejs/devtools-kit/client'
286
+
287
+ export default function setup(ctx: DevToolsClientScriptContext) {
288
+ ctx.current.events.on('entry:activated', () => {
289
+ console.log('Action activated')
290
+ // Your inspector/tool logic here
291
+ })
292
+
293
+ ctx.current.events.on('entry:deactivated', () => {
294
+ console.log('Action deactivated')
295
+ // Cleanup
296
+ })
297
+ }
298
+ ```
299
+
300
+ Export from package.json:
301
+
302
+ ```json
303
+ {
304
+ "exports": {
305
+ ".": "./dist/index.mjs",
306
+ "./devtools-action": "./dist/devtools-action.mjs"
307
+ }
308
+ }
309
+ ```
310
+
311
+ ## Best Practices
312
+
313
+ 1. **Always namespace** - Prefix all identifiers with your plugin name
314
+ 2. **Use type augmentation** - Extend `DevToolsRpcServerFunctions` for type-safe RPC
315
+ 3. **Keep state serializable** - No functions or circular references in shared state
316
+ 4. **Batch mutations** - Use single `mutate()` call for multiple changes
317
+ 5. **Host static files** - Use `ctx.views.hostStatic()` for your UI assets
318
+ 6. **Use Iconify icons** - Prefer `ph:*` (Phosphor) icons: `icon: 'ph:chart-bar-duotone'`
319
+
320
+ ## Further Reading
321
+
322
+ - [RPC Patterns](./references/rpc-patterns.md) - Advanced RPC patterns and type utilities
323
+ - [Dock Entry Types](./references/dock-entry-types.md) - Detailed dock configuration options
324
+ - [Shared State Patterns](./references/shared-state-patterns.md) - Framework integration examples
325
+ - [Project Structure](./references/project-structure.md) - Recommended file organization