forked from MapComplete/MapComplete
Fix: fix #1817, some more improvements to the loading screen
This commit is contained in:
parent
e36e594b89
commit
6394ee8e68
2 changed files with 545 additions and 523 deletions
|
@ -17,19 +17,38 @@ import * as eli_global from "../src/assets/global-raster-layers.json"
|
||||||
import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils"
|
import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils"
|
||||||
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||||
|
import Script from "./Script"
|
||||||
|
import crypto from "crypto"
|
||||||
|
|
||||||
const sharp = require("sharp")
|
const sharp = require("sharp")
|
||||||
const template = readFileSync("theme.html", "utf8")
|
|
||||||
let codeTemplate = readFileSync("src/index_theme.ts.template", "utf8")
|
|
||||||
|
|
||||||
function enc(str: string): string {
|
class GenerateLayouts extends Script {
|
||||||
|
private readonly template = readFileSync("theme.html", "utf8")
|
||||||
|
private readonly codeTemplate = readFileSync("src/index_theme.ts.template", "utf8")
|
||||||
|
private readonly removeOtherLanguages = readFileSync("src/UI/RemoveOtherLanguages.ts", "utf8")
|
||||||
|
.split("\n")
|
||||||
|
.slice(1)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s !== "")
|
||||||
|
.join("\n")
|
||||||
|
private readonly removeOtherLanguagesHash =
|
||||||
|
"sha256-" + crypto.createHash("sha256").update(this.removeOtherLanguages).digest("base64")
|
||||||
|
private previousSrc: Set<string> = new Set<string>()
|
||||||
|
private eliUrlsCached: string[]
|
||||||
|
private date = new Date().toISOString()
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super("Generates an '<theme>.html' and 'index_<theme>.ts' for every theme")
|
||||||
|
}
|
||||||
|
|
||||||
|
enc(str: string): string {
|
||||||
return encodeURIComponent(str.toLowerCase())
|
return encodeURIComponent(str.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createIcon(iconPath: string, size: number, alreadyWritten: string[]) {
|
async createIcon(iconPath: string, size: number, alreadyWritten: string[]) {
|
||||||
let name = iconPath.split(".").slice(0, -1).join(".") // drop svg suffix
|
let name = iconPath.split(".").slice(0, -1).join(".") // drop svg suffix
|
||||||
if (name.startsWith("./")) {
|
if (name.startsWith("./")) {
|
||||||
name = name.substr(2)
|
name = name.substring(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
const newname = `assets/generated/images/${name.replace(/\//g, "_")}${size}.png`
|
const newname = `assets/generated/images/${name.replace(/\//g, "_")}${size}.png`
|
||||||
|
@ -57,9 +76,9 @@ async function createIcon(iconPath: string, size: number, alreadyWritten: string
|
||||||
}
|
}
|
||||||
|
|
||||||
return newname
|
return newname
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> {
|
async createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> {
|
||||||
if (!layout.icon.endsWith(".svg")) {
|
if (!layout.icon.endsWith(".svg")) {
|
||||||
console.warn(
|
console.warn(
|
||||||
"Not creating a social image for " +
|
"Not creating a social image for " +
|
||||||
|
@ -96,7 +115,9 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
|
||||||
return {
|
return {
|
||||||
$: {
|
$: {
|
||||||
id: "icon",
|
id: "icon",
|
||||||
transform: `translate(${cx - r},${cy - r}) scale(${(r * 2) / Number(width)}) `,
|
transform: `translate(${cx - r},${cy - r}) scale(${
|
||||||
|
(r * 2) / Number(width)
|
||||||
|
}) `,
|
||||||
},
|
},
|
||||||
g: [svg],
|
g: [svg],
|
||||||
}
|
}
|
||||||
|
@ -114,15 +135,15 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
|
||||||
writeFileSync(path, xml)
|
writeFileSync(path, xml)
|
||||||
console.log("Created social image at ", path)
|
console.log("Created social image at ", path)
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createManifest(
|
async createManifest(
|
||||||
layout: LayoutConfig,
|
layout: LayoutConfig,
|
||||||
alreadyWritten: string[]
|
alreadyWritten: string[]
|
||||||
): Promise<{
|
): Promise<{
|
||||||
manifest: any
|
manifest: any
|
||||||
whiteIcons: string[]
|
whiteIcons: string[]
|
||||||
}> {
|
}> {
|
||||||
Translation.forcedLanguage = "en"
|
Translation.forcedLanguage = "en"
|
||||||
const icons = []
|
const icons = []
|
||||||
|
|
||||||
|
@ -153,8 +174,8 @@ async function createManifest(
|
||||||
|
|
||||||
const sizes = [72, 96, 120, 128, 144, 152, 180, 192, 384, 512]
|
const sizes = [72, 96, 120, 128, 144, 152, 180, 192, 384, 512]
|
||||||
for (const size of sizes) {
|
for (const size of sizes) {
|
||||||
const name = await createIcon(path, size, alreadyWritten)
|
const name = await this.createIcon(path, size, alreadyWritten)
|
||||||
const whiteIcon = await createIcon(whiteBackgroundPath, size, alreadyWritten)
|
const whiteIcon = await this.createIcon(whiteBackgroundPath, size, alreadyWritten)
|
||||||
whiteIcons.push(whiteIcon)
|
whiteIcons.push(whiteIcon)
|
||||||
icons.push({
|
icons.push({
|
||||||
src: name,
|
src: name,
|
||||||
|
@ -196,9 +217,9 @@ async function createManifest(
|
||||||
manifest,
|
manifest,
|
||||||
whiteIcons,
|
whiteIcons,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function asLangSpan(t: Translation, tag = "span"): string {
|
asLangSpan(t: Translation, tag = "span"): string {
|
||||||
const values: string[] = []
|
const values: string[] = []
|
||||||
for (const lang in t.translations) {
|
for (const lang in t.translations) {
|
||||||
if (lang === "_context") {
|
if (lang === "_context") {
|
||||||
|
@ -207,15 +228,11 @@ function asLangSpan(t: Translation, tag = "span"): string {
|
||||||
values.push(`<${tag} lang="${lang}">${t.translations[lang]}</${tag}>`)
|
values.push(`<${tag} lang="${lang}">${t.translations[lang]}</${tag}>`)
|
||||||
}
|
}
|
||||||
return values.join("\n")
|
return values.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
let previousSrc: Set<string> = new Set<string>()
|
async eliUrls(): Promise<string[]> {
|
||||||
|
if (this.eliUrlsCached) {
|
||||||
let eliUrlsCached: string[]
|
return this.eliUrlsCached
|
||||||
|
|
||||||
async function eliUrls(): Promise<string[]> {
|
|
||||||
if (eliUrlsCached) {
|
|
||||||
return eliUrlsCached
|
|
||||||
}
|
}
|
||||||
const urls: string[] = []
|
const urls: string[] = []
|
||||||
const regex = /{switch:([^}]+)}/
|
const regex = /{switch:([^}]+)}/
|
||||||
|
@ -260,17 +277,17 @@ async function eliUrls(): Promise<string[]> {
|
||||||
urls.push(styleSpec["glyphs"])
|
urls.push(styleSpec["glyphs"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eliUrlsCached = urls
|
this.eliUrlsCached = urls
|
||||||
return Utils.NoNull(urls).sort()
|
return Utils.NoNull(urls).sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateCsp(
|
async generateCsp(
|
||||||
layout: LayoutConfig,
|
layout: LayoutConfig,
|
||||||
layoutJson: LayoutConfigJson,
|
layoutJson: LayoutConfigJson,
|
||||||
options: {
|
options: {
|
||||||
scriptSrcs: string[]
|
scriptSrcs: string[]
|
||||||
}
|
}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const apiUrls: string[] = [
|
const apiUrls: string[] = [
|
||||||
...Constants.defaultOverpassUrls,
|
...Constants.defaultOverpassUrls,
|
||||||
Constants.countryCoderEndpoint,
|
Constants.countryCoderEndpoint,
|
||||||
|
@ -279,7 +296,7 @@ async function generateCsp(
|
||||||
"https://api.openstreetmap.org",
|
"https://api.openstreetmap.org",
|
||||||
"https://pietervdvn.goatcounter.com",
|
"https://pietervdvn.goatcounter.com",
|
||||||
"https://cache.mapcomplete.org",
|
"https://cache.mapcomplete.org",
|
||||||
].concat(...(await eliUrls()))
|
].concat(...(await this.eliUrls()))
|
||||||
|
|
||||||
SpecialVisualizations.specialVisualizations.forEach((sv) => {
|
SpecialVisualizations.specialVisualizations.forEach((sv) => {
|
||||||
if (typeof sv.needsUrls === "function") {
|
if (typeof sv.needsUrls === "function") {
|
||||||
|
@ -339,17 +356,16 @@ async function generateCsp(
|
||||||
|
|
||||||
const connectSrc = Array.from(hosts).sort()
|
const connectSrc = Array.from(hosts).sort()
|
||||||
|
|
||||||
const newSrcs = connectSrc.filter((newItem) => !previousSrc.has(newItem))
|
const newSrcs = connectSrc.filter((newItem) => !this.previousSrc.has(newItem))
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"Got",
|
"Got",
|
||||||
hosts.size,
|
hosts.size,
|
||||||
"connect-src items for theme",
|
"connect-src items for theme",
|
||||||
layout.id,
|
layout.id,
|
||||||
"(extra sources: ",
|
newSrcs.length > 0 ? "(extra sources: " + newSrcs.join(" ") + ")" : ""
|
||||||
newSrcs.join(" ") + ")"
|
|
||||||
)
|
)
|
||||||
previousSrc = hosts
|
this.previousSrc = hosts
|
||||||
|
|
||||||
const csp: Record<string, string> = {
|
const csp: Record<string, string> = {
|
||||||
"default-src": "'self'",
|
"default-src": "'self'",
|
||||||
|
@ -359,9 +375,11 @@ async function generateCsp(
|
||||||
"report-to": "https://report.mapcomplete.org/csp",
|
"report-to": "https://report.mapcomplete.org/csp",
|
||||||
"worker-src": "'self' blob:", // Vite somehow loads the worker via a 'blob'
|
"worker-src": "'self' blob:", // Vite somehow loads the worker via a 'blob'
|
||||||
"style-src": "'self' 'unsafe-inline'", // unsafe-inline is needed to change the default background pin colours
|
"style-src": "'self' 'unsafe-inline'", // unsafe-inline is needed to change the default background pin colours
|
||||||
"script-src": ["'self'", "https://gc.zgo.at/count.js", ...(options?.scriptSrcs ?? [])].join(
|
"script-src": [
|
||||||
" "
|
"'self'",
|
||||||
),
|
"https://gc.zgo.at/count.js",
|
||||||
|
...(options?.scriptSrcs?.map((s) => "'" + s + "'") ?? []),
|
||||||
|
].join(" "),
|
||||||
}
|
}
|
||||||
const content = Object.keys(csp)
|
const content = Object.keys(csp)
|
||||||
.map((k) => k + " " + csp[k])
|
.map((k) => k + " " + csp[k])
|
||||||
|
@ -371,15 +389,14 @@ async function generateCsp(
|
||||||
`<meta http-equiv ="Report-To" content='{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}'>`,
|
`<meta http-equiv ="Report-To" content='{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}'>`,
|
||||||
`<meta http-equiv="Content-Security-Policy" content="${content}">`,
|
`<meta http-equiv="Content-Security-Policy" content="${content}">`,
|
||||||
].join("\n")
|
].join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createLandingPage(
|
async createLandingPage(
|
||||||
layout: LayoutConfig,
|
layout: LayoutConfig,
|
||||||
layoutJson: LayoutConfigJson,
|
layoutJson: LayoutConfigJson,
|
||||||
manifest,
|
|
||||||
whiteIcons,
|
whiteIcons,
|
||||||
alreadyWritten
|
alreadyWritten
|
||||||
) {
|
) {
|
||||||
Locale.language.setData(layout.language[0])
|
Locale.language.setData(layout.language[0])
|
||||||
const targetLanguage = layout.language[0]
|
const targetLanguage = layout.language[0]
|
||||||
const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"')
|
const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"')
|
||||||
|
@ -391,16 +408,16 @@ async function createLandingPage(
|
||||||
let ogImage = layout.socialImage
|
let ogImage = layout.socialImage
|
||||||
let twitterImage = ogImage
|
let twitterImage = ogImage
|
||||||
if (ogImage === LayoutConfig.defaultSocialImage && layout.official) {
|
if (ogImage === LayoutConfig.defaultSocialImage && layout.official) {
|
||||||
ogImage = (await createSocialImage(layout, "")) ?? layout.socialImage
|
ogImage = (await this.createSocialImage(layout, "")) ?? layout.socialImage
|
||||||
twitterImage = (await createSocialImage(layout, "Wide")) ?? layout.socialImage
|
twitterImage = (await this.createSocialImage(layout, "Wide")) ?? layout.socialImage
|
||||||
}
|
}
|
||||||
if (twitterImage.endsWith(".svg")) {
|
if (twitterImage.endsWith(".svg")) {
|
||||||
// svgs are badly supported as social image, we use a generated svg instead
|
// svgs are badly supported as social image, we use a generated svg instead
|
||||||
twitterImage = await createIcon(twitterImage, 512, alreadyWritten)
|
twitterImage = await this.createIcon(twitterImage, 512, alreadyWritten)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ogImage.endsWith(".svg")) {
|
if (ogImage.endsWith(".svg")) {
|
||||||
ogImage = await createIcon(ogImage, 512, alreadyWritten)
|
ogImage = await this.createIcon(ogImage, 512, alreadyWritten)
|
||||||
}
|
}
|
||||||
|
|
||||||
let customCss = ""
|
let customCss = ""
|
||||||
|
@ -409,7 +426,7 @@ async function createLandingPage(
|
||||||
const cssContent = readFileSync(layout.customCss)
|
const cssContent = readFileSync(layout.customCss)
|
||||||
customCss = "<style>" + cssContent + "</style>"
|
customCss = "<style>" + cssContent + "</style>"
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
customCss = `<link rel='stylesheet' href="${layout.customCss}"/>`
|
customCss = `<link rel="stylesheet" href="${layout.customCss}"/>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +459,7 @@ async function createLandingPage(
|
||||||
|
|
||||||
let themeSpecific = [
|
let themeSpecific = [
|
||||||
`<title>${ogTitle}</title>`,
|
`<title>${ogTitle}</title>`,
|
||||||
`<link rel="manifest" href="${enc(layout.id)}.webmanifest">`,
|
`<link rel="manifest" href="${this.enc(layout.id)}.webmanifest">`,
|
||||||
og,
|
og,
|
||||||
customCss,
|
customCss,
|
||||||
`<link rel="icon" href="${icon}" sizes="any" type="image/svg+xml">`,
|
`<link rel="icon" href="${icon}" sizes="any" type="image/svg+xml">`,
|
||||||
|
@ -450,9 +467,10 @@ async function createLandingPage(
|
||||||
].join("\n")
|
].join("\n")
|
||||||
|
|
||||||
const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title })
|
const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title })
|
||||||
const templateLines = template.split("\n")
|
// const templateLines: string[] = this.template.split("\n").slice(1) // Slice to remove the 'export {}'-line
|
||||||
let output = template
|
|
||||||
.replace("Loading MapComplete, hang on...", asLangSpan(loadingText, "h1"))
|
return this.template
|
||||||
|
.replace("Loading MapComplete, hang on...", this.asLangSpan(loadingText, "h1"))
|
||||||
.replace(
|
.replace(
|
||||||
"Made with OpenStreetMap",
|
"Made with OpenStreetMap",
|
||||||
Translations.t.general.poweredByOsm.textFor(targetLanguage)
|
Translations.t.general.poweredByOsm.textFor(targetLanguage)
|
||||||
|
@ -460,29 +478,31 @@ async function createLandingPage(
|
||||||
.replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific)
|
.replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific)
|
||||||
.replace(
|
.replace(
|
||||||
/<!-- CSP -->/,
|
/<!-- CSP -->/,
|
||||||
await generateCsp(layout, layoutJson, {
|
await this.generateCsp(layout, layoutJson, {
|
||||||
scriptSrcs: [],
|
scriptSrcs: [this.removeOtherLanguagesHash],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.replace(
|
.replace(
|
||||||
/<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s,
|
/<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s,
|
||||||
asLangSpan(layout.shortDescription)
|
this.asLangSpan(layout.shortDescription)
|
||||||
)
|
)
|
||||||
.replace(
|
.replace(
|
||||||
/<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s,
|
/<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s,
|
||||||
"<img class='p-8 h-32 w-32 self-start' src='" + icon + "' />"
|
"<img class='p-4 h-32 w-32 self-start' src='" + icon + "' />"
|
||||||
)
|
)
|
||||||
|
|
||||||
.replace(
|
.replace(
|
||||||
/.*\/src\/index\.ts.*/,
|
/.*\/src\/index\.ts.*/,
|
||||||
`<script type="module" src="./index_${layout.id}.ts"></script>`
|
`<script type="module" src="./index_${layout.id}.ts"></script>`
|
||||||
)
|
)
|
||||||
.replace("Version", Constants.vNumber)
|
|
||||||
|
|
||||||
return output
|
.replace(
|
||||||
}
|
/\n.*RemoveOtherLanguages.*\n/i,
|
||||||
|
"\n<script>" + this.removeOtherLanguages + "</script>\n"
|
||||||
|
)
|
||||||
|
.replace("Version", `${Constants.vNumber} <div class='text-xs'>${this.date}</div>`)
|
||||||
|
}
|
||||||
|
|
||||||
async function createIndexFor(theme: LayoutConfig) {
|
async createIndexFor(theme: LayoutConfig) {
|
||||||
const filename = "index_" + theme.id + ".ts"
|
const filename = "index_" + theme.id + ".ts"
|
||||||
|
|
||||||
const imports = [
|
const imports = [
|
||||||
|
@ -490,7 +510,9 @@ async function createIndexFor(theme: LayoutConfig) {
|
||||||
`import { ThemeMetaTagging } from "./src/assets/generated/metatagging/${theme.id}"`,
|
`import { ThemeMetaTagging } from "./src/assets/generated/metatagging/${theme.id}"`,
|
||||||
]
|
]
|
||||||
for (const layerName of Constants.added_by_default) {
|
for (const layerName of Constants.added_by_default) {
|
||||||
imports.push(`import ${layerName} from "./src/assets/generated/layers/${layerName}.json"`)
|
imports.push(
|
||||||
|
`import ${layerName} from "./src/assets/generated/layers/${layerName}.json"`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
writeFileSync(filename, imports.join("\n") + "\n")
|
writeFileSync(filename, imports.join("\n") + "\n")
|
||||||
|
|
||||||
|
@ -500,22 +522,25 @@ async function createIndexFor(theme: LayoutConfig) {
|
||||||
addLayers.push(` layout.layers.push(<any> ${layerName})`)
|
addLayers.push(` layout.layers.push(<any> ${layerName})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
codeTemplate = codeTemplate.replace(" // LAYOUT.ADD_LAYERS", addLayers.join("\n"))
|
let codeTemplate = this.codeTemplate.replace(
|
||||||
|
" // LAYOUT.ADD_LAYERS",
|
||||||
|
addLayers.join("\n")
|
||||||
|
)
|
||||||
|
|
||||||
appendFileSync(filename, codeTemplate)
|
appendFileSync(filename, codeTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDir(path) {
|
createDir(path) {
|
||||||
if (!existsSync(path)) {
|
if (!existsSync(path)) {
|
||||||
mkdirSync(path)
|
mkdirSync(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async main(): Promise<void> {
|
||||||
const alreadyWritten = []
|
const alreadyWritten = []
|
||||||
createDir("./public/assets/")
|
this.createDir("./public/assets/")
|
||||||
createDir("./public/assets/generated")
|
this.createDir("./public/assets/generated")
|
||||||
createDir("./public/assets/generated/images")
|
this.createDir("./public/assets/generated/images")
|
||||||
|
|
||||||
const blacklist = [
|
const blacklist = [
|
||||||
"",
|
"",
|
||||||
|
@ -554,25 +579,24 @@ async function main(): Promise<void> {
|
||||||
console.log("Could not write manifest for ", layoutName, " because ", err)
|
console.log("Could not write manifest for ", layoutName, " because ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { manifest, whiteIcons } = await createManifest(layout, alreadyWritten)
|
const { manifest, whiteIcons } = await this.createManifest(layout, alreadyWritten)
|
||||||
const manif = JSON.stringify(manifest, undefined, 2)
|
const manif = JSON.stringify(manifest, undefined, 2)
|
||||||
const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest"
|
const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest"
|
||||||
writeFile("public/" + manifestLocation, manif, err)
|
writeFile("public/" + manifestLocation, manif, err)
|
||||||
|
|
||||||
// Create a landing page for the given theme
|
// Create a landing page for the given theme
|
||||||
const landing = await createLandingPage(
|
const landing = await this.createLandingPage(
|
||||||
layout,
|
layout,
|
||||||
layoutConfigJson,
|
layoutConfigJson,
|
||||||
manifest,
|
|
||||||
whiteIcons,
|
whiteIcons,
|
||||||
alreadyWritten
|
alreadyWritten
|
||||||
)
|
)
|
||||||
|
|
||||||
writeFile(enc(layout.id) + ".html", landing, err)
|
writeFile(this.enc(layout.id) + ".html", landing, err)
|
||||||
await createIndexFor(layout)
|
await this.createIndexFor(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { manifest } = await createManifest(
|
const { manifest } = await this.createManifest(
|
||||||
new LayoutConfig({
|
new LayoutConfig({
|
||||||
icon: "./assets/svg/mapcomplete_logo.svg",
|
icon: "./assets/svg/mapcomplete_logo.svg",
|
||||||
id: "index",
|
id: "index",
|
||||||
|
@ -589,9 +613,7 @@ async function main(): Promise<void> {
|
||||||
|
|
||||||
const manif = JSON.stringify(manifest, undefined, 2)
|
const manif = JSON.stringify(manifest, undefined, 2)
|
||||||
writeFileSync("public/index.webmanifest", manif)
|
writeFileSync("public/index.webmanifest", manif)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptUtils.fixUtils()
|
new GenerateLayouts().run()
|
||||||
main().then(() => {
|
|
||||||
console.log("All done!")
|
|
||||||
})
|
|
||||||
|
|
|
@ -59,12 +59,12 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between items-start w-full">
|
<div class="flex justify-between items-end w-full">
|
||||||
|
|
||||||
<!-- IMAGE-START -->
|
<!-- IMAGE-START -->
|
||||||
<img aria-hidden="true" class="p-8 h-32 w-32 self-start" src="./assets/svg/add.svg">
|
<img aria-hidden="true" class="p-4 h-32 w-32 self-start" src="./assets/svg/add.svg">
|
||||||
<!-- IMAGE-END -->
|
<!-- IMAGE-END -->
|
||||||
<div class="h-min subtle">
|
<div class="h-min subtle flex flex-col items-end">
|
||||||
Version
|
Version
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue