@rr0/ufoathome 0.66.0 → 0.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +95 -95
  2. package/dist-embed-scene/UfoMessages_fr-_6zR9Sw1.js +1 -0
  3. package/dist-embed-scene/rr0-scene.mjs +166 -166
  4. package/dist-embed-sighting/CoverageAssessor-CZfxbN9A.js +1 -0
  5. package/dist-embed-sighting/HynekAssessor-DPsLpEGQ.js +1 -0
  6. package/dist-embed-sighting/SightingMessages_fr-CdnlQOHg.js +1 -0
  7. package/dist-embed-sighting/UfoMessages_fr-_6zR9Sw1.js +1 -0
  8. package/dist-embed-sighting/rr0-sighting.mjs +113 -113
  9. package/dist-embed-sighting-editor/{BodyEditor-eKnfK1A2.js → BodyEditor-Q5FX46YD.js} +2 -2
  10. package/dist-embed-sighting-editor/CoverageAssessor-C_QKvYFk.js +1 -0
  11. package/dist-embed-sighting-editor/HynekAssessor-Onzg_YHp.js +1 -0
  12. package/dist-embed-sighting-editor/SightingEditorMessages_fr-lSHcWCCl.js +1 -0
  13. package/dist-embed-sighting-editor/UfoMessages_fr-_6zR9Sw1.js +1 -0
  14. package/dist-embed-sighting-editor/rr0-sighting-editor.mjs +289 -289
  15. package/dist-embed-sighting-editor/sightingSchema-k4pEltg7.js +1 -0
  16. package/package.json +1 -1
  17. package/dist-embed-scene/UfoMessages_fr-BbAWUSSI.js +0 -1
  18. package/dist-embed-sighting/CoverageAssessor-mVlehblF.js +0 -1
  19. package/dist-embed-sighting/HynekAssessor-BiP2di32.js +0 -1
  20. package/dist-embed-sighting/SightingMessages_fr-KwK5zJGF.js +0 -1
  21. package/dist-embed-sighting/UfoMessages_fr-BbAWUSSI.js +0 -1
  22. package/dist-embed-sighting-editor/CoverageAssessor-FIt771Wz.js +0 -1
  23. package/dist-embed-sighting-editor/HynekAssessor-DUqRa3Su.js +0 -1
  24. package/dist-embed-sighting-editor/SightingEditorMessages_fr-D2QBeLp7.js +0 -1
  25. package/dist-embed-sighting-editor/UfoMessages_fr-BbAWUSSI.js +0 -1
  26. package/dist-embed-sighting-editor/sightingSchema-DGLfQ-J1.js +0 -1
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  # UFO@home
4
4
 
