Skip to content

Commit a366e1a

Browse files
committed
Merge remote-tracking branch 'origin/main' into antfu/self-inspect-plugin
2 parents 90d29a5 + 39b11d8 commit a366e1a

File tree

16 files changed

+84
-171
lines changed

16 files changed

+84
-171
lines changed

docs/guide/index.md

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,19 @@ Install or upgrade your Vite to the beta version 13+:
3636
}
3737
```
3838

39-
Install the required DevTools plugin for both client modes:
39+
Install the required DevTools package:
4040

4141
```bash
4242
pnpm add -D @vitejs/devtools
4343
```
4444

45-
Vite DevTools supports two client modes: embedded and standalone. You can choose the mode in your Vite config.
45+
Vite DevTools has two client modes. Configure one mode at a time.
4646

47-
Use embedded mode:
47+
### Standalone mode
4848

49-
```ts [vite.config.ts] twoslash
50-
import { DevTools } from '@vitejs/devtools'
51-
import { defineConfig } from 'vite'
49+
The DevTools client runs in a standalone window (no user app).
5250

53-
export default defineConfig({
54-
plugins: [
55-
DevTools(),
56-
],
57-
build: {
58-
rolldownOptions: {
59-
devtools: {}, // enable devtools mode
60-
},
61-
}
62-
})
63-
```
64-
Or use standalone mode:
51+
Configure `vite.config.ts`:
6552

6653
```ts [vite.config.ts] twoslash
6754
import { defineConfig } from 'vite'
@@ -73,19 +60,45 @@ export default defineConfig({
7360
})
7461
```
7562

76-
Run a Vite build to generate Rolldown build metadata. In standalone mode, DevTools also starts a local server to host the client after the build completes.
63+
Run:
7764

7865
```bash
7966
pnpm build
8067
```
8168

69+
After the build completes, open the DevTools URL shown in the terminal.
8270

83-
If you're using embedded mode, start your app and open the DevTools panel in the browser:
71+
### Embedded mode
72+
73+
The DevTools client runs inside an embedded floating panel.
74+
75+
Configure `vite.config.ts`:
76+
77+
```ts [vite.config.ts] twoslash
78+
import { DevTools } from '@vitejs/devtools'
79+
import { defineConfig } from 'vite'
80+
81+
export default defineConfig({
82+
plugins: [
83+
DevTools(),
84+
],
85+
build: {
86+
rolldownOptions: {
87+
devtools: {}, // enable devtools mode
88+
},
89+
}
90+
})
91+
```
92+
93+
Run:
8494

8595
```bash
96+
pnpm build
8697
pnpm dev
8798
```
8899

100+
Then open your app in the browser and open the DevTools panel.
101+
89102
## What's Next?
90103

91104
Now that you have Vite DevTools set up, you can:

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
"magic-string": "catalog:build",
5757
"nano-staged": "catalog:devtools",
5858
"nuxt": "catalog:build",
59-
"nuxt-eslint-auto-explicit-import": "catalog:devtools",
6059
"p-limit": "catalog:deps",
6160
"simple-git-hooks": "catalog:devtools",
6261
"tsdown": "catalog:build",

packages/core/playground/vite.config.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -149,23 +149,34 @@ export default defineConfig({
149149
initialValue: { count: 1 },
150150
})
151151

152-
// eslint-disable-next-line unimport/auto-insert
153-
setInterval(() => {
154-
counterState.mutate((current) => {
155-
current.count = (current.count + 1) % 5
156-
})
157-
const count = counterState.value().count
152+
counterState.on('updated', (newState) => {
158153
ctx.docks.update({
159154
id: 'counter',
160155
type: 'action',
161-
icon: `material-symbols:counter-${count}`,
162-
title: `Counter ${count}`,
163-
// TODO: HMR?
156+
icon: `material-symbols:counter-${newState.count}`,
157+
title: `Counter ${newState.count}`,
164158
action: ctx.utils.createSimpleClientScript(`() => {
165-
alert('Counter ${count}')
159+
alert('Counter ${newState.count}')
166160
}`),
167161
})
168-
}, 1000)
162+
})
163+
164+
// setInterval(() => {
165+
// counterState.mutate((current) => {
166+
// current.count = (current.count + 1) % 5
167+
// })
168+
// const count = counterState.value().count
169+
// ctx.docks.update({
170+
// id: 'counter',
171+
// type: 'action',
172+
// icon: `material-symbols:counter-${count}`,
173+
// title: `Counter ${count}`,
174+
// // TODO: HMR?
175+
// action: ctx.utils.createSimpleClientScript(`() => {
176+
// alert('Counter ${count}')
177+
// }`),
178+
// })
179+
// }, 1000)
169180
},
170181
},
171182
},

