From a093d5264702b5c4544440ddb41e0093726518cd Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 13 Feb 2024 00:52:50 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Allow=20editing=20of=20layer=20f?= =?UTF-8?q?ile=20in=20Studio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 49 ++++++++++++++++++- package.json | 2 + src/UI/Base/TabbedGroup.svelte | 3 ++ src/UI/Studio/EditLayer.svelte | 88 ++++++++++++++++++++++++++++------ 4 files changed, 125 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 45b19f1fb..66c7d3901 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mapcomplete", - "version": "0.37.0", + "version": "0.37.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mapcomplete", - "version": "0.37.0", + "version": "0.37.2", "license": "GPL-3.0-or-later", "dependencies": { "@rgossiaux/svelte-headlessui": "^1.0.2", @@ -44,6 +44,7 @@ "lz-string": "^1.4.4", "mangrove-reviews-typescript": "^1.1.0", "maplibre-gl": "^3.5.0", + "monaco-editor": "^0.46.0", "nano-markdown": "^1.2.2", "opening_hours": "^3.6.0", "osm-auth": "^2.2.0", @@ -68,6 +69,7 @@ "@babeard/svelte-heroicons": "^2.0.0-rc.0", "@babel/polyfill": "^7.10.4", "@babel/preset-env": "7.13.8", + "@monaco-editor/loader": "^1.4.0", "@parcel/service-worker": "^2.6.0", "@rollup/plugin-json": "^6.0.0", "@sveltejs/vite-plugin-svelte": "^2.0.2", @@ -2355,6 +2357,18 @@ "gl-style-validate": "dist/gl-style-validate.mjs" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "dev": true, + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9377,6 +9391,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/monaco-editor": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz", + "integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==" + }, "node_modules/monotone-convex-hull-2d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz", @@ -11499,6 +11518,12 @@ "node": ">=0.1.14" } }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "dev": true + }, "node_modules/std-env": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz", @@ -15357,6 +15382,15 @@ "sort-object": "^3.0.3" } }, + "@monaco-editor/loader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", + "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", + "dev": true, + "requires": { + "state-local": "^1.0.6" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -20688,6 +20722,11 @@ } } }, + "monaco-editor": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz", + "integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==" + }, "monotone-convex-hull-2d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz", @@ -22222,6 +22261,12 @@ "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", "optional": true }, + "state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "dev": true + }, "std-env": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.2.tgz", diff --git a/package.json b/package.json index e696db7d2..5417d790e 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "lz-string": "^1.4.4", "mangrove-reviews-typescript": "^1.1.0", "maplibre-gl": "^3.5.0", + "monaco-editor": "^0.46.0", "nano-markdown": "^1.2.2", "opening_hours": "^3.6.0", "osm-auth": "^2.2.0", @@ -166,6 +167,7 @@ "@babeard/svelte-heroicons": "^2.0.0-rc.0", "@babel/polyfill": "^7.10.4", "@babel/preset-env": "7.13.8", + "@monaco-editor/loader": "^1.4.0", "@parcel/service-worker": "^2.6.0", "@rollup/plugin-json": "^6.0.0", "@sveltejs/vite-plugin-svelte": "^2.0.2", diff --git a/src/UI/Base/TabbedGroup.svelte b/src/UI/Base/TabbedGroup.svelte index b3aeb531a..1bef33d5d 100644 --- a/src/UI/Base/TabbedGroup.svelte +++ b/src/UI/Base/TabbedGroup.svelte @@ -28,6 +28,9 @@ window.setTimeout(() => tabElements[tab.data].click(), 50) } } + export function getTab() { + return tab + }
diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index fc74eb3ab..0920b030d 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -17,14 +17,24 @@ import AllTagsPanel from "../Popup/AllTagsPanel.svelte" import QuestionPreview from "./QuestionPreview.svelte" import ShowConversionMessages from "./ShowConversionMessages.svelte" + import loader from "@monaco-editor/loader" + import type * as Monaco from "monaco-editor/esm/vs/editor/editor.api" + import { onMount } from "svelte" + import layerSchemaJSON from "../../../Docs/Schemas/LayerConfigJson.schema.json" const layerSchema: ConfigMeta[] = layerSchemaRaw export let state: EditLayerState + + // Throw error if we don't have a state + if (!state) { + throw new Error("No state provided") + } + export let backToStudio: () => void let messages = state.messages let hasErrors = messages.mapD( - (m: ConversionMessage[]) => m.filter((m) => m.level === "error").length, + (m: ConversionMessage[]) => m.filter((m) => m.level === "error").length ) const configuration = state.configuration @@ -78,6 +88,61 @@ state.delete() backToStudio() } + + let tabbedGroup: TabbedGroup + let openTab: UIEventSource = new UIEventSource(0) + + let monaco: typeof Monaco + let editorContainer: HTMLDivElement + let layerEditor: Monaco.editor.IStandaloneCodeEditor + let model: Monaco.editor.ITextModel + + onMount(async () => { + openTab = tabbedGroup.getTab() + + const monacoEditor = await import("monaco-editor") + loader.config({ monaco: monacoEditor.default }) + + monaco = await loader.init() + + // Prepare the Monaco editor (language settings) + // A.K.A. The schemas for the Monaco editor + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + schemas: [ + { + uri: "https://mapcomplete.org/schemas/layerconfig.json", + fileMatch: ["layer.json"], + schema: layerSchemaJSON, + }, + ], + }) + let modelUri = monaco.Uri.parse("inmemory://inmemory/layer.json") + model = monaco.editor.createModel( + JSON.stringify(state.configuration.data, null, " "), + "json", + modelUri + ) + + layerEditor = monaco.editor.create(editorContainer, { + model: model, + automaticLayout: true, + }) + + // When the editor is changed, update the configuration, but only if the user hasn't typed for 500ms and the JSON is valid + let timeout: number + layerEditor.onDidChangeModelContent(() => { + clearTimeout(timeout) + timeout = setTimeout(() => { + try { + const newConfig = JSON.parse(layerEditor.getValue()) + state.configuration.setData(newConfig) + } catch (e) { + console.error(e) + } + }, 500) + }) + })
@@ -102,10 +167,7 @@ rel="noopener" >
- - - Test in safe mode - + Test in safe mode
No changes are recoded to OSM
@@ -129,7 +191,7 @@ {/each} {:else}
- +
General properties @@ -190,11 +252,9 @@
Below, you'll find the raw configuration file in `.json`-format. This is mostly for - debugging purposes -
-
- ")} /> + debugging purposes, but you can also edit the file directly if you want.
+
@@ -209,11 +269,9 @@ {#if $highlightedItem !== undefined} highlightedItem.setData(undefined)}>
- + +
{/if}