From d19986dbb5cbcbb1f978ed0d3b8cacbf06fd3873 Mon Sep 17 00:00:00 2001 From: Junhao-Yao Date: Wed, 5 Nov 2025 00:00:04 +1100 Subject: [PATCH] feat: added syntax highlighting in playground text editor --- playground/pages/index.yaml | 15 +++++-- playground/static/public/main.js | 30 ++++++++++++++ playground/static/public/theme.css | 66 ++++++++++++++++++++++++++++++ playground/templates/base.yaml | 6 +++ 4 files changed, 114 insertions(+), 3 deletions(-) diff --git a/playground/pages/index.yaml b/playground/pages/index.yaml index 24007f5..62a5b36 100644 --- a/playground/pages/index.yaml +++ b/playground/pages/index.yaml @@ -16,12 +16,21 @@ title: Jempl Playground - rtgl-view flex= wh=f: - rtgl-view wh="f": - rtgl-text s=lg: "Template (YAML):" - - textarea#input-template placeholder="Enter YAML template here..." rows="10" cols="50": + - div class="highlighted-textarea": + - textarea#input-template spellcheck="false" placeholder="Enter YAML template here..." rows="10" cols="50": + - pre#highlighted-input-template aria-hidden="true": + - code#highlighted-input-template-content class="language-yaml": - rtgl-view wh="f": - rtgl-text s=lg: "Data (YAML):" - - textarea#input-data placeholder="Enter YAML data here..." rows="10" cols="50": + - div class="highlighted-textarea": + - textarea#input-data spellcheck="false" placeholder="Enter YAML data here..." rows="10" cols="50": + - pre#highlighted-input-data aria-hidden="true": + - code#highlighted-input-data-content class="language-yaml": - rtgl-view flex=1 w=f h=f sm-h=inherit: - rtgl-text s="lg": "Output: " - - textarea#output readonly: + - div class="highlighted-textarea": + - textarea#output readonly: + - pre#highlighted-output aria-hidden="true": + - code#highlighted-output-content class="language-yaml": - rtgl-view sm-show hide h=120: diff --git a/playground/static/public/main.js b/playground/static/public/main.js index 21a04a6..04486af 100644 --- a/playground/static/public/main.js +++ b/playground/static/public/main.js @@ -2,8 +2,17 @@ import { parseAndRender } from 'https://cdn.jsdelivr.net/npm/jempl@1.0.0-rc2/+es import jsYaml from 'https://cdn.jsdelivr.net/npm/js-yaml@4.1.0/+esm' const templateInput = document.getElementById("input-template") +const highlightedTemplateInput = document.getElementById("highlighted-input-template") +const highlightedTemplateInputContent = document.getElementById("highlighted-input-template-content") + const dataInput = document.getElementById("input-data") +const highlightedDataInput = document.getElementById("highlighted-input-data") +const highlightedDataInputContent = document.getElementById("highlighted-input-data-content") + const outputContainer = document.getElementById("output") +const highlightingOutput = document.getElementById("highlighted-output") +const highlightedOutputContent = document.getElementById("highlighted-output-content") + const exampleSelect = document.getElementById("example-select") const initDefaultExample = () => { @@ -49,10 +58,27 @@ const customFunctions = { }), } +const updateAndHighlight = (highlightedInput, input) => { + let text = input.value + if (text[text.length - 1] === "\n") { + text += " " + } + highlightedInput.innerHTML = text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<") + Prism.highlightElement(highlightedInput) +} + +const syncScroll = (highlightedElement, element) => { + highlightedElement.scrollTop = element.scrollTop; + highlightedElement.scrollLeft = element.scrollLeft; +} + const handleTextChange = () => { let template = {} let data = {} + updateAndHighlight(highlightedTemplateInputContent, templateInput) + updateAndHighlight(highlightedDataInputContent, dataInput) + try { template = jsYaml.load(templateInput.value) } catch (error) { @@ -77,11 +103,15 @@ const handleTextChange = () => { } outputContainer.textContent = jsYaml.dump(result) + updateAndHighlight(highlightedOutputContent, outputContainer) } const injectRenderButtonListener = () => { templateInput.addEventListener("input", handleTextChange) + templateInput.addEventListener("scroll", () => syncScroll(highlightedTemplateInput, templateInput)) dataInput.addEventListener("input", handleTextChange) + dataInput.addEventListener("scroll", () => syncScroll(highlightedDataInput, dataInput)) + outputContainer.addEventListener("scroll", () => syncScroll(highlightingOutput, outputContainer)) } const injectExampleSelectListener = () => { diff --git a/playground/static/public/theme.css b/playground/static/public/theme.css index 878ea47..4d24497 100644 --- a/playground/static/public/theme.css +++ b/playground/static/public/theme.css @@ -171,3 +171,69 @@ textarea { min-height: 150px; } +.highlighted-textarea { + background-color: var(--input); + border: none; + border-radius: 0; + padding: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + resize: none; + min-height: 150px; + position: relative; +} + +.highlighted-textarea pre, +.highlighted-textarea textarea, +.highlighted-textarea pre code { + width: 100%; + height: 100%; + box-sizing: border-box; + resize: none; + min-height: 150px; +} + + +.highlighted-textarea pre, +.highlighted-textarea textarea { + overflow: auto; + white-space: pre; + position: absolute; + top: 0; + left: 0; + border: var(--border-width-sm) solid var(--border); + border-radius: var(--border-radius-sm); + padding: var(--spacing-sm) !important; +} + +.highlighted-textarea textarea, +.highlighted-textarea pre code { + font-family: Roboto, -apple-system, "Helvetica Neue", sans-serif; + font-size: var(--md-font-size); + font-weight: var(--md-font-weight); + line-height: var(--md-line-height); + letter-spacing: var(--md-letter-spacing); + background-color: transparent; +} + +.highlighted-textarea pre { + margin: 1px !important; + margin-left: 0 !important; + margin-right: 0 !important; + z-index: 0; + scrollbar-width: thin; + scrollbar-color: transparent transparent; +} + +.highlighted-textarea textarea { + resize: none; + color: transparent; + caret-color: var(--foreground); + z-index: 1; +} + + +.token { + font-weight: normal !important; +} \ No newline at end of file diff --git a/playground/templates/base.yaml b/playground/templates/base.yaml index fa95322..e8bce57 100644 --- a/playground/templates/base.yaml +++ b/playground/templates/base.yaml @@ -4,6 +4,12 @@ - script src="./public/main.js" defer type="module": - script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js": - script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@0.1.8/dist/rettangoli-iife-ui.min.js": + + - link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.30/themes/prism-tomorrow.css": + - script src="https://cdn.jsdelivr.net/npm/prismjs@1.30/components/prism-core.min.js" data-manual: + - script src="https://cdn.jsdelivr.net/npm/prismjs@1.30/plugins/autoloader/prism-autoloader.min.js": + + - meta charset="utf-8": - meta name="viewport" content="width=device-width,initial-scale=1": - title: Jempl Playground