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 1/4] =?UTF-8?q?=F0=9F=9A=A7=20Allow=20editing=20of=20layer?= =?UTF-8?q?=20file=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 45b19f1fbc..66c7d3901b 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 e696db7d22..5417d790ed 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 b3aeb531a1..1bef33d5d8 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 fc74eb3abc..0920b030de 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} From a8313022a081476930c1a50ac427142af3ab0c73 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 13 Feb 2024 11:44:09 +0100 Subject: [PATCH 2/4] Split out editor, add to theme editing --- src/UI/Studio/EditLayer.svelte | 74 ++---------------------- src/UI/Studio/EditTheme.svelte | 13 +++-- src/UI/Studio/RawEditor.svelte | 101 +++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 72 deletions(-) create mode 100644 src/UI/Studio/RawEditor.svelte diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index 0920b030de..e8fa9168d1 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -13,24 +13,15 @@ import SchemaBasedInput from "./SchemaBasedInput.svelte" import FloatOver from "../Base/FloatOver.svelte" import TagRenderingInput from "./TagRenderingInput.svelte" - import FromHtml from "../Base/FromHtml.svelte" 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" + import RawEditor from "./RawEditor.svelte" 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( @@ -69,7 +60,7 @@ } let requiredFields = ["id", "name", "description", "source"] - let currentlyMissing = state.configuration.map((config) => { + let currentlyMissing = configuration.map((config) => { if (!config) { return [] } @@ -88,61 +79,6 @@ 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) - }) - })
@@ -191,7 +127,7 @@ {/each} {:else}
- +
General properties @@ -254,7 +190,9 @@ Below, you'll find the raw configuration file in `.json`-format. This is mostly for debugging purposes, but you can also edit the file directly if you want.
-
+
+ +
diff --git a/src/UI/Studio/EditTheme.svelte b/src/UI/Studio/EditTheme.svelte index 34eff126ed..16f9e0a363 100644 --- a/src/UI/Studio/EditTheme.svelte +++ b/src/UI/Studio/EditTheme.svelte @@ -6,6 +6,7 @@ import TabbedGroup from "../Base/TabbedGroup.svelte" import ShowConversionMessages from "./ShowConversionMessages.svelte" import Region from "./Region.svelte" + import RawEditor from "./RawEditor.svelte" export let state: EditThemeState let schema: ConfigMeta[] = state.schema.filter((schema) => schema.path.length > 0) @@ -20,7 +21,7 @@ const perRegion: Record = {} for (const schemaElement of schema) { - if(schemaElement.path.length > 1 && schemaElement.path[0] === "layers"){ + if (schemaElement.path.length > 1 && schemaElement.path[0] === "layers") { continue } const key = schemaElement.hints.group ?? "no-group" @@ -73,9 +74,13 @@
Configuration file
-
-
- {JSON.stringify($config)} +
+
+ Below, you'll find the raw configuration file in `.json`-format. This is mostly for + debugging purposes, but you can also edit the file directly if you want. +
+
+
diff --git a/src/UI/Studio/RawEditor.svelte b/src/UI/Studio/RawEditor.svelte new file mode 100644 index 0000000000..679f2f3146 --- /dev/null +++ b/src/UI/Studio/RawEditor.svelte @@ -0,0 +1,101 @@ + + +
From 302289c36ece28006a7f4235f815902537934c6f Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 13 Feb 2024 12:19:28 +0100 Subject: [PATCH 3/4] Change height --- src/UI/Studio/EditLayer.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index e8fa9168d1..ee1f5849d3 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -185,12 +185,12 @@
Configuration file
-
+
Below, you'll find the raw configuration file in `.json`-format. This is mostly for debugging purposes, but you can also edit the file directly if you want.
-
+
From 5e4f67e9a67971d998fc8dbe94b172ef12db0259 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 22 Feb 2024 03:05:50 +0100 Subject: [PATCH 4/4] Slightly clean up styling --- package-lock.json | 2 +- public/css/index-tailwind-output.css | 44 ++++++++++++++++++---------- src/UI/Studio/EditLayer.svelte | 23 ++++++++------- src/UI/Studio/EditTheme.svelte | 7 ++--- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 571e8ef667..edabe369d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24014,4 +24014,4 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } -} \ No newline at end of file +} diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 828557555f..81bf9ff94d 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -777,6 +777,10 @@ video { float: left; } +.m-8 { + margin: 2rem; +} + .m-4 { margin: 1rem; } @@ -789,10 +793,6 @@ video { margin: 0px; } -.m-8 { - margin: 2rem; -} - .m-2 { margin: 0.5rem; } @@ -896,6 +896,10 @@ video { margin-right: 4rem; } +.mb-4 { + margin-bottom: 1rem; +} + .mt-4 { margin-top: 1rem; } @@ -928,10 +932,6 @@ video { margin-right: 0.25rem; } -.mb-4 { - margin-bottom: 1rem; -} - .ml-1 { margin-left: 0.25rem; } @@ -1127,14 +1127,14 @@ video { height: 50%; } -.h-7 { - height: 1.75rem; -} - .h-3 { height: 0.75rem; } +.h-7 { + height: 1.75rem; +} + .h-11 { height: 2.75rem; } @@ -1163,6 +1163,10 @@ video { height: 20rem; } +.h-5\/6 { + height: 83.333333%; +} + .h-56 { height: 14rem; } @@ -1233,14 +1237,14 @@ video { width: 1rem; } -.w-7 { - width: 1.75rem; -} - .w-3 { width: 0.75rem; } +.w-7 { + width: 1.75rem; +} + .w-11 { width: 2.75rem; } @@ -1266,6 +1270,14 @@ video { width: 4rem; } +.w-5\/6 { + width: 83.333333%; +} + +.w-1\/6 { + width: 16.666667%; +} + .w-min { width: -webkit-min-content; width: min-content; diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index ee1f5849d3..41c309e16f 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -185,22 +185,25 @@
Configuration file
-
+
Below, you'll find the raw configuration file in `.json`-format. This is mostly for debugging purposes, but you can also edit the file directly if you want.
-
- -
- -
- The testobject (which is used to render the questions in the 'information panel' item - has the following tags: -
+
+
+ +
+
+
+ The testobject (which is used to render the questions in the 'information panel' + item has the following tags: +
- + +
+
diff --git a/src/UI/Studio/EditTheme.svelte b/src/UI/Studio/EditTheme.svelte index 16f9e0a363..bd71c0134f 100644 --- a/src/UI/Studio/EditTheme.svelte +++ b/src/UI/Studio/EditTheme.svelte @@ -51,7 +51,7 @@
- {Object.keys(perRegion).join(";")} +
Basic properties
@@ -74,16 +74,15 @@
Configuration file
-
+
Below, you'll find the raw configuration file in `.json`-format. This is mostly for debugging purposes, but you can also edit the file directly if you want.
+
- -