diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts index 5280f10599..36173c0e9d 100644 --- a/integration/__tests__/cluster-pages.tests.ts +++ b/integration/__tests__/cluster-pages.tests.ts @@ -378,11 +378,12 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => { await kubeApiServerRow.click(); await frame.waitForSelector(".Drawer", { state: "visible" }); - const logButton = await frame.waitForSelector("ul.KubeObjectMenu li.MenuItem i.Icon span[data-icon-name='subject']"); + const showPodLogsIcon = await frame.waitForSelector(".Drawer .drawer-title .Icon >> text=subject"); - await logButton.click(); + showPodLogsIcon.click(); // Check if controls are available + await frame.waitForSelector(".Dock.isOpen"); await frame.waitForSelector(".LogList .VirtualList"); await frame.waitForSelector(".LogResourceSelector"); @@ -447,31 +448,31 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => { await frame.waitForTimeout(100_000); } - const inputField = await frame.waitForSelector(".CreateResource div.react-monaco-editor-container"); + const testPodName = "nginx-create-pod-test"; + const monacoEditor = await frame.waitForSelector(`.Dock.isOpen [data-test-component="monaco-editor"]`); - await inputField.click(); - await inputField.type("apiVersion: v1", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type("kind: Pod", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type("metadata:", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type(" name: nginx-create-pod-test", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type(`namespace: ${TEST_NAMESPACE}`, { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.press("Backspace", { delay: 10 }); - await inputField.type("spec:", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type(" containers:", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type("- name: nginx-create-pod-test", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); - await inputField.type(" image: nginx:alpine", { delay: 10 }); - await inputField.press("Enter", { delay: 10 }); + await monacoEditor.click(); + await monacoEditor.type("apiVersion: v1", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type("kind: Pod", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type("metadata:", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type(` name: ${testPodName}`, { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type(`namespace: ${TEST_NAMESPACE}`, { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.press("Backspace", { delay: 10 }); + await monacoEditor.type("spec:", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type(" containers:", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type(`- name: ${testPodName}`, { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); + await monacoEditor.type(" image: nginx:alpine", { delay: 10 }); + await monacoEditor.press("Enter", { delay: 10 }); - await frame.click("button.Button >> text='Create & Close'"); - await frame.click("div.TableCell >> text=nginx-create-pod-test"); - await frame.waitForSelector("div.drawer-title-text >> text='Pod: nginx-create-pod-test'"); + await frame.click(".Dock .Button >> text='Create'"); + await frame.waitForSelector(`.TableCell >> text=${testPodName}`); }, 10*60*1000); }); diff --git a/package.json b/package.json index d8629829ab..aa41f85a78 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,8 @@ "mock-fs": "^4.14.0", "moment": "^2.29.1", "moment-timezone": "^0.5.33", - "monaco-editor": "^0.26.1", + "monaco-editor": "^0.29.1", + "monaco-editor-webpack-plugin": "^5.0.0", "node-fetch": "^2.6.6", "node-pty": "^0.10.1", "npm": "^6.14.15", @@ -228,7 +229,6 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-material-ui-carousel": "^2.3.5", - "react-monaco-editor": "^0.44.0", "react-router": "^5.2.0", "react-virtualized-auto-sizer": "^1.0.6", "readable-stream": "^3.6.0", diff --git a/src/common/__tests__/search-store.test.ts b/src/common/__tests__/search-store.test.ts index b83477d42d..bfb183ccdc 100644 --- a/src/common/__tests__/search-store.test.ts +++ b/src/common/__tests__/search-store.test.ts @@ -23,8 +23,6 @@ import { SearchStore } from "../search-store"; import { Console } from "console"; import { stdout, stderr } from "process"; -jest.mock("react-monaco-editor", () => null); - jest.mock("electron", () => ({ app: { getPath: () => "/foo", diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index a20730acdb..1f6050017e 100644 --- a/src/common/user-store/preferences-helpers.ts +++ b/src/common/user-store/preferences-helpers.ts @@ -24,7 +24,7 @@ import path from "path"; import os from "os"; import { ThemeStore } from "../../renderer/theme.store"; import { getAppVersion, ObservableToggleSet } from "../utils"; -import type { monaco } from "react-monaco-editor"; +import type { editor } from "monaco-editor"; import merge from "lodash/merge"; import { SemVer } from "semver"; @@ -32,20 +32,19 @@ export interface KubeconfigSyncEntry extends KubeconfigSyncValue { filePath: string; } -export interface KubeconfigSyncValue { } - -export interface EditorConfiguration { - miniMap?: monaco.editor.IEditorMinimapOptions; - lineNumbers?: monaco.editor.LineNumbersType; - tabSize?: number; +export interface KubeconfigSyncValue { } +export type EditorConfiguration = Pick; + export const defaultEditorConfig: EditorConfiguration = { - lineNumbers: "on", - miniMap: { - enabled: true, - }, tabSize: 2, + lineNumbers: "on", + minimap: { + enabled: true, + side: "right", + }, }; interface PreferenceDescription { diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index 1e4cae3089..ffe9b2f289 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -21,18 +21,16 @@ import { app, ipcMain } from "electron"; import semver, { SemVer } from "semver"; -import { action, computed, observable, reaction, makeObservable } from "mobx"; +import { action, computed, makeObservable, observable, reaction } from "mobx"; import { BaseStore } from "../base-store"; -import migrations from "../../migrations/user-store"; +import migrations, { fileNameMigration } from "../../migrations/user-store"; import { getAppVersion } from "../utils/app-version"; import { kubeConfigDefaultPath } from "../kube-helpers"; import { appEventBus } from "../event-bus"; import path from "path"; -import { fileNameMigration } from "../../migrations/user-store"; import { ObservableToggleSet, toJS } from "../../renderer/utils"; -import { DESCRIPTORS, KubeconfigSyncValue, UserPreferencesModel, EditorConfiguration } from "./preferences-helpers"; +import { DESCRIPTORS, EditorConfiguration, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; import logger from "../../main/logger"; -import type { monaco } from "react-monaco-editor"; import { AppPaths } from "../app-paths"; export interface UserStoreModel { @@ -92,7 +90,7 @@ export class UserStore extends BaseStore /* implements UserStore /** * Monaco editor configs */ - @observable editorConfiguration:EditorConfiguration = { tabSize: null, miniMap: null, lineNumbers: null }; + @observable editorConfiguration: EditorConfiguration; /** * The set of file/folder paths to be synced @@ -129,28 +127,6 @@ export class UserStore extends BaseStore /* implements UserStore }); } - // Returns monaco editor options for selected editor type (the place, where a particular instance of the editor is mounted) - getEditorOptions(): monaco.editor.IStandaloneEditorConstructionOptions { - return { - automaticLayout: true, - tabSize: this.editorConfiguration.tabSize, - minimap: this.editorConfiguration.miniMap, - lineNumbers: this.editorConfiguration.lineNumbers, - }; - } - - setEditorLineNumbers(lineNumbers: monaco.editor.LineNumbersType) { - this.editorConfiguration.lineNumbers = lineNumbers; - } - - setEditorTabSize(tabSize: number) { - this.editorConfiguration.tabSize = tabSize; - } - - enableEditorMinimap(miniMap: boolean ) { - this.editorConfiguration.miniMap.enabled = miniMap; - } - /** * Checks if a column (by ID) for a table (by ID) is configured to be hidden * @param tableId The ID of the table to be checked against diff --git a/src/extensions/registries/__tests__/page-registry.test.ts b/src/extensions/registries/__tests__/page-registry.test.ts index a6ff672401..00d0096197 100644 --- a/src/extensions/registries/__tests__/page-registry.test.ts +++ b/src/extensions/registries/__tests__/page-registry.test.ts @@ -19,19 +19,18 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { jest } from "@jest/globals"; import { ClusterPageRegistry, getExtensionPageUrl, GlobalPageRegistry, PageParams } from "../page-registry"; import { LensExtension } from "../../lens-extension"; import React from "react"; import fse from "fs-extra"; import { Console } from "console"; -import { stdout, stderr } from "process"; +import { stderr, stdout } from "process"; +import { TerminalStore } from "../../../renderer/components/dock/terminal.store"; import { ThemeStore } from "../../../renderer/theme.store"; -import { TerminalStore } from "../../renderer-api/components"; import { UserStore } from "../../../common/user-store"; import { AppPaths } from "../../../common/app-paths"; -jest.mock("react-monaco-editor", () => null); - jest.mock("electron", () => ({ app: { getVersion: () => "99.99.99", diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 9839cb6f42..ab19e5a64a 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -28,7 +28,6 @@ import * as ReactRouter from "react-router"; import * as ReactRouterDom from "react-router-dom"; import * as LensExtensionsCommonApi from "../extensions/common-api"; import * as LensExtensionsRendererApi from "../extensions/renderer-api"; -import { monaco } from "react-monaco-editor"; import { render } from "react-dom"; import { delay } from "../common/utils"; import { isMac, isDevelopment } from "../common/vars"; @@ -48,14 +47,15 @@ import { FilesystemProvisionerStore } from "../main/extension-filesystem"; import { ThemeStore } from "./theme.store"; import { SentryInit } from "../common/sentry"; import { TerminalStore } from "./components/dock/terminal.store"; -import cloudsMidnight from "./monaco-themes/Clouds Midnight.json"; import { AppPaths } from "../common/app-paths"; +import { registerCustomThemes } from "./components/monaco-editor"; if (process.isMainFrame) { SentryInit(); } -configurePackages(); +configurePackages(); // global packages +registerCustomThemes(); // monaco editor themes /** * If this is a development build, wait a second to attach @@ -106,12 +106,6 @@ export async function bootstrap(comp: () => Promise) { ExtensionsStore.createInstance(); FilesystemProvisionerStore.createInstance(); - // define Monaco Editor themes - const { base, ...params } = cloudsMidnight; - const baseTheme = base as monaco.editor.BuiltinTheme; - - monaco.editor.defineTheme("clouds-midnight", { base: baseTheme, ...params }); - // ThemeStore depends on: UserStore ThemeStore.createInstance(); diff --git a/src/renderer/components/+add-cluster/add-cluster.scss b/src/renderer/components/+add-cluster/add-cluster.module.css similarity index 72% rename from src/renderer/components/+add-cluster/add-cluster.scss rename to src/renderer/components/+add-cluster/add-cluster.module.css index 2ede9dc271..f80ed3bdea 100644 --- a/src/renderer/components/+add-cluster/add-cluster.scss +++ b/src/renderer/components/+add-cluster/add-cluster.module.css @@ -20,38 +20,26 @@ */ .AddClusters { - --flex-gap: #{$unit * 2}; - $spacing: $padding * 2; - - .MonacoEditor { - min-height: 600px; - max-height: 600px; - border: 1px solid var(--colorVague); - border-radius: $radius; - - .theme-light & { - border-color: var(--borderFaintColor); - } - - .editor { - border-radius: $radius; - } - } + --flex-gap: calc(var(--unit) * 2); code { - color: $pink-400; - } - - .text-primary { - color: var(--textColorAccent); - } - - .hint { - display: block; - padding-top: 6px; + color: rgb(236, 64, 122); } a[href] { color: var(--colorInfo); } } + +.editor { + min-height: 600px; + max-height: 600px; + border: 1px solid var(--colorVague); + border-radius: var(--border-radius); +} + +:global(.theme-light) { + .editor { + border-color: var(--borderFaintColor); + } +} diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 7773ba5e9c..f6b683f654 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -19,12 +19,12 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./add-cluster.scss"; +import styles from "./add-cluster.module.css"; import type { KubeConfig } from "@kubernetes/client-node"; import fse from "fs-extra"; import { debounce } from "lodash"; -import { action, computed, observable, makeObservable } from "mobx"; +import { action, computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import path from "path"; import React from "react"; @@ -34,13 +34,11 @@ import { appEventBus } from "../../../common/event-bus"; import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers"; import { docsUrl } from "../../../common/vars"; import { navigate } from "../../navigation"; -import { getCustomKubeConfigPath, cssNames, iter } from "../../utils"; +import { getCustomKubeConfigPath, iter } from "../../utils"; import { Button } from "../button"; import { Notifications } from "../notifications"; import { SettingLayout } from "../layout/setting-layout"; -import MonacoEditor from "react-monaco-editor"; -import { ThemeStore } from "../../theme.store"; -import { UserStore } from "../../../common/user-store"; +import { MonacoEditor } from "../monaco-editor"; interface Option { config: KubeConfig; @@ -85,7 +83,7 @@ export class AddCluster extends React.Component { const { config, error } = loadConfigFromString(this.customConfig.trim() || "{}"); this.kubeContexts.replace(getContexts(config)); - + if (error) { this.errors.push(error.toString()); } @@ -116,7 +114,7 @@ export class AddCluster extends React.Component { render() { return ( - +

Add Clusters from Kubeconfig

Clusters added here are not merged into the ~/.kube/config file.{" "} @@ -124,10 +122,8 @@ export class AddCluster extends React.Component {

{ this.customConfig = value; diff --git a/src/renderer/components/+apps-releases/release-details.scss b/src/renderer/components/+apps-releases/release-details.scss index 759bd5cec4..e807290f81 100644 --- a/src/renderer/components/+apps-releases/release-details.scss +++ b/src/renderer/components/+apps-releases/release-details.scss @@ -86,13 +86,7 @@ font-size: small; } - .values { - .MonacoEditor { - min-height: 300px; - } - - .MonacoEditor + .Button { - align-self: flex-start; - } + .values + .Button { + align-self: flex-start; } } \ No newline at end of file diff --git a/src/renderer/components/+apps-releases/release-details.tsx b/src/renderer/components/+apps-releases/release-details.tsx index ad0db6b1b4..0a3d947411 100644 --- a/src/renderer/components/+apps-releases/release-details.tsx +++ b/src/renderer/components/+apps-releases/release-details.tsx @@ -24,7 +24,7 @@ import "./release-details.scss"; import React, { Component } from "react"; import groupBy from "lodash/groupBy"; import isEqual from "lodash/isEqual"; -import { observable, reaction, makeObservable } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { Link } from "react-router-dom"; import kebabCase from "lodash/kebabCase"; import { getRelease, getReleaseValues, HelmRelease, IReleaseDetails } from "../../../common/k8s-api/endpoints/helm-releases.api"; @@ -46,8 +46,7 @@ import { secretsStore } from "../+config-secrets/secrets.store"; import { Secret } from "../../../common/k8s-api/endpoints"; import { getDetailsUrl } from "../kube-detail-params"; import { Checkbox } from "../checkbox"; -import MonacoEditor from "react-monaco-editor"; -import { UserStore } from "../../../common/user-store"; +import { MonacoEditor } from "../monaco-editor"; interface Props { release: HelmRelease; @@ -97,7 +96,7 @@ export class ReleaseDetails extends Component { async loadDetails() { const { release } = this.props; - + try { this.details = null; this.details = await getRelease(release.getName(), release.getNs()); @@ -165,14 +164,13 @@ export class ReleaseDetails extends Component { disabled={valuesLoading} /> this.values = text} - theme={ThemeStore.getInstance().activeTheme.monacoTheme} - className={cssNames("MonacoEditor", { loading: valuesLoading })} - options={{ readOnly: valuesLoading, ...UserStore.getInstance().getEditorOptions() }} > - {valuesLoading && } + {valuesLoading && }
); diff --git a/src/renderer/components/+preferences/editor.tsx b/src/renderer/components/+preferences/editor.tsx index c5b24172c2..6f346eb63d 100644 --- a/src/renderer/components/+preferences/editor.tsx +++ b/src/renderer/components/+preferences/editor.tsx @@ -22,10 +22,10 @@ import { observer } from "mobx-react"; import React from "react"; import { UserStore } from "../../../common/user-store"; import { FormSwitch, Switcher } from "../switch"; +import { Select } from "../select"; import { SubTitle } from "../layout/sub-title"; -import { Input } from "../input"; -import { isNumber } from "../input/input_validators"; -import { Select, SelectOption } from "../select"; +import { SubHeader } from "../layout/sub-header"; +import { Input, InputValidators } from "../input"; enum EditorLineNumbersStyles { on = "On", @@ -35,50 +35,60 @@ enum EditorLineNumbersStyles { } export const Editor = observer(() => { - const userStore = UserStore.getInstance(); + const editorConfiguration = UserStore.getInstance().editorConfiguration; return (

Editor configuration

+ +
- userStore.enableEditorMinimap(v.target.checked)} - name="minimap" +
+
+ Show minimap} + control={ + editorConfiguration.minimap.enabled = checked} + /> + } /> - } - label="Show minimap" - /> +
+
+ Position + ({ label: entry[1], value: entry[0] }))} - value={userStore.editorConfiguration?.lineNumbers} - onChange={({ value }: SelectOption) => userStore.setEditorLineNumbers(value)} + options={Object.entries(EditorLineNumbersStyles).map(([value, label]) => ({ label, value }))} + value={editorConfiguration.lineNumbers} + onChange={({ value }) => editorConfiguration.lineNumbers = value} themeName="lens" />
+
{ - const n = Number(value); - - if (!isNaN(n)) { - userStore.setEditorTabSize(n); - } - }} + validators={InputValidators.isNumber} + value={editorConfiguration.tabSize.toString()} + onChange={value => editorConfiguration.tabSize = Number(value)} />
); }); - + diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss b/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss index 383690c28f..2136054cd8 100644 --- a/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss @@ -20,7 +20,4 @@ */ .AddRoleDialog { - .MonacoEditor { - min-height: 200px; - } } diff --git a/src/renderer/components/+workloads-pods/pod-details-affinities.tsx b/src/renderer/components/+workloads-pods/pod-details-affinities.tsx index 3dfcca76cb..16885aedf5 100644 --- a/src/renderer/components/+workloads-pods/pod-details-affinities.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-affinities.tsx @@ -19,15 +19,11 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./pod-details-affinities.scss"; import React from "react"; import yaml from "js-yaml"; -import { DrawerParamToggler, DrawerItem } from "../drawer"; -import type { Pod, Deployment, DaemonSet, StatefulSet, ReplicaSet, Job } from "../../../common/k8s-api/endpoints"; -import MonacoEditor from "react-monaco-editor"; -import { cssNames } from "../../utils"; -import { ThemeStore } from "../../theme.store"; -import { UserStore } from "../../../common/user-store"; +import { DrawerItem, DrawerParamToggler } from "../drawer"; +import type { DaemonSet, Deployment, Job, Pod, ReplicaSet, StatefulSet } from "../../../common/k8s-api/endpoints"; +import { MonacoEditor } from "../monaco-editor"; interface Props { workload: Pod | Deployment | DaemonSet | StatefulSet | ReplicaSet | Job; @@ -44,15 +40,11 @@ export class PodDetailsAffinities extends React.Component { return ( -
- -
+
); diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx index 3d2c1a397c..9d4ae2b345 100644 --- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx +++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx @@ -23,7 +23,6 @@ import React from "react"; import { fireEvent, render } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import fse from "fs-extra"; - import { DockTabs } from "../dock-tabs"; import { dockStore, DockTab, TabKind } from "../dock.store"; import { noop } from "../../../utils"; @@ -32,14 +31,6 @@ import { TerminalStore } from "../terminal.store"; import { UserStore } from "../../../../common/user-store"; import { AppPaths } from "../../../../common/app-paths"; -jest.mock("react-monaco-editor", () => ({ - monaco: { - editor: { - getModel: jest.fn(), - }, - }, -})); - jest.mock("electron", () => ({ app: { getVersion: () => "99.99.99", diff --git a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx index 7f3c24bc73..b87adba0a7 100644 --- a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx +++ b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx @@ -33,8 +33,6 @@ import { UserStore } from "../../../../common/user-store"; import mockFs from "mock-fs"; import { AppPaths } from "../../../../common/app-paths"; -jest.mock("react-monaco-editor", () => null); - jest.mock("electron", () => ({ app: { getVersion: () => "99.99.99", diff --git a/src/renderer/components/dock/__test__/log-tab.store.test.ts b/src/renderer/components/dock/__test__/log-tab.store.test.ts index c46450ada9..a342ddce79 100644 --- a/src/renderer/components/dock/__test__/log-tab.store.test.ts +++ b/src/renderer/components/dock/__test__/log-tab.store.test.ts @@ -32,8 +32,6 @@ import { AppPaths } from "../../../../common/app-paths"; mockWindow(); -jest.mock("react-monaco-editor", () => null); - jest.mock("electron", () => ({ app: { getVersion: () => "99.99.99", diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index ee55b2c5f2..51c039a841 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -24,30 +24,27 @@ import "./create-resource.scss"; import React from "react"; import path from "path"; import fs from "fs-extra"; -import { Select, GroupSelectOption, SelectOption } from "../select"; +import { GroupSelectOption, Select, SelectOption } from "../select"; import yaml from "js-yaml"; -import { observable, makeObservable } from "mobx"; +import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; -import { cssNames } from "../../utils"; import { createResourceStore } from "./create-resource.store"; import type { DockTab } from "./dock.store"; import { EditorPanel } from "./editor-panel"; import { InfoPanel } from "./info-panel"; import * as resourceApplierApi from "../../../common/k8s-api/endpoints/resource-applier.api"; import { Notifications } from "../notifications"; -import { monacoModelsManager } from "./monaco-model-manager"; import logger from "../../../common/logger"; interface Props { - className?: string; tab: DockTab; } @observer export class CreateResource extends React.Component { - @observable currentTemplates:Map = new Map(); + @observable currentTemplates: Map = new Map(); @observable error = ""; - @observable templates:GroupSelectOption[] = []; + @observable templates: GroupSelectOption[] = []; constructor(props: Props) { super(props); @@ -59,12 +56,12 @@ export class CreateResource extends React.Component { createResourceStore.watchUserTemplates(() => createResourceStore.getMergedTemplates().then(v => this.updateGroupSelectOptions(v))); } - updateGroupSelectOptions(templates :Record) { + updateGroupSelectOptions(templates: Record) { this.templates = Object.entries(templates) .map(([name, grouping]) => this.convertToGroup(name, grouping)); } - convertToGroup(group:string, items:string[]):GroupSelectOption { + convertToGroup(group: string, items: string[]): GroupSelectOption { const options = items.map(v => ({ label: path.parse(v).name, value: v })); return { label: group, options }; @@ -82,16 +79,19 @@ export class CreateResource extends React.Component { return this.currentTemplates.get(this.tabId) ?? null; } - onChange = (value: string, error?: string) => { + onChange = (value: string) => { + this.error = ""; // reset first, validation goes later createResourceStore.setData(this.tabId, value); - this.error = error; + }; + + onError = (error: Error | string) => { + this.error = error.toString(); }; onSelectTemplate = (item: SelectOption) => { this.currentTemplates.set(this.tabId, item); fs.readFile(item.value, "utf8").then(v => { createResourceStore.setData(this.tabId, v); - monacoModelsManager.getModel(this.tabId).setValue(v ?? ""); }); }; @@ -129,42 +129,42 @@ export class CreateResource extends React.Component { return undefined; }; - renderControls(){ + renderControls() { return (
{
); diff --git a/src/renderer/components/dock/terminal-window.scss b/src/renderer/components/dock/terminal-window.scss index c243c6c71a..630916f5e0 100644 --- a/src/renderer/components/dock/terminal-window.scss +++ b/src/renderer/components/dock/terminal-window.scss @@ -22,11 +22,11 @@ @import "~xterm"; .TerminalWindow { - margin: $padding; - margin-left: $padding * 2; - margin-top: $padding * 2; + --spacing: calc(var(--unit) * 2); - > .xterm { - overflow: hidden; - } + flex: 1; + overflow: hidden; + left: var(--spacing) !important; + top: var(--spacing) !important; + bottom: var(--spacing) !important; } \ No newline at end of file diff --git a/src/renderer/components/dock/terminal-window.tsx b/src/renderer/components/dock/terminal-window.tsx index 8e7a913152..233f74859a 100644 --- a/src/renderer/components/dock/terminal-window.tsx +++ b/src/renderer/components/dock/terminal-window.tsx @@ -22,16 +22,14 @@ import "./terminal-window.scss"; import React from "react"; -import { reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { cssNames } from "../../utils"; -import type { DockTab } from "./dock.store"; import type { Terminal } from "./terminal"; -import { terminalStore } from "./terminal.store"; +import { TerminalStore } from "./terminal.store"; import { ThemeStore } from "../../theme.store"; +import { dockStore, DockTab, TabKind, TabId } from "./dock.store"; interface Props { - className?: string; tab: DockTab; } @@ -42,25 +40,29 @@ export class TerminalWindow extends React.Component { componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.props.tab.id, tabId => this.activate(tabId), { + dockStore.onTabChange(({ tabId }) => this.activate(tabId), { + tabKind: TabKind.TERMINAL, + fireImmediately: true, + }), + + // refresh terminal available space (cols/rows) when resized + dockStore.onResize(() => this.terminal?.fitLazy(), { fireImmediately: true, }), ]); } - activate(tabId = this.props.tab.id) { + activate(tabId: TabId) { if (this.terminal) this.terminal.detach(); // detach previous - this.terminal = terminalStore.getTerminal(tabId); + this.terminal = TerminalStore.getInstance().getTerminal(tabId); this.terminal.attachTo(this.elem); } render() { - const { className } = this.props; - return (
this.elem = e} + className={cssNames("TerminalWindow", ThemeStore.getInstance().activeTheme.type)} + ref={elem => this.elem = elem} /> ); } diff --git a/src/renderer/components/dock/upgrade-chart.store.ts b/src/renderer/components/dock/upgrade-chart.store.ts index 31152c297d..81230466b9 100644 --- a/src/renderer/components/dock/upgrade-chart.store.ts +++ b/src/renderer/components/dock/upgrade-chart.store.ts @@ -25,7 +25,6 @@ import { DockTabStore } from "./dock-tab.store"; import { getReleaseValues, HelmRelease } from "../../../common/k8s-api/endpoints/helm-releases.api"; import { releaseStore } from "../+apps-releases/release.store"; import { iter } from "../../utils"; -import { monacoModelsManager } from "./monaco-model-manager"; export interface IChartUpgradeData { releaseName: string; @@ -119,7 +118,6 @@ export class UpgradeChartStore extends DockTabStore { const values = await getReleaseValues(releaseName, releaseNamespace, true); this.values.setData(tabId, values); - monacoModelsManager.getModel(tabId).setValue(values); } getTabByRelease(releaseName: string): DockTab { diff --git a/src/renderer/components/dock/upgrade-chart.tsx b/src/renderer/components/dock/upgrade-chart.tsx index e127f3140b..4eaaaa4d21 100644 --- a/src/renderer/components/dock/upgrade-chart.tsx +++ b/src/renderer/components/dock/upgrade-chart.tsx @@ -22,7 +22,7 @@ import "./upgrade-chart.scss"; import React from "react"; -import { observable, reaction, makeObservable } from "mobx"; +import { action, makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { cssNames } from "../../utils"; import type { DockTab } from "./dock.store"; @@ -86,9 +86,15 @@ export class UpgradeChart extends React.Component { this.version = this.versions[0]; } - onChange = (value: string, error?: string) => { + @action + onChange = (value: string) => { + this.error = ""; upgradeChartStore.values.setData(this.tabId, value); - this.error = error; + }; + + @action + onError = (error: Error | string) => { + this.error = error.toString(); }; upgrade = async () => { @@ -118,7 +124,7 @@ export class UpgradeChart extends React.Component { }; render() { - const { tabId, release, value, error, onChange, upgrade, versions, version } = this; + const { tabId, release, value, error, onChange, onError, upgrade, versions, version } = this; const { className } = this.props; if (!release || upgradeChartStore.isLoading() || !version) { @@ -157,6 +163,7 @@ export class UpgradeChart extends React.Component { tabId={tabId} value={value} onChange={onChange} + onError={onError} />
); diff --git a/src/renderer/components/input/input.scss b/src/renderer/components/input/input.scss index a14f6ee2e1..7455d6f8b9 100644 --- a/src/renderer/components/input/input.scss +++ b/src/renderer/components/input/input.scss @@ -63,6 +63,10 @@ } } + input[type=number]::-webkit-inner-spin-button { + -webkit-appearance: none; // hide browser controls (top/bottom arrows) + } + input, textarea { background: none; color: inherit; diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.scss b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.css similarity index 89% rename from src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.scss rename to src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.css index 110df20d91..364d10ad01 100644 --- a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.scss +++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.css @@ -20,22 +20,22 @@ */ .KubeConfigDialog { - .theme-light & { - .MonacoEditor { - border: 1px solid gainsboro; - border-radius: $radius; - } - } - - .Wizard { + :global(.Wizard) { width: 50vw; min-width: 600px; --wizard-content-height: 600px; } - .config-copy { + .configCopy { position: absolute; opacity: 0; pointer-events: none; } -} \ No newline at end of file +} + +:global(.theme-light) { + .editor { + border: 1px solid gainsboro; + border-radius: var(--border-radius); + } +} diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx index 2cd9408153..8c9f6a44d1 100644 --- a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx +++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx @@ -19,23 +19,20 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./kubeconfig-dialog.scss"; - +import styles from "./kubeconfig-dialog.module.css"; import React from "react"; -import { observable, makeObservable } from "mobx"; +import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import yaml from "js-yaml"; import type { ServiceAccount } from "../../../common/k8s-api/endpoints"; -import { copyToClipboard, cssNames, saveFileDialog } from "../../utils"; +import { copyToClipboard, saveFileDialog } from "../../utils"; import { Button } from "../button"; import { Dialog, DialogProps } from "../dialog"; import { Icon } from "../icon"; import { Notifications } from "../notifications"; import { Wizard, WizardStep } from "../wizard"; import { apiBase } from "../../api"; -import MonacoEditor from "react-monaco-editor"; -import { ThemeStore } from "../../theme.store"; -import { UserStore } from "../../../common/user-store"; +import { MonacoEditor } from "../monaco-editor"; interface IKubeconfigDialogData { title?: React.ReactNode; @@ -122,7 +119,7 @@ export class KubeConfigDialog extends React.Component { return ( {