Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add realtime editing and theme overrides #116

Merged
merged 3 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 |
Expand Down
129 changes: 129 additions & 0 deletions resources/theme.css
Original file line number Diff line number Diff line change
@@ -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 {
}
4 changes: 4 additions & 0 deletions src/main/framework/runtime-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ export function attach({ app, workspace }: BootstrapContext): void {
)
})

ipcMain.handle('runner.theme', async (_, profile): Promise<string> => {
return workspace.theme.get(profile)
})

ipcMain.handle('runtime.kill', async (_, commandId, runtimeId): Promise<boolean> => {
const runtime = workspace.runtimes.find((r) => r.id === runtimeId)
if (!runtime) {
Expand Down
4 changes: 3 additions & 1 deletion src/main/framework/runtime-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -37,7 +38,8 @@ const systemCommands: Array<{
Edit,
Reset,
Commands,
Restart
Restart,
Theme
]
export async function execute(context: ExecuteContext): Promise<void | boolean> {
// check for system commands
Expand Down
2 changes: 1 addition & 1 deletion src/main/framework/system-commands/reload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ExecuteContext } from '../execute-context'

async function reload(context: ExecuteContext): Promise<void> {
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')
Expand Down
39 changes: 39 additions & 0 deletions src/main/framework/system-commands/theme.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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')
})
}
}
}
76 changes: 76 additions & 0 deletions src/main/framework/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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<string, string> = new Map()
constructor(
public workspace: Workspace,
public defaultThemeLocation: string
) {}

getProfileThemeLocation(profileKey: string): string {
const profiles = this.workspace.settings.get<ProfileMap>('profiles', DEFAULT_PROFILES)
if (profileKey === 'default') {
profileKey = this.workspace.settings.get<string>('defaultProfile', DEFAULT_PROFILE)
}
let theme = profiles[profileKey]?.theme
if (theme) {
theme = this.workspace.resolve(theme)
}
return theme
}
async load(): Promise<void> {
this.themes.clear()
//get profiles
const profiles = this.workspace.settings.get<ProfileMap>('profiles', DEFAULT_PROFILES)
const cache = {}

const getDefaultTheme = async (): Promise<string> => {
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<string>('defaultProfile', DEFAULT_PROFILE)
}
return this.themes.get(profileKey) || ''
}
}
13 changes: 13 additions & 0 deletions src/main/framework/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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[] = []
Expand All @@ -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 {
Expand Down Expand Up @@ -88,6 +91,7 @@ export class Workspace {
}

await this.settings.load()
await this.theme.load()

/**
* Load an initial index
Expand Down Expand Up @@ -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.
*
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/src/assets/runner.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
17 changes: 17 additions & 0 deletions src/renderer/src/runner/runner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <p>Loading</p>
}
Expand Down
Loading