This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch
4200-replace-ngx-codemirror-with-ngx-monaco-editor-in-angular-ui
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to
refs/heads/4200-replace-ngx-codemirror-with-ngx-monaco-editor-in-angular-ui by
this push:
new 0ec9cf18e5 refactor(#4200): Replace ngx-codemirror with
ngx-monaco-editor
0ec9cf18e5 is described below
commit 0ec9cf18e5b43c2607d613626bd48038143ee61e
Author: Philipp Zehnder <[email protected]>
AuthorDate: Wed Feb 25 11:01:54 2026 +0100
refactor(#4200): Replace ngx-codemirror with ngx-monaco-editor
---
ui/angular.json | 7 +-
ui/cypress/support/utils/connect/ConnectBtns.ts | 10 +-
.../support/utils/userInput/StaticPropertyUtils.ts | 9 +-
.../utils/userInput/TreeStaticPropertyUtils.ts | 10 +-
.../opcua/opcAdapterConfiguration.smoke.spec.ts | 11 +-
ui/cypress/tests/connect/scriptTemplate.spec.ts | 9 +-
.../tests/pipelineElement/SinglePipelineElement.ts | 2 +-
ui/package-lock.json | 64 ++++-------
ui/package.json | 4 +-
ui/src/app/app.config.ts | 4 +
.../email-template-configuration.component.html | 19 ++--
.../email-template-configuration.component.scss | 22 ++++
.../email-template-configuration.component.ts | 23 ++--
.../configure-schema.component.html | 1 +
.../configure-schema.component.scss | 19 ----
.../configure-schema/configure-schema.component.ts | 27 ++---
.../adapter-script-editor.component.html | 5 +-
.../adapter-script-editor.component.scss} | 13 ++-
.../adapter-script-editor.component.ts | 60 ++++++++++-
.../static-code-input.component.html | 23 ++--
.../static-code-input.component.scss | 19 +++-
.../static-code-input.component.ts | 118 ++++++++++++---------
.../static-tree-input-text-editor.component.html | 6 +-
.../static-tree-input-text-editor.component.scss} | 6 +-
.../static-tree-input-text-editor.component.ts | 23 ++--
ui/src/scss/main.scss | 6 --
ui/src/scss/sp/main.scss | 4 -
27 files changed, 312 insertions(+), 212 deletions(-)
diff --git a/ui/angular.json b/ui/angular.json
index 221614d1f8..27051005fa 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -17,13 +17,18 @@
"index": "src/index.html",
"tsConfig": "src/tsconfig.app.json",
"polyfills": ["src/polyfills.ts", "zone.js"],
- "allowedCommonJsDependencies": ["codemirror"],
+ "allowedCommonJsDependencies": [],
"assets": [
"src/assets",
{
"glob": "**/*",
"input": "node_modules/leaflet/dist/images",
"output": "assets/img"
+ },
+ {
+ "glob": "**/*",
+ "input": "node_modules/monaco-editor",
+ "output": "/assets/monaco/"
}
],
"styles": ["src/scss/main.scss"],
diff --git a/ui/cypress/support/utils/connect/ConnectBtns.ts
b/ui/cypress/support/utils/connect/ConnectBtns.ts
index 54c9c71419..864a6a2470 100644
--- a/ui/cypress/support/utils/connect/ConnectBtns.ts
+++ b/ui/cypress/support/utils/connect/ConnectBtns.ts
@@ -87,6 +87,7 @@ export class ConnectBtns {
public static getNewSampleBtn() {
return cy.dataCy('connect-get-new-sample-button');
}
+
public static refreshSchemaBtn() {
return cy.dataCy('connect-refresh-schema-button', { timeout: 10000 });
}
@@ -134,6 +135,7 @@ export class ConnectBtns {
public static connectRemoveDuplicateBox() {
return cy.dataCy('connect-remove-duplicates-box');
}
+
public static connectReduceEventRate() {
return cy.dataCy('connect-reduce-event-rate-box');
}
@@ -201,9 +203,11 @@ export class ConnectBtns {
}
public static configureSchemaScriptEditor() {
- return cy.dataCy('configure-schema-script-editor', {
- timeout: 10000,
- });
+ return cy
+ .dataCy('configure-schema-script-editor', {
+ timeout: 10000,
+ })
+ .find('.view-lines');
}
public static configureSchemaRunScriptBtn() {
diff --git a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
index acc1a5eaf4..8edb837812 100644
--- a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
+++ b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
@@ -45,7 +45,14 @@ export class StaticPropertyUtils {
}).click();
cy.dataCy('code-editor-' + config.selector, {
timeout: 2000,
- }).type(config.value);
+ })
+ .find('textarea.inputarea')
+ .click({ force: true })
+ .type('{selectall}{backspace}', { force: true })
+ .type(config.value, {
+ force: true,
+ parseSpecialCharSequences: false,
+ });
} else if (config.type === 'input') {
cy.dataCy(config.selector, { timeout: 2000 })
.clear()
diff --git a/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
b/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
index 7fec1438ff..bfc7c366ba 100644
--- a/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
+++ b/ui/cypress/support/utils/userInput/TreeStaticPropertyUtils.ts
@@ -51,7 +51,13 @@ export class TreeStaticPropertyUtils {
* Appends the @param text to the text editor
*/
public static typeInTextEditor(text: string) {
- cy.dataCy('static-tree-input-text-editor').type(text + '{enter}');
+ const editor = cy.dataCy('static-tree-input-text-editor');
+
+ editor
+ .find('textarea.inputarea')
+ .click({ force: true })
+ .type('{selectall}{backspace}', { force: true })
+ .type(text, { force: true, parseSpecialCharSequences: false });
}
/**
@@ -60,7 +66,7 @@ export class TreeStaticPropertyUtils {
public static getTextInTextEditor() {
return cy
.dataCy('static-tree-input-text-editor')
- .find('.CodeMirror-line')
+ .find('.view-lines')
.invoke('text');
}
diff --git
a/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.smoke.spec.ts
b/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.smoke.spec.ts
index d7ce37e1ba..ace1314fdc 100644
--- a/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.smoke.spec.ts
+++ b/ui/cypress/tests/connect/opcua/opcAdapterConfiguration.smoke.spec.ts
@@ -110,10 +110,13 @@ describe('Test OPC-UA Adapter Configuration', () => {
// Go back tree view and validate that the node is still selected
TreeStaticPropertyUtils.switchToTextEditor();
TreeStaticPropertyUtils.getTextInTextEditor().should(
- 'equal',
- '# Provide OPC UA Node IDs below, one per line.# Format: ' +
- 'ns=<namespace>;s=<node_id> (e.g., ns=3;s=SampleNodeId)' +
- 'ns=3;s=StepUpns=3;s=AlternatingBoolean',
+ 'contain',
+ 's=AlternatingBoolean',
+ );
+
+ TreeStaticPropertyUtils.getTextInTextEditor().should(
+ 'contain',
+ 'ns=3;s=StepUpns=3;',
);
TreeStaticPropertyUtils.switchToTreeEditor();
diff --git a/ui/cypress/tests/connect/scriptTemplate.spec.ts
b/ui/cypress/tests/connect/scriptTemplate.spec.ts
index 313917d2b4..9c00ec5004 100644
--- a/ui/cypress/tests/connect/scriptTemplate.spec.ts
+++ b/ui/cypress/tests/connect/scriptTemplate.spec.ts
@@ -20,8 +20,7 @@ import { AdapterBuilder } from
'../../support/builder/AdapterBuilder';
import { ConnectBtns } from '../../support/utils/connect/ConnectBtns';
const TEMPLATE_NAME = 'TestTemplate';
-const SCRIPT_LINE = "event.b = 'b';";
-const SCRIPT = ` ${SCRIPT_LINE}
+const SCRIPT = ` event.b = 'b';
out.collect(event);
}`;
@@ -55,17 +54,17 @@ describe('Validate Warning Pops For Configuration Changes
', () => {
const validateScriptTemplateIsStored = (templateName: string) => {
ConnectBtns.configureSchemaScriptEditor().should(
'contain.text',
- SCRIPT_LINE,
+ 'event.b',
);
ConnectBtns.resetScriptBtn().click();
ConnectBtns.configureSchemaScriptEditor().should(
'not.contain',
- SCRIPT_LINE,
+ 'event.b',
);
ConnectUtils.useScriptTemplate(templateName);
ConnectBtns.configureSchemaScriptEditor().should(
'contain.text',
- SCRIPT_LINE,
+ 'event.b',
);
};
diff --git a/ui/cypress/tests/pipelineElement/SinglePipelineElement.ts
b/ui/cypress/tests/pipelineElement/SinglePipelineElement.ts
index f27c485d30..4fda998dfa 100644
--- a/ui/cypress/tests/pipelineElement/SinglePipelineElement.ts
+++ b/ui/cypress/tests/pipelineElement/SinglePipelineElement.ts
@@ -22,7 +22,7 @@ import { ProcessorTest } from
'../../support/model/ProcessorTest';
const allTests = Cypress.env('processingElements');
allTests.forEach(test => {
- const testNames = ['numericalFilter1'];
+ const testNames = ['jsEvaluator1'];
const processorTest = test as ProcessorTest;
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 4e032d4bab..daa171ee30 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -20,7 +20,6 @@
"@angular/router": "^20.3.16",
"@auth0/angular-jwt": "^5.2.0",
"@bluehalo/ngx-leaflet": "20.0.0",
- "@ctrl/ngx-codemirror": "7.0.0",
"@fortawesome/fontawesome-free": "6.5.1",
"@gradle-tech/develocity-agent": "^2.0.2",
"@jsplumb/browser-ui": "^6.2.10",
@@ -31,7 +30,6 @@
"@ngx-translate/core": "^17.0.0",
"@ngx-translate/http-loader": "^17.0.0",
"@panzoom/panzoom": "^4.5.1",
- "codemirror": "^5.65.21",
"console-browserify": "^1.2.0",
"d3-array": "^3.2.4",
"d3-contour": "^4.0.2",
@@ -49,9 +47,11 @@
"maplibre-gl": "^5.14.0",
"marked": "^15.0.12",
"material-icons": "^1.13.14",
+ "monaco-editor": "^0.52.2",
"ngx-color-picker": "^20.1.1",
"ngx-echarts": "20.0.2",
"ngx-markdown": "^20.1.0",
+ "ngx-monaco-editor-v2": "20.3.0",
"ngx-quill": "^28.0.2",
"quill": "^2.0.3",
"roboto-fontface": "0.10.0",
@@ -2156,21 +2156,6 @@
"node": ">=0.1.90"
}
},
- "node_modules/@ctrl/ngx-codemirror": {
- "version": "7.0.0",
- "resolved":
"https://registry.npmjs.org/@ctrl/ngx-codemirror/-/ngx-codemirror-7.0.0.tgz",
- "integrity":
"sha512-qvIWtSTw/8fdXDnofBTX6LmTW9646HhawG2+Qyagf1vH40jCy0ZbHnkC20UYOVpUX+QCd1e/PQpkvWQ/1iGFzQ==",
- "license": "MIT",
- "dependencies": {
- "@types/codemirror": "^5.60.7",
- "tslib": "^2.3.0"
- },
- "peerDependencies": {
- "@angular/core": ">=16.0.0-0",
- "@angular/forms": ">=16.0.0-0",
- "codemirror": "^5.65.9"
- }
- },
"node_modules/@cypress/request": {
"version": "3.0.8",
"resolved":
"https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz",
@@ -5771,15 +5756,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/codemirror": {
- "version": "5.60.15",
- "resolved":
"https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz",
- "integrity":
"sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==",
- "license": "MIT",
- "dependencies": {
- "@types/tern": "*"
- }
- },
"node_modules/@types/cors": {
"version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
@@ -6095,6 +6071,7 @@
"version": "1.0.8",
"resolved":
"https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity":
"sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": {
@@ -6355,15 +6332,6 @@
"@types/geojson": "*"
}
},
- "node_modules/@types/tern": {
- "version": "0.23.9",
- "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
- "integrity":
"sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*"
- }
- },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved":
"https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -7923,12 +7891,6 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "node_modules/codemirror": {
- "version": "5.65.21",
- "resolved":
"https://registry.npmjs.org/codemirror/-/codemirror-5.65.21.tgz",
- "integrity":
"sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==",
- "license": "MIT"
- },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved":
"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -14246,6 +14208,12 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/monaco-editor": {
+ "version": "0.52.2",
+ "resolved":
"https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
+ "integrity":
"sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
+ "license": "MIT"
+ },
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -14545,6 +14513,20 @@
"zone.js": "~0.15.0"
}
},
+ "node_modules/ngx-monaco-editor-v2": {
+ "version": "20.3.0",
+ "resolved":
"https://registry.npmjs.org/ngx-monaco-editor-v2/-/ngx-monaco-editor-v2-20.3.0.tgz",
+ "integrity":
"sha512-j6zZIOYdSSUnbo58RWox2ZMEzIlA3lN/QchJREb1j734kUeJM1aW9RMz6FpkKt/UW1okdCTGZCnAavtKGou/2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^20.3.2",
+ "@angular/core": "^20.3.2",
+ "monaco-editor": "^0.52.2"
+ }
+ },
"node_modules/ngx-quill": {
"version": "28.0.2",
"resolved":
"https://registry.npmjs.org/ngx-quill/-/ngx-quill-28.0.2.tgz",
diff --git a/ui/package.json b/ui/package.json
index 6c6af3bf74..8c7183afa1 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -41,7 +41,6 @@
"@angular/router": "^20.3.16",
"@auth0/angular-jwt": "^5.2.0",
"@bluehalo/ngx-leaflet": "20.0.0",
- "@ctrl/ngx-codemirror": "7.0.0",
"@fortawesome/fontawesome-free": "6.5.1",
"@gradle-tech/develocity-agent": "^2.0.2",
"@jsplumb/browser-ui": "^6.2.10",
@@ -52,7 +51,7 @@
"@ngx-translate/core": "^17.0.0",
"@ngx-translate/http-loader": "^17.0.0",
"@panzoom/panzoom": "^4.5.1",
- "codemirror": "^5.65.21",
+ "monaco-editor": "^0.52.2",
"console-browserify": "^1.2.0",
"d3-array": "^3.2.4",
"d3-contour": "^4.0.2",
@@ -73,6 +72,7 @@
"ngx-color-picker": "^20.1.1",
"ngx-echarts": "20.0.2",
"ngx-markdown": "^20.1.0",
+ "ngx-monaco-editor-v2": "20.3.0",
"ngx-quill": "^28.0.2",
"quill": "^2.0.3",
"roboto-fontface": "0.10.0",
diff --git a/ui/src/app/app.config.ts b/ui/src/app/app.config.ts
index 30a5b916ba..6f3a05bbaa 100644
--- a/ui/src/app/app.config.ts
+++ b/ui/src/app/app.config.ts
@@ -48,6 +48,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { provideTranslateHttpLoader } from '@ngx-translate/http-loader';
import { NgxEchartsModule } from 'ngx-echarts';
import { MatNativeDateModule } from '@angular/material/core';
+import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
export const appConfig: ApplicationConfig = {
providers: [
@@ -65,6 +66,9 @@ export const appConfig: ApplicationConfig = {
NgxEchartsModule.forRoot({
echarts: () => import('echarts'),
}),
+ MonacoEditorModule.forRoot({
+ baseUrl: window.location.origin + '/assets/monaco/min/vs',
+ }),
),
provideHttpClient(withInterceptorsFromDi()),
provideRouter(routes, withHashLocation()),
diff --git
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.html
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.html
index 6add5c0b43..6ea2ed64ea 100644
---
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.html
+++
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.html
@@ -27,9 +27,8 @@
mat-button
mat-flat-button
color="accent"
- class="small-button"
+ class="small-button action-button"
(click)="restoreTemplate()"
- style="margin-right: 10px; margin-bottom: 10px"
>
{{ 'Reset changes' | translate }}
</button>
@@ -61,20 +60,22 @@
</sp-alert-banner>
</div>
<div fxFlex="100" fxLayout="row">
- <ngx-codemirror
- [(ngModel)]="template.template"
- style="width: 100%"
- [options]="editorOptions"
- >
- </ngx-codemirror>
+ <div class="code-editor-resizable">
+ <ngx-monaco-editor
+ class="code-editor"
+ [(ngModel)]="template.template"
+ [options]="editorOptions"
+ >
+ </ngx-monaco-editor>
+ </div>
</div>
<div fxLayout="row" class="mt-10">
<button
mat-button
mat-flat-button
color="accent"
+ class="save-button"
(click)="saveTemplate()"
- style="margin-right: 10px"
[disabled]="template.template === ''"
>
<i class="material-icons">save</i
diff --git
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.scss
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.scss
index d04ad7d591..3c59e85f99 100644
---
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.scss
+++
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.scss
@@ -38,3 +38,25 @@
.placeholder-description {
}
+
+.code-editor-resizable {
+ width: 100%;
+ height: 320px;
+ resize: vertical;
+ overflow: hidden;
+}
+
+.code-editor {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.action-button {
+ margin-right: 10px;
+ margin-bottom: 10px;
+}
+
+.save-button {
+ margin-right: 10px;
+}
diff --git
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.ts
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.ts
index 09e57a001e..7280a64b67 100644
---
a/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.ts
+++
b/ui/src/app/configuration/email-configuration/email-template-configuration/email-template-configuration.component.ts
@@ -24,7 +24,6 @@ import {
} from '@streampipes/platform-services';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
-import 'codemirror/mode/htmlembedded/htmlembedded';
import {
FlexDirective,
LayoutAlignDirective,
@@ -36,7 +35,8 @@ import {
SplitSectionComponent,
} from '@streampipes/shared-ui';
import { MatButton } from '@angular/material/button';
-import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
+import type { editor as MonacoEditor } from 'monaco-editor';
@Component({
selector: 'sp-email-template-configuration',
@@ -50,7 +50,7 @@ import { CodemirrorModule } from '@ctrl/ngx-codemirror';
MatButton,
SpAlertBannerComponent,
LayoutGapDirective,
- CodemirrorModule,
+ MonacoEditorModule,
FormsModule,
TranslatePipe,
],
@@ -62,15 +62,14 @@ export class SpEmailTemplateConfigurationComponent
implements OnInit {
templateStored = false;
private translateService = inject(TranslateService);
- editorOptions = {
- mode: 'htmlembedded',
- autoRefresh: true,
- theme: 'dracula',
- autoCloseBrackets: true,
- lineNumbers: true,
- lineWrapping: true,
- gutters: ['CodeMirror-lint-markers'],
- lint: true,
+ editorOptions: MonacoEditor.IStandaloneEditorConstructionOptions = {
+ language: 'html',
+ theme: 'vs-dark',
+ lineNumbers: 'on',
+ wordWrap: 'on',
+ automaticLayout: true,
+ scrollBeyondLastLine: false,
+ minimap: { enabled: false },
};
allowedPlaceholders: { placeholder: string; description: string }[] = [
diff --git
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
index e317fb3ec3..3b3be24a73 100644
---
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.html
@@ -23,6 +23,7 @@
[availableScripts]="availableScripts()"
[loadingAvailableScriptsError]="loadingAvailableScriptsError()"
[script]="script()"
+ [eventPropertyNames]="eventPropertyNames()"
[editorOptions]="editorOptions"
(codeChange)="onCodeChange($event)"
(languageChange)="onLanguageChange($event)"
diff --git
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
index 0992466f03..63da642e69 100644
---
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.scss
@@ -27,22 +27,3 @@
.stepper-button {
margin-left: 10px;
}
-
-.code-editor-outer {
- margin: var(--space-2xs);
-}
-
-.code-editor {
- width: 100%;
- height: 220px;
-}
-
-.preview-text {
- margin: var(--space-2xs);
- background-color: var(--color-bg-0);
- font:
- var(--font-size-xs) Inconsolata,
- monospace;
- color: var(--color-default-text);
- padding: 10px;
-}
diff --git
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
index c9e20280af..2debf80606 100644
---
a/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/configure-schema.component.ts
@@ -56,6 +56,7 @@ import { AdapterScriptEditorComponent } from
'./script-editor/adapter-script-edi
import { AdapterSamplePreviewComponent } from
'./sample-preview/adapter-sample-preview.component';
import { AdapterResultPreviewComponent } from
'./result-preview/adapter-result-preview.component';
import { MatButton } from '@angular/material/button';
+import type { editor as MonacoEditor } from 'monaco-editor';
@Component({
selector: 'sp-configure-schema',
@@ -128,6 +129,10 @@ export class ConfigureSchemaComponent implements OnInit {
?.inputs?.[0] || {},
);
+ eventPropertyNames = computed(() =>
+ Object.keys(this.input() ?? {}).filter(runtimeName => !!runtimeName),
+ );
+
fieldStatusInfos = computed(
() => this.stateService.state().sampleFieldStatusInfos || {},
);
@@ -165,18 +170,16 @@ export class ConfigureSchemaComponent implements OnInit {
});
}
- editorOptions = {
- mode: 'javascript',
- autoRefresh: true,
- theme: 'dracula',
- autoCloseBrackets: true,
- lineNumbers: true,
- lineWrapping: true,
- gutters: ['CodeMirror-lint-markers'],
- lint: true,
- extraKeys: {
- 'Ctrl-Space': 'autocomplete',
- },
+ editorOptions: MonacoEditor.IStandaloneEditorConstructionOptions = {
+ language: 'javascript',
+ theme: 'vs-dark',
+ lineNumbers: 'on',
+ wordWrap: 'on',
+ automaticLayout: true,
+ scrollBeyondLastLine: false,
+ minimap: { enabled: false },
+ quickSuggestions: true,
+ suggestOnTriggerCharacters: true,
};
ngOnInit(): void {
diff --git
a/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.html
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.html
index ff06d7c2fa..3e0fee221a 100644
---
a/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.html
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.html
@@ -90,13 +90,14 @@
@if (scriptActive()) {
<div class="code-editor-outer">
- <ngx-codemirror
+ <ngx-monaco-editor
class="code-editor"
[ngModel]="script()"
(ngModelChange)="codeChange.emit($event)"
[options]="editorOptions()"
+ (onInit)="onEditorInit()"
data-cy="configure-schema-script-editor"
- ></ngx-codemirror>
+ ></ngx-monaco-editor>
</div>
<div fxLayoutGap="10px">
<button
diff --git
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.scss
similarity index 82%
copy from
ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
copy to
ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.scss
index 4aa684934b..55720c4748 100644
---
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.scss
@@ -16,6 +16,15 @@
*
*/
-::ng-deep.CodeMirror-lint-tooltip {
- z-index: 1001;
+.code-editor-outer {
+ width: 100%;
+ height: 220px;
+ resize: vertical;
+ overflow: hidden;
+}
+
+.code-editor {
+ width: 100%;
+ height: 100%;
+ display: block;
}
diff --git
a/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.ts
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.ts
index abb1d8c681..6f93edb9bd 100644
---
a/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.ts
+++
b/ui/src/app/connect/components/adapter-configuration/configure-schema/script-editor/adapter-script-editor.component.ts
@@ -16,7 +16,7 @@
*
*/
-import { Component, input, output } from '@angular/core';
+import { Component, input, OnDestroy, output } from '@angular/core';
import { ScriptMetadata } from '@streampipes/platform-services';
import {
SpAlertBannerComponent,
@@ -32,14 +32,18 @@ import { MatMenu, MatMenuItem, MatMenuTrigger } from
'@angular/material/menu';
import { MatIcon } from '@angular/material/icon';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { FormsModule } from '@angular/forms';
-import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { MatTooltip } from '@angular/material/tooltip';
import { TitleCasePipe } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
+import type * as monacoType from 'monaco-editor';
+
+declare const monaco: typeof monacoType;
@Component({
selector: 'sp-adapter-script-editor',
templateUrl: './adapter-script-editor.component.html',
+ styleUrl: './adapter-script-editor.component.scss',
imports: [
SpBasicInnerPanelComponent,
SpAlertBannerComponent,
@@ -53,19 +57,21 @@ import { TranslatePipe } from '@ngx-translate/core';
MatMenuItem,
MatSlideToggle,
FormsModule,
- CodemirrorModule,
+ MonacoEditorModule,
MatTooltip,
TitleCasePipe,
TranslatePipe,
],
})
-export class AdapterScriptEditorComponent {
+export class AdapterScriptEditorComponent implements OnDestroy {
scriptActive = input(false);
selectedScriptMetadata = input<ScriptMetadata>();
availableScripts = input<ScriptMetadata[]>([]);
loadingAvailableScriptsError = input<any>();
script = input('');
+ eventPropertyNames = input<string[]>([]);
editorOptions = input<any>();
+ private completionProvider?: monacoType.IDisposable;
codeChange = output<string>();
languageChange = output<ScriptMetadata>();
@@ -74,4 +80,50 @@ export class AdapterScriptEditorComponent {
toggleScriptActive = output<void>();
runScript = output<void>();
createTemplate = output<void>();
+
+ onEditorInit() {
+ this.registerEventPropertyCompletionProvider();
+ }
+
+ ngOnDestroy() {
+ this.completionProvider?.dispose();
+ }
+
+ private registerEventPropertyCompletionProvider() {
+ this.completionProvider?.dispose();
+ this.completionProvider =
+ monaco.languages.registerCompletionItemProvider('javascript', {
+ triggerCharacters: ['.'],
+ provideCompletionItems: (model, position) => {
+ const linePrefix = model.getValueInRange({
+ startLineNumber: position.lineNumber,
+ startColumn: 1,
+ endLineNumber: position.lineNumber,
+ endColumn: position.column,
+ });
+ const match =
linePrefix.match(/(?:^|[^\w$])event\.(\w*)$/);
+ if (!match) {
+ return { suggestions: [] };
+ }
+
+ const word = model.getWordUntilPosition(position);
+ const range = {
+ startLineNumber: position.lineNumber,
+ endLineNumber: position.lineNumber,
+ startColumn: word.startColumn,
+ endColumn: word.endColumn,
+ };
+
+ const suggestions: monacoType.languages.CompletionItem[] =
+ this.eventPropertyNames().map(runtimeName => ({
+ label: runtimeName,
+ kind: monaco.languages.CompletionItemKind.Property,
+ insertText: runtimeName,
+ range,
+ }));
+
+ return { suggestions };
+ },
+ });
+ }
}
diff --git
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.html
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.html
index 53028c581f..110017b847 100644
---
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.html
+++
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.html
@@ -22,9 +22,8 @@
mat-button
mat-flat-button
color="accent"
- class="small-button"
+ class="small-button action-button"
(click)="resetCode()"
- style="margin-right: 10px; margin-bottom: 10px"
>
{{ 'Reset code template' | translate }}
</button>
@@ -32,21 +31,23 @@
mat-button
mat-flat-button
color="accent"
- class="small-button"
+ class="small-button action-button"
(click)="cleanCode()"
[attr.data-cy]="'reset-code-' + fieldName"
- style="margin-right: 10px; margin-bottom: 10px"
>
{{ 'Clean code' | translate }}
</button>
</div>
<div fxFlex="100" fxLayout="row">
- <ngx-codemirror
- style="width: 100%"
- [(ngModel)]="staticProperty.value"
- [options]="editorOptions"
- [attr.data-cy]="'code-editor-' + fieldName"
- >
- </ngx-codemirror>
+ <div class="code-editor-resizable">
+ <ngx-monaco-editor
+ class="code-editor"
+ [(ngModel)]="staticProperty.value"
+ [options]="editorOptions"
+ (onInit)="onEditorInit()"
+ [attr.data-cy]="'code-editor-' + fieldName"
+ >
+ </ngx-monaco-editor>
+ </div>
</div>
</div>
diff --git
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
index 4aa684934b..5e90db092a 100644
---
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
+++
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
@@ -15,7 +15,22 @@
* limitations under the License.
*
*/
+.code-editor-resizable {
+ width: 100%;
+ min-height: 280px;
+ height: 420px;
+ max-height: 75vh;
+ resize: vertical;
+ overflow: hidden;
+}
+
+.code-editor {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
-::ng-deep.CodeMirror-lint-tooltip {
- z-index: 1001;
+.action-button {
+ margin-right: 10px;
+ margin-bottom: 10px;
}
diff --git
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.ts
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.ts
index 552bbedb08..9060a5c2e3 100644
---
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.ts
@@ -18,26 +18,21 @@
import { CodeInputStaticProperty } from '@streampipes/platform-services';
import { AbstractValidatedStaticPropertyRenderer } from
'../base/abstract-validated-static-property';
-import { AfterViewInit, Component, OnInit } from '@angular/core';
-
-import 'codemirror/mode/javascript/javascript';
-import 'codemirror/mode/python/python';
-import 'codemirror/addon/edit/closebrackets';
-import 'codemirror/addon/hint/show-hint';
-import 'codemirror/addon/hint/javascript-hint';
-import 'codemirror/addon/lint/javascript-lint';
-import 'codemirror/addon/lint/lint';
-import CodeMirror from 'codemirror';
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import type * as monacoType from 'monaco-editor';
+import type { editor as MonacoEditor } from 'monaco-editor';
import {
FlexDirective,
LayoutAlignDirective,
LayoutDirective,
} from '@ngbracket/ngx-layout/flex';
import { MatButton } from '@angular/material/button';
-import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { FormsModule } from '@angular/forms';
import { TranslatePipe } from '@ngx-translate/core';
+declare const monaco: typeof monacoType;
+
@Component({
selector: 'sp-static-code-input',
templateUrl: './static-code-input.component.html',
@@ -47,28 +42,27 @@ import { TranslatePipe } from '@ngx-translate/core';
LayoutDirective,
LayoutAlignDirective,
MatButton,
- CodemirrorModule,
+ MonacoEditorModule,
FormsModule,
TranslatePipe,
],
})
export class StaticCodeInputComponent
extends AbstractValidatedStaticPropertyRenderer<CodeInputStaticProperty>
- implements OnInit, AfterViewInit
+ implements OnInit, OnDestroy
{
- editorOptions = {
- mode: 'javascript',
- autoRefresh: true,
- theme: 'dracula',
- autoCloseBrackets: true,
- lineNumbers: true,
- lineWrapping: true,
- gutters: ['CodeMirror-lint-markers'],
- lint: true,
- extraKeys: {
- 'Ctrl-Space': 'autocomplete',
- },
+ editorOptions: MonacoEditor.IStandaloneEditorConstructionOptions = {
+ language: 'javascript',
+ theme: 'vs-dark',
+ lineNumbers: 'on',
+ wordWrap: 'on',
+ automaticLayout: true,
+ scrollBeyondLastLine: false,
+ minimap: { enabled: false },
+ quickSuggestions: true,
+ suggestOnTriggerCharacters: true,
};
+ private completionProvider?: monacoType.IDisposable;
constructor() {
super();
@@ -83,14 +77,18 @@ export class StaticCodeInputComponent
applyLanguage() {
if (this.staticProperty.language === 'None') {
- this.editorOptions.mode = '';
+ this.editorOptions.language = 'plaintext';
} else {
- this.editorOptions.mode =
+ this.editorOptions.language =
this.staticProperty.language.toLowerCase();
}
}
- ngAfterViewInit() {
+ ngOnDestroy() {
+ this.completionProvider?.dispose();
+ }
+
+ onEditorInit() {
this.enableCodeHints();
}
@@ -107,31 +105,45 @@ export class StaticCodeInputComponent
}
enableCodeHints() {
- if (this.editorOptions.mode === 'javascript') {
- const jsHint = (CodeMirror as any).hint.javascript;
- (CodeMirror as any).hint.javascript = cm => {
- const cursor = cm.getCursor();
- const token = cm.getTokenAt(cursor);
- let inner = {
- from: cm.getCursor(),
- to: cm.getCursor(),
- list: [],
- };
- const previousCursor = {
- line: cursor.line,
- ch: cursor.ch - 1,
- sticky: null,
- };
- const previousToken = cm.getTokenAt(previousCursor);
- if (token.string === '.' && previousToken.string === 'event') {
- this.eventSchemas[0].eventProperties.forEach(ep => {
- inner.list.unshift(ep.runtimeName);
- });
- } else {
- inner = jsHint(cm);
- }
- return inner;
- };
+ if (this.editorOptions.language !== 'javascript') {
+ return;
}
+ this.completionProvider?.dispose();
+ this.completionProvider =
+ monaco.languages.registerCompletionItemProvider('javascript', {
+ triggerCharacters: ['.'],
+ provideCompletionItems: (model, position) => {
+ const linePrefix = model.getValueInRange({
+ startLineNumber: position.lineNumber,
+ startColumn: 1,
+ endLineNumber: position.lineNumber,
+ endColumn: position.column,
+ });
+ const match =
linePrefix.match(/(?:^|[^\w$])event\.(\w*)$/);
+ if (!match) {
+ return { suggestions: [] };
+ }
+
+ const word = model.getWordUntilPosition(position);
+ const range = {
+ startLineNumber: position.lineNumber,
+ endLineNumber: position.lineNumber,
+ startColumn: word.startColumn,
+ endColumn: word.endColumn,
+ };
+
+ const eventProperties =
+ this.eventSchemas?.[0]?.eventProperties ?? [];
+ const suggestions: monacoType.languages.CompletionItem[] =
+ eventProperties.map(ep => ({
+ label: ep.runtimeName,
+ kind: monaco.languages.CompletionItemKind.Property,
+ insertText: ep.runtimeName,
+ range,
+ }));
+
+ return { suggestions };
+ },
+ });
}
}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.html
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.html
index 8e3d97b00f..4b40f15fe9 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.html
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.html
@@ -16,11 +16,11 @@
~
-->
-<ngx-codemirror
- style="width: 100%"
+<ngx-monaco-editor
+ class="code-editor"
[(ngModel)]="textEditor"
(ngModelChange)="onTextEditorChange($event)"
[options]="editorOptions"
data-cy="static-tree-input-text-editor"
>
-</ngx-codemirror>
+</ngx-monaco-editor>
diff --git
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.scss
similarity index 91%
copy from
ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
copy to
ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.scss
index 4aa684934b..45aded714e 100644
---
a/ui/src/app/core-ui/static-properties/static-code-input/static-code-input.component.scss
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.scss
@@ -16,6 +16,8 @@
*
*/
-::ng-deep.CodeMirror-lint-tooltip {
- z-index: 1001;
+.code-editor {
+ width: 100%;
+ height: 280px;
+ display: block;
}
diff --git
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.ts
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.ts
index 2ad5f8a101..0a7d413ae3 100644
---
a/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component.ts
@@ -28,13 +28,15 @@ import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { RuntimeResolvableTreeInputStaticProperty } from
'@streampipes/platform-services';
import { TranslateService } from '@ngx-translate/core';
-import { CodemirrorModule } from '@ctrl/ngx-codemirror';
+import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
+import type { editor as MonacoEditor } from 'monaco-editor';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'sp-static-tree-input-text-editor',
templateUrl: './static-tree-input-text-editor.component.html',
- imports: [CodemirrorModule, FormsModule],
+ styleUrl: './static-tree-input-text-editor.component.scss',
+ imports: [MonacoEditorModule, FormsModule],
})
export class StaticTreeInputTextEditorComponent implements OnInit {
@Input()
@@ -45,16 +47,15 @@ export class StaticTreeInputTextEditorComponent implements
OnInit {
translateService = inject(TranslateService);
- editorOptions = {
- mode: 'text/plain',
- autoRefresh: true,
- theme: 'dracula',
- lineNumbers: true,
- lineWrapping: true,
+ editorOptions: MonacoEditor.IStandaloneEditorConstructionOptions = {
+ language: 'plaintext',
+ theme: 'vs-dark',
+ lineNumbers: 'on',
+ wordWrap: 'on',
readOnly: false,
- extraKeys: {
- 'Ctrl-Space': 'autocomplete',
- },
+ automaticLayout: true,
+ scrollBeyondLastLine: false,
+ minimap: { enabled: false },
};
headerText = this.translateService.instant(
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 07c6acebff..7bd22b90b1 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -52,12 +52,6 @@
@use './sp/colors.scss';
-@use 'codemirror/lib/codemirror.css';
-@use 'codemirror/theme/darcula.css';
-@use 'codemirror/theme/dracula.css';
-@use 'codemirror/addon/hint/show-hint.css';
-@use 'codemirror/addon/lint/lint.css';
-
// Mat 3 overrides
@use './sp/buttons-mat3';
diff --git a/ui/src/scss/sp/main.scss b/ui/src/scss/sp/main.scss
index 35c8bd5dbe..a30240ae90 100644
--- a/ui/src/scss/sp/main.scss
+++ b/ui/src/scss/sp/main.scss
@@ -69,10 +69,6 @@ pre[class*='language-'] {
height: auto;
}
-.CodeMirror-hints {
- z-index: 1000;
-}
-
.editor-error-notifications {
position: absolute;
left: -226px;