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 @@ - - - - - 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 @@ + + + + + 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) => {