From 7acc936c79de0c7662d430c46f68fdcac933b087 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 19 Feb 2024 19:32:14 +0100 Subject: [PATCH] refactor --- apps/remix-ide/src/app/panels/terminal.tsx | 6 +- libs/remix-ui/terminal/src/index.ts | 3 +- .../lib/components/remix-ui-terminal-bar.tsx | 101 +++--------- .../remix-ui-terminal-menu-buttons.tsx | 25 +++ .../remix-ui-terminal-menu-toggle.tsx | 30 ++++ .../lib/components/remix-ui-terminal-menu.tsx | 76 +++++++++ .../terminal/src/lib/context/context.ts | 3 - .../terminal/src/lib/context/index.ts | 11 ++ .../src/lib/reducers/terminalReducer.ts | 8 +- .../src/lib/remix-ui-terminal-wrapper.tsx | 25 ++- .../terminal/src/lib/remix-ui-terminal.css | 2 +- .../terminal/src/lib/remix-ui-terminal.tsx | 40 ++--- .../terminal/src/lib/types/terminalTypes.ts | 1 + libs/remix-ui/xterm/src/index.ts | 5 +- libs/remix-ui/xterm/src/lib/actions/index.ts | 27 +++ .../remix-ui-terminal-menu-xterm.tsx | 55 +++++++ .../lib/components/remix-ui-xterminals.tsx | 154 ++++-------------- libs/remix-ui/xterm/src/lib/reducer/index.ts | 69 ++++++++ libs/remix-ui/xterm/src/lib/types/index.ts | 36 ++++ 19 files changed, 429 insertions(+), 248 deletions(-) create mode 100644 libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.tsx create mode 100644 libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-toggle.tsx create mode 100644 libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu.tsx delete mode 100644 libs/remix-ui/terminal/src/lib/context/context.ts create mode 100644 libs/remix-ui/terminal/src/lib/context/index.ts create mode 100644 libs/remix-ui/xterm/src/lib/actions/index.ts create mode 100644 libs/remix-ui/xterm/src/lib/components/remix-ui-terminal-menu-xterm.tsx create mode 100644 libs/remix-ui/xterm/src/lib/reducer/index.ts create mode 100644 libs/remix-ui/xterm/src/lib/types/index.ts diff --git a/apps/remix-ide/src/app/panels/terminal.tsx b/apps/remix-ide/src/app/panels/terminal.tsx index af86317697..5b2f236865 100644 --- a/apps/remix-ide/src/app/panels/terminal.tsx +++ b/apps/remix-ide/src/app/panels/terminal.tsx @@ -142,12 +142,12 @@ class Terminal extends Plugin { } updateComponent(state) { - return (Registry.getInstance().get('platform').api.isDesktop()) ? - : + />) } renderComponent() { diff --git a/libs/remix-ui/terminal/src/index.ts b/libs/remix-ui/terminal/src/index.ts index d6966038bd..60fa49f835 100644 --- a/libs/remix-ui/terminal/src/index.ts +++ b/libs/remix-ui/terminal/src/index.ts @@ -1,2 +1,3 @@ export * from './lib/remix-ui-terminal' -export * from './lib/remix-ui-terminal-wrapper' \ No newline at end of file +export * from './lib/remix-ui-terminal-wrapper' +export * from './lib/context' \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx index 5bea69856c..0321ec7ffe 100644 --- a/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx +++ b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx @@ -1,100 +1,37 @@ +import { appPlatformTypes, platformContext } from '@remix-ui/app' import { CustomTooltip } from '@remix-ui/helper' import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line import { FormattedMessage, useIntl } from 'react-intl' import { listenOnNetworkAction } from '../actions/terminalAction' -import { TerminalContext } from '../context/context' +import { TerminalContext } from '../context' import { RemixUiTerminalProps } from '../types/terminalTypes' +import { RemixUITerminalMenu } from './remix-ui-terminal-menu' +import { RemixUITerminalMenuToggle } from './remix-ui-terminal-menu-toggle' +import { RemixUIXtermMenu } from '../../../../xterm/src/lib/components/remix-ui-terminal-menu-xterm' +import { RemixUITerminalMenuButtons } from './remix-ui-terminal-menu-buttons' export const RemixUITerminalBar = (props: RemixUiTerminalProps) => { - const { newstate: state, dispatch } = useContext(TerminalContext) + const { terminalState, xtermState } = useContext(TerminalContext) + const platform = useContext(platformContext) const intl = useIntl() const terminalMenu = useRef(null) - function handleToggleTerminal(event: any): void { - dispatch({ type: 'toggle' }) - } - - useEffect(() => { - props.plugin.call('layout', 'minimize', props.plugin.profile.name, !state.isOpen) - }, [state.isOpen]) - useEffect(() => { - console.log('state change', state) - }, [state]) - - function handleClearConsole(event: any): void { - dispatch({ type: 'clearconsole', payload: [] }) - } - - function listenOnNetwork(event: any): void { - const isListening = event.target.checked - listenOnNetworkAction(props.plugin, isListening) - } - - function setSearchInput(arg0: string): void { - dispatch({ type: 'search', payload: arg0 }) - } + props.plugin.call('layout', 'minimize', props.plugin.profile.name, !terminalState.isOpen) + }, [terminalState.isOpen]) return (<>
- : } - > - - -
- }> - - -
- }> -
0
-
- -
- - - - -
-
-
- - setSearchInput(event.target.value.trim())} - type="text" - className="remix_ui_terminal_filter border form-control" - id="searchInput" - placeholder={intl.formatMessage({ id: 'terminal.search' })} - data-id="terminalInputSearch" - /> -
+ + {platform === appPlatformTypes.desktop ? + <> + + {xtermState.showOutput? : } + : + + } +
) } \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.tsx b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.tsx new file mode 100644 index 0000000000..4201e74d13 --- /dev/null +++ b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-buttons.tsx @@ -0,0 +1,25 @@ +import React, { useContext, useEffect } from 'react' // eslint-disable-line +import { TerminalContext } from '../context' +import { RemixUiTerminalProps, SET_OPEN } from '../types/terminalTypes' +export const RemixUITerminalMenuButtons = (props: RemixUiTerminalProps) => { + const { xtermState, dispatchXterm, terminalState, dispatch } = useContext(TerminalContext) + + function selectOutput(event: any): void { + props.plugin.call('layout', 'minimize', props.plugin.profile.name, false) + dispatchXterm({ type: 'SHOW_OUTPUT', payload: true }) + dispatch({ type: SET_OPEN, payload: true }) + } + + function showTerminal(event: any): void { + props.plugin.call('layout', 'minimize', props.plugin.profile.name, false) + dispatchXterm({ type: 'SHOW_OUTPUT', payload: false }) + dispatch({ type: SET_OPEN, payload: true }) + } + + return ( +
+ + +
+ ) +} \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-toggle.tsx b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-toggle.tsx new file mode 100644 index 0000000000..0cfa811fc0 --- /dev/null +++ b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu-toggle.tsx @@ -0,0 +1,30 @@ +import { CustomTooltip } from '@remix-ui/helper' +import React, { useContext, useEffect } from 'react' // eslint-disable-line +import { FormattedMessage } from 'react-intl' +import { TerminalContext } from '../context' +import { RemixUiTerminalProps, TOGGLE } from '../types/terminalTypes' +export const RemixUITerminalMenuToggle = (props: RemixUiTerminalProps) => { + + const { terminalState, dispatch } = useContext(TerminalContext) + + function handleToggleTerminal(event: any): void { + dispatch({ type: TOGGLE }) + } + + return ( + <> + : } + > + + + + ) +} \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu.tsx b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu.tsx new file mode 100644 index 0000000000..0bd6eae755 --- /dev/null +++ b/libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-menu.tsx @@ -0,0 +1,76 @@ +import { CustomTooltip } from '@remix-ui/helper' +import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line +import { FormattedMessage, useIntl } from 'react-intl' +import { listenOnNetworkAction } from '../actions/terminalAction' +import { TerminalContext } from '../context' +import { RemixUiTerminalProps } from '../types/terminalTypes' + +export const RemixUITerminalMenu = (props: RemixUiTerminalProps) => { + const { terminalState, dispatch } = useContext(TerminalContext) + const intl = useIntl() + + useEffect(() => { + props.plugin.call('layout', 'minimize', props.plugin.profile.name, !terminalState.isOpen) + }, [terminalState.isOpen]) + + function handleClearConsole(event: any): void { + dispatch({ type: 'clearconsole', payload: [] }) + } + + function listenOnNetwork(event: any): void { + const isListening = event.target.checked + listenOnNetworkAction(props.plugin, isListening) + } + + function setSearchInput(arg0: string): void { + dispatch({ type: 'search', payload: arg0 }) + } + + return (<> +
+ }> + + +
+ }> +
0
+
+ +
+ + + + +
+
+
+ + setSearchInput(event.target.value.trim())} + type="text" + className="remix_ui_terminal_filter border form-control" + id="searchInput" + placeholder={intl.formatMessage({ id: 'terminal.search' })} + data-id="terminalInputSearch" + /> +
+ ) +} \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/context/context.ts b/libs/remix-ui/terminal/src/lib/context/context.ts deleted file mode 100644 index fa24572c4e..0000000000 --- a/libs/remix-ui/terminal/src/lib/context/context.ts +++ /dev/null @@ -1,3 +0,0 @@ -import React from 'react' - -export const TerminalContext = React.createContext(null) \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/context/index.ts b/libs/remix-ui/terminal/src/lib/context/index.ts new file mode 100644 index 0000000000..810b3fba20 --- /dev/null +++ b/libs/remix-ui/terminal/src/lib/context/index.ts @@ -0,0 +1,11 @@ +import { Actions, xTerminalUiState } from '@remix-ui/xterm' +import React, { Dispatch } from 'react' + +type terminalProviderContextType = { + terminalState: any, + dispatch: Dispatch, + xtermState: xTerminalUiState, + dispatchXterm: Dispatch +} + +export const TerminalContext = React.createContext(null) \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts index 7542b46375..1df749173e 100644 --- a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts +++ b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts @@ -1,4 +1,4 @@ -import {CLEAR_CONSOLE, CMD_HISTORY, EMPTY_BLOCK, ERROR, HTML, INFO, KNOWN_TRANSACTION, LISTEN_ON_NETWORK, LOG, TYPEWRITERLOG, TYPEWRITERWARNING, TYPEWRITERSUCCESS, NEW_TRANSACTION, SCRIPT, UNKNOWN_TRANSACTION, WARN, TOGGLE, SEARCH, SET_ISVM} from '../types/terminalTypes' +import {CLEAR_CONSOLE, CMD_HISTORY, EMPTY_BLOCK, ERROR, HTML, INFO, KNOWN_TRANSACTION, LISTEN_ON_NETWORK, LOG, TYPEWRITERLOG, TYPEWRITERWARNING, TYPEWRITERSUCCESS, NEW_TRANSACTION, SCRIPT, UNKNOWN_TRANSACTION, WARN, TOGGLE, SEARCH, SET_ISVM, SET_OPEN} from '../types/terminalTypes' export const initialState = { journalBlocks: [], @@ -89,6 +89,12 @@ export const registerCommandReducer = (state, action) => { isOpen: !state.isOpen, } + case SET_OPEN: + return { + ...state, + isOpen: action.payload, + } + case LISTEN_ON_NETWORK: return { ...state, diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal-wrapper.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal-wrapper.tsx index 90cdc79140..bb64973616 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal-wrapper.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal-wrapper.tsx @@ -1,22 +1,33 @@ -import React, { useReducer } from 'react' // eslint-disable-line +import { appPlatformTypes, platformContext } from '@remix-ui/app' +import { RemixUiXterminals, xTerminInitialState, xtermReducer } from '@remix-ui/xterm' +import React, { useContext, useReducer } from 'react' // eslint-disable-line import { RemixUITerminalBar } from './components/remix-ui-terminal-bar' -import { TerminalContext } from './context/context' +import { TerminalContext } from './context' import { initialState, registerCommandReducer } from './reducers/terminalReducer' import RemixUiTerminal from './remix-ui-terminal' import { RemixUiTerminalProps } from './types/terminalTypes' export const RemixUITerminalWrapper = (props: RemixUiTerminalProps) => { - const [newstate, dispatch] = useReducer(registerCommandReducer, initialState) - + const [terminalState, dispatch] = useReducer(registerCommandReducer, initialState) + const [xtermState, dispatchXterm] = useReducer(xtermReducer, xTerminInitialState) + const platform = useContext(platformContext) const providerState = { - newstate, - dispatch + terminalState, + dispatch, + xtermState, + dispatchXterm } return (<> - + {platform !== appPlatformTypes.desktop && } + {platform === appPlatformTypes.desktop && + <> + + + + } ) } \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css index 13402e45ee..fb9d6bf735 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css @@ -19,7 +19,7 @@ element.style { min-height : 3em; } .remix_ui_terminal_bar { - z-index : 2; + z-index : 20; } .remix_ui_terminal_menu { max-height : 35px; diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 96111d2d47..c48eff1d12 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -28,7 +28,7 @@ import RenderKnownTransactions from './components/RenderKnownTransactions' // es import parse from 'html-react-parser' import { EMPTY_BLOCK, KNOWN_TRANSACTION, RemixUiTerminalProps, SET_ISVM, UNKNOWN_TRANSACTION } from './types/terminalTypes' import { wrapScript } from './utils/wrapScript' -import { TerminalContext } from './context/context' +import { TerminalContext } from './context' const _paq = (window._paq = window._paq || []) /* eslint-disable-next-line */ @@ -41,7 +41,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const [_cmdIndex, setCmdIndex] = useState(-1) const [_cmdTemp, setCmdTemp] = useState('') const [isOpen, setIsOpen] = useState(true) - const { newstate, dispatch } = useContext(TerminalContext) + const { terminalState, dispatch } = useContext(TerminalContext) const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState) const [, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState) const [toaster, setToaster] = useState(false) @@ -130,10 +130,10 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { // events useEffect(() => { initListeningOnNetwork(props.plugin, scriptRunnerDispatch) - registerLogScriptRunnerAction(on, 'log', newstate.commands, scriptRunnerDispatch) - registerInfoScriptRunnerAction(on, 'info', newstate.commands, scriptRunnerDispatch) - registerWarnScriptRunnerAction(on, 'warn', newstate.commands, scriptRunnerDispatch) - registerErrorScriptRunnerAction(on, 'error', newstate.commands, scriptRunnerDispatch) + registerLogScriptRunnerAction(on, 'log', terminalState.commands, scriptRunnerDispatch) + registerInfoScriptRunnerAction(on, 'info', terminalState.commands, scriptRunnerDispatch) + registerWarnScriptRunnerAction(on, 'warn', terminalState.commands, scriptRunnerDispatch) + registerErrorScriptRunnerAction(on, 'error', terminalState.commands, scriptRunnerDispatch) registerCommandAction('html', _blocksRenderer('html'), { activate: true }, dispatch) registerCommandAction('log', _blocksRenderer('log'), { activate: true }, dispatch) registerCommandAction('info', _blocksRenderer('info'), { activate: true }, dispatch) @@ -154,14 +154,9 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { ) }, [autoCompletState.text]) - useEffect(() => { - console.log('newstate.journalBlocks change', newstate.journalBlocks) - }, [newstate]) - useEffect(() => { scrollToBottom() - console.log('newstate.journalBlocks.length', newstate.journalBlocks) - }, [newstate.journalBlocks.length, toaster]) + }, [terminalState.journalBlocks.length, toaster]) function execute(file, cb) { function _execute(content, cb) { @@ -170,7 +165,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { if (cb) cb() return } - newstate.commands.script(content) + terminalState.commands.script(content) } if (typeof file === 'undefined') { @@ -306,7 +301,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const script = autoCompletState.userInput.trim() // inputEl.current.innerText.trim() if (script.length) { cmdHistoryDispatch({ type: 'cmdHistory', payload: { script } }) - newstate.commands.script(wrapScript(script)) + terminalState.commands.script(wrapScript(script)) } setAutoCompleteState((prevState) => ({ ...prevState, userInput: '' })) inputEl.current.innerText = '' @@ -316,11 +311,11 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { showSuggestions: false, })) } - } else if (newstate._commandHistory.length && event.which === 38 && !autoCompletState.showSuggestions && autoCompletState.userInput === '') { + } else if (terminalState._commandHistory.length && event.which === 38 && !autoCompletState.showSuggestions && autoCompletState.userInput === '') { event.preventDefault() setAutoCompleteState((prevState) => ({ ...prevState, - userInput: newstate._commandHistory[0], + userInput: terminalState._commandHistory[0], })) } else if (event.which === 38 && autoCompletState.showSuggestions) { event.preventDefault() @@ -401,12 +396,11 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { } useEffect(() => { - console.log('clearConsole change', newstate.clearConsole) - if(newstate.clearConsole){ + if(terminalState.clearConsole){ typeWriterIndexes.current = [] inputEl.current.focus() } - },[newstate.clearConsole]) + },[terminalState.clearConsole]) /* end of block content that gets rendered from script Runner */ @@ -592,9 +586,9 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { {handleAutoComplete()}
- {!newstate.clearConsole && } - {newstate.journalBlocks && - newstate.journalBlocks.map((x, index) => { + {!terminalState.clearConsole && } + {terminalState.journalBlocks && + terminalState.journalBlocks.map((x, index) => { if (x.name === EMPTY_BLOCK) { return (
@@ -607,7 +601,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { ) } else if (x.name === UNKNOWN_TRANSACTION) { return x.message - .filter((x) => x.tx.hash.includes(newstate.searchInput) || x.tx.from.includes(newstate.searchInput) || x.tx.to.includes(newstate.searchInput)) + .filter((x) => x.tx.hash.includes(terminalState.searchInput) || x.tx.from.includes(terminalState.searchInput) || x.tx.to.includes(terminalState.searchInput)) .map((trans) => { return (
diff --git a/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts index 05857ab801..5977707fcc 100644 --- a/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts +++ b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts @@ -24,6 +24,7 @@ export const ERROR = 'error' export const SCRIPT = 'script' export const CLEAR_CONSOLE = 'clearconsole' export const TOGGLE = 'toggle' +export const SET_OPEN = 'setOpen' export const LISTEN_ON_NETWORK = 'listenOnNetWork' export const CMD_HISTORY = 'cmdHistory' export const SEARCH = 'search' diff --git a/libs/remix-ui/xterm/src/index.ts b/libs/remix-ui/xterm/src/index.ts index d0850bef79..6d438dd286 100644 --- a/libs/remix-ui/xterm/src/index.ts +++ b/libs/remix-ui/xterm/src/index.ts @@ -1,2 +1,5 @@ export * from './lib/components/remix-ui-xterm' -export * from './lib/components/remix-ui-xterminals' \ No newline at end of file +export * from './lib/components/remix-ui-xterminals' +export * from './lib/reducer' +export * from './lib/types' +export * from './lib/actions' \ No newline at end of file diff --git a/libs/remix-ui/xterm/src/lib/actions/index.ts b/libs/remix-ui/xterm/src/lib/actions/index.ts new file mode 100644 index 0000000000..398532ec8e --- /dev/null +++ b/libs/remix-ui/xterm/src/lib/actions/index.ts @@ -0,0 +1,27 @@ +import { Actions } from "@remix-ui/xterm" +import { Plugin } from "@remixproject/engine" + +export const createTerminal = async (shell: string = '', plugin: Plugin, workingDir: string, dispatch: React.Dispatch) => { + const shells: string[] = await plugin.call('xterm', 'getShells') + dispatch({ type: 'ADD_SHELLS', payload: shells }) + const pid = await plugin.call('xterm', 'createTerminal', workingDir, shell) + dispatch({ type: 'SHOW_OUTPUT', payload: false }) + dispatch({ type: 'HIDE_ALL_TERMINALS', payload: null }) + dispatch({ type: 'ADD_TERMINAL', payload: { pid, queue: '', timeStamp: Date.now(), ref: null, hidden: false } }) + + /* + setTerminals(prevState => { + // set all to hidden + prevState.forEach(xtermState => { + xtermState.hidden = true + }) + return [...prevState, { + pid: pid, + queue: '', + timeStamp: Date.now(), + ref: null, + hidden: false + }] + }) + */ +} \ No newline at end of file diff --git a/libs/remix-ui/xterm/src/lib/components/remix-ui-terminal-menu-xterm.tsx b/libs/remix-ui/xterm/src/lib/components/remix-ui-terminal-menu-xterm.tsx new file mode 100644 index 0000000000..931265b40d --- /dev/null +++ b/libs/remix-ui/xterm/src/lib/components/remix-ui-terminal-menu-xterm.tsx @@ -0,0 +1,55 @@ +import { CustomTooltip } from '@remix-ui/helper'; +import { TerminalContext } from '@remix-ui/terminal'; +import { createTerminal } from '@remix-ui/xterm'; +import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line +import { Dropdown, ButtonGroup } from 'react-bootstrap'; +import { FormattedMessage } from 'react-intl'; +import { RemixUiTerminalProps } from "../../../../terminal/src/lib/types/terminalTypes"; + +export const RemixUIXtermMenu = (props: RemixUiTerminalProps) => { + const { xtermState, dispatchXterm } = useContext(TerminalContext) + + function onClearTerminal(): void | PromiseLike { + const terminal = xtermState.terminals.find(xtermState => xtermState.hidden === false) + if (terminal && terminal.ref && terminal.ref.terminal) + terminal.ref.terminal.clear() + } + + function onCreateTerminal(shell?: string): void | PromiseLike { + createTerminal(shell, props.plugin, xtermState.workingDir, dispatchXterm) + } + + function onCloseTerminal(): void | PromiseLike { + const pid = xtermState.terminals.find(xtermState => xtermState.hidden === false).pid + if (pid) + props.plugin.call('xterm', 'closeTerminal', pid) + } + + return (<> +
+ + + + + + {xtermState.shells.map((shell, index) => { + return ( await onCreateTerminal(shell)}>{shell}) + })} + + + +
+ ) +} \ No newline at end of file diff --git a/libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx b/libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx index 86011372f2..22c38cf952 100644 --- a/libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx +++ b/libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx @@ -1,31 +1,25 @@ -import React, { useState, useEffect } from 'react' // eslint-disable-line +import React, { useState, useEffect, useContext } from 'react' // eslint-disable-line import { ElectronPlugin } from '@remixproject/engine-electron' import RemixUiXterm from './remix-ui-xterm' import '../css/index.css' import { Button, ButtonGroup, Dropdown, Tab, Tabs } from 'react-bootstrap' import { CustomTooltip } from '@remix-ui/helper' -import { RemixUiTerminal } from '@remix-ui/terminal' +import { RemixUiTerminal, TerminalContext } from '@remix-ui/terminal' import { FormattedMessage } from 'react-intl' +import { xtermState } from '../types' +import { createTerminal } from '@remix-ui/xterm' export interface RemixUiXterminalsProps { plugin: ElectronPlugin onReady: (api: any) => void } -export interface xtermState { - pid: number - queue: string - timeStamp: number - ref: any - hidden: boolean -} - export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { + const { xtermState, dispatchXterm } = useContext(TerminalContext) const [terminals, setTerminals] = useState([]) - const [workingDir, setWorkingDir] = useState('') - const [showOutput, setShowOutput] = useState(true) + //const [workingDir, setWorkingDir] = useState('') + const [theme, setTheme] = useState(themeCollection[0]) - const [terminalsEnabled, setTerminalsEnabled] = useState(false) - const [shells, setShells] = useState([]) + const { plugin } = props useEffect(() => { @@ -36,43 +30,25 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { }) plugin.on('xterm', 'close', async (pid: number) => { - setTerminals(prevState => { - const removed = prevState.filter(xtermState => xtermState.pid !== pid) - if (removed.length > 0) - removed[removed.length - 1].hidden = false - if (removed.length === 0) - setShowOutput(true) - return [...removed] - }) + dispatchXterm({ type: 'REMOVE_TERMINAL', payload: pid }) }) plugin.on('xterm', 'new', async (pid: number) => { - setShowOutput(false) - setTerminals(prevState => { - // set all to hidden - prevState.forEach(xtermState => { - xtermState.hidden = true - }) - return [...prevState, { - pid: pid, - queue: '', - timeStamp: Date.now(), - ref: null, - hidden: false - }] - }) + console.log('new terminal') + dispatchXterm({ type: 'SHOW_OUTPUT', payload: false }) + dispatchXterm({ type: 'ADD_TERMINAL', payload: { pid, queue: '', timeStamp: Date.now(), ref: null, hidden: false } }) }) plugin.on('fs', 'workingDirChanged', (path: string) => { - setWorkingDir(path) - setTerminalsEnabled(true) + dispatchXterm({ type: 'SET_WORKING_DIR', payload: path }) + dispatchXterm({ type: 'ENABLE_TERMINALS', payload: null }) }) const workingDir = await plugin.call('fs', 'getWorkingDir') if(workingDir && workingDir !== '') { - setTerminalsEnabled(true) - setWorkingDir(workingDir) + dispatchXterm({ type: 'ENABLE_TERMINALS', payload: null }) + dispatchXterm({ type: 'SET_WORKING_DIR', payload: workingDir }) } plugin.on('theme', 'themeChanged', async (theme) => { @@ -97,6 +73,12 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { }, 2000) }, []) + useEffect(() => { + setTerminals(xtermState.terminals) + if(xtermState.terminals.length === 0) { + dispatchXterm({ type: 'SHOW_OUTPUT', payload: true }) + } + }, [xtermState.terminals]) const handleThemeChange = (theme: any) => { themeCollection.forEach((themeItem) => { @@ -129,27 +111,6 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { plugin.call('xterm', 'resize', event, pid) } - - const createTerminal = async (shell?: string) => { - const shells = await plugin.call('xterm', 'getShells') - setShells(shells) - const pid = await plugin.call('xterm', 'createTerminal', workingDir, shell) - setShowOutput(false) - setTerminals(prevState => { - // set all to hidden - prevState.forEach(xtermState => { - xtermState.hidden = true - }) - return [...prevState, { - pid: pid, - queue: '', - timeStamp: Date.now(), - ref: null, - hidden: false - }] - }) - } - const setTerminalRef = (pid: number, ref: any) => { setTerminals(prevState => { const terminal = prevState.find(xtermState => xtermState.pid === pid) @@ -174,76 +135,17 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { }) } - const closeTerminal = () => { - const pid = terminals.find(xtermState => xtermState.hidden === false).pid - if (pid) - plugin.call('xterm', 'closeTerminal', pid) - } - - const selectOutput = () => { - props.plugin.call('layout', 'minimize', props.plugin.profile.name, false) - setShowOutput(true) - } - - const showTerminal = () => { - setShowOutput(false) - props.plugin.call('layout', 'minimize', props.plugin.profile.name, false) - if (terminals.length === 0) createTerminal() - } - - const clearTerminal = () => { - const terminal = terminals.find(xtermState => xtermState.hidden === false) - if (terminal && terminal.ref && terminal.ref.terminal) - terminal.ref.terminal.clear() - } + useEffect(() => { + if (!xtermState.showOutput) { + if (terminals.length === 0) createTerminal('', plugin, xtermState.workingDir, dispatchXterm) + } + }, [xtermState.showOutput]) return (<> -
-
- - -
-
- - - - - - - - - - {shells.map((shell, index) => { - return ( await createTerminal(shell)}>{shell}) - })} - - - -
-
- - -
<> -
+
{terminals.map((xtermState) => { return (
diff --git a/libs/remix-ui/xterm/src/lib/reducer/index.ts b/libs/remix-ui/xterm/src/lib/reducer/index.ts new file mode 100644 index 0000000000..b97cbb757e --- /dev/null +++ b/libs/remix-ui/xterm/src/lib/reducer/index.ts @@ -0,0 +1,69 @@ +import { Actions, xTerminalUiState } from "@remix-ui/xterm" + +export const xTerminInitialState: xTerminalUiState = { + terminalsEnabled: false, + terminals: [], + shells: [], + showOutput: true, + workingDir: '' +} + +export const xtermReducer = (state = xTerminInitialState, action: Actions) => { + switch (action.type) { + case 'ENABLE_TERMINALS': + return { + ...state, + terminalsEnabled: true + } + case 'DISABLE_TERMINALS': + return { + ...state, + terminalsEnabled: false + } + case 'ADD_TERMINAL': + return { + ...state, + terminals: [...state.terminals, action.payload] + } + case 'HIDE_TERMINAL': + return { + ...state, + terminals: state.terminals.map(terminal => terminal.pid === action.payload ? { ...terminal, hidden: true } : terminal) + } + case 'SHOW_TERMINAL': + return { + ...state, + terminals: state.terminals.map(terminal => terminal.pid === action.payload ? { ...terminal, hidden: false } : terminal) + } + case 'HIDE_ALL_TERMINALS': + return { + ...state, + terminals: state.terminals.map(terminal => ({ ...terminal, hidden: true })) + } + case 'REMOVE_TERMINAL': + const removed = state.terminals.filter(xtermState => xtermState.pid !== action.payload) + if (removed.length > 0) + removed[removed.length - 1].hidden = false + return { + ...state, + terminals: removed + } + case 'ADD_SHELLS': + return { + ...state, + shells: action.payload + } + case 'SHOW_OUTPUT': + return { + ...state, + showOutput: action.payload + } + case 'SET_WORKING_DIR': + return { + ...state, + workingDir: action.payload + } + default: + return state + } +} diff --git a/libs/remix-ui/xterm/src/lib/types/index.ts b/libs/remix-ui/xterm/src/lib/types/index.ts new file mode 100644 index 0000000000..170441acb9 --- /dev/null +++ b/libs/remix-ui/xterm/src/lib/types/index.ts @@ -0,0 +1,36 @@ + +export interface xtermState { + pid: number + queue: string + timeStamp: number + ref: any + hidden: boolean +} + +export interface xTerminalUiState { + terminalsEnabled: boolean + terminals: xtermState[] + shells: string[] + showOutput: boolean + workingDir: string +} + +export interface ActionPayloadTypes { + ENABLE_TERMINALS: undefined, + DISABLE_TERMINALS: undefined, + ADD_TERMINAL: xtermState, + HIDE_TERMINAL: number, + SHOW_TERMINAL: number, + HIDE_ALL_TERMINALS: undefined, + REMOVE_TERMINAL: number, + ADD_SHELLS: string[], + SHOW_OUTPUT: boolean + SET_WORKING_DIR: string +} + +export interface Action { + type: T, + payload: ActionPayloadTypes[T] +} + +export type Actions = {[A in keyof ActionPayloadTypes]: Action}[keyof ActionPayloadTypes] \ No newline at end of file