pull/5370/head
filip mertens 9 months ago
parent 59390c74c1
commit 7a7d5850c9
  1. 35
      apps/remix-ide/src/app/panels/terminal.tsx
  2. 100
      libs/remix-ui/terminal/src/lib/components/remix-ui-terminal-bar.tsx
  3. 3
      libs/remix-ui/terminal/src/lib/context/context.ts
  4. 79
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  5. 21
      libs/remix-ui/terminal/src/lib/remix-ui-terminal-wrapper.tsx
  6. 72
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  7. 1
      libs/remix-ui/terminal/src/lib/types/terminalTypes.ts

@ -1,6 +1,6 @@
/* global Node, requestAnimationFrame */ // eslint-disable-line
import React from 'react' // eslint-disable-line
import { RemixUiTerminal } from '@remix-ui/terminal' // eslint-disable-line
import { RemixUiTerminal, RemixUITerminalWrapper } from '@remix-ui/terminal' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import {Registry} from '@remix-project/remix-lib'
@ -26,6 +26,34 @@ const profile = {
}
class Terminal extends Plugin {
fileImport: CompilerImports
event: any
globalRegistry: Registry
element: HTMLDivElement
eventsDecoder: any
txListener: any
_deps: { fileManager: any; editor: any; compilersArtefacts: any; offsetToLineColumnConverter: any }
commandHelp: { 'remix.loadgist(id)': string; 'remix.loadurl(url)': string; 'remix.execute(filepath)': string; 'remix.exeCurrent()': string; 'remix.help()': string }
blockchain: any
vm: typeof vm
_api: any
_opts: any
config: any
version: string
data: {
lineLength: any // ????
session: any[]; activeFilters: { commands: {}; input: string }; filterFns: {}
}
_view: { el: any; bar: any; input: any; term: any; journal: any; cli: any }
_components: {}
_commands: {}
commands: {}
_JOURNAL: any[]
_jobs: any[]
_INDEX: any
_shell: any
dispatch: any
terminalApi: any
constructor(opts, api) {
super(profile)
this.fileImport = new CompilerImports()
@ -114,9 +142,8 @@ class Terminal extends Plugin {
}
updateComponent(state) {
return (Registry.getInstance().get('platform').api.isDesktop()) ? <RemixUiXterminals onReady={state.onReady} plugin={state.plugin}>
</RemixUiXterminals>
: <RemixUiTerminal
return (Registry.getInstance().get('platform').api.isDesktop()) ? <RemixUiXterminals onReady={state.onReady} plugin={state.plugin}/>
: <RemixUITerminalWrapper
plugin={state.plugin}
onReady={state.onReady}
visible={true}

@ -0,0 +1,100 @@
import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect, useReducer, useRef, useContext } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { TerminalContext } from '../context/context'
import { initialState, registerCommandReducer } from '../reducers/terminalReducer'
import { RemixUiTerminalProps } from '../types/terminalTypes'
export const RemixUITerminalBar = (props: RemixUiTerminalProps) => {
const { newstate: state, dispatch } = useContext(TerminalContext)
const [isVM, setIsVM] = useState(false)
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 {
throw new Error('Function not implemented.')
}
function setSearchInput(arg0: string): void {
throw new Error('Function not implemented.')
}
return (<>
<div className="remix_ui_terminal_bar d-flex">
<div className="remix_ui_terminal_menu d-flex w-100 align-items-center position-relative border-top border-dark bg-light" ref={terminalMenu} data-id="terminalToggleMenu">
<CustomTooltip
placement="top"
tooltipId="terminalToggle"
tooltipClasses="text-nowrap"
tooltipText={state.isOpen ? <FormattedMessage id="terminal.hideTerminal" /> : <FormattedMessage id="terminal.showTerminal" />}
>
<i
className={`mx-2 remix_ui_terminal_toggleTerminal fas ${state.isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`}
data-id="terminalToggleIcon"
onClick={handleToggleTerminal}
></i>
</CustomTooltip>
<div className="mx-2 remix_ui_terminal_console" id="clearConsole" data-id="terminalClearConsole" onClick={handleClearConsole}>
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="terminal.clearConsole" />}>
<i className="fas fa-ban" aria-hidden="true"></i>
</CustomTooltip>
</div>
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="terminal.pendingTransactions" />}>
<div className="mx-2">0</div>
</CustomTooltip>
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText={intl.formatMessage({ id: isVM ? 'terminal.listenVM' : 'terminal.listenTitle' })}
>
<div className="h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}>
<input
className="custom-control-input"
id="listenNetworkCheck"
onChange={listenOnNetwork}
type="checkbox"
disabled={isVM}
/>
</CustomTooltip>
<label
className="form-check-label custom-control-label text-nowrap"
style={{ paddingTop: '0.125rem' }}
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
>
<FormattedMessage id="terminal.listen" />
</label>
</div>
</CustomTooltip>
<div className="remix_ui_terminal_search d-flex align-items-center h-100">
<i className="remix_ui_terminal_searchIcon d-flex align-items-center justify-content-center fas fa-search bg-light" aria-hidden="true"></i>
<input
onChange={(event) => 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"
/>
</div>
</div>
</div></>)
}

@ -0,0 +1,3 @@
import React from 'react'
export const TerminalContext = React.createContext(null)

@ -1,26 +1,25 @@
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 } 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} from '../types/terminalTypes'
export const initialState = {
journalBlocks: [
],
journalBlocks: [],
data: {
// lineLength: props.options.lineLength || 80,
session: [],
activeFilters: {commands: {}, input: ''},
filterFns: {}
filterFns: {},
},
_commandHistory: [],
_commands: {},
commands: {},
_JOURNAL: [],
_jobs: [],
_INDEX: {
},
_INDEX: {},
_INDEXall: [],
_INDEXallMain: [],
_INDEXcommands: {},
_INDEXcommandsMain: {},
message: []
message: [],
isOpen: true,
}
export const registerCommandReducer = (state, action) => {
@ -30,53 +29,59 @@ export const registerCommandReducer = (state, action) => {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, { ...action.payload.data })
data: Object.assign(initialState.data, {...action.payload.data}),
}
case LOG:
return {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, { ...action.payload.data })
data: Object.assign(initialState.data, {...action.payload.data}),
}
case INFO:
return {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data)
data: Object.assign(initialState.data, action.payload.data),
}
case WARN:
return {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data)
data: Object.assign(initialState.data, action.payload.data),
}
case ERROR:
return {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data)
data: Object.assign(initialState.data, action.payload.data),
}
case SCRIPT:
return {
...state,
_commands: Object.assign(initialState._commands, action.payload._commands),
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data)
data: Object.assign(initialState.data, action.payload.data),
}
case CLEAR_CONSOLE:
console.log('clear console', state)
return {
...state,
...state.journalBlocks.splice(0),
}
case TOGGLE:
return {
...state,
...state.journalBlocks.splice(0)
isOpen: !state.isOpen,
}
case LISTEN_ON_NETWORK:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-log'}),
}
default:
return {state}
@ -88,28 +93,27 @@ export const registerFilterReducer = (state, action) => {
case LOG:
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns),
}
case INFO:
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns),
}
case WARN:
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns),
}
case ERROR:
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns),
}
case SCRIPT:
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns),
}
default:
return {state}
@ -121,8 +125,7 @@ export const addCommandHistoryReducer = (state, action) => {
case CMD_HISTORY:
return {
...state,
_commandHistory: initialState._commandHistory.unshift(action.payload.script)
_commandHistory: initialState._commandHistory.unshift(action.payload.script),
}
default:
return {state}
@ -134,7 +137,7 @@ export const remixWelcomeTextReducer = (state, action) => {
case 'welcomeText':
return {
...state,
journalBlocks: initialState.journalBlocks.push(action.payload.welcomeText)
journalBlocks: initialState.journalBlocks.push(action.payload.welcomeText),
}
}
}
@ -144,67 +147,67 @@ export const registerScriptRunnerReducer = (state, action) => {
case HTML:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-log', provider: action.payload.provider}),
}
case LOG:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-log', provider: action.payload.provider}),
}
case TYPEWRITERLOG:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-log', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, typewriter: true, style: 'text-log', provider: action.payload.provider}),
}
case TYPEWRITERWARNING:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-warning', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, typewriter: true, style: 'text-warning', provider: action.payload.provider}),
}
case TYPEWRITERSUCCESS:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-success', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, typewriter: true, style: 'text-success', provider: action.payload.provider}),
}
case INFO:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-success', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-success', provider: action.payload.provider}),
}
case WARN:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-warning', provider: action.payload.provider}),
}
case ERROR:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-danger', provider: action.payload.provider}),
}
case SCRIPT:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: 'text-log', provider: action.payload.provider}),
}
case KNOWN_TRANSACTION:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: '', name: 'knownTransaction', provider: action.payload.provider}),
}
case UNKNOWN_TRANSACTION:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: '', name: 'unknownTransaction', provider: action.payload.provider}),
}
case EMPTY_BLOCK:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: '', name: 'emptyBlock', provider: action.payload.provider}),
}
case NEW_TRANSACTION:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({message: action.payload.message, style: '', provider: action.payload.provider}),
}
}
}

