Add various improvements and fixes to studio, should fix #2055

This commit is contained in:
Pieter Vander Vennet 2024-08-02 19:06:14 +02:00
parent b19d9ef077
commit d1ec9a43fc
19 changed files with 532 additions and 419 deletions

View file

@ -6,7 +6,7 @@
<Accordion>
<AccordionItem open={expanded} paddingDefault="p-0" inactiveClass="text-black">
<span slot="header" class="p-2 text-base">
<span slot="header" class="p-2 text-base w-full">
<slot name="header" />
</span>
<div class="low-interaction rounded-b p-2">

View file

@ -373,7 +373,6 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -418,7 +417,6 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -464,7 +462,6 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}

View file

@ -0,0 +1,207 @@
<script lang="ts">
import Translations from "../i18n/Translations"
import type { ConfigMeta } from "./configMeta"
import Icon from "../Map/Icon.svelte"
import Tr from "../Base/Tr.svelte"
import { Translation } from "../i18n/Translation"
import { UIEventSource } from "../../Logic/UIEventSource"
import SchemaBasedInput from "./SchemaBasedInput.svelte"
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
import QuestionPreview from "./QuestionPreview.svelte"
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"
import { EditJsonState } from "./EditLayerState"
import type {
QuestionableTagRenderingConfigJson,
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { AccordionItem } from "flowbite-svelte"
export let state: EditJsonState<any>
export let isTagRenderingBlock: boolean
export let schema: ConfigMeta
export let currentValue: UIEventSource<(string | QuestionableTagRenderingConfigJson)[]>
export let value: any
export let singular: string
export let i: number
export let path: (string | number)[] = []
export let expanded = new UIEventSource(false)
const subparts: ConfigMeta[] = state
.getSchemaStartingWith(schema.path)
.filter((part) => part.path.length - 1 === schema.path.length)
function schemaForMultitype() {
const sch = { ...schema }
sch.hints.typehint = undefined
return sch
}
function fusePath(i: number, subpartPath: string[]): (string | number)[] {
const newPath = [...path, i]
const toAdd = [...subpartPath]
for (const part of path) {
if (toAdd[0] === part) {
toAdd.splice(0, 1)
} else {
break
}
}
newPath.push(...toAdd)
return newPath
}
function del(i: number) {
expanded.setData(false)
currentValue.data.splice(i, 1)
currentValue.ping()
}
function swap(i: number, j: number) {
expanded.setData(false)
const x = currentValue.data[i]
currentValue.data[i] = currentValue.data[j]
currentValue.data[j] = x
currentValue.ping()
}
function moveTo(source: number, target: number) {
expanded.setData(false)
const x = currentValue.data[source]
currentValue.data.splice(source, 1)
currentValue.data.splice(target, 0, x)
currentValue.ping()
}
function genTitle(value: any, singular: string, i: number): Translation {
try {
if (schema.hints.title) {
const v = Function("value", "return " + schema.hints.title)(value)
return Translations.T(v)
}
} catch (e) {
console.log(
"Warning: could not translate a title for " +
`${singular} ${i} with function ` +
schema.hints.title +
" and value " +
JSON.stringify(value),
)
}
return Translations.T(`${singular} ${i}`)
}
let genIconF: (x: any) => { icon: string; color: string } = <any>(
Function("value", "return " + schema.hints.icon)
)
function genIcon(value: any): string {
return genIconF(value)?.icon
}
function genColor(value: any): string {
if (!schema.hints.icon) {
return undefined
}
return genIconF(value)?.color
}
</script>
<AccordionItem open={$expanded} paddingDefault="p-0" inactiveClass="text-black m-0" >
<div slot="header" class="p-1 text-base w-full m-0 text-black">
{#if !isTagRenderingBlock}
<div class="flex items-center justify-between w-full">
<div class="m-0 flex">
{#if schema.hints.icon}
<Icon clss="w-6 h-6" icon={genIcon(value)} color={genColor(value)} />
{/if}
{#if schema.hints.title}
<Tr t={genTitle(value, singular, i)} />
<div class="subtle ml-2">
{singular}
{i}
</div>
{:else}
{singular}
{i}
{/if}
</div>
<button
class="h-fit w-fit rounded-full border border-black p-1"
on:click={() => {
del(i)
}}
>
<TrashIcon class="h-4 w-4" />
</button>
</div>
{:else if typeof value === "string"}
Builtin: <b>{value}</b>
{:else if value["builtin"]}
reused tagrendering <span class="font-bold">{JSON.stringify(value["builtin"])}</span>
{:else}
<Tr cls="font-bold" t={Translations.T(value?.question ?? value?.render)} />
{/if}
</div>
<div class="normal-background p-2">
{#if isTagRenderingBlock}
<QuestionPreview {state} {path} {schema}>
<button
on:click={() => {
del(i)
}}
>
<TrashIcon class="h-4 w-4" />
Delete this question
</button>
{#if i > 0}
<button
on:click={() => {
moveTo(i, 0)
}}
>
Move to front
</button>
<button
on:click={() => {
swap(i, i - 1)
}}
>
Move up
</button>
{/if}
{#if i + 1 < $currentValue.length}
<button
on:click={() => {
swap(i, i + 1)
}}
>
Move down
</button>
<button
on:click={() => {
moveTo(i, $currentValue.length - 1)
}}
>
Move to back
</button>
{/if}
</QuestionPreview>
{:else if schema.hints.types}
<SchemaBasedMultiType {state} path={fusePath(i, [])} schema={schemaForMultitype()} />
{:else}
{#each subparts as subpart}
<SchemaBasedInput
{state}
path={fusePath(i, [subpart.path.at(-1)])}
schema={subpart}
/>
{/each}
{/if}
</div>
</AccordionItem>

View file

@ -8,7 +8,7 @@ import {
Pipe,
} from "../../Models/ThemeConfig/Conversion/Conversion"
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
import { ValidateLayer, ValidateTheme } from "../../Models/ThemeConfig/Conversion/Validation"
import { PrevalidateTheme, ValidateLayer, ValidateTheme } from "../../Models/ThemeConfig/Conversion/Validation"
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
import { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { TagUtils } from "../../Logic/Tags/TagUtils"
@ -33,6 +33,8 @@ export abstract class EditJsonState<T> {
public readonly schema: ConfigMeta[]
public readonly category: "layers" | "themes"
public readonly server: StudioServer
public readonly osmConnection: OsmConnection
public readonly showIntro: UIEventSource<"no" | "intro" | "tagrenderings"> = <any>(
LocalStorageSource.Get("studio-show-intro", "intro")
)
@ -51,7 +53,7 @@ export abstract class EditJsonState<T> {
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
*/
public readonly highlightedItem: UIEventSource<HighlightedTagRendering> = new UIEventSource(
undefined
undefined,
)
private sendingUpdates = false
private readonly _stores = new Map<string, UIEventSource<any>>()
@ -60,10 +62,12 @@ export abstract class EditJsonState<T> {
schema: ConfigMeta[],
server: StudioServer,
category: "layers" | "themes",
osmConnection: OsmConnection,
options?: {
expertMode?: UIEventSource<boolean>
}
},
) {
this.osmConnection = osmConnection
this.schema = schema
this.server = server
this.category = category
@ -88,6 +92,10 @@ export abstract class EditJsonState<T> {
await this.server.update(id, config, this.category)
})
this.messages = this.createMessagesStore()
this.register(["credits"], this.osmConnection.userDetails.mapD(u => u.name), false)
this.register(["credits:uid"], this.osmConnection.userDetails.mapD(u => u.uid), false)
}
public startSavingUpdates(enabled = true) {
@ -132,7 +140,7 @@ export abstract class EditJsonState<T> {
public register(
path: ReadonlyArray<string | number>,
value: Store<any>,
noInitialSync: boolean = true
noInitialSync: boolean = true,
): () => void {
const unsync = value.addCallback((v) => {
this.setValueAt(path, v)
@ -146,7 +154,7 @@ export abstract class EditJsonState<T> {
public getSchemaStartingWith(path: string[]) {
return this.schema.filter(
(sch) =>
!path.some((part, i) => !(sch.path.length > path.length && sch.path[i] === part))
!path.some((part, i) => !(sch.path.length > path.length && sch.path[i] === part)),
)
}
@ -167,7 +175,7 @@ export abstract class EditJsonState<T> {
const schemas = this.schema.filter(
(sch) =>
sch !== undefined &&
!path.some((part, i) => !(sch.path.length == path.length && sch.path[i] === part))
!path.some((part, i) => !(sch.path.length == path.length && sch.path[i] === part)),
)
if (schemas.length == 0) {
console.warn("No schemas found for path", path.join("."))
@ -257,12 +265,12 @@ class ContextRewritingStep<T> extends Conversion<LayerConfigJson, T> {
constructor(
state: DesugaringContext,
step: Conversion<LayerConfigJson, T>,
getTagRenderings: (t: T) => TagRenderingConfigJson[]
getTagRenderings: (t: T) => TagRenderingConfigJson[],
) {
super(
"When validating a layer, the tagRenderings are first expanded. Some builtin tagRendering-calls (e.g. `contact`) will introduce _multiple_ tagRenderings, causing the count to be off. This class rewrites the error messages to fix this",
[],
"ContextRewritingStep"
"ContextRewritingStep",
)
this._state = state
this._step = step
@ -272,7 +280,7 @@ class ContextRewritingStep<T> extends Conversion<LayerConfigJson, T> {
convert(json: LayerConfigJson, context: ConversionContext): T {
const converted = this._step.convert(json, context)
const originalIds = json.tagRenderings?.map(
(tr) => (<QuestionableTagRenderingConfigJson>tr)["id"]
(tr) => (<QuestionableTagRenderingConfigJson>tr)["id"],
)
if (!originalIds) {
return converted
@ -307,7 +315,6 @@ class ContextRewritingStep<T> extends Conversion<LayerConfigJson, T> {
export default class EditLayerState extends EditJsonState<LayerConfigJson> {
// Needed for the special visualisations
public readonly osmConnection: OsmConnection
public readonly imageUploadManager = {
getCountsFor() {
return 0
@ -335,10 +342,9 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
schema: ConfigMeta[],
server: StudioServer,
osmConnection: OsmConnection,
options: { expertMode: UIEventSource<boolean> }
options: { expertMode: UIEventSource<boolean> },
) {
super(schema, server, "layers", options)
this.osmConnection = osmConnection
super(schema, server, "layers", osmConnection, options)
this.layout = {
getMatchingLayer: () => {
try {
@ -393,7 +399,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
return new ContextRewritingStep(
state,
new Pipe(new PrepareLayer(state), new ValidateLayer("dynamic", false, undefined, true)),
(t) => <TagRenderingConfigJson[]>t.raw.tagRenderings
(t) => <TagRenderingConfigJson[]>t.raw.tagRenderings,
)
}
@ -427,7 +433,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
}
protected async validate(
configuration: Partial<LayerConfigJson>
configuration: Partial<LayerConfigJson>,
): Promise<ConversionMessage[]> {
const layers = AllSharedLayers.getSharedLayersConfigs()
@ -456,16 +462,19 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
constructor(
schema: ConfigMeta[],
server: StudioServer,
options: { expertMode: UIEventSource<boolean> }
osmConnection: OsmConnection,
options: { expertMode: UIEventSource<boolean> },
) {
super(schema, server, "themes", options)
super(schema, server, "themes", osmConnection, options)
this.setupFixers()
}
protected buildValidation(state: DesugaringContext): Conversion<LayoutConfigJson, any> {
return new Pipe(
new PrepareTheme(state),
new ValidateTheme(undefined, "", false, new Set(state.tagRenderings.keys()))
return new Pipe(new PrevalidateTheme(),
new Pipe(
new PrepareTheme(state),
new ValidateTheme(undefined, "", false, new Set(state.tagRenderings.keys())),
), true,
)
}

View file

@ -1,11 +1,13 @@
<script lang="ts">
import type { ConfigMeta } from "./configMeta"
import EditLayerState from "./EditLayerState"
import EditLayerState, { EditJsonState } from "./EditLayerState"
import * as questions from "../../assets/generated/layers/questions.json"
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.js"
import type {
QuestionableTagRenderingConfigJson,
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.js"
import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
import FromHtml from "../Base/FromHtml.svelte"
import ShowConversionMessage from "./ShowConversionMessage.svelte"
@ -31,15 +33,25 @@
if (typeof x === "string") {
return perId[x]
} else {
return [x]
return <any>[x]
}
})
let configs: Store<TagRenderingConfig[]> = configJson.map((configs) => {
if (!configs) {
return [{ error: "No configuartions found" }]
}
console.log("Regenerating configs")
return configs.map((config) => {
if (config["builtin"]) {
let override = ""
if (config["override"]) {
override = ". Some items are changed with an override. Editing this is not yet supported with Studio."
}
return new TagRenderingConfig({
render: {
"en": "This question reuses <b>" + JSON.stringify(config["builtin"]) + "</b>" + override,
},
})
}
try {
return new TagRenderingConfig(config)
} catch (e) {
@ -47,15 +59,6 @@
}
})
})
let id: Store<string> = value.mapD((c) => {
if (c?.id) {
return c.id
}
if (typeof c === "string") {
return c
}
return undefined
})
let tags = state.testTags
@ -66,11 +69,18 @@
<div class="flex">
<div class="m-4 flex w-full flex-col">
<NextButton clss="primary" on:click={() => state.highlightedItem.setData({ path, schema })}>
{#if schema.hints.question}
{schema.hints.question}
{/if}
</NextButton>
{#if $configJson.some(config => config["builtin"] !== undefined)}
<div class="interactive p-2 rounded-2xl">
This question uses an advanced 'builtin'+'override' construction in the source code.
Editing this with Studio is not supported.
</div>
{:else}
<NextButton clss="primary" on:click={() => state.highlightedItem.setData({ path, schema })}>
{#if schema.hints.question}
{schema.hints.question}
{/if}
</NextButton>
{/if}
{#if description}
<Markdown src={description} />
{/if}
@ -86,6 +96,7 @@
{#each $configs as config}
{#if config.error !== undefined}
<div class="alert">Could not create a preview of this tagRendering: {config.error}</div>
{JSON.stringify($value)}
{:else if config.condition && !config.condition.matchesProperties($tags)}
This tagRendering is currently not shown. It will appear if the feature matches the
condition

View file

@ -8,6 +8,15 @@
export let state: EditLayerState | EditThemeState
let rawConfig = state.configuration.sync(f => JSON.stringify(f, null, " "), [], json => {
try {
return JSON.parse(json)
} catch (e) {
console.error("Could not parse", json)
return undefined
}
})
let container: HTMLDivElement
let monaco: typeof Monaco
let editor: Monaco.editor.IStandaloneCodeEditor
@ -34,13 +43,19 @@
return () => window.removeEventListener("keydown", handler)
})
let useFallback = false
onMount(async () => {
const monacoEditor = await import("monaco-editor")
loader.config({
monaco: monacoEditor.default,
})
monaco = await loader.init()
try {
monaco = await loader.init()
} catch (e) {
console.error("Could not load Monaco Editor, falling back", e)
useFallback = true
}
// Determine schema based on the state
let schemaUri: string
@ -50,7 +65,7 @@
schemaUri = "https://mapcomplete.org/schemas/layoutconfig.json"
}
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
monaco?.languages?.json?.jsonDefaults?.setDiagnosticsOptions({
validate: true,
schemas: [
{
@ -64,23 +79,28 @@
],
})
let modelUri = monaco.Uri.parse("inmemory://inmemory/file.json")
let modelUri = monaco?.Uri?.parse("inmemory://inmemory/file.json")
// Create a new model
model = monaco.editor.createModel(
JSON.stringify(state.configuration.data, null, " "),
"json",
modelUri
)
try {
model = monaco?.editor?.createModel(
JSON.stringify(state.configuration.data, null, " "),
"json",
modelUri,
)
} catch (e) {
console.error("Could not create model in MOnaco Editor", e)
useFallback = true
}
editor = monaco.editor.create(container, {
editor = monaco?.editor?.create(container, {
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
editor.onDidChangeModelContent(() => {
editor?.onDidChangeModelContent(() => {
clearTimeout(timeout)
timeout = setTimeout(() => {
save()
@ -98,4 +118,8 @@
})
</script>
<div bind:this={container} class="h-full w-full" />
{#if useFallback}
<textarea class="w-full" rows="25" bind:value={$rawConfig} />
{:else}
<div bind:this={container} class="h-full w-full" />
{/if}

View file

@ -4,9 +4,10 @@
* They will typically be a subset of some properties
*/
import type { ConfigMeta } from "./configMeta"
import EditLayerState from "./EditLayerState"
import EditLayerState, { EditJsonState } from "./EditLayerState"
import SchemaBasedInput from "./SchemaBasedInput.svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import { Utils } from "../../Utils"
export let state: EditLayerState
export let configs: ConfigMeta[]
@ -30,8 +31,8 @@
<div slot="header">{title}</div>
<div class="flex w-full flex-col gap-y-1 pl-2">
<slot name="description" />
{#each configsFiltered as config}
<SchemaBasedInput {state} path={config.path} schema={config} />
{#each configsFiltered as config (config.path)}
<SchemaBasedInput {state} path={config.path} } />
{/each}
</div>
</AccordionSingle>

View file

@ -1,24 +1,21 @@
<script lang="ts">
import EditLayerState from "./EditLayerState"
import { EditJsonState } from "./EditLayerState"
import type { ConfigMeta } from "./configMeta"
import { UIEventSource } from "../../Logic/UIEventSource"
import SchemaBasedInput from "./SchemaBasedInput.svelte"
import SchemaBasedField from "./SchemaBasedField.svelte"
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
import QuestionPreview from "./QuestionPreview.svelte"
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"
import ShowConversionMessage from "./ShowConversionMessage.svelte"
import Markdown from "../Base/Markdown.svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import Icon from "../Map/Icon.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import { Utils } from "../../Utils"
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import CollapsedTagRenderingPreview from "./CollapsedTagRenderingPreview.svelte"
import { Accordion } from "flowbite-svelte"
export let state: EditLayerState
export let state: EditJsonState<any>
export let path: (string | number)[] = []
export let schema: ConfigMeta
schema = Utils.Clone(schema)
let title = schema.path.at(-1)
console.log(">>>", schema)
let singular = title
if (title?.endsWith("s")) {
singular = title.slice(0, title.length - 1)
@ -27,7 +24,6 @@
if (singular?.match(/^[aeoui]/)) {
article = "an"
}
export let path: (string | number)[] = []
const isTagRenderingBlock = path.length === 1 && path[0] === "tagRenderings"
@ -41,12 +37,12 @@
.filter((part) => part.path.length - 1 === schema.path.length)
let messages = state.messagesFor(path)
const currentValue: UIEventSource<any[]> = state.getStoreFor(path)
const currentValue = state.getStoreFor<(string | QuestionableTagRenderingConfigJson)[]>(path)
if (currentValue.data === undefined) {
currentValue.setData([])
}
function createItem(valueToSet?: any) {
function createItem(valueToSet?: string | QuestionableTagRenderingConfigJson) {
if (currentValue.data === undefined) {
currentValue.setData([])
}
@ -72,64 +68,13 @@
return newPath
}
function schemaForMultitype() {
const sch = { ...schema }
sch.hints.typehint = undefined
return sch
}
function del(i: number) {
currentValue.data.splice(i, 1)
currentValue.ping()
}
function swap(i: number, j: number) {
const x = currentValue.data[i]
currentValue.data[i] = currentValue.data[j]
currentValue.data[j] = x
currentValue.ping()
}
function moveTo(source: number, target: number) {
const x = currentValue.data[source]
currentValue.data.splice(source, 1)
currentValue.data.splice(target, 0, x)
currentValue.ping()
}
function genTitle(value: any, singular: string, i: number): Translation {
try {
if (schema.hints.title) {
const v = Function("value", "return " + schema.hints.title)(value)
return Translations.T(v)
}
} catch (e) {
console.log(
"Warning: could not translate a title for " +
`${singular} ${i} with function ` +
schema.hints.title +
" and value " +
JSON.stringify(value)
)
}
return Translations.T(`${singular} ${i}`)
}
let genIconF: (x: any) => { icon: string; color: string } = <any>(
Function("value", "return " + schema.hints.icon)
)
console.log("Icon lambda is", schema.hints.icon, path, genIconF("test"))
function genIcon(value: any): string {
return genIconF(value)?.icon
}
function genColor(value: any): string {
if (!schema.hints.icon) {
return undefined
}
return genIconF(value)?.color
}
</script>
<div class="pl-2">
@ -163,99 +108,11 @@
</div>
{/each}
{:else}
{#each $currentValue as value, i}
<AccordionSingle expanded={false}>
<span slot="header">
{#if !isTagRenderingBlock}
<div class="flex items-center justify-between">
<h3 class="m-0 flex">
{#if schema.hints.icon}
<Icon clss="w-6 h-6" icon={genIcon(value)} color={genColor(value)} />
{/if}
{singular}
{i}
{#if schema.hints.title}
<div class="subtle ml-2">
<Tr t={genTitle(value, singular, i)} />
</div>
{/if}
</h3>
<button
class="h-fit w-fit rounded-full border border-black p-1"
on:click={() => {
del(i)
}}
>
<TrashIcon class="h-4 w-4" />
</button>
</div>
{:else if typeof value === "string"}
Builtin: <b>{value}</b>
{:else}
<Tr cls="font-bold" t={Translations.T(value?.question ?? value?.render)} />
{/if}
</span>
<div class="normal-background p-2">
{#if isTagRenderingBlock}
<QuestionPreview {state} path={fusePath(i, [])} {schema}>
<button
on:click={() => {
del(i)
}}
>
<TrashIcon class="h-4 w-4" />
Delete this question
</button>
{#if i > 0}
<button
on:click={() => {
moveTo(i, 0)
}}
>
Move to front
</button>
<button
on:click={() => {
swap(i, i - 1)
}}
>
Move up
</button>
{/if}
{#if i + 1 < $currentValue.length}
<button
on:click={() => {
swap(i, i + 1)
}}
>
Move down
</button>
<button
on:click={() => {
moveTo(i, $currentValue.length - 1)
}}
>
Move to back
</button>
{/if}
</QuestionPreview>
{:else if schema.hints.types}
<SchemaBasedMultiType {state} path={fusePath(i, [])} schema={schemaForMultitype()} />
{:else}
{#each subparts as subpart}
<SchemaBasedInput
{state}
path={fusePath(i, [subpart.path.at(-1)])}
schema={subpart}
/>
{/each}
{/if}
</div>
</AccordionSingle>
<Accordion>
{#each $currentValue as value, i (value)}
<CollapsedTagRenderingPreview {state} {isTagRenderingBlock} {schema} {currentValue} {value} {i} {singular} path={fusePath(i, [])}/>
{/each}
</Accordion>
{/if}
<div class="flex">
<button on:click={() => createItem()}>Add {article} {singular}</button>

View file

@ -4,13 +4,13 @@
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import EditLayerState from "./EditLayerState"
import { EditJsonState } from "./EditLayerState"
import { onDestroy } from "svelte"
import type { JsonSchemaType } from "./jsonSchema"
import { ConfigMetaUtils } from "./configMeta"
import ShowConversionMessage from "./ShowConversionMessage.svelte"
export let state: EditLayerState
export let state: EditJsonState<any>
export let path: (string | number)[] = []
export let schema: ConfigMeta
export let startInEditModeIfUnset: boolean = schema.hints && !schema.hints.ifunset

View file

@ -1,14 +1,15 @@
<script lang="ts">
import type { ConfigMeta } from "./configMeta"
import SchemaBasedField from "./SchemaBasedField.svelte"
import EditLayerState from "./EditLayerState"
import { EditJsonState } from "./EditLayerState"
import SchemaBasedArray from "./SchemaBasedArray.svelte"
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"
import ArrayMultiAnswer from "./ArrayMultiAnswer.svelte"
export let schema: ConfigMeta
export let state: EditLayerState
export let state: EditJsonState<any>
export let path: (string | number)[] = []
console.log("Fetched schema:", path, state.getSchema(<any> path))
let schema: ConfigMeta = state.getSchema(<any> path)[0]
let expertMode = state.expertMode
</script>

View file

@ -204,9 +204,10 @@
</script>
<div class="m-1 flex flex-col gap-y-2 border-2 border-dashed border-gray-300 p-2">
{#if schema.hints.title !== undefined}
{#if schema.hints.title !== undefined && typeof path.at(-1) !== "number"}
<h3>{schema.hints.title}</h3>
<div>{schema.description}</div>
{path.join(".")}
{/if}
{#if hasOverride}
This object refers to {existingValue.builtin} and overrides some properties. This cannot be edited
@ -221,7 +222,6 @@
{#if $expertMode || subschema.hints?.group !== "expert"}
<SchemaBasedInput
{state}
schema={subschema}
path={[...subpath, subschema?.path?.at(-1) ?? "???"]}
/>
{:else if window.location.hostname === "127.0.0.1"}

View file

@ -42,9 +42,11 @@
undefined,
"Used to complete the login"
)
const fakeUser = UIEventSource.asBoolean( QueryParameters.GetQueryParameter("fake-user", "Test switch for fake login"))
let osmConnection = new OsmConnection({
oauth_token,
checkOnlineRegularly: true,
fakeUser: fakeUser.data
})
const expertMode = UIEventSource.asBoolean(
osmConnection.GetPreference("studio-expert-mode", "false", {
@ -129,7 +131,7 @@
let showIntro = editLayerState.showIntro
const layoutSchema: ConfigMeta[] = <any>layoutSchemaRaw
let editThemeState = new EditThemeState(layoutSchema, studio, { expertMode })
let editThemeState = new EditThemeState(layoutSchema, studio, osmConnection, { expertMode })
const version = meta.version