feat(director): validate scene documents and preserve saved edits

This commit is contained in:
Sameh Khamis committed 2026-09-15 16:20:36 -07:00
1 parent c894d7b920
commit 18300e7e00
13 files changed
+1155 -404

No files matched your search

+5 -2
View File
@@ -25,8 +25,11 @@ Pure authored-time/seek calculations and a playback clock now own timing,
hold deadlines and subscriptions. Stop immediately clears timers and settles
holds; stale tick callbacks cannot publish into a replacement. Registered pack
rules own presentation overrides and Nepal map fallback without changing saved
shots or content. The versioned scene/data-pack format remains planned in
[Director](DIRECTOR.md).
shots or content. Scene-document validation and legacy migration now have separate
owners. Invalid imports preserve the current project; unreadable saved projects
are protected from fallback writes. Version 3 remains the export format, with
zero pitch, low camera heights, scope and detection edits preserved. See [the document contract](SCENE-DOCUMENT.md);
data packs and interactions remain planned in [Director](DIRECTOR.md).
Search framing and annotations request semantic map features from an explicit source. The Overpass adapter owns bounded queries, member/tag decoding and request deadlines; callers retain candidate ranking, outline caching, deferred retries and scene placement. Empty, transient and throttled outcomes remain distinct. Traffic sources return road records and installation sources return mapped records with freshness/saturation metadata, so their layers no longer decode upstream elements. ALPR already normalizes its records in the source. Default providers, footprints, road directions, exact-viewport retries and source attribution are unchanged.
+6 -7
View File
@@ -66,13 +66,12 @@ and river coordinates are bundled. See the
calculations, an owned clock and registered presentation rules preserve
existing recipes and content. Tests cover direct load, forward/backward seek,
replay, bounded media waits and cancellation at pending transitions.
2. **Versioned scene document.** Build on the existing version-3 project export
and its importer. Specify a schema and migrations before changing writes.
Keep old imports and local-storage projects readable; reject unsupported
versions without replacing the current project. Include stable scene, shot,
anchor, pack and action IDs. Separate portable authored state from transient
loading state and run diagnostics. Bound document size, collection lengths,
numeric values and nesting; show errors at the affected field.
2. **Versioned scene document — implemented for existing camera-pose shots.**
[Version 3](SCENE-DOCUMENT.md) remains the write format. Bounded validation
rejects malformed files before replacing current state; legacy migration
preserves IDs, pack bindings and visual edits. Unreadable saved files are
protected from fallback writes. Camera anchors, pack manifests and actions
receive stable IDs with their respective later format extensions.
3. **Camera directions.** Define named geographic anchors and explicit
move/hold segments: start and destination, duration, easing, heading, pitch,
roll and stop duration. State coordinate units, altitude reference and
+102
View File
@@ -0,0 +1,102 @@
# Scene document version 3
Scenes exports a UTF-8 JSON project. Version 3 remains the write format; existing
version-3 exports need no conversion. Versions 1, 2 and unversioned legacy files
are migrated on import. Unknown versions or unsupported fields are rejected with
a field path before the current project, selection or saved bytes change.
```json
{
"version": 3,
"scenes": [{
"id": "my-scene",
"title": "My scene",
"shots": [{
"id": "my-shot",
"title": "Arrival",
"durationSec": 4,
"holdSec": 1,
"camera": { "lat": 51.5, "lon": -0.12, "alt": 800,
"heading": 0, "pitch": -35, "roll": 0 },
"visual": { "style": "normal" },
"layers": { "traffic": { "enabled": true } }
}]
}]
}
```
## Fields and migration
The root requires `scenes` (an array, which may be empty). Optional root fields
are `version`, `createdAt`, `updatedAt` and `installedBuiltInSceneIds` (string IDs).
Each scene requires a `shots` array; optional fields are `id`, `title`,
`releaseLayerIds` and `appliedShotPacks`. A pack marker has `id`, optional numeric
`version` and `shotBindings` (original shot title to saved shot ID). These markers
retain the editor's existing pack-update and rename behavior.
Shots accept `id`, `title`, `durationSec`, `holdSec`, `camera`, `visual`, `layers`,
`sourcePackId` and `sourcePackVersion`. Scene IDs are unique within the project;
shot IDs are unique within their scene. Existing IDs and pack bindings survive
migration. Missing IDs are generated once and persisted by the next save/export.
Missing titles and optional camera/visual fields receive the editor's defaults.
Empty scenes and intentionally empty projects remain empty.
Camera latitude/longitude and heading/pitch/roll use degrees. Altitude uses the
existing camera-pose height in meters. Import preserves finite heights below
100 meters and zero pitch, which previously fell through to defaults. Explicit
altitude-reference and terrain-relative directions are a subsequent format step.
Flight durations retain the editor's minimum of 0.2 seconds and its 4-second
fallback for zero; holds may be zero.
Visual fields are `style`, `mapStack`, `styleParams`, `bloom`, `sharpen`, `hud`, and
`detection`, and `scope`. Bloom has `enabled`, `intensity`, `version`; sharpen has `enabled`,
`intensity`; HUD has `visible`, `variant`; detection has `mode`, `density`,
`allocation`, `fadePct` and `outsideOpacityPct`. Scope has `enabled`, `featherPct`.
The latter settings were written by Update Shot but previously lost during
normalization; they now survive import and capture.
Version-1/2 bloom without an explicit scale version uses the original inverted
scale and migrates once to scale 2. The scene document and bloom scale versions
are separate. Legacy numeric strings are accepted for numeric fields.
Layer entries accept a boolean shorthand or `{ "enabled": true, "params": {} }`.
Parameters and style parameters retain nested JSON data. They are interpreted by
already registered components; importing a file does not fetch its URLs, register
modules, evaluate code or apply its layers. Unsupported structural fields are
rejected instead of silently dropping potential edits. Map-stack selection
retains the existing registered-stack behavior.
## Validation limits
`gods-eye-view/director` exports `parseSceneDocument(text)`,
`validateSceneDocument(project)`, `stringifySceneDocument(project)`,
`SceneDocumentError`, `SCENE_DOCUMENT_VERSION` and `SCENE_DOCUMENT_LIMITS`.
Validation is independent of the renderer, storage and recipes. Parsing checks
UTF-8 size before JSON decoding; the file importer also checks file size before
reading it. Migration/defaults live separately in `src/scenes/project.js`.
Limits are 5 MiB, 256 scenes, 10,000 total shots, 10,000 entries per collection,
24 levels of nesting, 200,000 visited values, 65,536 characters per JSON string,
256 characters per field/ID, and 4,096 characters per title. Numeric values must
be finite. Camera limits are latitude ±90, longitude ±180, altitude −12,000 to
1 billion meters, heading/roll ±360 and pitch ±90; durations/holds are at most
86,400 seconds. Post-processing values are bounded and normalized to the existing
slider ranges. Object prototype keys are refused at every depth.
An invalid import leaves the current project and playback untouched. A valid
import cancels and settles pending playback before replacing the project. If
multiple files are read concurrently, only the newest request can replace it.
Disposal prevents a pending import from publishing.
The browser storage key remains `godsEyeView.sceneProject.v2` for compatibility;
that key's suffix is not the document version. An unreadable saved project stays
in storage. The app supplies temporary defaults and a visible warning, but blocks
saving over the original bytes until a valid file is explicitly imported. Export
can preserve temporary edits. Storage writes use the same validation before replacing saved bytes; invalid
edits and storage write failures surface an unsaved toast.
Only authored project fields belong in this file. Runtime clocks, camera flights,
loading progress and run diagnostics remain separate. Camera anchors, asset-pack
manifests and declarative interactions are planned extensions, not accepted fields
in version 3. They will receive stable IDs and their own validated migration when
introduced. No scene assets, source links, licenses or contributor attribution
change with this document boundary.