From b5f0c4bf7394ee2548d7bc168d7c99ff3ac53ee7 Mon Sep 17 00:00:00 2001
From: pythongosssss <125205205+pythongosssss@users.noreply.github.com>
Date: Sun, 17 Nov 2024 19:43:08 +0000
Subject: [PATCH] [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
---
.../bottomPanel/tabs/IntegratedTerminal.vue | 104 ------------------
.../tabs/terminal/BaseTerminal.vue | 30 +++++
.../tabs/terminal/CommandTerminal.vue | 73 ++++++++++++
.../tabs/terminal/LogsTerminal.vue | 90 +++++++++++++++
.../bottomPanelTabs/integratedTerminalTab.ts | 14 ---
src/hooks/bottomPanelTabs/terminalTabs.ts | 25 +++++
src/hooks/bottomPanelTabs/useTerminal.ts | 69 ++++++++++++
src/i18n.ts | 1 +
src/stores/workspace/bottomPanelStore.ts | 11 +-
9 files changed, 297 insertions(+), 120 deletions(-)
delete mode 100644 src/components/bottomPanel/tabs/IntegratedTerminal.vue
create mode 100644 src/components/bottomPanel/tabs/terminal/BaseTerminal.vue
create mode 100644 src/components/bottomPanel/tabs/terminal/CommandTerminal.vue
create mode 100644 src/components/bottomPanel/tabs/terminal/LogsTerminal.vue
delete mode 100644 src/hooks/bottomPanelTabs/integratedTerminalTab.ts
create mode 100644 src/hooks/bottomPanelTabs/terminalTabs.ts
create mode 100644 src/hooks/bottomPanelTabs/useTerminal.ts
diff --git a/src/components/bottomPanel/tabs/IntegratedTerminal.vue b/src/components/bottomPanel/tabs/IntegratedTerminal.vue
deleted file mode 100644
index 84a6567f0..000000000
--- a/src/components/bottomPanel/tabs/IntegratedTerminal.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
{{ errorMessage }}
-
-
-
-
-
-
-
-
diff --git a/src/components/bottomPanel/tabs/terminal/BaseTerminal.vue b/src/components/bottomPanel/tabs/terminal/BaseTerminal.vue
new file mode 100644
index 000000000..1e5f97d06
--- /dev/null
+++ b/src/components/bottomPanel/tabs/terminal/BaseTerminal.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/components/bottomPanel/tabs/terminal/CommandTerminal.vue b/src/components/bottomPanel/tabs/terminal/CommandTerminal.vue
new file mode 100644
index 000000000..764a78e99
--- /dev/null
+++ b/src/components/bottomPanel/tabs/terminal/CommandTerminal.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
diff --git a/src/components/bottomPanel/tabs/terminal/LogsTerminal.vue b/src/components/bottomPanel/tabs/terminal/LogsTerminal.vue
new file mode 100644
index 000000000..460d56b78
--- /dev/null
+++ b/src/components/bottomPanel/tabs/terminal/LogsTerminal.vue
@@ -0,0 +1,90 @@
+
+
+
{{ errorMessage }}
+
+
+
+
+
+
+
+
diff --git a/src/hooks/bottomPanelTabs/integratedTerminalTab.ts b/src/hooks/bottomPanelTabs/integratedTerminalTab.ts
deleted file mode 100644
index 7c92da551..000000000
--- a/src/hooks/bottomPanelTabs/integratedTerminalTab.ts
+++ /dev/null
@@ -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'
- }
-}
diff --git a/src/hooks/bottomPanelTabs/terminalTabs.ts b/src/hooks/bottomPanelTabs/terminalTabs.ts
new file mode 100644
index 000000000..7357b5f3d
--- /dev/null
+++ b/src/hooks/bottomPanelTabs/terminalTabs.ts
@@ -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'
+ }
+}
diff --git a/src/hooks/bottomPanelTabs/useTerminal.ts b/src/hooks/bottomPanelTabs/useTerminal.ts
new file mode 100644
index 000000000..e7a8742a6
--- /dev/null
+++ b/src/hooks/bottomPanelTabs/useTerminal.ts
@@ -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) {
+ 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,
+ 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 }
+ }
+ }
+}
diff --git a/src/i18n.ts b/src/i18n.ts
index 2da0c5da4..66614af1a 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -57,6 +57,7 @@ const messages = {
loadAllFolders: 'Load All Folders',
refresh: 'Refresh',
terminal: 'Terminal',
+ logs: 'Logs',
videoFailedToLoad: 'Video failed to load',
extensionName: 'Extension Name',
reloadToApplyChanges: 'Reload to apply changes',
diff --git a/src/stores/workspace/bottomPanelStore.ts b/src/stores/workspace/bottomPanelStore.ts
index 62a3c8575..e3a74ff35 100644
--- a/src/stores/workspace/bottomPanelStore.ts
+++ b/src/stores/workspace/bottomPanelStore.ts
@@ -2,8 +2,12 @@ import type { BottomPanelExtension } from '@/types/extensionTypes'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { useCommandStore } from '@/stores/commandStore'
-import { useIntegratedTerminalTab } from '@/hooks/bottomPanelTabs/integratedTerminalTab'
+import {
+ useLogsTerminalTab,
+ useCommandTerminalTab
+} from '@/hooks/bottomPanelTabs/terminalTabs'
import { ComfyExtension } from '@/types/comfy'
+import { isElectron } from '@/utils/envUtil'
export const useBottomPanelStore = defineStore('bottomPanel', () => {
const bottomPanelVisible = ref(false)
@@ -49,7 +53,10 @@ export const useBottomPanelStore = defineStore('bottomPanel', () => {
}
const registerCoreBottomPanelTabs = () => {
- registerBottomPanelTab(useIntegratedTerminalTab())
+ registerBottomPanelTab(useLogsTerminalTab())
+ if (isElectron()) {
+ registerBottomPanelTab(useCommandTerminalTab())
+ }
}
const registerExtensionBottomPanelTabs = (extension: ComfyExtension) => {