From 43f692e75c894aea6f7d384aee1b9e7dd7879d12 Mon Sep 17 00:00:00 2001 From: Hare Date: Thu, 9 Jul 2026 19:21:12 +0900 Subject: [PATCH] Add playground entry selector --- site/decodal-site/src/pages/playground.astro | 22 +++++++++---- site/decodal-site/src/scripts/playground.js | 34 ++++++++++++++++---- site/decodal-site/src/style.css | 26 +++++++++++++-- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/site/decodal-site/src/pages/playground.astro b/site/decodal-site/src/pages/playground.astro index 1a179a5..95aefba 100644 --- a/site/decodal-site/src/pages/playground.astro +++ b/site/decodal-site/src/pages/playground.astro @@ -9,14 +9,22 @@ import ManualLayout from '../layouts/ManualLayout.astro';

Virtual files are evaluated in the browser through WebAssembly. Use import paths such as ./schemas/service.dcdl.

- - +
+ + +
+
+ + + +

Loading WASM...

- -
diff --git a/site/decodal-site/src/scripts/playground.js b/site/decodal-site/src/scripts/playground.js index 44128e6..ecb4af0 100644 --- a/site/decodal-site/src/scripts/playground.js +++ b/site/decodal-site/src/scripts/playground.js @@ -20,6 +20,7 @@ const activeFile = document.getElementById('active-file'); const newFile = document.getElementById('new-file'); const deleteFile = document.getElementById('delete-file'); const exampleSelect = document.getElementById('example-select'); +const entrySelect = document.getElementById('entry-select'); const loadExample = document.getElementById('load-example'); const project = loadProject(); @@ -178,18 +179,32 @@ function setActiveFile(path) { } project.activePath = normalized; setEditorText(project.files[normalized]); - activeFile.textContent = normalized === project.entryPath ? `${normalized} (entry)` : normalized; + activeFile.textContent = normalized; deleteFile.disabled = Object.keys(project.files).length <= 1; renderFileTree(); updateRunLabel(); saveProject(); } -function updateRunLabel() { +function updateEntrySelect() { + const paths = Object.keys(project.files).sort(); const entryPath = project.files[project.entryPath] === undefined ? project.activePath : project.entryPath; project.entryPath = entryPath; - run.textContent = `Run ${entryPath}`; - run.title = `Materialize ${entryPath}`; + entrySelect.replaceChildren( + ...paths.map((path) => { + const option = document.createElement('option'); + option.value = path; + option.textContent = path; + return option; + }), + ); + entrySelect.value = entryPath; +} + +function updateRunLabel() { + updateEntrySelect(); + run.textContent = 'Run'; + run.title = `Materialize ${project.entryPath}`; } function execute() { @@ -247,8 +262,8 @@ function renderTreeList(children) { const button = document.createElement('button'); button.type = 'button'; button.className = child.path === project.activePath ? 'file active' : 'file'; - button.textContent = child.path === project.entryPath ? `${child.name} *` : child.name; - button.title = child.path === project.entryPath ? `${child.path} (entry point)` : child.path; + button.textContent = child.name; + button.title = child.path; button.addEventListener('click', () => setActiveFile(child.path)); item.append(button); } else { @@ -277,6 +292,13 @@ try { status.textContent = `Failed to load WASM: ${error?.message ?? error}`; } +entrySelect.addEventListener('change', () => { + const path = normalizePath(entrySelect.value); + if (project.files[path] === undefined) return; + project.entryPath = path; + updateRunLabel(); + saveProject(); +}); run.addEventListener('click', execute); formatButton.addEventListener('click', formatActiveFile); loadExample.addEventListener('click', () => { diff --git a/site/decodal-site/src/style.css b/site/decodal-site/src/style.css index e155c96..0fcdc3c 100644 --- a/site/decodal-site/src/style.css +++ b/site/decodal-site/src/style.css @@ -293,11 +293,26 @@ main.playground { align-items: center; display: flex; flex-wrap: wrap; - gap: 8px; + gap: 10px; justify-content: flex-end; } -.example-picker { +.action-group { + align-items: center; + background: rgba(15, 23, 42, 0.55); + border: 1px solid var(--border); + border-radius: 12px; + display: flex; + gap: 8px; + padding: 6px; +} + +.run-actions { + border-color: rgba(96, 165, 250, 0.35); +} + +.example-picker, +.entry-picker { align-items: center; color: var(--muted); display: flex; @@ -307,7 +322,8 @@ main.playground { text-transform: uppercase; } -.example-picker select { +.example-picker select, +.entry-picker select { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; @@ -318,6 +334,10 @@ main.playground { text-transform: none; } +.entry-picker select { + max-width: 220px; +} + button { background: var(--link); border: 0;