mterm-io / mterm

An electron terminal written with TypeScript, and rendered with React. Extend your terminal with themes, plugins and commands written in typescript. Scripting for the modern age.
https://mterm.io
8 stars 0 forks source link
electron react terminal

mterm

An electron terminal written with TypeScript, and rendered with React.
Join us on discord @ discord.gg/mterm
mterm

Quality Gate Status release

image image image

mterm is a cross-platform command-line terminal that proxies the underlying command-line interpreters, such as powershell, sh or wsl. commands are executed in the background and results streamed to the foreground.

this means commands such as ls, cd or program commands such as node -v or yarn install will work out of the box as long as the host machine supports these commands. you can configure the desired interpreter below

Why

Install

Head over to the release page to find the binary for your system type. mterm is evergreen and updates are automatically installed on your system as hey get released. Run :v to see your current mterm version.

Customize

mterm is customizable in a few ways -

https://github.com/mterm-io/mterm/assets/7341502/c920853f-1f27-4ef9-ae72-945f1663e36d

Autocomplete

Start typing, mterm will pick up your available: programs, system commands, custom commands and history

Finally, hit tab to finish the completion - image

Command Mode

By default, mterm opens in command mode (you can change this in settings). A couple of notes about command mode -

This is a nice way to focus on execution details but it can be annoying if multiple windows are in use.

Hide mterm with the default toggle hotkey -

~ + CommandOrControl

Disable command mode and go to normal terminal window mode with -

~ + Shift + CommandOrControl

Or change the behaviour of all of this with settings.

Configure

mterm creates the mterm folder on a host machine on first launch a the user's home directory

for windows -

C:/Users/<YOUR_NAME>/mterm

for mac -

/Users/<YOUR_NAME>/mterm

for everything else -

/home/<YOUR_NAME>/mterm

in this folder, there are a couple of important files to help configure mterm -

Settings

here is an example ~/mterm/settings.json -

{
  "defaultProfile": "wsl",
  "profiles": {
    "powershell": {
      "platform": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -noprofile -command $ARGS",
      "theme": "theme.css",
      "icon": "default"
    },
    "wsl": {
      "platform": "bash -c $ARGS",
      "theme": "theme.css",
      "icon": "default"
    }
  },
  "runner": {
    "shortcut": "`+CommandOrControl",
    "bounds": {
      "screen": "PRIMARY",
      "x": "SCREEN:-.5",
      "y": "SCREEN:-.5",
      "w": 720,
      "h": 500
    },
    "commanderModeShortcut": "`+Shift+CommandOrControl",
    "commanderMode": true,
    "commanderModeBounds": {
      "screen": 0,
      "x": 0,
      "y": "SCREEN:-.5",
      "w": "SCREEN",
      "h": 500
    }
  }
}

System

mterm provided a few system commands to help control the terminal and settings. mterm settings will always start with : (a colon) unless the intention is to override a system command. for example, because clear needs to be handled in a special way for mterm windows + tabs, it is overriden in mterm.

Command Alias Purpose
clear cls Clear the current terminal output
cd Navigate the file tree on the host machine
:exit exit Exit the current tab, or mterm if only 1 tab is open
:edit edit Open the in-terminal editor with the file provided. Hit Ctrl+S to save in the editor
:history Print out terminal history for debugging in a JSON format
:reload Reload settings, the ui, and commands without restarting
:tab Open a new tab
:test Sample command that executes after 10 seconds. Helpful for debugging
:vault Open the secret management tool, the mterm vault
:version :v Print the current mterm version
:workspace Open the mterm workspace folder on disk: ~/mterm
:theme :css Edit the terminal theme real time
:cmd :commands Edit the command file
:ext ext List terminal extensions Edit the command file
:ext add {extenstion} Add the extension, see extension Edit the command file
:ext rm {extenstion} Remove the extension, see extension Edit the command file
:cmd {cmd_name} Edit the command file for the cmd_name, creates if this doesn't exist
:settings Open the mterm settings gui to manage ~/mterm/settings.json
:settings edit Open the ~/mterm/settings.json in the terminal editor with hot reloading
:settings reload Reload ~/mterm/settings.json and all ui etc associated with the settings
:settings {get\|set} {key} Set the setting key matching the path in ~/mterm/settings.json and reload

Commands

Need your own command? MTERM includes ~/mterm/commands.ts from your home directory - with any exported functions as available commands.

Here an example -

import * as os from 'node:os'

export function hello(name: string = os.userInfo().username): string {
  return `Hi, ${name}`
}

Now run hello X from mterm -

image

In this case, no argument was provided so the os.userInfo().username was the fallback. Hello, DR is the result!

Note: commands are in snake case always when running. helloWorld will be hello_world when running in mterm. however, mterm will still resolve helloWorld as hello_world when running - it's just important to note this when dealing with name collisions. every command is stored in snake case.

add a command

add a command to and edit this command -

:cmd command_name

image

command_name is now available to mterm (or commandName or COMMAND_NAME - note all names are resolved to snake_case)

editing a command

in the same fasion you add a command, edit a command with the same syntax -

:cmd command_name

this editor will save with Ctrl+S and reload the command every time you save.

other notes on commands

Try fetching data!

export async function query(): Promise<{
  userId: number
  id: number
  title: string
  completed: boolean
}> {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')

  return await response.json()
}
drawing

Note the return type is optional, just added above to highlight the typescript engine provided

Utilities