@ -1,7 +1,22 @@
import React, { useState, useEffect, useReducer, useRef} from 'react' // eslint-disable-line
import React, { useReducer } from 'react' // eslint-disable-line
import { RemixUITerminalBar } from './components/remix-ui-terminal-bar'
import { TerminalContext } from './context/context'
import { initialState, registerCommandReducer } from './reducers/terminalReducer'
import RemixUiTerminal from './remix-ui-terminal'
import { RemixUiTerminalProps } from './types/terminalTypes'
export const RemixUITerminalWrapper = () => {
export const RemixUITerminalWrapper = (props: RemixUiTerminalProps) => {
const [newstate, dispatch] = useReducer(registerCommandReducer, initialState)
return (<>terminal</>)
const providerState = {
newstate,
dispatch
}
return (<>
<TerminalContext.Provider value={providerState}>
<RemixUITerminalBar {...props} />
<RemixUiTerminal {...props} />
</TerminalContext.Provider>
</>)
}

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent, useContext } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import {
registerCommandAction,
@ -28,6 +28,7 @@ import RenderKnownTransactions from './components/RenderKnownTransactions' // es
import parse from 'html-react-parser'
import { EMPTY_BLOCK, KNOWN_TRANSACTION, RemixUiTerminalProps, UNKNOWN_TRANSACTION } from './types/terminalTypes'
import { wrapScript } from './utils/wrapScript'
import { TerminalContext } from './context/context'
const _paq = (window._paq = window._paq || [])
/* eslint-disable-next-line */
@ -40,7 +41,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [_cmdIndex, setCmdIndex] = useState(-1)
const [_cmdTemp, setCmdTemp] = useState('')
const [isOpen, setIsOpen] = useState<boolean>(true)
const [newstate, dispatch] = useReducer(registerCommandReducer, initialState)
const { newstate, dispatch } = useContext(TerminalContext)
const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState)
const [, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState)
const [toaster, setToaster] = useState(false)
@ -155,8 +156,13 @@ 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])
function execute(file, cb) {
@ -586,67 +592,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
return (
( !props.visible? <></>:
<div style={{ flexGrow: 1 }} className="remix_ui_terminal_panel" ref={panelRef}>
<div className="remix_ui_terminal_bar d-flex">
<div className="remix_ui_terminal_menu d-flex w-100 align-items-center position-relative border-top border-dark bg-light" ref={terminalMenu} data-id="terminalToggleMenu">
<CustomTooltip
placement="top"
tooltipId="terminalToggle"
tooltipClasses="text-nowrap"
tooltipText={isOpen ? <FormattedMessage id="terminal.hideTerminal" /> : <FormattedMessage id="terminal.showTerminal" />}
>
<i
className={`mx-2 remix_ui_terminal_toggleTerminal fas ${isOpen ? 'fa-angle-double-down' : 'fa-angle-double-up'}`}
data-id="terminalToggleIcon"
onClick={handleToggleTerminal}
></i>
</CustomTooltip>
<div className="mx-2 remix_ui_terminal_console" id="clearConsole" data-id="terminalClearConsole" onClick={handleClearConsole}>
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="terminal.clearConsole" />}>
<i className="fas fa-ban" aria-hidden="true"></i>
</CustomTooltip>
</div>
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="terminal.pendingTransactions" />}>
<div className="mx-2">0</div>
</CustomTooltip>
<CustomTooltip
placement="top"
tooltipId="terminalClear"
tooltipClasses="text-nowrap"
tooltipText={intl.formatMessage({ id: isVM ? 'terminal.listenVM' : 'terminal.listenTitle'})}
>
<div className="h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}>
<input
className="custom-control-input"
id="listenNetworkCheck"
onChange={listenOnNetwork}
type="checkbox"
disabled={isVM}
/>
</CustomTooltip>
<label
className="form-check-label custom-control-label text-nowrap"
style={{ paddingTop: '0.125rem' }}
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
>
<FormattedMessage id="terminal.listen" />
</label>
</div>
</CustomTooltip>
<div className="remix_ui_terminal_search d-flex align-items-center h-100">
<i className="remix_ui_terminal_searchIcon d-flex align-items-center justify-content-center fas fa-search bg-light" aria-hidden="true"></i>
<input
onChange={(event) => 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"
/>
</div>
</div>
</div>
<div tabIndex={-1} className="remix_ui_terminal_container d-flex h-100 m-0 flex-column" data-id="terminalContainer">
{handleAutoComplete()}
<div className="position-relative d-flex flex-column-reverse h-100">

@ -23,6 +23,7 @@ export const WARN = 'warn'
export const ERROR = 'error'
export const SCRIPT = 'script'
export const CLEAR_CONSOLE = 'clearconsole'
export const TOGGLE = 'toggle'
export const LISTEN_ON_NETWORK = 'listenOnNetWork'
export const CMD_HISTORY = 'cmdHistory'

Loading…
Cancel
Save