@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.
- package/dist/chunk-5MLADB2N.js +53 -0
- package/dist/chunk-5MLADB2N.js.map +1 -0
- package/dist/index.cjs +904 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -1459
- package/dist/index.d.ts +36 -1459
- package/dist/index.js +913 -154
- package/dist/index.js.map +1 -1
- package/dist/types-B-tpUFqz.d.cts +1552 -0
- package/dist/types-B-tpUFqz.d.ts +1552 -0
- package/dist/view3d/index.cjs +1972 -0
- package/dist/view3d/index.cjs.map +1 -0
- package/dist/view3d/index.d.cts +176 -0
- package/dist/view3d/index.d.ts +176 -0
- package/dist/view3d/index.js +1906 -0
- package/dist/view3d/index.js.map +1 -0
- package/package.json +22 -2
|
@@ -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 };
|