Skip to content

Commit 7b18894

Browse files
committed
xterm
1 parent a283bc6 commit 7b18894

File tree

5 files changed

+114
-96
lines changed

5 files changed

+114
-96
lines changed

src/components/div_xterm.vue

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { Base64 } from "js-base64"
1313
import { useResizeObserver, useWebSocket } from "@vueuse/core"
1414
import { ws_uri } from "@/utils/const"
1515
import { webttyRx, webttyTx } from "@/utils/webtty"
16-
1716
import { ActionEvent, actionBus } from "@/utils/action"
1817
import "xterm/css/xterm.css"
1918
@@ -22,23 +21,18 @@ const props = withDefaults(
2221
/** The command to execute */
2322
cmd: string
2423
connected: boolean
24+
rows?: number
25+
backgroundColor?: string
2526
}>(),
26-
{ connected: true }
27+
{ connected: true, rows: 24, backgroundColor: "#000" }
2728
)
2829
const emit = defineEmits<{
2930
(e: "update:connected", connected: boolean): void
3031
}>()
31-
defineExpose({ focus, fit })
32+
defineExpose({ focus })
3233
3334
const terminal = ref()
3435
35-
function fit() {
36-
fitAddon.fit()
37-
38-
term.refresh(0, term.rows - 1)
39-
console.log("fit")
40-
}
41-
4236
const term = new Terminal({
4337
rows: 24,
4438
cols: 80,
@@ -51,25 +45,54 @@ const term = new Terminal({
5145
})
5246
const fitAddon = new FitAddon()
5347
54-
const { status, data, close, send, open } = useWebSocket(ws_uri + "pty", {
48+
const {
49+
status: wsStatus,
50+
data: wsData,
51+
close: wsClose,
52+
send: wsSend,
53+
open: wsOpen,
54+
} = useWebSocket(ws_uri + "pty", {
5555
immediate: false,
5656
})
5757
58+
onMounted(() => {
59+
wsOpen()
60+
term.resize(80, props.rows)
61+
if (props.backgroundColor !== "") {
62+
if (!term.options.theme) {
63+
term.options.theme = {}
64+
}
65+
term.options.theme.background = props.backgroundColor
66+
}
67+
term.loadAddon(fitAddon)
68+
term.loadAddon(new WebglAddon())
69+
term.open(terminal.value)
70+
term.writeln(`$ \x1B[1;3;31m${props.cmd}\x1B[0m`)
71+
focus()
72+
})
73+
74+
onBeforeUnmount(() => {
75+
wsClose()
76+
if (term) {
77+
term.dispose()
78+
}
79+
})
80+
5881
watch(
5982
() => props.connected,
6083
(c: boolean) => {
61-
if (c == false && status.value !== "CLOSED") {
62-
close() // the websocket
84+
if (c == false && wsStatus.value !== "CLOSED") {
85+
wsClose() // the websocket
6386
return
6487
}
65-
if (c && status.value === "CLOSED") {
66-
open()
88+
if (c && wsStatus.value === "CLOSED") {
89+
wsOpen()
6790
term.writeln(`\n$ \x1B[1;3;31m${props.cmd}\x1B[0m`)
6891
}
6992
}
7093
)
7194
72-
watch(status, (value) => {
95+
watch(wsStatus, (value) => {
7396
if (value == "CLOSED") {
7497
termDispose.map((v) => v.dispose())
7598
termDispose.splice(0, termDispose.length)
@@ -82,7 +105,7 @@ watch(status, (value) => {
82105
}
83106
})
84107
85-
watch(data, (val: string) => {
108+
watch(wsData, (val: string) => {
86109
const code = val.slice(0, 1)
87110
const data = Base64.decode(val.slice(1))
88111
switch (code) {
@@ -97,11 +120,11 @@ const termDispose: IDisposable[] = []
97120
98121
function termAttach(cmd: string) {
99122
// Send connection parameters
100-
send(JSON.stringify({ cmd: cmd }))
123+
wsSend(JSON.stringify({ cmd: cmd }))
101124
102125
termDispose.push(
103126
term.onResize((size) => {
104-
send(
127+
wsSend(
105128
webttyTx.ResizeTerminal +
106129
JSON.stringify({ Columns: size.cols, Rows: size.rows })
107130
)
@@ -120,22 +143,22 @@ function termAttach(cmd: string) {
120143
} else {
121144
bufData = data
122145
setTimeout(() => {
123-
send(webttyTx.Input + bufData)
146+
wsSend(webttyTx.Input + bufData)
124147
if (bufData.length > 1) {
125148
console.log(`send ${bufData}`)
126149
}
127150
bufData = ""
128151
}, bufTime)
129152
}
130153
} else {
131-
send(webttyTx.Input + data)
154+
wsSend(webttyTx.Input + data)
132155
}
133156
})
134157
)
135158
136159
// send heartbeat to avoid server closing webSocket (i.e. nginx)
137160
const heartBeatTimer = setInterval(function () {
138-
send(webttyTx.Ping)
161+
wsSend(webttyTx.Ping)
139162
}, 20 * 1000)
140163
termDispose.push({
141164
dispose: () => {
@@ -149,22 +172,6 @@ useResizeObserver(terminal, (el) => {
149172
fitAddon.fit()
150173
})
151174
152-
onMounted(() => {
153-
open()
154-
term.loadAddon(fitAddon)
155-
term.loadAddon(new WebglAddon())
156-
term.open(terminal.value)
157-
term.writeln(`$ \x1B[1;3;31m${props.cmd}\x1B[0m`)
158-
focus()
159-
})
160-
161-
onBeforeUnmount(() => {
162-
close()
163-
if (term) {
164-
term.dispose()
165-
}
166-
})
167-
168175
function focus() {
169176
terminal.value?.scrollIntoView()
170177
term.focus()

src/components/l_panel.vue

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,42 @@
11
<template>
2-
<n-card
3-
:title="props.title"
4-
closable
5-
style="min-height: 500px"
6-
@close="close"
7-
>
2+
<n-card :title="props.title" style="min-height: 500px">
83
<template #header-extra>
9-
<slot name="header-extra"></slot>
10-
<n-button
11-
v-if="vis > minV"
12-
text
13-
class="n-base-close"
14-
:focusable="false"
15-
style="
16-
margin-right: 5px;
17-
margin-left: 12px;
18-
--n-text-color-hover: black;
19-
"
20-
@click="vis -= 1"
21-
>
22-
<n-icon>
23-
<ArrowMinimize20Regular />
24-
</n-icon>
25-
</n-button>
26-
<n-button
27-
v-if="vis < maxV"
28-
text
29-
class="n-base-close"
30-
:focusable="false"
31-
style="
32-
margin-right: 5px;
33-
margin-left: 12px;
34-
--n-text-color-hover: black;
35-
"
36-
@click="vis += 1"
37-
>
38-
<n-icon>
39-
<ArrowMaximize20Regular />
40-
</n-icon>
41-
</n-button>
4+
<n-space :size="0">
5+
<slot name="header-extra"></slot>
6+
<n-button
7+
v-if="vis > minV"
8+
quaternary
9+
size="small"
10+
:focusable="false"
11+
@click="vis -= 1"
12+
>
13+
<template #icon>
14+
<n-icon :component="ArrowMinimize20Regular" />
15+
</template>
16+
</n-button>
17+
<n-button
18+
v-if="vis < maxV"
19+
quaternary
20+
:focusable="false"
21+
size="small"
22+
@click="vis += 1"
23+
>
24+
<template #icon>
25+
<n-icon :component="ArrowMaximize20Regular" />
26+
</template>
27+
</n-button>
28+
<n-button
29+
v-if="vis < maxV"
30+
quaternary
31+
:focusable="false"
32+
size="small"
33+
@click="close"
34+
>
35+
<template #icon>
36+
<n-icon :component="CloseFilled" />
37+
</template>
38+
</n-button>
39+
</n-space>
4240
</template>
4341

4442
<slot></slot>
@@ -47,18 +45,21 @@
4745

4846
<script setup lang="ts">
4947
import { computed } from "vue"
50-
import { NButton, NCard, NIcon } from "naive-ui"
48+
import { NButton, NCard, NIcon, NSpace } from "naive-ui"
5149
import { ArrowMaximize20Regular, ArrowMinimize20Regular } from "@vicons/fluent"
50+
import { CloseFilled } from "@vicons/material"
5251
53-
export interface PropDef {
54-
visible: number
55-
title: string
56-
minV?: number
57-
maxV?: number
58-
}
59-
const props = withDefaults(defineProps<PropDef>(), { minV: 2, maxV: 4 })
52+
const props = withDefaults(
53+
defineProps<{
54+
visible: number
55+
title: string
56+
minV?: number
57+
maxV?: number
58+
}>(),
59+
{ minV: 2, maxV: 4 }
60+
)
6061
61-
const emit = defineEmits(["update:visible"])
62+
const emit = defineEmits<{ "update:visible": [v: number] }>()
6263
6364
function bound(v: number): number {
6465
if (v > 0) {

src/components/panel_xterm.vue

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
<template>
22
<l-panel v-model:visible="vis" :title="'SSH ' + props.target">
33
<template #header-extra>
4-
<n-button quaternary @click="connected = !connected">
4+
<n-button quaternary size="small" @click="connected = !connected">
55
<n-icon
66
:component="connected ? CancelOutlined : PlugDisconnected20Filled"
77
/>
88
{{ connected ? "Disconnect" : "Connect" }}
99
</n-button>
10-
<l-button ref="xtermref" quaternary @click="pingToggle">
11-
<n-icon :component="ConnectWithoutContactRound" />
12-
<template #tooltip>Ping the node. Click to start/stop/hide</template>
13-
</l-button>
10+
<n-tooltip trigger="hover" placement="bottom">
11+
<template #trigger>
12+
<n-button quaternary size="small" @click="pingToggle">
13+
<n-icon :component="ConnectWithoutContactRound" />
14+
{{ !ping ? "Ping" : pconnected ? "Stop ping" : "Close ping" }}
15+
</n-button>
16+
</template>
17+
Ping the node. Click to start/stop/hide
18+
</n-tooltip>
1419
</template>
1520
<div-xterm
1621
v-if="ping"
1722
v-model:connected="pconnected"
1823
:cmd="'ping ' + props.target"
24+
:rows="6"
25+
background-color="#00008B"
1926
/>
2027
<div ref="xtermref">
2128
<div-xterm
@@ -28,9 +35,8 @@
2835

2936
<script setup lang="ts">
3037
import { computed, ref, watch } from "vue"
31-
import { NButton, NIcon } from "naive-ui"
38+
import { NButton, NIcon, NTooltip } from "naive-ui"
3239
import DivXterm from "@/components/div_xterm.vue"
33-
import LButton from "@/components/l_button.vue"
3440
import LPanel from "@/components/l_panel.vue"
3541
import { PlugDisconnected20Filled } from "@vicons/fluent"
3642
import { CancelOutlined, ConnectWithoutContactRound } from "@vicons/material"

src/components/panel_xterm_exec.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
console.log("visible")
21
<template>
32
<l-panel
43
v-model:visible="vis"
@@ -7,7 +6,12 @@ console.log("visible")
76
:max-v="maxV"
87
>
98
<template #header-extra>
10-
<n-button v-if="connected" quaternary @click="connected = !connected">
9+
<n-button
10+
v-if="connected"
11+
quaternary
12+
size="small"
13+
@click="connected = !connected"
14+
>
1115
<n-icon
1216
:component="connected ? CancelOutlined : PlugDisconnected20Filled"
1317
/>

src/views/graphView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
v-model:selected-links="selectedLinks"
141141
:node-labels="lblNode"
142142
:link-labels="lblLink"
143-
:opt-layout-handler="optLayoutHandler"
143+
:opt-layout-handler="optLayoutHandler ?? ''"
144144
canedit
145145
:topo-links="store.topo.links"
146146
:topo-nodes="store.topo.nodes"

0 commit comments

Comments
 (0)