mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-08-16 04:39:15 +00:00
refactor: fine tune serial console
Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
parent
9f9069fb63
commit
12a51b2451
@ -21,22 +21,24 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
socket: null,
|
socket: null,
|
||||||
terminal: null,
|
terminal: null,
|
||||||
textDecoder: null,
|
textDecoder: null,
|
||||||
fitAddon: null,
|
fitAddon: null,
|
||||||
searchAddon: null,
|
searchAddon: null,
|
||||||
webglAddon: null,
|
webglAddon: null,
|
||||||
onResize: null,
|
onResize: null,
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
isOpening: false,
|
isOpening: false,
|
||||||
backlog: [],
|
backlog: [],
|
||||||
firstTime: true,
|
firstTime: true,
|
||||||
queue: [],
|
queue: [],
|
||||||
isDraining: false,
|
isDraining: false,
|
||||||
showSearch: false,
|
drainScheduled: false,
|
||||||
searchQuery: '',
|
drainTimer: null,
|
||||||
searchOptions: {
|
showSearch: false,
|
||||||
|
searchQuery: '',
|
||||||
|
searchOptions: {
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
regex: false,
|
regex: false,
|
||||||
wholeWord: false,
|
wholeWord: false,
|
||||||
@ -54,12 +56,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
serialConsoleUrl() {
|
||||||
|
return this.value?.getSerialConsolePath || '';
|
||||||
|
},
|
||||||
|
|
||||||
xtermConfig() {
|
xtermConfig() {
|
||||||
return {
|
return {
|
||||||
allowProposedApi: true,
|
allowProposedApi: true,
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
cursorStyle: 'bar',
|
cursorStyle: 'bar',
|
||||||
cursorWidth: 2,
|
cursorWidth: 2,
|
||||||
|
scrollback: 5000,
|
||||||
useStyle: true,
|
useStyle: true,
|
||||||
fontFamily: 'JetBrains Mono, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
fontFamily: 'JetBrains Mono, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -72,6 +79,14 @@ export default {
|
|||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
serialConsoleUrl() {
|
||||||
|
if (this.terminal) {
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.textDecoder = new TextDecoder('utf-8');
|
this.textDecoder = new TextDecoder('utf-8');
|
||||||
await this.setupTerminal();
|
await this.setupTerminal();
|
||||||
@ -146,6 +161,7 @@ export default {
|
|||||||
|
|
||||||
this.fit();
|
this.fit();
|
||||||
this.flush();
|
this.flush();
|
||||||
|
this.scheduleDrainQueue();
|
||||||
|
|
||||||
terminal.onData((input) => {
|
terminal.onData((input) => {
|
||||||
const msg = this.str2ab(input);
|
const msg = this.str2ab(input);
|
||||||
@ -191,10 +207,17 @@ export default {
|
|||||||
|
|
||||||
closeSearch() {
|
closeSearch() {
|
||||||
this.showSearch = false;
|
this.showSearch = false;
|
||||||
this.searchAddon?.clearDecorations();
|
this.searchQuery = '';
|
||||||
|
this.clearSearchHighlights();
|
||||||
this.terminal?.focus();
|
this.terminal?.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearSearchHighlights() {
|
||||||
|
this.terminal?.clearSelection();
|
||||||
|
this.searchAddon?.clearActiveDecoration();
|
||||||
|
this.searchAddon?.clearDecorations();
|
||||||
|
},
|
||||||
|
|
||||||
findNext() {
|
findNext() {
|
||||||
if (!this.searchAddon || !this.searchQuery) {
|
if (!this.searchAddon || !this.searchQuery) {
|
||||||
return;
|
return;
|
||||||
@ -213,7 +236,7 @@ export default {
|
|||||||
|
|
||||||
onSearchInput() {
|
onSearchInput() {
|
||||||
if (!this.searchQuery) {
|
if (!this.searchQuery) {
|
||||||
this.searchAddon?.clearDecorations();
|
this.clearSearchHighlights();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -282,7 +305,31 @@ export default {
|
|||||||
|
|
||||||
enqueueMessage(messagePromise) {
|
enqueueMessage(messagePromise) {
|
||||||
this.queue.push(messagePromise);
|
this.queue.push(messagePromise);
|
||||||
this.drainQueue();
|
this.scheduleDrainQueue();
|
||||||
|
},
|
||||||
|
|
||||||
|
scheduleDrainQueue() {
|
||||||
|
if (this.drainScheduled || this.isDraining || !this.terminal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drainScheduled = true;
|
||||||
|
|
||||||
|
const shouldDrainImmediately = this.queue.length <= 3;
|
||||||
|
|
||||||
|
const runDrain = () => {
|
||||||
|
this.drainScheduled = false;
|
||||||
|
this.drainTimer = null;
|
||||||
|
this.drainQueue();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (shouldDrainImmediately) {
|
||||||
|
runDrain();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drainTimer = requestAnimationFrame(runDrain);
|
||||||
},
|
},
|
||||||
|
|
||||||
async decodeMessageData(data) {
|
async decodeMessageData(data) {
|
||||||
@ -331,7 +378,11 @@ export default {
|
|||||||
const output = messages.filter(Boolean).join('');
|
const output = messages.filter(Boolean).join('');
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
this.terminal.write(output);
|
await this.writeOutputInChunks(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.queue.length > 0) {
|
||||||
|
await this.nextFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -340,17 +391,43 @@ export default {
|
|||||||
// If data arrived between the last while-check and releasing the lock,
|
// If data arrived between the last while-check and releasing the lock,
|
||||||
// immediately schedule another drain so output never gets stuck.
|
// immediately schedule another drain so output never gets stuck.
|
||||||
if (this.queue.length > 0 && this.terminal) {
|
if (this.queue.length > 0 && this.terminal) {
|
||||||
this.drainQueue();
|
this.scheduleDrainQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async writeOutputInChunks(output) {
|
||||||
|
if (!output) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunkSize = output.length > 20000 ? 16000 : output.length > 8000 ? 8000 : output.length;
|
||||||
|
|
||||||
|
if (chunkSize === output.length) {
|
||||||
|
this.terminal.write(output);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < output.length; index += chunkSize) {
|
||||||
|
this.terminal.write(output.slice(index, index + chunkSize));
|
||||||
|
|
||||||
|
if (index + chunkSize < output.length) {
|
||||||
|
await this.nextFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
nextFrame() {
|
||||||
|
return new Promise((resolve) => requestAnimationFrame(resolve));
|
||||||
|
},
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.terminal.clear();
|
this.terminal.clear();
|
||||||
},
|
},
|
||||||
|
|
||||||
getSocketUrl() {
|
getSocketUrl() {
|
||||||
return `${ this.value?.getSerialConsolePath }`;
|
return this.serialConsoleUrl;
|
||||||
},
|
},
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
@ -432,6 +509,11 @@ export default {
|
|||||||
this.onResize = null;
|
this.onResize = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.drainTimer) {
|
||||||
|
cancelAnimationFrame(this.drainTimer);
|
||||||
|
this.drainTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
if ( this.socket ) {
|
if ( this.socket ) {
|
||||||
this.socket.disconnect();
|
this.socket.disconnect();
|
||||||
}
|
}
|
||||||
@ -544,13 +626,22 @@ export default {
|
|||||||
.shell-body {
|
.shell-body {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal.xterm {
|
.terminal.xterm,
|
||||||
|
.terminal.xterm .xterm-viewport,
|
||||||
|
.terminal.xterm .xterm-screen,
|
||||||
|
.terminal.xterm .xterm-scrollable-element {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal.xterm .xterm-scrollable-element {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import Loading from '@shell/components/Loading';
|
|
||||||
import { HCI } from '../../../../../types';
|
import { HCI } from '../../../../../types';
|
||||||
import SerialConsole from '../../../../../components/SerialConsole';
|
import SerialConsole from '../../../../../components/SerialConsole';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SerialConsole, Loading },
|
components: { SerialConsole },
|
||||||
|
|
||||||
async fetch() {
|
async fetch() {
|
||||||
this.rows = await this.$store.dispatch('harvester/findAll', { type: HCI.VMI });
|
this.rows = await this.$store.dispatch('harvester/findAll', { type: HCI.VMI });
|
||||||
@ -39,11 +38,9 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="serial-console-page">
|
<div class="serial-console-page">
|
||||||
<Loading v-if="$fetchState.pending" />
|
|
||||||
<SerialConsole
|
<SerialConsole
|
||||||
v-else
|
|
||||||
ref="serialConsole"
|
ref="serialConsole"
|
||||||
v-model:value="vmi"
|
:value="vmi || {}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -61,9 +58,23 @@ export default {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
background: var(--body-bg);
|
||||||
|
color: var(--terminal-text);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
|
||||||
|
}
|
||||||
|
|
||||||
.harvester-shell-container {
|
.harvester-shell-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: var(--terminal-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user