diff --git a/docs/docs/config.mdx b/docs/docs/config.mdx
index c97ef839b..b1c7b89c5 100644
--- a/docs/docs/config.mdx
+++ b/docs/docs/config.mdx
@@ -45,6 +45,8 @@ wsh editconfig
| term:localshellopts | string[] | set to pass additional parameters to the term:localshellpath (example: `["-NoLogo"]` for PowerShell will remove the copyright notice) |
| term:copyonselect | bool | set to false to disable terminal copy-on-select |
| term:scrollback | int | size of terminal scrollback buffer, max is 10000 |
+| term:theme | string | preset name of terminal theme to apply by default (default is "default-dark") |
+| term:transparency | float64 | set the background transparency of terminal theme (default 0.5, 0 = not transparent, 1.0 = fully transparent) |
| editor:minimapenabled | bool | set to false to disable editor minimap |
| editor:stickyscrollenabled | bool | enables monaco editor's stickyScroll feature (pinning headers of current context, e.g. class names, method names, etc.), defaults to false |
| editor:wordwrap | bool | set to true to enable word wrapping in the editor (defaults to false) |
diff --git a/docs/docs/keybindings.mdx b/docs/docs/keybindings.mdx
index cfb1569e5..7bd2775d6 100644
--- a/docs/docs/keybindings.mdx
+++ b/docs/docs/keybindings.mdx
@@ -71,6 +71,14 @@ replace "Cmd" with "Alt" (note that "Ctrl" is "Ctrl" on both Mac, Windows, and L
| ---------------- | ------------- |
| | Clear AI Chat |
+## Terminal Keybindings
+
+| Key | Function |
+| ----------------------- | -------------- |
+| | Copy |
+| | Paste |
+| | Clear Terminal |
+
## Customizeable Systemwide Global Hotkey
Wave allows setting a custom global hotkey to focus your most recent window from anywhere in your computer. For more information on this, see [the config docs](./config#customizable-systemwide-global-hotkey).
diff --git a/frontend/app/view/term/term.tsx b/frontend/app/view/term/term.tsx
index a834adfd5..d6ad8eec9 100644
--- a/frontend/app/view/term/term.tsx
+++ b/frontend/app/view/term/term.tsx
@@ -24,6 +24,7 @@ import {
} from "@/store/global";
import * as services from "@/store/services";
import * as keyutil from "@/util/keyutil";
+import { boundNumber } from "@/util/util";
import clsx from "clsx";
import debug from "debug";
import * as jotai from "jotai";
@@ -62,6 +63,7 @@ class TermViewModel implements ViewModel {
vdomToolbarTarget: jotai.PrimitiveAtom;
fontSizeAtom: jotai.Atom;
termThemeNameAtom: jotai.Atom;
+ termTransparencyAtom: jotai.Atom;
noPadding: jotai.PrimitiveAtom;
endIconButtons: jotai.Atom;
shellProcFullStatus: jotai.PrimitiveAtom;
@@ -203,10 +205,17 @@ class TermViewModel implements ViewModel {
return get(getOverrideConfigAtom(this.blockId, "term:theme")) ?? DefaultTermTheme;
});
});
+ this.termTransparencyAtom = useBlockAtom(blockId, "termtransparencyatom", () => {
+ return jotai.atom((get) => {
+ let value = get(getOverrideConfigAtom(this.blockId, "term:transparency")) ?? 0.5;
+ return boundNumber(value, 0, 1);
+ });
+ });
this.blockBg = jotai.atom((get) => {
const fullConfig = get(atoms.fullConfigAtom);
const themeName = get(this.termThemeNameAtom);
- const [_, bgcolor] = computeTheme(fullConfig, themeName);
+ const termTransparency = get(this.termTransparencyAtom);
+ const [_, bgcolor] = computeTheme(fullConfig, themeName, termTransparency);
if (bgcolor != null) {
return { bg: bgcolor };
}
@@ -407,6 +416,11 @@ class TermViewModel implements ViewModel {
event.preventDefault();
event.stopPropagation();
return false;
+ } else if (keyutil.checkKeyPressed(waveEvent, "Cmd:k")) {
+ event.preventDefault();
+ event.stopPropagation();
+ this.termRef.current?.terminal?.clear();
+ return false;
}
const shellProcStatus = globalStore.get(this.shellProcStatus);
if ((shellProcStatus == "done" || shellProcStatus == "init") && keyutil.checkKeyPressed(waveEvent, "Enter")) {
@@ -453,6 +467,7 @@ class TermViewModel implements ViewModel {
const termThemeKeys = Object.keys(termThemes);
const curThemeName = globalStore.get(getBlockMetaKeyAtom(this.blockId, "term:theme"));
const defaultFontSize = globalStore.get(getSettingsKeyAtom("term:fontsize")) ?? 12;
+ const transparencyMeta = globalStore.get(getBlockMetaKeyAtom(this.blockId, "term:transparency"));
const blockData = globalStore.get(this.blockAtom);
const overrideFontSize = blockData?.meta?.["term:fontsize"];
@@ -474,6 +489,41 @@ class TermViewModel implements ViewModel {
checked: curThemeName == null,
click: () => this.setTerminalTheme(null),
});
+ const transparencySubMenu: ContextMenuItem[] = [];
+ transparencySubMenu.push({
+ label: "Default",
+ type: "checkbox",
+ checked: transparencyMeta == null,
+ click: () => {
+ RpcApi.SetMetaCommand(TabRpcClient, {
+ oref: WOS.makeORef("block", this.blockId),
+ meta: { "term:transparency": null },
+ });
+ },
+ });
+ transparencySubMenu.push({
+ label: "Transparent Background",
+ type: "checkbox",
+ checked: transparencyMeta == 0.5,
+ click: () => {
+ RpcApi.SetMetaCommand(TabRpcClient, {
+ oref: WOS.makeORef("block", this.blockId),
+ meta: { "term:transparency": 0.5 },
+ });
+ },
+ });
+ transparencySubMenu.push({
+ label: "No Transparency",
+ type: "checkbox",
+ checked: transparencyMeta == 0,
+ click: () => {
+ RpcApi.SetMetaCommand(TabRpcClient, {
+ oref: WOS.makeORef("block", this.blockId),
+ meta: { "term:transparency": 0 },
+ });
+ },
+ });
+
const fontSizeSubMenu: ContextMenuItem[] = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18].map(
(fontSize: number) => {
return {
@@ -508,6 +558,10 @@ class TermViewModel implements ViewModel {
label: "Font Size",
submenu: fontSizeSubMenu,
});
+ fullMenu.push({
+ label: "Transparency",
+ submenu: transparencySubMenu,
+ });
fullMenu.push({ type: "separator" });
fullMenu.push({
label: "Force Restart Controller",
@@ -734,7 +788,8 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
React.useEffect(() => {
const fullConfig = globalStore.get(atoms.fullConfigAtom);
const termThemeName = globalStore.get(model.termThemeNameAtom);
- const [termTheme, _] = computeTheme(fullConfig, termThemeName);
+ const termTransparency = globalStore.get(model.termTransparencyAtom);
+ const [termTheme, _] = computeTheme(fullConfig, termThemeName, termTransparency);
let termScrollback = 1000;
if (termSettings?.["term:scrollback"]) {
termScrollback = Math.floor(termSettings["term:scrollback"]);
diff --git a/frontend/app/view/term/termtheme.ts b/frontend/app/view/term/termtheme.ts
index 8852ae15a..32937b5be 100644
--- a/frontend/app/view/term/termtheme.ts
+++ b/frontend/app/view/term/termtheme.ts
@@ -17,7 +17,8 @@ interface TermThemeProps {
const TermThemeUpdater = ({ blockId, model, termRef }: TermThemeProps) => {
const fullConfig = useAtomValue(atoms.fullConfigAtom);
const blockTermTheme = useAtomValue(model.termThemeNameAtom);
- const [theme, _] = computeTheme(fullConfig, blockTermTheme);
+ const transparency = useAtomValue(model.termTransparencyAtom);
+ const [theme, _] = computeTheme(fullConfig, blockTermTheme, transparency);
useEffect(() => {
if (termRef.current?.terminal) {
termRef.current.terminal.options.theme = theme;
diff --git a/frontend/app/view/term/termutil.ts b/frontend/app/view/term/termutil.ts
index 1bed0e6d5..6b2eb357c 100644
--- a/frontend/app/view/term/termutil.ts
+++ b/frontend/app/view/term/termutil.ts
@@ -2,14 +2,32 @@
// SPDX-License-Identifier: Apache-2.0
export const DefaultTermTheme = "default-dark";
+import { colord } from "colord";
-// returns (theme, bgcolor)
-function computeTheme(fullConfig: FullConfigType, themeName: string): [TermThemeType, string] {
+function applyTransparencyToColor(hexColor: string, transparency: number): string {
+ const alpha = 1 - transparency; // transparency is already 0-1
+ return colord(hexColor).alpha(alpha).toHex();
+}
+
+// returns (theme, bgcolor, transparency (0 - 1.0))
+function computeTheme(
+ fullConfig: FullConfigType,
+ themeName: string,
+ termTransparency: number
+): [TermThemeType, string] {
let theme: TermThemeType = fullConfig?.termthemes?.[themeName];
if (theme == null) {
theme = fullConfig?.termthemes?.[DefaultTermTheme] || ({} as any);
}
const themeCopy = { ...theme };
+ if (termTransparency != null && termTransparency > 0) {
+ if (themeCopy.background) {
+ themeCopy.background = applyTransparencyToColor(themeCopy.background, termTransparency);
+ }
+ if (themeCopy.selectionBackground) {
+ themeCopy.selectionBackground = applyTransparencyToColor(themeCopy.selectionBackground, termTransparency);
+ }
+ }
let bgcolor = themeCopy.background;
themeCopy.background = "#00000000";
return [themeCopy, bgcolor];
diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts
index 831c9e734..15838e1ab 100644
--- a/frontend/types/gotypes.d.ts
+++ b/frontend/types/gotypes.d.ts
@@ -491,6 +491,7 @@ declare global {
"term:scrollback"?: number;
"term:vdomblockid"?: string;
"term:vdomtoolbarblockid"?: string;
+ "term:transparency"?: number;
"web:zoom"?: number;
"markdown:fontsize"?: number;
"markdown:fixedfontsize"?: number;
@@ -641,6 +642,7 @@ declare global {
"term:localshellopts"?: string[];
"term:scrollback"?: number;
"term:copyonselect"?: boolean;
+ "term:transparency"?: number;
"editor:minimapenabled"?: boolean;
"editor:stickyscrollenabled"?: boolean;
"editor:wordwrap"?: boolean;
diff --git a/pkg/waveobj/metaconsts.go b/pkg/waveobj/metaconsts.go
index 9071bf326..6b65249b6 100644
--- a/pkg/waveobj/metaconsts.go
+++ b/pkg/waveobj/metaconsts.go
@@ -93,6 +93,7 @@ const (
MetaKey_TermScrollback = "term:scrollback"
MetaKey_TermVDomSubBlockId = "term:vdomblockid"
MetaKey_TermVDomToolbarBlockId = "term:vdomtoolbarblockid"
+ MetaKey_TermTransparency = "term:transparency"
MetaKey_WebZoom = "web:zoom"
diff --git a/pkg/waveobj/wtypemeta.go b/pkg/waveobj/wtypemeta.go
index 41f0b7be5..bbf30cffb 100644
--- a/pkg/waveobj/wtypemeta.go
+++ b/pkg/waveobj/wtypemeta.go
@@ -94,6 +94,7 @@ type MetaTSType struct {
TermScrollback *int `json:"term:scrollback,omitempty"`
TermVDomSubBlockId string `json:"term:vdomblockid,omitempty"`
TermVDomToolbarBlockId string `json:"term:vdomtoolbarblockid,omitempty"`
+ TermTransparency *float64 `json:"term:transparency,omitempty"` // default 0.5
WebZoom float64 `json:"web:zoom,omitempty"`
diff --git a/pkg/wconfig/defaultconfig/termthemes.json b/pkg/wconfig/defaultconfig/termthemes.json
index ea8a5f1a0..6ac212f31 100644
--- a/pkg/wconfig/defaultconfig/termthemes.json
+++ b/pkg/wconfig/defaultconfig/termthemes.json
@@ -22,12 +22,38 @@
"cmdtext": "#f0f0f0",
"foreground": "#c1c1c1",
"selectionBackground": "",
- "background": "#00000077",
+ "background": "#000000",
"cursor": ""
},
+ "onedarkpro": {
+ "display:name": "One Dark Pro",
+ "display:order": 2,
+ "background": "#282C34",
+ "foreground": "#ABB2BF",
+ "cursor": "#D7DAE0",
+ "selectionBackground": "#528BFF",
+ "black": "#3F4451",
+ "red": "#E05561",
+ "green": "#8CC265",
+ "yellow": "#D18F52",
+ "blue": "#4AA5F0",
+ "magenta": "#C162DE",
+ "cyan": "#42B3C2",
+ "white": "#D7DAE0",
+ "brightBlack": "#4F5666",
+ "brightRed": "#FF616E",
+ "brightGreen": "#A5E075",
+ "brightYellow": "#F0A45D",
+ "brightBlue": "#4DC4FF",
+ "brightMagenta": "#DE73FF",
+ "brightCyan": "#4CD1E0",
+ "brightWhite": "#E6E6E6",
+ "gray": "#495162",
+ "cmdtext": "#ABB2BF"
+ },
"dracula": {
"display:name": "Dracula",
- "display:order": 2,
+ "display:order": 3,
"black": "#21222C",
"red": "#FF5555",
"green": "#50FA7B",
@@ -53,7 +79,7 @@
},
"monokai": {
"display:name": "Monokai",
- "display:order": 3,
+ "display:order": 4,
"black": "#1B1D1E",
"red": "#F92672",
"green": "#A6E22E",
@@ -79,7 +105,7 @@
},
"campbell": {
"display:name": "Campbell",
- "display:order": 4,
+ "display:order": 5,
"black": "#0C0C0C",
"red": "#C50F1F",
"green": "#13A10E",
@@ -105,7 +131,7 @@
},
"warmyellow": {
"display:name": "Warm Yellow",
- "display:order": 4,
+ "display:order": 6,
"black": "#3C3228",
"red": "#E67E22",
"green": "#A5D6A7",
@@ -127,30 +153,30 @@
"selectionBackground": "#B7950B",
"cursor": "#F9D784"
},
- "onedarkpro": {
- "display:name": "One Dark Pro",
- "display:order": 1.5,
- "background": "#282C34",
- "foreground": "#ABB2BF",
- "cursor": "#D7DAE0",
- "selectionBackground": "#528BFF",
- "black": "#3F4451",
- "red": "#E05561",
- "green": "#8CC265",
- "yellow": "#D18F52",
- "blue": "#4AA5F0",
- "magenta": "#C162DE",
- "cyan": "#42B3C2",
- "white": "#D7DAE0",
- "brightBlack": "#4F5666",
- "brightRed": "#FF616E",
- "brightGreen": "#A5E075",
- "brightYellow": "#F0A45D",
- "brightBlue": "#4DC4FF",
- "brightMagenta": "#DE73FF",
- "brightCyan": "#4CD1E0",
- "brightWhite": "#E6E6E6",
- "gray": "#495162",
- "cmdtext": "#ABB2BF"
+ "rosepine": {
+ "display:name": "Rose Pine",
+ "display:order": 7,
+ "black": "#26233a",
+ "red": "#eb6f92",
+ "green": "#3e8fb0",
+ "yellow": "#f6c177",
+ "blue": "#9ccfd8",
+ "magenta": "#c4a7e7",
+ "cyan": "#ebbcba",
+ "white": "#e0def4",
+ "brightBlack": "#908caa",
+ "brightRed": "#ff8cab",
+ "brightGreen": "#9ccfb0",
+ "brightYellow": "#ffd196",
+ "brightBlue": "#bee6e0",
+ "brightMagenta": "#e2c4ff",
+ "brightCyan": "#ffd1d0",
+ "brightWhite": "#fffaf3",
+ "gray": "#908caa",
+ "cmdtext": "#e0def4",
+ "foreground": "#e0def4",
+ "selectionBackground": "#403d52",
+ "background": "#191724",
+ "cursor": "#524f67"
}
}
diff --git a/pkg/wconfig/metaconsts.go b/pkg/wconfig/metaconsts.go
index a1ddaeb4a..3197b9ad0 100644
--- a/pkg/wconfig/metaconsts.go
+++ b/pkg/wconfig/metaconsts.go
@@ -33,6 +33,7 @@ const (
ConfigKey_TermLocalShellOpts = "term:localshellopts"
ConfigKey_TermScrollback = "term:scrollback"
ConfigKey_TermCopyOnSelect = "term:copyonselect"
+ ConfigKey_TermTransparency = "term:transparency"
ConfigKey_EditorMinimapEnabled = "editor:minimapenabled"
ConfigKey_EditorStickyScrollEnabled = "editor:stickyscrollenabled"
diff --git a/pkg/wconfig/settingsconfig.go b/pkg/wconfig/settingsconfig.go
index b1461eb6f..b65a73a05 100644
--- a/pkg/wconfig/settingsconfig.go
+++ b/pkg/wconfig/settingsconfig.go
@@ -60,6 +60,7 @@ type SettingsType struct {
TermLocalShellOpts []string `json:"term:localshellopts,omitempty"`
TermScrollback *int64 `json:"term:scrollback,omitempty"`
TermCopyOnSelect *bool `json:"term:copyonselect,omitempty"`
+ TermTransparency *float64 `json:"term:transparency,omitempty"`
EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"`
EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"`