This commit is contained in:
Red Adaya 2024-04-04 11:02:27 +08:00
parent e4d353b5c1
commit 97595a7718
11 changed files with 102 additions and 31 deletions

View File

@ -125,6 +125,7 @@ class App extends React.Component<{}, {}> {
const activeMainView = GlobalModel.activeMainView.get(); const activeMainView = GlobalModel.activeMainView.get();
const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light"; const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light";
const termTheme = GlobalModel.getTermTheme(); const termTheme = GlobalModel.getTermTheme();
return ( return (
<div <div
key={`version- + ${renderVersion}`} key={`version- + ${renderVersion}`}
@ -152,7 +153,12 @@ class App extends React.Component<{}, {}> {
</div> </div>
</If> </If>
<div ref={this.mainContentRef} className="main-content"> <div ref={this.mainContentRef} className="main-content">
<StyleBlock termTheme={termTheme} themeSrcEl={this.mainContentRef.current} themeKey="global" /> <StyleBlock
scope="main"
termTheme={termTheme}
themeSrcEl={this.mainContentRef.current}
themeKey="main"
/>
<MainSideBar parentRef={this.mainContentRef} /> <MainSideBar parentRef={this.mainContentRef} />
<ErrorBoundary> <ErrorBoundary>
<PluginsView /> <PluginsView />

View File

@ -76,14 +76,14 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
@boundMethod @boundMethod
handleChangeTermTheme(theme: string): void { handleChangeTermTheme(theme: string): void {
// For global terminal theme, the key is global, otherwise it's either // For main terminal theme, the key is main, otherwise it's either
// sessionId or screenId. // sessionId or screenId.
const currTheme = GlobalModel.getTermTheme()["global"]; const currTheme = GlobalModel.getTermTheme()["main"];
if (currTheme == theme) { if (currTheme == theme) {
return; return;
} }
const prtn = GlobalCommandRunner.setGlobalTermTheme(theme, false); const prtn = GlobalCommandRunner.setMainTermTheme(theme, false);
commandRtnHandler(prtn, this.errorMessage); commandRtnHandler(prtn, this.errorMessage);
} }
@ -208,7 +208,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
const curFontFamily = GlobalModel.getTermFontFamily(); const curFontFamily = GlobalModel.getTermFontFamily();
const curTheme = GlobalModel.getThemeSource(); const curTheme = GlobalModel.getThemeSource();
const termThemes = getTermThemes(GlobalModel.termThemes, "Wave Default"); const termThemes = getTermThemes(GlobalModel.termThemes, "Wave Default");
const currTermTheme = GlobalModel.getTermTheme()["global"] ?? termThemes[0].label; const currTermTheme = GlobalModel.getTermTheme()["main"] ?? termThemes[0].label;
return ( return (
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}> <MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>

View File

@ -9,14 +9,13 @@ import { isBlank } from "@/util/util";
@mobxReact.observer @mobxReact.observer
class StyleBlock extends React.Component< class StyleBlock extends React.Component<
{ themeSrcEl: HTMLElement; themeKey: string; termTheme: TermThemeType }, { scope: "main" | "session" | "screen"; themeSrcEl: HTMLElement; themeKey: string; termTheme: TermThemeType },
{ styleRules: string } { styleRules: string }
> { > {
styleRules: OV<string> = mobx.observable.box("", { name: "StyleBlock-styleRules" }); styleRules: OV<string> = mobx.observable.box("", { name: "StyleBlock-styleRules" });
theme: string; theme: string;
componentDidMount(): void { componentDidMount(): void {
GlobalModel.termThemeSrcEl.set(this.props.themeSrcEl);
this.loadThemeStyles(); this.loadThemeStyles();
} }
@ -26,13 +25,10 @@ class StyleBlock extends React.Component<
if (themeKey !== prevProps.themeKey || currTheme !== this.theme) { if (themeKey !== prevProps.themeKey || currTheme !== this.theme) {
this.loadThemeStyles(); this.loadThemeStyles();
} }
if (this.props.themeSrcEl !== prevProps.themeSrcEl) {
GlobalModel.termThemeSrcEl.set(this.props.themeSrcEl);
}
} }
async loadThemeStyles() { async loadThemeStyles() {
const { themeKey, termTheme } = this.props; const { themeKey, termTheme, scope } = this.props;
const currTheme = termTheme[themeKey]; const currTheme = termTheme[themeKey];
if (currTheme && currTheme !== this.theme && currTheme) { if (currTheme && currTheme !== this.theme && currTheme) {
@ -43,7 +39,10 @@ class StyleBlock extends React.Component<
.map(([key, value]) => `--term-${key}: ${value};`) .map(([key, value]) => `--term-${key}: ${value};`)
.join(" "); .join(" ");
this.styleRules.set(`:root { ${styleProperties} }`); mobx.action(() => {
this.styleRules.set(`:root { ${styleProperties} }`);
GlobalModel.termThemeSrcEls.set(scope, this.props.themeSrcEl);
})();
GlobalModel.bumpTermRenderVersion(); GlobalModel.bumpTermRenderVersion();
this.theme = currTheme; this.theme = currTheme;
} else { } else {
@ -53,7 +52,12 @@ class StyleBlock extends React.Component<
console.error("error loading theme styles:", error); console.error("error loading theme styles:", error);
}); });
} else { } else {
this.styleRules.set(""); mobx.action(() => {
this.styleRules.set("");
GlobalModel.termThemeSrcEls.set(scope, null);
})();
this.theme = currTheme;
GlobalModel.bumpTermRenderVersion();
} }
} }

View File

@ -185,11 +185,18 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, {
sidebarWidth = realWidth - MagicLayout.ScreenSidebarWidthPadding + "px"; sidebarWidth = realWidth - MagicLayout.ScreenSidebarWidthPadding + "px";
} }
const termTheme = GlobalModel.getTermTheme(); const termTheme = GlobalModel.getTermTheme();
const termRenderVersion = GlobalModel.termRenderVersion.get();
return ( return (
<div className="screen-view" data-screenid={screen.screenId} ref={this.screenViewRef}> <div className="screen-view" data-screenid={screen.screenId} ref={this.screenViewRef}>
{/* <StyleBlock termTheme={termTheme} themeSrcEl={} /> */} <StyleBlock
scope="screen"
termTheme={termTheme}
themeSrcEl={this.screenViewRef.current}
themeKey={screen.screenId}
/>
<ScreenWindowView <ScreenWindowView
key={screen.screenId + ":" + fontSize + ":" + dprStr} key={screen.screenId + ":" + fontSize + ":" + dprStr + ":" + termRenderVersion}
session={session} session={session}
screen={screen} screen={screen}
width={winWidth} width={winWidth}

View File

@ -0,0 +1,45 @@
.screen-tabs-container {
position: relative;
height: var(--screentabs-height);
.screen-tabs-container-inner {
position: relative; // Needed for absolute positioning of child tabs
white-space: nowrap;
height: 100%;
margin-right: 42px;
overflow: hidden;
}
.new-screen {
width: 42px;
height: 40px;
background-color: var(--app-bg-color);
cursor: pointer;
position: absolute;
top: 50%;
transform: translateY(-50%);
font-size: 20px;
text-align: center;
line-height: 38px;
user-select: none;
}
}
// This ensures the tab bar does not collide with the floating logo. The floating logo sits above the sidebar when it is not collapsed, so no additional margin is needed in that case.
// More margin is given on macOS to account for the traffic light buttons
#app.platform-darwin.mainsidebar-collapsed .screen-tabs-container {
margin-left: var(--floating-logo-width-darwin);
}
#app:not(.platform-darwin).mainsidebar-collapsed .screen-tabs-container {
margin-left: var(--floating-logo-width);
}
// This ensures the tab bar does not collide with the right sidebar triggers.
#app.platform-darwin.rightsidebar-collapsed .screen-tabs-container {
margin-right: var(--floating-right-sidebar-triggers-width-darwin);
}
#app:not(.platform-darwin).rightsidebar-collapsed .screen-tabs-container {
margin-left: var(--floating-right-sidebar-triggers-width);
}

