1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Font settings for editor and ternimal (#4694)

* Add font settings for editor. Add terminal tab

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Move shell settings to terminal tab

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Add termilan font settings to store

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Fix store crash

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Cleanup

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Remove unsued font-list

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Cleanup. Move default font size to a const

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Remove package.lock

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* PR fixes and cleanup

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Move to settings object

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Test

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Add reaction to update terminals. Cleanup

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Add config field to terminal

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Merge with master and resolve conflicts

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Fix merge issues

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

* Fix lint and resolve conflicts

Signed-off-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>

Co-authored-by: DMYTRO ZHARKOV <dmytrozharkov@DMYTROs-MBP.fritz.box>
This commit is contained in:
Dmitriy Noa 2022-01-21 09:18:53 +01:00 committed by GitHub
parent 6cba82c491
commit b7d29f8c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 165 additions and 55 deletions

View File

@ -34,6 +34,9 @@ export const extensionRoute: RouteProps = {
path: `${preferencesRoute.path}/extensions`, path: `${preferencesRoute.path}/extensions`,
}; };
export const terminalRoute: RouteProps = {
path: `${preferencesRoute.path}/terminal`,
};
export const preferencesURL = buildURL(preferencesRoute.path); export const preferencesURL = buildURL(preferencesRoute.path);
export const appURL = buildURL(appRoute.path); export const appURL = buildURL(appRoute.path);
export const proxyURL = buildURL(proxyRoute.path); export const proxyURL = buildURL(proxyRoute.path);
@ -41,3 +44,4 @@ export const kubernetesURL = buildURL(kubernetesRoute.path);
export const editorURL = buildURL(editorRoute.path); export const editorURL = buildURL(editorRoute.path);
export const telemetryURL = buildURL(telemetryRoute.path); export const telemetryURL = buildURL(telemetryRoute.path);
export const extensionURL = buildURL(extensionRoute.path); export const extensionURL = buildURL(extensionRoute.path);
export const terminalURL = buildURL(terminalRoute.path);

View File

