"A runtime-written Data-filesystem image needs a code Texture, not a CSS background-image URL"
▸ SYMPTOM
You write an image to the user Data filesystem at runtime — a per-save thumbnail, an in-game screenshot, a generated minimap — and then try to show it in a razor panel the obvious way:
.thumb {
background-image: url("thumbnails/save_01.png");
}Nothing renders. No exception, no red error — the panel just stays blank. The same URL scheme works fine for images that ship in your content, which makes it look like the file wasn't written, so you go chasing a phantom write bug.
▸ CAUSE
A CSS url(...) in s&box UI resolves against the mounted content/asset filesystems, not the user Data filesystem (FileSystem.Data). A path that only exists under Data FS therefore never resolves from CSS, and the miss is silent — the property is simply dropped.
The write almost certainly succeeded; the addressing is what's wrong. CSS has no handle on the Data FS at all.
▸ FIX
Turn the image into a Texture in code and assign it to Style.BackgroundImage. The proven, whitelist-clean recipe is to decode your own pixels and push them into a dynamic texture:
var tex = Texture.Create( width, height )
.WithFormat( ImageFormat.RGBA8888 )
.WithDynamicUsage()
.Finish();
tex.Update( pixels ); // pixels is a Color32[]
panel.Style.BackgroundImage = tex;This is the same shape used to build a live minimap texture from code, and it works because you are handing the UI an in-memory Texture object rather than asking CSS to resolve a filesystem path.
Two things that bite alongside this:
- Don't
@refa collection of stateful child panels for a@foreachof cards. Refs into a dynamically-built list get fragile fast. Use a small customPanelsubclass driven by markup attributes that it reads in its ownTick— each card owns its state instead of the parent juggling refs. - Data FS's clean write surface is text-only. The whitelist-friendly path is
WriteAllText. If you want a thumbnail without touching a binary/PNG API, store a palette-indexed text thumbnail — one character per cell. At 64×64 that's about 4 KB, trivially written and read back, and you rebuild theColor32[]from it at load time.
▸ WHY IT WORKS
The UI resolves background-image: url(...) through the asset/content mount points, which are read-only, compiled, and known at build time — exactly what CSS expects. The user Data FS is a runtime, per-machine write target with no CSS binding. By decoding the bytes yourself and creating a Texture, you skip the resolver entirely and give the renderer something it can draw directly, which is also why the dynamic-usage minimap path is the reliable pattern for any image you generate at runtime.
- Published