From d41629abd094fc1300c60cfd2724b98a15822449 Mon Sep 17 00:00:00 2001 From: Bastien OLLIER Date: Wed, 24 Jan 2024 14:33:35 +0100 Subject: [PATCH] Add color in result (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: bastien ollier Reviewed-on: https://codefirst.iut.uca.fr/git/sandkasten/sandkasten-web/pulls/6 Reviewed-by: Clément FRÉVILLE Co-authored-by: Bastien OLLIER Co-committed-by: Bastien OLLIER --- package-lock.json | 23 +++++++++++++++++++ package.json | 1 + src/app/app.module.ts | 5 ++-- .../components/editor/editor.component.html | 15 +----------- src/app/components/editor/editor.component.ts | 13 ++++++++--- src/app/safe-html.pipe.spec.ts | 8 +++++++ src/app/safe-html.pipe.ts | 15 ++++++++++++ src/app/services/codeExecution.service.ts | 22 ++++++++++++------ tsconfig.json | 1 + 9 files changed, 77 insertions(+), 26 deletions(-) create mode 100644 src/app/safe-html.pipe.spec.ts create mode 100644 src/app/safe-html.pipe.ts diff --git a/package-lock.json b/package-lock.json index b7b5a1a..49a7f13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@emailjs/browser": "^3.12.1", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", + "ansi-to-html": "^0.7.2", "codemirror": "^5.65.16", "rxjs": "~7.8.1", "sse.js": "^2.2.0", @@ -4070,6 +4071,28 @@ "node": ">=4" } }, + "node_modules/ansi-to-html": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", + "dependencies": { + "entities": "^2.2.0" + }, + "bin": { + "ansi-to-html": "bin/ansi-to-html" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ansi-to-html/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", diff --git a/package.json b/package.json index 961b408..e51187b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@emailjs/browser": "^3.12.1", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", + "ansi-to-html": "^0.7.2", "codemirror": "^5.65.16", "rxjs": "~7.8.1", "sse.js": "^2.2.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c39c84a..2ed0287 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,6 +24,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TranslationService } from './services/translation.service'; +import { SafeHTMLPipe } from './safe-html.pipe'; @NgModule({ declarations: [ @@ -37,8 +38,8 @@ import { TranslationService } from './services/translation.service'; FormComponent, PrivacyPolicyComponent, TermsOfServiceComponent, - OurStoryComponent - + OurStoryComponent, + SafeHTMLPipe ], imports: [ BrowserModule, diff --git a/src/app/components/editor/editor.component.html b/src/app/components/editor/editor.component.html index d02a36c..3945bba 100644 --- a/src/app/components/editor/editor.component.html +++ b/src/app/components/editor/editor.component.html @@ -14,20 +14,7 @@ -
- - -
+

 
   
diff --git a/src/app/components/editor/editor.component.ts b/src/app/components/editor/editor.component.ts index 07f941c..593b443 100644 --- a/src/app/components/editor/editor.component.ts +++ b/src/app/components/editor/editor.component.ts @@ -3,6 +3,8 @@ import { Router } from "@angular/router"; import { CodeExecutionService } from "src/app/services/codeExecution.service"; import "codemirror/mode/shell/shell.js"; import "codemirror/mode/clike/clike.js"; +import Convert from 'ansi-to-html'; + // Exemple de code pour chaque langage autorisé const codeDefaults = { @@ -68,6 +70,13 @@ export class EditorComponent implements OnInit { ngOnInit(): void { // Appel à changeMode pour mettre à jour le contenu de l'éditeur et le mode this.changeMode(); + const convert = new Convert(); + + this.codeExecutionService.getResult().subscribe((result) => { + if (result.type !== 'exit') { + this.resultContent += convert.toHtml(result.text); + } + }); } // Change le langage de l'éditeur @@ -160,8 +169,6 @@ export class EditorComponent implements OnInit { this.codeExecutionService.executeCode(codeToExecute, this.mode); - this.codeExecutionService.getResult().subscribe((result) => { - this.resultContent = result; - }); + this.resultContent = ''; } } diff --git a/src/app/safe-html.pipe.spec.ts b/src/app/safe-html.pipe.spec.ts new file mode 100644 index 0000000..400afdd --- /dev/null +++ b/src/app/safe-html.pipe.spec.ts @@ -0,0 +1,8 @@ +import { SafeHTMLPipe } from './safe-html.pipe'; + +describe('SafeHTMLPipe', () => { + it('create an instance', () => { + const pipe = new SafeHTMLPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/safe-html.pipe.ts b/src/app/safe-html.pipe.ts new file mode 100644 index 0000000..963ebbd --- /dev/null +++ b/src/app/safe-html.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Pipe({ + name: 'safeHTML', +}) +export class SafeHTMLPipe implements PipeTransform { + + constructor(protected sanitizer: DomSanitizer) {} + + transform(value: unknown, ...args: unknown[]): unknown { + return this.sanitizer.bypassSecurityTrustHtml(value as string); + } + +} diff --git a/src/app/services/codeExecution.service.ts b/src/app/services/codeExecution.service.ts index f7f1b97..4ec25ad 100644 --- a/src/app/services/codeExecution.service.ts +++ b/src/app/services/codeExecution.service.ts @@ -1,16 +1,20 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; import { SSE } from 'sse.js'; import { Observable, Subject } from 'rxjs'; +export type ExecutionMessage = { + type: 'stdout' | 'stderr' | 'exit', + text: string +}; + @Injectable({ providedIn: 'root', }) export class CodeExecutionService { private apiUrl = 'http://localhost:3000/run'; - private resultSubject = new Subject(); + private resultSubject = new Subject(); - constructor(private http: HttpClient) {} + constructor() {} executeCode(code: string, language: string) { const sse = new SSE(this.apiUrl, { @@ -21,17 +25,21 @@ export class CodeExecutionService { }, payload: JSON.stringify({ code, language }), }); - + sse.addEventListener('message', (event: MessageEvent) => { const result = event.data; - // Émettre le résultat à tous les abonnés + // @ts-ignore + const type = event.id; const text = decodeURIComponent(result.replace(/%00/g, '')); - this.resultSubject.next(text); + if (type === 'end') { + sse.close(); + } + this.resultSubject.next({ type, text }); }); } - getResult(): Observable { + getResult(): Observable { return this.resultSubject.asObservable(); } } diff --git a/tsconfig.json b/tsconfig.json index ed966d4..64a863e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "target": "ES2022", "module": "ES2022", "useDefineForClassFields": false, + "allowSyntheticDefaultImports": true, "lib": [ "ES2022", "dom"