From 36a32597b4b2d1d36defd1eafcc6455305bbc455 Mon Sep 17 00:00:00 2001 From: Nicolas Giard Date: Sun, 16 Feb 2025 20:01:38 -0500 Subject: [PATCH] feat: terminal shell preferences + reset button --- src-electron/handlers.js | 4 +- src-electron/terminal.js | 47 +++++++--------- src/components/PreferencesDialog.vue | 30 +++++++++++ src/components/TerminalDialog.vue | 80 ++++++++++++++++++++++------ src/stores/editor.js | 23 ++++++++ 5 files changed, 140 insertions(+), 44 deletions(-) diff --git a/src-electron/handlers.js b/src-electron/handlers.js index 6d61ce6..fa33c77 100644 --- a/src-electron/handlers.js +++ b/src-electron/handlers.js @@ -1,7 +1,7 @@ import { app, clipboard, dialog, ipcMain, shell } from 'electron' import fs from 'node:fs/promises' import path from 'node:path' -import { orderBy } from 'lodash-es' +import { pick, orderBy } from 'lodash-es' import { encode, decode } from '@msgpack/msgpack' /** @@ -380,7 +380,7 @@ export function registerCallbacks (mainWindow, mainMenu, auth, git, lsp, tlm, te // TERMINAL // ---------------------------------------------------------- ipcMain.on('terminalInit', (ev, opts) => { - terminal.initialize(mainWindow, opts.cwd) + terminal.initialize(mainWindow, pick(opts, ['cwd', 'shell', 'args'])) }) ipcMain.on('terminalInput', (ev, data) => { terminal.write(data) diff --git a/src-electron/terminal.js b/src-electron/terminal.js index 0a553bd..19e54f9 100644 --- a/src-electron/terminal.js +++ b/src-electron/terminal.js @@ -1,29 +1,33 @@ import os from 'node:os' -// import { spawn } from 'node:child_process' import * as pty from '@lydell/node-pty' -const shell = os.platform() === 'win32' - ? { - cmd: 'powershell.exe', - args: ['-NoLogo'] - } - : { - cmd: 'bash', - args: [] - } +const defaultShell = { + cmd: 'bash', + args: '' +} +switch (os.platform()) { + case 'darwin': + defaultShell.cmd = 'zsh' + defaultShell.args = '' + break + case 'win32': + defaultShell.cmd = 'pwsh.exe' + defaultShell.args = '-NoLogo -NoProfile' + break +} export default { term: null, /** * Initialize PTY */ - initialize (mainWindow, cwd) { + initialize (mainWindow, opts = {}) { if (this.term) { return } - this.term = pty.spawn(shell.cmd, shell.args, { + this.term = pty.spawn(opts.shell || defaultShell.cmd, (opts.args ?? defaultShell.args).split(' '), { name: 'draftforge-terminal', cols: 80, rows: 30, - cwd, + cwd: opts.cwd, env: process.env }) this.term.onData(data => { @@ -32,23 +36,12 @@ export default { this.term.onExit(() => { this.term = null }) - // this.term = spawn(shell, [], { - // windowsHide: true, - // cwd - // }) - // this.term.stdout.on('data', data => { - // mainWindow.webContents.send('terminal.incomingData', data) - // }) - // this.term.stderr.on('data', data => { - // mainWindow.webContents.send('terminal.incomingData', data) - // }) - // this.term.on('exit', () => { - // this.term = null - // }) }, + /** + * Stdin + */ write (data) { if (this.term) { - // this.term.stdin.write(data) this.term.write(data) } }, diff --git a/src/components/PreferencesDialog.vue b/src/components/PreferencesDialog.vue index e082e14..2ee8bea 100644 --- a/src/components/PreferencesDialog.vue +++ b/src/components/PreferencesDialog.vue @@ -378,6 +378,31 @@ q-dialog( @update:model-value='updateTelemetryState' ) + template(v-else-if='state.tab === `terminal`') + q-form.q-gutter-md.q-pa-lg + .row + .col-8 + .text-body2 Shell Executable + .text-caption.text-grey-5 The shell to use for the terminal. + .col-4 + q-input( + v-model.number='editorStore.terminalShell' + outlined + dense + color='light-blue-4' + ) + .row + .col-8 + .text-body2 Shell Arguments + .text-caption.text-grey-5 Additional arguments to supply to the shell executable. + .col-4 + q-input( + v-model.number='editorStore.terminalArgs' + outlined + dense + color='light-blue-4' + ) + template(v-if='state.tab === `dev`') q-form.q-gutter-md.q-pa-lg .flex.items-center.text-red-5 @@ -476,6 +501,11 @@ const tabs = computed(() => ([ icon: 'mdi-broadcast', label: 'Telemetry' }, + { + key: 'terminal', + icon: 'mdi-console', + label: 'Terminal' + }, { key: 'dev', icon: 'mdi-flask', diff --git a/src/components/TerminalDialog.vue b/src/components/TerminalDialog.vue index de2abb9..8b87f04 100644 --- a/src/components/TerminalDialog.vue +++ b/src/components/TerminalDialog.vue @@ -12,24 +12,42 @@ q-dialog( q-icon(name='mdi-console', left, size='sm') span Terminal q-space - //- q-banner.q-mr-md.text-white.bg-negative.q-px-md(rounded, dense) - //- template(#avatar) - //- q-icon(name='mdi-alert', size='xs') - //- span TTY not supported yet. + q-btn.q-mr-md( + unelevated + icon='mdi-restart-alert' + color='primary' + padding='xs md' + @click='resetTerminal' + label='Reset' + no-caps + :loading='state.resetLoading' + ) + q-tooltip Kill active terminal and launch new one + q-btn.q-mr-md( + unelevated + icon='mdi-cog-outline' + color='primary' + padding='xs md' + @click='openTerminalSettings' + label='Settings' + no-caps + ) + q-tooltip Configure Terminal q-btn( unelevated icon='mdi-close' color='primary' padding='xs' @click='onDialogCancel' - ) + ) + q-tooltip Close Terminal q-card-section.card-border.q-pa-md.bg-black div(ref='terminal', style='height: 500px;') diff --git a/src/stores/editor.js b/src/stores/editor.js index 6edf2ab..8346497 100644 --- a/src/stores/editor.js +++ b/src/stores/editor.js @@ -1,6 +1,25 @@ import { defineStore } from 'pinia' import { decorationsStore } from 'src/stores/models' +// -> Default Values + +const defaultShell = { + cmd: 'bash', + args: '' +} +switch (process.env.OS_PLATFORM) { + case 'darwin': + defaultShell.cmd = 'zsh' + defaultShell.args = '' + break + case 'win32': + defaultShell.cmd = 'pwsh.exe' + defaultShell.args = '-NoLogo -NoProfile' + break +} + +// -> Editor Store + export const useEditorStore = defineStore('editor', { state: () => ({ animationEffects: true, @@ -42,6 +61,8 @@ export const useEditorStore = defineStore('editor', { symbols: [], tabSize: 2, telemetry: false, + terminalShell: defaultShell.cmd, + terminalArgs: defaultShell.args, theme: 'ietf-dark', translucencyEffects: true, validationChecksCurrent: null, @@ -147,6 +168,8 @@ export const useEditorStore = defineStore('editor', { 'previewPaneShown', 'tabSize', 'telemetry', + 'terminalShell', + 'terminalArgs', 'theme', 'translucencyEffects', 'wordWrap',