From 3e93d313caeb6707ff4760e6c018900e553c7c57 Mon Sep 17 00:00:00 2001 From: DR Date: Sat, 11 May 2024 08:28:31 -0400 Subject: [PATCH 1/3] fix: add custom themes for runner --- resources/theme.css | 129 +++++++++++++++++++ src/main/framework/runtime-events.ts | 4 + src/main/framework/system-commands/reload.ts | 2 +- src/main/framework/theme.ts | 65 ++++++++++ src/main/framework/workspace.ts | 13 ++ src/renderer/src/assets/runner.css | 6 + src/renderer/src/runner/runner.tsx | 17 +++ 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 resources/theme.css create mode 100644 src/main/framework/theme.ts diff --git a/resources/theme.css b/resources/theme.css new file mode 100644 index 0000000..76fa49c --- /dev/null +++ b/resources/theme.css @@ -0,0 +1,129 @@ +/** + * Override themes for your terminal here + * See default here: https://github.com/mterm-io/mterm/blob/main/src/renderer/src/assets/runner.css + */ + +/* + * prepend .runner-container__commander_mode + * to apply styles only for commander mode + */ +.runner-container { + background: rgba(27, 27, 31, 0.89); + color: white; +} +.runner-container__commander_mode .runner-result { +} + +.runner-input-container { +} + +.runner-input { +} + +.runner-input-field { +} + +.runner-input-field { +} +.runner-input-field:focus { +} + +.runner-container { +} + +.runner-tabs { +} + +.runner-tabs-title { +} + +.runner-tabs-title-active { +} +.runner-spacer { +} + +.runner-context { +} + +.runner-main { +} + +.runner-editor { +} + + +.runner-editor-header { +} + +.runner-editor-header-result { +} +.runner-editor-header-result::-webkit-scrollbar { +} +.runner-editor-header-result::-webkit-scrollbar-corner { +} +.runner-editor-header-result::-webkit-scrollbar-thumb { +} +.runner-editor-header-result pre { +} +.runner-context-folder { +} + +.runner-history { +} + +.runner-history::-webkit-scrollbar { +} +.runner-history::-webkit-scrollbar-corner { +} +.runner-history::-webkit-scrollbar-thumb { +} +.runner-history-item { +} +.runner-history-item:hover { +} +.runner-history-selected { +} +.runner-history-running { +} +.runner-history-complete { +} +.runner-history-aborted { +} +.runner-history-error { +} + +.runner-history::-webkit-scrollbar-track { +} + +.runner-result-error { +} +.runner-result { +} + +.space { +} +.runner-info { +} +.toggle-button-slider { +} +.toggle-button-circle { +} +.toggle-button { +} +.toggle-button-spacer { +} +.toggle-button.toggle-button-on .toggle-button-slider { +} + +.runner-result::-webkit-scrollbar { +} +.runner-result::-webkit-scrollbar-corner { +} +.runner-result::-webkit-scrollbar-thumb { +} +.contextmenu.tab-context-menu { +} +.tab-context-menu > .contextmenu__item { +} +.tab-context-menu > .contextmenu__item:hover { +} diff --git a/src/main/framework/runtime-events.ts b/src/main/framework/runtime-events.ts index d75b597..879ef93 100644 --- a/src/main/framework/runtime-events.ts +++ b/src/main/framework/runtime-events.ts @@ -203,6 +203,10 @@ export function attach({ app, workspace }: BootstrapContext): void { ) }) + ipcMain.handle('runner.theme', async (_, profile): Promise => { + return workspace.theme.get(profile) + }) + ipcMain.handle('runtime.kill', async (_, commandId, runtimeId): Promise => { const runtime = workspace.runtimes.find((r) => r.id === runtimeId) if (!runtime) { diff --git a/src/main/framework/system-commands/reload.ts b/src/main/framework/system-commands/reload.ts index 8cdf762..1d56c23 100644 --- a/src/main/framework/system-commands/reload.ts +++ b/src/main/framework/system-commands/reload.ts @@ -3,7 +3,7 @@ import { ExecuteContext } from '../execute-context' async function reload(context: ExecuteContext): Promise { await context.workspace.load() - context.out('- settings reloaded \n') + context.out('- settings and themes reloaded \n') await context.workspace.commands.load(context.workspace.settings) context.out('- commands reloaded \n') diff --git a/src/main/framework/theme.ts b/src/main/framework/theme.ts new file mode 100644 index 0000000..926405b --- /dev/null +++ b/src/main/framework/theme.ts @@ -0,0 +1,65 @@ +import { Workspace } from './workspace' +import { ProfileMap } from './runtime' +import { DEFAULT_PROFILE, DEFAULT_PROFILES } from '../../constants' +import { pathExists, readFile, writeFile } from 'fs-extra' +import { log } from '../logger' + +export class Theme { + public themes: Map = new Map() + constructor( + public workspace: Workspace, + public defaultThemeLocation: string + ) {} + + async load(): Promise { + this.themes.clear() + //get profiles + const profiles = this.workspace.settings.get('profiles', DEFAULT_PROFILES) + const cache = {} + + const getDefaultTheme = async (): Promise => { + if (cache[this.defaultThemeLocation]) { + return cache[this.defaultThemeLocation] + } + + const cssBuffer = await readFile(this.defaultThemeLocation) + const css = cssBuffer.toString() + + cache[this.defaultThemeLocation] = css + + return css + } + + for (const profileKey in profiles) { + const profile = profiles[profileKey] + const cssFileLocation = this.workspace.resolve(profile.theme) + + let css = cache[cssFileLocation] + if (!css) { + const isExist = await pathExists(cssFileLocation) + if (!isExist) { + log( + `profile = ${profileKey} is mapped to css file @ ${profile.theme}. could not find @ ${cssFileLocation} - creating default` + ) + css = await getDefaultTheme() + + await writeFile(cssFileLocation, css, 'utf-8') + } else { + const cssFileBuffer = await readFile(cssFileLocation) + css = cssFileBuffer.toString() + } + + cache[cssFileLocation] = css + } + + this.themes.set(profileKey, css) + } + } + + get(profileKey: string): string { + if (profileKey === 'default') { + profileKey = this.workspace.settings.get('defaultProfile', DEFAULT_PROFILE) + } + return this.themes.get(profileKey) || '' + } +} diff --git a/src/main/framework/workspace.ts b/src/main/framework/workspace.ts index 00df01a..905c356 100644 --- a/src/main/framework/workspace.ts +++ b/src/main/framework/workspace.ts @@ -11,6 +11,7 @@ import { DEFAULT_FOLDER, DEFAULT_HISTORY_ENABLED, DEFAULT_HISTORY_MAX_ITEMS } fr import { Commands } from './commands' import { Store } from './store' import { History } from './history' +import { Theme } from './theme' export function resolveFolderPathForMTERM(folder: string): string { folder = folder.replace('~', homedir()) @@ -25,6 +26,7 @@ export class Workspace { public history: History public settings: Settings public commands: Commands + public theme: Theme public isAppQuiting: boolean = false public windows: MTermWindow[] = [] public runtimes: Runtime[] = [] @@ -42,6 +44,7 @@ export class Workspace { this.settings = new Settings(join(this.folder, 'settings.json'), defaultSettings) this.history = new History(join(this.folder, '.history')) this.store = new Store(join(this.folder, '.mterm-store')) + this.theme = new Theme(this, join(app.getAppPath(), './resources/theme.css')) } get runtime(): Runtime { @@ -88,6 +91,7 @@ export class Workspace { } await this.settings.load() + await this.theme.load() /** * Load an initial index @@ -172,6 +176,15 @@ export class Workspace { } } + resolve(path: string): string { + let location = resolve(this.folder, path) + if (path.startsWith('~')) { + location = resolveFolderPathForMTERM(path) + } + + return location + } + /** * Applies the updated settings to all the windows in the workspace. * diff --git a/src/renderer/src/assets/runner.css b/src/renderer/src/assets/runner.css index 1e12fc9..b6cf5ea 100644 --- a/src/renderer/src/assets/runner.css +++ b/src/renderer/src/assets/runner.css @@ -12,7 +12,13 @@ html,body { width: 100%; height: 100%; } + + body { + background: rgba(27, 27, 31, 0); +} + +.runner-container { background: rgba(27, 27, 31, 0.89); color: white; } diff --git a/src/renderer/src/runner/runner.tsx b/src/renderer/src/runner/runner.tsx index 10e7085..8800e29 100644 --- a/src/renderer/src/runner/runner.tsx +++ b/src/renderer/src/runner/runner.tsx @@ -266,6 +266,23 @@ export default function Runner(): ReactElement { reloadRuntimesFromBackend().catch((error) => console.error(error)) }, []) + useEffect(() => { + let styleSheet = document.getElementById('theme') + if (!styleSheet) { + styleSheet = document.createElement('style') + styleSheet.setAttribute('id', 'theme') + document.head.appendChild(styleSheet) + } + + window.electron.ipcRenderer + .invoke('runner.theme', runtimeList.find((o) => o.target)?.profile) + .then((theme) => { + styleSheet.innerText = theme + }) + + return () => {} + }, [runtimeList]) + if (!runtime) { return

Loading

} From 1c53ea6efbbbd2de88eec19aec86088f8d0f8841 Mon Sep 17 00:00:00 2001 From: DR Date: Sat, 11 May 2024 08:38:17 -0400 Subject: [PATCH 2/3] fix: add the :theme command --- src/main/framework/runtime-executor.ts | 4 ++- src/main/framework/system-commands/theme.ts | 39 +++++++++++++++++++++ src/main/framework/theme.ts | 11 ++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/main/framework/system-commands/theme.ts diff --git a/src/main/framework/runtime-executor.ts b/src/main/framework/runtime-executor.ts index 6726287..b545bd5 100644 --- a/src/main/framework/runtime-executor.ts +++ b/src/main/framework/runtime-executor.ts @@ -17,6 +17,7 @@ import Edit from './system-commands/edit' import Reset from './system-commands/reset' import Commands from './system-commands/commands' import Restart from './system-commands/restart' +import Theme from './system-commands/theme' const systemCommands: Array<{ command: string @@ -37,7 +38,8 @@ const systemCommands: Array<{ Edit, Reset, Commands, - Restart + Restart, + Theme ] export async function execute(context: ExecuteContext): Promise { // check for system commands diff --git a/src/main/framework/system-commands/theme.ts b/src/main/framework/system-commands/theme.ts new file mode 100644 index 0000000..75fa58c --- /dev/null +++ b/src/main/framework/system-commands/theme.ts @@ -0,0 +1,39 @@ +import { ExecuteContext } from '../execute-context' +import { errorModal } from '../../index' + +export default { + command: ':theme', + alias: [':css'], + async task(context: ExecuteContext, task?: string): Promise { + context.out('') + if (!task) { + if (!context.profile) { + context.out( + 'No profile file to edit, make sure your ~/mterm/settings.json defaultProfile is set' + ) + return + } + + const profileThemeLocation = context.workspace.theme.getProfileThemeLocation(context.profile) + if (!profileThemeLocation) { + context.out( + 'No theme location was found for the default profile, please check your settings!' + ) + return + } + + await context.edit(profileThemeLocation, async () => { + context.out('Saved theme file!\n') + + try { + await context.workspace.theme.load() + } catch (e) { + console.log(e) + await errorModal.showError(e) + } + + context.out('Theme reloaded\n') + }) + } + } +} diff --git a/src/main/framework/theme.ts b/src/main/framework/theme.ts index 926405b..b3adec6 100644 --- a/src/main/framework/theme.ts +++ b/src/main/framework/theme.ts @@ -11,6 +11,17 @@ export class Theme { public defaultThemeLocation: string ) {} + getProfileThemeLocation(profileKey: string): string { + const profiles = this.workspace.settings.get('profiles', DEFAULT_PROFILES) + if (profileKey === 'default') { + profileKey = this.workspace.settings.get('defaultProfile', DEFAULT_PROFILE) + } + let theme = profiles[profileKey]?.theme + if (theme) { + theme = this.workspace.resolve(theme) + } + return theme + } async load(): Promise { this.themes.clear() //get profiles From 6c2676f516b372746c9ff213cf59329e8e40e778 Mon Sep 17 00:00:00 2001 From: DR Date: Sat, 11 May 2024 08:41:27 -0400 Subject: [PATCH 3/3] fix: add readme about theme --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 08136e6..698c2ea 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ ![image](https://github.com/mterm-io/mterm/assets/7341502/6eb47f43-1ab5-41c5-9c0e-5eb61ce575bf) ![image](https://github.com/mterm-io/mterm/assets/7341502/27bcad62-6891-4b49-80b5-e5a17e0562ab) +![image](https://github.com/mterm-io/mterm/assets/7341502/ab7b3a97-98c0-4dda-aa39-af3d6a33d0f7) **mterm** is a cross-platform command-line terminal that proxies the underlying command-line interpreters, such as [powershell](https://learn.microsoft.com/en-us/powershell/), [sh](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) or [wsl](https://ubuntu.com/desktop/wsl). commands are executed in the background and results streamed to the foreground. @@ -68,7 +69,8 @@ in this folder, there are a couple of important files to help configure mterm - - `commands.ts`, used to attach commands to the terminal. see [commands](#commands) - `package.json`, an npm package declaration for use by [commands](#commands) -- `settings`, general runtime settings [settings](#settings) +- `theme.css`, default theme used by the terminal, change this path/file with the `theme` property for profiles +- `settings.json`, general runtime settings [settings](#settings) ### Settings @@ -142,6 +144,7 @@ mterm provided a few system commands to help control the terminal and settings. | `: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 | | `: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 |