5
- **UFO@home** lets a UFO witness record the shape, appearance and movement of what they saw — and replay it like a VCR —
5
+ **UFO@home** lets a UFO observer record the shape, appearance and movement of what they saw — and replay it like a VCR —
6
6
  instead of relying only on a written or spoken account. The approach follows [Roger Shepard's
7
7
  recommendation](https://rr0.org/time/1/9/6/8/07/29/Symposium/Shepard/index_fr.html) that a visual reconstruction of a
8
- testimony is more faithful than an oral or written one.
8
+ account is more faithful than an oral or written one.
9
9
 
10
10
  Originally a Java applet (2003), the project has been rewritten from scratch in TypeScript: a small,
11
11
  dependency-light engine (keyframe timeline, recording, playback, a three.js scene) wrapped in three vanilla
@@ -17,13 +17,13 @@ for what that buys.
17
17
  ### Naming
18
18
 
19
19
  `<rr0-scene>` is named without "ufo" on purpose: it renders a generic 3D scene (sky/horizon/stars/decor) from a
20
- real-world time and place, and stands the witness's own phenomenon in it. Under it lives a playback layer —
20
+ real-world time and place, and stands the observer's own phenomenon in it. Under it lives a playback layer —
21
21
  `UfoElement`, the timeline, the controls, the canvas the pointer works on — reached as `scene.ufoElement`; it was a
22
22
  component of its own (`<rr0-ufo>`, the shape painted on a bare background) until 0.54.0, when the phenomenon moved
23
23
  into the scene and a shape with no sky stopped being a thing this project draws. Read-only playback needs no
24
24
  "player" suffix, since it is every component's default behavior, and `<rr0-sighting-editor>` is the one that needs
25
- a qualifier (it *adds* recording on top). `<rr0-sighting>` (renamed from `<rr0-ufo-witnesses>` — see below) is the
26
- standard way to display any real sighting, whether it has one witness or several: a witness account always implies
25
+ a qualifier (it *adds* recording on top). `<rr0-sighting>` (renamed from `<rr0-ufo-observers>` — see below) is the
26
+ standard way to display any real sighting, whether it has one observer or several: a observer account always implies
27
27
  a real place and time, so it always composes `<rr0-scene>`.
28
28
 
29
29
  The project's own site is **[ufoathome.org](https://ufoathome.org)**: [the demos](https://ufoathome.org/demos/)
@@ -84,7 +84,7 @@ attribute the three other elements take. That is what makes an address per obser
84
84
  this attribute, and [its player](https://ufoathome.org/play/) does the same for read-only replay.
85
85
  Any path the site does not otherwise serve becomes that parameter, so
86
86
 
87
- - `https://ufoathome.org/play/?sighting=/demo-data/witness-socorro.json`, or simply
87
+ - `https://ufoathome.org/play/?sighting=/demo-data/observer-socorro.json`, or simply
88
88
  - `https://ufoathome.org/Socorro`
89
89
 
90
90
  open that observation. A bare name with no `/` is looked for among the site's own demos first, then
@@ -110,11 +110,11 @@ the host page's own `lang` then `navigator.languages`, no picker UI.
110
110
 
111
111
  The element the two others build on (~530KB gzip — [Three.js](https://threejs.org/) plus
112
112
  [`astronomy-engine`](https://github.com/cosinekitty/astronomy)'s planetary/lunar position tables, which don't
113
- tree-shake since they're one shared data table used internally for every body): the witness's own phenomenon
113
+ tree-shake since they're one shared data table used internally for every body): the observer's own phenomenon
114
114
  standing in a 3D sky/horizon/starfield/decor scene computed from the recording's real time and place, with the
115
115
  playback controls under it. Its own members are `src`, `sightingData`, `loadFromSrc`, `enableClickToPlay` (forwarded
116
116
  to the playback layer), `ufoElement` (that layer), `sceneRenderer`, and the attributes `show-compass`,
117
- `show-witness-map` and `hide-milestones`. Click-to-play/pause works anywhere on the scene (the playback layer's
117
+ `show-observer-map` and `hide-milestones`. Click-to-play/pause works anywhere on the scene (the playback layer's
118
118
  transparent canvas covers the whole stage), and the fullscreen button fullscreens the *whole* scene — it sets the
119
119
  layer's `fullscreenTarget` to its own outer stage for this.
120
120
 
@@ -206,7 +206,7 @@ blurred one in a sharp frame was close.
206
206
  The frame is letterboxed inside the widget's own box rather than resizing it, and the field it
207
207
  implies becomes the recording's default — a recording that states its own (a zoom, binoculars) keeps
208
208
  it. An eye has no rectangle and no format, and neither has a camera nobody identified: both fall back
209
- to the scene's own 16:9 at the 60° vertical field this project draws an unaided witness through
209
+ to the scene's own 16:9 at the 60° vertical field this project draws an unaided observer through
210
210
  (about the middle half of a real human field, which is where acuity actually is). The catalogue is
211
211
  dated, so the picker offers what existed: no telephone in 1964.
212
212
 
@@ -222,13 +222,13 @@ through a 50 mm at f/2 for twenty seconds (see `src/engine/instrument/LimitingMa
222
222
  darkness/color follows the sun's altitude (day/twilight bands/night), and its dawn/dusk glow is anchored on the
223
223
  sun's real compass direction, not spread uniformly around the horizon — see `src/render3d/skyColors.ts`.
224
224
 
225
- The witness's own pose — geographic position, elevation, and viewing heading/pitch/field of view — can vary over
226
- the sighting's timeline via `witnessTrack` in the sighting JSON (a keyframe array of `{ t, pose }` alongside
227
- `timeline` — the model class is `ObserverTrack`, but the serialized key is `witnessTrack`, and writing the class's
225
+ The observer's own pose — geographic position, elevation, and viewing heading/pitch/field of view — can vary over
226
+ the sighting's timeline via `observerTrack` in the sighting JSON (a keyframe array of `{ t, pose }` alongside
227
+ `timeline` — the model class is `ObserverTrack`, but the serialized key is `observerTrack`, and writing the class's
228
228
  name into a file is a mistake that costs an afternoon; same
229
229
  hold-last/interpolated-lookup shape — see `src/engine/model/ObserverTrack.ts`), driving both the camera's own
230
230
  orientation and which real-world instant the astronomy is computed for as playback advances. Older recordings
231
- with no `witnessTrack` fall back to the legacy static `place[0]` (see `resolveObserverPoseAt` in
231
+ with no `observerTrack` fall back to the legacy static `place[0]` (see `resolveObserverPoseAt` in
232
232
  `src/engine/model/Sighting.ts`) — usable for sky darkness/color and camera pitch/fov, but with no compass heading
233
233
  to orient the camera by.
234
234
 
@@ -237,14 +237,14 @@ approximation) stays in the repo, tested, and still backs `skyBrightness()`'s tw
237
237
  the live rendering path now uses `astronomy-engine` for the Sun too, for a single source of truth and to get the
238
238
  Sun's azimuth from the same call used for the sky's directional glow.
239
239
 
240
- `<rr0-sighting-editor>` has editor fields for the witness's latitude/longitude/heading and the observation's start
241
- date/time (all optional) — filling in lat+lng writes both the legacy `place` and a single t=0 `witnessTrack`
240
+ `<rr0-sighting-editor>` has editor fields for the observer's latitude/longitude/heading and the observation's start
241
+ date/time (all optional) — filling in lat+lng writes both the legacy `place` and a single t=0 `observerTrack`
242
242
  keyframe (elevation/pitch/field of view stay at neutral defaults; there's no UI yet for authoring the observer
243
243
  *moving* over time, only a single static pose per recording).
244
244
 
245
245
  Not yet done: a mirage, the supernumerary arcs crowded inside a bright rainbow and the corona round a Sun seen
246
246
  through a thin water cloud (all three are interference, and nothing here models the wave — see `WaterDrop.ts`),
247
- and a multi-keyframe `witnessTrack` authoring UI (today the editor can only set
247
+ and a multi-keyframe `observerTrack` authoring UI (today the editor can only set
248
248
  one static pose; an observer that moves/re-orients mid-recording still needs hand-authored or scripted JSON). The
249
249
  Moon's phase currently only dims/brightens its disc's overall
250
250
  color rather than rendering a geometrically accurate crescent shape — a natural follow-up.
@@ -283,7 +283,7 @@ coverage floor:
283
283
  recorded tail length and are drawn with no tail at all.
284
284
 
285
285
  - **Satellites** (`src/engine/astronomy/Satellites.ts`). For every date, the ILLUMINATION: `h = R(sec B - 1)` gives how high the Earth's shadow stood
286
- above the witness, so deep in the night nothing in low orbit is lit and a light crossing the sky
286
+ above the observer, so deep in the night nothing in low orbit is lit and a light crossing the sky
287
287
  then was not a satellite. Being lit and being seen are kept apart — everything in orbit is sunlit
288
288
  by day, and an Iridium flare at magnitude -8 was genuinely watched at noon. Also complete, and
289
289
  from CelesTrak's SATCAT: how many tracked objects were in orbit that month, and when each named
@@ -309,10 +309,10 @@ coverage floor:
309
309
  family keeps a second, independent derivation in closed form (`IceHalos.ts`, `Rainbows.ts`) whose only job is to
310
310
  disagree with the trace; that check has already caught one shipped error.
311
311
 
312
- All of them appear in the editor's read-only "Sky:" line, with a button to turn the witness toward
312
+ All of them appear in the editor's read-only "Sky:" line, with a button to turn the observer toward
313
313
  the meteor or the comet. The bow line is said only when rain was reported — everybody knows whether it was
314
314
  raining, so the interesting answers are the negative ones: a Sun higher than 42° puts every bow below a ground
315
- witness's horizon, and an unbroken deck between the Sun and the rain is the missing half of the famous
315
+ observer's horizon, and an unbroken deck between the Sun and the rain is the missing half of the famous
316
316
  condition.
317
317
 
318
318
  **Regenerating the satellite catalog.** `src/engine/astronomy/satelliteCatalog.ts` is generated by
@@ -339,9 +339,9 @@ apparitions, and the peak magnitudes and tail lengths recorded at the time, are
339
339
  `scripts/build-comet-catalog.ts` — the orbits are looked up, the brightness is an observation, and the script's
340
340
  own doc comment explains why the two cannot come from the same place. The generated file *is* checked in.
341
341
 
342
- **The phenomenon stands in the scene, and is still only what the witness saw.** The shape used to be painted on a
342
+ **The phenomenon stands in the scene, and is still only what the observer saw.** The shape used to be painted on a
343
343
  2D canvas laid over the 3D scene, so that nothing about it could be read as a claim about a solid at a distance. It
344
- is now a plane *in* the three.js scene (`src/render3d/PhenomenonSystem.ts`), facing the witness and scaled so that
344
+ is now a plane *in* the three.js scene (`src/render3d/PhenomenonSystem.ts`), facing the observer and scaled so that
345
345
  it subtends exactly the angle the recording states — which makes it look the same from their eye at any distance
346
346
  whatever, and is what keeps the claim where it was: the recording states angles and nothing else, and the distance
347
347
  the plane is drawn at is a parameter of the picture (`src/engine/shape/PhenomenonDepth.ts`, see *Where the shape
@@ -350,7 +350,7 @@ the same halo, blur, veil and spikes into its texture. What changed is who decid
350
350
  depth, per pixel, so a patrol car in front of it hides exactly the part of it a patrol car would, where the overlay
351
351
  sampled nine points and hid the shape whole or not at all. The ground and the terrain are kept out of that on
352
352
  purpose — the phenomena are drawn in a pass of their own, depth-tested against the decor alone — because a relief
353
- patch at thirty-metre resolution deciding what a witness saw would not be a reconstruction (Socorro's craft, a
353
+ patch at thirty-metre resolution deciding what a observer saw would not be a reconstruction (Socorro's craft, a
354
354
  hundred feet away in the arroyo below the road, sank two metres under one). It also means the phenomenon goes
355
355
  through the instrument's own projection like everything else in the scene, and through the same long-exposure
356
356
  accumulation, instead of an approximation of each on a separate layer. The overlay keeps the pointer's business:
@@ -359,17 +359,17 @@ to test "was it a helicopter" — is a different statement, and a different obje
359
359
 
360
360
  ## `<rr0-sighting>` — standard sighting view
361
361
 
362
- The standard way to display any real sighting, whether it has one witness or several — renamed from
363
- `<rr0-ufo-witnesses>` once it stopped being just a multi-witness selector (see [Naming](#naming)). It composes a
364
- nested `<rr0-scene>` the same way `<rr0-sighting-editor>` does, since a witness recording is
362
+ The standard way to display any real sighting, whether it has one observer or several — renamed from
363
+ `<rr0-ufo-observers>` once it stopped being just a multi-observer selector (see [Naming](#naming)). It composes a
364
+ nested `<rr0-scene>` the same way `<rr0-sighting-editor>` does, since a observer recording is
365
365
  always a real sighting and always needs the real sky/ground backdrop.
366
366
 
367
367
  ```html
368
368
  <rr0-sighting src="sighting.json"></rr0-sighting>
369
369
  ```
370
370
 
371
- `src` accepts either a single witness's `sighting.json` directly or a **case**: RR0's own `case.json`, whose
372
- `events` of `eventType: "sighting"` each point at one witness's `SightingRecordingJson` by `url`, read relative to
371
+ `src` accepts either a single observer's `sighting.json` directly or a **case**: RR0's own `case.json`, whose
372
+ `events` of `eventType: "sighting"` each point at one observer's `SightingRecordingJson` by `url`, read relative to
373
373
  the case file itself (so the same case works from its dossier's page and from anywhere else). A case's other events
374
374
  (analyses, articles, films, confessions) are not replayed. See `CaseJson` in `src/engine/persistence/caseJson.ts`:
375
375
 
@@ -378,45 +378,45 @@ the case file itself (so the same case works from its dossier's page and from an
378
378
  "id": "ChilesWhitted",
379
379
  "title": "Chiles et Whitted",
380
380
  "events": [
381
- { "type": "event", "eventType": "sighting", "url": "witness-chiles.json" },
382
- { "type": "event", "eventType": "sighting", "url": "witness-whitted.json" }
381
+ { "type": "event", "eventType": "sighting", "url": "observer-chiles.json" },
382
+ { "type": "event", "eventType": "sighting", "url": "observer-whitted.json" }
383
383
  ]
384
384
  }
385
385
  ```
386
386
 
387
387
  The two shapes are told apart automatically — an object with `events` and no `timeline` is a case, anything else
388
- one witness's own recording. A bare JSON array (the witness manifest read before cases) is refused with an error
389
- naming `case.json`. No labels are duplicated in the case — each witness's display name and the shared
390
- case id grouping them together are read from that witness's *own* file (`witness`/`caseId`, see
388
+ one observer's own recording. A bare JSON array (the observer manifest read before cases) is refused with an error
389
+ naming `case.json`. No labels are duplicated in the case — each observer's display name and the shared
390
+ case id grouping them together are read from that observer's *own* file (`observer`/`caseId`, see
391
391
  [Data format](#data-format)), so there's a single source of truth and nothing to drift out of sync. This means
392
- every listed witness's recording is fetched upfront (to read its name), not lazily on selection — fine at the
393
- scale a case's witness list actually has. If a witness has no `witness.title`, its `witness.id` is shown instead, or
394
- the URL itself as a last resort. A mismatched `caseId` across the listed witnesses logs a console warning (doesn't
392
+ every listed observer's recording is fetched upfront (to read its name), not lazily on selection — fine at the
393
+ scale a case's observer list actually has. If a observer has no `observer.title`, its `observer.id` is shown instead, or
394
+ the URL itself as a last resort. A mismatched `caseId` across the listed observers logs a console warning (doesn't
395
395
  block) — likely means unrelated recordings got listed together by mistake.
396
396
 
397
- **Where two witnesses described different things, the recordings have to show it.** Not that they
398
- must differ — witnesses often agree, and two matching recordings are then simply true. But a
399
- difference that exists in the record and not in the files makes the witness picker a control that
400
- does nothing, and nothing *looks* broken. Chiles-Whitted shipped that way for months: one testimony
397
+ **Where two observers described different things, the recordings have to show it.** Not that they
398
+ must differ — observers often agree, and two matching recordings are then simply true. But a
399
+ difference that exists in the record and not in the files makes the observer picker a control that
400
+ does nothing, and nothing *looks* broken. Chiles-Whitted shipped that way for months: one account
401
401
  under two names. Its two pilots drew the object differently for Project Sign — the captain a slim
402
402
  ribbed cigar with a pointed nose and no windows, the co-pilot a blunt cylinder with two rows of
403
403
  lit windows — and only the co-pilot, in the right seat, saw the terminal phase (McDonald's 1968
404
- cross-check). Each file now carries its own witness's account.
404
+ cross-check). Each file now carries its own observer's account.
405
405
 
406
406
  | Member | Kind | Description |
407
407
  |---|---|---|
408
408
  | `src` | attribute | URL of a single `sighting.json` or a `case.json` (above), fetched automatically on connect and whenever the attribute changes |
409
- | `witnessUrls` | property (get/set) | The recordings to show as a plain array of URLs, for programmatic use instead of `src` |
410
- | `sightingData` | property (get/set) | One witness's recording, set directly instead of fetched — for a page holding one in memory (text pasted into a form, a file the reader picked). Its entry carries no URL, so the info panel's editor link and embed lines fall back to the bare application, which is the honest answer for something published nowhere |
409
+ | `observerUrls` | property (get/set) | The recordings to show as a plain array of URLs, for programmatic use instead of `src` |
410
+ | `sightingData` | property (get/set) | One observer's recording, set directly instead of fetched — for a page holding one in memory (text pasted into a form, a file the reader picked). Its entry carries no URL, so the info panel's editor link and embed lines fall back to the bare application, which is the honest answer for something published nowhere |
411
411
  | `scene` | property (readonly) | The `<rr0-scene>` this composes — and through `scene.ufoElement`, the playback members above |
412
412
  | `loadFromSrc(url)` | method (async) | What the `src` attribute triggers internally; can be called directly too |
413
413
 
414
- A toolbar row sits above the scene: a "Testimony by &lt;witness&gt;" sentence on the left, and a round "?" info
415
- button on the right. The witness portion is plain text for a single witness (a one-option `<select>` would be
414
+ A toolbar row sits above the scene: a "Account by &lt;observer&gt;" sentence on the left, and a round "?" info
415
+ button on the right. The observer portion is plain text for a single observer (a one-option `<select>` would be
416
416
  pointless); once there's more than one, it becomes the live `<select>` instead — but the sentence itself, and the
417
- info button, stay visible either way. The first witness loads automatically once the list is known; switching the
418
- selector loads that witness's already-fetched recording into the nested `<rr0-scene>` (no re-fetch). Setting
419
- `witnessUrls` again (e.g. a case re-read) keeps the current selection if that witness is still present, instead
417
+ info button, stay visible either way. The first observer loads automatically once the list is known; switching the
418
+ selector loads that observer's already-fetched recording into the nested `<rr0-scene>` (no re-fetch). Setting
419
+ `observerUrls` again (e.g. a case re-read) keeps the current selection if that observer is still present, instead
420
420
  of resetting back to the first.
421
421
 
422
422
  Clicking "?" opens a panel anchored under the button (it never shifts the canvas below it). Where the browser has
@@ -425,9 +425,9 @@ wrapper cannot clip, which is what rr0.org's layout was doing to it — kept und
425
425
  positioning, flipping above it or centring in the viewport when that side is too short, and closing on Escape or a
426
426
  click outside. Browsers without the API get the plain absolutely-positioned overlay instead.
427
427
 
428
- Its main content is the currently-selected witness's observation metadata (date, location, case id, description,
429
- tags — whichever are actually present in that witness's own `sighting.json`; the witness's own name isn't repeated
430
- here, since it's already in the toolbar's testimony line). The date is shown on the WITNESS's own clock, never
428
+ Its main content is the currently-selected observer's observation metadata (date, location, case id, description,
429
+ tags — whichever are actually present in that observer's own `sighting.json`; the observer's own name isn't repeated
430
+ here, since it's already in the toolbar's account line). The date is shown on the OBSERVER's own clock, never
431
431
  converted into the reader's time zone (see `utcOffsetHours` in [Data format](#data-format)).
432
432
 
433
433
  A footer row holds the app's own name/version on the left — linking to that very observation in the editor (see
@@ -439,7 +439,7 @@ fold-outs on the right, both closed until asked for:
439
439
 
440
440
  ```html
441
441
  <script type="module" src="https://ufoathome.org/lib/rr0-sighting.mjs"></script>
442
- <rr0-sighting src="https://ufoathome.org/demo-data/witness-socorro.json"></rr0-sighting>
442
+ <rr0-sighting src="https://ufoathome.org/demo-data/observer-socorro.json"></rr0-sighting>
443
443
  ```
444
444
 
445
445
  The script URL is derived from where the running bundle was itself loaded from (`import.meta.url`), never
@@ -452,7 +452,7 @@ fold-outs on the right, both closed until asked for:
452
452
  - **Credits** reveals third-party credits (the live terrain imagery attribution, once a real relief patch has
453
453
  resolved, plus the bundled thunder sound's own required attribution — see [`CREDITS.md`](CREDITS.md)).
454
454
 
455
- All of this component's own labels (Testimony by, About, Close, Observation/Date/Location/Case, Credits) are
455
+ All of this component's own labels (Account by, About, Close, Observation/Date/Location/Case, Credits) are
456
456
  translated (English/French) the same way as the playback layer's own labels.
457
457
 
458
458
  ## Data format
@@ -465,10 +465,10 @@ interface SightingRecordingJson {
465
465
  time?: { year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number }
466
466
  endTime?: { year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number } // alternative to durationSeconds
467
467
  durationSeconds?: number // alternative to endTime; takes precedence if both are set
468
- utcOffsetHours?: number // the LEGAL time zone the witness's clock was on (+1 for France in 1965, -7 for New Mexico in April 1964). Absent = approximated from the longitude, which cannot know legal time or a daylight-saving switch
468
+ utcOffsetHours?: number // the LEGAL time zone the observer's clock was on (+1 for France in 1965, -7 for New Mexico in April 1964). Absent = approximated from the longitude, which cannot know legal time or a daylight-saving switch
469
469
  place?: { lat: number, lng: number, name?: string }[] // `name` is the fully qualified place name the coordinates were resolved from — see Naming a place
470
- witness?: { id?: string, dirName?: string, title?: string, lastName?: string, firstNames?: string[] } // every field optional — supply whichever is known; omit entirely for an anonymous witness
471
- caseId?: string // shared by every witness's own sighting.json for the same case — see <rr0-sighting>
470
+ observer?: { id?: string, dirName?: string, title?: string, lastName?: string, firstNames?: string[] } // every field optional — supply whichever is known; omit entirely for an anonymous observer
471
+ caseId?: string // shared by every observer's own sighting.json for the same case — see <rr0-sighting>
472
472
  description?: string
473
473
  tags?: string[]
474
474
  timeline: {
@@ -485,7 +485,7 @@ interface SightingRecordingJson {
485
485
  haloScale: number // 0 = no glow
486
486
  selected: boolean
487
487
  title?: string // shown as an on-canvas tooltip when hovered
488
- angular?: { widthDeg: number, heightDeg: number } // how big it LOOKED — the only size a testimony holds, see Apparent size
488
+ angular?: { widthDeg: number, heightDeg: number } // how big it LOOKED — the only size a account holds, see Apparent size
489
489
  points?: { x: number, y: number }[] // "polygon" shapes only
490
490
  }
491
491
  }>
@@ -493,13 +493,13 @@ interface SightingRecordingJson {
493
493
  order?: string[] // back-to-front paint/hit-test order; absent = first-appearance order
494
494
  groups?: string[][] // each inner array is one group's member sourceIds
495
495
  }
496
- witnessTrack?: { keyframes: Array<{ t: number, pose: { lat?: number, lng?: number, elevationM: number, headingDeg?: number, pitchDeg: number, fovDeg: number } }> }
496
+ observerTrack?: { keyframes: Array<{ t: number, pose: { lat?: number, lng?: number, elevationM: number, headingDeg?: number, pitchDeg: number, fovDeg: number } }> }
497
497
  weatherTrack?: { keyframes: Array<{ t: number, weather: Weather }> }
498
498
  weather?: Weather // legacy static fallback for recordings predating weatherTrack
499
- weatherSource?: { id: string, name: string, url: string } // the meteorological record weatherTrack was looked up from — see Weather is looked up, not remembered. Absent = the witness's own account
499
+ weatherSource?: { id: string, name: string, url: string } // the meteorological record weatherTrack was looked up from — see Weather is looked up, not remembered. Absent = the observer's own account
500
500
  instrument?: "eye" | "rectilinear-lens" // what it was observed THROUGH — see Instrument. Absent = the naked eye
501
- soundTrack?: { keyframes: Array<{ t: number, sound: { kind: "none" | "hum" | "whistle" | "rumble" | "crackle", volume: number, pitchHz: number, src?: string } }> } // what the witness heard — see What it sounded like
502
- decor?: DecorObject[] // buildings, trees, streetlights, vehicles, other witnesses — see src/engine/model/Decor.ts
501
+ soundTrack?: { keyframes: Array<{ t: number, sound: { kind: "none" | "hum" | "whistle" | "rumble" | "crackle", volume: number, pitchHz: number, src?: string } }> } // what the observer heard — see What it sounded like
502
+ decor?: DecorObject[] // buildings, trees, streetlights, vehicles, other observers — see src/engine/model/Decor.ts
503
503
  references?: SceneReference[] // pictures of the place laid over the scene, each at a registered heading/pitch/roll/field — see Pictures of the place
504
504
  }
505
505
  ```
@@ -515,12 +515,12 @@ same clock as the shapes, because a sound rarely starts when the object does: a
515
515
  ground and heard only as it lifts off is two keyframes, `kind: "none"` at the start and a hum at the instant it
516
516
  took off.
517
517
 
518
- `volume` (0..1, how loud the witness could describe it, never a dB figure) and `pitchHz` blend between keyframes;
518
+ `volume` (0..1, how loud the observer could describe it, never a dB figure) and `pitchHz` blend between keyframes;
519
519
  `kind` and `src` are **held**, like every other discrete field in this format — so the example above really is
520
520
  silent right up to that second keyframe. To record a sound emerging gradually instead, give it two keyframes of
521
521
  its own kind (hum at volume 0, then hum at full).
522
522
 
523
- `kind: "none"` is a statement — the witness reported hearing nothing. A recording with no `soundTrack` at all is
523
+ `kind: "none"` is a statement — the observer reported hearing nothing. A recording with no `soundTrack` at all is
524
524
  the different, weaker case: nobody was asked. Both replay as silence, and neither invents a noise.
525
525
 
526
526
  Sounds are **synthesized** from that description (a drone, a whistle, a rumble, a crackle — `pitchHz` is the tone
@@ -535,7 +535,7 @@ audio without one.
535
535
  The same rule governs the whole scene, not just the object's own sound: **paused is paused**. Falling
536
536
  precipitation and its splashes, twinkling stars, lightning flashes, the sun's lens flare and the weather's own
537
537
  ambient beds all stop with the player and resume with it, leaving the frozen frame on screen. A paused replay is
538
- one instant of a sighting — weather still going on over it would be the reader's own room, not the witness's
538
+ one instant of a sighting — weather still going on over it would be the reader's own room, not the observer's
539
539
  evening. Clouds likewise use the recording's timeline: wind advection stops on pause and is
540
540
  recomputed deterministically when seeking.
541
541
 
@@ -545,7 +545,7 @@ Everything the scene draws is computed, and a reader has no way to tell a faithf
545
545
  one. A photograph of the same place does: `references` lays pictures over the render, each at the direction it was
546
546
  registered in (`registration`: heading, pitch, roll and vertical field, angles and nothing else), at an opacity the
547
547
  reader varies with the slider beside the 🖼 button — all picture, all render, and every step between, with the
548
- witness's own phenomenon drawn over both. A `photo` is a flat panel at its lens's field, a `panorama` an
548
+ observer's own phenomenon drawn over both. A `photo` is a flat panel at its lens's field, a `panorama` an
549
549
  equirectangular sphere. `src` is an address (rr0.org's case pictures are served to any origin; the bytes must be, since
550
550
  WebGL draws them) or the picture itself as a `data:` URL when it was added from a disk, which keeps the recording
551
551
  self-contained at the price of its size. `credit` and `creditUrl` are shown in the info panel's credits, `t` says it
@@ -553,30 +553,30 @@ was taken during the observation at that instant, and `drawing` that somebody dr
553
553
  spot carries the sphere and its spiral by hand.
554
554
 
555
555
  Nothing in the scene hides a picture and a picture hides nothing: it is a field of directions from one point, valid
556
- from that point alone, and the reconstruction stands it at the witness's eye.
556
+ from that point alone, and the reconstruction stands it at the observer's eye.
557
557
 
558
- **Lining it up.** The editor's Pictures group types the registration, copies the witness's pose into it, and while that
558
+ **Lining it up.** The editor's Pictures group types the registration, copies the observer's pose into it, and while that
559
559
  group is open the canvas belongs to the selected picture: a drag turns it, the wheel changes its field, and *Add a
560
560
  landmark* arms two clicks that name a detail on the picture, then the same detail in the render. Landmarks are kept in
561
561
  the recording with a name ("Arbre masquant", "barrière") and listed with how far each still is from fitting; either end
562
562
  of one can be dragged, and the selected one is bolder on the canvas — two landmarks turn the picture to fit them, three
563
563
  or more fit its field too (`PictureRegistration`, a triad first guess refined by Gauss-Newton on the rotation), and the
564
- status line says how far off the landmarks still are. *Adopt as the witness's pose* then writes the fitted heading,
564
+ status line says how far off the landmarks still are. *Adopt as the observer's pose* then writes the fitted heading,
565
565
  pitch and roll into the pose at the playhead as a **measurement**, with a `derived` provenance naming the picture and
566
- the residual — a heading read off a picture that fits the relief, where the one typed in the Witness group is the
567
- witness's word. *Street-level pictures nearby* asks Panoramax (open imagery, CC BY-SA, served to any origin) for
568
- pictures taken within 300 m of the witness's spot; each comes with where it was taken from and which way it looked, so
566
+ the residual — a heading read off a picture that fits the relief, where the one typed in the Observer group is the
567
+ observer's word. *Street-level pictures nearby* asks Panoramax (open imagery, CC BY-SA, served to any origin) for
568
+ pictures taken within 300 m of the observer's spot; each comes with where it was taken from and which way it looked, so
569
569
  it arrives registered in heading, a full turn arriving as a panorama.
570
570
 
571
571
  ### Naming a place
572
572
 
573
- Testimony names a place. It says "on the Valensole plateau", "near Socorro", "over Montgomery" —
573
+ Account names a place. It says "on the Valensole plateau", "near Socorro", "over Montgomery" —
574
574
  never 43.8379 / 5.9840. So the Location group leads with a **Place** field: type a name, press Enter
575
575
  (or **Locate**), and the latitude and longitude below are filled from
576
576
  [Nominatim](https://nominatim.openstreetmap.org/), OpenStreetMap's own geocoder — which, unlike the
577
577
  gazetteer-style services, knows the hamlets, farms and airfields that cases actually happen at.
578
578
  A name is often ambiguous, so every candidate stays listed in **Matches** and the best one is
579
- applied straight away; picking another moves the witness. Results are named in the reader's own
579
+ applied straight away; picking another moves the observer. Results are named in the reader's own
580
580
  language, and credited as their licence requires.
581
581
 
582
582
  What gets stored is the *qualified* name the search resolved (`place[].name`), not the two words
@@ -587,9 +587,9 @@ recording can still say "the lavender field east of the farm" beside coordinates
587
587
  The field reads both ways: move the latitude or longitude by hand and a resolved name is re-derived
588
588
  from the new coordinates, or cleared if there is no place there. A name left describing somewhere
589
589
  the sighting is no longer at is worse than no name — the recording would state, in writing, that it
590
- happened there. A name the witness typed themselves is never replaced.
590
+ happened there. A name the observer typed themselves is never replaced.
591
591
 
592
- **Altitude is above sea level**, and the ground at the location sets its floor: a witness in the
592
+ **Altitude is above sea level**, and the ground at the location sets its floor: a observer in the
593
593
  Alps is not at 0 m, and an editor that offers it invites a recording that says so. The ground's own
594
594
  height is read from whichever elevation source is live and shown beside the field. What gets stored
595
595
  is unchanged — `ObserverPose.elevationM` stays a height above the local ground, which is what the
@@ -598,7 +598,7 @@ terrain patch is built around.
598
598
  ### Time zones
599
599
 
600
600
  An hour of `utcOffsetHours` is an hour of Earth's rotation *and* a different row of the weather
601
- record. Pick the witness's own zone (`Europe/Paris`, `America/Denver`, …) and the offset is derived
601
+ record. Pick the observer's own zone (`Europe/Paris`, `America/Denver`, …) and the offset is derived
602
602
  from that zone's rules **at the observation's date**: Valensole in July 1965 resolves to UTC+1, not
603
603
  the UTC+2 the same place gives today — France only reintroduced summer time in 1976. Change the date
604
604
  and it is derived again. The recording stores both: `timeZone` is the rule, `utcOffsetHours` is the
@@ -606,7 +606,7 @@ number it produced, and every consumer keeps reading only the number.
606
606
 
607
607
  The rules come from the platform's own IANA database. What it cannot fix is a zone whose
608
608
  *boundaries* are coarse: Montgomery, Alabama is `America/Chicago`, which observed summer time in
609
- 1948 while Alabama did not. That is why the zone is chosen by the witness rather than derived from
609
+ 1948 while Alabama did not. That is why the zone is chosen by the observer rather than derived from
610
610
  the coordinates — and why the plain entered offset remains available for exactly those cases.
611
611
 
612
612
  The search runs only when asked, never per keystroke: Nominatim's usage policy allows the first and
@@ -638,14 +638,14 @@ the declared longitude is flagged on the field, with the meridian's own solar ti
638
638
  Deliberately a wide net rather than a precise one. Legal time genuinely departs from solar time,
639
639
  sometimes by hours (all of China runs on UTC+8), and the historical rules are worse — the check must
640
640
  never cry wolf at a correct "France on UTC+1 in 1965". It flags only what no country has ever done,
641
- and only as a warning: the recording states the witness's clock, and nothing here knows better than
642
- the witness.
641
+ and only as a warning: the recording states the observer's clock, and nothing here knows better than
642
+ the observer.
643
643
 
644
644
  ### Weather is looked up, not remembered
645
645
 
646
- The Circumstances group is the one part of this editor that isn't testimony. Weather is a
646
+ The Circumstances group is the one part of this editor that isn't account. Weather is a
647
647
  measurable fact about a place at an instant, and the recording already states both — so instead of
648
- leaving a witness (or an author reconstructing a case decades later) to set a cloud-cover slider
648
+ leaving a observer (or an author reconstructing a case decades later) to set a cloud-cover slider
649
649
  from memory, the editor looks the conditions up from [ERA5](https://open-meteo.com/en/docs/historical-weather-api),
650
650
  the ECMWF reanalysis, hourly and worldwide from 1940 on. The fields then show the record's own
651
651
  values, **read-only**, above a line naming the dataset and the exact UTC instant they describe (a
@@ -657,7 +657,7 @@ estimate weighted by cloud level, rain and thunderstorm. A low `baseM` starts fr
657
657
  temperature/dew-point spread. Both derivations are documented in
658
658
  `src/engine/weather/providers/OpenMeteoWeatherProvider.ts`; crystal alignment is never inferred.
659
659
 
660
- Unchecking **From weather records** hands the fields back to the witness: the looked-up values stay
660
+ Unchecking **From weather records** hands the fields back to the observer: the looked-up values stay
661
661
  as a starting point, `weatherSource` is dropped, and no later lookup may overwrite them — the same
662
662
  "declared outranks deduced" rule [Behind a cloud](#behind-a-cloud) follows. A recording that names
663
663
  a `weatherSource` is replayed exactly as authored and never looked up again, so a published case
@@ -678,11 +678,11 @@ first recorded shape turns a fifteen-hour span into a few seconds, so the track
678
678
  whenever it does. Getting either half wrong looks the same from outside — a sighting whose weather
679
679
  never changes.
680
680
 
681
- It follows the witness too, not just the clock. Half of aviation testimony is given from a cockpit,
681
+ It follows the observer too, not just the clock. Half of aviation account is given from a cockpit,
682
682
  and an aircraft under observation for an hour is a long way from where it started — so each sample
683
- is looked up at the position the `witnessTrack` puts the witness at that instant. Positions inside
683
+ is looked up at the position the `observerTrack` puts the observer at that instant. Positions inside
684
684
  one ERA5 grid cell (~28 km) are one query, and several cells still travel in a single request, so
685
- a stationary witness costs exactly what it always did.
685
+ a stationary observer costs exactly what it always did.
686
686
 
687
687
  `scripts/infer-case-weather.ts` runs the same lookup over case files on disk:
688
688
 
@@ -695,7 +695,7 @@ diff shows the weather and nothing else.
695
695
 
696
696
  ### Apparent size — and why there is no real one
697
697
 
698
- A witness never perceives meters. They perceive an angle: the thing covered a thumbnail at arm's length, or a fifth
698
+ A observer never perceives meters. They perceive an angle: the thing covered a thumbnail at arm's length, or a fifth
699
699
  of the windshield, or two full Moons. "About thirty meters long" is a conclusion they drew from a distance they
700
700
  could not perceive either, and the two errors multiply. So a recording stores `angular` — how wide and how tall the
701
701
  object *looked*, in degrees — and stores no real size and no real distance anywhere.
@@ -714,7 +714,7 @@ one of the other two follows, and the one the **Hold** select pins never moves.
714
714
  is what a thing keeps: moving it further makes it look smaller, and editing either width moves the other at the
715
715
  distance it stands (the shape is resized on the canvas). Hold the apparent width instead and a distance edit
716
716
  leaves the shape looking the same, stood elsewhere — which is what asking what the decor would hide of it needs.
717
- Only the angle is kept; the metres are an authoring aid, never testimony, and the distance is where the scene
717
+ Only the angle is kept; the metres are an authoring aid, never account, and the distance is where the scene
718
718
  *draws* the shape (see *Where the shape is drawn*).
719
719
 
720
720
  ### Decor that moves, and lights that blink
@@ -752,14 +752,14 @@ dot from the next rather than dotting the line at the sampler's own rate). A lam
752
752
  candela (a wingtip strobe really is some twenty times one) — because a pose spreads a lamp's light over hundreds
753
753
  of pixels, and at white a whole aircraft trail came out at a thousandth of white, i.e. invisible.
754
754
 
755
- An aircraft in a scene is a **hypothesis**, not testimony — "here is what a flight at that altitude and heading
755
+ An aircraft in a scene is a **hypothesis**, not account — "here is what a flight at that altitude and heading
756
756
  would have looked like" — and belongs to the decor for that reason, next to the buildings and trees whose
757
757
  positions are likewise known rather than reported.
758
758
 
759
759
  ### How big it was, and what it looked like
760
760
 
761
761
  `DecorObject` used to have no size at all. Every building was a six-metre cube per storey, every vehicle the same
762
- 1.8 × 4.35 × 2.0 box, whatever the testimony said. In a project that will not store an angle nobody perceived (see
762
+ 1.8 × 4.35 × 2.0 box, whatever the account said. In a project that will not store an angle nobody perceived (see
763
763
  *Apparent size*, above), that was the last place a number was invented rather than stated: Zamora's Pontiac and a
764
764
  delivery van were the same object, and "the dynamite shack" was a warehouse.
765
765
 
@@ -807,10 +807,10 @@ and it is worth making: a car-shaped silhouette at forty metres in evening light
807
807
  prism reads as a building, which is where this started. Socorro's own patrol car uses one of them, at the real
808
808
  Pontiac's dimensions and with the substitution stated in the recording's description.
809
809
 
810
- One thing a model does NOT yet do is let the witness look out from inside it. A recording can place them inside a
810
+ One thing a model does NOT yet do is let the observer look out from inside it. A recording can place them inside a
811
811
  building or a vehicle, and what they then look at is the room the built-in shape builds around them — walls, window
812
812
  openings sized from the data, the pillar between two door windows. A downloaded model is a hull with none of that, so
813
- while the witness is inside an object the built-in shape is kept. Looking out through a real model is the objective
813
+ while the observer is inside an object the built-in shape is kept. Looking out through a real model is the objective
814
814
  (it is what makes "how much of the sky did the windscreen pillar hide?" answerable) and needs models with interiors,
815
815
  glazing made genuinely transparent, and the viewpoint taken from the model rather than from the primitive's seat.
816
816
 
@@ -881,7 +881,7 @@ on each pose is read back through the first pose that stated one.
881
881
  A pose is drawn in two moves, because a sky costs about 8 ms to rebuild and a five-minute pose is 37 of them: the
882
882
  **viewfinder** immediately (one instant, a twentieth of a millisecond) and the **photograph** as the scene settles,
883
883
  a dozen milliseconds of instants per animation frame, shown as the film fills and gained up to stay properly
884
- exposed — so it goes from beady to smooth rather than from black to bright. Anything the witness does interrupts it
884
+ exposed — so it goes from beady to smooth rather than from black to bright. Anything the observer does interrupts it
885
885
  and starts it again, which is why the editor stays answerable while a long pose is on. Doing it all inside one call
886
886
  is what made it crawl: the dozen setters a single tick touches would each rebuild the whole pose, and the per-frame
887
887
  animation loop (twinkle, rain, lightning — none of which survives a pose of minutes anyway) restarted it sixty
@@ -913,7 +913,7 @@ Two consequences of drawing a pose rather than an instant, both of which cost an
913
913
 
914
914
  #### Where meters do come back
915
915
 
916
- The only real distance a testimony can support is an **inequality**, and only where the witness saw the object
916
+ The only real distance a account can support is an **inequality**, and only where the observer saw the object
917
917
  cross something whose position is known: it passed *behind* that hangar (at least that far), or *in front of* that
918
918
  tree (at most that far). `DecorObject.eastM/northM` give the decor its real position, `occludesSourceIds` says
919
919
  which side of it the object was on, and `SceneRenderer.decorDistancesAt` raycasts the exact line of sight to
@@ -926,11 +926,11 @@ rather than clamping one, and the editor prints the result under the apparent si
926
926
 
927
927
  #### Where the shape is drawn
928
928
 
929
- A plane facing the witness, scaled to the stated angle, looks the same from their eye at any distance — so the
929
+ A plane facing the observer, scaled to the stated angle, looks the same from their eye at any distance — so the
930
930
  scene has to put it *somewhere*, and where is a parameter of the picture, never a fact the recording states.
931
931
  `PhenomenonDepth` (`src/engine/shape/PhenomenonDepth.ts`) is the one place that decides it, from five sources in
932
932
  the order they outrank each other: a distance the recording **states** (none does yet — the tier exists for the
933
- day a close encounter is written as a body in metres); a **hypothesis** the reader is trying; what the witness's
933
+ day a close encounter is written as a body in metres); a **hypothesis** the reader is trying; what the observer's
934
934
  own walk **derives** (`ShapeDistance`, see below); what the crossings **bound**, the geometric middle of the
935
935
  interval they leave, since any distance in it draws every crossing correctly; and, for the majority that
936
936
  establishes nothing, a **conventional** few metres — in front of everything that was not declared to hide it,
@@ -972,7 +972,7 @@ case's `sighting.json` from its `RR0Event`).
972
972
  the editor reaches through to its public `ufoElement` property for the actual canvas/timeline/appearance work
973
973
  (the toolbar edits the exact same `Sighting` instance the nested scene renders from, so an observer/time/
974
974
  appearance change needs no separate sync step to reach the sky), while `SightingElement` reaches through to
975
- its public `sightingData`/`currentTerrainAttribution` for its own toolbar (witness picker) and info panel.
975
+ its public `sightingData`/`currentTerrainAttribution` for its own toolbar (observer picker) and info panel.
976
976
  - Playback linearly interpolates shapes between a source's surrounding keyframes for smooth motion
977
977
  (`Timeline.getInterpolatedShapeAt`/`Shape.lerpShape`), holding at the ends of its recorded range.
978
978
  - Recording samples the pointer position at a configurable rate via `requestAnimationFrame`, not on every
@@ -0,0 +1 @@
1
+ const e={play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",currentPosition:"Position actuelle",duration:"Durée",switchToElapsed:"cliquer pour afficher la durée écoulée",switchToClockTime:"cliquer pour afficher l'heure de l'observation",fullscreen:"Plein écran",exitFullscreen:"Quitter le plein écran",showObserverMap:"Voir où était l'observateur",hideObserverMap:"Masquer où était l'observateur",mapImageryUnavailable:"Vue aérienne indisponible",showReferences:"Voir les photos des lieux",hideReferences:"Masquer les photos des lieux",referenceOpacity:"Opacité des photos",showMilestones:"Voir les moments du récit",hideMilestones:"Masquer les moments du récit",observerHere:"L'observateur, ici",decorHere:"Élément de décor"};export{e as ufoMessages_fr};