use themes coming from backend

This commit is contained in:
Red Adaya 2024-04-15 13:30:12 +08:00
parent b10910d229
commit 6b5d2833eb
9 changed files with 57 additions and 109 deletions

View File

@ -1,27 +0,0 @@
/* Copyright 2024, Command Line Inc.
SPDX-License-Identifier: Apache-2.0 */
.main-content {
--term-black: #757575;
--term-red: #cc685c;
--term-green: #76c266;
--term-yellow: #cbca9b;
--term-blue: #85aacb;
--term-magenta: #cc72ca;
--term-cyan: #74a7cb;
--term-white: #c1c1c1;
--term-bright-black: #727272;
--term-bright-red: #cc9d97;
--term-bright-green: #a3dd97;
--term-bright-yellow: #cbcaaa;
--term-bright-blue: #9ab6cb;
--term-bright-magenta: #cc8ecb;
--term-bright-cyan: #b7b8cb;
--term-bright-white: #f0f0f0;
--term-gray: #8b918a;
--term-cmdtext: #f0f0f0;
--term-foreground: red;
--term-background: #00000000;
}

View File

@ -82,7 +82,6 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
if (currTheme == theme) {
return;
}
const prtn = GlobalCommandRunner.setMainTermTheme(theme, false);
commandRtnHandler(prtn, this.errorMessage);
}
@ -207,7 +206,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
const curFontSize = GlobalModel.getTermFontSize();
const curFontFamily = GlobalModel.getTermFontFamily();
const curTheme = GlobalModel.getThemeSource();
const termThemes = getTermThemes(GlobalModel.termThemes, "Wave Default");
const termThemes = getTermThemes(GlobalModel.termThemeOptions.get(), "Wave Default");
const currTermTheme = GlobalModel.getTermTheme()["main"] ?? termThemes[0].label;
return (

View File

@ -3,7 +3,6 @@
import * as React from "react";
import * as mobxReact from "mobx-react";
import * as mobx from "mobx";
import { GlobalModel } from "@/models";
const VALID_CSS_VARIABLES = [
@ -36,8 +35,9 @@ class TermStyleBlock extends React.Component<{
themeName: string;
selector: string;
}> {
styleRules: OV<string> = mobx.observable.box("", { name: "StyleBlock-styleRules" });
injectedStyleElement: HTMLStyleElement | null = null;
componentDidUpdate(): void {
GlobalModel.bumpTermRenderVersion();
}
isValidCSSColor(color) {
const element = document.createElement("div");
@ -45,7 +45,7 @@ class TermStyleBlock extends React.Component<{
return element.style.color !== "";
}
isValidTermCSSVariable(key, value) {
isValidTermCSSVariable(key) {
const cssVarName = `--term-${key}`;
return VALID_CSS_VARIABLES.includes(cssVarName);
}
@ -54,50 +54,34 @@ class TermStyleBlock extends React.Component<{
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
}
removeInjectedStyle() {
if (this.injectedStyleElement) {
document.head.removeChild(this.injectedStyleElement);
this.injectedStyleElement = null;
getStyleRules() {
const { selector, themeName } = this.props;
const termThemeOptions = GlobalModel.getTermThemeOptions();
if (!termThemeOptions) {
return null;
}
const theme = termThemeOptions[themeName];
if (!theme) {
return null;
}
loadThemeStyles(selector: string, theme: string) {
// Inject new style element
GlobalModel.getTermThemeJson(theme)
.then((termThemeJson) => {
if (termThemeJson && typeof termThemeJson === "object") {
const styleProperties = Object.entries(termThemeJson)
.filter(([key, value]) => {
const cssVarName = `--term-${this.camelCaseToKebabCase(key)}`;
return VALID_CSS_VARIABLES.includes(cssVarName) && this.isValidCSSColor(value);
})
const styleProperties = Object.entries(theme)
// .filter(([key, value]) => {
// const cssVarName = `--term-${this.camelCaseToKebabCase(key)}`;
// return this.isValidTermCSSVariable(cssVarName) && this.isValidCSSColor(value);
// })
.map(([key, value]) => `--term-${key}: ${value};`)
.join(" ");
const styleRules = `${selector} { ${styleProperties} }`;
mobx.action(() => {
this.styleRules.set(styleRules);
})();
console.log("loaded theme styles:", this.styleRules.get());
} else {
console.error("termThemeJson is not an object:", termThemeJson);
if (!styleProperties) {
return null;
}
})
.then(() => {
GlobalModel.bumpTermRenderVersion();
})
.catch((error) => {
console.error("error loading theme styles:", error);
});
return `${selector} { ${styleProperties} }`;
}
render() {
const { themeName, selector } = this.props;
console.log("themeName:", themeName, "selector:", selector);
const styleRules = this.getStyleRules();
return this.styleRules.get() ? <style>{this.styleRules.get()}</style> : null;
return styleRules ? <style>{styleRules}</style> : null;
}
}

View File

@ -99,7 +99,7 @@ class SessionSettingsModal extends React.Component<{}, {}> {
if (this.session == null) {
return null;
}
const termThemes = getTermThemes(GlobalModel.termThemes);
const termThemes = getTermThemes(GlobalModel.termThemeOptions.get());
const currTermTheme = GlobalModel.getTermTheme()[this.sessionId] ?? termThemes[0].label;
return (

View File

@ -153,7 +153,7 @@ class TabSettings extends React.Component<{ screen: Screen }, {}> {
render() {
const { screen } = this.props;
const rptr = screen.curRemote.get();
const termThemes = getTermThemes(GlobalModel.termThemes);
const termThemes = getTermThemes(GlobalModel.termThemeOptions.get());
const currTermTheme = GlobalModel.getTermTheme()[screen.screenId] ?? termThemes[0].label;
return (
<div className="newtab-container">

View File

@ -135,11 +135,10 @@ class Model {
renderVersion: OV<number> = mobx.observable.box(0, {
name: "renderVersion",
});
appUpdateStatus = mobx.observable.box(getApi().getAppUpdateStatus(), {
name: "appUpdateStatus",
});
termThemes: OMap<string, OMap<string, string>> = mobx.observable.array([], {
termThemeOptions: OV<TermThemeOptionsType> = mobx.observable.box(null, {
name: "terminalThemes",
deep: false,
});
@ -159,7 +158,6 @@ class Model {
this.ws.reconnect();
this.keybindManager = new KeybindManager(this);
this.readConfigKeybindings();
this.fetchTerminalThemes();
this.initSystemKeybindings();
this.initAppKeybindings();
this.inputModel = new InputModel(this);
@ -231,35 +229,6 @@ class Model {
}
}
fetchTerminalThemes() {
const url = new URL(this.getBaseHostPort() + "/config/terminal-themes");
fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() })
.then((resp) => {
if (resp.status == 404) {
return [];
} else if (!resp.ok) {
util.handleNotOkResp(resp, url);
}
return resp.json();
})
.then((themes) => {
const tt = themes.map((theme) => theme.name.split(".")[0]);
this.termThemes.replace(tt);
});
}
getTermThemeJson(themeFileName: string) {
const url = new URL(this.getBaseHostPort() + `/config/terminal-themes/${themeFileName}.json`);
return fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() })
.then((resp) => resp.json())
.then((themeVars: TermThemeType) => {
return themeVars;
})
.catch((error) => {
console.error(`error applying theme: ${themeFileName}`, error);
});
}
bumpTermRenderVersion() {
mobx.action(() => {
this.termRenderVersion.set(this.termRenderVersion.get() + 1);
@ -914,6 +883,16 @@ class Model {
}
}
setTermThemeOptions(termThemeOptions: TermThemeOptionsType) {
mobx.action(() => {
this.termThemeOptions.set(termThemeOptions);
})();
}
getTermThemeOptions(): TermThemeOptionsType {
return this.termThemeOptions.get();
}
updateScreenStatusIndicators(screenStatusIndicators: ScreenStatusIndicatorUpdateType[]) {
for (const update of screenStatusIndicators) {
this.getScreenById_single(update.screenid)?.setStatusIndicator(update.status);
@ -1034,6 +1013,9 @@ class Model {
} else if (update.userinputrequest != null) {
const userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (update.termthemeoptions != null) {
console.log("got termthemeoptions==============", update.termthemeoptions);
this.setTermThemeOptions(update.termthemeoptions);
} else if (update.sessiontombstone != null || update.screentombstone != null) {
// nothing (ignore)
} else {

View File

@ -379,6 +379,13 @@ declare global {
userinputrequest?: UserInputRequest;
screentombstone?: any;
sessiontombstone?: any;
termthemeoptions?: TermThemeOptionsType;
};
type TermThemeOptionsType = {
[key: string]: {
[innerKey: string]: string;
};
};
type HistoryViewDataType = {
@ -582,7 +589,7 @@ declare global {
termfontsize: number;
termfontfamily: string;
theme: NativeThemeSource;
termtheme: TermThemeType;
termthemeconfig: TermThemeType;
};
type ConfirmFlagsType = {

View File

@ -1,10 +1,13 @@
function getTermThemes(termThemes: string[], noneLabel = "Inherit"): DropdownItem[] {
function getTermThemes(termThemeOptions: string[], noneLabel = "Inherit"): DropdownItem[] {
if (!termThemeOptions) {
return [];
}
const tt: DropdownItem[] = [];
tt.push({
label: noneLabel,
value: null,
});
for (const themeName of termThemes) {
for (const themeName of Object.keys(termThemeOptions)) {
tt.push({
label: themeName,
value: themeName,

View File

@ -16,7 +16,7 @@ import (
)
const (
TermThemesTypeStr = "termthemes"
TermThemesTypeStr = "termthemeoptions"
TermThemeDir = "config/terminal-themes/"
)