pull/5370/head
filip mertens 1 year ago
parent 2b6a77cff1
commit 45c29243e6
  1. 16
      apps/remixdesktop/src/lib/index.ts
  2. 117
      apps/remixdesktop/src/plugins/xtermPlugin.ts
  3. 2
      apps/remixdesktop/yarn.lock

@ -0,0 +1,16 @@
export const ansiRegex = ({onlyFirst = false} = {}) => {
const pattern = [
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
].join('|');
return new RegExp(pattern, onlyFirst ? undefined : 'g');
}
export const stripAnsi = (string: string) => {
const regex = ansiRegex();
if (typeof string !== 'string') {
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
}
return string.replace(regex, '');
}

@ -1,41 +1,70 @@
import { PluginClient } from "@remixproject/plugin"; import {PluginClient} from '@remixproject/plugin'
import { Profile } from "@remixproject/plugin-utils"; import {Profile} from '@remixproject/plugin-utils'
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" import {
ElectronBasePlugin,
import os from 'os'; ElectronBasePluginClient,
import * as pty from "node-pty" } from '@remixproject/plugin-electron'
import process from 'node:process'; import os from 'os'
import { userInfo } from 'node:os'; import * as pty from 'node-pty'
import { findExecutable } from "../utils/findExecutable"; import process from 'node:process'
import {userInfo} from 'node:os'
import {findExecutable} from '../utils/findExecutable'
import {spawnSync} from 'child_process'
import { stripAnsi } from '../lib'
export const detectDefaultShell = () => { export const detectDefaultShell = () => {
const { env } = process; const {env} = process
if (process.platform === 'win32') { if (process.platform === 'win32') {
return env.SHELL || 'powershell.exe'; return env.SHELL || 'powershell.exe'
} }
try { try {
const { shell } = userInfo(); const {shell} = userInfo()
if (shell) { if (shell) {
return shell; return shell
} }
} catch {} } catch {}
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
return env.SHELL || '/bin/zsh'; return env.SHELL || '/bin/zsh'
} }
return env.SHELL || '/bin/sh'; return env.SHELL || '/bin/sh'
}; }
// Stores default shell when imported. // Stores default shell when imported.
const defaultShell = detectDefaultShell(); const defaultShell = detectDefaultShell()
const getShellEnvArgs = [
'-ilc',
'echo -n "_SHELL_ENV_DELIMITER_"; env; echo -n "_SHELL_ENV_DELIMITER_"; exit',
]
const getShellEnvEnv = {
// Disables Oh My Zsh auto-update thing that can block the process.
DISABLE_AUTO_UPDATE: 'true',
}
const parseEnv = (env: any) => {
env = env.split('_SHELL_ENV_DELIMITER_')[1]
const returnValue = {}
export default defaultShell; for (const line of stripAnsi(env)
.split('\n')
.filter((l) => Boolean(l))) {
const [key, ...values] = line.split('=')
console.log(key, values)
Object.assign(returnValue, {
[key]: values.join('='),
})
}
return returnValue
}
export default defaultShell
const profile: Profile = { const profile: Profile = {
name: 'xterm', name: 'xterm',
@ -51,7 +80,7 @@ export class XtermPlugin extends ElectronBasePlugin {
} }
new(webContentsId: any): void { new(webContentsId: any): void {
const client = this.clients.find(c => c.webContentsId === webContentsId) const client = this.clients.find((c) => c.webContentsId === webContentsId)
if (client) { if (client) {
client.new() client.new()
} }
@ -62,18 +91,16 @@ export class XtermPlugin extends ElectronBasePlugin {
await client.closeAll() await client.closeAll()
} }
} }
} }
const clientProfile: Profile = { const clientProfile: Profile = {
name: 'xterm', name: 'xterm',
displayName: 'xterm', displayName: 'xterm',
description: 'xterm plugin', description: 'xterm plugin',
methods: ['createTerminal', 'close', 'keystroke', 'getShells', 'resize'] methods: ['createTerminal', 'close', 'keystroke', 'getShells', 'resize'],
} }
class XtermPluginClient extends ElectronBasePluginClient { class XtermPluginClient extends ElectronBasePluginClient {
terminals: pty.IPty[] = [] terminals: pty.IPty[] = []
constructor(webContentsId: number, profile: Profile) { constructor(webContentsId: number, profile: Profile) {
super(webContentsId, profile) super(webContentsId, profile)
@ -99,32 +126,39 @@ class XtermPluginClient extends ElectronBasePluginClient {
return [defaultShell] return [defaultShell]
} }
async createTerminal(path?: string, shell?: string): Promise<number> { async createTerminal(path?: string, shell?: string): Promise<number> {
let parsedEnv: any = null
if (!(process.platform === 'win32')) {
const {stdout} = spawnSync(defaultShell, getShellEnvArgs, {
encoding: 'utf8',
})
parsedEnv = parseEnv(stdout)
}
// filter undefined out of the env // filter undefined out of the env
const env = Object.keys(process.env) const env = Object.keys(parsedEnv || process.env)
.filter(key => process.env[key] !== undefined) .filter((key) => process.env[key] !== undefined)
.reduce((env, key) => { .reduce((env, key) => {
env[key] = process.env[key] || ''; env[key] = process.env[key] || ''
return env; return env
}, {} as Record<string, string>); }, {} as Record<string, string>)
const ptyProcess = pty.spawn(shell || defaultShell, [], { const ptyProcess = pty.spawn(shell || defaultShell, [], {
name: 'xterm-color', name: 'xterm-color',
cols: 80, cols: 80,
rows: 20, rows: 20,
cwd: path || process.cwd(), cwd: path || process.cwd(),
env: env env: env,
}); })
ptyProcess.onData((data: string) => { ptyProcess.onData((data: string) => {
this.sendData(data, ptyProcess.pid); this.sendData(data, ptyProcess.pid)
}) })
this.terminals[ptyProcess.pid] = ptyProcess this.terminals[ptyProcess.pid] = ptyProcess
setTimeout(() => {
this.sendData(JSON.stringify(env), ptyProcess.pid)
}, 2000)
return ptyProcess.pid return ptyProcess.pid
} }
@ -137,13 +171,13 @@ class XtermPluginClient extends ElectronBasePluginClient {
async resize(pid: number, {cols, rows}: {cols: number; rows: number}) { async resize(pid: number, {cols, rows}: {cols: number; rows: number}) {
if (this.terminals[pid]) { if (this.terminals[pid]) {
try { try {
this.terminals[pid].resize(cols, rows); this.terminals[pid].resize(cols, rows)
} catch (_err) { } catch (_err) {
const err = _err as {stack: any}; const err = _err as {stack: any}
console.error(err.stack); console.error(err.stack)
} }
} else { } else {
console.warn('Warning: Attempted to resize a session with no pty'); console.warn('Warning: Attempted to resize a session with no pty')
} }
} }
@ -155,12 +189,9 @@ class XtermPluginClient extends ElectronBasePluginClient {
} }
} }
async sendData(data: string, pid: number) { async sendData(data: string, pid: number) {
this.emit('data', data, pid) this.emit('data', data, pid)
} }
async new(): Promise<void> { async new(): Promise<void> {}
}
} }

@ -4486,7 +4486,7 @@ string_decoder@^1.1.1:
strip-ansi@^7.0.1: strip-ansi@^7.0.1:
version "7.1.0" version "7.1.0"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
dependencies: dependencies:
ansi-regex "^6.0.1" ansi-regex "^6.0.1"

Loading…
Cancel
Save