Compare commits

...

35 Commits

Author SHA1 Message Date
Red Adaya 80e1e3ce55 re-render terminal when theme is updated 2024-04-15 20:06:34 +08:00
Red Adaya 85886afeb4 fix validation 2024-04-15 20:03:07 +08:00
Red Adaya 567e5b662c reload terminal when styel block is unmounted and mounted 2024-04-15 19:52:09 +08:00
Red Adaya 6b5d2833eb use themes coming from backend 2024-04-15 13:30:12 +08:00
Red Adaya b10910d229 make methods cohesive 2024-04-15 10:02:51 +08:00
Red Adaya a7e6224862 use UpdatePacket for theme changes as well 2024-04-14 23:07:51 +08:00
Red Adaya 5476b45f22 save work 2024-04-14 22:42:00 +08:00
Red Adaya efcd8ad03a save work 2024-04-12 14:57:42 +08:00
Red Adaya 03acda07b5 cleanup 2024-04-11 22:34:26 +08:00
Red Adaya 32342629c8 trigger componentDidUpdate when switching tabs and sessions 2024-04-11 22:27:46 +08:00
Red Adaya d2ddf7bc58 working 2024-04-11 22:02:53 +08:00
Red Adaya 01fb217785 save work 2024-04-11 21:55:55 +08:00
Red Adaya e5d3db7631 save work 2024-04-11 17:35:32 +08:00
Red Adaya c24e99e9ff save work 2024-04-11 14:31:34 +08:00
Red Adaya 65cab8a59a add label to tab settings terminal theme dropdown 2024-04-10 14:58:53 +08:00
Red Adaya c737c9df57 add key and value validation 2024-04-10 10:14:33 +08:00
Red Adaya 17cc4769af fix issue where line in history view doesn't reflect the terminal theme 2024-04-10 09:21:13 +08:00
Red Adaya b4d04c3a57 merge main 2024-04-10 09:09:34 +08:00
Mike Sawka 73e5515e17 when the window gets focus, if our mainview is session (and no modals are open), refocus either the cmdinput or the cmd (#562) 2024-04-09 11:48:34 -07:00
Mike Sawka 6919dbfb5f force our exit trap to always run (for rtnstate commands) (#556)
* add command validation to shellapi.  mock out bash/zsh versions

* implement validate command fn bash and zsh

* test validate command

* change rtnstate commands to always end with a builtin, so we always get our exit trap to run

* simplify the rtnstate modification, don't add the 'wait' (as this is a different problem/feature)

* update schema
2024-04-09 11:33:23 -07:00
Red Adaya 5e4eb738b3 remove file from another branch 2024-04-04 22:02:07 +08:00
Red Adaya 936e9fa7b4 cleanup 2024-04-04 11:15:38 +08:00
Red Adaya af6486e8bb remove old implementation 2024-04-04 11:11:08 +08:00
Red Adaya 1038a08789 revert changes in tabs2.less 2024-04-04 11:09:32 +08:00
Red Adaya 7a377c4a2a loadThemeStyles doesn't have to be async 2024-04-04 11:08:04 +08:00
Red Adaya 8afa62fff2 cleanup 2024-04-04 11:04:34 +08:00
Red Adaya 97595a7718 working 2024-04-04 11:02:27 +08:00
Red Adaya e4d353b5c1 remove selector prop from StyleBlock 2024-04-04 07:34:38 +08:00
Red Adaya 2462c64793 selector should always be :root 2024-04-04 07:33:10 +08:00
Red Adaya b451ee1ef9 change prop from classname to selector 2024-04-04 07:21:26 +08:00
Red Adaya 2c64668382 ability to inherit root styles 2024-04-03 22:22:05 +08:00
Red Adaya de21303fb1 root level 2024-04-03 22:12:48 +08:00
Red Adaya f6cdd53fd4 StyleBlock in elements dir 2024-04-03 20:50:47 +08:00
Red Adaya 840b69d100 reusable StyleBlock component 2024-04-03 20:48:13 +08:00
Red Adaya 683c66f386 save work 2024-04-03 16:37:15 +08:00
31 changed files with 533 additions and 223 deletions
+1
View File
@@ -13,6 +13,7 @@
<link rel="stylesheet" href="dist-dev/waveterm.css" />
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
<link rel="stylesheet" href="public/themes/test.css" />
</head>
<body>
<div id="measure"></div>
+28 -2
View File
@@ -20,8 +20,9 @@ import { MainSideBar } from "./sidebar/main";
import { RightSideBar } from "./sidebar/right";
import { DisconnectedModal, ClientStopModal } from "@/modals";
import { ModalsProvider } from "@/modals/provider";
import { Button } from "@/elements";
import { Button, TermStyleBlock } from "@/elements";
import { ErrorBoundary } from "@/common/error/errorboundary";
import { For } from "tsx-control-statements/components";
import cn from "classnames";
import "./app.less";
@@ -80,6 +81,21 @@ class App extends React.Component<{}, {}> {
rightSidebarModel.saveState(width, false);
}
getSelector(themeKey: string) {
const sessions = GlobalModel.getSessionNames();
const screens = GlobalModel.getScreenNames();
if (themeKey === "main") {
return ":root";
} else if (themeKey in screens) {
return `.main-content [data-screenid="${themeKey}"]`;
} else if (themeKey in sessions) {
return `.main-content [data-sessionid="${themeKey}"]`;
}
return null;
}
render() {
const remotesModel = GlobalModel.remotesModel;
const disconnected = !GlobalModel.ws.open.get() || !GlobalModel.waveSrvRunning.get();
@@ -124,9 +140,12 @@ class App extends React.Component<{}, {}> {
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
const activeMainView = GlobalModel.activeMainView.get();
const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light";
const termTheme = GlobalModel.getTermTheme();
const themeKey = null;
return (
<div
key={"version-" + renderVersion}
key={`version- + ${renderVersion}`}
id="main"
className={cn(
"platform-" + platform,
@@ -151,6 +170,13 @@ class App extends React.Component<{}, {}> {
</div>
</If>
<div ref={this.mainContentRef} className="main-content">
<For index="idx" each="themeKey" of={Object.keys(termTheme)}>
<TermStyleBlock
key={themeKey}
themeName={termTheme[themeKey]}
selector={this.getSelector(themeKey)}
/>
</For>
<MainSideBar parentRef={this.mainContentRef} />
<ErrorBoundary>
<PluginsView />
+5 -6
View File
@@ -76,14 +76,13 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
@boundMethod
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.
const currTheme = GlobalModel.getTermTheme()["global"];
const currTheme = GlobalModel.getTermTheme()["main"];
if (currTheme == theme) {
return;
}
const prtn = GlobalCommandRunner.setGlobalTermTheme(theme, false);
const prtn = GlobalCommandRunner.setMainTermTheme(theme, false);
commandRtnHandler(prtn, this.errorMessage);
}
@@ -207,8 +206,8 @@ 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 currTermTheme = GlobalModel.getTermTheme()["global"] ?? termThemes[0].label;
const termThemes = getTermThemes(GlobalModel.termThemeOptions.get(), "Wave Default");
const currTermTheme = GlobalModel.getTermTheme()["main"] ?? termThemes[0].label;
return (
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
+1 -1
View File
@@ -5,7 +5,7 @@
width: 100%;
border: 2px solid var(--form-element-border-color);
border-radius: 6px;
line-height: 22px;
line-height: 20px;
background: var(--form-element-bg-color);
&.no-label {
+1
View File
@@ -18,4 +18,5 @@ export { Toggle } from "./toggle";
export { Tooltip } from "./tooltip";
export { TabIcon } from "./tabicon";
export { DatePicker } from "./datepicker";
export { TermStyleBlock } from "./termstyleblock";
export { CopyButton } from "./copybutton";
@@ -0,0 +1,95 @@
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import * as React from "react";
import * as mobxReact from "mobx-react";
import { GlobalModel } from "@/models";
const VALID_CSS_VARIABLES = [
"--term-black",
"--term-red",
"--term-green",
"--term-yellow",
"--term-blue",
"--term-magenta",
"--term-cyan",
"--term-white",
"--term-bright-black",
"--term-bright-red",
"--term-bright-green",
"--term-bright-yellow",
"--term-bright-blue",
"--term-bright-magenta",
"--term-bright-cyan",
"--term-bright-white",
"--term-gray",
"--term-cmdtext",
"--term-foreground",
"--term-background",
"--term-selection-background",
"--term-cursor-accent",
];
@mobxReact.observer
class TermStyleBlock extends React.Component<{
themeName: string;
selector: string;
}> {
componentDidMount() {
GlobalModel.bumpTermRenderVersion();
}
componentWillUnmount() {
GlobalModel.bumpTermRenderVersion();
}
componentDidUpdate(prevProps) {
if (prevProps.themeName !== this.props.themeName || prevProps.selector !== this.props.selector) {
GlobalModel.bumpTermRenderVersion();
}
}
isValidCSSColor(color) {
const element = document.createElement("div");
element.style.color = color;
return element.style.color !== "";
}
camelCaseToKebabCase(str) {
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
}
getStyleRules() {
const { selector, themeName } = this.props;
const termThemeOptions = GlobalModel.getTermThemeOptions();
if (!termThemeOptions) {
return null;
}
const theme = termThemeOptions[themeName];
if (!theme) {
return null;
}
const styleProperties = Object.entries(theme)
.filter(([key, value]) => {
const cssVarName = `--term-${this.camelCaseToKebabCase(key)}`;
return VALID_CSS_VARIABLES.includes(cssVarName) && this.isValidCSSColor(value);
})
.map(([key, value]) => `--term-${key}: ${value};`)
.join(" ");
if (!styleProperties) {
return null;
}
return `${selector} { ${styleProperties} }`;
}
render() {
const styleRules = this.getStyleRules();
if (!styleRules) {
return null;
}
return <style>{styleRules}</style>;
}
}
export { TermStyleBlock };
+1 -5
View File
@@ -160,6 +160,7 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
if (currTheme == theme) {
return;
}
GlobalModel.setTermThemeScope(this.screenId, `.main-content [data-screenid="${this.screenId}"]`);
const prtn = GlobalCommandRunner.setScreenTermTheme(this.screenId, theme, false);
commandRtnHandler(prtn, this.errorMessage);
}
@@ -175,13 +176,8 @@ class ScreenSettingsModal extends React.Component<{}, {}> {
if (screen == null) {
return null;
}
let color: string = null;
let icon: string = null;
let index: number = 0;
const curRemote = GlobalModel.getRemote(GlobalModel.getActiveScreen().getCurRemoteInstance().remoteid);
const termThemes = getTermThemes(GlobalModel.termThemes);
const currTermTheme = GlobalModel.getTermTheme()[this.screenId] ?? termThemes[0].label;
return (
<Modal className="screen-settings-modal">
<Modal.Header onClose={this.closeModal} title={`Tab Settings (${screen.name.get()})`} />
+1 -1
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 (
-5
View File
@@ -649,11 +649,6 @@ class LineContainer extends React.Component<{ historyId: string; width: number }
this.line = hvm.getLineById(this.historyItem.lineid);
}
componentDidMount(): void {
GlobalModel.bumpTermRenderVersion();
GlobalModel.termThemeSrcEl.set(null);
}
@boundMethod
handleHeightChange(lineNum: number, newHeight: number, oldHeight: number): void {
return;
+11 -3
View File
@@ -12,7 +12,7 @@ import { debounce } from "throttle-debounce";
import dayjs from "dayjs";
import { GlobalCommandRunner, ForwardLineContainer, GlobalModel, ScreenLines, Screen, Session } from "@/models";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { Button } from "@/elements";
import { Button, TermStyleBlock } from "@/elements";
import { Line } from "@/app/line/linecomps";
import { LinesView } from "@/app/line/linesview";
import * as util from "@/util/util";
@@ -184,10 +184,18 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, {
winWidth = screenWidth - realWidth + "px";
sidebarWidth = realWidth - MagicLayout.ScreenSidebarWidthPadding + "px";
}
const termRenderVersion = GlobalModel.termRenderVersion.get();
return (
<div className="screen-view" data-screenid={screen.screenId} ref={this.screenViewRef}>
<div className="screen-view" id={screen.screenId} data-screenid={screen.screenId} ref={this.screenViewRef}>
{/* <TermStyleBlock
scope="screen"
termTheme={termTheme}
themeSrcEl={this.screenViewRef.current}
themeKey={screen.screenId}
/> */}
<ScreenWindowView
key={screen.screenId + ":" + fontSize + ":" + dprStr}
key={screen.screenId + ":" + fontSize + ":" + dprStr + ":" + termRenderVersion}
session={session}
screen={screen}
width={winWidth}
+4
View File
@@ -187,4 +187,8 @@
}
}
}
.terminal-theme-dropdown {
width: 412px;
}
}
+6 -62
View File
@@ -15,7 +15,7 @@ import { ScreenTabs } from "./screen/tabs";
import { ErrorBoundary } from "@/common/error/errorboundary";
import { boundMethod } from "autobind-decorator";
import type { Screen } from "@/models";
import { Button } from "@/elements";
import { Button, TermStyleBlock } from "@/elements";
import { commandRtnHandler } from "@/util/util";
import { getTermThemes } from "@/util/themeutil";
import { Dropdown } from "@/elements/dropdown";
@@ -140,6 +140,7 @@ class TabSettings extends React.Component<{ screen: Screen }, {}> {
@boundMethod
handleChangeTermTheme(theme: string): void {
console.log("theme", theme);
const { screenId } = this.props.screen;
const currTheme = GlobalModel.getTermTheme()[screenId];
if (currTheme == theme) {
@@ -152,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">
@@ -175,6 +176,7 @@ class TabSettings extends React.Component<{ screen: Screen }, {}> {
<If condition={termThemes.length > 0}>
<div className="newtab-section">
<Dropdown
label="Terminal Theme"
className="terminal-theme-dropdown"
options={termThemes}
defaultValue={currTermTheme}
@@ -208,59 +210,6 @@ class TabSettings extends React.Component<{ screen: Screen }, {}> {
@mobxReact.observer
class WorkspaceView extends React.Component<{}, {}> {
sessionRef = React.createRef<HTMLDivElement>();
theme: string;
themeReactionDisposer: mobx.IReactionDisposer;
componentDidMount() {
this.setupThemeReaction();
}
componentDidUpdate() {
this.setupThemeReaction();
}
setupThemeReaction() {
if (this.themeReactionDisposer) {
this.themeReactionDisposer();
}
// This handles session and screen-level terminal theming.
// Ideally, screen-level theming should be handled in the inner-level component, but
// the frequent mounting and unmounting of the screen view make it really difficult to work.
this.themeReactionDisposer = mobx.reaction(
() => {
return {
termTheme: GlobalModel.getTermTheme(),
session: GlobalModel.getActiveSession(),
screen: GlobalModel.getActiveScreen(),
};
},
({ termTheme, session, screen }) => {
let currTheme = termTheme[session.sessionId];
if (termTheme[screen.screenId]) {
currTheme = termTheme[screen.screenId];
}
if (session && currTheme !== this.theme && this.sessionRef.current) {
const reset = currTheme == null;
const theme = currTheme ?? this.theme;
const themeSrcEl = reset ? null : this.sessionRef.current;
const rtn = GlobalModel.updateTermTheme(this.sessionRef.current, theme, reset);
rtn.then(() => {
GlobalModel.termThemeSrcEl.set(themeSrcEl);
}).then(() => {
GlobalModel.bumpTermRenderVersion();
});
this.theme = currTheme;
}
}
);
}
componentWillUnmount() {
if (this.themeReactionDisposer) {
this.themeReactionDisposer();
}
}
@boundMethod
toggleTabSettings() {
@@ -277,15 +226,14 @@ class WorkspaceView extends React.Component<{}, {}> {
sessionId = session.sessionId;
activeScreen = session.getActiveScreen();
}
const isHidden = GlobalModel.activeMainView.get() != "session";
const mainSidebarModel = GlobalModel.mainSidebarModel;
const termRenderVersion = GlobalModel.termRenderVersion.get();
const showTabSettings = GlobalModel.tabSettingsOpen.get();
return (
<div
ref={this.sessionRef}
className={cn("mainview", "session-view", { "is-hidden": isHidden })}
id={sessionId}
data-sessionid={sessionId}
style={{
width: `${window.innerWidth - mainSidebarModel.getWidth()}px`,
@@ -307,11 +255,7 @@ class WorkspaceView extends React.Component<{}, {}> {
</div>
</If>
<ErrorBoundary key="eb">
<ScreenView
key={`screenview-${sessionId}-${termRenderVersion}`}
session={session}
screen={activeScreen}
/>
<ScreenView key={`screenview-${sessionId}`} session={session} screen={activeScreen} />
<If condition={activeScreen != null}>
<CmdInput key={"cmdinput-" + sessionId} />
</If>
+1 -2
View File
@@ -377,7 +377,7 @@ class CommandRunner {
return GlobalModel.submitCommand("client", "set", null, kwargs, interactive);
}
setGlobalTermTheme(theme: string, interactive: boolean): Promise<CommandRtnType> {
setMainTermTheme(theme: string, interactive: boolean): Promise<CommandRtnType> {
let kwargs = {
nohist: "1",
termtheme: theme,
@@ -386,7 +386,6 @@ class CommandRunner {
}
setSessionTermTheme(sessionId: string, name: string, interactive: boolean): Promise<CommandRtnType> {
console.log("setSessionTermTheme-------");
let kwargs = {
nohist: "1",
id: sessionId,
+4
View File
@@ -24,6 +24,10 @@ class ModalsModel {
})();
callback && callback();
}
hasOpenModals(): boolean {
return this.store.length > 0;
}
}
export { ModalsModel };
+21 -77
View File
@@ -32,7 +32,7 @@ import { MainSidebarModel } from "./mainsidebar";
import { RightSidebarModel } from "./rightsidebar";
import { Screen } from "./screen";
import { Cmd } from "./cmd";
import { GlobalCommandRunner } from "./global";
import { GlobalCommandRunner, GlobalModel } from "./global";
import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure";
import type { TermWrap } from "@/plugins/terminal/term";
import * as util from "@/util/util";
@@ -135,22 +135,16 @@ class Model {
renderVersion: OV<number> = mobx.observable.box(0, {
name: "renderVersion",
});
appUpdateStatus = mobx.observable.box(getApi().getAppUpdateStatus(), {
name: "appUpdateStatus",
});
termThemes: OMap<string, string> = mobx.observable.array([], {
termThemeOptions: OV<TermThemeOptionsType> = mobx.observable.box(null, {
name: "terminalThemes",
deep: false,
});
termThemeSrcEl: OV<HTMLElement> = mobx.observable.box(null, {
name: "termThemeSrcEl",
});
termRenderVersion: OV<number> = mobx.observable.box(0, {
name: "termRenderVersion",
});
currGlobalTermTheme: string;
private constructor() {
this.clientId = getApi().getId();
@@ -164,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);
@@ -203,6 +196,7 @@ class Model {
getApi().onNativeThemeUpdated(this.onNativeThemeUpdated.bind(this));
document.addEventListener("keydown", this.docKeyDownHandler.bind(this));
document.addEventListener("selectionchange", this.docSelectionChangeHandler.bind(this));
window.addEventListener("focus", this.windowFocus.bind(this));
setTimeout(() => this.getClientDataLoop(1), 10);
this.lineHeightEnv = {
// defaults
@@ -229,40 +223,10 @@ 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);
});
}
updateTermTheme(element: HTMLElement, themeFileName: string, reset: boolean) {
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) => {
Object.keys(themeVars).forEach((key) => {
if (reset) {
this.resetStyleVar(element, `--term-${key}`);
} else {
this.resetStyleVar(element, `--term-${key}`);
this.setStyleVar(element, `--term-${key}`, themeVars[key]);
}
});
})
.catch((error) => {
console.error(`error applying theme: ${themeFileName}`, error);
});
windowFocus(): void {
if (this.activeMainView.get() == "session" && !this.modalsModel.hasOpenModals()) {
this.refocus();
}
}
bumpTermRenderVersion() {
@@ -919,6 +883,17 @@ class Model {
}
}
setTermThemeOptions(termThemeOptions: TermThemeOptionsType) {
mobx.action(() => {
this.termThemeOptions.set(termThemeOptions);
})();
GlobalModel.bumpTermRenderVersion();
}
getTermThemeOptions(): TermThemeOptionsType {
return this.termThemeOptions.get();
}
updateScreenStatusIndicators(screenStatusIndicators: ScreenStatusIndicatorUpdateType[]) {
for (const update of screenStatusIndicators) {
this.getScreenById_single(update.screenid)?.setStatusIndicator(update.status);
@@ -1038,6 +1013,8 @@ class Model {
} else if (update.userinputrequest != null) {
const userInputRequest: UserInputRequest = update.userinputrequest;
this.modalsModel.pushModal(appconst.USER_INPUT, userInputRequest);
} else if (update.termthemeoptions != null) {
this.setTermThemeOptions(update.termthemeoptions);
} else if (update.sessiontombstone != null || update.screentombstone != null) {
// nothing (ignore)
} else {
@@ -1084,7 +1061,7 @@ class Model {
const activeScreen = this.getActiveScreen();
if (activeScreen?.getCurRemoteInstance() != null) {
setTimeout(() => {
GlobalCommandRunner.syncShellState();
// GlobalCommandRunner.syncShellState();
}, 100);
}
}
@@ -1291,9 +1268,6 @@ class Model {
newTheme = appconst.DefaultTheme;
}
const themeUpdated = newTheme != this.getThemeSource();
const oldTermTheme = this.getTermTheme();
const newTermTheme = clientData?.feopts?.termtheme;
const ttUpdated = this.termThemeUpdated(newTermTheme, oldTermTheme);
mobx.action(() => {
this.clientData.set(clientData);
})();
@@ -1314,36 +1288,6 @@ class Model {
getApi().setNativeThemeSource(newTheme);
this.bumpRenderVersion();
}
// Only for global terminal theme. For session and screen terminal theme,
// they are handled in workspace view.
if (newTermTheme) {
const el = document.documentElement;
const globaltt = newTermTheme["global"] ?? this.currGlobalTermTheme;
const reset = newTermTheme["global"] == null;
if (globaltt) {
const rtn = this.updateTermTheme(el, globaltt, reset);
rtn.then(() => {
if (ttUpdated) {
this.bumpTermRenderVersion();
}
});
this.currGlobalTermTheme = globaltt;
}
}
}
termThemeUpdated(newTermTheme, oldTermTheme) {
for (const key in oldTermTheme) {
if (!(key in newTermTheme)) {
return true;
}
}
for (const key in newTermTheme) {
if (!oldTermTheme[key] || oldTermTheme[key] !== newTermTheme[key]) {
return true;
}
}
return false;
}
submitCommandPacket(cmdPk: FeCmdPacketType, interactive: boolean): Promise<CommandRtnType> {
+5 -5
View File
@@ -51,10 +51,11 @@ type TermWrapOpts = {
onUpdateContentHeight: (termContext: RendererContext, height: number) => void;
};
function getThemeFromCSSVars(themeSrcEl: HTMLElement): ITheme {
function getThemeFromCSSVars(el: Element): ITheme {
const theme: ITheme = {};
const tse = themeSrcEl ?? document.documentElement;
let rootStyle = getComputedStyle(tse);
// console.log("el======", el);
const rootStyle = getComputedStyle(el);
// console.log("rootStyle======", rootStyle.getPropertyValue("--term-foreground"));
theme.foreground = rootStyle.getPropertyValue("--term-foreground");
theme.background = rootStyle.getPropertyValue("--term-background");
theme.black = rootStyle.getPropertyValue("--term-black");
@@ -131,8 +132,7 @@ class TermWrap {
let cols = windowWidthToCols(opts.winSize.width, opts.fontSize);
this.termSize = { rows: opts.termOpts.rows, cols: cols };
}
const themeSrcEl = GlobalModel.termThemeSrcEl.get();
let theme = getThemeFromCSSVars(themeSrcEl);
let theme = getThemeFromCSSVars(this.connectedElem);
this.terminal = new Terminal({
rows: this.termSize.rows,
cols: this.termSize.cols,
+8 -1
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 = {
+5 -2
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,
+17
View File
@@ -6,6 +6,7 @@ package shellapi
import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"runtime"
@@ -266,6 +267,22 @@ func (bashShellApi) MakeShellStateDiff(oldState *packet.ShellState, oldStateHash
return rtn, nil
}
func (bashShellApi) ValidateCommandSyntax(cmdStr string) error {
ctx, cancelFn := context.WithTimeout(context.Background(), ValidateTimeout)
defer cancelFn()
cmd := exec.CommandContext(ctx, GetLocalBashPath(), "-n", "-c", cmdStr)
output, err := cmd.CombinedOutput()
if err == nil {
return nil
}
errStr := utilfn.GetFirstLine(string(output))
errStr = strings.TrimPrefix(errStr, "bash: -c: ")
if len(errStr) == 0 {
return errors.New("bash syntax error")
}
return errors.New(errStr)
}
func (bashShellApi) ApplyShellStateDiff(oldState *packet.ShellState, diff *packet.ShellStateDiff) (*packet.ShellState, error) {
if oldState == nil {
return nil, fmt.Errorf("cannot apply diff, oldState is nil")
+9 -2
View File
@@ -214,9 +214,16 @@ func bashParseDeclareOutput(state *packet.ShellState, declareBytes []byte, pvarB
firstParseErr = err
}
}
if decl != nil && !BashNoStoreVarNames[decl.Name] {
declMap[decl.Name] = decl
if decl == nil {
continue
}
if BashNoStoreVarNames[decl.Name] {
continue
}
if strings.HasPrefix(decl.Name, "_wavetemp_") {
continue
}
declMap[decl.Name] = decl
}
pvarMap := parseExtVarOutput(pvarBytes, "", "")
utilfn.CombineMaps(declMap, pvarMap)

Some files were not shown because too many files have changed in this diff Show More