Skip to content

Commit a283bc6

Browse files
committed
run
1 parent 886b390 commit a283bc6

File tree

10 files changed

+257
-59
lines changed

10 files changed

+257
-59
lines changed

package-lock.json

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"vue3-json-viewer": "^2.2.2",
3131
"webfontloader": "^1.6.28",
3232
"xterm": "^5.0.0",
33-
"xterm-addon-fit": "^0.7.0"
33+
"xterm-addon-fit": "^0.7.0",
34+
"xterm-addon-webgl": "^0.16.0"
3435
},
3536
"devDependencies": {
3637
"@types/dompurify": "^3.0.2",

src/App.vue

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ const theme = computed(() => (store.dark ? darkTheme : lightTheme))
9696
9797
/** websocket to eventbus handlers */
9898
const {
99-
status,
100-
data,
101-
send,
102-
open: openWs,
99+
status: wsStatus,
100+
data: wsData,
101+
send: wsSend,
102+
open: wsOpen,
103103
} = useWebSocket<string>(ws_uri, {
104104
heartbeat: {
105105
message: "%",
@@ -112,11 +112,11 @@ const {
112112
113113
wsTxBus.on((tx) => {
114114
console.debug("WS Tx", tx)
115-
send(JSON.stringify(tx))
115+
wsSend(JSON.stringify(tx))
116116
})
117117
118118
// on any data change, transmit the message on the template bus or send to store
119-
watch(data, (msg) => {
119+
watch(wsData, (msg) => {
120120
if (!msg) return
121121
const m: WsMessage = JSON.parse(msg)
122122
if (m.code === WsMsgCodes.template) {
@@ -125,17 +125,17 @@ watch(data, (msg) => {
125125
store.websock_handler(m)
126126
}
127127
// Clear the last rx websocket message to allow duplicate messages (i.e file saves)
128-
if (msg === data.value) data.value = null
128+
if (msg === wsData.value) wsData.value = null
129129
})
130130
131-
watch(status, (s) => {
131+
watch(wsStatus, (s) => {
132132
if (s === "CLOSED") {
133133
setTimeout(open_if_closed, 2000)
134134
}
135135
})
136136
137137
const wsstatus = computed(() => {
138-
switch (status.value) {
138+
switch (wsStatus.value) {
139139
case "OPEN":
140140
return { color: "green", icon: ContactlessTwotone }
141141
case "CONNECTING":
@@ -147,12 +147,12 @@ const wsstatus = computed(() => {
147147
148148
onBeforeMount(() => {
149149
store.init()
150-
openWs()
150+
wsOpen()
151151
})
152152
153153
function open_if_closed() {
154-
if (status.value !== "OPEN") {
155-
open()
154+
if (wsStatus.value !== "OPEN") {
155+
wsOpen()
156156
}
157157
}
158158
</script>

src/components/div_markdown.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const md = computed(() => {
3333
try {
3434
return DOMPurify.sanitize(result, {
3535
ALLOWED_URI_REGEXP:
36-
/^(?:(?:(?:f|ht)tps?|mailto|es|path|run|config|gnmic|containerlab|clab):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
36+
/^(?:(?:(?:f|ht)tps?|mailto|es|path|run|run\+|config|ssh|gnmic|containerlab|clab):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i,
3737
})
3838
} catch (error) {
3939
/* eslint-disable-next-line no-console */
@@ -63,9 +63,6 @@ function uriHandler(e: Event) {
6363
command: url.replaceAll("/", " ").trim() || target.textContent || "",
6464
} as ActionEvent
6565
switch (proto) {
66-
case "run":
67-
action.action = "config"
68-
break
6966
case "containerlab":
7067
action.action = "clab"
7168
break

src/components/div_xterm.vue

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,37 @@
88
import { onBeforeUnmount, onMounted, ref, watch } from "vue"
99
import { IDisposable, Terminal } from "xterm"
1010
import { FitAddon } from "xterm-addon-fit"
11+
import { WebglAddon } from "xterm-addon-webgl"
1112
import { Base64 } from "js-base64"
1213
import { useResizeObserver, useWebSocket } from "@vueuse/core"
1314
import { ws_uri } from "@/utils/const"
1415
import { webttyRx, webttyTx } from "@/utils/webtty"
1516
16-
import "xterm/css/xterm.css"
1717
import { ActionEvent, actionBus } from "@/utils/action"
18+
import "xterm/css/xterm.css"
1819
19-
interface PropDef {
20-
cmd: string
21-
connected: boolean
22-
}
23-
const props = withDefaults(defineProps<PropDef>(), { connected: true })
24-
const emit = defineEmits(["update:connected"])
25-
defineExpose({ focus })
20+
const props = withDefaults(
21+
defineProps<{
22+
/** The command to execute */
23+
cmd: string
24+
connected: boolean
25+
}>(),
26+
{ connected: true }
27+
)
28+
const emit = defineEmits<{
29+
(e: "update:connected", connected: boolean): void
30+
}>()
31+
defineExpose({ focus, fit })
2632
2733
const terminal = ref()
2834
35+
function fit() {
36+
fitAddon.fit()
37+
38+
term.refresh(0, term.rows - 1)
39+
console.log("fit")
40+
}
41+
2942
const term = new Terminal({
3043
rows: 24,
3144
cols: 80,
@@ -139,6 +152,7 @@ useResizeObserver(terminal, (el) => {
139152
onMounted(() => {
140153
open()
141154
term.loadAddon(fitAddon)
155+
term.loadAddon(new WebglAddon())
142156
term.open(terminal.value)
143157
term.writeln(`$ \x1B[1;3;31m${props.cmd}\x1B[0m`)
144158
focus()
@@ -152,12 +166,12 @@ onBeforeUnmount(() => {
152166
})
153167
154168
function focus() {
155-
terminal.value.scrollIntoView()
169+
terminal.value?.scrollIntoView()
156170
term.focus()
157171
}
158172
159173
actionBus.on((action: ActionEvent) => {
160-
if (action.action == "ssh" && props.cmd.endsWith(action.command)) {
174+
if (action.action == "focus" && props.cmd.endsWith(action.command)) {
161175
// console.log("focus", action.command, props.cmd)
162176
focus()
163177
}

src/components/panel_xterm.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ import LPanel from "@/components/l_panel.vue"
3535
import { PlugDisconnected20Filled } from "@vicons/fluent"
3636
import { CancelOutlined, ConnectWithoutContactRound } from "@vicons/material"
3737
38-
export interface PropDef {
38+
const props = defineProps<{
3939
target: string
4040
visible: number
41-
}
42-
const props = defineProps<PropDef>()
43-
const emit = defineEmits(["close", "update:visible"])
41+
}>()
42+
const emit = defineEmits<{
43+
(e: "close"): void
44+
(e: "update:visible", v: number): void
45+
}>()
4446
4547
const vis = computed({
4648
get: () => props.visible,
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
console.log("visible")
2+
<template>
3+
<l-panel
4+
v-model:visible="vis"
5+
:title="'Run script ' + props.cmd"
6+
:min-v="minV"
7+
:max-v="maxV"
8+
>
9+
<template #header-extra>
10+
<n-button v-if="connected" quaternary @click="connected = !connected">
11+
<n-icon
12+
:component="connected ? CancelOutlined : PlugDisconnected20Filled"
13+
/>
14+
{{ connected ? "Disconnect" : "Connect" }}
15+
</n-button>
16+
</template>
17+
<div ref="xtermwrap">
18+
<div-xterm
19+
v-if="!delayShow || showterm"
20+
v-model:connected="connected"
21+
:cmd="props.cmd"
22+
/>
23+
</div>
24+
</l-panel>
25+
</template>
26+
27+
<script setup lang="ts">
28+
import { computed, ref, watch } from "vue"
29+
import { NButton, NIcon } from "naive-ui"
30+
import DivXterm from "@/components/div_xterm.vue"
31+
import LPanel from "@/components/l_panel.vue"
32+
import { PlugDisconnected20Filled } from "@vicons/fluent"
33+
import { CancelOutlined } from "@vicons/material"
34+
import { useElementVisibility } from "@vueuse/core"
35+
36+
const props = withDefaults(
37+
defineProps<{
38+
cmd: string
39+
visible: number
40+
minV?: number
41+
maxV?: number
42+
/** Delay the show for a 'modal' window */
43+
delayShow?: boolean
44+
}>(),
45+
{
46+
minV: 2,
47+
maxV: 4,
48+
delayShow: false,
49+
}
50+
)
51+
const emit = defineEmits<{
52+
(e: "close"): void
53+
(e: "update:visible", v: number): void
54+
}>()
55+
56+
const xtermwrap = ref<HTMLElement>()
57+
const isvisible = useElementVisibility(xtermwrap)
58+
watch(
59+
() => isvisible.value,
60+
(v) => {
61+
console.log(props.delayShow)
62+
if (props.delayShow && v) {
63+
console.log("visible")
64+
setTimeout(() => {
65+
showterm.value = true
66+
}, 200)
67+
}
68+
}
69+
)
70+
71+
const vis = computed({
72+
get: () => props.visible,
73+
set: (v) => {
74+
if (v < 0) {
75+
emit("close")
76+
}
77+
emit("update:visible", v)
78+
},
79+
})
80+
81+
const showterm = ref(false)
82+
const connected = ref(false)
83+
84+
// watch(
85+
// () => props.visible,
86+
// (v: number) => {
87+
// console.log("visible", v, props.visible)
88+
// if (v > 0) {
89+
// return
90+
// }
91+
// }
92+
// )
93+
</script>
94+
95+
<style></style>

src/utils/action.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
import { useEventBus } from "@vueuse/core"
22

33
export interface ActionEvent {
4-
action: "config" | "clab" | "path" | "center" | "ssh"
4+
/** action can be one of:
5+
* - config: Send commands using the config engine
6+
* - run: execute a script on the server
7+
* - run+: execute a long-running script on the server
8+
* - clab: execute clab commands (not yet implemented)
9+
* - path: highlight a path
10+
* - ssh: ssh to a node
11+
* - center: center the graph on a node
12+
*/
13+
action:
14+
| "config"
15+
| "clab"
16+
| "path"
17+
| "center"
18+
| "ssh"
19+
| "run"
20+
| "run+"
21+
| "focus"
522
command: string
623
}
724

src/utils/panel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** Toggle visibility (negative) */
2+
export function toggleVis(v: number) {
3+
return isNaN(v) || v === 0 ? 2 : -v
4+
}

0 commit comments

Comments
 (0)