mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-01 23:21:59 +01:00
Support os-native theming (#495)
* save work * Add native theme support * update index * update var name * remove comment * fix code setting * bump render version on change * remove themeutil
This commit is contained in:
parent
69b9ce33b3
commit
6065ee931f
@ -5,14 +5,14 @@
|
|||||||
<base href="../" />
|
<base href="../" />
|
||||||
<script charset="UTF-8" src="dist-dev/waveterm.js"></script>
|
<script charset="UTF-8" src="dist-dev/waveterm.js"></script>
|
||||||
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/solid.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/solid.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css" />
|
||||||
<link rel="stylesheet" href="dist-dev/waveterm.css" />
|
<link rel="stylesheet" href="dist-dev/waveterm.css" />
|
||||||
|
|
||||||
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/default.css">
|
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="measure"></div>
|
<div id="measure"></div>
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
<base href="../" />
|
<base href="../" />
|
||||||
<script charset="UTF-8" src="dist/waveterm.js"></script>
|
<script charset="UTF-8" src="dist/waveterm.js"></script>
|
||||||
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
<link rel="stylesheet" href="public/bulma-0.9.4.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/fontawesome.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/brands.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/solid.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/solid.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/sharp-solid.min.css" />
|
||||||
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css">
|
<link rel="stylesheet" href="public/fontawesome/css/sharp-regular.min.css" />
|
||||||
<link rel="stylesheet" href="dist/waveterm.css" />
|
<link rel="stylesheet" href="dist/waveterm.css" />
|
||||||
|
|
||||||
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/default.css" />
|
<link rel="stylesheet" id="theme-stylesheet" href="public/themes/themequery.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="measure"></div>
|
<div id="measure"></div>
|
||||||
|
3
public/themes/themequery.css
Normal file
3
public/themes/themequery.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@import url("./default.css") screen;
|
||||||
|
|
||||||
|
@import url("./light.css") screen and (prefers-color-scheme: light);
|
@ -8,7 +8,7 @@ import { boundMethod } from "autobind-decorator";
|
|||||||
import { If } from "tsx-control-statements/components";
|
import { If } from "tsx-control-statements/components";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
import { GlobalCommandRunner, GlobalModel } from "@/models";
|
import { GlobalModel } from "@/models";
|
||||||
import { isBlank } from "@/util/util";
|
import { isBlank } from "@/util/util";
|
||||||
import { WorkspaceView } from "./workspace/workspaceview";
|
import { WorkspaceView } from "./workspace/workspaceview";
|
||||||
import { PluginsView } from "./pluginsview/pluginsview";
|
import { PluginsView } from "./pluginsview/pluginsview";
|
||||||
@ -123,7 +123,7 @@ class App extends React.Component<{}, {}> {
|
|||||||
const mainSidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
|
const mainSidebarCollapsed = GlobalModel.mainSidebarModel.getCollapsed();
|
||||||
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
|
const rightSidebarCollapsed = GlobalModel.rightSidebarModel.getCollapsed();
|
||||||
const activeMainView = GlobalModel.activeMainView.get();
|
const activeMainView = GlobalModel.activeMainView.get();
|
||||||
const lightDarkClass = GlobalModel.isThemeDark() ? "is-dark" : "is-light";
|
const lightDarkClass = GlobalModel.isDarkTheme.get() ? "is-dark" : "is-light";
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={"version-" + renderVersion}
|
key={"version-" + renderVersion}
|
||||||
|
@ -64,11 +64,12 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleChangeTheme(theme: string): void {
|
handleChangeThemeSource(themeSource: NativeThemeSource): void {
|
||||||
if (GlobalModel.getTheme() == theme) {
|
if (GlobalModel.getThemeSource() == themeSource) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prtn = GlobalCommandRunner.setTheme(theme, false);
|
const prtn = GlobalCommandRunner.setTheme(themeSource, false);
|
||||||
|
getApi().setNativeThemeSource(themeSource);
|
||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,11 +112,12 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
return availableFontFamilies;
|
return availableFontFamilies;
|
||||||
}
|
}
|
||||||
|
|
||||||
getThemes(): DropdownItem[] {
|
getThemeSources(): DropdownItem[] {
|
||||||
const themes: DropdownItem[] = [];
|
const themeSources: DropdownItem[] = [];
|
||||||
themes.push({ label: "Dark", value: "dark" });
|
themeSources.push({ label: "Dark", value: "dark" });
|
||||||
themes.push({ label: "Light", value: "light" });
|
themeSources.push({ label: "Light", value: "light" });
|
||||||
return themes;
|
themeSources.push({ label: "System", value: "system" });
|
||||||
|
return themeSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
@ -190,7 +192,7 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
);
|
);
|
||||||
const curFontSize = GlobalModel.getTermFontSize();
|
const curFontSize = GlobalModel.getTermFontSize();
|
||||||
const curFontFamily = GlobalModel.getTermFontFamily();
|
const curFontFamily = GlobalModel.getTermFontFamily();
|
||||||
const curTheme = GlobalModel.getTheme();
|
const curTheme = GlobalModel.getThemeSource();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
|
<MainView className="clientsettings-view" title="Client Settings" onClose={this.handleClose}>
|
||||||
@ -225,9 +227,9 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
<div className="settings-input">
|
<div className="settings-input">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
className="theme-dropdown"
|
className="theme-dropdown"
|
||||||
options={this.getThemes()}
|
options={this.getThemeSources()}
|
||||||
defaultValue={curTheme}
|
defaultValue={curTheme}
|
||||||
onChange={this.handleChangeTheme}
|
onChange={this.handleChangeThemeSource}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -539,6 +539,25 @@ electron.ipcMain.on("get-last-logs", (event, numberOfLines) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-shouldusedarkcolors", (event) => {
|
||||||
|
event.returnValue = electron.nativeTheme.shouldUseDarkColors;
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-nativethemesource", (event) => {
|
||||||
|
event.returnValue = electron.nativeTheme.themeSource;
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("set-nativethemesource", (event, themeSource: "system" | "light" | "dark") => {
|
||||||
|
electron.nativeTheme.themeSource = themeSource;
|
||||||
|
event.returnValue = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.nativeTheme.on("updated", () => {
|
||||||
|
if (MainWindow != null) {
|
||||||
|
MainWindow.webContents.send("nativetheme-updated");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function readLastLinesOfFile(filePath: string, lineCount: number) {
|
function readLastLinesOfFile(filePath: string, lineCount: number) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
child_process.exec(`tail -n ${lineCount} "${filePath}"`, (err, stdout, stderr) => {
|
child_process.exec(`tail -n ${lineCount} "${filePath}"`, (err, stdout, stderr) => {
|
||||||
|
@ -13,6 +13,10 @@ contextBridge.exposeInMainWorld("api", {
|
|||||||
ipcRenderer.once("last-logs", (event, data) => callback(data));
|
ipcRenderer.once("last-logs", (event, data) => callback(data));
|
||||||
},
|
},
|
||||||
getInitialTermFontFamily: () => ipcRenderer.sendSync("get-initial-termfontfamily"),
|
getInitialTermFontFamily: () => ipcRenderer.sendSync("get-initial-termfontfamily"),
|
||||||
|
getShouldUseDarkColors: () => ipcRenderer.sendSync("get-shouldusedarkcolors"),
|
||||||
|
getNativeThemeSource: () => ipcRenderer.sendSync("get-nativethemesource"),
|
||||||
|
setNativeThemeSource: (source) => ipcRenderer.send("set-nativethemesource", source),
|
||||||
|
onNativeThemeUpdated: (callback) => ipcRenderer.on("nativetheme-updated", callback),
|
||||||
restartWaveSrv: () => ipcRenderer.sendSync("restart-server"),
|
restartWaveSrv: () => ipcRenderer.sendSync("restart-server"),
|
||||||
reloadWindow: () => ipcRenderer.sendSync("reload-window"),
|
reloadWindow: () => ipcRenderer.sendSync("reload-window"),
|
||||||
reregisterGlobalShortcut: (shortcut) => ipcRenderer.sendSync("reregister-global-shortcut", shortcut),
|
reregisterGlobalShortcut: (shortcut) => ipcRenderer.sendSync("reregister-global-shortcut", shortcut),
|
||||||
|
@ -365,7 +365,7 @@ class CommandRunner {
|
|||||||
return GlobalModel.submitCommand("client", "set", null, kwargs, interactive);
|
return GlobalModel.submitCommand("client", "set", null, kwargs, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme(theme: string, interactive: boolean): Promise<CommandRtnType> {
|
setTheme(theme: NativeThemeSource, interactive: boolean): Promise<CommandRtnType> {
|
||||||
let kwargs = {
|
let kwargs = {
|
||||||
nohist: "1",
|
nohist: "1",
|
||||||
theme: theme,
|
theme: theme,
|
||||||
|
@ -13,12 +13,11 @@ import {
|
|||||||
isModKeyPress,
|
isModKeyPress,
|
||||||
isBlank,
|
isBlank,
|
||||||
} from "@/util/util";
|
} from "@/util/util";
|
||||||
import { loadTheme } from "@/util/themeutil";
|
|
||||||
import { WSControl } from "./ws";
|
import { WSControl } from "./ws";
|
||||||
import { cmdStatusIsRunning } from "@/app/line/lineutil";
|
import { cmdStatusIsRunning } from "@/app/line/lineutil";
|
||||||
import * as appconst from "@/app/appconst";
|
import * as appconst from "@/app/appconst";
|
||||||
import { remotePtrToString, cmdPacketString } from "@/util/modelutil";
|
import { remotePtrToString, cmdPacketString } from "@/util/modelutil";
|
||||||
import { KeybindManager, checkKeyPressed, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
|
import { KeybindManager, adaptFromReactOrNativeKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
|
||||||
import { Session } from "./session";
|
import { Session } from "./session";
|
||||||
import { ScreenLines } from "./screenlines";
|
import { ScreenLines } from "./screenlines";
|
||||||
import { InputModel } from "./input";
|
import { InputModel } from "./input";
|
||||||
@ -118,6 +117,9 @@ class Model {
|
|||||||
modalsModel: ModalsModel;
|
modalsModel: ModalsModel;
|
||||||
mainSidebarModel: MainSidebarModel;
|
mainSidebarModel: MainSidebarModel;
|
||||||
rightSidebarModel: RightSidebarModel;
|
rightSidebarModel: RightSidebarModel;
|
||||||
|
isDarkTheme: OV<boolean> = mobx.observable.box(getApi().getShouldUseDarkColors(), {
|
||||||
|
name: "isDarkTheme",
|
||||||
|
});
|
||||||
clientData: OV<ClientDataType> = mobx.observable.box(null, {
|
clientData: OV<ClientDataType> = mobx.observable.box(null, {
|
||||||
name: "clientData",
|
name: "clientData",
|
||||||
});
|
});
|
||||||
@ -181,6 +183,7 @@ class Model {
|
|||||||
getApi().onMenuItemAbout(this.onMenuItemAbout.bind(this));
|
getApi().onMenuItemAbout(this.onMenuItemAbout.bind(this));
|
||||||
getApi().onWaveSrvStatusChange(this.onWaveSrvStatusChange.bind(this));
|
getApi().onWaveSrvStatusChange(this.onWaveSrvStatusChange.bind(this));
|
||||||
getApi().onAppUpdateStatus(this.onAppUpdateStatus.bind(this));
|
getApi().onAppUpdateStatus(this.onAppUpdateStatus.bind(this));
|
||||||
|
getApi().onNativeThemeUpdated(this.onNativeThemeUpdated.bind(this));
|
||||||
document.addEventListener("keydown", this.docKeyDownHandler.bind(this));
|
document.addEventListener("keydown", this.docKeyDownHandler.bind(this));
|
||||||
document.addEventListener("selectionchange", this.docSelectionChangeHandler.bind(this));
|
document.addEventListener("selectionchange", this.docSelectionChangeHandler.bind(this));
|
||||||
setTimeout(() => this.getClientDataLoop(1), 10);
|
setTimeout(() => this.getClientDataLoop(1), 10);
|
||||||
@ -389,18 +392,19 @@ class Model {
|
|||||||
return ff;
|
return ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTheme(): string {
|
getThemeSource(): NativeThemeSource {
|
||||||
let cdata = this.clientData.get();
|
return getApi().getNativeThemeSource();
|
||||||
let theme = cdata?.feopts?.theme;
|
|
||||||
if (theme == null) {
|
|
||||||
theme = appconst.DefaultTheme;
|
|
||||||
}
|
|
||||||
return theme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isThemeDark(): boolean {
|
onNativeThemeUpdated(): void {
|
||||||
let cdata = this.clientData.get();
|
console.log("native theme updated");
|
||||||
return cdata?.feopts?.theme != "light";
|
const isDark = getApi().getShouldUseDarkColors();
|
||||||
|
if (isDark != this.isDarkTheme.get()) {
|
||||||
|
mobx.action(() => {
|
||||||
|
this.isDarkTheme.set(isDark);
|
||||||
|
this.bumpRenderVersion();
|
||||||
|
})();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTermFontSize(): number {
|
getTermFontSize(): number {
|
||||||
@ -1197,7 +1201,7 @@ class Model {
|
|||||||
if (newTheme == null) {
|
if (newTheme == null) {
|
||||||
newTheme = appconst.DefaultTheme;
|
newTheme = appconst.DefaultTheme;
|
||||||
}
|
}
|
||||||
const themeUpdated = newTheme != this.getTheme();
|
const themeUpdated = newTheme != this.getThemeSource();
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
this.clientData.set(clientData);
|
this.clientData.set(clientData);
|
||||||
})();
|
})();
|
||||||
@ -1215,7 +1219,7 @@ class Model {
|
|||||||
this.updateTermFontSizeVars();
|
this.updateTermFontSizeVars();
|
||||||
}
|
}
|
||||||
if (themeUpdated) {
|
if (themeUpdated) {
|
||||||
loadTheme(newTheme);
|
getApi().setNativeThemeSource(newTheme);
|
||||||
this.bumpRenderVersion();
|
this.bumpRenderVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,11 @@ import Editor, { Monaco } from "@monaco-editor/react";
|
|||||||
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
|
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { If } from "tsx-control-statements/components";
|
import { If } from "tsx-control-statements/components";
|
||||||
import { Markdown } from "@/elements";
|
import { Markdown, Button } from "@/elements";
|
||||||
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
import { GlobalModel, GlobalCommandRunner } from "@/models";
|
||||||
import Split from "react-split-it";
|
import Split from "react-split-it";
|
||||||
import loader from "@monaco-editor/loader";
|
import loader from "@monaco-editor/loader";
|
||||||
import {
|
import { adaptFromReactOrNativeKeyEvent } from "@/util/keyutil";
|
||||||
checkKeyPressed,
|
|
||||||
adaptFromReactOrNativeKeyEvent,
|
|
||||||
KeybindManager,
|
|
||||||
adaptFromElectronKeyEvent,
|
|
||||||
} from "@/util/keyutil";
|
|
||||||
import { Button, Dropdown } from "@/elements";
|
|
||||||
|
|
||||||
import "./code.less";
|
import "./code.less";
|
||||||
|
|
||||||
@ -99,7 +93,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
* codeCache is a Hashmap with key=screenId:lineId:filepath and value=code
|
* codeCache is a Hashmap with key=screenId:lineId:filepath and value=code
|
||||||
* Editor should never read the code directly from the filesystem. it should read from the cache.
|
* Editor should never read the code directly from the filesystem. it should read from the cache.
|
||||||
*/
|
*/
|
||||||
static codeCache = new Map();
|
static readonly codeCache = new Map();
|
||||||
|
|
||||||
// which languages have preview options
|
// which languages have preview options
|
||||||
languagesWithPreviewer: string[] = ["markdown", "mdx"];
|
languagesWithPreviewer: string[] = ["markdown", "mdx"];
|
||||||
@ -109,7 +103,6 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
monacoEditor: MonacoTypes.editor.IStandaloneCodeEditor; // reference to mounted monaco editor. TODO need the correct type
|
monacoEditor: MonacoTypes.editor.IStandaloneCodeEditor; // reference to mounted monaco editor. TODO need the correct type
|
||||||
markdownRef: React.RefObject<HTMLDivElement>;
|
markdownRef: React.RefObject<HTMLDivElement>;
|
||||||
syncing: boolean;
|
syncing: boolean;
|
||||||
monacoOptions: MonacoTypes.editor.IEditorOptions & MonacoTypes.editor.IGlobalEditorOptions;
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -117,7 +110,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
const editorHeight = Math.max(props.savedHeight - this.getEditorHeightBuffer(), 0); // must subtract the padding/margin to get the real editorHeight
|
const editorHeight = Math.max(props.savedHeight - this.getEditorHeightBuffer(), 0); // must subtract the padding/margin to get the real editorHeight
|
||||||
this.markdownRef = React.createRef();
|
this.markdownRef = React.createRef();
|
||||||
this.syncing = false;
|
this.syncing = false;
|
||||||
let isClosed = props.lineState["prompt:closed"];
|
const isClosed = props.lineState["prompt:closed"];
|
||||||
this.state = {
|
this.state = {
|
||||||
code: null,
|
code: null,
|
||||||
languages: [],
|
languages: [],
|
||||||
@ -161,12 +154,12 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveLineState = (kvp) => {
|
saveLineState(kvp) {
|
||||||
const { screenId, lineId } = this.props.context;
|
const { screenId, lineId } = this.props.context;
|
||||||
GlobalCommandRunner.setLineState(screenId, lineId, { ...this.props.lineState, ...kvp }, false);
|
GlobalCommandRunner.setLineState(screenId, lineId, { ...this.props.lineState, ...kvp }, false);
|
||||||
};
|
}
|
||||||
|
|
||||||
setInitialLanguage = (editor) => {
|
setInitialLanguage(editor) {
|
||||||
// set all languages
|
// set all languages
|
||||||
const languages = monaco.languages.getLanguages().map((lang) => lang.id);
|
const languages = monaco.languages.getLanguages().map((lang) => lang.id);
|
||||||
this.setState({ languages });
|
this.setState({ languages });
|
||||||
@ -175,7 +168,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
// if not found, we try to grab the filename from with filePath (coming from lineState["prompt:file"]) or cmdstr
|
// if not found, we try to grab the filename from with filePath (coming from lineState["prompt:file"]) or cmdstr
|
||||||
if (!detectedLanguage) {
|
if (!detectedLanguage) {
|
||||||
const strForFilePath = this.filePath || this.props.cmdstr;
|
const strForFilePath = this.filePath || this.props.cmdstr;
|
||||||
const extension = strForFilePath.match(/(?:[^\\\/:*?"<>|\r\n]+\.)([a-zA-Z0-9]+)\b/)?.[1] || "";
|
const extension = RegExp(/(?:[^\\/:*?"<>|\r\n]+\.)([a-zA-Z0-9]+)\b/).exec(strForFilePath)?.[1] || "";
|
||||||
const detectedLanguageObj = monaco.languages
|
const detectedLanguageObj = monaco.languages
|
||||||
.getLanguages()
|
.getLanguages()
|
||||||
.find((lang) => lang.extensions?.includes("." + extension));
|
.find((lang) => lang.extensions?.includes("." + extension));
|
||||||
@ -194,12 +187,12 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
registerKeybindings() {
|
registerKeybindings() {
|
||||||
const { lineId } = this.props.context;
|
const { lineId } = this.props.context;
|
||||||
let domain = "code-" + lineId;
|
const domain = "code-" + lineId;
|
||||||
let keybindManager = GlobalModel.keybindManager;
|
const keybindManager = GlobalModel.keybindManager;
|
||||||
keybindManager.registerKeybinding("plugin", domain, "codeedit:save", (waveEvent) => {
|
keybindManager.registerKeybinding("plugin", domain, "codeedit:save", (waveEvent) => {
|
||||||
this.doSave();
|
this.doSave();
|
||||||
return true;
|
return true;
|
||||||
@ -216,20 +209,20 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
|
|
||||||
unregisterKeybindings() {
|
unregisterKeybindings() {
|
||||||
const { lineId } = this.props.context;
|
const { lineId } = this.props.context;
|
||||||
let domain = "code-" + lineId;
|
const domain = "code-" + lineId;
|
||||||
GlobalModel.keybindManager.unregisterDomain(domain);
|
GlobalModel.keybindManager.unregisterDomain(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditorDidMount = (editor: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
handleEditorDidMount(editor: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) {
|
||||||
this.monacoEditor = editor;
|
this.monacoEditor = editor;
|
||||||
this.setInitialLanguage(editor);
|
this.setInitialLanguage(editor);
|
||||||
this.setEditorHeight();
|
this.setEditorHeight();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let opts = this.getEditorOptions();
|
const opts = this.getEditorOptions();
|
||||||
editor.updateOptions(opts);
|
editor.updateOptions(opts);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
editor.onKeyDown((e: MonacoTypes.IKeyboardEvent) => {
|
editor.onKeyDown((e: MonacoTypes.IKeyboardEvent) => {
|
||||||
let waveEvent = adaptFromReactOrNativeKeyEvent(e.browserEvent);
|
const waveEvent = adaptFromReactOrNativeKeyEvent(e.browserEvent);
|
||||||
console.log("keydown?", waveEvent);
|
console.log("keydown?", waveEvent);
|
||||||
if (
|
if (
|
||||||
GlobalModel.keybindManager.checkKeysPressed(waveEvent, [
|
GlobalModel.keybindManager.checkKeysPressed(waveEvent, [
|
||||||
@ -261,7 +254,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!this.getAllowEditing()) this.setState({ showReadonly: true });
|
if (!this.getAllowEditing()) this.setState({ showReadonly: true });
|
||||||
};
|
}
|
||||||
|
|
||||||
handleEditorScrollChange(e) {
|
handleEditorScrollChange(e) {
|
||||||
if (!this.state.showPreview) return;
|
if (!this.state.showPreview) return;
|
||||||
@ -291,7 +284,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLanguageChange = (e: any) => {
|
handleLanguageChange(e: any) {
|
||||||
const selectedLanguage = e.target.value;
|
const selectedLanguage = e.target.value;
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedLanguage,
|
selectedLanguage,
|
||||||
@ -304,9 +297,9 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
this.saveLineState({ lang: selectedLanguage });
|
this.saveLineState({ lang: selectedLanguage });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
doSave = (onSave = () => {}) => {
|
doSave(onSave = () => {}) {
|
||||||
if (!this.state.isSave) return;
|
if (!this.state.isSave) return;
|
||||||
const { screenId, lineId } = this.props.context;
|
const { screenId, lineId } = this.props.context;
|
||||||
const encodedCode = new TextEncoder().encode(this.state.code);
|
const encodedCode = new TextEncoder().encode(this.state.code);
|
||||||
@ -326,9 +319,9 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
this.setState({ message: { status: "error", text: e.message } });
|
this.setState({ message: { status: "error", text: e.message } });
|
||||||
setTimeout(() => this.setState({ message: null }), 3000);
|
setTimeout(() => this.setState({ message: null }), 3000);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
doClose = () => {
|
doClose() {
|
||||||
// if there is unsaved data
|
// if there is unsaved data
|
||||||
if (this.state.isSave)
|
if (this.state.isSave)
|
||||||
return GlobalModel.showAlert({
|
return GlobalModel.showAlert({
|
||||||
@ -363,15 +356,15 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
if (this.props.shouldFocus) {
|
if (this.props.shouldFocus) {
|
||||||
GlobalCommandRunner.screenSetFocus("input");
|
GlobalCommandRunner.screenSetFocus("input");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
handleEditorChange = (code) => {
|
handleEditorChange(code) {
|
||||||
SourceCodeRenderer.codeCache.set(this.cacheKey, code);
|
SourceCodeRenderer.codeCache.set(this.cacheKey, code);
|
||||||
this.setState({ code }, () => {
|
this.setState({ code }, () => {
|
||||||
this.setEditorHeight();
|
this.setEditorHeight();
|
||||||
this.setState({ isSave: code !== this.originalCode });
|
this.setState({ isSave: code !== this.originalCode });
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
getEditorHeightBuffer(): number {
|
getEditorHeightBuffer(): number {
|
||||||
const heightBuffer = GlobalModel.lineHeightEnv.lineHeight + 11;
|
const heightBuffer = GlobalModel.lineHeightEnv.lineHeight + 11;
|
||||||
@ -381,7 +374,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
setEditorHeight = () => {
|
setEditorHeight = () => {
|
||||||
const maxEditorHeight = this.props.opts.maxSize.height - this.getEditorHeightBuffer();
|
const maxEditorHeight = this.props.opts.maxSize.height - this.getEditorHeightBuffer();
|
||||||
let _editorHeight = maxEditorHeight;
|
let _editorHeight = maxEditorHeight;
|
||||||
let allowEditing = this.getAllowEditing();
|
const allowEditing = this.getAllowEditing();
|
||||||
if (!allowEditing) {
|
if (!allowEditing) {
|
||||||
const noOfLines = Math.max(this.state.code.split("\n").length, 5);
|
const noOfLines = Math.max(this.state.code.split("\n").length, 5);
|
||||||
const lineHeight = Math.ceil(GlobalModel.lineHeightEnv.lineHeight);
|
const lineHeight = Math.ceil(GlobalModel.lineHeightEnv.lineHeight);
|
||||||
@ -395,8 +388,8 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
getAllowEditing(): boolean {
|
getAllowEditing(): boolean {
|
||||||
let lineState = this.props.lineState;
|
const lineState = this.props.lineState;
|
||||||
let mode = lineState["mode"] || "view";
|
const mode = lineState["mode"] || "view";
|
||||||
if (mode == "view") {
|
if (mode == "view") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -407,32 +400,25 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
if (!this.monacoEditor) {
|
if (!this.monacoEditor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let opts = this.getEditorOptions();
|
const opts = this.getEditorOptions();
|
||||||
this.monacoEditor.updateOptions(opts);
|
this.monacoEditor.updateOptions(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEditorOptions(): MonacoTypes.editor.IEditorOptions {
|
getEditorOptions(): MonacoTypes.editor.IEditorOptions {
|
||||||
let opts: MonacoTypes.editor.IEditorOptions = {
|
const opts: MonacoTypes.editor.IEditorOptions = {
|
||||||
scrollBeyondLastLine: false,
|
scrollBeyondLastLine: false,
|
||||||
fontSize: GlobalModel.getTermFontSize(),
|
fontSize: GlobalModel.getTermFontSize(),
|
||||||
fontFamily: GlobalModel.getTermFontFamily(),
|
fontFamily: GlobalModel.getTermFontFamily(),
|
||||||
readOnly: !this.getAllowEditing(),
|
readOnly: !this.getAllowEditing(),
|
||||||
};
|
};
|
||||||
let lineState = this.props.lineState;
|
const lineState = this.props.lineState;
|
||||||
let minimap = true;
|
if (this.state.showPreview || ("minimap" in lineState && !lineState["minimap"])) {
|
||||||
if (this.state.showPreview) {
|
|
||||||
minimap = false;
|
|
||||||
} else if ("minimap" in lineState && !lineState["minimap"]) {
|
|
||||||
minimap = false;
|
|
||||||
}
|
|
||||||
if (!minimap) {
|
|
||||||
opts.minimap = { enabled: false };
|
opts.minimap = { enabled: false };
|
||||||
}
|
}
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCodeEditor = () => {
|
getCodeEditor(theme: string) {
|
||||||
let theme = GlobalModel.isThemeDark() ? "wave-theme-dark" : "wave-theme-light";
|
|
||||||
return (
|
return (
|
||||||
<div className="editor-wrap" style={{ maxHeight: this.state.editorHeight }}>
|
<div className="editor-wrap" style={{ maxHeight: this.state.editorHeight }}>
|
||||||
{this.state.showReadonly && <div className="readonly">{"read-only"}</div>}
|
{this.state.showReadonly && <div className="readonly">{"read-only"}</div>}
|
||||||
@ -447,9 +433,9 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
getPreviewer = () => {
|
getPreviewer() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="scroller"
|
className="scroller"
|
||||||
@ -460,17 +446,20 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
<Markdown text={this.state.code} style={{ width: "100%", padding: "1rem" }} />
|
<Markdown text={this.state.code} style={{ width: "100%", padding: "1rem" }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
togglePreview = () => {
|
togglePreview() {
|
||||||
this.saveLineState({ showPreview: !this.state.showPreview });
|
this.setState((prevState) => {
|
||||||
this.setState({ showPreview: !this.state.showPreview });
|
const newPreviewState = { showPreview: !prevState.showPreview };
|
||||||
|
this.saveLineState(newPreviewState);
|
||||||
|
return newPreviewState;
|
||||||
|
});
|
||||||
setTimeout(() => this.updateEditorOpts(), 0);
|
setTimeout(() => this.updateEditorOpts(), 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
getEditorControls = () => {
|
getEditorControls() {
|
||||||
const { selectedLanguage, isSave, languages, isPreviewerAvailable, showPreview } = this.state;
|
const { selectedLanguage, languages, isPreviewerAvailable, showPreview } = this.state;
|
||||||
let allowEditing = this.getAllowEditing();
|
const allowEditing = this.getAllowEditing();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<If condition={isPreviewerAvailable}>
|
<If condition={isPreviewerAvailable}>
|
||||||
@ -507,20 +496,22 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
</If>
|
</If>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
getMessage = () => (
|
getMessage() {
|
||||||
|
return (
|
||||||
<div className="messageContainer">
|
<div className="messageContainer">
|
||||||
<div className={`message ${this.state.message.status === "error" ? "error" : ""}`}>
|
<div className={`message ${this.state.message.status === "error" ? "error" : ""}`}>
|
||||||
{this.state.message.text}
|
{this.state.message.text}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
setSizes = (sizes) => {
|
setSizes(sizes: number[]) {
|
||||||
this.setState({ editorFraction: sizes[0] });
|
this.setState({ editorFraction: sizes[0] });
|
||||||
this.saveLineState({ editorFraction: sizes[0] });
|
this.saveLineState({ editorFraction: sizes[0] });
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { exitcode } = this.props;
|
const { exitcode } = this.props;
|
||||||
@ -544,14 +535,16 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let { lineNum } = this.props.context;
|
const { lineNum } = this.props.context;
|
||||||
let screen = GlobalModel.getActiveScreen();
|
const screen = GlobalModel.getActiveScreen();
|
||||||
let lineIsSelected = mobx.computed(
|
const lineIsSelected = mobx.computed(
|
||||||
() => screen.getSelectedLine() == lineNum && screen.getFocusType() == "cmd",
|
() => screen.getSelectedLine() == lineNum && screen.getFocusType() == "cmd",
|
||||||
{
|
{
|
||||||
name: "code-lineisselected",
|
name: "code-lineisselected",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const theme = `wave-theme-${GlobalModel.isDarkTheme.get() ? "dark" : "light"}`;
|
||||||
console.log("lineis selected:", lineIsSelected.get());
|
console.log("lineis selected:", lineIsSelected.get());
|
||||||
return (
|
return (
|
||||||
<div className="code-renderer">
|
<div className="code-renderer">
|
||||||
@ -559,7 +552,7 @@ class SourceCodeRenderer extends React.Component<
|
|||||||
<CodeKeybindings codeObject={this}></CodeKeybindings>
|
<CodeKeybindings codeObject={this}></CodeKeybindings>
|
||||||
</If>
|
</If>
|
||||||
<Split sizes={[editorFraction, 1 - editorFraction]} onSetSizes={this.setSizes}>
|
<Split sizes={[editorFraction, 1 - editorFraction]} onSetSizes={this.setSizes}>
|
||||||
{this.getCodeEditor()}
|
{this.getCodeEditor(theme)}
|
||||||
{isPreviewerAvailable && showPreview && this.getPreviewer()}
|
{isPreviewerAvailable && showPreview && this.getPreviewer()}
|
||||||
</Split>
|
</Split>
|
||||||
<div className="flex-spacer" />
|
<div className="flex-spacer" />
|
||||||
|
@ -23,17 +23,17 @@ class TerminalKeybindings extends React.Component<{ termWrap: any; lineid: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerKeybindings() {
|
registerKeybindings() {
|
||||||
let keybindManager = GlobalModel.keybindManager;
|
const keybindManager = GlobalModel.keybindManager;
|
||||||
let domain = "line-" + this.props.lineid;
|
const domain = "line-" + this.props.lineid;
|
||||||
let termWrap = this.props.termWrap;
|
const termWrap = this.props.termWrap;
|
||||||
keybindManager.registerKeybinding("plugin", domain, "terminal:copy", (waveEvent) => {
|
keybindManager.registerKeybinding("plugin", domain, "terminal:copy", (waveEvent) => {
|
||||||
let termWrap = this.props.termWrap;
|
const termWrap = this.props.termWrap;
|
||||||
let sel = termWrap.terminal.getSelection();
|
const sel = termWrap.terminal.getSelection();
|
||||||
navigator.clipboard.writeText(sel);
|
navigator.clipboard.writeText(sel);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
keybindManager.registerKeybinding("plugin", domain, "terminal:paste", (waveEvent) => {
|
keybindManager.registerKeybinding("plugin", domain, "terminal:paste", (waveEvent) => {
|
||||||
let p = navigator.clipboard.readText();
|
const p = navigator.clipboard.readText();
|
||||||
p.then((text) => {
|
p.then((text) => {
|
||||||
termWrap.dataHandler?.(text, termWrap);
|
termWrap.dataHandler?.(text, termWrap);
|
||||||
});
|
});
|
||||||
@ -105,7 +105,7 @@ class TerminalRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSnapshotBeforeUpdate(prevProps, prevState): { height: number } {
|
getSnapshotBeforeUpdate(prevProps, prevState): { height: number } {
|
||||||
let elem = this.elemRef.current;
|
const elem = this.elemRef.current;
|
||||||
if (elem == null) {
|
if (elem == null) {
|
||||||
return { height: 0 };
|
return { height: 0 };
|
||||||
}
|
}
|
||||||
@ -116,9 +116,8 @@ class TerminalRenderer extends React.Component<
|
|||||||
if (this.props.onHeightChange == null) {
|
if (this.props.onHeightChange == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let { line } = this.props;
|
|
||||||
let curHeight = 0;
|
let curHeight = 0;
|
||||||
let elem = this.elemRef.current;
|
const elem = this.elemRef.current;
|
||||||
if (elem != null) {
|
if (elem != null) {
|
||||||
curHeight = elem.offsetHeight;
|
curHeight = elem.offsetHeight;
|
||||||
}
|
}
|
||||||
@ -133,12 +132,12 @@ class TerminalRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkLoad(): void {
|
checkLoad(): void {
|
||||||
let { line, staticRender, visible, collapsed } = this.props;
|
let { staticRender, visible, collapsed } = this.props;
|
||||||
if (staticRender) {
|
if (staticRender) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let vis = visible && visible.get() && !collapsed;
|
const vis = visible?.get() && !collapsed;
|
||||||
let curVis = this.termLoaded.get();
|
const curVis = this.termLoaded.get();
|
||||||
if (vis && !curVis) {
|
if (vis && !curVis) {
|
||||||
this.loadTerminal();
|
this.loadTerminal();
|
||||||
} else if (!vis && curVis) {
|
} else if (!vis && curVis) {
|
||||||
@ -147,13 +146,12 @@ class TerminalRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadTerminal(): void {
|
loadTerminal(): void {
|
||||||
let { screen, line } = this.props;
|
const { screen, line } = this.props;
|
||||||
let model = GlobalModel;
|
const cmd = screen.getCmd(line);
|
||||||
let cmd = screen.getCmd(line);
|
|
||||||
if (cmd == null) {
|
if (cmd == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let termElem = this.termRef.current;
|
const termElem = this.termRef.current;
|
||||||
if (termElem == null) {
|
if (termElem == null) {
|
||||||
console.log("cannot load terminal, no term elem found", line);
|
console.log("cannot load terminal, no term elem found", line);
|
||||||
return;
|
return;
|
||||||
@ -163,11 +161,11 @@ class TerminalRenderer extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
unloadTerminal(unmount: boolean): void {
|
unloadTerminal(unmount: boolean): void {
|
||||||
let { screen, line } = this.props;
|
const { screen, line } = this.props;
|
||||||
screen.unloadRenderer(line.lineid);
|
screen.unloadRenderer(line.lineid);
|
||||||
if (!unmount) {
|
if (!unmount) {
|
||||||
mobx.action(() => this.termLoaded.set(false))();
|
mobx.action(() => this.termLoaded.set(false))();
|
||||||
let termElem = this.termRef.current;
|
const termElem = this.termRef.current;
|
||||||
if (termElem != null) {
|
if (termElem != null) {
|
||||||
termElem.replaceChildren();
|
termElem.replaceChildren();
|
||||||
}
|
}
|
||||||
@ -176,22 +174,21 @@ class TerminalRenderer extends React.Component<
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
clickTermBlock(e: any) {
|
clickTermBlock(e: any) {
|
||||||
let { screen, line } = this.props;
|
const { screen, line } = this.props;
|
||||||
let model = GlobalModel;
|
const termWrap = screen.getTermWrap(line.lineid);
|
||||||
let termWrap = screen.getTermWrap(line.lineid);
|
|
||||||
if (termWrap != null) {
|
if (termWrap != null) {
|
||||||
termWrap.giveFocus();
|
termWrap.giveFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { screen, line, width, staticRender, visible, collapsed } = this.props;
|
const { screen, line, width, collapsed } = this.props;
|
||||||
let isPhysicalFocused = mobx
|
const isPhysicalFocused = mobx
|
||||||
.computed(() => screen.getIsFocused(line.linenum), {
|
.computed(() => screen.getIsFocused(line.linenum), {
|
||||||
name: "computed-getIsFocused",
|
name: "computed-getIsFocused",
|
||||||
})
|
})
|
||||||
.get();
|
.get();
|
||||||
let isFocused = mobx
|
const isFocused = mobx
|
||||||
.computed(
|
.computed(
|
||||||
() => {
|
() => {
|
||||||
let screenFocusType = screen.getFocusType();
|
let screenFocusType = screen.getFocusType();
|
||||||
@ -200,15 +197,15 @@ class TerminalRenderer extends React.Component<
|
|||||||
{ name: "computed-isFocused" }
|
{ name: "computed-isFocused" }
|
||||||
)
|
)
|
||||||
.get();
|
.get();
|
||||||
let cmd = screen.getCmd(line); // will not be null
|
const cmd = screen.getCmd(line); // will not be null
|
||||||
let usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width);
|
const usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width);
|
||||||
let termHeight = termHeightFromRows(usedRows, GlobalModel.getTermFontSize(), cmd.getTermMaxRows());
|
let termHeight = termHeightFromRows(usedRows, GlobalModel.getTermFontSize(), cmd.getTermMaxRows());
|
||||||
if (usedRows === 0) {
|
if (usedRows === 0) {
|
||||||
termHeight = 0;
|
termHeight = 0;
|
||||||
}
|
}
|
||||||
let termLoaded = this.termLoaded.get();
|
const termLoaded = this.termLoaded.get();
|
||||||
let lineid = line.lineid;
|
const lineid = line.lineid;
|
||||||
let termWrap = screen.getTermWrap(lineid);
|
const termWrap = screen.getTermWrap(lineid);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={this.elemRef}
|
ref={this.elemRef}
|
||||||
|
7
src/types/custom.d.ts
vendored
7
src/types/custom.d.ts
vendored
@ -12,6 +12,7 @@ declare global {
|
|||||||
type RemoteStatusTypeStrs = "connected" | "connecting" | "disconnected" | "error";
|
type RemoteStatusTypeStrs = "connected" | "connecting" | "disconnected" | "error";
|
||||||
type LineContainerStrs = "main" | "sidebar" | "history";
|
type LineContainerStrs = "main" | "sidebar" | "history";
|
||||||
type AppUpdateStatusType = "unavailable" | "ready";
|
type AppUpdateStatusType = "unavailable" | "ready";
|
||||||
|
type NativeThemeSource = "system" | "light" | "dark";
|
||||||
|
|
||||||
type OV<V> = mobx.IObservableValue<V>;
|
type OV<V> = mobx.IObservableValue<V>;
|
||||||
type OArr<V> = mobx.IObservableArray<V>;
|
type OArr<V> = mobx.IObservableArray<V>;
|
||||||
@ -563,7 +564,7 @@ declare global {
|
|||||||
type FeOptsType = {
|
type FeOptsType = {
|
||||||
termfontsize: number;
|
termfontsize: number;
|
||||||
termfontfamily: string;
|
termfontfamily: string;
|
||||||
theme: string;
|
theme: NativeThemeSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ConfirmFlagsType = {
|
type ConfirmFlagsType = {
|
||||||
@ -898,6 +899,10 @@ declare global {
|
|||||||
getAuthKey: () => string;
|
getAuthKey: () => string;
|
||||||
getWaveSrvStatus: () => boolean;
|
getWaveSrvStatus: () => boolean;
|
||||||
getInitialTermFontFamily: () => string;
|
getInitialTermFontFamily: () => string;
|
||||||
|
getShouldUseDarkColors: () => boolean;
|
||||||
|
getNativeThemeSource: () => NativeThemeSource;
|
||||||
|
setNativeThemeSource: (source: NativeThemeSource) => void;
|
||||||
|
onNativeThemeUpdated: (callback: () => void) => void;
|
||||||
restartWaveSrv: () => boolean;
|
restartWaveSrv: () => boolean;
|
||||||
reloadWindow: () => void;
|
reloadWindow: () => void;
|
||||||
openExternalLink: (url: string) => void;
|
openExternalLink: (url: string) => void;
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
function loadTheme(theme: string) {
|
|
||||||
const linkTag: any = document.getElementById("theme-stylesheet");
|
|
||||||
if (theme === "dark") {
|
|
||||||
linkTag.href = "public/themes/default.css";
|
|
||||||
} else {
|
|
||||||
linkTag.href = `public/themes/${theme}.css`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { loadTheme };
|
|
@ -104,7 +104,7 @@ var RemoteColorNames = []string{"red", "green", "yellow", "blue", "magenta", "cy
|
|||||||
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
var RemoteSetArgs = []string{"alias", "connectmode", "key", "password", "autoinstall", "color"}
|
||||||
var ConfirmFlags = []string{"hideshellprompt"}
|
var ConfirmFlags = []string{"hideshellprompt"}
|
||||||
var SidebarNames = []string{"main"}
|
var SidebarNames = []string{"main"}
|
||||||
var ThemeNames = []string{"light", "dark"}
|
var ThemeSources = []string{"light", "dark", "system"}
|
||||||
|
|
||||||
var ScreenCmds = []string{"run", "comment", "cd", "cr", "clear", "sw", "reset", "signal", "chat"}
|
var ScreenCmds = []string{"run", "comment", "cd", "cr", "clear", "sw", "reset", "signal", "chat"}
|
||||||
var NoHistCmds = []string{"_compgen", "line", "history", "_killserver"}
|
var NoHistCmds = []string{"_compgen", "line", "history", "_killserver"}
|
||||||
@ -5529,20 +5529,20 @@ func ClientSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sc
|
|||||||
}
|
}
|
||||||
varsUpdated = append(varsUpdated, "termfontfamily")
|
varsUpdated = append(varsUpdated, "termfontfamily")
|
||||||
}
|
}
|
||||||
if themeStr, found := pk.Kwargs["theme"]; found {
|
if themeSourceStr, found := pk.Kwargs["theme"]; found {
|
||||||
newTheme := themeStr
|
newThemeSource := themeSourceStr
|
||||||
found := false
|
found := false
|
||||||
for _, theme := range ThemeNames {
|
for _, theme := range ThemeSources {
|
||||||
if newTheme == theme {
|
if newThemeSource == theme {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid theme name")
|
return nil, fmt.Errorf("invalid theme source")
|
||||||
}
|
}
|
||||||
feOpts := clientData.FeOpts
|
feOpts := clientData.FeOpts
|
||||||
feOpts.Theme = newTheme
|
feOpts.Theme = newThemeSource
|
||||||
err = sstore.UpdateClientFeOpts(ctx, feOpts)
|
err = sstore.UpdateClientFeOpts(ctx, feOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error updating client feopts: %v", err)
|
return nil, fmt.Errorf("error updating client feopts: %v", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user