Themes: make 'description' and 'title' optional if there is a single theme and it can be reused from this single theme

This commit is contained in:
Pieter Vander Vennet 2025-07-05 04:38:56 +02:00
parent 54e1f5d44c
commit c7b905d1fb
4 changed files with 37 additions and 7 deletions

View file

@ -617,6 +617,33 @@ class OrderTheme extends DesugaringStep<ThemeConfigJson>{
return Utils.reorder(json, OrderTheme.themeAttributesOrder)
}
}
class DeriveDescription extends DesugaringStep<ThemeConfigJson> {
private readonly _key: string
private readonly _sourceKey: "name"
constructor(key: "icon" | "description" | "title", sourceKey?: "name") {
super("DeriveDescription", "If a single layer and no description is given, steal the description from the layer")
this._key = key
this._sourceKey = sourceKey
}
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (json[this._key]) {
return json
}
const msg = "This theme has no "+this._key+". If there is only a single layer, we can steal it from this layer though. Current layers are "+json.layers.map(l => l["id"]).join("; ")
if (json.layers.length !== 1) {
context.err(msg)
}
const l = <LayerConfigJson> json.layers[0] // Already expanded
context.info(`Added '${this._key}' to theme ${json.id} based on single layer`)
return {
...json,
[this._key]: l[this._sourceKey ?? this._key],
}
}
}
export class PrepareTheme extends Fuse<ThemeConfigJson> {
private state: DesugaringContext
@ -632,6 +659,9 @@ export class PrepareTheme extends Fuse<ThemeConfigJson> {
new PreparePersonalTheme(state),
new WarnForUnsubstitutedLayersInTheme(),
new On("layers", new Concat(new SubstituteLayer(state))),
new DeriveDescription( "description"),
new DeriveDescription( "icon"),
new DeriveDescription( "title", "name"),
new SetDefault("socialImage", "assets/SocialImage.png", true),
// We expand all tagrenderings first...

View file

@ -35,6 +35,9 @@ export class ValidateTheme extends DesugaringStep<ThemeConfigJson> {
}
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
if (!json.title) {
context.enter("title").err(`The theme ${json.id} does not have a title defined.`)
}
const theme = new ThemeConfig(json, this._isBuiltin)
{
// Legacy format checks
@ -55,9 +58,7 @@ export class ValidateTheme extends DesugaringStep<ThemeConfigJson> {
}
}
}
if (!json.title) {
context.enter("title").err(`The theme ${json.id} does not have a title defined.`)
}
if (!json.icon) {
context.enter("icon").err("A theme should have an icon")
}

View file

@ -56,10 +56,11 @@ export interface ThemeConfigJson {
/**
* question: How would you describe this theme?
* The description, as shown in the welcome message and the more-screen
* ifunset: reuse the description of the only layer
* group: basic
*
*/
description: Translatable
description?: Translatable
/**
* A short description, showed as social description and in the 'more theme'-buttons.

View file

@ -132,9 +132,7 @@ export default class ThemeConfig implements ThemeInformation {
}
const context = this.id
this.credits = Array.isArray(json.credits) ? json.credits.join("; ") : json.credits
if (!json.title) {
throw `The theme ${json.id} does not have a title defined.`
}
this.language = json.mustHaveLanguage ?? Object.keys(json.title)
{