diff --git a/README.md b/README.md index 9ba58f2..fbfde32 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,26 @@ here is an example `~/mterm/settings.json` - - x, y, w, h: `SCREEN:ratio | number` use a ratio of the screen size (for centering etc) or use a static number +### 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 | +| `: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` | +| `:settings` | | Open the mterm settings gui to manage `~/mterm/settings.json` | +| `: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. diff --git a/src/main/framework/runtime-executor.ts b/src/main/framework/runtime-executor.ts index 7c54af5..9edf2dc 100644 --- a/src/main/framework/runtime-executor.ts +++ b/src/main/framework/runtime-executor.ts @@ -10,12 +10,14 @@ import Test from './system-commands/test' import Clear from './system-commands/clear' import Version from './system-commands/version' import Vault from './system-commands/vault' +import Workspace from './system-commands/workspace' +import Settings from './system-commands/settings' const systemCommands: Array<{ command: string alias?: string[] - task: (context: ExecuteContext) => Promise | void -}> = [Reload, Exit, History, Cd, Tab, Test, Clear, Version, Vault] + task: (context: ExecuteContext, ...args: string[]) => Promise | void +}> = [Reload, Exit, History, Cd, Tab, Test, Clear, Version, Vault, Workspace, Settings] export async function execute(context: ExecuteContext): Promise { const { platform, workspace, runtime, command, out, finish } = context const [cmd, ...args] = command.prompt.split(' ') @@ -23,7 +25,7 @@ export async function execute(context: ExecuteContext): Promise // check for system commands for (const systemCommand of systemCommands) { if (systemCommand.command === cmd || systemCommand?.alias?.includes(cmd)) { - await systemCommand.task(context) + await systemCommand.task(context, ...args) return } diff --git a/src/main/framework/settings.ts b/src/main/framework/settings.ts index f836719..e574ed3 100644 --- a/src/main/framework/settings.ts +++ b/src/main/framework/settings.ts @@ -10,10 +10,19 @@ export class Settings { private properties: object = {} private overrides: object = {} constructor( - private location: string, + public location: string, private defaultSettings: object ) {} + set(path: string, value: T): void { + setFromPath(this.properties, path, value) + } + + async save(): Promise { + const prettyJSON = JSON.stringify(this.properties, null, 2) + + await writeFile(this.location, prettyJSON, 'utf-8') + } value(path: string): T { const props = merge({}, this.properties, this.overrides) return getFromPath(props, path) diff --git a/src/main/framework/system-commands/settings.ts b/src/main/framework/system-commands/settings.ts new file mode 100644 index 0000000..212e98f --- /dev/null +++ b/src/main/framework/system-commands/settings.ts @@ -0,0 +1,85 @@ +import { ExecuteContext } from '../runtime' +import { RunnerWindow } from '../../window/windows/runner' +import { shell } from 'electron' +import { parseInt } from 'lodash' + +export default { + command: ':settings', + async task( + context: ExecuteContext, + task?: string, + key?: string, + ...valueBlocks: string[] + ): Promise { + let value: string | boolean | number | object = valueBlocks.join(' ') + if (!task) { + await context.workspace.showAndHideOthers(RunnerWindow, 'settings/general') + } + if (task === 'reload') { + await context.workspace.settings.load() + await context.workspace.reload(RunnerWindow) + + context.out(`settings reloaded`) + } else if (task === 'open') { + await shell.openPath(context.workspace.settings.location) + } else if (task === 'get') { + if (!key) { + context.out('no key provided to :settings get', true) + context.finish(1) + return + } + let value = context.workspace.settings.get(key, 'NOT FOUND') + if (typeof value === 'object') { + value = JSON.stringify(value, null, 2) + } + context.out(value) + return + } else if (task === 'set') { + if (!key) { + context.out('no key provided to :settings set', true) + context.finish(1) + return + } + if (!value) { + context.out('no value provided to :settings set', true) + context.finish(1) + return + } + const currentValue = context.workspace.settings.get(key, null) + if (currentValue !== null && typeof currentValue === 'object') { + try { + const complexValue = JSON.parse(value) + if (typeof complexValue === 'string' || typeof complexValue === 'number') { + context.out(`The value provided was a simple primitive...`) + context.out(`The current value of ${key} = \n`) + context.out(JSON.stringify(currentValue, null, 2)) + context.finish(1) + return + } + value = complexValue + } catch (e) { + context.out(`The value provided could not be parsed\n\n${e}\n`) + context.out(`The current value of ${key} = \n`) + context.out(JSON.stringify(currentValue, null, 2)) + context.finish(1) + return + } + } else if (currentValue !== null) { + if (typeof currentValue === 'boolean') { + value = value === 'true' + } else if (typeof currentValue === 'number') { + value = parseInt(value, 10) + } + } + const path = `${key}` + + context.workspace.settings.set(path, value) + + await context.workspace.settings.save() + + context.out(`value '${path}' set and saved`) + + await context.workspace.reload(RunnerWindow) + } + } +} diff --git a/src/main/framework/system-commands/workspace.ts b/src/main/framework/system-commands/workspace.ts new file mode 100644 index 0000000..05e5c39 --- /dev/null +++ b/src/main/framework/system-commands/workspace.ts @@ -0,0 +1,11 @@ +import { ExecuteContext } from '../runtime' +import { shell } from 'electron' + +export default { + command: ':workspace', + async task(context: ExecuteContext): Promise { + await shell.openPath(context.workspace.folder) + + context.out('opened workspace in explorer') + } +}