feat(serial-console): add terminal search toolbar and highlight tuning

Signed-off-by: Andy Lee <andy.lee@suse.com>
This commit is contained in:
Andy Lee 2026-08-03 19:35:49 +08:00
parent ad3f91f298
commit 57ee92051e
No known key found for this signature in database
GPG Key ID: 39DC4436AE3564D5

View File

@ -21,19 +21,35 @@ 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,
searchQuery: '',
searchOptions: {
caseSensitive: false,
regex: false,
wholeWord: false,
incremental: true,
decorations: {
matchBackground: '#B45309',
matchBorder: '#FDBA74',
matchOverviewRuler: '#FDBA74',
activeMatchBackground: '#EA580C',
activeMatchBorder: '#FED7AA',
activeMatchColorOverviewRuler: '#FED7AA',
},
},
}; };
}, },
@ -137,9 +153,88 @@ export default {
this.write(msg); this.write(msg);
}); });
terminal.attachCustomKeyEventHandler((event) => {
const key = event.key?.toLowerCase();
if ((event.ctrlKey || event.metaKey) && key === 'f') {
event.preventDefault();
this.openSearch();
return false;
}
if (key === 'escape' && this.showSearch) {
event.preventDefault();
this.closeSearch();
return false;
}
return true;
});
this.terminal = terminal; this.terminal = terminal;
}, },
openSearch() {
this.showSearch = true;
this.$nextTick(() => {
const input = this.$refs.searchInput;
if (input) {
input.focus();
input.select();
}
});
},
closeSearch() {
this.showSearch = false;
this.terminal?.focus();
},
findNext() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findNext(this.searchQuery, this.searchOptions);
},
findPrevious() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findPrevious(this.searchQuery, this.searchOptions);
},
onSearchInput() {
if (!this.searchQuery) {
return;
}
this.findNext();
},
onSearchKeydown(event) {
if (event.key === 'Enter') {
event.preventDefault();
if (event.shiftKey) {
this.findPrevious();
} else {
this.findNext();
}
}
if (event.key === 'Escape') {
event.preventDefault();
this.closeSearch();
}
},
scheduleFit() { scheduleFit() {
this.$nextTick(() => { this.$nextTick(() => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
@ -348,6 +443,44 @@ export default {
<template> <template>
<div class="harvester-shell-container"> <div class="harvester-shell-container">
<div
v-if="showSearch"
class="shell-search"
>
<input
ref="searchInput"
v-model="searchQuery"
type="text"
class="shell-search-input"
placeholder="Search in terminal"
@input="onSearchInput"
@keydown="onSearchKeydown"
>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Shift+Enter"
@click="findPrevious"
>
Prev
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Enter"
@click="findNext"
>
Next
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Esc"
@click="closeSearch"
>
Close
</button>
</div>
<div <div
ref="xterm" ref="xterm"
class="shell-body" class="shell-body"
@ -366,6 +499,7 @@ export default {
.harvester-shell-container { .harvester-shell-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: relative;
height: 100%; height: 100%;
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
@ -374,6 +508,36 @@ export default {
display: none; display: none;
} }
.shell-search {
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-end;
position: absolute;
top: 8px;
right: 10px;
z-index: 5;
padding: 8px;
border-radius: 6px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--body-bg) 88%, transparent);
}
.shell-search-input {
min-width: 220px;
max-width: 420px;
flex: 0 1 320px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--body-bg);
color: var(--body-text);
padding: 6px 8px;
}
.shell-search-btn {
min-width: 64px;
}
.shell-body { .shell-body {
flex: 1 1 auto; flex: 1 1 auto;
height: 100%; height: 100%;