@faclon-labs/app-sdk 0.1.0 → 0.3.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.
- package/README.md +39 -1
- package/dist/IOSenseApp.js +26 -6
- package/dist/SignIn.js +2 -2
- package/dist/auth.d.ts +7 -0
- package/dist/auth.js +35 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/map/globe.d.ts +5 -0
- package/dist/map/globe.js +5 -0
- package/dist/map/index.d.ts +21 -0
- package/dist/map/index.js +21 -0
- package/dist/map/source.d.ts +48 -0
- package/dist/map/source.js +73 -0
- package/dist/map/testing.d.ts +5 -0
- package/dist/map/testing.js +5 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/package.json +55 -5
- package/vendor/SOURCE.txt +1 -0
- package/vendor/map-engine/cesium/camera.d.ts +10 -0
- package/vendor/map-engine/cesium/camera.js +25 -0
- package/vendor/map-engine/cesium/index.d.ts +2 -0
- package/vendor/map-engine/cesium/index.js +2 -0
- package/vendor/map-engine/cesium/renderer.d.ts +149 -0
- package/vendor/map-engine/cesium/renderer.js +837 -0
- package/vendor/map-engine/commands/index.d.ts +2 -0
- package/vendor/map-engine/commands/index.js +2 -0
- package/vendor/map-engine/commands/post-message.d.ts +32 -0
- package/vendor/map-engine/commands/post-message.js +77 -0
- package/vendor/map-engine/commands/webmcp.d.ts +17 -0
- package/vendor/map-engine/commands/webmcp.js +59 -0
- package/vendor/map-engine/core/commands.d.ts +82 -0
- package/vendor/map-engine/core/commands.js +46 -0
- package/vendor/map-engine/core/emitter.d.ts +9 -0
- package/vendor/map-engine/core/emitter.js +38 -0
- package/vendor/map-engine/core/equal.d.ts +2 -0
- package/vendor/map-engine/core/equal.js +28 -0
- package/vendor/map-engine/core/errors.d.ts +20 -0
- package/vendor/map-engine/core/errors.js +34 -0
- package/vendor/map-engine/core/geometry-family.d.ts +3 -0
- package/vendor/map-engine/core/geometry-family.js +13 -0
- package/vendor/map-engine/core/index.d.ts +4 -0
- package/vendor/map-engine/core/index.js +3 -0
- package/vendor/map-engine/core/types.d.ts +709 -0
- package/vendor/map-engine/core/types.js +7 -0
- package/vendor/map-engine/data/annotations.d.ts +3 -0
- package/vendor/map-engine/data/annotations.js +63 -0
- package/vendor/map-engine/data/contracts.d.ts +172 -0
- package/vendor/map-engine/data/contracts.js +12 -0
- package/vendor/map-engine/data/index.d.ts +4 -0
- package/vendor/map-engine/data/index.js +3 -0
- package/vendor/map-engine/data/schema.d.ts +7 -0
- package/vendor/map-engine/data/schema.js +40 -0
- package/vendor/map-engine/geofence/evaluator.d.ts +35 -0
- package/vendor/map-engine/geofence/evaluator.js +172 -0
- package/vendor/map-engine/geofence/history.d.ts +9 -0
- package/vendor/map-engine/geofence/history.js +19 -0
- package/vendor/map-engine/geofence/index.d.ts +2 -0
- package/vendor/map-engine/geofence/index.js +2 -0
- package/vendor/map-engine/index.d.ts +27 -0
- package/vendor/map-engine/index.js +14 -0
- package/vendor/map-engine/layers/index.d.ts +1 -0
- package/vendor/map-engine/layers/index.js +1 -0
- package/vendor/map-engine/layers/layers.d.ts +49 -0
- package/vendor/map-engine/layers/layers.js +316 -0
- package/vendor/map-engine/maplibre/deck-host.d.ts +105 -0
- package/vendor/map-engine/maplibre/deck-host.js +401 -0
- package/vendor/map-engine/maplibre/icons.d.ts +17 -0
- package/vendor/map-engine/maplibre/icons.js +65 -0
- package/vendor/map-engine/maplibre/index.d.ts +3 -0
- package/vendor/map-engine/maplibre/index.js +2 -0
- package/vendor/map-engine/maplibre/map-like.d.ts +56 -0
- package/vendor/map-engine/maplibre/map-like.js +1 -0
- package/vendor/map-engine/maplibre/renderer.d.ts +173 -0
- package/vendor/map-engine/maplibre/renderer.js +889 -0
- package/vendor/map-engine/maplibre/style.d.ts +84 -0
- package/vendor/map-engine/maplibre/style.js +493 -0
- package/vendor/map-engine/present/decorations.d.ts +25 -0
- package/vendor/map-engine/present/decorations.js +115 -0
- package/vendor/map-engine/present/index.d.ts +2 -0
- package/vendor/map-engine/present/index.js +2 -0
- package/vendor/map-engine/present/present.d.ts +47 -0
- package/vendor/map-engine/present/present.js +149 -0
- package/vendor/map-engine/query/index.d.ts +1 -0
- package/vendor/map-engine/query/index.js +1 -0
- package/vendor/map-engine/query/planner.d.ts +26 -0
- package/vendor/map-engine/query/planner.js +47 -0
- package/vendor/map-engine/react/MapView.d.ts +87 -0
- package/vendor/map-engine/react/MapView.js +270 -0
- package/vendor/map-engine/react/context.d.ts +39 -0
- package/vendor/map-engine/react/context.js +94 -0
- package/vendor/map-engine/react/index.d.ts +2 -0
- package/vendor/map-engine/react/index.js +2 -0
- package/vendor/map-engine/react/store.d.ts +19 -0
- package/vendor/map-engine/react/store.js +18 -0
- package/vendor/map-engine/realtime/apply.d.ts +74 -0
- package/vendor/map-engine/realtime/apply.js +155 -0
- package/vendor/map-engine/realtime/index.d.ts +1 -0
- package/vendor/map-engine/realtime/index.js +1 -0
- package/vendor/map-engine/render/index.d.ts +1 -0
- package/vendor/map-engine/render/index.js +1 -0
- package/vendor/map-engine/render/offline.d.ts +8 -0
- package/vendor/map-engine/render/offline.js +47 -0
- package/vendor/map-engine/render/renderer.d.ts +74 -0
- package/vendor/map-engine/render/renderer.js +1 -0
- package/vendor/map-engine/render/terrarium.d.ts +48 -0
- package/vendor/map-engine/render/terrarium.js +110 -0
- package/vendor/map-engine/runtime/drawing.d.ts +28 -0
- package/vendor/map-engine/runtime/drawing.js +58 -0
- package/vendor/map-engine/runtime/index.d.ts +5 -0
- package/vendor/map-engine/runtime/index.js +2 -0
- package/vendor/map-engine/runtime/offline.d.ts +24 -0
- package/vendor/map-engine/runtime/offline.js +37 -0
- package/vendor/map-engine/runtime/overlays.d.ts +25 -0
- package/vendor/map-engine/runtime/overlays.js +65 -0
- package/vendor/map-engine/runtime/runtime.d.ts +4 -0
- package/vendor/map-engine/runtime/runtime.js +2105 -0
- package/vendor/map-engine/runtime/types.d.ts +407 -0
- package/vendor/map-engine/runtime/types.js +1 -0
- package/vendor/map-engine/scene/index.d.ts +1 -0
- package/vendor/map-engine/scene/index.js +1 -0
- package/vendor/map-engine/scene/scene.d.ts +31 -0
- package/vendor/map-engine/scene/scene.js +216 -0
- package/vendor/map-engine/selection/index.d.ts +1 -0
- package/vendor/map-engine/selection/index.js +1 -0
- package/vendor/map-engine/selection/selection.d.ts +12 -0
- package/vendor/map-engine/selection/selection.js +28 -0
- package/vendor/map-engine/spatial/geojson.d.ts +15 -0
- package/vendor/map-engine/spatial/geojson.js +83 -0
- package/vendor/map-engine/spatial/geometry.d.ts +13 -0
- package/vendor/map-engine/spatial/geometry.js +206 -0
- package/vendor/map-engine/spatial/import.d.ts +27 -0
- package/vendor/map-engine/spatial/import.js +96 -0
- package/vendor/map-engine/spatial/index.d.ts +38 -0
- package/vendor/map-engine/spatial/index.js +37 -0
- package/vendor/map-engine/spatial/line-of-sight.d.ts +35 -0
- package/vendor/map-engine/spatial/line-of-sight.js +43 -0
- package/vendor/map-engine/spatial/measure.d.ts +28 -0
- package/vendor/map-engine/spatial/measure.js +90 -0
- package/vendor/map-engine/spatial/operations.d.ts +21 -0
- package/vendor/map-engine/spatial/operations.js +171 -0
- package/vendor/map-engine/spatial/query.d.ts +17 -0
- package/vendor/map-engine/spatial/query.js +88 -0
- package/vendor/map-engine/spatial/spatial-index.d.ts +24 -0
- package/vendor/map-engine/spatial/spatial-index.js +36 -0
- package/vendor/map-engine/spatial/volume-mesh.d.ts +19 -0
- package/vendor/map-engine/spatial/volume-mesh.js +56 -0
- package/vendor/map-engine/temporal/index.d.ts +3 -0
- package/vendor/map-engine/temporal/index.js +3 -0
- package/vendor/map-engine/temporal/journal.d.ts +20 -0
- package/vendor/map-engine/temporal/journal.js +34 -0
- package/vendor/map-engine/temporal/playback.d.ts +41 -0
- package/vendor/map-engine/temporal/playback.js +80 -0
- package/vendor/map-engine/temporal/tracks.d.ts +33 -0
- package/vendor/map-engine/temporal/tracks.js +119 -0
- package/vendor/map-engine/testing/annotation-store.d.ts +24 -0
- package/vendor/map-engine/testing/annotation-store.js +43 -0
- package/vendor/map-engine/testing/contracts.d.ts +41 -0
- package/vendor/map-engine/testing/contracts.js +328 -0
- package/vendor/map-engine/testing/fixtures-monitoring.d.ts +22 -0
- package/vendor/map-engine/testing/fixtures-monitoring.js +103 -0
- package/vendor/map-engine/testing/fixtures.d.ts +18 -0
- package/vendor/map-engine/testing/fixtures.js +28 -0
- package/vendor/map-engine/testing/index.d.ts +7 -0
- package/vendor/map-engine/testing/index.js +7 -0
- package/vendor/map-engine/testing/mock-ontology.d.ts +13 -0
- package/vendor/map-engine/testing/mock-ontology.js +24 -0
- package/vendor/map-engine/testing/mock-provider.d.ts +97 -0
- package/vendor/map-engine/testing/mock-provider.js +369 -0
- package/vendor/map-engine/testing/test-renderer.d.ts +72 -0
- package/vendor/map-engine/testing/test-renderer.js +153 -0
- package/vendor/map-engine/viewport/index.d.ts +1 -0
- package/vendor/map-engine/viewport/index.js +1 -0
- package/vendor/map-engine/viewport/viewport.d.ts +19 -0
- package/vendor/map-engine/viewport/viewport.js +102 -0
- package/vendor/map-provider-uns/client.d.ts +75 -0
- package/vendor/map-provider-uns/client.js +111 -0
- package/vendor/map-provider-uns/index.d.ts +2 -0
- package/vendor/map-provider-uns/index.js +2 -0
- package/vendor/map-provider-uns/provider.d.ts +131 -0
- package/vendor/map-provider-uns/provider.js +561 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MapRuntime's public contract: its interface, events, dependencies, options and stats. The orchestration that
|
|
3
|
+
* implements it lives in runtime.ts; how live frames are applied in ../realtime; query planning in ../query.
|
|
4
|
+
*/
|
|
5
|
+
import type { MapEngineError } from '../core/errors.js';
|
|
6
|
+
import type { BasemapConfig, BoundingBox, Geofence, GeofenceEvent, Geometry, LayerSource, MapLayer, MapViewport, Padding, Position, ScenePatch, SceneState, SelectionContext, Size, SpatialObject, SpatialQuery, TimeSetting, TimeState, TerrainConfig, TilesetConfig, MapConfig, Unsubscribe, Annotation, Highlight, LayerStatus, Proposal, TimeRange } from '../core/types.js';
|
|
7
|
+
import type { CommandResult, MapCommand } from '../core/commands.js';
|
|
8
|
+
import type { AnnotationProvider, AuthContext, ElevationProvider, JournalEntry, JournalProvider, LinkProvider, OntologySpatialResolver, RealtimeProvider, SpatialDataProvider, TrackProvider } from '../data/contracts.js';
|
|
9
|
+
import type { AgeState } from '../present/present.js';
|
|
10
|
+
import type { Measurement } from '../spatial/measure.js';
|
|
11
|
+
import type { PickEvent, Renderer } from '../render/renderer.js';
|
|
12
|
+
import type { SpatialIndex } from '../spatial/spatial-index.js';
|
|
13
|
+
import type { DrawingState, DrawKind } from './drawing.js';
|
|
14
|
+
import type { SightResult } from '../spatial/line-of-sight.js';
|
|
15
|
+
export type RuntimeState = 'idle' | 'loading' | 'ready' | 'error' | 'destroyed';
|
|
16
|
+
export interface MapRuntimeEvents extends Record<string, unknown> {
|
|
17
|
+
state: RuntimeState;
|
|
18
|
+
viewport: MapViewport;
|
|
19
|
+
selection: SelectionContext;
|
|
20
|
+
hover: PickEvent | null;
|
|
21
|
+
mapClick: {
|
|
22
|
+
position: Position;
|
|
23
|
+
};
|
|
24
|
+
/** Every click: its position and what it hit (null on empty map). Fires even when selection is paused. */
|
|
25
|
+
click: {
|
|
26
|
+
position: Position;
|
|
27
|
+
hit: {
|
|
28
|
+
objectId: string;
|
|
29
|
+
layerId: string;
|
|
30
|
+
} | null;
|
|
31
|
+
};
|
|
32
|
+
/** Every patch sent to the renderer, for observers such as inspectors or dev tools. */
|
|
33
|
+
scene: ScenePatch;
|
|
34
|
+
/** Failures the application should know about (provider, renderer). The runtime keeps the last good scene. */
|
|
35
|
+
error: MapEngineError;
|
|
36
|
+
/** Recoverable problems, e.g. an object with invalid geometry that was left out. */
|
|
37
|
+
warning: MapEngineError;
|
|
38
|
+
/** The map switched between live and replay, or moved to another replay time. */
|
|
39
|
+
time: TimeState;
|
|
40
|
+
/** A live geofence transition (enter, exit, dwell). Replay events are read with getGeofenceEvents(). */
|
|
41
|
+
geofence: GeofenceEvent;
|
|
42
|
+
/** The geofence set changed (setGeofences). */
|
|
43
|
+
geofences: Geofence[];
|
|
44
|
+
/** The camera moved a frame (pan, zoom, animation). Use it to keep overlays such as popups on their object. */
|
|
45
|
+
move: null;
|
|
46
|
+
/** Auditable (doc §76): a layer was added, changed or removed; `fields` names what changed (e.g. source.filter). */
|
|
47
|
+
layerChanged: {
|
|
48
|
+
action: 'added' | 'updated' | 'removed';
|
|
49
|
+
layerId: string;
|
|
50
|
+
fields?: string[];
|
|
51
|
+
};
|
|
52
|
+
/** Auditable (doc §76): a spatial query ran through runtime.query(). */
|
|
53
|
+
spatialQuery: {
|
|
54
|
+
query: SpatialQuery;
|
|
55
|
+
count: number;
|
|
56
|
+
durationMs: number;
|
|
57
|
+
error?: string;
|
|
58
|
+
};
|
|
59
|
+
/** Drawing started, gained a point, or ended (null). */
|
|
60
|
+
drawing: DrawingState | null;
|
|
61
|
+
/** Auditable (doc §76): the user finished drawing. The application decides what the geometry means. */
|
|
62
|
+
geometryDrawn: {
|
|
63
|
+
kind: DrawKind;
|
|
64
|
+
geometry: Geometry;
|
|
65
|
+
};
|
|
66
|
+
/** Auditable (doc §76): what a layer loads was narrowed or widened (source filter, where, spatialQuery or objectIds). */
|
|
67
|
+
filterChanged: {
|
|
68
|
+
layerId: string;
|
|
69
|
+
before: LayerFilter;
|
|
70
|
+
after: LayerFilter;
|
|
71
|
+
};
|
|
72
|
+
/** Auditable (doc §76): a geofence was created, changed or removed through setGeofences(). */
|
|
73
|
+
geofenceChanged: {
|
|
74
|
+
action: 'created' | 'updated' | 'removed';
|
|
75
|
+
geofenceId: string;
|
|
76
|
+
};
|
|
77
|
+
/** Auditable (doc §76): the application opened an object's details (inspect()). */
|
|
78
|
+
objectInspected: {
|
|
79
|
+
objectId: string;
|
|
80
|
+
source: string;
|
|
81
|
+
layerIds: string[];
|
|
82
|
+
};
|
|
83
|
+
/** Auditable: an annotation was created, changed or removed, and by whom (`user`, `agent:<id>`…). */
|
|
84
|
+
annotation: {
|
|
85
|
+
action: 'created' | 'updated' | 'removed';
|
|
86
|
+
id: string;
|
|
87
|
+
annotation?: Annotation;
|
|
88
|
+
origin: string;
|
|
89
|
+
};
|
|
90
|
+
/** Auditable: a proposal was drawn, accepted (it became an annotation), rejected or cleared. */
|
|
91
|
+
proposal: {
|
|
92
|
+
action: 'proposed' | 'accepted' | 'rejected' | 'cleared';
|
|
93
|
+
proposal: Proposal;
|
|
94
|
+
reason?: string;
|
|
95
|
+
by?: string;
|
|
96
|
+
annotationId?: string;
|
|
97
|
+
};
|
|
98
|
+
/** The highlight changed (null: cleared or expired). */
|
|
99
|
+
highlight: Highlight | null;
|
|
100
|
+
/** A layer's status changed (loading, ok, denied, error, missing-capability). */
|
|
101
|
+
layerStatus: {
|
|
102
|
+
layerId: string;
|
|
103
|
+
status: LayerStatus;
|
|
104
|
+
reason?: string;
|
|
105
|
+
};
|
|
106
|
+
/** Auditable (doc 28 G6): a command list ran or was refused, with its origin. */
|
|
107
|
+
command: {
|
|
108
|
+
commands: MapCommand[];
|
|
109
|
+
origin: string;
|
|
110
|
+
result: CommandResult;
|
|
111
|
+
};
|
|
112
|
+
/** A command asked the application to open its details view for an object. */
|
|
113
|
+
inspectRequested: {
|
|
114
|
+
objectId: string;
|
|
115
|
+
origin: string;
|
|
116
|
+
};
|
|
117
|
+
/** A command asked the application's timeline to play (with a speed) or pause. */
|
|
118
|
+
playbackRequested: {
|
|
119
|
+
action: 'play' | 'pause';
|
|
120
|
+
speed?: number;
|
|
121
|
+
origin: string;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** The parts of a layer source that decide which objects it loads. */
|
|
125
|
+
export type LayerFilter = Pick<LayerSource, 'filter' | 'where' | 'spatialQuery' | 'objectIds'>;
|
|
126
|
+
/**
|
|
127
|
+
* What the engine knows about one object (doc §34): its current state (as shown, so the replayed state in replay),
|
|
128
|
+
* the layers showing it, whether it is selected, and the map's time. The application decides how to display it.
|
|
129
|
+
*/
|
|
130
|
+
export interface ObjectInspection {
|
|
131
|
+
object: SpatialObject;
|
|
132
|
+
layerIds: string[];
|
|
133
|
+
selected: boolean;
|
|
134
|
+
time: TimeState;
|
|
135
|
+
/** Under the age thresholds of the first layer showing it that has some, at the map's time. */
|
|
136
|
+
age?: {
|
|
137
|
+
state: AgeState;
|
|
138
|
+
seconds: number;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** What an id in a text (an assistant reply, an alert) points at, for the person reading it (doc 28 G8). */
|
|
142
|
+
export interface ResolvedReference {
|
|
143
|
+
/** shown: on the map now; loaded: on the client but not drawn (hidden layer, outside its time); available: the
|
|
144
|
+
* provider has it (not loaded here); unavailable: no such object, or not visible to this user. */
|
|
145
|
+
status: 'shown' | 'loaded' | 'available' | 'unavailable';
|
|
146
|
+
object?: SpatialObject;
|
|
147
|
+
}
|
|
148
|
+
export interface LineOfSightOptions {
|
|
149
|
+
/** Ground samples along the path. Default 128 (8 … 1024). */
|
|
150
|
+
samples?: number;
|
|
151
|
+
/** Height above the ground of an end without altitude. Defaults 2 m. */
|
|
152
|
+
fromHeightAboveGround?: number;
|
|
153
|
+
toHeightAboveGround?: number;
|
|
154
|
+
signal?: AbortSignal;
|
|
155
|
+
}
|
|
156
|
+
/** Where flyTo() should take the camera. */
|
|
157
|
+
export interface FlyToOptions {
|
|
158
|
+
/** Zoom for a point. Default: the current zoom, at least 15. For lines and areas, the most it may zoom in (default 18). */
|
|
159
|
+
zoom?: number;
|
|
160
|
+
/** Pixels kept clear at each side when framing a line or an area. Default 40. */
|
|
161
|
+
padding?: Padding;
|
|
162
|
+
/** Default true. */
|
|
163
|
+
animate?: boolean;
|
|
164
|
+
}
|
|
165
|
+
export interface MapRuntimeDeps {
|
|
166
|
+
renderer: Renderer;
|
|
167
|
+
provider: SpatialDataProvider;
|
|
168
|
+
/** Snapshot + stream (ADR-015): after each load, layers subscribe for incremental changes. */
|
|
169
|
+
realtime?: RealtimeProvider;
|
|
170
|
+
/** Resolves each layer's ontology type to a spatial binding before it is queried (doc 12, doc 20). */
|
|
171
|
+
ontology?: OntologySpatialResolver;
|
|
172
|
+
/** Historical positions: trails (track layers) and replay (doc 07). */
|
|
173
|
+
history?: TrackProvider;
|
|
174
|
+
/** Ground heights for sampleElevation() (ADR-028). Without it, the renderer's terrain answers if it can. */
|
|
175
|
+
elevation?: ElevationProvider;
|
|
176
|
+
/** Relations between objects, for link layers (doc 28 G4). */
|
|
177
|
+
links?: LinkProvider;
|
|
178
|
+
/** The event journal: replay rebuilds whole objects at a time, not only positions (doc 29 §7–8). */
|
|
179
|
+
journal?: JournalProvider;
|
|
180
|
+
/** Where annotations are kept and shared (doc 28 G10). Without it they live in this runtime only. */
|
|
181
|
+
annotations?: AnnotationProvider;
|
|
182
|
+
/** Who is looking; handed to every provider that accepts it (doc 28 G13). Change it with setAuth(). */
|
|
183
|
+
auth?: AuthContext;
|
|
184
|
+
spatialIndex?: SpatialIndex;
|
|
185
|
+
}
|
|
186
|
+
export interface MapRuntimeStats {
|
|
187
|
+
loads: number;
|
|
188
|
+
providerCalls: number;
|
|
189
|
+
lastLoadMs: number;
|
|
190
|
+
lastPlan: {
|
|
191
|
+
calls: number;
|
|
192
|
+
layers: number;
|
|
193
|
+
deduplicated: number;
|
|
194
|
+
pushedDown: boolean;
|
|
195
|
+
} | null;
|
|
196
|
+
realtime: {
|
|
197
|
+
subscriptions: number;
|
|
198
|
+
received: number;
|
|
199
|
+
applied: number;
|
|
200
|
+
stale: number;
|
|
201
|
+
outOfView: number;
|
|
202
|
+
frames: number;
|
|
203
|
+
reconnects: number;
|
|
204
|
+
};
|
|
205
|
+
temporal: {
|
|
206
|
+
trackFetches: number;
|
|
207
|
+
tracksLoaded: number;
|
|
208
|
+
replayFrames: number;
|
|
209
|
+
trailPointsAppended: number;
|
|
210
|
+
};
|
|
211
|
+
geofence: {
|
|
212
|
+
geofences: number;
|
|
213
|
+
liveEvents: number;
|
|
214
|
+
replayEvents: number;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
export interface MapRuntimeOptions {
|
|
218
|
+
/** Delay after the last viewport or layer change before querying. Default 150 ms. */
|
|
219
|
+
debounceMs?: number;
|
|
220
|
+
/** Used when the renderer reports a zero size (hidden container, headless tests). */
|
|
221
|
+
fallbackSize?: Size;
|
|
222
|
+
now?: () => number;
|
|
223
|
+
/** Schedules one coalesced realtime frame. Default: requestAnimationFrame, else a 16 ms timer. */
|
|
224
|
+
frame?: (callback: () => void) => void;
|
|
225
|
+
/** Wait before reloading after a realtime error (snapshot, then stream again). Default 1000 ms. */
|
|
226
|
+
reconnectMs?: number;
|
|
227
|
+
/**
|
|
228
|
+
* How far ahead of this clock an `observedAt` may be before it is treated as clock skew (doc 14). Such a time is
|
|
229
|
+
* reported once per object (CLOCK_SKEW warning) and not trusted for ordering, so later real updates still apply.
|
|
230
|
+
* Default 60 000 ms.
|
|
231
|
+
*/
|
|
232
|
+
clockSkewToleranceMs?: number;
|
|
233
|
+
}
|
|
234
|
+
export interface MapRuntime {
|
|
235
|
+
/** Shows the map in a container. After unmount() it can be mounted again (same or another container). */
|
|
236
|
+
mount(container: HTMLElement): Promise<void>;
|
|
237
|
+
/**
|
|
238
|
+
* Takes the map off screen and releases the renderer (GPU context, DOM, listeners), live subscriptions and
|
|
239
|
+
* timers — but keeps layers, viewport, selection, time, geofences and loaded objects, so mount() shows the same
|
|
240
|
+
* map again at once. State goes back to 'idle'. Use it when a view is hidden or moved; destroy() is the end.
|
|
241
|
+
*/
|
|
242
|
+
unmount(): void;
|
|
243
|
+
/** True between a completed mount() and unmount()/destroy(). */
|
|
244
|
+
isMounted(): boolean;
|
|
245
|
+
/**
|
|
246
|
+
* Changes the basemap and keeps everything else (camera, layers, selection, time, geofence log). Renderers that
|
|
247
|
+
* can swap styles keep their GPU context; others are remounted in the same container.
|
|
248
|
+
*/
|
|
249
|
+
setBasemap(basemap: BasemapConfig): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Switches renderer (2D ↔ 3D, ADR-027) and keeps the map: layers, camera, selection, time, geofences and loaded
|
|
252
|
+
* objects. The old renderer is released. Capabilities are re-checked for the new one.
|
|
253
|
+
*/
|
|
254
|
+
setRenderer(renderer: Renderer): Promise<void>;
|
|
255
|
+
/** Flat map or globe (needs the renderer's `globe` capability). */
|
|
256
|
+
setProjection(projection: NonNullable<MapConfig['projection']>): void;
|
|
257
|
+
/** Terrain relief on (a config) or off (null) (needs the renderer's `terrain` capability). */
|
|
258
|
+
setTerrain(terrain: TerrainConfig | null): void;
|
|
259
|
+
/** Replaces the 3D Tiles scenery (needs the renderer's `tiles3d` capability; reported otherwise). */
|
|
260
|
+
setTilesets(tilesets: TilesetConfig[]): void;
|
|
261
|
+
/**
|
|
262
|
+
* Ground heights in metres above sea level under positions (null where unknown): from `deps.elevation`, else
|
|
263
|
+
* the renderer's terrain. Rejects with MISSING_CAPABILITY when neither can answer.
|
|
264
|
+
*/
|
|
265
|
+
sampleElevation(positions: Position[], signal?: AbortSignal): Promise<Array<number | null>>;
|
|
266
|
+
/**
|
|
267
|
+
* Line of sight over the ground (phase 3E): is `to` visible from `from`? Ends with an `altitude` use it (metres
|
|
268
|
+
* above sea level); ends without stand on the ground plus a height (default 2 m). The ground comes from
|
|
269
|
+
* sampleElevation (Earth curvature and refraction included); it rejects with MISSING_CAPABILITY without ground
|
|
270
|
+
* heights. Large-area visibility (viewsheds) belongs to a server (§77).
|
|
271
|
+
*/
|
|
272
|
+
lineOfSight(from: Position, to: Position, options?: LineOfSightOptions): Promise<SightResult>;
|
|
273
|
+
destroy(): void;
|
|
274
|
+
readonly state: RuntimeState;
|
|
275
|
+
getViewport(): MapViewport;
|
|
276
|
+
/** Screen pixel (relative to the map container) of a position, or null when the renderer cannot say. */
|
|
277
|
+
project(position: Position): {
|
|
278
|
+
x: number;
|
|
279
|
+
y: number;
|
|
280
|
+
} | null;
|
|
281
|
+
/** Call when the container size changed; the renderer re-measures and the visible area is re-queried. */
|
|
282
|
+
resize(): void;
|
|
283
|
+
setViewport(viewport: MapViewport, options?: {
|
|
284
|
+
animate?: boolean;
|
|
285
|
+
}): void;
|
|
286
|
+
fitBounds(bounds: BoundingBox, options?: {
|
|
287
|
+
padding?: Padding;
|
|
288
|
+
animate?: boolean;
|
|
289
|
+
}): void;
|
|
290
|
+
/**
|
|
291
|
+
* Moves the camera to an object (by id) or a position (doc §35: search → flyTo → select). A point is centred;
|
|
292
|
+
* a line or area is framed. An object not loaded on the client is asked from the provider. Resolves false when
|
|
293
|
+
* the object does not exist (or is not authorized). Does not select: call selectObject() for that.
|
|
294
|
+
*/
|
|
295
|
+
flyTo(to: string | Position, options?: FlyToOptions): Promise<boolean>;
|
|
296
|
+
getLayers(): MapLayer[];
|
|
297
|
+
addLayer(layer: MapLayer): void;
|
|
298
|
+
updateLayer(id: string, patch: Partial<Omit<MapLayer, 'id'>>): void;
|
|
299
|
+
removeLayer(id: string): void;
|
|
300
|
+
setLayerVisibility(id: string, visible: boolean): void;
|
|
301
|
+
setLayerZIndex(id: string, zIndex: number): void;
|
|
302
|
+
selectObject(objectId: string, source?: string): void;
|
|
303
|
+
clearSelection(source?: string): void;
|
|
304
|
+
/** Controlled mode: the application owns selection and pushes it in. */
|
|
305
|
+
setSelection(selection: Pick<SelectionContext, 'objectIds' | 'source'> & {
|
|
306
|
+
primaryObjectId?: string;
|
|
307
|
+
}): void;
|
|
308
|
+
getSelection(): SelectionContext;
|
|
309
|
+
/** Pause or resume click-to-select (e.g. while an application tool uses clicks). Default: enabled. */
|
|
310
|
+
setSelectable(enabled: boolean): void;
|
|
311
|
+
getScene(): SceneState;
|
|
312
|
+
/** An object currently loaded on the client (not a data query). */
|
|
313
|
+
getObject(objectId: string): SpatialObject | undefined;
|
|
314
|
+
/** Details of a loaded object for an inspector (doc §34), reported as `objectInspected`; undefined when not loaded. */
|
|
315
|
+
inspect(objectId: string, source?: string): ObjectInspection | undefined;
|
|
316
|
+
/** Filters objects already loaded on the client. For data beyond the viewport, use query(). */
|
|
317
|
+
queryLoaded(query: SpatialQuery): SpatialObject[];
|
|
318
|
+
/** Runs a spatial query through the provider (authorized data only), validated, and reports it (`spatialQuery`). */
|
|
319
|
+
query(query: SpatialQuery, signal?: AbortSignal): Promise<SpatialObject[]>;
|
|
320
|
+
/**
|
|
321
|
+
* Starts drawing (doc §37). Map clicks add points and do not select. Point, rectangle and circle finish by
|
|
322
|
+
* themselves; lines and polygons finish with finishDrawing(). The result arrives as `geometryDrawn`.
|
|
323
|
+
*/
|
|
324
|
+
startDrawing(kind: DrawKind): void;
|
|
325
|
+
/** Adds a point as if the map was clicked there (keyboard input, snapping, tests). */
|
|
326
|
+
addDrawingPoint(position: Position): void;
|
|
327
|
+
undoDrawingPoint(): void;
|
|
328
|
+
/** Finishes and returns the geometry, or null when there are too few points (drawing continues). */
|
|
329
|
+
finishDrawing(): Geometry | null;
|
|
330
|
+
cancelDrawing(): void;
|
|
331
|
+
getDrawing(): DrawingState | null;
|
|
332
|
+
/** Re-query every visible layer now. */
|
|
333
|
+
refresh(): Promise<void>;
|
|
334
|
+
/** Resolves when no query is scheduled or in flight. */
|
|
335
|
+
whenIdle(): Promise<void>;
|
|
336
|
+
/** What the engine is doing: plans, provider calls, realtime throughput. For dev tools and observability (doc 18). */
|
|
337
|
+
getStats(): MapRuntimeStats;
|
|
338
|
+
/**
|
|
339
|
+
* Live, or the world as it was at a time (replay). The application owns time (ADR-010): it calls this from its
|
|
340
|
+
* timeline or a Playback. Replay fetches tracks for the range once; moving `at` inside it only re-interpolates.
|
|
341
|
+
*/
|
|
342
|
+
setTime(time: TimeSetting): void;
|
|
343
|
+
getTime(): TimeState;
|
|
344
|
+
/** Replaces the geofences (doc 15). Client evaluation is for display; authoritative alerts are server-side. */
|
|
345
|
+
setGeofences(geofences: Geofence[]): void;
|
|
346
|
+
getGeofences(): Geofence[];
|
|
347
|
+
/** Live: recent transitions. Replay: transitions over the replay range up to the replay time. Newest first. */
|
|
348
|
+
getGeofenceEvents(limit?: number): GeofenceEvent[];
|
|
349
|
+
/** Which objects are inside each geofence now (live) or at the replay time. */
|
|
350
|
+
getGeofenceOccupancy(): Record<string, string[]>;
|
|
351
|
+
/** Length, area and bearing of a geometry, on the ground (doc 28 G9). */
|
|
352
|
+
measure(geometry: Geometry): Measurement;
|
|
353
|
+
/** Where an id stands for this user, for object references in text (doc 28 G8). */
|
|
354
|
+
resolveReference(objectId: string): Promise<ResolvedReference>;
|
|
355
|
+
/** What each layer shows right now (doc 28 G13): denied layers say so instead of failing. */
|
|
356
|
+
getLayerStatus(layerId: string): LayerStatus | undefined;
|
|
357
|
+
/** Who is looking: handed to every provider that accepts it, then the map reloads with what they return. */
|
|
358
|
+
setAuth(auth: AuthContext | null): void;
|
|
359
|
+
/** Emphasis on objects without changing the selection, for a while or until cleared (doc 28 G7). */
|
|
360
|
+
highlight(objectIds: string[], options?: {
|
|
361
|
+
durationMs?: number;
|
|
362
|
+
color?: string;
|
|
363
|
+
origin?: string;
|
|
364
|
+
}): void;
|
|
365
|
+
clearHighlight(): void;
|
|
366
|
+
getHighlight(): Highlight | null;
|
|
367
|
+
/** A suggested geometry, drawn as "not confirmed" (doc 28 G7). Returns it with its id. */
|
|
368
|
+
propose(proposal: {
|
|
369
|
+
geometry: Geometry;
|
|
370
|
+
label?: string;
|
|
371
|
+
note?: string;
|
|
372
|
+
origin?: string;
|
|
373
|
+
id?: string;
|
|
374
|
+
}): Proposal;
|
|
375
|
+
/** A person confirms a proposal: it becomes an annotation (saved through deps.annotations when there is one). */
|
|
376
|
+
acceptProposal(id: string, options?: {
|
|
377
|
+
by?: string;
|
|
378
|
+
label?: string;
|
|
379
|
+
}): Promise<Annotation>;
|
|
380
|
+
/** A person turns a proposal down: it leaves the map; the rejection is an event its origin can read. */
|
|
381
|
+
rejectProposal(id: string, options?: {
|
|
382
|
+
by?: string;
|
|
383
|
+
reason?: string;
|
|
384
|
+
}): void;
|
|
385
|
+
getProposals(): Proposal[];
|
|
386
|
+
/** Removes proposals (all, or those from one origin). */
|
|
387
|
+
clearProposals(origin?: string): void;
|
|
388
|
+
/** First-class annotations (doc 29 §5): drawn by the engine, kept by deps.annotations when there is one. */
|
|
389
|
+
addAnnotation(annotation: Omit<Annotation, 'id' | 'createdAt'> & {
|
|
390
|
+
id?: string;
|
|
391
|
+
}, origin?: string): Promise<Annotation>;
|
|
392
|
+
updateAnnotation(id: string, patch: Partial<Omit<Annotation, 'id'>>, origin?: string): Promise<Annotation>;
|
|
393
|
+
removeAnnotation(id: string, origin?: string): Promise<void>;
|
|
394
|
+
getAnnotations(): Annotation[];
|
|
395
|
+
/** Shows or hides the annotations (they stay kept and selectable through the API). Default shown. */
|
|
396
|
+
setAnnotationsVisible(visible: boolean): void;
|
|
397
|
+
/**
|
|
398
|
+
* Runs map commands (doc 28 G6) as one unit: checked first, then applied in order, or refused whole. Each run is
|
|
399
|
+
* an auditable `command` event with its origin (default 'user').
|
|
400
|
+
*/
|
|
401
|
+
execute(commands: MapCommand | MapCommand[], options?: {
|
|
402
|
+
origin?: string;
|
|
403
|
+
}): Promise<CommandResult>;
|
|
404
|
+
/** The event journal for a query and range, straight from deps.journal (MISSING_CAPABILITY without one). */
|
|
405
|
+
getJournal(query: SpatialQuery, range: TimeRange, signal?: AbortSignal): Promise<JournalEntry[]>;
|
|
406
|
+
on<K extends keyof MapRuntimeEvents>(event: K, listener: (payload: MapRuntimeEvents[K]) => void): Unsubscribe;
|
|
407
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SceneManager, isEmptyPatch, mergePatches, sceneKey } from './scene.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SceneManager, isEmptyPatch, mergePatches, sceneKey } from './scene.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MapLayer, MapViewport, ScenePatch, SceneState, SelectionContext, SpatialObject } from '../core/types.js';
|
|
2
|
+
export declare const sceneKey: (layerId: string, objectId: string) => string;
|
|
3
|
+
export declare function isEmptyPatch(p: ScenePatch): boolean;
|
|
4
|
+
/** Combines patches in order. A key added then removed disappears; updates after an add stay adds. */
|
|
5
|
+
export declare function mergePatches(...patches: ScenePatch[]): ScenePatch;
|
|
6
|
+
export declare class SceneManager {
|
|
7
|
+
private viewport;
|
|
8
|
+
private layers;
|
|
9
|
+
private readonly objects;
|
|
10
|
+
private selected;
|
|
11
|
+
private selection;
|
|
12
|
+
private hovered;
|
|
13
|
+
constructor(viewport: MapViewport);
|
|
14
|
+
setViewport(viewport: MapViewport): void;
|
|
15
|
+
/** Replaces layer definitions. Objects of removed or hidden layers leave the scene. */
|
|
16
|
+
setLayers(layers: MapLayer[]): ScenePatch;
|
|
17
|
+
/** Replaces the objects shown through one layer. Objects whose geometry the layer cannot draw are skipped. */
|
|
18
|
+
setLayerObjects(layerId: string, objects: SpatialObject[]): ScenePatch;
|
|
19
|
+
/**
|
|
20
|
+
* Changes only the given objects of one layer (doc §40: realtime touches what changed, never the whole layer).
|
|
21
|
+
* `upserted` are added or replaced; `removed` are objectIds that leave the layer.
|
|
22
|
+
*/
|
|
23
|
+
patchLayerObjects(layerId: string, upserted: Iterable<SpatialObject>, removed: Iterable<string>): ScenePatch;
|
|
24
|
+
/** Updates one object everywhere it appears (for incremental changes such as realtime positions). */
|
|
25
|
+
updateObject(object: SpatialObject): ScenePatch;
|
|
26
|
+
setSelection(selection: SelectionContext | undefined): ScenePatch;
|
|
27
|
+
setHover(objectId: string | null): ScenePatch;
|
|
28
|
+
getState(): SceneState;
|
|
29
|
+
objectIds(): Set<string>;
|
|
30
|
+
private refreshFlags;
|
|
31
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scene: what the application wants drawn right now (ADR-007).
|
|
3
|
+
* It never queries data, authorizes, or knows any domain. Every change returns a minimal ScenePatch.
|
|
4
|
+
*/
|
|
5
|
+
import { deepEqual } from '../core/equal.js';
|
|
6
|
+
import { drawable } from '../layers/layers.js';
|
|
7
|
+
export const sceneKey = (layerId, objectId) => `${layerId}:${objectId}`;
|
|
8
|
+
export function isEmptyPatch(p) {
|
|
9
|
+
return !p.added?.length && !p.updated?.length && !p.removed?.length && !p.layers;
|
|
10
|
+
}
|
|
11
|
+
/** Combines patches in order. A key added then removed disappears; updates after an add stay adds. */
|
|
12
|
+
export function mergePatches(...patches) {
|
|
13
|
+
const added = new Map();
|
|
14
|
+
const updated = new Map();
|
|
15
|
+
const removed = new Set();
|
|
16
|
+
let layers;
|
|
17
|
+
for (const p of patches) {
|
|
18
|
+
for (const o of p.added ?? []) {
|
|
19
|
+
removed.delete(o.key);
|
|
20
|
+
updated.delete(o.key);
|
|
21
|
+
added.set(o.key, o);
|
|
22
|
+
}
|
|
23
|
+
for (const o of p.updated ?? []) {
|
|
24
|
+
if (added.has(o.key))
|
|
25
|
+
added.set(o.key, o);
|
|
26
|
+
else
|
|
27
|
+
updated.set(o.key, o);
|
|
28
|
+
}
|
|
29
|
+
for (const k of p.removed ?? []) {
|
|
30
|
+
if (added.has(k))
|
|
31
|
+
added.delete(k);
|
|
32
|
+
else {
|
|
33
|
+
updated.delete(k);
|
|
34
|
+
removed.add(k);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (p.layers)
|
|
38
|
+
layers = p.layers;
|
|
39
|
+
}
|
|
40
|
+
const out = {};
|
|
41
|
+
if (added.size)
|
|
42
|
+
out.added = [...added.values()];
|
|
43
|
+
if (updated.size)
|
|
44
|
+
out.updated = [...updated.values()];
|
|
45
|
+
if (removed.size)
|
|
46
|
+
out.removed = [...removed];
|
|
47
|
+
if (layers)
|
|
48
|
+
out.layers = layers;
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
export class SceneManager {
|
|
52
|
+
viewport;
|
|
53
|
+
layers = [];
|
|
54
|
+
objects = new Map();
|
|
55
|
+
selected = new Set();
|
|
56
|
+
selection;
|
|
57
|
+
hovered = null;
|
|
58
|
+
constructor(viewport) {
|
|
59
|
+
this.viewport = viewport;
|
|
60
|
+
}
|
|
61
|
+
setViewport(viewport) {
|
|
62
|
+
this.viewport = viewport;
|
|
63
|
+
}
|
|
64
|
+
/** Replaces layer definitions. Objects of removed or hidden layers leave the scene. */
|
|
65
|
+
setLayers(layers) {
|
|
66
|
+
const changed = !deepEqual(layers, this.layers);
|
|
67
|
+
this.layers = structuredClone(layers);
|
|
68
|
+
const keep = new Set(layers.filter((l) => l.visible).map((l) => l.id));
|
|
69
|
+
const removed = [];
|
|
70
|
+
for (const [key, so] of this.objects) {
|
|
71
|
+
const layer = layers.find((l) => l.id === so.layerId);
|
|
72
|
+
// Gone, hidden, or now drawing a different geometry type: the object leaves this layer.
|
|
73
|
+
if (!layer || !keep.has(so.layerId) || !drawable(layer, so.object)) {
|
|
74
|
+
this.objects.delete(key);
|
|
75
|
+
removed.push(key);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const patch = {};
|
|
79
|
+
if (removed.length)
|
|
80
|
+
patch.removed = removed;
|
|
81
|
+
if (changed)
|
|
82
|
+
patch.layers = structuredClone(layers);
|
|
83
|
+
return patch;
|
|
84
|
+
}
|
|
85
|
+
/** Replaces the objects shown through one layer. Objects whose geometry the layer cannot draw are skipped. */
|
|
86
|
+
setLayerObjects(layerId, objects) {
|
|
87
|
+
const layer = this.layers.find((l) => l.id === layerId);
|
|
88
|
+
const patch = {};
|
|
89
|
+
const next = new Map();
|
|
90
|
+
if (layer && layer.visible) {
|
|
91
|
+
for (const object of objects) {
|
|
92
|
+
if (!drawable(layer, object))
|
|
93
|
+
continue;
|
|
94
|
+
const key = sceneKey(layerId, object.objectId);
|
|
95
|
+
next.set(key, { key, layerId, object: structuredClone(object), selected: this.selected.has(object.objectId), hovered: this.hovered === object.objectId });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const added = [];
|
|
99
|
+
const updated = [];
|
|
100
|
+
const removed = [];
|
|
101
|
+
for (const [key, so] of this.objects) {
|
|
102
|
+
if (so.layerId !== layerId)
|
|
103
|
+
continue;
|
|
104
|
+
if (!next.has(key)) {
|
|
105
|
+
this.objects.delete(key);
|
|
106
|
+
removed.push(key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const [key, so] of next) {
|
|
110
|
+
const prev = this.objects.get(key);
|
|
111
|
+
if (!prev)
|
|
112
|
+
added.push(so);
|
|
113
|
+
else if (!deepEqual(prev, so))
|
|
114
|
+
updated.push(so);
|
|
115
|
+
this.objects.set(key, so);
|
|
116
|
+
}
|
|
117
|
+
if (added.length)
|
|
118
|
+
patch.added = added;
|
|
119
|
+
if (updated.length)
|
|
120
|
+
patch.updated = updated;
|
|
121
|
+
if (removed.length)
|
|
122
|
+
patch.removed = removed;
|
|
123
|
+
return patch;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Changes only the given objects of one layer (doc §40: realtime touches what changed, never the whole layer).
|
|
127
|
+
* `upserted` are added or replaced; `removed` are objectIds that leave the layer.
|
|
128
|
+
*/
|
|
129
|
+
patchLayerObjects(layerId, upserted, removed) {
|
|
130
|
+
const layer = this.layers.find((l) => l.id === layerId);
|
|
131
|
+
if (!layer || !layer.visible)
|
|
132
|
+
return {};
|
|
133
|
+
const added = [];
|
|
134
|
+
const updated = [];
|
|
135
|
+
const gone = [];
|
|
136
|
+
for (const id of removed) {
|
|
137
|
+
const key = sceneKey(layerId, id);
|
|
138
|
+
if (this.objects.delete(key))
|
|
139
|
+
gone.push(key);
|
|
140
|
+
}
|
|
141
|
+
for (const object of upserted) {
|
|
142
|
+
const key = sceneKey(layerId, object.objectId);
|
|
143
|
+
if (!drawable(layer, object)) {
|
|
144
|
+
if (this.objects.delete(key))
|
|
145
|
+
gone.push(key);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const so = { key, layerId, object: structuredClone(object), selected: this.selected.has(object.objectId), hovered: this.hovered === object.objectId };
|
|
149
|
+
const prev = this.objects.get(key);
|
|
150
|
+
if (!prev)
|
|
151
|
+
added.push(so);
|
|
152
|
+
else if (!deepEqual(prev, so))
|
|
153
|
+
updated.push(so);
|
|
154
|
+
this.objects.set(key, so);
|
|
155
|
+
}
|
|
156
|
+
const patch = {};
|
|
157
|
+
if (added.length)
|
|
158
|
+
patch.added = added;
|
|
159
|
+
if (updated.length)
|
|
160
|
+
patch.updated = updated;
|
|
161
|
+
if (gone.length)
|
|
162
|
+
patch.removed = gone;
|
|
163
|
+
return patch;
|
|
164
|
+
}
|
|
165
|
+
/** Updates one object everywhere it appears (for incremental changes such as realtime positions). */
|
|
166
|
+
updateObject(object) {
|
|
167
|
+
const updated = [];
|
|
168
|
+
for (const [key, so] of this.objects) {
|
|
169
|
+
if (so.object.objectId !== object.objectId)
|
|
170
|
+
continue;
|
|
171
|
+
const next = { ...so, object: structuredClone(object) };
|
|
172
|
+
if (!deepEqual(next, so)) {
|
|
173
|
+
this.objects.set(key, next);
|
|
174
|
+
updated.push(next);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return updated.length ? { updated } : {};
|
|
178
|
+
}
|
|
179
|
+
setSelection(selection) {
|
|
180
|
+
this.selection = selection && selection.objectIds.length ? { ...selection, objectIds: [...selection.objectIds] } : undefined;
|
|
181
|
+
this.selected = new Set(this.selection?.objectIds ?? []);
|
|
182
|
+
return this.refreshFlags();
|
|
183
|
+
}
|
|
184
|
+
setHover(objectId) {
|
|
185
|
+
if (this.hovered === objectId)
|
|
186
|
+
return {};
|
|
187
|
+
this.hovered = objectId;
|
|
188
|
+
return this.refreshFlags();
|
|
189
|
+
}
|
|
190
|
+
getState() {
|
|
191
|
+
const state = {
|
|
192
|
+
viewport: structuredClone(this.viewport),
|
|
193
|
+
layers: structuredClone(this.layers),
|
|
194
|
+
objects: [...this.objects.values()].map((o) => structuredClone(o)),
|
|
195
|
+
};
|
|
196
|
+
if (this.selection)
|
|
197
|
+
state.selection = structuredClone(this.selection);
|
|
198
|
+
return state;
|
|
199
|
+
}
|
|
200
|
+
objectIds() {
|
|
201
|
+
return new Set([...this.objects.values()].map((o) => o.object.objectId));
|
|
202
|
+
}
|
|
203
|
+
refreshFlags() {
|
|
204
|
+
const updated = [];
|
|
205
|
+
for (const [key, so] of this.objects) {
|
|
206
|
+
const selected = this.selected.has(so.object.objectId);
|
|
207
|
+
const hovered = this.hovered === so.object.objectId;
|
|
208
|
+
if (selected !== so.selected || hovered !== so.hovered) {
|
|
209
|
+
const next = { ...so, selected, hovered };
|
|
210
|
+
this.objects.set(key, next);
|
|
211
|
+
updated.push(next);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return updated.length ? { updated } : {};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SelectionState } from './selection.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SelectionState } from './selection.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SelectionContext } from '../core/types.js';
|
|
2
|
+
/** Interaction state keyed by stable objectId (ADR-009). Knows nothing about layers, data or rendering. */
|
|
3
|
+
export declare class SelectionState {
|
|
4
|
+
private readonly now;
|
|
5
|
+
private current;
|
|
6
|
+
constructor(now?: () => number);
|
|
7
|
+
get(): SelectionContext;
|
|
8
|
+
isSelected(objectId: string): boolean;
|
|
9
|
+
/** Returns true when the selected ids changed. */
|
|
10
|
+
set(objectIds: string[], source: string, primaryObjectId?: string): boolean;
|
|
11
|
+
clear(source: string): boolean;
|
|
12
|
+
}
|