View File

@ -287,7 +287,6 @@ class WorkspaceView extends React.Component<{}, {}> {
const mainSidebarModel = GlobalModel.mainSidebarModel; const mainSidebarModel = GlobalModel.mainSidebarModel;
const showTabSettings = GlobalModel.tabSettingsOpen.get(); const showTabSettings = GlobalModel.tabSettingsOpen.get();
const termTheme = GlobalModel.getTermTheme(); const termTheme = GlobalModel.getTermTheme();
const termRenderVersion = GlobalModel.termRenderVersion.get();
return ( return (
<div <div
@ -300,6 +299,7 @@ class WorkspaceView extends React.Component<{}, {}> {
> >
<If condition={session != null}> <If condition={session != null}>
<StyleBlock <StyleBlock
scope="session"
themeSrcEl={this.sessionRef.current} themeSrcEl={this.sessionRef.current}
themeKey={session.sessionId} themeKey={session.sessionId}
termTheme={termTheme} termTheme={termTheme}
@ -321,11 +321,7 @@ class WorkspaceView extends React.Component<{}, {}> {
</div> </div>
</If> </If>
<ErrorBoundary key="eb"> <ErrorBoundary key="eb">
<ScreenView <ScreenView key={`screenview-${sessionId}`} session={session} screen={activeScreen} />
key={`screenview-${sessionId}-${termRenderVersion}`}
session={session}
screen={activeScreen}
/>
<div className="cmdinput-height-placeholder" style={{ height: cmdInputHeight }}></div> <div className="cmdinput-height-placeholder" style={{ height: cmdInputHeight }}></div>
<If condition={activeScreen != null}> <If condition={activeScreen != null}>
<CmdInput key={"cmdinput-" + sessionId} /> <CmdInput key={"cmdinput-" + sessionId} />

View File

@ -377,7 +377,7 @@ class CommandRunner {
return GlobalModel.submitCommand("client", "set", null, kwargs, interactive); return GlobalModel.submitCommand("client", "set", null, kwargs, interactive);
} }
setGlobalTermTheme(theme: string, interactive: boolean): Promise<CommandRtnType> { setMainTermTheme(theme: string, interactive: boolean): Promise<CommandRtnType> {
let kwargs = { let kwargs = {
nohist: "1", nohist: "1",
termtheme: theme, termtheme: theme,
@ -386,7 +386,6 @@ class CommandRunner {
} }
setSessionTermTheme(sessionId: string, name: string, interactive: boolean): Promise<CommandRtnType> { setSessionTermTheme(sessionId: string, name: string, interactive: boolean): Promise<CommandRtnType> {
console.log("setSessionTermTheme-------");
let kwargs = { let kwargs = {
nohist: "1", nohist: "1",
id: sessionId, id: sessionId,

View File

@ -144,13 +144,15 @@ class Model {
name: "terminalThemes", name: "terminalThemes",
deep: false, deep: false,
}); });
termThemeSrcEl: OV<HTMLElement> = mobx.observable.box(null, { termThemeSrcEls: OMap<string, HTMLElement> = mobx.observable.map(
name: "termThemeSrcEl", {},
}); {
name: "termThemeSrcEls",
}
);
termRenderVersion: OV<number> = mobx.observable.box(0, { termRenderVersion: OV<number> = mobx.observable.box(0, {
name: "termRenderVersion", name: "termRenderVersion",
}); });
currGlobalTermTheme: string;
private constructor() { private constructor() {
this.clientId = getApi().getId(); this.clientId = getApi().getId();
@ -214,6 +216,16 @@ class Model {
}; };
} }
getThemeSrcElForScope() {
const scopes = ["screen", "session", "main"];
for (let scope of scopes) {
if (this.termThemeSrcEls.get(scope)) {
return this.termThemeSrcEls.get(scope);
}
}
return document.documentElement;
}
readConfigKeybindings() { readConfigKeybindings() {
const url = new URL(this.getBaseHostPort() + "/config/keybindings.json"); const url = new URL(this.getBaseHostPort() + "/config/keybindings.json");
let prtn = fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() }); let prtn = fetch(url, { method: "get", body: null, headers: this.getFetchHeaders() });
@ -1331,7 +1343,7 @@ class Model {
// Only for global terminal theme. For session and screen terminal theme, // Only for global terminal theme. For session and screen terminal theme,
// they are handled in workspace view. // they are handled in workspace view.
if (ttUpdated) { if (ttUpdated) {
this.bumpTermRenderVersion(); // this.bumpTermRenderVersion();
// const el = document.documentElement; // const el = document.documentElement;
// const globaltt = newTermTheme["global"] ?? this.currGlobalTermTheme; // const globaltt = newTermTheme["global"] ?? this.currGlobalTermTheme;
// const reset = newTermTheme["global"] == null; // const reset = newTermTheme["global"] == null;

View File

@ -169,6 +169,7 @@ class RemotesModel {
} }
createTermWrap(elem: HTMLElement): void { createTermWrap(elem: HTMLElement): void {
console.log("createTermWrap", elem);
this.disposeTerm(); this.disposeTerm();
let remoteId = this.selectedRemoteId.get(); let remoteId = this.selectedRemoteId.get();
if (remoteId == null) { if (remoteId == null) {

View File

@ -53,6 +53,7 @@ type TermWrapOpts = {
function getThemeFromCSSVars(themeSrcEl: HTMLElement): ITheme { function getThemeFromCSSVars(themeSrcEl: HTMLElement): ITheme {
const theme: ITheme = {}; const theme: ITheme = {};
console.log("themeSrcEl", themeSrcEl);
const tse = themeSrcEl ?? document.documentElement; const tse = themeSrcEl ?? document.documentElement;
let rootStyle = getComputedStyle(tse); let rootStyle = getComputedStyle(tse);
theme.foreground = rootStyle.getPropertyValue("--term-foreground"); theme.foreground = rootStyle.getPropertyValue("--term-foreground");
@ -131,7 +132,7 @@ class TermWrap {
let cols = windowWidthToCols(opts.winSize.width, opts.fontSize); let cols = windowWidthToCols(opts.winSize.width, opts.fontSize);
this.termSize = { rows: opts.termOpts.rows, cols: cols }; this.termSize = { rows: opts.termOpts.rows, cols: cols };
} }
const themeSrcEl = GlobalModel.termThemeSrcEl.get(); const themeSrcEl = GlobalModel.getThemeSrcElForScope();
let theme = getThemeFromCSSVars(themeSrcEl); let theme = getThemeFromCSSVars(themeSrcEl);
this.terminal = new Terminal({ this.terminal = new Terminal({
rows: this.termSize.rows, rows: this.termSize.rows,

View File

@ -5811,9 +5811,9 @@ func ClientSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sc
feOpts.TermTheme = make(map[string]string) feOpts.TermTheme = make(map[string]string)
} }
if termthemeStr == "" { if termthemeStr == "" {
delete(feOpts.TermTheme, "global") delete(feOpts.TermTheme, "main")
} else { } else {
feOpts.TermTheme["global"] = termthemeStr feOpts.TermTheme["main"] = termthemeStr
} }
err = sstore.UpdateClientFeOpts(ctx, feOpts) err = sstore.UpdateClientFeOpts(ctx, feOpts)
if err != nil { if err != nil {