@ -10,7 +10,7 @@ import { getAppVersion, ObservableToggleSet } from "../utils";
import type { editor } from "monaco-editor"; import type { editor } from "monaco-editor";
import merge from "lodash/merge"; import merge from "lodash/merge";
import { SemVer } from "semver"; import { SemVer } from "semver";
import { defaultTheme } from "../vars"; import { defaultTheme, defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars";
export interface KubeconfigSyncEntry extends KubeconfigSyncValue { export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
filePath: string; filePath: string;
@ -18,19 +18,29 @@ export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
export interface KubeconfigSyncValue { export interface KubeconfigSyncValue {
} }
export interface TerminalConfig {
fontSize: number;
fontFamily: string;
}
export const defaultTerminalConfig: TerminalConfig = {
fontSize: defaultFontSize,
fontFamily: defaultTerminalFontFamily,
};
export type EditorConfiguration = Pick<editor.IStandaloneEditorConstructionOptions, export type EditorConfiguration = Pick<editor.IStandaloneEditorConstructionOptions,
"minimap" | "tabSize" | "lineNumbers">; "minimap" | "tabSize" | "lineNumbers" | "fontSize" | "fontFamily">;
export const defaultEditorConfig: EditorConfiguration = { export const defaultEditorConfig: EditorConfiguration = {
tabSize: 2, tabSize: 2,
lineNumbers: "on", lineNumbers: "on",
fontSize: defaultFontSize,
fontFamily: defaultEditorFontFamily,
minimap: { minimap: {
enabled: true, enabled: true,
side: "right", side: "right",
}, },
}; };
interface PreferenceDescription<T, R = T> { interface PreferenceDescription<T, R = T> {
fromStore(val: T | undefined): R; fromStore(val: T | undefined): R;
toStore(val: R): T | undefined; toStore(val: R): T | undefined;
@ -273,6 +283,15 @@ const editorConfiguration: PreferenceDescription<EditorConfiguration, EditorConf
}, },
}; };
const terminalConfig: PreferenceDescription<TerminalConfig, TerminalConfig> = {
fromStore(val) {
return merge(defaultTerminalConfig, val);
},
toStore(val) {
return val;
},
};
const updateChannels = new Map([ const updateChannels = new Map([
["latest", { ["latest", {
label: "Stable", label: "Stable",
@ -358,6 +377,7 @@ export const DESCRIPTORS = {
syncKubeconfigEntries, syncKubeconfigEntries,
editorConfiguration, editorConfiguration,
terminalCopyOnSelect, terminalCopyOnSelect,
terminalConfig,
updateChannel, updateChannel,
extensionRegistryUrl, extensionRegistryUrl,
}; };

View File

@ -12,7 +12,7 @@ import { getAppVersion } from "../utils/app-version";
import { kubeConfigDefaultPath } from "../kube-helpers"; import { kubeConfigDefaultPath } from "../kube-helpers";
import { appEventBus } from "../app-event-bus/event-bus"; import { appEventBus } from "../app-event-bus/event-bus";
import { ObservableToggleSet, toJS } from "../../renderer/utils"; import { ObservableToggleSet, toJS } from "../../renderer/utils";
import { DESCRIPTORS, EditorConfiguration, ExtensionRegistry, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; import { DESCRIPTORS, EditorConfiguration, ExtensionRegistry, KubeconfigSyncValue, UserPreferencesModel, TerminalConfig } from "./preferences-helpers";
import logger from "../../main/logger"; import logger from "../../main/logger";
export interface UserStoreModel { export interface UserStoreModel {
@ -57,6 +57,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
@observable downloadBinariesPath?: string; @observable downloadBinariesPath?: string;
@observable kubectlBinariesPath?: string; @observable kubectlBinariesPath?: string;
@observable terminalCopyOnSelect: boolean; @observable terminalCopyOnSelect: boolean;
@observable terminalConfig: TerminalConfig;
@observable updateChannel?: string; @observable updateChannel?: string;
@observable extensionRegistryUrl: ExtensionRegistry; @observable extensionRegistryUrl: ExtensionRegistry;
@ -185,6 +186,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
this.syncKubeconfigEntries.replace(DESCRIPTORS.syncKubeconfigEntries.fromStore(preferences?.syncKubeconfigEntries)); this.syncKubeconfigEntries.replace(DESCRIPTORS.syncKubeconfigEntries.fromStore(preferences?.syncKubeconfigEntries));
this.editorConfiguration = DESCRIPTORS.editorConfiguration.fromStore(preferences?.editorConfiguration); this.editorConfiguration = DESCRIPTORS.editorConfiguration.fromStore(preferences?.editorConfiguration);
this.terminalCopyOnSelect = DESCRIPTORS.terminalCopyOnSelect.fromStore(preferences?.terminalCopyOnSelect); this.terminalCopyOnSelect = DESCRIPTORS.terminalCopyOnSelect.fromStore(preferences?.terminalCopyOnSelect);
this.terminalConfig = DESCRIPTORS.terminalConfig.fromStore(preferences?.terminalConfig);
this.updateChannel = DESCRIPTORS.updateChannel.fromStore(preferences?.updateChannel); this.updateChannel = DESCRIPTORS.updateChannel.fromStore(preferences?.updateChannel);
this.extensionRegistryUrl = DESCRIPTORS.extensionRegistryUrl.fromStore(preferences?.extensionRegistryUrl); this.extensionRegistryUrl = DESCRIPTORS.extensionRegistryUrl.fromStore(preferences?.extensionRegistryUrl);
} }
@ -210,6 +212,7 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
syncKubeconfigEntries: DESCRIPTORS.syncKubeconfigEntries.toStore(this.syncKubeconfigEntries), syncKubeconfigEntries: DESCRIPTORS.syncKubeconfigEntries.toStore(this.syncKubeconfigEntries),
editorConfiguration: DESCRIPTORS.editorConfiguration.toStore(this.editorConfiguration), editorConfiguration: DESCRIPTORS.editorConfiguration.toStore(this.editorConfiguration),
terminalCopyOnSelect: DESCRIPTORS.terminalCopyOnSelect.toStore(this.terminalCopyOnSelect), terminalCopyOnSelect: DESCRIPTORS.terminalCopyOnSelect.toStore(this.terminalCopyOnSelect),
terminalConfig: DESCRIPTORS.terminalConfig.toStore(this.terminalConfig),
updateChannel: DESCRIPTORS.updateChannel.toStore(this.updateChannel), updateChannel: DESCRIPTORS.updateChannel.toStore(this.updateChannel),
extensionRegistryUrl: DESCRIPTORS.extensionRegistryUrl.toStore(this.extensionRegistryUrl), extensionRegistryUrl: DESCRIPTORS.extensionRegistryUrl.toStore(this.extensionRegistryUrl),
}, },

View File

@ -26,6 +26,9 @@ export const productName = packageInfo.productName;
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`; export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
export const publicPath = "/build/" as string; export const publicPath = "/build/" as string;
export const defaultTheme = "lens-dark" as string; export const defaultTheme = "lens-dark" as string;
export const defaultFontSize = 12;
export const defaultTerminalFontFamily = "RobotoMono";
export const defaultEditorFontFamily = "RobotoMono";
// Webpack build paths // Webpack build paths
export const contextDir = process.cwd(); export const contextDir = process.cwd();

View File

@ -10,7 +10,6 @@ import { Select, SelectOption } from "../select";
import { ThemeStore } from "../../theme.store"; import { ThemeStore } from "../../theme.store";
import { UserStore } from "../../../common/user-store"; import { UserStore } from "../../../common/user-store";
import { Input } from "../input"; import { Input } from "../input";
import { isWindows } from "../../../common/vars";
import { Switch } from "../switch"; import { Switch } from "../switch";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { CONSTANTS, defaultExtensionRegistryUrl, ExtensionRegistryLocation } from "../../../common/user-store/preferences-helpers"; import { CONSTANTS, defaultExtensionRegistryUrl, ExtensionRegistryLocation } from "../../../common/user-store/preferences-helpers";
@ -36,16 +35,7 @@ interface Dependencies {
const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems }) => { const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems }) => {
const userStore = UserStore.getInstance(); const userStore = UserStore.getInstance();
const defaultShell = process.env.SHELL
|| process.env.PTYSHELL
|| (
isWindows
? "powershell.exe"
: "System default shell"
);
const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || ""); const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || "");
const [shell, setShell] = React.useState(userStore.shell || "");
const extensionSettings = appPreferenceItems.get().filter((preference) => preference.showInPreferencesTab === "application"); const extensionSettings = appPreferenceItems.get().filter((preference) => preference.showInPreferencesTab === "application");
const themeStore = ThemeStore.getInstance(); const themeStore = ThemeStore.getInstance();
@ -64,42 +54,6 @@ const NonInjectedApplication: React.FC<Dependencies> = ({ appPreferenceItems })
<hr/> <hr/>
<section id="terminalTheme">
<SubTitle title="Terminal theme" />
<Select
themeName="lens"
options={[
{ label: "Match theme", value: "" },
...themeStore.themeOptions,
]}
value={userStore.terminalTheme}
onChange={({ value }) => userStore.terminalTheme = value}
/>
</section>
<section id="shell">
<SubTitle title="Terminal Shell Path" />
<Input
theme="round-black"
placeholder={defaultShell}
value={shell}
onChange={setShell}
onBlur={() => userStore.shell = shell}
/>
</section>
<section id="terminalSelection">
<SubTitle title="Terminal copy & paste" />
<Switch
checked={userStore.terminalCopyOnSelect}
onChange={() => userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect}
>
Copy on select and paste on right-click
</Switch>
</section>
<hr />
<section id="extensionRegistryUrl"> <section id="extensionRegistryUrl">
<SubTitle title="Extension Install Registry" /> <SubTitle title="Extension Install Registry" />
<Select <Select

View File

@ -69,6 +69,27 @@ export const Editor = observer(() => {
onChange={value => editorConfiguration.tabSize = Number(value)} onChange={value => editorConfiguration.tabSize = Number(value)}
/> />
</section> </section>
<section>
<SubTitle title="Font size"/>
<Input
theme="round-black"
type="number"
min={10}
validators={InputValidators.isNumber}
value={editorConfiguration.fontSize.toString()}
onChange={value => editorConfiguration.fontSize = Number(value)}
/>
</section>
<section>
<SubTitle title="Font family"/>
<Input
theme="round-black"
type="text"
validators={InputValidators.isNumber}
value={editorConfiguration.fontFamily}
onChange={value => editorConfiguration.fontFamily = value}
/>
</section>
</section> </section>
); );
}); });

View File

@ -22,6 +22,8 @@ import {
editorRoute, editorRoute,
telemetryRoute, telemetryRoute,
telemetryURL, telemetryURL,
terminalRoute,
terminalURL,
} from "../../../common/routes"; } from "../../../common/routes";
import { navigateWithoutHistoryChange, navigation } from "../../navigation"; import { navigateWithoutHistoryChange, navigation } from "../../navigation";
import { SettingLayout } from "../layout/setting-layout"; import { SettingLayout } from "../layout/setting-layout";
@ -29,6 +31,7 @@ import { Tab, Tabs } from "../tabs";
import { Application } from "./application"; import { Application } from "./application";
import { Kubernetes } from "./kubernetes"; import { Kubernetes } from "./kubernetes";
import { Editor } from "./editor"; import { Editor } from "./editor";
import { Terminal } from "./terminal";
import { LensProxy } from "./proxy"; import { LensProxy } from "./proxy";
import { Telemetry } from "./telemetry"; import { Telemetry } from "./telemetry";
import { Extensions } from "./extensions"; import { Extensions } from "./extensions";
@ -56,6 +59,7 @@ const NonInjectedPreferences: React.FC<Dependencies> = ({ appPreferenceItems })
<Tab value={proxyURL()} label="Proxy" data-testid="proxy-tab" active={isActive(proxyRoute)}/> <Tab value={proxyURL()} label="Proxy" data-testid="proxy-tab" active={isActive(proxyRoute)}/>
<Tab value={kubernetesURL()} label="Kubernetes" data-testid="kubernetes-tab" active={isActive(kubernetesRoute)}/> <Tab value={kubernetesURL()} label="Kubernetes" data-testid="kubernetes-tab" active={isActive(kubernetesRoute)}/>
<Tab value={editorURL()} label="Editor" data-testid="editor-tab" active={isActive(editorRoute)}/> <Tab value={editorURL()} label="Editor" data-testid="editor-tab" active={isActive(editorRoute)}/>
<Tab value={terminalURL()} label="Terminal" data-testid="terminal-tab" active={isActive(terminalRoute)}/>
{(telemetryExtensions.length > 0 || !!sentryDsn) && {(telemetryExtensions.length > 0 || !!sentryDsn) &&
<Tab value={telemetryURL()} label="Telemetry" data-testid="telemetry-tab" active={isActive(telemetryRoute)}/> <Tab value={telemetryURL()} label="Telemetry" data-testid="telemetry-tab" active={isActive(telemetryRoute)}/>
} }
@ -77,6 +81,7 @@ const NonInjectedPreferences: React.FC<Dependencies> = ({ appPreferenceItems })
<Route path={proxyURL()} component={LensProxy}/> <Route path={proxyURL()} component={LensProxy}/>
<Route path={kubernetesURL()} component={Kubernetes}/> <Route path={kubernetesURL()} component={Kubernetes}/>
<Route path={editorURL()} component={Editor}/> <Route path={editorURL()} component={Editor}/>
<Route path={terminalURL()} component={Terminal}/>
<Route path={telemetryURL()} component={Telemetry}/> <Route path={telemetryURL()} component={Telemetry}/>
<Route path={extensionURL()} component={Extensions}/> <Route path={extensionURL()} component={Extensions}/>
<Redirect exact from={`${preferencesURL()}/`} to={appURL()}/> <Redirect exact from={`${preferencesURL()}/`} to={appURL()}/>

View File

@ -0,0 +1,82 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { observer } from "mobx-react";
import { UserStore } from "../../../common/user-store";
import { SubTitle } from "../layout/sub-title";
import { Input, InputValidators } from "../input";
import { isWindows } from "../../../common/vars";
import { Switch } from "../switch";
import { Select } from "../select";
import { ThemeStore } from "../../theme.store";
export const Terminal = observer(() => {
const userStore = UserStore.getInstance();
const themeStore = ThemeStore.getInstance();
const defaultShell = process.env.SHELL
|| process.env.PTYSHELL
|| (
isWindows
? "powershell.exe"
: "System default shell"
);
return (<div>
<section id="shell">
<SubTitle title="Terminal Shell Path"/>
<Input
theme="round-black"
placeholder={defaultShell}
value={userStore.shell}
onChange={(value) => userStore.shell = value}
/>
</section>
<section id="terminalSelection">
<SubTitle title="Terminal copy & paste" />
<Switch
checked={userStore.terminalCopyOnSelect}
onChange={() => userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect}
>
Copy on select and paste on right-click
</Switch>
</section>
<section id="terminalTheme">
<SubTitle title="Terminal theme" />
<Select
themeName="lens"
options={[
{ label: "Match theme", value: "" },
...themeStore.themeOptions,
]}
value={userStore.terminalTheme}
onChange={({ value }) => userStore.terminalTheme = value}
/>
</section>
<section>
<SubTitle title="Font size"/>
<Input
theme="round-black"
type="number"
min={10}
validators={InputValidators.isNumber}
value={userStore.terminalConfig.fontSize.toString()}
onChange={(value) => userStore.terminalConfig.fontSize=Number(value)}
/>
</section>
<section>
<SubTitle title="Font family"/>
<Input
theme="round-black"
type="text"
value={userStore.terminalConfig.fontFamily}
onChange={(value) => userStore.terminalConfig.fontFamily=value}
/>
</section>
</div>);
});

View File

@ -11,24 +11,28 @@ import type { DockStore, TabId } from "../dock-store/dock.store";
import { TerminalApi, TerminalChannels } from "../../../api/terminal-api"; import { TerminalApi, TerminalChannels } from "../../../api/terminal-api";
import { ThemeStore } from "../../../theme.store"; import { ThemeStore } from "../../../theme.store";
import { disposer } from "../../../utils"; import { disposer } from "../../../utils";
import { isMac } from "../../../../common/vars"; import { isMac, defaultTerminalFontFamily } from "../../../../common/vars";
import { once } from "lodash"; import { once } from "lodash";
import { UserStore } from "../../../../common/user-store"; import { UserStore } from "../../../../common/user-store";
import { clipboard } from "electron"; import { clipboard } from "electron";
import logger from "../../../../common/logger"; import logger from "../../../../common/logger";
import type { TerminalConfig } from "../../../../common/user-store/preferences-helpers";
interface Dependencies { interface Dependencies {
dockStore: DockStore dockStore: DockStore
} }
export class Terminal { export class Terminal {
private terminalConfig: TerminalConfig = UserStore.getInstance().terminalConfig;
public static get spawningPool() { public static get spawningPool() {
return document.getElementById("terminal-init"); return document.getElementById("terminal-init");
} }
static async preloadFonts() { static async preloadFonts() {
const fontPath = require("../../fonts/roboto-mono-nerd.ttf").default; // eslint-disable-line @typescript-eslint/no-var-requires const fontPath = require("../../fonts/roboto-mono-nerd.ttf").default; // eslint-disable-line @typescript-eslint/no-var-requires
const fontFace = new FontFace("RobotoMono", `url(${fontPath})`); const fontFace = new FontFace(defaultTerminalFontFamily, `url(${fontPath})`);
await fontFace.load(); await fontFace.load();
document.fonts.add(fontFace); document.fonts.add(fontFace);
@ -37,8 +41,8 @@ export class Terminal {
private xterm: XTerm | null = new XTerm({ private xterm: XTerm | null = new XTerm({
cursorBlink: true, cursorBlink: true,
cursorStyle: "bar", cursorStyle: "bar",
fontSize: 13, fontSize: this.terminalConfig.fontSize,
fontFamily: "RobotoMono", fontFamily: this.terminalConfig.fontFamily,
}); });
private readonly fitAddon = new FitAddon(); private readonly fitAddon = new FitAddon();
private scrollPos = 0; private scrollPos = 0;
@ -97,6 +101,12 @@ export class Terminal {
}, { }, {
fireImmediately: true, fireImmediately: true,
}), }),
reaction(() => UserStore.getInstance().terminalConfig.fontSize, this.setFontSize, {
fireImmediately: true,
}),
reaction(() => UserStore.getInstance().terminalConfig.fontFamily, this.setFontFamily, {
fireImmediately: true,
}),
dependencies.dockStore.onResize(this.onResize), dependencies.dockStore.onResize(this.onResize),
() => onDataHandler.dispose(), () => onDataHandler.dispose(),
() => this.fitAddon.dispose(), () => this.fitAddon.dispose(),
@ -192,6 +202,14 @@ export class Terminal {
} }
}; };
setFontSize = (size: number) => {
this.xterm.options.fontSize = size;
};
setFontFamily = (family: string) => {
this.xterm.options.fontFamily = family;
};
keyHandler = (evt: KeyboardEvent): boolean => { keyHandler = (evt: KeyboardEvent): boolean => {
const { code, ctrlKey, metaKey } = evt; const { code, ctrlKey, metaKey } = evt;