mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
transparent terminal themes (#1561)
This commit is contained in:
parent
8ae6e47d9b
commit
eff12635d7
@ -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) |
|
||||
|
@ -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).
|
||||
|
@ -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"]);
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
2
frontend/types/gotypes.d.ts
vendored
2
frontend/types/gotypes.d.ts
vendored
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"`
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"`
|
||||
|
Loading…
Reference in New Issue
Block a user