Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 42 additions & 21 deletions docs/content/2.module/1.utils-kit.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,39 +73,60 @@ dock entries directly via the handle returned by

A shorthand for call hook `devtools:customTabs:refresh`. It will refresh all custom tabs.

### Spawning terminals

**Use Vite DevTools' own terminals host** (`ctx.terminals`), reached through the
connected context from [`onDevtoolsReady()`](#ondevtoolsready). It spawns and
owns the process and surfaces it in the built-in **Terminals** dock — no
Nuxt-specific wrapper needed:

```ts
import { onDevtoolsReady } from '@nuxt/devtools-kit'

onDevtoolsReady((ctx) => {
// Output-only child process
ctx.terminals.startChildProcess(
{ command: 'npm', args: ['run', 'dev'] },
{ id: 'my-module:dev', title: 'Dev Server', icon: 'logos-npm-icon' },
)

// Fully interactive PTY (powered by `zigpty`, with a graceful pipe fallback)
ctx.terminals.startPtySession(
{ command: 'bash' },
{ id: 'my-module:shell', title: 'Shell', interactive: true },
)
})
```

If your module needs to keep owning the process itself and merely stream output,
register a read-only session with `ctx.terminals.register({ id, title, status, stream })`.
See the [Vite DevTools Kit](https://github.com/vitejs/devtools) docs for the full
`ctx.terminals` API.

### `startSubprocess()`

::warning
**Deprecated.** `startSubprocess()` is soft-deprecated (`NDT_DEP_0004`) in favour
of the Vite DevTools terminals host
(`nuxt.devtools.terminals.startChildProcess(...)`). It still works as a shim. See
the [migration guide](/module/migration-v4#ndt_dep_0004).
**Deprecated — do not use in new code.** `startSubprocess()` is soft-deprecated
(`NDT_DEP_0004`) in favour of the Vite DevTools terminals host, used from the
[`onDevtoolsReady`](#ondevtoolsready) hook
(`onDevtoolsReady((ctx) => ctx.terminals.startChildProcess(...))` — see
[Spawning terminals](#spawning-terminals) above). It still works as a shim
(bridged onto `ctx.terminals`), so existing modules keep working, but it emits
the `NDT_DEP_0004` deprecation diagnostic and will be removed in a future major.
See the [migration guide](/module/migration-v4#ndt_dep_0004).
::

Start a sub process using `tinyexec` and create a terminal tab in DevTools.
Legacy usage (module owns the process; output is surfaced as a read-only session
in the built-in **Terminals** dock):

```ts
import { startSubprocess } from '@nuxt/devtools-kit'

const subprocess = startSubprocess(
{
command: 'code-server',
args: [
'serve-local',
'--accept-server-license-terms',
'--without-connection-token',
`--port=${port}`,
],
},
{
id: 'devtools:vscode',
name: 'VS Code Server',
icon: 'logos-visual-studio-code',
},
{ command: 'vite', args: ['build', '--watch'] },
{ id: 'my-module:build', name: 'Build', icon: 'ph:terminal-duotone' },
)
```

```ts
subprocess.restart()
subprocess.terminate()
```
Expand Down
18 changes: 5 additions & 13 deletions packages/devtools-kit/src/_types/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ declare module '@nuxt/schema' {
'devtools:customTabs:refresh': () => void

/**
* Register a terminal.
* Register a terminal whose process is owned by the caller (module).
*
* The registered session is surfaced **read-only** in the built-in Vite
* DevTools **Terminals** dock; stream output into it via
* `devtools:terminal:write`.
*/
'devtools:terminal:register': (terminal: TerminalState) => void

Expand All @@ -78,16 +82,4 @@ declare module '@nuxt/schema' {
}
}

declare module '@nuxt/schema' {
/**
* Runtime Hooks
*/
interface RuntimeNuxtHooks {
/**
* On terminal data.
*/
'devtools:terminal:data': (payload: { id: string, data: string }) => void
}
}

export {}
11 changes: 6 additions & 5 deletions packages/devtools-kit/src/_types/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type { AssetEntry, AssetInfo, AutoImportsWithMetadata, ComponentRelations
import type { NuxtDevtoolsNotifyInput } from './notify'
import type { ModuleOptions, NuxtDevToolsOptions } from './options'
import type { InstallModuleReturn, ServerDebugContext } from './server-ctx'
import type { TerminalAction, TerminalInfo } from './terminals'

export interface ServerFunctions {
// Static RPCs (can be provide on production build in the future)
Expand Down Expand Up @@ -40,9 +39,7 @@ export interface ServerFunctions {
runNpmCommand: (command: NpmCommandType, packageName: string, options?: NpmCommandOptions) => Promise<{ processId: string } | undefined>

// Terminal
getTerminals: () => TerminalInfo[]
getTerminalDetail: (id: string) => Promise<TerminalInfo | undefined>
runTerminalAction: (id: string, action: TerminalAction) => Promise<boolean>
revealTerminal: (id: string) => Promise<boolean>

// Storage
getStorageMounts: () => Promise<StorageMounts>
Expand Down Expand Up @@ -87,7 +84,11 @@ export interface ClientFunctions {
callHook: (hook: string, ...args: any[]) => Promise<void>
navigateTo: (path: string) => void

onTerminalData: (_: { id: string, data: string }) => void
/**
* Server→client signal that a terminal session has exited. Kept so the
* client can clear transient subprocess UI state (installing-modules /
* analyze-build / npm updates) when the underlying process finishes.
*/
onTerminalExit: (_: { id: string, code?: number }) => void
}

Expand Down
11 changes: 5 additions & 6 deletions packages/devtools/client/components/ModuleItem.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { InstalledModuleInfo } from '../../src/types'
import { computed } from 'vue'
import { useCurrentTerminalId } from '~/composables/state-routes'
import { rpc } from '~/composables/rpc'

const props = defineProps<{
mod: InstalledModuleInfo
Expand All @@ -13,7 +13,6 @@ const data = computed(() => ({
...props.mod,
...staticInfo.value,
}))
const terminalId = useCurrentTerminalId()
</script>

<template>
Expand All @@ -27,15 +26,15 @@ const terminalId = useCurrentTerminalId()
<!-- NPM Version bump -->
<NpmVersionCheck v-if="data.npm" :key="data.npm" :package-name="data.npm" :options="{ dev: true }">
<template #default="{ info, update, state, id, restart }">
<NuxtLink
<button
v-if="state === 'running'" flex="~ gap-2"
animate-pulse items-center
:to="id ? '/modules/terminals' : undefined"
@click="id ? terminalId = id : undefined"
:title="id ? 'Open the output in the Terminals dock' : undefined"
@click="id ? rpc.revealTerminal(id) : undefined"
>
<span i-carbon-circle-dash flex-none animate-spin text-lg op50 />
<code text-sm op50>Upgrading...</code>
</NuxtLink>
</button>
<div v-else-if="state === 'updated'" mx--2>
<button
flex="~ gap-2"
Expand Down
17 changes: 6 additions & 11 deletions packages/devtools/client/components/NpmVersionCheck.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import type { NpmCommandOptions } from '../../src/types'
import { createTemplatePromise } from '@vueuse/core'
import { ref } from 'vue'
import { useRouter } from '#app/composables/router'
import { useRestartDialogs } from '~/composables/dialog'
import { usePackageUpdate } from '~/composables/npm'
import { useCurrentTerminalId } from '~/composables/state-routes'
import { rpc } from '~/composables/rpc'
import { telemetry } from '~/composables/telemetry'

const props = withDefaults(
Expand All @@ -19,7 +18,6 @@ const props = withDefaults(
},
)

const router = useRouter()
const {
info,
update,
Expand All @@ -28,12 +26,11 @@ const {
restart,
} = usePackageUpdate(props.packageName, props.options)

const shouldGotoTerminal = ref(true)
const shouldRevealTerminal = ref(true)
const shouldRestartServer = ref(true)
const restartDialogs = useRestartDialogs()

const PromiseConfirm = createTemplatePromise<boolean, [string]>()
const terminalId = useCurrentTerminalId()

async function updateWithConfirm() {
const processId = await update(async (command) => {
Expand All @@ -51,10 +48,8 @@ async function updateWithConfirm() {
message: `${props.packageName} has been updated. Do you want to restart the Nuxt server now?`,
})
}
if (processId && shouldGotoTerminal.value) {
terminalId.value = processId
router.push('/modules/terminals')
}
if (processId && shouldRevealTerminal.value)
rpc.revealTerminal(processId)
}
</script>

Expand Down Expand Up @@ -88,8 +83,8 @@ async function updateWithConfirm() {
The following command will be executed in your terminal:
</p>
<NCodeBlock :code="args[0]" lang="bash" my3 px4 py2 border="~ base rounded" :lines="false" />
<NCheckbox v-model="shouldGotoTerminal" n="primary">
Navigate to terminal
<NCheckbox v-model="shouldRevealTerminal" n="primary">
Open the Terminals dock
</NCheckbox>
<NCheckbox v-model="shouldRestartServer" n="primary">
Restart Nuxt server after update
Expand Down
64 changes: 0 additions & 64 deletions packages/devtools/client/components/TerminalPage.vue

This file was deleted.

68 changes: 0 additions & 68 deletions packages/devtools/client/components/TerminalView.vue

This file was deleted.

5 changes: 0 additions & 5 deletions packages/devtools/client/composables/state-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ export function useCurrentVirtualFile() {
return useSessionState<string>('virtual-files:current', '')
}

// terminals Tab
export function useCurrentTerminalId() {
return useSessionState<string>('terminals:current', '')
}

// server-routes Tab
export function useCurrentServeRoute() {
return useSessionState<string>('server-routes:current', '')
Expand Down
4 changes: 0 additions & 4 deletions packages/devtools/client/composables/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ export function useCustomTabs() {
return useAsyncState('getCustomTabs', () => rpc.getCustomTabs())
}

export function useTerminals() {
return useAsyncState('getTerminals', () => rpc.getTerminals())
}

export function useAnalyzeBuildInfo() {
return useAsyncState('getAnalyzeBuildInfo', () => rpc.getAnalyzeBuildInfo())
}
Expand Down
Loading
Loading