diff --git a/package.json b/package.json index 6b19e77edc..29443c28e0 100644 --- a/package.json +++ b/package.json @@ -221,6 +221,7 @@ "moment": "^2.29.1", "moment-timezone": "^0.5.33", "monaco-editor": "^0.26.1", + "monaco-editor-webpack-plugin": "^4.2.0", "node-fetch": "^2.6.1", "node-pty": "^0.10.1", "npm": "^6.14.15", diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index 59f201f55f..3c36911949 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 { ObservableToggleSet } from "../utils"; -import type {monaco} from "react-monaco-editor"; +import type * as monaco from "monaco-editor"; import merge from "lodash/merge"; export interface KubeconfigSyncEntry extends KubeconfigSyncValue { @@ -34,9 +34,9 @@ export interface KubeconfigSyncEntry extends KubeconfigSyncValue { export interface KubeconfigSyncValue { } export interface EditorConfiguration { - miniMap?: monaco.editor.IEditorMinimapOptions; - lineNumbers?: monaco.editor.LineNumbersType; - tabSize?: number; + miniMap: monaco.editor.IEditorMinimapOptions; + lineNumbers: monaco.editor.LineNumbersType; + tabSize: number; } export const defaultEditorConfig: EditorConfiguration = { diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index ee0022da1b..44aa209fdc 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -21,18 +21,16 @@ import { app } from "electron"; import 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 { defaultEditorConfig, DESCRIPTORS, EditorConfiguration, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; import logger from "../../main/logger"; -import type { monaco } from "react-monaco-editor"; import { getPath } from "../utils/getPath"; export interface UserStoreModel { @@ -86,7 +84,7 @@ export class UserStore extends BaseStore /* implements UserStore /** * Monaco editor configs */ - @observable editorConfiguration:EditorConfiguration = {tabSize: null, miniMap: null, lineNumbers: null}; + @observable editorConfiguration: EditorConfiguration = defaultEditorConfig; /** * The set of file/folder paths to be synced @@ -119,28 +117,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/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index b6b97cfc6b..3e7daa5404 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, unmountComponentAtNode } from "react-dom"; import { delay } from "../common/utils"; import { isMac, isDevelopment } from "../common/vars"; @@ -50,7 +49,6 @@ 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"; configurePackages(); @@ -104,12 +102,6 @@ export async function bootstrap(App: AppComponent) { 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.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index 81a75019fa..ac6a2de5cd 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -24,7 +24,7 @@ import "./add-cluster.scss"; 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()); } @@ -124,12 +122,8 @@ export class AddCluster extends React.Component {

{ + onChange={(value: string) => { this.customConfig = value; this.errors.length = 0; this.refreshContexts(); diff --git a/src/renderer/components/+apps-releases/release-details.tsx b/src/renderer/components/+apps-releases/release-details.tsx index 4e93136c03..5e17ca1cdd 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; @@ -160,14 +159,12 @@ export class ReleaseDetails extends Component { disabled={valuesLoading} /> this.values = text} - theme={ThemeStore.getInstance().activeTheme.monacoTheme} - className={cssNames("MonacoEditor", {loading: valuesLoading})} - options={{readOnly: valuesLoading || this.showOnlyUserSuppliedValues, ...UserStore.getInstance().getEditorOptions()}} > - {valuesLoading && } + {valuesLoading && }
diff --git a/src/renderer/components/+preferences/editor.tsx b/src/renderer/components/+preferences/editor.tsx index 707bb14428..a9d4ac3abf 100644 --- a/src/renderer/components/+preferences/editor.tsx +++ b/src/renderer/components/+preferences/editor.tsx @@ -25,7 +25,7 @@ import { FormSwitch, Switcher } from "../switch"; import { SubTitle } from "../layout/sub-title"; import { Input } from "../input"; import { isNumber } from "../input/input_validators"; -import { Select, SelectOption } from "../select"; +import { Select } from "../select"; enum EditorLineNumbersStyles { on = "On", @@ -35,6 +35,7 @@ enum EditorLineNumbersStyles { } export const Editor = observer(() => { + const editorConfiguration = UserStore.getInstance().editorConfiguration; return (
@@ -43,8 +44,8 @@ export const Editor = observer(() => { UserStore.getInstance().enableEditorMinimap(v.target.checked)} + checked={editorConfiguration.miniMap.enabled} + onChange={(evt, checked: boolean) => editorConfiguration.miniMap.enabled = checked} name="minimap" /> } @@ -54,9 +55,9 @@ export const Editor = observer(() => {
this.onSelectTemplate(v)} - value = {this.currentTemplate} + value={this.currentTemplate} /> ); } - render() { - const { tabId, data, error, create, onChange } = this; + const { tabId, data, error, create } = this; const { className } = this.props; return ( @@ -162,10 +163,11 @@ export class CreateResource extends React.Component { submitLabel="Create" showNotifications={false} /> - ); diff --git a/src/renderer/components/dock/dock.store.ts b/src/renderer/components/dock/dock.store.ts index 7551de1a2f..167b3de273 100644 --- a/src/renderer/components/dock/dock.store.ts +++ b/src/renderer/components/dock/dock.store.ts @@ -23,7 +23,6 @@ import * as uuid from "uuid"; import { action, computed, IReactionOptions, makeObservable, observable, reaction } from "mobx"; import { autoBind, createStorage } from "../../utils"; import throttle from "lodash/throttle"; -import {monacoModelsManager} from "./monaco-model-manager"; export type TabId = string; @@ -158,12 +157,6 @@ export class DockStore implements DockStorageState { private init() { // adjust terminal height if window size changes window.addEventListener("resize", throttle(this.adjustHeight, 250)); - // create monaco models - this.whenReady.then(() => {this.tabs.forEach(tab => { - if (this.usesMonacoEditor(tab)) { - monacoModelsManager.addModel(tab.id); - } - });}); } get maxHeight() { @@ -193,13 +186,6 @@ export class DockStore implements DockStorageState { return this.tabs.length > 0; } - usesMonacoEditor(tab: DockTab): boolean { - return [TabKind.CREATE_RESOURCE, - TabKind.EDIT_RESOURCE, - TabKind.INSTALL_CHART, - TabKind.UPGRADE_CHART].includes(tab.kind); - } - @action open(fullSize?: boolean) { this.isOpen = true; @@ -274,11 +260,6 @@ export class DockStore implements DockStorageState { title }; - // add monaco model - if (this.usesMonacoEditor(tab)) { - monacoModelsManager.addModel(id); - } - this.tabs.push(tab); this.selectTab(tab.id); this.open(); @@ -294,11 +275,6 @@ export class DockStore implements DockStorageState { return; } - // remove monaco model - if (this.usesMonacoEditor(tab)) { - monacoModelsManager.removeModel(tabId); - } - this.tabs = this.tabs.filter(tab => tab.id !== tabId); if (this.selectedTabId === tab.id) { diff --git a/src/renderer/components/dock/edit-resource.store.ts b/src/renderer/components/dock/edit-resource.store.ts index b5be580313..2c8d26bbb9 100644 --- a/src/renderer/components/dock/edit-resource.store.ts +++ b/src/renderer/components/dock/edit-resource.store.ts @@ -26,7 +26,6 @@ import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./doc import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { apiManager } from "../../../common/k8s-api/api-manager"; import type { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; -import {monacoModelsManager} from "./monaco-model-manager"; export interface EditingResource { resource: string; // resource path, e.g. /api/v1/namespaces/default @@ -62,7 +61,6 @@ export class EditResourceStore extends DockTabStore { // preload resource for editing if (!obj && !store.isLoaded && !store.isLoading && isActiveTab) { store.loadFromPath(resource).catch(noop); - monacoModelsManager.getModel(tabId).setValue(resource); } // auto-close tab when resource removed from store else if (!obj && store.isLoaded) { diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource.tsx index 19a238ab30..8c67780cf2 100644 --- a/src/renderer/components/dock/edit-resource.tsx +++ b/src/renderer/components/dock/edit-resource.tsx @@ -30,7 +30,7 @@ import { cssNames } from "../../utils"; import { editResourceStore } from "./edit-resource.store"; import { InfoPanel } from "./info-panel"; import { Badge } from "../badge"; -import { EditorPanel } from "./editor-panel"; +import { MonacoEditor } from "../monaco-editor"; import { Spinner } from "../spinner"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; @@ -86,11 +86,14 @@ export class EditResource extends React.Component { }); } - onChange = (draft: string, error?: string) => { - this.error = error; + onChange = (draft: string) => { this.saveDraft(draft); }; + onError = (error: string) => { + this.error = error; + }; + save = async () => { if (this.error) { return null; @@ -110,7 +113,7 @@ export class EditResource extends React.Component { }; render() { - const { tabId, error, onChange, save, draft, isReadyForEditing, resource } = this; + const { tabId, error, draft, isReadyForEditing, resource } = this; if (!isReadyForEditing) { return ; @@ -121,7 +124,7 @@ export class EditResource extends React.Component { { )} /> - ); diff --git a/src/renderer/components/dock/editor-panel.tsx b/src/renderer/components/dock/editor-panel.tsx deleted file mode 100644 index 31ccc5b2e2..0000000000 --- a/src/renderer/components/dock/editor-panel.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -import MonacoEditor, {monaco} from "react-monaco-editor"; -import React from "react"; -import jsYaml from "js-yaml"; -import { observable, makeObservable } from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; -import { dockStore, TabId } from "./dock.store"; -import { monacoModelsManager } from "./monaco-model-manager"; -import { ThemeStore } from "../../theme.store"; -import { UserStore } from "../../../common/user-store"; - -import "monaco-editor"; - -interface Props { - className?: string; - tabId: TabId; - value?: string; - onChange(value: string, error?: string): void; -} - - -@observer -export class EditorPanel extends React.Component { - model: monaco.editor.ITextModel; - public editor: monaco.editor.IStandaloneCodeEditor; - @observable yamlError = ""; - - constructor(props: Props) { - super(props); - makeObservable(this); - } - - componentDidMount() { - // validate and run callback with optional error - this.onChange(this.props.value || ""); - - disposeOnUnmount(this, [ - dockStore.onTabChange(this.onTabChange, { delay: 250 }), - dockStore.onResize(this.onResize, { delay: 250 }), - ]); - } - - editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => { - this.editor = editor; - const model = monacoModelsManager.getModel(this.props.tabId); - - model.setValue(this.props.value ?? ""); - this.editor.setModel(model); - }; - - validate(value: string) { - try { - jsYaml.safeLoadAll(value); - this.yamlError = ""; - } catch (err) { - this.yamlError = err.toString(); - } - } - - onTabChange = () => { - this.editor.focus(); - const model = monacoModelsManager.getModel(this.props.tabId); - - model.setValue(this.props.value ?? ""); - this.editor.setModel(model); - }; - - onResize = () => { - this.editor.focus(); - }; - - onChange = (value: string) => { - this.validate(value); - - if (this.props.onChange) { - this.props.onChange(value, this.yamlError); - } - }; - - render() { - return ( - - ); - } -} diff --git a/src/renderer/components/dock/install-chart.store.ts b/src/renderer/components/dock/install-chart.store.ts index 4f02629353..fb185ffb52 100644 --- a/src/renderer/components/dock/install-chart.store.ts +++ b/src/renderer/components/dock/install-chart.store.ts @@ -25,7 +25,6 @@ import { DockTabStore } from "./dock-tab.store"; import { getChartDetails, getChartValues, HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; import type { IReleaseUpdateDetails } from "../../../common/k8s-api/endpoints/helm-releases.api"; import { Notifications } from "../notifications"; -import { monacoModelsManager } from "./monaco-model-manager"; export interface IChartInstallData { name: string; @@ -91,15 +90,10 @@ export class InstallChartStore extends DockTabStore { if (values) { this.setData(tabId, { ...data, values }); - monacoModelsManager.getModel(tabId).setValue(values); } else if (attempt < 4) { return this.loadValues(tabId, attempt + 1); } } - - setData(tabId: TabId, data: IChartInstallData){ - super.setData(tabId, data); - } } export const installChartStore = new InstallChartStore(); diff --git a/src/renderer/components/dock/install-chart.tsx b/src/renderer/components/dock/install-chart.tsx index b0113018ea..2eabf49e4b 100644 --- a/src/renderer/components/dock/install-chart.tsx +++ b/src/renderer/components/dock/install-chart.tsx @@ -22,13 +22,13 @@ import "./install-chart.scss"; import React, { Component } from "react"; -import { observable, makeObservable } from "mobx"; +import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import { dockStore, DockTab } from "./dock.store"; import { InfoPanel } from "./info-panel"; import { Badge } from "../badge"; import { NamespaceSelect } from "../+namespaces/namespace-select"; -import { boundMethod, prevDefault } from "../../utils"; +import { prevDefault } from "../../utils"; import { IChartInstallData, installChartStore } from "./install-chart.store"; import { Spinner } from "../spinner"; import { Icon } from "../icon"; @@ -37,7 +37,7 @@ import { releaseStore } from "../+apps-releases/release.store"; import { LogsDialog } from "../dialog/logs-dialog"; import { Select, SelectOption } from "../select"; import { Input } from "../input"; -import { EditorPanel } from "./editor-panel"; +import { MonacoEditor } from "../monaco-editor"; import { navigate } from "../../navigation"; import { releaseURL } from "../../../common/routes"; @@ -75,8 +75,7 @@ export class InstallChart extends Component { return installChartStore.details.getData(this.tabId); } - @boundMethod - viewRelease() { + viewRelease = () => { const { release } = this.releaseDetails; navigate(releaseURL({ @@ -86,38 +85,32 @@ export class InstallChart extends Component { } })); dockStore.closeTab(this.tabId); - } + }; - @boundMethod - save(data: Partial) { + save = (data: Partial) => { const chart = { ...this.chartData, ...data }; installChartStore.setData(this.tabId, chart); - } + }; - @boundMethod - onVersionChange(option: SelectOption) { + onVersionChange = (option: SelectOption) => { const version = option.value; this.save({ version, values: "" }); installChartStore.loadValues(this.tabId); - } + }; - @boundMethod - onValuesChange(values: string, error?: string) { - this.error = error; + onValuesChange = (values: string) => { this.save({ values }); - } + }; - @boundMethod - onNamespaceChange(opt: SelectOption) { + onNamespaceChange = (opt: SelectOption) => { this.save({ namespace: opt.value }); - } + }; - @boundMethod - onReleaseNameChange(name: string) { + onReleaseNameChange = (name: string) => { this.save({ releaseName: name }); - } + }; install = async () => { const { repo, name, version, namespace, values, releaseName } = this.chartData; @@ -138,14 +131,14 @@ export class InstallChart extends Component { const { tabId, chartData, values, versions, install } = this; if (chartData?.values === undefined || !versions) { - return ; + return ; } if (this.releaseDetails) { return (

- +

Installation complete!

@@ -174,7 +167,7 @@ export class InstallChart extends Component { const panelControls = (
Chart - + Version