Project Metadata
wallpaperEnginePlugin() emits project.json as a Vite asset. It combines generated build facts with source-controlled author metadata and preserves editor-owned fields from previous output.
import { wallpaperEnginePlugin } from 'wallpaper-engine/plugin';
wallpaperEnginePlugin({ title: 'Night Sky', schemeColor: '#5994ff', metadata: { description: 'An animated night sky.', preview: 'preview.jpg', tags: ['Landscape'], },});Options
Section titled “Options”| Option | Type | Default / role |
|---|---|---|
title |
string |
Required Wallpaper Engine title |
file |
string |
Entry HTML path; defaults to 'index.html' |
schemeColor |
string |
Color.js syntax normalized into the reserved index-free general.properties.schemecolor; previous editor state is the fallback |
properties |
Property record | User properties written under general.properties |
localization |
Localization map | Labels written under general.localization |
supportsAudioProcessing |
boolean |
Defined value overrides bundle-call detection |
metadata |
WallpaperProjectMetadata |
Source-controlled description, preview, tags, ratings, and visibility |
metadataFile |
string |
Auto-created, synchronized top-level JSON object plus a sibling <metadata stem>.assets/ preview sidecar, resolved from Vite’s final project root |
projectLink |
WallpaperProjectLinkOptions |
Persistent link to written output; see Project Links |
minify |
boolean |
Build output defaults to minified JSON; false uses tab indentation |
devtools |
boolean |
Development overlay; defaults to true, always omitted from production |
Generated shape
Section titled “Generated shape”The plugin always generates these core fields:
{ "file": "index.html", "title": "Night Sky", "type": "web"}It always generates general.properties, even when the property record is empty. Localization and audio support are included when configured or detected:
{ "general": { "properties": {}, "localization": {}, "supportsaudioprocessing": true }}properties are normalized with zero-based index and order according to record insertion order; explicit values win. supportsaudioprocessing is included only when an explicit true or emitted-bundle detection enables it. A false/absent result does not write a false field.
Written build JSON is one line by default. Set minify: false to use tab indentation. Serve mode does not emit project.json.
Ownership and precedence
Section titled “Ownership and precedence”Merge precedence is exact:
previous output < metadata file < defined metadata options < generated fields- Existing
dist/project.jsonis the lowest-priority preservation source. metadataFilereplaces colliding author-owned fields from previous output.- Each
metadatafield replaces lower-priority values only when it is notundefined. - Generated
file,title,type, and ordinarygeneralfields always win. - A defined
schemeColoroption wins over every previousgeneral.properties.schemecolorvalue. When the option is omitted and the source property schema does not define that key, a valid editor-managed value is carried into regenerated properties.
Merging is otherwise top-level and shallow. general is removed from preservation sources and regenerated from plugin options and bundle audio detection. Put localization and ordinary properties in their defined options rather than an editor-owned general object.
Unknown top-level fields survive from previous output or a metadata file unless a higher source replaces them. This allows Wallpaper Engine editor state to coexist with build-owned fields.
Metadata files
Section titled “Metadata files”wallpaperEnginePlugin({ title: 'Night Sky', metadataFile: 'metadata.json',});The path resolves from Vite’s final root. Before a written build cleans the output directory, the plugin creates a missing file (including parent directories) and synchronizes non-generated fields from the previous project.json. Existing author-owned fields (description, preview, tags, ratings, and visibility) stay authoritative in the metadata file. Wallpaper Engine-managed and unknown fields are updated from the editor output, including workshopid, workshopurl, and version. This makes the file a source-controlled handoff for clean builds on another machine.
Watch rebuilds refresh this capture before Vite cleans the previous output. Unchanged metadata and sidecar bytes are left untouched, so importing them into a wallpaper does not cause repeated builds.
The metadata file and each resolved sidecar preview must remain outside Vite’s final build.outDir. Overlapping paths, including paths through symlinks or junctions, are rejected before output cleanup because Vite would otherwise delete the preservation source during the same build.
When the synchronized final preview path matches a preview captured from previous output, the same pre-clean step writes its exact bytes beneath a sibling directory whose name replaces the metadata filename’s final extension with .assets. The project-relative preview path is retained below that directory. For example:
config/metadata.jsonconfig/metadata.assets/previews/editor.jpgThis convention applies only when metadataFile is configured and build.write is true. Check in both the JSON file and its sidecar directory. The sidecar replaces the metadata filename’s final extension with .assets: metadata.json uses metadata.assets/, while project.state.json uses project.state.assets/. No global cache or dist file is consulted by a clean clone.
An existing file must contain valid JSON with a non-null, non-array top-level object. Unreadable, malformed, wrong-shaped, or unwritable metadata fails before output cleanup rather than silently discarding publishing state. Generated core keys are ignored during project generation even if an existing metadata file contains them.
Preview paths and restoration
Section titled “Preview paths and restoration”A non-empty final preview must be a safe project-relative file path inside the project output. preview: '' is preserved and skips path validation and restoration. For non-empty paths, the plugin rejects:
- POSIX, Windows drive, and UNC absolute paths.
- Traversal that resolves outside the project directory.
.or another value that does not identify a project file.
Backslashes are normalized for preview lookup and emitted asset filenames, but the preview string serialized in project.json is preserved verbatim. Use forward slashes in metadata when that serialized form is required. During a written build, a non-empty final preview must come from one of these sources:
- An asset already emitted into the Vite bundle at the final path.
- A matching file under Vite’s final
publicDir. - The previous build output, captured before Vite cleans
outDir. - The matching
<metadata stem>.assets/<preview>sidecar.
Bundle and publicDir sources retain output precedence. Otherwise the plugin prefers a matching current capture over the persistent sidecar, synchronizes that capture into the sidecar, and emits the selected bytes back to the same normalized relative path. It does not decode, resize, or re-encode the preview. A changed higher-priority preview path never relabels bytes captured under a different path.
When build.copyPublicDir is false, the plugin captures and emits the referenced public preview itself. In-memory builds (build.write: false) also include those preview bytes in their returned bundle.
A clean clone or CI job has no previous output. With metadataFile, check in <metadata stem>.assets/<preview> after one synchronization build. Alternatively, keep the preview under publicDir at the exact final path or explicitly emit/configure an asset whose final bundle fileName exactly matches metadata.preview.
The build lifecycle follows Vite’s resolved root, publicDir, build.outDir, and build.write configuration. See Vite’s build guide for those host-owned settings.
Source
Section titled “Source”Generation and preservation are implemented in src/plugin/index.ts.
Next steps
Section titled “Next steps”Link safe written output with Project Links, then follow the Workshop Workflow to preserve editor state.