Skip to content

Commit 9d357ef

Browse files
committed
feat: Add Local Echo
1 parent 2955e15 commit 9d357ef

3 files changed

Lines changed: 41 additions & 3 deletions

File tree

frontend/package-lock.json

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

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"vue-router": "^4.2.5",
2222
"xterm": "^5.3.0",
2323
"xterm-addon-fit": "^0.8.0",
24-
"xterm-addon-web-links": "^0.9.0"
24+
"xterm-addon-web-links": "^0.9.0",
25+
"xterm-zerolag-input": "^0.1.4"
2526
},
2627
"devDependencies": {
2728
"@vitejs/plugin-vue": "^4.4.0",

frontend/src/components/XtermTerminal.vue

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
5050
import { Terminal } from 'xterm'
5151
import { FitAddon } from 'xterm-addon-fit'
5252
import { WebLinksAddon } from 'xterm-addon-web-links'
53+
import { ZerolagInputAddon } from 'xterm-zerolag-input'
5354
import 'xterm/css/xterm.css'
5455
import { DocumentCopy, DocumentAdd, Select, Delete } from '@element-plus/icons-vue'
5556
import { ElMessage } from 'element-plus'
@@ -67,6 +68,7 @@ const terminalRef = ref(null)
6768
6869
let term = null
6970
let fitAddon = null
71+
let zerolag = null
7072
let isComposing = false
7173
let wheelHandler = null
7274
let viewportWheelHandler = null
@@ -142,6 +144,11 @@ const initTerminal = () => {
142144
const webLinksAddon = new WebLinksAddon()
143145
term.loadAddon(webLinksAddon)
144146
147+
zerolag = new ZerolagInputAddon({
148+
prompt: { type: 'regex', pattern: /[$#%❯>]\s*$/, offset: 2 }
149+
})
150+
term.loadAddon(zerolag)
151+
145152
if (terminalRef.value) {
146153
term.open(terminalRef.value)
147154
fitAddon.fit()
@@ -161,7 +168,20 @@ const initTerminal = () => {
161168
// Terminal input handler
162169
term.onData((data) => {
163170
if (isComposing) return
164-
emit('data', data)
171+
172+
if (data === '\r') {
173+
zerolag.clear()
174+
emit('data', data)
175+
} else if (data === '\x7f' || data === '\b') {
176+
zerolag.removeChar()
177+
emit('data', data)
178+
} else if (data.length === 1 && data.charCodeAt(0) >= 32) {
179+
zerolag.addChar(data)
180+
emit('data', data)
181+
} else {
182+
zerolag.clear()
183+
emit('data', data)
184+
}
165185
})
166186
167187
term.onSelectionChange(() => {
@@ -198,6 +218,7 @@ const initTerminal = () => {
198218
if (event.code === 'Space' || event.key === ' ') {
199219
event.preventDefault()
200220
if (props.isConnected) {
221+
if (zerolag) zerolag.addChar(' ')
201222
emit('data', ' ')
202223
term.scrollToBottom()
203224
}
@@ -212,6 +233,7 @@ const initTerminal = () => {
212233
event.preventDefault()
213234
navigator.clipboard.readText().then(text => {
214235
if (props.isConnected) {
236+
if (zerolag && text) zerolag.appendText(text)
215237
emit('data', text)
216238
}
217239
}).catch(err => console.warn('Paste failed:', err))
@@ -228,6 +250,13 @@ const initTerminal = () => {
228250
}
229251
return true
230252
})
253+
254+
// Rerender after write parsed
255+
term.onWriteParsed(() => {
256+
if (zerolag && zerolag.hasPending) {
257+
zerolag.rerender()
258+
}
259+
})
231260
}
232261
}
233262
@@ -330,6 +359,7 @@ const focus = () => {
330359
}
331360
332361
const clear = () => {
362+
if (zerolag) zerolag.clear()
333363
if (term) term.clear()
334364
}
335365

0 commit comments

Comments
 (0)