diff --git a/Docs/Hotkeys.md b/Docs/Hotkeys.md new file mode 100644 index 000000000..9a64227fd --- /dev/null +++ b/Docs/Hotkeys.md @@ -0,0 +1,30 @@ + + + Hotkeys +========= + + + +## Table of contents + +1. [Hotkeys](#hotkeys) + + + +MapComplete supports the following keys: + + + +Key combination | Action +----------------- | -------- +B | Opens the Background, layers and filters panel +Escape | Close the sidebar +L | Pan the map to the current location or zoom the map to the current location. Requests geopermission +M | Switch to a background layer of category map +O | Switch to a background layer of category osmbasedmap +P | Switch to a background layer of category photo +ctrl+F | Select the search bar to search locations +shift+O | Switch to default Mapnik-OpenStreetMap background + + +This document is autogenerated from \ No newline at end of file diff --git a/Logic/Actors/StrayClickHandler.ts b/Logic/Actors/StrayClickHandler.ts index 0ba1a850a..003917f02 100644 --- a/Logic/Actors/StrayClickHandler.ts +++ b/Logic/Actors/StrayClickHandler.ts @@ -1,8 +1,6 @@ -import * as L from "leaflet" import { UIEventSource } from "../UIEventSource" -import ScrollableFullScreen from "../../UI/Base/ScrollableFullScreen" import FilteredLayer from "../../Models/FilteredLayer" -import Constants from "../../Models/Constants" +import ScrollableFullScreen from "../../UI/Base/ScrollableFullScreen" import BaseUIElement from "../../UI/BaseUIElement" /** @@ -10,70 +8,16 @@ import BaseUIElement from "../../UI/BaseUIElement" * Shows the given uiToShow-element in the messagebox */ export default class StrayClickHandler { - private _lastMarker - - constructor( + public static construct = ( state: { LastClickLocation: UIEventSource<{ lat: number; lon: number }> selectedElement: UIEventSource filteredLayers: UIEventSource - leafletMap: UIEventSource + leafletMap: UIEventSource }, uiToShow: ScrollableFullScreen, iconToShow: BaseUIElement - ) { - const self = this - const leafletMap = state.leafletMap - state.filteredLayers.data.forEach((filteredLayer) => { - filteredLayer.isDisplayed.addCallback((isEnabled) => { - if (isEnabled && self._lastMarker && leafletMap.data !== undefined) { - // When a layer is activated, we remove the 'last click location' in order to force the user to reclick - // This reclick might be at a location where a feature now appeared... - state.leafletMap.data.removeLayer(self._lastMarker) - } - }) - }) - - state.LastClickLocation.addCallback(function (lastClick) { - if (self._lastMarker !== undefined) { - state.leafletMap.data?.removeLayer(self._lastMarker) - } - - if (lastClick === undefined) { - return - } - - state.selectedElement.setData(undefined) - const clickCoor: [number, number] = [lastClick.lat, lastClick.lon] - self._lastMarker = L.marker(clickCoor, { - icon: L.divIcon({ - html: iconToShow.ConstructElement(), - iconSize: [50, 50], - iconAnchor: [25, 50], - popupAnchor: [0, -45], - }), - }) - - self._lastMarker.addTo(leafletMap.data) - - self._lastMarker.on("click", () => { - if (leafletMap.data.getZoom() < Constants.userJourney.minZoomLevelToAddNewPoints) { - leafletMap.data.flyTo( - clickCoor, - Constants.userJourney.minZoomLevelToAddNewPoints - ) - return - } - - uiToShow.Activate() - }) - }) - - state.selectedElement.addCallback(() => { - if (self._lastMarker !== undefined) { - leafletMap.data.removeLayer(self._lastMarker) - this._lastMarker = undefined - } - }) + ) => { + return undefined } } diff --git a/Models/Constants.ts b/Models/Constants.ts index e90a9bc21..d718cdf50 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -1,7 +1,7 @@ import { Utils } from "../Utils" export default class Constants { - public static vNumber = "0.25.1" + public static vNumber = "0.25.2" public static ImgurApiKey = "7070e7167f0a25a" public static readonly mapillary_client_token_v4 = diff --git a/UI/Base/Hotkeys.ts b/UI/Base/Hotkeys.ts new file mode 100644 index 000000000..019a57265 --- /dev/null +++ b/UI/Base/Hotkeys.ts @@ -0,0 +1,110 @@ +import { Utils } from "../../Utils" +import Combine from "./Combine" +import BaseUIElement from "../BaseUIElement" +import Title from "./Title" +import Table from "./Table" +import { UIEventSource } from "../../Logic/UIEventSource" +import { VariableUiElement } from "./VariableUIElement" + +export default class Hotkeys { + private static readonly _docs: UIEventSource< + { + key: { ctrl?: string; shift?: string; alt?: string; nomod?: string; onUp?: boolean } + documentation: string + }[] + > = new UIEventSource< + { + key: { ctrl?: string; shift?: string; alt?: string; nomod?: string; onUp?: boolean } + documentation: string + }[] + >([]) + public static RegisterHotkey( + key: ( + | { + ctrl: string + } + | { + shift: string + } + | { + alt: string + } + | { + nomod: string + } + ) & { + onUp?: boolean + }, + documentation: string, + action: () => void + ) { + const type = key["onUp"] ? "keyup" : "keypress" + let keycode: string = key["ctrl"] ?? key["shift"] ?? key["alt"] ?? key["nomod"] + if (keycode.length == 1) { + keycode = keycode.toLowerCase() + if (key["shift"] !== undefined) { + keycode = keycode.toUpperCase() + } + } + + this._docs.data.push({ key, documentation }) + this._docs.ping() + if (Utils.runningFromConsole) { + return + } + if (key["ctrl"] !== undefined) { + document.addEventListener("keydown", function (event) { + if (event.ctrlKey && event.key === keycode) { + action() + event.preventDefault() + } + }) + } else if (key["shift"] !== undefined) { + document.addEventListener(type, function (event) { + if (event.shiftKey && event.key === keycode) { + action() + event.preventDefault() + } + }) + } else if (key["alt"] !== undefined) { + document.addEventListener(type, function (event) { + if (event.altKey && event.key === keycode) { + action() + event.preventDefault() + } + }) + } else if (key["nomod"] !== undefined) { + document.addEventListener(type, function (event) { + if (event.key === keycode) { + action() + event.preventDefault() + } + }) + } + } + + static generateDocumentation(): BaseUIElement { + return new Combine([ + new Title("Hotkeys", 1), + "MapComplete supports the following keys:", + new Table( + ["Key combination", "Action"], + Hotkeys._docs.data + .map(({ key, documentation }) => { + const modifiers = Object.keys(key).filter( + (k) => k !== "nomod" && k !== "onUp" + ) + const keycode: string = + key["ctrl"] ?? key["shift"] ?? key["alt"] ?? key["nomod"] + modifiers.push(keycode) + return [modifiers.join("+"), documentation] + }) + .sort() + ), + ]) + } + + static generateDocumentationDynamic(): BaseUIElement { + return new VariableUiElement(Hotkeys._docs.map((_) => Hotkeys.generateDocumentation())) + } +} diff --git a/UI/Base/MinimapImplementation.ts b/UI/Base/MinimapImplementation.ts index 32e5da187..3b6364602 100644 --- a/UI/Base/MinimapImplementation.ts +++ b/UI/Base/MinimapImplementation.ts @@ -14,7 +14,83 @@ import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch" import AvailableBaseLayersImplementation from "../../Logic/Actors/AvailableBaseLayersImplementation" import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" import ShowDataLayerImplementation from "../ShowDataLayer/ShowDataLayerImplementation" +import FilteredLayer from "../../Models/FilteredLayer" +import ScrollableFullScreen from "./ScrollableFullScreen" +import Constants from "../../Models/Constants" +import StrayClickHandler from "../../Logic/Actors/StrayClickHandler" +/** + * The stray-click-hanlders adds a marker to the map if no feature was clicked. + * Shows the given uiToShow-element in the messagebox + */ +export class StrayClickHandlerImplementation { + private _lastMarker + + constructor( + state: { + LastClickLocation: UIEventSource<{ lat: number; lon: number }> + selectedElement: UIEventSource + filteredLayers: UIEventSource + leafletMap: UIEventSource + }, + uiToShow: ScrollableFullScreen, + iconToShow: BaseUIElement + ) { + const self = this + const leafletMap = state.leafletMap + state.filteredLayers.data.forEach((filteredLayer) => { + filteredLayer.isDisplayed.addCallback((isEnabled) => { + if (isEnabled && self._lastMarker && leafletMap.data !== undefined) { + // When a layer is activated, we remove the 'last click location' in order to force the user to reclick + // This reclick might be at a location where a feature now appeared... + state.leafletMap.data.removeLayer(self._lastMarker) + } + }) + }) + + state.LastClickLocation.addCallback(function (lastClick) { + if (self._lastMarker !== undefined) { + state.leafletMap.data?.removeLayer(self._lastMarker) + } + + if (lastClick === undefined) { + return + } + + state.selectedElement.setData(undefined) + const clickCoor: [number, number] = [lastClick.lat, lastClick.lon] + self._lastMarker = L.marker(clickCoor, { + icon: L.divIcon({ + html: iconToShow.ConstructElement(), + iconSize: [50, 50], + iconAnchor: [25, 50], + popupAnchor: [0, -45], + }), + }) + + self._lastMarker.addTo(leafletMap.data) + + self._lastMarker.on("click", () => { + if (leafletMap.data.getZoom() < Constants.userJourney.minZoomLevelToAddNewPoints) { + leafletMap.data.flyTo( + clickCoor, + Constants.userJourney.minZoomLevelToAddNewPoints + ) + return + } + + uiToShow.Activate() + }) + }) + + state.selectedElement.addCallback(() => { + if (self._lastMarker !== undefined) { + leafletMap.data.removeLayer(self._lastMarker) + this._lastMarker = undefined + } + }) + } +} export default class MinimapImplementation extends BaseUIElement implements MinimapObj { private static _nextId = 0 public readonly leafletMap: UIEventSource @@ -53,6 +129,18 @@ export default class MinimapImplementation extends BaseUIElement implements Mini AvailableBaseLayers.implement(new AvailableBaseLayersImplementation()) Minimap.createMiniMap = (options) => new MinimapImplementation(options) ShowDataLayer.actualContstructor = (options) => new ShowDataLayerImplementation(options) + StrayClickHandler.construct = ( + state: { + LastClickLocation: UIEventSource<{ lat: number; lon: number }> + selectedElement: UIEventSource + filteredLayers: UIEventSource + leafletMap: UIEventSource + }, + uiToShow: ScrollableFullScreen, + iconToShow: BaseUIElement + ) => { + return new StrayClickHandlerImplementation(state, uiToShow, iconToShow) + } } public installBounds(factor: number | BBox, showRange?: boolean) { diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 0f4d424c9..b0e8602ce 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -5,6 +5,7 @@ import { UIEventSource } from "../../Logic/UIEventSource" import Hash from "../../Logic/Web/Hash" import BaseUIElement from "../BaseUIElement" import Title from "./Title" +import Hotkeys from "./Hotkeys" /** * @@ -82,12 +83,11 @@ export default class ScrollableFullScreen { } private static initEmpty(): FixedUiElement { - document.addEventListener("keyup", function (event) { - if (event.code === "Escape") { - ScrollableFullScreen.collapse() - event.preventDefault() - } - }) + Hotkeys.RegisterHotkey( + { nomod: "Escape", onUp: true }, + "Close the sidebar", + ScrollableFullScreen.collapse + ) return new FixedUiElement("") } @@ -117,7 +117,7 @@ export default class ScrollableFullScreen { this._fullscreencomponent.AttachTo("fullscreen") const fs = document.getElementById("fullscreen") ScrollableFullScreen._currentlyOpen = this - fs.classList.remove("hidden") + fs?.classList?.remove("hidden") } private BuildComponent(title: BaseUIElement, content: BaseUIElement): BaseUIElement { diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index b7f92fc90..cc5e1ad90 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -24,6 +24,10 @@ export default abstract class BaseUIElement { AttachTo(divId: string) { let element = document.getElementById(divId) if (element === null) { + if (Utils.runningFromConsole) { + this.ConstructElement() + return + } throw "SEVERE: could not attach UIElement to " + divId } diff --git a/UI/BigComponents/BackgroundMapSwitch.ts b/UI/BigComponents/BackgroundMapSwitch.ts index 186d2e44f..c210df525 100644 --- a/UI/BigComponents/BackgroundMapSwitch.ts +++ b/UI/BigComponents/BackgroundMapSwitch.ts @@ -7,6 +7,7 @@ import BaseLayer from "../../Models/BaseLayer" import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" import BaseUIElement from "../BaseUIElement" import { GeoOperations } from "../../Logic/GeoOperations" +import Hotkeys from "../Base/Hotkeys" class SingleLayerSelectionButton extends Toggle { public readonly activate: () => void @@ -48,13 +49,13 @@ class SingleLayerSelectionButton extends Toggle { let toggle: BaseUIElement = new Toggle( selected, unselected, - options.currentBackground.map((bg) => bg.category === options.preferredType) + options.currentBackground.map((bg) => bg?.category === options.preferredType) ) super( toggle, undefined, - available.map((av) => av.category === options.preferredType) + available.map((av) => av?.category === options.preferredType) ) /** @@ -174,6 +175,7 @@ export default class BackgroundMapSwitch extends Combine { options?: { preferredCategory?: string allowedCategories?: ("osmbasedmap" | "photo" | "map")[] + enableHotkeys?: boolean } ) { const allowedCategories = options?.allowedCategories ?? ["osmbasedmap", "photo", "map"] @@ -183,7 +185,7 @@ export default class BackgroundMapSwitch extends Combine { let activatePrevious: () => void = undefined for (const category of allowedCategories) { let preferredLayer = undefined - if (previousLayer.category === category) { + if (previousLayer?.category === category) { preferredLayer = previousLayer } @@ -198,6 +200,16 @@ export default class BackgroundMapSwitch extends Combine { if (category === options?.preferredCategory) { button.activate() } + + if (options?.enableHotkeys) { + Hotkeys.RegisterHotkey( + { nomod: category.charAt(0).toUpperCase() }, + "Switch to a background layer of category " + category, + () => { + button.activate() + } + ) + } buttons.push(button) } diff --git a/UI/BigComponents/DownloadPanel.ts b/UI/BigComponents/DownloadPanel.ts index 82baf3b1d..bf69624b2 100644 --- a/UI/BigComponents/DownloadPanel.ts +++ b/UI/BigComponents/DownloadPanel.ts @@ -26,7 +26,7 @@ export class DownloadPanel extends Toggle { currentBounds: UIEventSource }) { const t = Translations.t.general.download - const name = State.state.layoutToUse.id + const name = state.layoutToUse.id const includeMetaToggle = new CheckBoxes([t.includeMetaData]) const metaisIncluded = includeMetaToggle.GetValue().map((selected) => selected.length > 0) diff --git a/UI/BigComponents/FullWelcomePaneWithTabs.ts b/UI/BigComponents/FullWelcomePaneWithTabs.ts index 87ea2a053..f21e01c29 100644 --- a/UI/BigComponents/FullWelcomePaneWithTabs.ts +++ b/UI/BigComponents/FullWelcomePaneWithTabs.ts @@ -20,6 +20,7 @@ import FilteredLayer from "../../Models/FilteredLayer" import CopyrightPanel from "./CopyrightPanel" import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline" import PrivacyPolicy from "./PrivacyPolicy" +import Hotkeys from "../Base/Hotkeys" export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { public static MoreThemesTabIndex = 1 @@ -126,6 +127,7 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { osmcha_link: Utils.OsmChaLinkFor(7), }), "
Version " + Constants.vNumber, + Hotkeys.generateDocumentationDynamic(), ]).SetClass("link-underline"), }) diff --git a/UI/BigComponents/GeolocationControl.ts b/UI/BigComponents/GeolocationControl.ts index b9feeab5e..265ceeb66 100644 --- a/UI/BigComponents/GeolocationControl.ts +++ b/UI/BigComponents/GeolocationControl.ts @@ -4,6 +4,7 @@ import { UIEventSource } from "../../Logic/UIEventSource" import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler" import { BBox } from "../../Logic/BBox" import Loc from "../../Models/Loc" +import Hotkeys from "../Base/Hotkeys" /** * Displays an icon depending on the state of the geolocation. @@ -70,7 +71,7 @@ export class GeolocationControl extends VariableUiElement { ) ) - this.onClick(async () => { + async function handleClick() { if (geolocationState.permission.data !== "granted") { await geolocationState.requestPermission() } @@ -108,7 +109,14 @@ export class GeolocationControl extends VariableUiElement { } lastClick.setData(new Date()) - }) + } + + this.onClick(handleClick) + Hotkeys.RegisterHotkey( + { nomod: "L" }, + "Pan the map to the current location or zoom the map to the current location. Requests geopermission", + handleClick + ) lastClick.addCallbackAndRunD((_) => { window.setTimeout(() => { diff --git a/UI/BigComponents/LeftControls.ts b/UI/BigComponents/LeftControls.ts index 0de612417..e9af1bb9c 100644 --- a/UI/BigComponents/LeftControls.ts +++ b/UI/BigComponents/LeftControls.ts @@ -13,7 +13,7 @@ import { VariableUiElement } from "../Base/VariableUIElement" import FeatureInfoBox from "../Popup/FeatureInfoBox" import CopyrightPanel from "./CopyrightPanel" import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" -import { FixedUiElement } from "../Base/FixedUiElement" +import Hotkeys from "../Base/Hotkeys" export default class LeftControls extends Combine { constructor( @@ -73,7 +73,7 @@ export default class LeftControls extends Combine { guiState.downloadControlIsOpened.setData(true) ) - const downloadButtonn = new Toggle( + const downloadButton = new Toggle( toggledDownload, undefined, state.featureSwitchEnableExport.map( @@ -94,11 +94,20 @@ export default class LeftControls extends Combine { const toggledFilter = new MapControlButton(Svg.layers_svg()).onClick(() => guiState.filterViewIsOpened.setData(true) ) + state.featureSwitchFilter.addCallbackAndRun((f) => { + Hotkeys.RegisterHotkey( + { nomod: "B" }, + "Opens the Background, layers and filters panel", + () => { + guiState.filterViewIsOpened.setData(!guiState.filterViewIsOpened.data) + } + ) + }) const filterButton = new Toggle(toggledFilter, undefined, state.featureSwitchFilter) const mapSwitch = new Toggle( - new BackgroundMapSwitch(state, state.backgroundLayer), + new BackgroundMapSwitch(state, state.backgroundLayer, { enableHotkeys: true }), undefined, state.featureSwitchBackgroundSelection ) @@ -120,7 +129,7 @@ export default class LeftControls extends Combine { state.featureSwitchWelcomeMessage ) - super([currentViewAction, filterButton, downloadButtonn, copyright, mapSwitch]) + super([currentViewAction, filterButton, downloadButton, copyright, mapSwitch]) this.SetClass("flex flex-col") } diff --git a/UI/BigComponents/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts index 2f84ed88b..5ff2354a6 100644 --- a/UI/BigComponents/ShareScreen.ts +++ b/UI/BigComponents/ShareScreen.ts @@ -80,7 +80,7 @@ export default class ShareScreen extends Combine { includeCurrentBackground.GetValue().map( (includeBG) => { if (includeBG) { - return "background=" + currentLayer.data.id + return "background=" + currentLayer.data?.id } else { return null } @@ -168,7 +168,7 @@ export default class ShareScreen extends Combine { return ` <iframe src="${url}" allow="geolocation" width="100%" height="100%" style="min-width: 250px; min-height: 250px" title="${ layout.title?.txt ?? "MapComplete" - } with MapComplete"></iframe> + } with MapComplete"></iframe> ` }) ) diff --git a/UI/DefaultGUI.ts b/UI/DefaultGUI.ts index 28c26eeec..260e519c0 100644 --- a/UI/DefaultGUI.ts +++ b/UI/DefaultGUI.ts @@ -31,6 +31,8 @@ import { LoginToggle } from "./Popup/LoginButton" import { FixedUiElement } from "./Base/FixedUiElement" import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" import { GeoLocationState } from "../Logic/State/GeoLocationState" +import Hotkeys from "./Base/Hotkeys" +import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers" /** * The default MapComplete GUI initializer @@ -61,6 +63,14 @@ export default class DefaultGUI { Utils.LoadCustomCss(this.state.layoutToUse.customCss) } + Hotkeys.RegisterHotkey( + { shift: "O" }, + "Switch to default Mapnik-OpenStreetMap background", + () => { + this.state.backgroundLayer.setData(AvailableBaseLayers.osmCarto) + } + ) + Utils.downloadJson("./service-worker-version") .then((data) => console.log("Service worker", data)) .catch((_) => console.log("Service worker not active")) @@ -128,7 +138,7 @@ export default class DefaultGUI { .SetStyle("left: calc( 50% - 15px )") // This is a bit hacky, yes I know! } - new StrayClickHandler( + StrayClickHandler.construct( state, addNewPoint, hasPresets ? new AddNewMarker(state.filteredLayers) : noteMarker @@ -151,6 +161,9 @@ export default class DefaultGUI { } private SetupMap() { + if (Utils.runningFromConsole) { + return + } const state = this.state const guiState = this.guiState @@ -242,12 +255,14 @@ export default class DefaultGUI { const search = new SearchAndGo(state).SetClass( "shadow rounded-full h-min w-full overflow-hidden sm:max-w-sm pointer-events-auto" ) - document.addEventListener("keydown", function (event) { - if (event.ctrlKey && event.code === "KeyF") { + Hotkeys.RegisterHotkey( + { ctrl: "F" }, + "Select the search bar to search locations", + () => { search.focus() - event.preventDefault() } - }) + ) + return search }), ]).AttachTo("top-right") @@ -256,7 +271,7 @@ export default class DefaultGUI { new RightControls(state, this.geolocationHandler).AttachTo("bottom-right") new CenterMessageBox(state).AttachTo("centermessage") - document.getElementById("centermessage").classList.add("pointer-events-none") + document?.getElementById("centermessage")?.classList?.add("pointer-events-none") // We have to ping the welcomeMessageIsOpened and other isOpened-stuff to activate the FullScreenMessage if needed for (const state of guiState.allFullScreenStates) { diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 125021780..72ab83269 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -17,7 +17,10 @@ export default class ShowDataLayer { */ constructor(options: ShowDataLayerOptions & { layerToShow: LayerConfig }) { if (ShowDataLayer.actualContstructor === undefined) { - throw "Show data layer is called, but it isn't initialized yet. Call ` ShowDataLayer.actualContstructor = (options => new ShowDataLayerImplementation(options)) ` somewhere, e.g. in your init" + console.error( + "Show data layer is called, but it isn't initialized yet. Call ` ShowDataLayer.actualContstructor = (options => new ShowDataLayerImplementation(options)) ` somewhere, e.g. in your init" + ) + return } ShowDataLayer.actualContstructor(options) } diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index af789f3b5..18075bc16 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -16,7 +16,13 @@ import SharedTagRenderings from "../Customizations/SharedTagRenderings" import { writeFile } from "fs" import Translations from "../UI/i18n/Translations" import * as themeOverview from "../assets/generated/theme_overview.json" - +import DefaultGUI from "../UI/DefaultGUI" +import FeaturePipelineState from "../Logic/State/FeaturePipelineState" +import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" +import * as bookcases from "../assets/generated/themes/bookcases.json" +import { DefaultGuiState } from "../UI/DefaultGuiState" +import * as fakedom from "fake-dom" +import Hotkeys from "../UI/Base/Hotkeys" function WriteFile( filename, html: BaseUIElement, @@ -217,5 +223,13 @@ WriteFile("./Docs/URL_Parameters.md", QueryParameterDocumentation.GenerateQueryP "Logic/Web/QueryParameters.ts", "UI/QueryParameterDocumentation.ts", ]) +if (fakedom === undefined || window === undefined) { + throw "FakeDom not initialized" +} +new DefaultGUI( + new FeaturePipelineState(new LayoutConfig(bookcases)), + new DefaultGuiState() +).setup() +WriteFile("./Docs/Hotkeys.md", Hotkeys.generateDocumentation(), []) console.log("Generated docs")