Skip to content

Commit 355ecf5

Browse files
committed
feat: improve terminals
1 parent 350f24d commit 355ecf5

File tree

10 files changed

+81
-42
lines changed

10 files changed

+81
-42
lines changed

packages/core/src/client/webcomponents/.generated/css.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ function toggleDockEntry(dock: DevToolsDockEntry) {
2525

2626
<template>
2727
<div>
28-
<DockEntry
29-
v-for="dock of entries"
30-
:key="dock.id"
31-
:dock
32-
:is-selected="selected?.id === dock.id"
33-
:is-dimmed="selected ? (selected.id !== dock.id) : false"
34-
:is-vertical="isVertical"
35-
@click="toggleDockEntry(dock)"
36-
/>
28+
<template v-for="dock of entries" :key="dock.id">
29+
<DockEntry
30+
v-if="!dock.isHidden"
31+
:dock
32+
:is-selected="selected?.id === dock.id"
33+
:is-dimmed="selected ? (selected.id !== dock.id) : false"
34+
:is-vertical="isVertical"
35+
@click="toggleDockEntry(dock)"
36+
/>
37+
</template>
3738
</div>
3839
</template>

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { TerminalState } from '../state/terminals'
55
import { useEventListener } from '@vueuse/core'
66
import { FitAddon } from '@xterm/addon-fit'
77
import { Terminal } from '@xterm/xterm'
8-
import { markRaw, onMounted, ref } from 'vue'
8+
import { markRaw, nextTick, onMounted, onUnmounted, ref } from 'vue'
99
1010
const props = defineProps<{
1111
context: DocksContext
@@ -30,14 +30,24 @@ onMounted(async () => {
3030
fitAddon.fit()
3131
})
3232
33+
nextTick(() => {
34+
fitAddon.fit()
35+
})
36+
37+
props.terminal.terminal = term
38+
3339
if (props.terminal.buffer == null) {
3440
const { buffer } = await props.context.rpc.$call('vite:internal:terminals:read', props.terminal.info.id)
3541
props.terminal.buffer = markRaw(buffer)
36-
for (const chunk of buffer)
37-
term.writeln(chunk)
3842
}
3943
40-
props.terminal.terminal = term
44+
for (const chunk of props.terminal.buffer)
45+
term.writeln(chunk)
46+
})
47+
48+
onUnmounted(() => {
49+
term.dispose()
50+
props.terminal.terminal = null
4151
})
4252
4353
// async function clear() {
@@ -57,9 +67,9 @@ onMounted(async () => {
5767
<template>
5868
<div ref="container" class="h-full w-full of-auto bg-black" />
5969
<!-- <div border="t base" flex="~ gap-2" items-center p2>
60-
<NButton title="Clear" icon="i-carbon-clean" :border="false" @click="clear()" />
61-
<NButton v-if="info?.restartable" title="Restart" icon="carbon-renew" :border="false" @click="restart()" />
62-
<NButton v-if="info?.terminatable" title="Terminate" icon="carbon-delete" :border="false" @click="terminate()" />
70+
<button title="Clear" icon="i-carbon-clean" :border="false" @click="clear()" />
71+
<button v-if="info?.restartable" title="Restart" icon="carbon-renew" :border="false" @click="restart()" />
72+
<button v-if="info?.terminatable" title="Terminate" icon="carbon-delete" :border="false" @click="terminate()" />
6373
<span text-sm op50>{{ info?.description }}</span>
6474
</div> -->
6575
</template>

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

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
22
import type { DocksContext } from '@vitejs/devtools-kit/client'
33
import type { TerminalState } from '../state/terminals'
4+
import { watchImmediate } from '@vueuse/core'
45
import { shallowRef } from 'vue'
56
import { useTerminals } from '../state/terminals'
7+
import DockIcon from './DockIcon.vue'
68
import ViewBuiltinTerminalPanel from './ViewBuiltinTerminalPanel.vue'
79
810
const props = defineProps<{
@@ -11,28 +13,40 @@ const props = defineProps<{
1113
1214
const terminals = useTerminals(props.context)
1315
const selectedTerminal = shallowRef<TerminalState | null>(null)
16+
17+
watchImmediate(
18+
terminals,
19+
() => {
20+
if (selectedTerminal.value == null && terminals.size > 0) {
21+
selectedTerminal.value = terminals.values().next().value!
22+
}
23+
},
24+
)
1425
</script>
1526

1627
<template>
17-
<div class="w-full h-full grid-cols-[max-content_1fr]">
18-
<div class="border-base border-b">
28+
<div class="w-full h-full grid grid-rows-[max-content_1fr]">
29+
<div class="border-base border-b rounded-t overflow-x-auto">
1930
<button
2031
v-for="terminal of terminals.values()"
2132
:key="terminal.info.id"
22-
class="px3 py2 border-r border-base hover:bg-active"
33+
class="px3 py1.5 border-r border-base hover:bg-active text-sm flex items-center gap-1"
34+
:class="{ 'bg-active': selectedTerminal?.info.id === terminal.info.id }"
2335
@click="selectedTerminal = terminal"
2436
>
25-
{{ terminal.info.title }}
37+
<DockIcon :icon="terminal.info.icon || 'ph:terminal-duotone'" />
38+
<span>{{ terminal.info.title }}</span>
2639
</button>
2740
</div>
28-
<div class="h-full flex relative">
41+
<div class="h-full w-full flex relative">
2942
<ViewBuiltinTerminalPanel
3043
v-if="selectedTerminal"
44+
:key="selectedTerminal.info.id"
3145
:context
3246
:terminal="selectedTerminal"
3347
/>
34-
<div v-else class="flex items-center justify-center h-full text-center">
35-
Select a terminal tab to start
48+
<div v-else class="flex items-center justify-center h-full w-full text-center">
49+
<div>Select a terminal tab to start</div>
3650
</div>
3751
</div>
3852
</div>

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

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DevToolsDockEntry, DevToolsRpcClientFunctions, DevToolsViewBuiltin } from '@vitejs/devtools-kit'
1+
import type { DevToolsDockEntry, DevToolsRpcClientFunctions } from '@vitejs/devtools-kit'
22
import type { ClientRpcReturn, DockEntryState, DockEntryStateEvents, DockPanelStorage } from '@vitejs/devtools-kit/client'
33
import type { Ref, ShallowRef } from 'vue'
44
import { createEventEmitter } from '@vitejs/devtools-kit/utils/events'
@@ -16,15 +16,6 @@ export function DEFAULT_DOCK_PANEL_STORE(): DockPanelStorage {
1616
}
1717
}
1818

19-
export const builtinDocksEntries: DevToolsViewBuiltin[] = [
20-
Object.freeze({
21-
type: '~builtin',
22-
id: '~terminals',
23-
title: 'Terminals',
24-
icon: 'ph:terminal-duotone',
25-
}),
26-
]
27-
2819
export function createDockEntryState(
2920
entry: DevToolsDockEntry,
3021
selected: Ref<DevToolsDockEntry | null>,
@@ -68,10 +59,8 @@ export async function useDocksEntries(rpcReturn: ClientRpcReturn): Promise<Ref<D
6859
}
6960
const dockEntries = _docksEntriesRef = shallowRef<DevToolsDockEntry[]>([])
7061
async function updateDocksEntries() {
71-
dockEntries.value = [
72-
...(await rpcReturn.rpc.$call('vite:internal:docks:list')).map(entry => Object.freeze(entry)),
73-
...builtinDocksEntries,
74-
]
62+
dockEntries.value = (await rpcReturn.rpc.$call('vite:internal:docks:list'))
63+
.map(entry => Object.freeze(entry))
7564
// eslint-disable-next-line no-console
7665
console.log('[VITE DEVTOOLS] Docks Entries Updated', [...dockEntries.value])
7766
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function useTerminals(context: DocksContext): Reactive<Map<string, Termin
1616
return _terminalsMap
1717
}
1818
const map: Reactive<Map<string, TerminalState>> = _terminalsMap = reactive(new Map())
19-
async function udpateTerminals() {
19+
async function updateTerminals() {
2020
const terminals = await context.rpc.$call('vite:internal:terminals:list')
2121

2222
for (const terminal of terminals) {
@@ -37,7 +37,7 @@ export function useTerminals(context: DocksContext): Reactive<Map<string, Termin
3737
context.clientRpc.register({
3838
name: 'vite:internal:terminals:updated' satisfies keyof DevToolsRpcClientFunctions,
3939
type: 'action',
40-
handler: () => udpateTerminals(),
40+
handler: () => updateTerminals(),
4141
})
4242
context.clientRpc.register({
4343
name: 'vite:internal:terminals:stream-chunk' satisfies keyof DevToolsRpcClientFunctions,
@@ -53,7 +53,7 @@ export function useTerminals(context: DocksContext): Reactive<Map<string, Termin
5353
terminal.terminal?.writeln(chunk)
5454
},
5555
})
56-
udpateTerminals()
56+
updateTerminals()
5757

5858
return map
5959
}

packages/core/src/node/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export async function createDevToolsContext(
4848
}, 10))
4949
terminalsHost.events.on('terminal:session:updated', debounce(() => {
5050
rpcHost.boardcast.$callOptional('vite:internal:terminals:updated')
51+
// New terminals might affect the visibility of the terminals dock entry, we trigger it here as well
52+
rpcHost.boardcast.$callOptional('vite:internal:docks:updated')
5153
}, 10))
5254
terminalsHost.events.on('terminal:session:stream-chunk', (data) => {
5355
rpcHost.boardcast.$callOptional('vite:internal:terminals:stream-chunk', data)
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1+
import type { DevToolsDockEntry, DevToolsViewBuiltin } from '@vitejs/devtools-kit'
12
import { defineRpcFunction } from '@vitejs/devtools-kit'
23

34
export const docksList = defineRpcFunction({
45
name: 'vite:internal:docks:list',
56
type: 'static',
67
setup: (context) => {
8+
const builtinDocksEntries: DevToolsViewBuiltin[] = [
9+
{
10+
type: '~builtin',
11+
id: '~terminals',
12+
title: 'Terminals',
13+
icon: 'ph:terminal-duotone',
14+
get isHidden() {
15+
return context.terminals.sessions.size === 0
16+
},
17+
},
18+
]
19+
720
return {
8-
handler: () => Array.from(context.docks.values()),
21+
handler: (): DevToolsDockEntry[] => [
22+
...Array.from(context.docks.values()),
23+
...builtinDocksEntries,
24+
],
925
}
1026
},
1127
})

packages/kit/src/types/docks.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ export interface DevToolsDockEntryBase {
3333
* @default 'default'
3434
*/
3535
category?: DevToolsDockEntryCategory
36+
/**
37+
* Whether the entry should be hidden from the user.
38+
* @default false
39+
*/
40+
isHidden?: boolean
3641
}
3742

3843
export interface ClientScriptEntry {

packages/kit/src/types/terminals.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ChildProcess } from 'node:child_process'
2+
import type { DevToolsDockEntryIcon } from './docks'
23
import type { EventEmitter } from './events'
34

45
export interface DevToolsTerminalSessionStreamChunkEvent {
@@ -30,6 +31,7 @@ export interface DevToolsTerminalSessionBase {
3031
title: string
3132
description?: string
3233
status: DevToolsTerminalStatus
34+
icon?: DevToolsDockEntryIcon
3335
}
3436

3537
export interface DevToolsTerminalSession extends DevToolsTerminalSessionBase {

0 commit comments

Comments
 (0)