Skip to content

Commit eb96a7c

Browse files
antfubotantfu
andauthored
feat(devtools)!: make embedded dock the only client mode (#1041)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent 3062113 commit eb96a7c

29 files changed

Lines changed: 312 additions & 1165 deletions

‎eslint.config.mjs‎

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
import{existsSync}from'node:fs'
2+
import{fileURLToPath}from'node:url'
13
importantfufrom'@antfu/eslint-config'
24
import{extend}from'eslint-flat-config-utils'
35

4-
exportdefaultantfu(
6+
// The client Nuxt app's generated flat config only exists once the app has been
7+
// prepared (`pnpm dev:prepare`/`pnpm build`). Skip it gracefully when it hasn't
8+
// been generated yet (e.g. a fresh `pnpm install && pnpm lint` in CI) so linting
9+
// doesn't hard-crash on the missing import.
10+
constclientConfigUrl=newURL('./packages/devtools/client/.nuxt/eslint.config.mjs',import.meta.url)
11+
consthasClientConfig=existsSync(fileURLToPath(clientConfigUrl))
12+
13+
constconfig=antfu(
514
{
615
formatters: true,
716
unocss: true,
@@ -22,12 +31,37 @@ export default antfu(
2231
},
2332
},
2433
)
25-
.append(
34+
.removeRules(
35+
'vue/no-multiple-template-root',
36+
)
37+
38+
// `FlatConfigComposer.append()` mutates the composer in place (and returns it),
39+
// so the export stays a `const`.
40+
if(hasClientConfig){
41+
config.append(
2642
extend(
27-
import('./packages/devtools/client/.nuxt/eslint.config.mjs').then(mod=>mod.default()),
43+
import(clientConfigUrl.href).then(mod=>mod.default()),
2844
'packages/devtools/client',
2945
),
3046
)
31-
.removeRules(
32-
'vue/no-multiple-template-root',
47+
}
48+
else{
49+
// Before the client app is prepared, its generated flat config (which wires up
50+
// `eslint-plugin-unimport`) is absent, so the `unimport/auto-insert` rule the
51+
// client's inline `eslint-disable` directives reference is undefined. Register
52+
// the plugin so those directives resolve and linting doesn't error.
53+
config.append(
54+
import('eslint-plugin-unimport').then(mod=>({
55+
name: 'nuxt-devtools/client/unimport-fallback',
56+
files: ['packages/devtools/client/**/*.{ts,vue}'],
57+
plugins: {unimport: mod.default??mod},
58+
rules: {'unimport/auto-insert': 'off'},
59+
// The rule is off in this fallback, so the client's inline disable
60+
// directives read as "unused"; don't report (or auto-fix away) them —
61+
// they are needed once the client app is prepared and the rule is active.
62+
linterOptions: {reportUnusedDisableDirectives: 'off'},
63+
})),
3364
)
65+
}
66+
67+
exportdefaultconfig

‎packages/devtools-kit/src/_types/options.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,10 @@ export interface NuxtDevToolsOptions {
235235
componentsView: 'list'|'graph'
236236
hiddenTabCategories: string[]
237237
hiddenTabs: string[]
238-
interactionCloseOnOutsideClick: boolean
239238
pinnedTabs: string[]
240239
scale: number
241240
showExperimentalFeatures: boolean
242241
showHelpButtons: boolean
243-
sidebarExpanded: boolean
244-
sidebarScrollable: boolean
245242
}
246243
serverRoutes: {
247244
selectedRoute: ServerRouteInfo|null

‎packages/devtools/client/app.vue‎

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
<script setup lang="ts">
22
import { useEventListener, useEyeDropper } from'@vueuse/core'
33
import { computed, onMounted, ref, watch, watchEffect } from'vue'
4-
import { useRoute } from'#app/composables/router'
54
import { useHead } from'#imports'
65
import { getColorMode, showConnectionWarning, useClient, useInjectionClient } from'~/composables/client'
76
import { useCopy } from'~/composables/editor'
8-
import { isEmbedded } from'~/composables/embed'
97
import { setupFrameNav } from'~/composables/frame-nav'
108
import { WS_DEBOUNCE_TIME } from'~/composables/rpc'
119
import { registerCommands } from'~/composables/state-commands'
12-
import { splitScreenAvailable, splitScreenEnabled } from'~/composables/storage'
1310
import { wsConnectedOnce } from'./composables/rpc'
1411
import { useSchemaInput } from'./composables/state-schema'
1512
import { useDevToolsOptions } from'./composables/storage-options'
@@ -43,12 +40,7 @@ useHead({
4340
setupClientRPC()
4441
4542
const client =useClient()
46-
const route =useRoute()
4743
const colorMode =getColorMode()
48-
// When embedded as the shared-frame anchor (`?embed=1`), hide the app shell
49-
// (SideNav + split pane) so the iframe shows only the current tab; the dock's
50-
// per-tab members drive navigation via the frame-nav shim.
51-
const isUtilityView =computed(() =>isEmbedded.value||route.path.startsWith('/__') ||route.path==='/')
5244
const waiting =computed(() =>!client.value&&!showConnectionWarning.value)
5345
const showDisconnectIndicator =ref(false)
5446
@@ -88,14 +80,14 @@ useEventListener('keydown', (e) => {
8880
}
8981
})
9082
91-
const { scale, sidebarExpanded } =useDevToolsOptions('ui')
83+
const { scale } =useDevToolsOptions('ui')
9284
const dataSchema =useSchemaInput()
9385
9486
onMounted(async () => {
95-
//As the shared-frame anchor, announce our tabs to the dock and answer
96-
// soft-navigation over postMessage.
97-
if (isEmbedded.value)
98-
setupFrameNav()
87+
//Nuxt DevTools always renders as the shared-frame anchor inside the Vite
88+
//DevTools dock: announce our tabs to the dock and answer soft-navigation
89+
// over postMessage.
90+
setupFrameNav()
9991
10092
const injectClient =useInjectionClient()
10193
watchEffect(() => {
@@ -111,16 +103,6 @@ const copy = useCopy()
111103
const eyeDropper =useEyeDropper({})
112104
113105
registerCommands(() => [
114-
...(splitScreenAvailable.value
115-
? [{
116-
id: 'action:split-screen',
117-
title: `${splitScreenEnabled.value?'Close':'Open'} Split Screen`,
118-
icon: 'i-carbon-split-screen',
119-
action: () => {
120-
splitScreenEnabled.value=!splitScreenEnabled.value
121-
},
122-
}]
123-
: []),
124106
...(eyeDropper.isSupported.value
125107
? [{
126108
id: 'action:eye-dropper',
@@ -158,21 +140,14 @@ registerCommands(() => [
158140
</NLoading>
159141
<div
160142
v-else
161-
:class="isEmbedded ? 'grid grid-cols-[1fr]' : isUtilityView ? 'flex' : sidebarExpanded ? 'grid grid-cols-[250px_1fr]' : 'grid grid-cols-[50px_1fr]'"
143+
id="nuxt-devtools-app"
144+
class="grid grid-cols-[1fr]"
162145
h-fullh-screenof-hiddenrounded-xlbg-basefont-sans
163146
>
164-
<SideNavv-show="!isUtilityView" of-x-hidden of-y-auto />
147+
<!-- Single-tab view: the shared-frame anchor mounts the current tab
148+
directly; the Vite DevTools dock provides navigation between tabs. -->
165149
<NuxtLayout>
166-
<!-- Embedded single-tab view: mount the tab content directly, no split pane -->
167-
<NuxtPagev-if="isEmbedded" />
168-
<NSplitPanev-else storage-key="devtools:split-screen-mode" :min-size="20">
169-
<template #left>
170-
<NuxtPage />
171-
</template>
172-
<templatev-if="!isUtilityView&&splitScreenEnabled&&splitScreenAvailable" #right>
173-
<SplitScreen />
174-
</template>
175-
</NSplitPane>
150+
<NuxtPage />
176151
</NuxtLayout>
177152
<CommandPalette />
178153
</div>

‎packages/devtools/client/components/DockingPanel.vue‎

Lines changed: 0 additions & 44 deletions
This file was deleted.

‎packages/devtools/client/components/SideNav.vue‎

Lines changed: 0 additions & 170 deletions
This file was deleted.

0 commit comments

Comments
 (0)