diff --git a/editor/web/index.html b/editor/web/index.html index a9e5a8e..93415a1 100644 --- a/editor/web/index.html +++ b/editor/web/index.html @@ -170,8 +170,10 @@ width: 100%; height: 100%; min-height: 700px !important; + max-height: 1200px !important; font-family: monospace; font-size: 0.8em !important; + overflow: auto; } .save-as-dialog__buttons { @@ -545,8 +547,7 @@ Save - +

           
         
       
diff --git a/editor/web/package.json b/editor/web/package.json
index ccf0d43..93baba5 100644
--- a/editor/web/package.json
+++ b/editor/web/package.json
@@ -14,6 +14,7 @@
     "@types/ejs": "^3.1.5",
     "@types/html-minifier-terser": "^7.0.2",
     "@types/node": "^24.10.2",
+    "@types/prismjs": "^1.26.5",
     "html-minifier-terser": "^7.2.0",
     "prettier": "3.7.4",
     "typescript": "^5.9.3",
@@ -23,6 +24,7 @@
   "dependencies": {
     "bootstrap": "^5.3.8",
     "cloudflare-error-page": "../../javascript",
-    "ejs": "^3.1.10"
+    "ejs": "^3.1.10",
+    "prismjs": "^1.30.0"
   }
 }
diff --git a/editor/web/src/codegen/index.ts b/editor/web/src/codegen/index.ts
index 0b0447f..b871936 100644
--- a/editor/web/src/codegen/index.ts
+++ b/editor/web/src/codegen/index.ts
@@ -6,14 +6,17 @@ import pythonTemplate from './python.ejs?raw';
 
 interface CodeGen {
   name: string;
+  language: string;
   generate(params: any): string;
 }
 
 class EjsCodeGen implements CodeGen {
   name: string;
+  language: string;
   private template: ejs.TemplateFunction;
-  constructor(name: string, templateContent: any) {
+  constructor(name: string, language: string, templateContent: any) {
     this.name = name;
+    this.language = language;
     this.template = ejs.compile(templateContent);
   }
   generate(params: any): string {
@@ -21,6 +24,6 @@ class EjsCodeGen implements CodeGen {
   }
 }
 
-export const jsCodeGen = new EjsCodeGen('NodeJS Example', jsTemplate);
-export const jsonCodeGen = new EjsCodeGen('JSON', jsonTemplate);
-export const pythonCodeGen = new EjsCodeGen('Python Example', pythonTemplate);
+export const jsCodeGen = new EjsCodeGen('NodeJS Example', 'javascript', jsTemplate);
+export const jsonCodeGen = new EjsCodeGen('JSON', 'json', jsonTemplate);
+export const pythonCodeGen = new EjsCodeGen('Python Example', 'python', pythonTemplate);
diff --git a/editor/web/src/index.js b/editor/web/src/index.js
index 5948194..450e33c 100644
--- a/editor/web/src/index.js
+++ b/editor/web/src/index.js
@@ -5,11 +5,16 @@
   - inputs call render() on change
   - "Open in new tab" opens the rendered HTML in a new window using a blob URL
 */
-import { render as render_cf_error_page } from 'cloudflare-error-page';
 
 import 'bootstrap/js/src/modal.js';
 import Popover from 'bootstrap/js/src/popover.js';
+import Prism from 'prismjs';
+import 'prismjs/components/prism-json.js';
+import 'prismjs/components/prism-python.js';
+import { render as render_cf_error_page } from 'cloudflare-error-page';
+
 import 'bootstrap/dist/css/bootstrap.min.css';
+import 'prismjs/themes/prism.css';
 
 import { jsCodeGen, jsonCodeGen, pythonCodeGen } from './codegen';
 
@@ -426,16 +431,20 @@ function updateSaveAsDialog(e) {
   }
   const params = { ...lastCfg };
   delete params.time;
+  let language;
   if (codegen) {
     saveAsContent = codegen.generate(params);
+    language = codegen.language;
   } else if (saveAsType == 'static') {
     render(); // rerender the page
     saveAsContent = lastRenderedHtml;
+    language = 'html';
   } else {
     throw new Error('unexpected saveAsType=' + saveAsType);
   }
-  $('saveAsDialogCode').value = saveAsContent;
-  $('saveAsDialogCode').scrollTop = 0;
+  const html = Prism.highlight(saveAsContent, Prism.languages[language], language);
+
+  $('saveAsDialogCode').innerHTML = html;
 
   document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
     const isCurrent = element.dataset.type == saveAsType;
@@ -456,10 +465,7 @@ document.querySelectorAll('#saveAsDialogTypes button').forEach((element) => {
 
 const saveAsDialogCopyPopover = new Popover($('saveAsDialogCopyBtn'));
 $('saveAsDialogCopyBtn').addEventListener('click', (e) => {
-  const field = $('saveAsDialogCode');
-  field.select();
-  // field.setSelectionRange(0, field.value.length);
-  navigator.clipboard.writeText(field.value).then(() => {
+  navigator.clipboard.writeText(saveAsContent).then(() => {
     saveAsDialogCopyPopover.show();
     setTimeout(() => {
       saveAsDialogCopyPopover.hide();
diff --git a/editor/web/yarn.lock b/editor/web/yarn.lock
index bf45c62..61afdd9 100644
--- a/editor/web/yarn.lock
+++ b/editor/web/yarn.lock
@@ -310,6 +310,11 @@
   dependencies:
     undici-types "~7.16.0"
 
+"@types/prismjs@^1.26.5":
+  version "1.26.5"
+  resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.5.tgz#72499abbb4c4ec9982446509d2f14fb8483869d6"
+  integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==
+
 acorn@^8.15.0:
   version "8.15.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
@@ -618,6 +623,11 @@ prettier@3.7.4:
   resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f"
   integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==
 
+prismjs@^1.30.0:
+  version "1.30.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9"
+  integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==
+
 readdirp@~3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"