[Electron] Terminal commands (#1531)

* Add live terminal output

* Fix scrolling

* Refactor loading

* Fallback to polling if endpoint fails

* Comment

* Move clientId to executionStore
Refactor types

* Remove polling

* wip terminal command input

* Refactor to use node-pty

* Hide tabs if not electron

* Lint fix

* ts fix

* Refactor tab components
This commit is contained in:
pythongosssss
2024-11-17 19:43:08 +00:00
committed by GitHub
parent 545a990365
commit b5f0c4bf73
9 changed files with 297 additions and 120 deletions

View File

@@ -1,14 +0,0 @@
import { useI18n } from 'vue-i18n'
import { markRaw } from 'vue'
import IntegratedTerminal from '@/components/bottomPanel/tabs/IntegratedTerminal.vue'
import { BottomPanelExtension } from '@/types/extensionTypes'
export const useIntegratedTerminalTab = (): BottomPanelExtension => {
const { t } = useI18n()
return {
id: 'integrated-terminal',
title: t('terminal'),
component: markRaw(IntegratedTerminal),
type: 'vue'
}
}

View File

@@ -0,0 +1,25 @@
import { useI18n } from 'vue-i18n'
import { markRaw } from 'vue'
import { BottomPanelExtension } from '@/types/extensionTypes'
import LogsTerminal from '@/components/bottomPanel/tabs/terminal/LogsTerminal.vue'
import CommandTerminal from '@/components/bottomPanel/tabs/terminal/CommandTerminal.vue'
export const useLogsTerminalTab = (): BottomPanelExtension => {
const { t } = useI18n()
return {
id: 'logs-terminal',
title: t('logs'),
component: markRaw(LogsTerminal),
type: 'vue'
}
}
export const useCommandTerminalTab = (): BottomPanelExtension => {
const { t } = useI18n()
return {
id: 'command-terminal',
title: t('terminal'),
component: markRaw(CommandTerminal),
type: 'vue'
}
}

View File

@@ -0,0 +1,69 @@
import { FitAddon } from '@xterm/addon-fit'
import { Terminal } from '@xterm/xterm'
import { debounce } from 'lodash'
import { onMounted, onUnmounted, Ref } from 'vue'
import '@xterm/xterm/css/xterm.css'
export function useTerminal(element: Ref<HTMLElement>) {
const fitAddon = new FitAddon()
const terminal = new Terminal({
convertEol: true
})
terminal.loadAddon(fitAddon)
onMounted(async () => {
terminal.open(element.value)
})
onUnmounted(() => {
terminal.dispose()
})
return {
terminal,
useAutoSize(
root: Ref<HTMLElement>,
autoRows: boolean = true,
autoCols: boolean = true,
onResize?: () => void
) {
const ensureValidRows = (rows: number | undefined) => {
if (rows == null || isNaN(rows)) {
return root.value?.clientHeight / 20
}
return rows
}
const ensureValidCols = (cols: number | undefined): number => {
if (cols == null || isNaN(cols)) {
// Sometimes this is NaN if so, estimate.
return root.value?.clientWidth / 8
}
return cols
}
const resize = () => {
const dims = fitAddon.proposeDimensions()
// Sometimes propose returns NaN, so we may need to estimate.
terminal.resize(
autoCols ? ensureValidCols(dims?.cols) : terminal.cols,
autoRows ? ensureValidRows(dims?.rows) : terminal.rows
)
onResize?.()
}
const resizeObserver = new ResizeObserver(debounce(resize, 25))
onMounted(async () => {
resizeObserver.observe(root.value)
resize()
})
onUnmounted(() => {
resizeObserver.disconnect()
})
return { resize }
}
}
}