--- name: tinyworld-runtime-state description: Use when adding or changing persisted user state — settings defaults, audio, camera/orbit, panel positions, feature flags, and the in-app "Save Defaults" pipeline that snapshots localStorage into tinyworld-defaults.json. Also covers the inline-script regex gotcha that has burned us twice. --- # Tiny World Runtime State Most browser-local persisted user state lives in `localStorage` under the `tinyworld:*` prefix. Read/write convention: stringified primitives or `JSON.stringify` for objects. Never store credentials, local world saves, cloud world saves, or per-viewport pixel positions in the shipped defaults file — see exclusion list below. Persisted render/material settings that affect shared Three materials must be re-applied during late boot, not only from control `input` handlers. In particular, material wear (`tinyworld:render:materialWear`) needs the `applyPersistedMaterialSettingsOnBoot()` pass so saved wear is visible on first render without toggling the slider. Builder directional sun defaults to `10.0` (1000%) under `tinyworld:render:directionalSun`. Keep the one-time migration narrow: upgrade missing/old untouched `1.0` builder values to `10.0`, but preserve clearly user-edited non-default values. Island Viewer has its own `tinyworld:island-viewer:*` graphics keys and migrates its old `1.1` default separately. Cloud saves are separate from defaults/localStorage: - The account modal posts full TinyWorld JSON to Netlify Functions (`/api/builds`) backed by Netlify Database. - On authenticated boot, local named worlds from `tinyworld:worlds.v1` are uploaded to `/api/builds`; the active unslotted `tinyworld:v1` state gets a local slot first so it can be bound to a cloud row. Top-menu "My worlds" and account-modal "My Worlds" must read from the same cloud-aware list. - The world menu's share action posts the same full state to `/api/share`; public share URLs load by resolving `?share=` to same-origin `/api/share?id=`. - Local custom assets are also synced once authenticated. `/api/assets` stores custom voxel-build stamps and saved asset templates, then merges the remote library into localStorage before pushing the merged local copy back up. - Keep `snapshotCurrentState()` in sync with `saveState()` so account saves and share URLs include grid size, islands, moorings, custom voxel stamps, camera, landscape settings, and cells outside the home board that the user edited. - Top-bar JSON import should accept the app's own portability shapes: a bare world state (`cells` at the root), cloud/account envelopes (`data` or `state` containing a world), named-world/localStorage lists, and exported asset bundles. Imported worlds should be inserted into `tinyworld:worlds.v1` so the account DB sync can pick them up after login. - The visible top-bar JSON import affordance should be a native `