transparent terminal themes (#1561)

This commit is contained in:
Mike Sawka 2024-12-19 10:41:28 -08:00 committed by GitHub
parent 8ae6e47d9b
commit eff12635d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 151 additions and 35 deletions

View File

@ -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) |

View File

@ -71,6 +71,14 @@ replace "Cmd" with "Alt" (note that "Ctrl" is "Ctrl" on both Mac, Windows, and L
| ---------------- | ------------- |
| <Kbd k="Cmd:l"/> | Clear AI Chat |
## Terminal Keybindings
| Key | Function |
| ----------------------- | -------------- |
| <Kbd k="Ctrl:Shift:c"/> | Copy |
| <Kbd k="Ctrl:Shift:v"/> | Paste |
| <Kbd k="Cmd:k"/> | 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).

View File

@ -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<VDomTargetToolbar>;
fontSizeAtom: jotai.Atom<number>;
termThemeNameAtom: jotai.Atom<string>;
termTransparencyAtom: jotai.Atom<number>;
noPadding: jotai.PrimitiveAtom<boolean>;
endIconButtons: jotai.Atom<IconButtonDecl[]>;
shellProcFullStatus: jotai.PrimitiveAtom<BlockControllerRuntimeStatus>;
@ -203,10 +205,17 @@ class TermViewModel implements ViewModel {
return get(getOverrideConfigAtom(this.blockId, "term:theme")) ?? DefaultTermTheme;
});
});
this.termTransparencyAtom = useBlockAtom(blockId, "termtransparencyatom", () => {
return jotai.atom<number>((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"]);

View File

@ -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;

View File

@ -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];

View File

@ -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;

View File

@ -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"

View File

@ -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"`

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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"`