New artwork reaches the app without an app-store release. The pipeline:
svg/. The output must follow this exact subset of SVG — it’s what
parseSvgDocument/parseSvgPathData in
composeApp/src/commonMain/kotlin/com/calmcoloring/app/content/remote/
understand:
<svg viewBox="0 0 W H"> root, no nested groups or transform.<rect>, <circle>, and <path> children only, each with an
id (region name — first element is conventionally "background").<path d="..."> may use M/m L/l H/h V/v Q/q C/c A/a Z/z. Arc flags
(the two 0/1 digits before the endpoint) must be space-separated —
"a30,30 0 0 1 58,-10", never digit-glued.svg/<id>.svg.Add or bump its manifest entry in svg/manifest.json: a new file
needs a new { id, name, accent, file, contentVersion: 1 } entry
(accent is one of SAGE/SKY/CLAY/SAND/LILAC/MOSS); editing an
existing file’s art requires bumping that entry’s contentVersion — the
app only re-downloads a template when the manifest’s contentVersion
differs from what it has cached.
main. The app fetches
https://raw.githubusercontent.com/Atul206/MontessoriArc/main/svg/manifest.json
(and the .svg files it lists) on every launch
(ContentRepository.refresh(), called from App.kt), diffs
contentVersion against its local SQLDelight cache, downloads and caches
whatever changed, and re-renders. No further action needed — nothing to
run, deploy, or release.Rendering itself always comes from the local cache (or, for the 8 launch
templates before any refresh has ever completed, the compiled-in
TemplateCatalog.all) — a refresh only updates what’s cached for next time,
it never blocks what’s on screen.
If a published piece is wrong but should still exist, never edit git
history or force-push — devices may have already fetched the bad commit, and
raw.githubusercontent.com has no CDN layer to invalidate anyway. Just fix
the .svg file and bump its contentVersion upward (never down — the
app only re-fetches when the manifest’s number differs from what it has
cached, so a higher number is what makes every device pick up the fix on its
next launch).
Use this when a piece shouldn’t exist at all (wrong subject, quality issue,
whatever) rather than just needing a fix. Deleting its entry from
templates is not enough — a device that already cached it has no way to
learn it should stop being shown, since ContentRepository.refresh() never
prunes a cached id just because it disappeared from the manifest (that could
just as easily mean “no update available” as “delete this”).
Instead, add the id to the top-level removedIds array:
{
"templates": [ /* ... entries for everything that should still exist ... */ ],
"removedIds": ["some-bad-template-id"]
}
On next refresh(), every device deletes that id from its local
TemplateCacheStore and drops it from the gallery. If that id was one of
the 8 launch templates (i.e. it also exists in the compiled-in
TemplateCatalog.all), removing the cached override doesn’t remove the
template entirely — it falls back to showing the original bundled version
again, since ContentRepository only hides a bundled entry while a cached
override for that same id exists. For a template that only ever existed as
OTA content (no bundled fallback), removing it makes it disappear from the
gallery, which is the correct “undo a bad publish” behavior.
Leave an id in removedIds permanently (don’t clean it up later) — it costs
nothing on devices that never cached it, and removing it from the list would
let a device that’s stuck offline for a long time re-download the entry if
it ever reappeared under templates by id collision with something new.