packages/core/src/client/webcomponents/components/Dock.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
7575
context.docks.switchEntry(null)
7676
})
7777
78-
const isPopupEntryVisible = computed(() => isDockPopupEntryVisible())
78+
const isPopupEntryVisible = computed(() => isDockPopupEntryVisible(context.clientType))
7979
const groupedEntries = computed(() => {
8080
if (isPopupEntryVisible.value)
8181
return context.docks.groupedEntries
8282
return filterPopupDockEntry(context.docks.groupedEntries)
8383
})
8484
85-
const splitedEntries = computed(() => {
85+
const splitEntries = computed(() => {
8686
return docksSplitGroupsWithCapacity(groupedEntries.value, 5)
8787
})
8888
@@ -330,18 +330,18 @@ onMounted(() => {
330330
>
331331
<DockEntriesWithCategories
332332
:context="context"
333-
:groups="splitedEntries.visible"
333+
:groups="splitEntries.visible"
334334
:is-vertical="context.panel.isVertical"
335335
:selected="selectedEntry"
336336
@select="(e) => context.docks.switchEntry(e?.id)"
337337
/>
338338

339-
<template v-if="splitedEntries.overflow.length > 0">
339+
<template v-if="splitEntries.overflow.length > 0">
340340
<div class="border-base m1 h-20px w-px border-r-1.5" />
341341
<DockOverflowButton
342342
:context="context"
343343
:is-vertical="context.panel.isVertical"
344-
:groups="splitedEntries.overflow"
344+
:groups="splitEntries.overflow"
345345
:selected="selectedEntry"
346346
@select="(e) => context.docks.switchEntry(e?.id)"
347347
/>

packages/core/src/client/webcomponents/components/DockStandalone.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,11 @@ import { defineCustomElement } from 'vue'
44
import css from '../.generated/css'
55
import Component from './DockStandalone.vue'
66

7-
const forcedColorModeCss = `
8-
:host([data-vite-devtools-color-mode='dark']) {
9-
color-scheme: dark;
10-
}
11-
:host([data-vite-devtools-color-mode='light']) {
12-
color-scheme: light;
13-
}
14-
`
15-
167
export const DockStandalone = defineCustomElement(
178
Component,
189
{
1910
shadowRoot: true,
20-
styles: [css, forcedColorModeCss],
11+
styles: [css],
2112
},
2213
) as VueElementConstructor<{
2314
context: DocksContext

packages/core/src/client/webcomponents/components/DockStandalone.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ watch(
3131
)
3232
3333
const groupedEntries = computed(() => {
34-
if (isDockPopupEntryVisible())
34+
if (isDockPopupEntryVisible('standalone'))
3535
return context.docks.groupedEntries
3636
3737
return filterPopupDockEntry(context.docks.groupedEntries)

packages/core/src/client/webcomponents/state/__tests__/popup.test.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
import type { DocksContext } from '@vitejs/devtools-kit/client'
22
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
33
import { setDocksOverflowPanel, useDocksOverflowPanel } from '../floating-tooltip'
4-
import { closeDockPopup, filterPopupDockEntry, isDockPopupEntryVisible, isDockPopupSupported, isRunningInDockPopupWindow, openDockPopup, setDockStandaloneLoaderForTest, useDockPopupWindow, useIsDockPopupOpen } from '../popup'
4+
import { closeDockPopup, filterPopupDockEntry, isDockPopupEntryVisible, isDockPopupSupported, openDockPopup, setDockStandaloneLoaderForTest, useDockPopupWindow, useIsDockPopupOpen } from '../popup'
55

66
const {
77
DockStandaloneElementMock,
88
dockStandaloneCtorCalls,
99
dockElementRemoveMock,
10-
dockElementSetAttributeMock,
1110
} = vi.hoisted(() => {
1211
const dockElementRemoveMock = vi.fn()
13-
const dockElementSetAttributeMock = vi.fn()
1412
const dockStandaloneCtorCalls: Array<{ context: DocksContext }> = []
1513
class DockStandaloneElementMock {
1614
context: DocksContext
1715
remove: () => void
18-
setAttribute: (name: string, value: string) => void
16+
style: Record<string, string>
1917
constructor({ context }: { context: DocksContext }) {
2018
this.context = context
2119
this.remove = dockElementRemoveMock
22-
this.setAttribute = dockElementSetAttributeMock
20+
this.style = {}
2321
dockStandaloneCtorCalls.push({ context })
2422
}
2523
}
2624
return {
2725
DockStandaloneElementMock: DockStandaloneElementMock as unknown as new (props: { context: DocksContext }) => HTMLElement,
2826
dockStandaloneCtorCalls,
2927
dockElementRemoveMock,
30-
dockElementSetAttributeMock,
3128
}
3229
})
3330

@@ -135,7 +132,6 @@ describe('dock popup state', () => {
135132
setDockStandaloneLoaderForTest(async () => DockStandaloneElementMock)
136133
dockStandaloneCtorCalls.length = 0
137134
dockElementRemoveMock.mockClear()
138-
dockElementSetAttributeMock.mockClear()
139135
;(globalThis as { window?: any }).window = {
140136
innerWidth: 1200,
141137
innerHeight: 800,
@@ -149,7 +145,7 @@ describe('dock popup state', () => {
149145

150146
it('returns null when the API is unavailable', async () => {
151147
expect(isDockPopupSupported()).toBe(false)
152-
expect(isDockPopupEntryVisible()).toBe(false)
148+
expect(isDockPopupEntryVisible('embedded')).toBe(false)
153149
const popup = await openDockPopup(createMockContext())
154150
expect(popup).toBeNull()
155151
expect(useIsDockPopupOpen().value).toBe(false)
@@ -160,21 +156,15 @@ describe('dock popup state', () => {
160156
const requestWindow = vi.fn().mockResolvedValue(popup)
161157
;(window as Window & { documentPictureInPicture?: unknown }).documentPictureInPicture = { requestWindow }
162158

163-
expect(isDockPopupEntryVisible()).toBe(true)
159+
expect(isDockPopupEntryVisible('embedded')).toBe(true)
164160
await openDockPopup(createMockContext())
165-
expect(isDockPopupEntryVisible()).toBe(false)
161+
expect(isDockPopupEntryVisible('embedded')).toBe(false)
166162
})
167163

168164
it('hides popup entry when running inside popup window', () => {
169165
;(window as Window & { documentPictureInPicture?: unknown }).documentPictureInPicture = { requestWindow: vi.fn() }
170-
;(window as Window & { document?: unknown }).document = {
171-
documentElement: {
172-
hasAttribute: vi.fn((name: string) => name === 'data-vite-devtools-popup-window'),
173-
},
174-
} as any
175166

176-
expect(isRunningInDockPopupWindow()).toBe(true)
177-
expect(isDockPopupEntryVisible()).toBe(false)
167+
expect(isDockPopupEntryVisible('standalone')).toBe(false)
178168
})
179169

180170
it('filters popup entry from grouped entries', () => {
@@ -220,7 +210,6 @@ describe('dock popup state', () => {
220210
expect(appRoot).toBeTruthy()
221211
expect(appRoot.id).toBe('vite-devtools-popup-root')
222212
expect(appRoot.appended).toHaveLength(1)
223-
expect(dockElementSetAttributeMock).toHaveBeenCalledWith('data-vite-devtools-color-mode', 'light')
224213
})
225214

226215
it('hides dock overflow panel when opening popup', async () => {

packages/core/src/client/webcomponents/state/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export async function createDocksContext(
6565

6666
// If the action is in a popup, delegate to the main frame
6767
if (entry.type === 'action') {
68-
const delegated = await triggerMainFrameDockAction(entry.id)
68+
const delegated = await triggerMainFrameDockAction(clientType, entry.id)
6969
if (delegated != null)
7070
return false
7171
}
@@ -135,7 +135,7 @@ export async function createDocksContext(
135135
clientType,
136136
})
137137

138-
registerMainFrameDockActionHandler(async (id) => {
138+
registerMainFrameDockActionHandler(clientType, async (id) => {
139139
const entry = dockEntries.value.find(e => e.id === id)
140140
if (!entry || entry.type !== 'action')
141141
return false

packages/core/src/client/webcomponents/state/popup.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const PANEL_MAX_SIZE = 100
1919
const POPUP_MIN_WIDTH = 320
2020
const POPUP_MIN_HEIGHT = 240
2121
const POPUP_DOCK_ID = '~popup'
22-
const POPUP_WINDOW_ATTRIBUTE = 'data-vite-devtools-popup-window'
2322
const MAIN_FRAME_ACTION_HANDLER_KEY = '__VITE_DEVTOOLS_TRIGGER_DOCK_ACTION__'
2423

2524
const popupWindow = shallowRef<Window | null>(null)
@@ -81,9 +80,7 @@ function resolveColorMode(): ColorMode {
8180
}
8281

8382
function applyPopupColorMode(popup: Window, mode: ColorMode) {
84-
popup.document.documentElement?.setAttribute('data-vite-devtools-color-mode', mode)
8583
popup.document.documentElement?.style.setProperty('color-scheme', mode)
86-
popupDockElement?.setAttribute('data-vite-devtools-color-mode', mode)
8784
}
8885

8986
function setupPopupColorModeSync(popup: Window): () => void {
@@ -177,7 +174,6 @@ async function mountStandaloneApp(context: DocksContext, popup: Window) {
177174
].join('\n')
178175

179176
popup.document.title = 'Vite DevTools'
180-
popup.document.documentElement?.setAttribute(POPUP_WINDOW_ATTRIBUTE, '')
181177
popup.document.head?.appendChild(baseStyle)
182178
popup.document.body.textContent = ''
183179

@@ -194,30 +190,26 @@ export function isDockPopupSupported(): boolean {
194190
return !!getDocumentPictureInPicture()?.requestWindow
195191
}
196192

197-
export function isRunningInDockPopupWindow(): boolean {
198-
if (typeof window === 'undefined')
199-
return false
200-
return !!window.document?.documentElement?.hasAttribute?.(POPUP_WINDOW_ATTRIBUTE)
201-
}
202-
203193
export function registerMainFrameDockActionHandler(
194+
clientType: 'embedded' | 'standalone',
204195
handler: MainFrameDockActionHandler,
205196
) {
206197
if (typeof window === 'undefined') {
207198
return
208199
}
209-
if (isRunningInDockPopupWindow()) {
200+
if (clientType === 'standalone') {
210201
return
211202
}
212203
;(window as Window & { [MAIN_FRAME_ACTION_HANDLER_KEY]?: MainFrameDockActionHandler })[MAIN_FRAME_ACTION_HANDLER_KEY] = handler
213204
}
214205

215206
export async function triggerMainFrameDockAction(
207+
clientType: 'embedded' | 'standalone',
216208
entryId: string,
217209
): Promise<boolean | undefined> {
218210
if (typeof window === 'undefined')
219211
return undefined
220-
if (!isRunningInDockPopupWindow())
212+
if (clientType !== 'standalone')
221213
return undefined
222214

223215
try {
@@ -234,8 +226,8 @@ export async function triggerMainFrameDockAction(
234226
}
235227
}
236228

237-
export function isDockPopupEntryVisible(): boolean {
238-
return isDockPopupSupported() && !isPopupOpen.value && !isRunningInDockPopupWindow()
229+
export function isDockPopupEntryVisible(clientType: 'embedded' | 'standalone'): boolean {
230+
return isDockPopupSupported() && !isPopupOpen.value && clientType !== 'standalone'
239231
}
240232

241233
export function filterPopupDockEntry(

0 commit comments

Comments
 (0)