Shell (https://www.electronjs.org/docs/latest/api/shell) is provided within the command context, use shell like this:

to open folder or file: await this.shell.openPath(path)

to open URL: await this.shell.openExternal(path)

to open editor await shell.openPath(${editorCommand} ${filePath})

Transformers

Sometimes we need to change variables, execution results or perform an operation on strings.

MTERM helps solve this by providing transformers as terminal operators. These transformers abstract away somewhat tedious actions in the terminal, they also provide common utilities within the scope of a prompt

drawing

For the prompt of

":snake(this is some text)"

We get the result

this_is_some_text

snake is a mterm transformer that takes a string and converts it to snake case

Transformers can be used anywhere including from within transformers -

":upper(hmm) :lower(WOW) :snake(:lower(HMM WOW))"

Prints out -

HMM wow hmm_wow

Transformer Arguments

Some transformers accept arguments. Arguments for transformers are seperated by : colon characters

Here is the split transformer on a string -

":split(1,2,3,4:1:,)"

one way to view this operation is "1,2,3,4".split(',')[1]

Note you can use " quotes to wrap transformer arguments if there is a colon within the argument

Transformers Provided

Here are the provided transformers for use -

echo

print out the argument, proxies the argument to the echo on the terminal

:echo examples -

:echo(HELLO) -> "HELLO"
:echo(:upper(hello)) -> "HELLO"
get

gets the argument from the vault, if not available gets the argument from the environment variables from the host machine, if not available returns the fallback

:get examples -

# e.g: Basic find
#
# assuming:
## there is a env variable called PATH=PATH_FROM_SYSTEM
:get(PATH) -> "PATH_FROM_SYSTEM"

# e.g: Fallback keys
#
# assuming:
## there is NOT a env variable called NOT_FOUND_A
## there is NOT a env variable called NOT_FOUND_B
## there is a env variable called FOUND_C=VALUE_OF_C
:get(NOT_FOUND_A,NOT_FOUND_B,FOUND_C) -> "VALUE_OF_C"

# e.g: Fallback variable
#
# assuming:
## there is NOT a env variable called NOT_FOUND_A
## there is a env variable called FOUND_C=VALUE_OF_C
:get(NOT_FOUND_A:im the fallback) -> "im the fallback"
:get(FOUND_C:im the fallback) -> "VALUE_OF_C"
lower

transforms the argument to lowercase

:lower examples -

":lower(HELLO)" -> "hello"
:echo(:lower(HELLO)) -> "hello"
run

runs the argument against the terminal and proxies the result

:run examples -


# e.g: Run command
#
# assuming:
# there is a `hello` function in `commands.ts`
# the `hello` function is declared as const hello = (arg = 'fallback') => `Hello, ${arg}!`
":run(hello)" -> "Hello, fallback!"
":run(hello argument)" -> "Hello, argument!"

# e.g: Run command within transformer
#
# assuming:
# there is a `hello` function in `commands.ts`
# the `hello` function is declared as const hello = (arg = 'fallback') => `Hello, ${arg}!`
":lower(:run(hello))" -> "hello, fallback!"
":lower(:run(hello)) :upper(:run(hello))" -> "hello, fallback! HELLO, FALLBACK!"
snake

transforms the argument to snakecase

:snake examples -

":snake(HELLO WORLD)" -> "hello_world"
title

transforms the argument to titlecase

:title examples -

":title(hello world)" -> "Hello World"
":title(hello_world)" -> "Hello World"
split

splits the provided argument, accepts the index or uses 0, accepts the delimiter or uses ,

:split examples -

":split(hello,world)" -> "hello"
":split(hello,world:1)" -> "world"
":split(hello@world:1:@)" -> "world"

:echo(":split(":get(xx,PATH)":0:;)") -> "FIRST_RESULT_IN_PATH"
upper

transforms the argument to uppercase

:title examples -

":uppercase(hello world)" -> "HELLO WORLD"

Secrets

Environment variables are a bit unsafe. You set these and leave the host machine all the ability to read and share these. Wonderful for services and backends, not the safest for personal usage.

While mterm does support reading env variables in the terminal, an alternative much safer secret mechanism is provided.

Passwords, URLS (any value really) can be stored in the vault -

drawing

Open the vault wih :vault or use the tray icon open Vault and Secrets

The mterm vault is an AES-256 encrypted store on your local machine. During setup, you provide a password for this store.

drawing

Every time mterm is launched, the password is required to use these values provided. You can find vault values by using this.vault.get in commands -

export function who() {
  const name = this.vault.get('NAME')

  return `name: ${name}`
}

image

Editor

mterm provides an editor with :edit <FILE> or edit <FILE> commands -

image

hit control + s within the file editor to save this

Other Notes

When you change the tab name to include $idx - this will be replaced with the current tab index

You can install packages in the ~/mterm folder and use them in commands.ts

contributing

see local setup for code setup info.

ensure editor has prettier settings configured for .pretierrc.yaml or PRs will fail.

every commit must be in conventional commit format. this is how we auto generate release notes and change logs. features might be ignored if this rule is not followed.

make sure to make a pr with this format: feature/<name_or_issue_number>.

reach out on discord for contributor role.

local setup

requires node requires yarn

note: quickly install yarn on node 20+ with corepack enable

clone -

git clone git@github.com:mterm-io/mterm.git

install deps -

yarn

run locally -

$ yarn dev

build

note, because these require github token during build - this will probably fail for you. adding here either way for completeness

# For windows
yarn build:win

# For macOS
yarn build:mac

# For Linux
yarn build:linux