terminal themes, use terminal bg to style the full block

This commit is contained in:
sawka 2024-07-30 19:52:50 -07:00
parent 9afb5a7bc1
commit d68d32f96c
6 changed files with 124 additions and 19 deletions

View File

@ -264,6 +264,7 @@ const BlockFrame_Default_Component = ({
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText);
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton);
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons);
const customBg = util.useAtomValueSafe(viewModel.blockBg);
if (preview) {
isFocused = true;
}
@ -381,6 +382,16 @@ const BlockFrame_Default_Component = ({
layoutModel?.onMagnifyToggle();
}
const innerStyle: React.CSSProperties = {};
if (customBg?.bg != null) {
innerStyle.background = customBg.bg;
if (customBg["bg:opacity"] != null) {
innerStyle.opacity = customBg["bg:opacity"];
}
if (customBg["bg:blendmode"] != null) {
innerStyle.backgroundBlendMode = customBg["bg:blendmode"];
}
}
return (
<div
className={clsx(
@ -397,7 +408,7 @@ const BlockFrame_Default_Component = ({
style={style}
>
<div className="block-mask"></div>
<div className="block-frame-default-inner">
<div className="block-frame-default-inner" style={innerStyle}>
<div
className="block-frame-default-header"
ref={layoutModel?.dragHandleRef}

View File

@ -6,31 +6,17 @@ import { VDomView } from "@/app/view/term/vdom";
import { WOS, atoms, getEventORefSubject, globalStore, useBlockAtom, useSettingsAtom } from "@/store/global";
import * as services from "@/store/services";
import * as keyutil from "@/util/keyutil";
import * as util from "@/util/util";
import clsx from "clsx";
import { produce } from "immer";
import * as jotai from "jotai";
import * as React from "react";
import { TermStickers } from "./termsticker";
import { TermThemeUpdater } from "./termtheme";
import { computeTheme } from "./termutil";
import { TermWrap } from "./termwrap";
import "public/xterm.css";
function computeTheme(settings: SettingsConfigType, themeName: string) {
let defaultThemeName = "default-dark";
themeName = themeName ?? "default-dark";
const defaultTheme: TermThemeType = settings?.termthemes?.[defaultThemeName] || ({} as any);
const theme: TermThemeType = settings?.termthemes?.[themeName] || ({} as any);
const combinedTheme = { ...defaultTheme };
for (const key in theme) {
if (!util.isBlank(theme[key])) {
combinedTheme[key] = theme[key];
}
}
return combinedTheme;
}
const keyMap = {
Enter: "\r",
Backspace: "\x7f",
@ -130,6 +116,7 @@ class TermViewModel {
viewIcon: jotai.Atom<string>;
viewText: jotai.Atom<string>;
viewName: jotai.Atom<string>;
blockBg: jotai.Atom<MetaType>;
constructor(blockId: string) {
this.blockId = blockId;
@ -152,6 +139,15 @@ class TermViewModel {
const blockData = get(this.blockAtom);
return blockData?.meta?.title ?? "";
});
this.blockBg = jotai.atom((get) => {
const blockData = get(this.blockAtom);
const settings = globalStore.get(atoms.settingsConfigAtom);
const theme = computeTheme(settings, blockData?.meta?.["term:theme"]);
if (theme != null && theme.background != null) {
return { bg: theme.background };
}
return null;
});
}
giveFocus(): boolean {
@ -169,6 +165,23 @@ class TermViewModel {
}
return false;
}
setTerminalTheme(themeName: string) {
WshServer.SetMetaCommand({ oref: WOS.makeORef("block", this.blockId), meta: { "term:theme": themeName } });
}
getSettingsMenuItems(): ContextMenuItem[] {
return [
{
label: "Themes",
submenu: [
{ label: "Default Dark", click: () => this.setTerminalTheme("default") },
{ label: "Dracula", click: () => this.setTerminalTheme("dracula") },
{ label: "Campbell", click: () => this.setTerminalTheme("campbell") },
],
},
];
}
}
function makeTerminalModel(blockId: string): TermViewModel {
@ -220,11 +233,13 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
}
const settings = globalStore.get(atoms.settingsConfigAtom);
const termTheme = computeTheme(settings, blockData?.meta?.["term:theme"]);
const themeCopy = { ...termTheme };
themeCopy.background = "#00000000";
const termWrap = new TermWrap(
blockId,
connectElemRef.current,
{
theme: termTheme,
theme: themeCopy,
fontSize: termSettings?.fontsize ?? 12,
fontFamily: termSettings?.fontfamily ?? "Hack",
drawBoldTextInBrightColors: false,

View File

@ -29,7 +29,9 @@ const TermThemeUpdater = ({ blockId, termRef }: TermThemeProps) => {
}
}
if (termRef.current?.terminal) {
termRef.current.terminal.options.theme = combinedTheme;
let themeCopy = { ...combinedTheme };
themeCopy.background = "#00000000";
termRef.current.terminal.options.theme = themeCopy;
}
}, [defaultTheme, theme]);

View File

@ -0,0 +1,20 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as util from "@/util/util";
function computeTheme(settings: SettingsConfigType, themeName: string): TermThemeType {
let defaultThemeName = "default-dark";
themeName = themeName ?? "default-dark";
const defaultTheme: TermThemeType = settings?.termthemes?.[defaultThemeName] || ({} as any);
const theme: TermThemeType = settings?.termthemes?.[themeName] || ({} as any);
const combinedTheme = { ...defaultTheme };
for (const key in theme) {
if (!util.isBlank(theme[key])) {
combinedTheme[key] = theme[key];
}
}
return combinedTheme;
}
export { computeTheme };

View File

@ -166,6 +166,7 @@ declare global {
viewText?: jotai.Atom<string | HeaderElem[]>;
preIconButton?: jotai.Atom<HeaderIconButton>;
endIconButtons?: jotai.Atom<HeaderIconButton[]>;
blockBg?: jotai.Atom<MetaType>;
onBack?: () => void;
onForward?: () => void;

View File

@ -125,10 +125,60 @@ var DefaultTermDarkTheme = TermThemeType{
CmdText: "#f0f0f0",
Foreground: "#c1c1c1",
SelectionBackground: "",
Background: "#00000000",
Background: "#00000077",
CursorAccent: "",
}
var DraculaTheme = TermThemeType{
Black: "#21222C", // AnsiBlack
Red: "#FF5555", // AnsiRed
Green: "#50FA7B", // AnsiGreen
Yellow: "#F1FA8C", // AnsiYellow
Blue: "#BD93F9", // AnsiBlue
Magenta: "#FF79C6", // AnsiMagenta
Cyan: "#8BE9FD", // AnsiCyan
White: "#F8F8F2", // AnsiWhite
BrightBlack: "#6272A4", // AnsiBrightBlack
BrightRed: "#FF6E6E", // AnsiBrightRed
BrightGreen: "#69FF94", // AnsiBrightGreen
BrightYellow: "#FFFFA5", // AnsiBrightYellow
BrightBlue: "#D6ACFF", // AnsiBrightBlue
BrightMagenta: "#FF92DF", // AnsiBrightMagenta
BrightCyan: "#A4FFFF", // AnsiBrightCyan
BrightWhite: "#FFFFFF", // AnsiBrightWhite
Gray: "#6272A4", // Comment or closest approximation
CmdText: "#F8F8F2", // Foreground
Foreground: "#F8F8F2", // Foreground
SelectionBackground: "#44475a", // Selection
Background: "#282a36", // Background
CursorAccent: "#f8f8f2", // Foreground (used for cursor accent)
}
var CampbellTheme = TermThemeType{
Black: "#0C0C0C", // Black
Red: "#C50F1F", // Red
Green: "#13A10E", // Green
Yellow: "#C19C00", // Yellow
Blue: "#0037DA", // Blue
Magenta: "#881798", // Purple (used as Magenta)
Cyan: "#3A96DD", // Cyan
White: "#CCCCCC", // White
BrightBlack: "#767676", // BrightBlack
BrightRed: "#E74856", // BrightRed
BrightGreen: "#16C60C", // BrightGreen
BrightYellow: "#F9F1A5", // BrightYellow
BrightBlue: "#3B78FF", // BrightBlue
BrightMagenta: "#B4009E", // BrightPurple (used as BrightMagenta)
BrightCyan: "#61D6D6", // BrightCyan
BrightWhite: "#F2F2F2", // BrightWhite
Gray: "#767676", // BrightBlack or closest approximation
CmdText: "#CCCCCC", // Foreground
Foreground: "#CCCCCC", // Foreground
SelectionBackground: "#3A96DD", // Cyan (chosen for selection background)
Background: "#0C0C0C", // Background
CursorAccent: "#CCCCCC", // Foreground (used for cursor accent)
}
func applyDefaultSettings(settings *SettingsConfigType) {
defaultMimeTypes := map[string]MimeTypeConfigType{
"audio": {Icon: "file-audio"},
@ -229,4 +279,10 @@ func applyDefaultSettings(settings *SettingsConfigType) {
if _, found := settings.TermThemes["default-dark"]; !found {
settings.TermThemes["default-dark"] = DefaultTermDarkTheme
}
if _, found := settings.TermThemes["dracula"]; !found {
settings.TermThemes["dracula"] = DraculaTheme
}
if _, found := settings.TermThemes["campbell"]; !found {
settings.TermThemes["campbell"] = CampbellTheme
}
}