rdesktop2
filip mertens 1 year ago
parent 297c476b21
commit 731aecd556
  1. 29
      apps/remix-ide/src/app/panels/terminal.js
  2. 4
      apps/remix-ide/src/app/plugins/electron/isoGitPlugin.ts
  3. 1
      apps/remixdesktop/src/plugins/fsPlugin.ts
  4. 12
      apps/remixdesktop/src/plugins/xtermPlugin.ts
  5. 1
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  6. 7
      libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx
  7. 11
      libs/remix-ui/xterm/src/lib/components/remix-ui-xterm.tsx
  8. 152
      libs/remix-ui/xterm/src/lib/components/remix-ui-xterminals.tsx
  9. 46
      libs/remix-ui/xterm/src/lib/css/index.css

@ -9,11 +9,12 @@ import vm from 'vm'
const EventManager = require('../../lib/events') const EventManager = require('../../lib/events')
import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line
import { RemixUiXterminals } from '@remix-ui/xterm'
const KONSOLES = [] const KONSOLES = []
function register (api) { KONSOLES.push(api) } function register(api) { KONSOLES.push(api) }
const profile = { const profile = {
displayName: 'Terminal', displayName: 'Terminal',
@ -25,7 +26,7 @@ const profile = {
} }
class Terminal extends Plugin { class Terminal extends Plugin {
constructor (opts, api) { constructor(opts, api) {
super(profile) super(profile)
this.fileImport = new CompilerImports() this.fileImport = new CompilerImports()
this.event = new EventManager() this.event = new EventManager()
@ -89,18 +90,18 @@ class Terminal extends Plugin {
this.renderComponent() this.renderComponent()
} }
onDeactivation () { onDeactivation() {
this.off('scriptRunner', 'log') this.off('scriptRunner', 'log')
this.off('scriptRunner', 'info') this.off('scriptRunner', 'info')
this.off('scriptRunner', 'warn') this.off('scriptRunner', 'warn')
this.off('scriptRunner', 'error') this.off('scriptRunner', 'error')
} }
logHtml (html) { logHtml(html) {
this.terminalApi.logHtml(html) this.terminalApi.logHtml(html)
} }
log (message, type) { log(message, type) {
this.terminalApi.log(message, type) this.terminalApi.log(message, type)
} }
@ -108,18 +109,20 @@ class Terminal extends Plugin {
this.dispatch = dispatch this.dispatch = dispatch
} }
render () { render() {
return <div id='terminal-view' className='panel' data-id='terminalContainer-view'><PluginViewWrapper plugin={this}/></div> return <div id='terminal-view' className='panel' data-id='terminalContainer-view'><PluginViewWrapper plugin={this} /></div>
} }
updateComponent(state) { updateComponent(state) {
return <RemixUiTerminal return isElectron() ? <RemixUiXterminals onReady={state.onReady} plugin={state.plugin}>
plugin={state.plugin} </RemixUiXterminals>
onReady={state.onReady} : <RemixUiTerminal
/> plugin={state.plugin}
onReady={state.onReady}
/>
} }
renderComponent () { renderComponent() {
const onReady = (api) => { this.terminalApi = api } const onReady = (api) => { this.terminalApi = api }
this.dispatch({ this.dispatch({
plugin: this, plugin: this,
@ -127,7 +130,7 @@ class Terminal extends Plugin {
}) })
} }
scroll2bottom () { scroll2bottom() {
setTimeout(function () { setTimeout(function () {
// do nothing. // do nothing.
}, 0) }, 0)

@ -16,9 +16,9 @@ export class isoGitPlugin extends ElectronPlugin {
setTimeout(async () => { setTimeout(async () => {
const version = await this.call('isogit', 'version') const version = await this.call('isogit', 'version')
if(version){ if(version){
this.call('terminal', 'log', version) //this.call('terminal', 'log', version)
}else{ }else{
this.call('terminal', 'log', 'Git is not installed on the system. Using builtin git instead. Performance will be affected. It is better to install git on the system and configure the credentials to connect to GitHub etc.') //this.call('terminal', 'log', 'Git is not installed on the system. Using builtin git instead. Performance will be affected. It is better to install git on the system and configure the credentials to connect to GitHub etc.')
} }

@ -138,7 +138,6 @@ class FSPluginClient extends ElectronBasePluginClient {
withFileTypes: true, withFileTypes: true,
...options ...options
}) })
console.log('glob', files)
const result: any[] = [] const result: any[] = []
for (const file of files) { for (const file of files) {

@ -55,7 +55,7 @@ const clientProfile: Profile = {
name: 'xterm', name: 'xterm',
displayName: 'xterm', displayName: 'xterm',
description: 'xterm plugin', description: 'xterm plugin',
methods: ['createTerminal', 'close', 'keystroke'] methods: ['createTerminal', 'close', 'keystroke', 'getShells']
} }
class XtermPluginClient extends ElectronBasePluginClient { class XtermPluginClient extends ElectronBasePluginClient {
@ -65,13 +65,23 @@ class XtermPluginClient extends ElectronBasePluginClient {
super(webContentsId, profile) super(webContentsId, profile)
this.onload(() => { this.onload(() => {
console.log('XtermPluginClient onload') console.log('XtermPluginClient onload')
this.emit('loaded')
}) })
} }
async onActivation(): Promise<void> {
console.log('XtermPluginClient onActivation')
}
async keystroke(key: string, pid: number): Promise<void> { async keystroke(key: string, pid: number): Promise<void> {
this.terminals[pid].write(key) this.terminals[pid].write(key)
} }
async getShells(): Promise<string[]> {
return [defaultShell]
}
async createTerminal(path?: string): Promise<number> { async createTerminal(path?: string): Promise<number> {
const shell = defaultShell; const shell = defaultShell;

@ -91,7 +91,6 @@ const RemixApp = (props: IRemixAppUi) => {
<DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={285} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar> <DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={285} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel d-flex'> <div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel d-flex'>
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel> <RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
<RemixUiXterminals plugin={props.app.appManager}></RemixUiXterminals>
<CustomTooltip <CustomTooltip
placement="bottom" placement="bottom"
tooltipId="overlay-tooltip-all-tabs" tooltipId="overlay-tooltip-all-tabs"

@ -16,15 +16,18 @@ const DragBar = (props: IRemixDragBarUi) => {
const nodeRef = React.useRef(null) // fix for strictmode const nodeRef = React.useRef(null) // fix for strictmode
function stopDrag (e: MouseEvent, data: any) { function stopDrag (e: MouseEvent, data: any) {
console.log('stopDrag', data)
const h = window.innerHeight - data.y const h = window.innerHeight - data.y
props.refObject.current.setAttribute('style', `height: ${h}px;`) props.refObject.current.setAttribute('style', `height: ${h}px;`)
setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) console.log('stopDrag', `height: ${h}px;`)
setDragBarPosY(props.refObject.current.offsetTop)
setDragState(false) setDragState(false)
props.setHideStatus(false) props.setHideStatus(false)
} }
const handleResize = () => { const handleResize = () => {
console.log('handleResize', props.refObject.current)
if (!props.refObject.current) return if (!props.refObject.current) return
setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) setDragBarPosY(props.refObject.current.offsetTop)
} }
useEffect(() => { useEffect(() => {

@ -9,6 +9,10 @@ export interface RemixUiXtermProps {
send: (data: string, pid: number) => void send: (data: string, pid: number) => void
timeStamp: number timeStamp: number
setTerminalRef: (pid: number, ref: any) => void setTerminalRef: (pid: number, ref: any) => void
theme: {
backgroundColor: string
textColor: string
}
} }
const RemixUiXterm = (props: RemixUiXtermProps) => { const RemixUiXterm = (props: RemixUiXtermProps) => {
@ -38,8 +42,11 @@ const RemixUiXterm = (props: RemixUiXtermProps) => {
return ( return (
<> <>
<button className='btn' onClick={closeTerminal}>close</button> <button className='btn d-none' onClick={closeTerminal}>close</button>
<XTerm ref={xtermRef} onData={onData} onKey={onKey}></XTerm> <XTerm
options={{theme: {background: props.theme.backgroundColor , foreground: props.theme.textColor}}}
ref={xtermRef} onData={onData}
onKey={onKey}></XTerm>
</> </>
) )

@ -2,9 +2,12 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line
import { ElectronPlugin } from '@remixproject/engine-electron' import { ElectronPlugin } from '@remixproject/engine-electron'
import RemixUiXterm from './remix-ui-xterm' import RemixUiXterm from './remix-ui-xterm'
import '../css/index.css' import '../css/index.css'
import { Tab, Tabs } from 'react-bootstrap' import { Button, ButtonGroup, Dropdown, Tab, Tabs } from 'react-bootstrap'
import { CustomIconsToggle } from '@remix-ui/helper'
import { RemixUiTerminal } from '@remix-ui/terminal'
export interface RemixUiXterminalsProps { export interface RemixUiXterminalsProps {
plugin: ElectronPlugin plugin: ElectronPlugin
onReady: (api: any) => void
} }
export interface xtermState { export interface xtermState {
@ -18,30 +21,65 @@ export interface xtermState {
export const RemixUiXterminals = (props: RemixUiXterminalsProps) => { export const RemixUiXterminals = (props: RemixUiXterminalsProps) => {
const [terminals, setTerminals] = useState<xtermState[]>([]) const [terminals, setTerminals] = useState<xtermState[]>([])
const [workingDir, setWorkingDir] = useState<string>('') const [workingDir, setWorkingDir] = useState<string>('')
const [showOutput, setShowOutput] = useState<boolean>(true)
const [theme, setTheme] = useState<any>(themeCollection[0])
const [terminalsEnabled, setTerminalsEnabled] = useState<boolean>(false)
const [shells, setShells] = useState<string[]>([])
const { plugin } = props const { plugin } = props
useEffect(() => { useEffect(() => {
setTimeout(async () => { setTimeout(async () => {
plugin.on('xterm', 'loaded', async () => { plugin.on('xterm', 'loaded', async () => {
console.log('xterm loaded')
}) })
plugin.on('xterm', 'data', async (data: string, pid: number) => { plugin.on('xterm', 'data', async (data: string, pid: number) => {
writeToTerminal(data, pid) writeToTerminal(data, pid)
}) })
plugin.on('xterm', 'close', async (pid: number) => { plugin.on('xterm', 'close', async (pid: number) => {
setTerminals(prevState => { setTerminals(prevState => {
const removed = prevState.filter(xtermState => xtermState.pid !== pid) const removed = prevState.filter(xtermState => xtermState.pid !== pid)
removed[removed.length-1].hidden = false if (removed.length > 0)
removed[removed.length - 1].hidden = false
if(removed.length === 0)
setShowOutput(true)
return [...removed] return [...removed]
}) })
}) })
plugin.on('fs', 'workingDirChanged', (path: string) => { plugin.on('fs', 'workingDirChanged', (path: string) => {
setWorkingDir(path) setWorkingDir(path)
setTerminalsEnabled(true)
})
plugin.on('theme', 'themeChanged', async (theme) => {
console.log('themeChanged', theme)
handleThemeChange(theme)
}) })
}, 5000)
const theme = await plugin.call('theme', 'currentTheme')
console.log('theme', theme)
handleThemeChange(theme)
const shells = await plugin.call('xterm', 'getShells')
console.log('shells', shells)
setShells(shells)
}, 2000)
}, []) }, [])
const handleThemeChange = (theme: any) => {
themeCollection.forEach((themeItem) => {
if (themeItem.themeName === theme.name) {
setTheme(themeItem)
console.log('setTheme', themeItem)
}
})
}
const writeToTerminal = (data: string, pid: number) => { const writeToTerminal = (data: string, pid: number) => {
setTerminals(prevState => { setTerminals(prevState => {
const terminal = prevState.find(xtermState => xtermState.pid === pid) const terminal = prevState.find(xtermState => xtermState.pid === pid)
@ -67,7 +105,7 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => {
const createTerminal = async () => { const createTerminal = async () => {
const pid = await plugin.call('xterm', 'createTerminal', workingDir) const pid = await plugin.call('xterm', 'createTerminal', workingDir)
setShowOutput(false)
setTerminals(prevState => { setTerminals(prevState => {
// set all to hidden // set all to hidden
prevState.forEach(xtermState => { prevState.forEach(xtermState => {
@ -108,33 +146,101 @@ export const RemixUiXterminals = (props: RemixUiXterminalsProps) => {
}) })
} }
const closeTerminal = () => {
const pid = terminals.find(xtermState => xtermState.hidden === false).pid
if (pid)
plugin.call('xterm', 'close', pid)
}
return (<> const selectOutput = () => {
setShowOutput(true)
}
<div className='xterm-panel'> const showTerminal = () => {
<button className='btn btn-sm btn-secondary' onClick={() => { setShowOutput(false)
createTerminal() if (terminals.length === 0) createTerminal()
}}>open new terminal</button> }
<div className='remix-ui-xterminals-container'> return (<>
<>
{terminals.map((xtermState, index) => {
return (<button onClick={async () => selectTerminal(xtermState)} className={`btn btn-sm btn-secondary ${xtermState.hidden ? 'xterm-btn-none' : 'xterm-btn-active'}`}>Terminal {index + 1}</button>) <div className='xterm-panel'>
})} <div className='xterm-panel-header bg-light'>
{terminals.map((xtermState) => { <div className='xterm-panel-header-left p-1'>
return ( <button className={`btn btn-sm btn-secondary mr-2 ${!showOutput ? 'xterm-btn-none' : 'xterm-btn-active'}`} onClick={selectOutput}>ouput</button>
<div className={xtermState.hidden ? 'hide-xterm' : 'show-xterm'} key={xtermState.pid} data-id={`remixUIXT${xtermState.pid}`}> <button className={`btn btn-sm btn-secondary ${terminalsEnabled ? '' : 'd-none'} ${showOutput ? 'xterm-btn-none' : 'xterm-btn-active'}`} onClick={showTerminal}><span className="far fa-terminal border-0 ml-1"></span></button>
<RemixUiXterm setTerminalRef={setTerminalRef} timeStamp={xtermState.timeStamp} send={send} pid={xtermState.pid} plugin={plugin}></RemixUiXterm> </div>
<div className={`xterm-panel-header-right ${showOutput ? 'd-none' : ''}`}>
<Dropdown as={ButtonGroup}>
<button className="btn btn-sm btn-secondary" onClick={createTerminal}><span className="far fa-plus border-0 p-0 m-0"></span></button>
<Dropdown.Toggle split variant="secondary" id="dropdown-split-basic" />
<Dropdown.Menu className='custom-dropdown-items remixui_menuwidth'>
{shells.map((shell, index) => {
return (<Dropdown.Item index={index} onClick={createTerminal}>{shell}</Dropdown.Item>)
})}
</Dropdown.Menu>
</Dropdown>
<button className="btn ml-2 btn-sm btn-secondary" onClick={closeTerminal}><span className="far fa-trash border-0 ml-1"></span></button>
</div>
</div>
<div className='remix-ui-xterminals-container'>
<>
<div className={`${!showOutput ? 'd-none' : 'd-block w-100'} `}>
<RemixUiTerminal
plugin={props.plugin}
onReady={props.onReady} />
</div>
<div className={`remix-ui-xterminals-section ${showOutput ? 'd-none' : 'd-flex'} `}>
{terminals.map((xtermState) => {
return (
<div className={`xterm-terminal ${xtermState.hidden ? 'hide-xterm' : 'show-xterm'}`} key={xtermState.pid} data-id={`remixUIXT${xtermState.pid}`}>
<RemixUiXterm theme={theme} setTerminalRef={setTerminalRef} timeStamp={xtermState.timeStamp} send={send} pid={xtermState.pid} plugin={plugin}></RemixUiXterm>
</div>
)
})}
<div className='remix-ui-xterminals-buttons border-left'>
{terminals.map((xtermState, index) => {
return (<button onClick={async () => selectTerminal(xtermState)} className={`btn btn-sm mt-2 btn-secondary ${xtermState.hidden ? 'xterm-btn-none' : 'xterm-btn-active'}`}><span className="fa fa-terminal border-0 p-0 m-0"></span></button>)
})}
</div> </div>
) </div>
})} </>
</> </div>
</div>
</div> </div>
</>) </>)
} }
const themeCollection = [
{ themeName: 'HackerOwl', backgroundColor: '#011628', textColor: '#babbcc',
shapeColor: '#8694a1',fillColor: '#011C32'},
{ themeName: 'Cerulean', backgroundColor: '#ffffff', textColor: '#343a40',
shapeColor: '#343a40',fillColor: '#f8f9fa'},
{ themeName: 'Cyborg', backgroundColor: '#060606', textColor: '#adafae',
shapeColor: '#adafae', fillColor: '#222222'},
{ themeName: 'Dark', backgroundColor: '#222336', textColor: '#babbcc',
shapeColor: '#babbcc',fillColor: '#2a2c3f'},
{ themeName: 'Flatly', backgroundColor: '#ffffff', textColor: '#343a40',
shapeColor: '#7b8a8b',fillColor: '#ffffff'},
{ themeName: 'Black', backgroundColor: '#1a1a1a', textColor: '#babbcc',
shapeColor: '#b5b4bc',fillColor: '#1f2020'},
{ themeName: 'Light', backgroundColor: '#eef1f6', textColor: '#3b445e',
shapeColor: '#343a40',fillColor: '#ffffff'},
{ themeName: 'Midcentury', backgroundColor: '#DBE2E0', textColor: '#11556c',
shapeColor: '#343a40',fillColor: '#eeede9'},
{ themeName: 'Spacelab', backgroundColor: '#ffffff', textColor: '#343a40',
shapeColor: '#333333', fillColor: '#eeeeee'},
{ themeName: 'Candy', backgroundColor: '#d5efff', textColor: '#11556c',
shapeColor: '#343a40',fillColor: '#fbe7f8' },
{ themeName: 'Violet', backgroundColor: '#f1eef6', textColor: '#3b445e',
shapeColor: '#343a40',fillColor: '#f8fafe' },
{ themeName: 'Pride', backgroundColor: '#f1eef6', textColor: '#343a40',
shapeColor: '#343a40',fillColor: '#f8fafe' },
]

@ -1,7 +1,12 @@
.remix-ui-xterminals-container { .remix-ui-xterminals-container {
overflow: scroll; display: flex;
flex-direction: row;
} }
.xterm-panel { .xterm-panel {
}
.remix-ui-xterminals-buttons {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -20,3 +25,42 @@
.xterm-btn-none { .xterm-btn-none {
background-color: var(--secondary); background-color: var(--secondary);
} }
.xterm-terminal {
flex-grow: 1;
height: 100%;
width: 100%;
}
.xterm-panel-header-right {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-self: flex-end;
}
.xterm-panel-header {
display: flex;
flex-direction: row;
}
.xterm-panel-header-left {
display: flex;
flex-direction: row;
flex-grow: 1;
}
.remix-ui-xterminals-section {
display: flex;
flex-direction: row;
width: 100%;
z-index: 3;
}
.hide-terminals {
width: 0;
}
.show-terminals {
width: 100%;
}

Loading…
Cancel
Save