@seatlayer/core 0.28.4 → 0.29.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.
@@ -0,0 +1,176 @@
1
+ import { C as ChartDoc, E as ExpandedSeat } from '../types-B-tpUFqz.js';
2
+
3
+ /**
4
+ * Dirty-flag render loop. A frame is drawn only while the camera is moving /
5
+ * damping or an availability update arrived; when idle, no rAF is scheduled at
6
+ * all (zero CPU/GPU when parked). Tracks an FPS EMA over frames actually
7
+ * rendered — it reads as "idle" when nothing is scheduled.
8
+ */
9
+ interface RenderLoopStats {
10
+ fps: number;
11
+ rendered: number;
12
+ idle: boolean;
13
+ }
14
+
15
+ /**
16
+ * Slice 3 hand-off — the DOM panorama overlay the fly-to-seat cinematic
17
+ * dissolves into. Decoupled by design: the CALLER supplies the equirectangular
18
+ * image (via mountVenue3D's getSeatView), so view3d never imports the app's
19
+ * panorama generator and the chunk stays lean.
20
+ *
21
+ * Technique (mirrors SeatPicker.openSeatView, reimplemented small): an equirect
22
+ * image panned with `repeat-x`; the initial horizontal offset is set so the
23
+ * panorama's bearing matches the final camera yaw — the dissolve reads as the
24
+ * same view sharpening, not a cut. CSS opacity fade is compositor-only.
25
+ */
26
+ interface SeatView {
27
+ url: string;
28
+ /** Bearing (deg, 0 = facing the focal/stage) the panorama should open centred
29
+ * on, to match the camera's final yaw. Default 0 (both face the stage). */
30
+ initialBearingDeg?: number;
31
+ }
32
+
33
+ /**
34
+ * view3d analytics — a tiny, decoupled event emitter for the venue view. The
35
+ * caller (app/harness) supplies `onAnalytics`; this class owns the per-mount
36
+ * state (first-orbit latch, panorama dwell timing) and, crucially, wraps EVERY
37
+ * callback invocation in try/catch so a throwing analytics sink can never break
38
+ * rendering. No DOM, no GL — unit-testable in isolation.
39
+ */
40
+ type Analytics3DCallback = (event: string, props?: Record<string, unknown>) => void;
41
+
42
+ /**
43
+ * view3d palette + seat-state model — the single source of colour truth for the
44
+ * OGL venue view. Pure data (no GPU, no DOM) so the scene builder and the unit
45
+ * tests share one definition. Colours are linear-ish RGB triplets in 0..1.
46
+ *
47
+ * Look brief (docs/3d-usp-strategy §3): desaturated cool greys for structure,
48
+ * one warm accent for the stage, availability colours only on seats.
49
+ */
50
+ type SeatState3D = 'available' | 'held' | 'sold' | 'selected' | 'dimmed';
51
+
52
+ /**
53
+ * Pure geometry primitives for the view3d scene — no OGL, no DOM, so the whole
54
+ * scene-model builder is unit-testable in a plain runtime.
55
+ *
56
+ * Coordinate convention: chart units (x, y) map to world metres as
57
+ * worldX = x * METRES_PER_CHART_UNIT
58
+ * worldZ = y * METRES_PER_CHART_UNIT
59
+ * worldY = up (height in metres)
60
+ * i.e. the chart's audience-depth (+y) becomes world +Z, and Y is the vertical.
61
+ */
62
+
63
+ interface MeshData {
64
+ /** Non-indexed triangle soup: 3 floats per vertex. */
65
+ position: Float32Array;
66
+ normal: Float32Array;
67
+ /** Baked vertex colour incl. AO, 3 floats per vertex. */
68
+ color: Float32Array;
69
+ /** Vertex count (position.length / 3). */
70
+ count: number;
71
+ }
72
+
73
+ /**
74
+ * Pure builder for the instanced seat cloud. Produces the per-instance arrays a
75
+ * single OGL InstancedMesh consumes (one draw call for every seat), plus the
76
+ * seatId → instanceIndex map that `setAvailability` uses to patch only the seats
77
+ * that actually changed via a sub-range `bufferSubData` upload.
78
+ */
79
+
80
+ interface SeatInstanceData {
81
+ count: number;
82
+ /** vec3 per instance: world (x, y, z) in metres. */
83
+ iPosition: Float32Array;
84
+ /** float per instance: index into the seat-state colour LUT. */
85
+ iState: Float32Array;
86
+ /** seatId → instance index (drives targeted availability updates). */
87
+ idToIndex: Map<string, number>;
88
+ }
89
+
90
+ interface SceneModel {
91
+ /** Every non-seat surface merged into one triangle soup (1 draw call). */
92
+ solids: MeshData;
93
+ seats: SeatInstanceData;
94
+ bounds: {
95
+ /** World-metre venue centre (camera target). */
96
+ center: [number, number, number];
97
+ /** Half-diagonal of the horizontal footprint, metres (camera fit). */
98
+ radius: number;
99
+ groundY: number;
100
+ };
101
+ /** 5 × vec3 flat LUT for the seat fragment shader. */
102
+ stateColorLUT: number[];
103
+ seatCount: number;
104
+ /** Venue focal point in world metres (cinematic look-at target). */
105
+ focalWorld: [number, number, number];
106
+ }
107
+ interface SceneModelInput {
108
+ doc: ChartDoc;
109
+ seats: ExpandedSeat[];
110
+ /** Optional initial per-seat state (default all available). */
111
+ initialState?: (seat: ExpandedSeat) => SeatState3D;
112
+ }
113
+ declare function buildSceneModel(input: SceneModelInput): SceneModel;
114
+
115
+ /**
116
+ * view3d — the sole dynamic-import boundary for the lazy OGL venue-view chunk.
117
+ *
118
+ * const { mountVenue3D } = await import('../view3d');
119
+ * const handle = mountVenue3D(container, { doc, seats }, { onSeatPick, getSeatView });
120
+ * await handle.flyToSeat(seatId);
121
+ *
122
+ * Read-only 3D of any chart, fed entirely from the existing height contract.
123
+ * Slice 1: orbit camera, extruded tiers/stage/GA, instanced seat dots, sub-range
124
+ * availability, dispose + context-loss survival. Slice 2: GPU color-pick. Slice
125
+ * 3: the fly-to-seat cinematic that dissolves into the view-from-seat panorama.
126
+ */
127
+
128
+ interface Venue3DInput {
129
+ doc: ChartDoc;
130
+ /** Expanded seats (from `expandChart`) — carry x/y + resolved eyeHeightM. */
131
+ seats: ExpandedSeat[];
132
+ /** Optional initial per-seat state (default all available). */
133
+ initialState?: (seat: ExpandedSeat) => SeatState3D;
134
+ }
135
+ interface Venue3DOptions {
136
+ /** Fired on a tap that hits a seat (GPU color-pick). Not fired on empty taps. */
137
+ onSeatPick?: (seatId: string) => void;
138
+ /**
139
+ * Supplies the view-from-seat panorama for the cinematic hand-off. Decoupled:
140
+ * the caller (app/harness) owns panorama generation; view3d never imports it.
141
+ * Called at PICK time to pre-render, so flyToSeat has zero wait on landing.
142
+ */
143
+ getSeatView?: (seatId: string) => SeatView | Promise<SeatView>;
144
+ /**
145
+ * Decoupled analytics sink. Emits the venue-view journey: `3d_opened`,
146
+ * `3d_orbit_engaged` (first user gesture), `3d_seat_picked`,
147
+ * `3d_cinematic_played`/`_skipped`/`_cancelled`, `3d_panorama_opened`/`_closed`.
148
+ * Every invocation is wrapped in try/catch — a throwing sink never breaks
149
+ * rendering. Absent = no events emitted.
150
+ */
151
+ onAnalytics?: Analytics3DCallback;
152
+ }
153
+ interface Venue3DStats extends RenderLoopStats {
154
+ drawCalls: number;
155
+ seatCount: number;
156
+ }
157
+ interface Venue3DHandle {
158
+ dispose(): void;
159
+ setAvailability(updates: {
160
+ seatId: string;
161
+ state: SeatState3D;
162
+ }[]): void;
163
+ setSelection(seatIds: string[]): void;
164
+ /** Fly the camera from the overview into `seatId` and dissolve into its
165
+ * view-from-seat panorama. Resolves at flight end; a drag cancels it, a second
166
+ * call retargets, dispose resolves early. Reduced-motion → a short fade. */
167
+ flyToSeat(seatId: string): Promise<void>;
168
+ resize(): void;
169
+ stats(): Venue3DStats;
170
+ loseContextForTest(): void;
171
+ /** Test hook: force (or clear) the reduced-motion path. */
172
+ setReducedMotionForTest(value: boolean | null): void;
173
+ }
174
+ declare function mountVenue3D(container: HTMLElement, input: Venue3DInput, opts?: Venue3DOptions): Venue3DHandle;
175
+
176
+ export { type Analytics3DCallback, type SeatState3D, type SeatView, type Venue3DHandle, type Venue3DInput, type Venue3DOptions, type Venue3DStats, buildSceneModel, mountVenue3D };