@seatlayer/core 0.1.1 → 0.1.3

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/index.d.cts CHANGED
@@ -15,8 +15,21 @@ interface Category {
15
15
  key: string;
16
16
  label: string;
17
17
  color: string;
18
- /** Demo-only convenience; real pricing comes from ticket tiers server-side. */
18
+ /** Base price used when the category has no explicit tiers. */
19
19
  price?: number;
20
+ /**
21
+ * Ticket tiers (Adult / Child / Senior…). When present, a buyer picks a tier
22
+ * per seat in this category and the tier's price applies; the first tier is the
23
+ * default. Per-category (not per-seat) pricing — see Batch 3.5. Empty/absent =
24
+ * a single price (the `price` above).
25
+ */
26
+ tiers?: CategoryTier[];
27
+ }
28
+ /** One ticket tier within a category: a named price (Adult, Child, Senior…). */
29
+ interface CategoryTier {
30
+ id: string;
31
+ name: string;
32
+ price: number;
20
33
  }
21
34
  /**
22
35
  * Accessibility accommodations a seat can carry. Mirrors the taxonomy real
@@ -103,7 +116,9 @@ interface RowObject {
103
116
  seatLabelStart?: number;
104
117
  /** Seat numbering within the row (default ltr, step 1). */
105
118
  seatNumbering?: {
106
- direction: 'ltr' | 'rtl';
119
+ /** ltr / rtl number from an end; `center` numbers outward from the middle
120
+ * (centre seat lowest — the premium-centre theatre convention). */
121
+ direction: 'ltr' | 'rtl' | 'center';
107
122
  /** 2 = odd/even numbering (1,3,5… — start at 2 for evens). */
108
123
  step?: 1 | 2;
109
124
  };
@@ -170,6 +185,13 @@ interface TableObject {
170
185
  rotation: number;
171
186
  /** Round tables. */
172
187
  radius?: number;
188
+ /**
189
+ * Round tables: the arc (in degrees) the seats occupy, default 360 (full
190
+ * ring). Below 360 leaves an open side — e.g. a service gap for waiters, a
191
+ * head table facing the room, or clearance against a wall. The opening is
192
+ * centred on the `rotation` direction; seats spread across the rest.
193
+ */
194
+ seatArc?: number;
173
195
  /** Rect tables. */
174
196
  width?: number;
175
197
  height?: number;
@@ -246,15 +268,33 @@ interface TextObject {
246
268
  color?: string;
247
269
  }
248
270
  type ChartObject = RowObject | GAAreaObject | ShapeObject | TableObject | BoothObject | TextObject | SectionObject;
271
+ /**
272
+ * One floor / level of a multi-floor venue (Batch 5). Each floor owns its own
273
+ * geometry, stage focal point, and trace image; categories/zones/tiers stay
274
+ * chart-global (one event, one inventory). A single-floor chart has NO `floors`
275
+ * — its `objects[]` is the whole venue — so all existing charts are untouched.
276
+ */
277
+ interface Floor {
278
+ id: string;
279
+ name: string;
280
+ objects: ChartObject[];
281
+ focalPoint: Point;
282
+ backgroundImage?: ChartDoc['backgroundImage'];
283
+ }
249
284
  interface ChartDoc {
250
285
  version: 1;
251
286
  name: string;
252
287
  venueType: 'SIMPLE' | 'MIXED';
253
- /** The stage / point every seat looks at. Anchors seat-view + sightlines. */
288
+ /** The stage / point every seat looks at. Anchors seat-view + sightlines.
289
+ * Multi-floor: mirrors floor 0; each floor also carries its own focalPoint. */
254
290
  focalPoint: Point;
255
291
  categories: Category[];
256
292
  /** Section groupings for far-zoom navigation + pricing (optional; sections reference by id). */
257
293
  zones?: ZoneDef[];
294
+ /** Multi-floor venues (Batch 5): present ⇒ floors[] is the source of truth;
295
+ * absent ⇒ single-floor and `objects` below is the whole chart. `objects`
296
+ * is kept mirroring floor 0 so single-floor readers never branch. */
297
+ floors?: Floor[];
258
298
  objects: ChartObject[];
259
299
  /** Floor-plan photo the organizer traces over (designer-only aid, also rendered dimly in picker if kept). */
260
300
  backgroundImage?: {
@@ -267,6 +307,13 @@ interface ChartDoc {
267
307
  };
268
308
  /** Brand/venue theming (colors); categories carry their own colors separately. */
269
309
  theme?: ChartTheme;
310
+ /** Parametric-template provenance: present ⇒ the chart came from a capacity-
311
+ * adjustable template family, and the designer offers a capacity control that
312
+ * regenerates it at a new target seat count (Batch 4 "curated singles + resize"). */
313
+ template?: {
314
+ family: string;
315
+ targetSeats: number;
316
+ };
270
317
  }
271
318
  interface ExpandedSeat {
272
319
  /** Stable id: `${rowId}:${index}` */
@@ -298,9 +345,22 @@ interface RendererCallbacks {
298
345
  onFps?: (fps: number) => void;
299
346
  /** Fired when a GA area is clicked (quantity picking is UI-side). */
300
347
  onGAClick?: (areaId: string) => void;
348
+ /**
349
+ * Fired when a tap lands on a section outline while seats are NOT the active
350
+ * rung (i.e. zoomed out, section/zone LOD). The host glides in + shows a
351
+ * section-summary card instead of trying to select a 4px seat (Slice 5).
352
+ */
353
+ onSectionTap?: (sectionId: string) => void;
354
+ /**
355
+ * Fired when a seat/deck is tapped in the 3D all-floors stacked overview — the
356
+ * host drops back to the flat 2D map on that floor ("tap a deck to enter").
357
+ */
358
+ onDeckTap?: (floorId: string) => void;
301
359
  /** Fired after any pan/zoom/resize settles — re-anchor screen-space overlays. */
302
360
  onViewChange?: () => void;
303
361
  }
362
+ /** Far-zoom level-of-detail rung: whole zones → section blocks → individual seats. */
363
+ type LodRung = 'zones' | 'sections' | 'seats';
304
364
  interface RendererOptions extends RendererCallbacks {
305
365
  /** Max seats selectable at once (default 10). */
306
366
  maxSelection?: number;
@@ -314,10 +374,16 @@ interface RendererOptions extends RendererCallbacks {
314
374
  * pushing straight into the cart; `deselect([seat.id])` on Cancel un-highlights.
315
375
  */
316
376
  confirmSelection?: boolean;
377
+ /** ISO 4217 currency for on-map prices ("FROM …"); defaults to money.DEFAULT_CURRENCY.
378
+ * Locale for grouping/symbol placement comes from the active i18n locale. */
379
+ currency?: string;
317
380
  }
318
381
  interface ISeatmapRenderer {
319
- /** Replace the chart. Resets selection and statuses, zooms to fit. */
320
- setChart(doc: ChartDoc): void;
382
+ /** Replace the chart. Resets selection and statuses, zooms to fit.
383
+ * `opts.floorId` picks which floor to render on a multi-floor chart (Batch 5). */
384
+ setChart(doc: ChartDoc, opts?: {
385
+ floorId?: string;
386
+ }): void;
321
387
  /** Bulk status update; re-renders affected seats only. */
322
388
  setStatus(seatIds: string[], status: SeatStatus): void;
323
389
  getStatus(seatId: string): SeatStatus;
@@ -357,12 +423,26 @@ interface ISeatmapRenderer {
357
423
  setAccessibilityFilter(types: AccessibilityType[] | null): void;
358
424
  /** Legend hover-highlight: dim free seats of other categories (null clears). */
359
425
  setCategoryHighlight?(key: string | null): void;
426
+ /** Dim the seats of these section/zone ids (organizer manager: held-back inventory). */
427
+ setDimmedSections?(ids: string[] | null): void;
360
428
  /**
361
429
  * Switch the projection. `'flat'` = normal top-down; `'isometric'` = the "3D"
362
430
  * view (affine skew/rotate + elevation lift), hit-testing preserved in screen
363
431
  * space. Purely visual — the chart is authored flat. Animated unless reduced-motion.
364
432
  */
365
433
  setViewMode?(mode: 'flat' | 'isometric'): void;
434
+ /** Current projection (defaults to 'flat' when unimplemented). */
435
+ getViewMode?(): 'flat' | 'isometric';
436
+ /** Multi-floor (Batch 5): switch the shown floor; list floors; read the active id. */
437
+ setActiveFloor?(floorId: string): void;
438
+ getFloors?(): {
439
+ id: string;
440
+ name: string;
441
+ }[];
442
+ getActiveFloorId?(): string;
443
+ /** Render all floors stacked (3D overview) vs the active floor. No-op single-floor. */
444
+ setStacked?(on: boolean): void;
445
+ isStacked?(): boolean;
366
446
  /**
367
447
  * Section id whose outline contains a container-relative screen point (or null).
368
448
  * Feeds the far-zoom "tap a section to zoom in" flow (Slice 5).
@@ -370,6 +450,23 @@ interface ISeatmapRenderer {
370
450
  sectionAt?(clientPoint: Point): string | null;
371
451
  /** Seat ids belonging to a section — for the section-summary card (Slice 5). */
372
452
  sectionMembers?(id: string): string[];
453
+ /**
454
+ * Smoothly glide (pan+zoom) the camera to frame a section (by id) or a world-
455
+ * space bounds rect over ~450ms easeInOutCubic. `prefers-reduced-motion` snaps.
456
+ * A pointer-down (grab/pan) cancels an in-flight glide. Slice 5 "glide in".
457
+ */
458
+ focusRegion?(target: string | {
459
+ x: number;
460
+ y: number;
461
+ width: number;
462
+ height: number;
463
+ }, opts?: {
464
+ animate?: boolean;
465
+ }): void;
466
+ /** Current LOD rung derived from zoom (for the ZONES/SECTIONS/SEATS pill). */
467
+ getRung?(): LodRung;
468
+ /** Jump the camera to a rung's zoom band, centred on the chart (glided). */
469
+ setRung?(rung: LodRung): void;
373
470
  destroy(): void;
374
471
  }
375
472
  /** localStorage key the Designer writes and the Picker reads. */
@@ -417,7 +514,24 @@ declare function pointInPolygon(p: Point, poly: Point[]): boolean;
417
514
  * shape: bbox centre; text: its position.
418
515
  */
419
516
  declare function objectCenter(o: ChartObject): Point;
420
- /** Expand every seat-bearing object (rows, tables, booths). */
517
+ /**
518
+ * Normalized floor list (Batch 5): a multi-floor chart's `floors`, or a synthetic
519
+ * single floor wrapping a single-floor chart's `objects`. Every consumer that needs
520
+ * to reason about floors goes through this so single-floor charts stay untouched.
521
+ */
522
+ declare function floorsOf(doc: ChartDoc): Floor[];
523
+ /** Objects of one floor by id (defaults to the first floor). */
524
+ declare function floorObjects(doc: ChartDoc, floorId?: string): ChartObject[];
525
+ /** Every object across ALL floors — the whole venue (single-floor = `doc.objects`). */
526
+ declare function allObjects(doc: ChartDoc): ChartObject[];
527
+ /**
528
+ * Multi-floor 3D stack (Batch 5): flatten every floor into ONE doc with floor `i`
529
+ * lifted by `i * spread` in −y, so the isometric view shows the floors as stacked
530
+ * decks (ground at the bottom). Returns a single-floor doc (no `floors`). Meant
531
+ * only for the 3D overview render — 2D still shows one floor via `floorObjects`.
532
+ */
533
+ declare function stackFloors(doc: ChartDoc, spread?: number): ChartDoc;
534
+ /** Expand every seat-bearing object across all floors (rows, tables, booths). */
421
535
  declare function expandChart(doc: ChartDoc): ExpandedSeat[];
422
536
  /** Axis-aligned bounds over every object plus the background image, with padding. */
423
537
  declare function chartBounds(doc: ChartDoc): {
@@ -427,6 +541,78 @@ declare function chartBounds(doc: ChartDoc): {
427
541
  height: number;
428
542
  };
429
543
 
544
+ /**
545
+ * Section membership + hide/show resolution (Batch 3.3).
546
+ *
547
+ * A `SectionObject` is a polygon drawn over existing seat objects; an object
548
+ * "belongs" to a section when its visual centre (`objectCenter`) falls inside
549
+ * the section outline, first section in doc order winning. This is the same
550
+ * spatial notion `objectCenter`'s doc-comment describes, resolved once here so
551
+ * BOTH the event manager (Sections tab counts) and the buyer picker (omit
552
+ * hidden seats) agree on which seats live in which section.
553
+ *
554
+ * Hiding is per-EVENT (the EventDO holds the hidden id set), not a chart edit —
555
+ * so a republish of the chart never disturbs it. A hidden id may be a section
556
+ * id, a zone id (hides every section in the zone), or `UNGROUPED_ID` (the
557
+ * catch-all bucket of seat objects that sit in no section).
558
+ */
559
+
560
+ /** Synthetic section id for seat objects that fall in no drawn section. */
561
+ declare const UNGROUPED_ID = "__ungrouped__";
562
+ /**
563
+ * A per section/zone availability window (Batch 3.4). Wire-compatible with the
564
+ * EventDO's own `AvailabilityRule`. Absence of a rule for an id = on sale.
565
+ * 'hidden' — manual: hidden until the organizer reveals it (3.3).
566
+ * 'timed' — hidden until `revealAt` (epoch ms), then auto-reveals.
567
+ * 'threshold' — auto-reveals once the on-sale inventory is `thresholdPct`% sold.
568
+ * `labels` are the seat labels the id governs, so a threshold's denominator can
569
+ * exclude still-hidden seats.
570
+ */
571
+ interface AvailabilityRule {
572
+ mode: 'hidden' | 'timed' | 'threshold';
573
+ revealAt?: number;
574
+ thresholdPct?: number;
575
+ labels?: string[];
576
+ }
577
+ interface SectionNode {
578
+ /** Section id (a `SectionObject.id`, or `UNGROUPED_ID`). */
579
+ id: string;
580
+ label: string;
581
+ /** Zone id this section points at (`SectionObject.zone`), if any. */
582
+ zone?: string;
583
+ /** Total seats across the member objects. */
584
+ seatCount: number;
585
+ /** Ids of the seat-bearing objects that belong to this section. */
586
+ objectIds: string[];
587
+ /** Seat labels across the member objects (for availability-rule denominators). */
588
+ seatLabels: string[];
589
+ }
590
+ /**
591
+ * Resolve section membership for a chart. Returns one node per drawn section
592
+ * (doc order), a synthetic "Ungrouped" node for loose seat objects (null when
593
+ * every seat sits in a section), and the object→section id map.
594
+ */
595
+ declare function computeSections(doc: ChartDoc): {
596
+ sections: SectionNode[];
597
+ ungrouped: SectionNode | null;
598
+ /** seat-object id → owning section id (or `UNGROUPED_ID`). */
599
+ objectToSection: Map<string, string>;
600
+ };
601
+ /** Is a drawn section hidden under `hidden` — directly, or via its zone? */
602
+ declare function isSectionHidden(s: SectionObject, hidden: ReadonlySet<string>): boolean;
603
+ /**
604
+ * The set of seat-object ids to omit from the buyer view for a given hidden id
605
+ * set. An object is hidden when its owning section is hidden (directly or via
606
+ * zone), or it is ungrouped and the catch-all bucket is hidden.
607
+ */
608
+ declare function hiddenObjectIds(doc: ChartDoc, hidden: ReadonlySet<string>): Set<string>;
609
+ /**
610
+ * A copy of the doc with hidden sections' member objects removed, plus the
611
+ * hidden section overlays themselves (so no empty block renders). Returns the
612
+ * SAME reference when nothing is hidden — callers can skip a re-render on `===`.
613
+ */
614
+ declare function applyHidden(doc: ChartDoc, hidden: ReadonlySet<string>): ChartDoc;
615
+
430
616
  /**
431
617
  * SeatmapRenderer — the shared canvas rendering core (buyer picker shell).
432
618
  *
@@ -447,6 +633,11 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
447
633
  private labelGroup;
448
634
  private seats;
449
635
  private seatById;
636
+ /** Multi-floor (Batch 5): the last-set chart + which floor we're rendering. */
637
+ private chartDoc;
638
+ private activeFloorId;
639
+ /** When true on a multi-floor chart, render ALL floors stacked (3D overview). */
640
+ private stacked;
450
641
  /** Interactive node per seat/booth — a Circle for seats, a Rect for booths. */
451
642
  private circleById;
452
643
  /** Booth block geometry, keyed by booth id (= the unit's rowId). */
@@ -471,6 +662,12 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
471
662
  private zones;
472
663
  private seatSection;
473
664
  private catPrice;
665
+ /** ISO 4217 currency for on-map "FROM …" prices (undefined ⇒ money default). */
666
+ private currency;
667
+ /** Section/zone ids to render dimmed (organizer manager: held-back inventory). */
668
+ private dimmedSections;
669
+ /** Object id → floor id (multi-floor only) — resolves a deck tap in the 3D stack. */
670
+ private objectFloor;
474
671
  /** Zone id → colour (drives extruded side faces in iso view). */
475
672
  private zoneColor;
476
673
  private hasSections;
@@ -482,6 +679,8 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
482
679
  private isoRaf;
483
680
  /** Chart centre the iso projection pivots about (bounds centre). */
484
681
  private isoCentre;
682
+ /** rAF for an in-flight camera glide (focusRegion / setRung); 0 = none. */
683
+ private glideRaf;
485
684
  /** Set in destroy() so an in-flight iso tween bails. */
486
685
  private destroyed;
487
686
  /** Cached scale the section/zone labels were last sized for (scale-compensation). */
@@ -507,7 +706,24 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
507
706
  /** Cumulative gesture movement in px — clicks are suppressed after a real pan/pinch. */
508
707
  private moved;
509
708
  constructor(container: HTMLDivElement, options?: RendererOptions);
510
- setChart(doc: ChartDoc): void;
709
+ setChart(doc: ChartDoc, opts?: {
710
+ floorId?: string;
711
+ }): void;
712
+ /** The chart to render: all floors stacked (3D overview), the active floor, or
713
+ * the whole chart for single-floor charts. */
714
+ private floorView;
715
+ /** Toggle the 3D all-floors stacked overview (Batch 5). Re-renders; no-op on
716
+ * single-floor charts. The caller re-applies statuses + animates the iso view. */
717
+ setStacked(on: boolean): void;
718
+ isStacked(): boolean;
719
+ /** Switch which floor is shown (2D). Re-renders + re-fits; no-op if unchanged. */
720
+ setActiveFloor(floorId: string): void;
721
+ /** Floors for the host's switcher (single-floor charts return one synthetic floor). */
722
+ getFloors(): {
723
+ id: string;
724
+ name: string;
725
+ }[];
726
+ getActiveFloorId(): string;
511
727
  setStatus(seatIds: string[], status: SeatStatus): void;
512
728
  getStatus(seatId: string): SeatStatus;
513
729
  getSelection(): ExpandedSeat[];
@@ -541,6 +757,8 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
541
757
  * inverse) keeps landing on the projected seats/sections.
542
758
  */
543
759
  setViewMode(mode: 'flat' | 'isometric'): void;
760
+ /** Current projection — reflects the tween target, not the mid-tween isoT. */
761
+ getViewMode(): 'flat' | 'isometric';
544
762
  /** Iso angle (rad) + y-squash for the current isoT. */
545
763
  private isoParams;
546
764
  /** Effective vertical scale = stage scale × iso squash — legibility math uses this. */
@@ -581,6 +799,12 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
581
799
  private renderBoothUnit;
582
800
  /** Apply fill/stroke/opacity for a seat's current status + selection. */
583
801
  private paintSeat;
802
+ /**
803
+ * Dim the seats of these section/zone ids (organizer manager use) so held-back
804
+ * inventory is visually distinct on the canvas without hiding it. `null`/empty
805
+ * clears. Unlike the buyer's applyHidden, the sections stay rendered.
806
+ */
807
+ setDimmedSections(ids: string[] | null): void;
584
808
  /** Legend hover: highlight one category (dim the rest), or null to clear. */
585
809
  setCategoryHighlight(key: string | null): void;
586
810
  private renderBackground;
@@ -639,6 +863,14 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
639
863
  private toggleSeat;
640
864
  private setSelected;
641
865
  private wireInteraction;
866
+ /**
867
+ * Which deck (floor) a screen tap landed on in the 3D stacked overview. The
868
+ * seat layer is melt-cached at that zoom so individual seat nodes aren't
869
+ * hit-testable; instead we invert the iso+stage transform via the layer's
870
+ * relative pointer and take the nearest seat's floor. Only the floor matters,
871
+ * so within-floor elevation offsets don't affect the result. Null if none.
872
+ */
873
+ private deckFloorAt;
642
874
  private toLocal;
643
875
  private onPointerDown;
644
876
  private onPointerMove;
@@ -652,6 +884,27 @@ declare class SeatmapRenderer implements ISeatmapRenderer {
652
884
  private zoomAbout;
653
885
  /** Zoom + pan so world-rect `b` fills the viewport (with a small margin). */
654
886
  private zoomToBounds;
887
+ /**
888
+ * Smoothly glide the camera to frame a section (by id) or a world-space bounds
889
+ * rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
890
+ * melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
891
+ * `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
892
+ * newer glide cancels an in-flight one.
893
+ */
894
+ focusRegion(target: string | {
895
+ x: number;
896
+ y: number;
897
+ width: number;
898
+ height: number;
899
+ }, opts?: {
900
+ animate?: boolean;
901
+ }): void;
902
+ /** Cancel an in-flight camera glide (a grab or a newer glide interrupts it). */
903
+ private cancelGlide;
904
+ /** Current LOD rung derived from effective zoom — drives the ZONES/SECTIONS/SEATS pill. */
905
+ getRung(): LodRung;
906
+ /** Jump the camera to a rung's zoom band, centred on the chart (glided). */
907
+ setRung(rung: LodRung): void;
655
908
  /** Recompute LOD (cache/labels) after any pan/zoom settles. */
656
909
  private afterViewChange;
657
910
  /** rAF-coalesced `onViewChange` — at most one host callback per animation frame. */
@@ -668,7 +921,12 @@ interface PickerSeat {
668
921
  id: string;
669
922
  label: string;
670
923
  categoryKey: string;
924
+ /** Price for the chosen tier when the category has tiers, else the base price. */
671
925
  price: number;
926
+ /** Ticket tiers the seat's category offers (Adult/Child/…); absent when none. */
927
+ tiers?: CategoryTier[];
928
+ /** The chosen tier's id — defaults to the first tier; absent when no tiers. */
929
+ tierId?: string;
672
930
  }
673
931
  interface HoldConflict {
674
932
  label: string;
@@ -679,6 +937,35 @@ interface HoldInfo {
679
937
  holdId: string;
680
938
  labels: string[];
681
939
  expiresAt: number;
940
+ /** The held seats with their chosen ticket tier — what the host books against. */
941
+ seats: PickerSeat[];
942
+ }
943
+ /** One category's slice of a section (dot + price + count) for the summary card. */
944
+ interface SectionCategory {
945
+ key: string;
946
+ label: string;
947
+ color: string;
948
+ price: number;
949
+ count: number;
950
+ }
951
+ /**
952
+ * Big-venue section-summary — everything the tapped-section card renders: name,
953
+ * its zone, live seats-left, price range, and the per-category breakdown.
954
+ * Computed from the renderer's spatial section membership (Slice 5).
955
+ */
956
+ interface SectionSummary {
957
+ id: string;
958
+ label: string;
959
+ /** Zone label (from ChartDoc.zones); '' when the section has no zone. */
960
+ zoneLabel: string;
961
+ /** Mix/section colour for the card's dot (section.color → zone colour → dominant category). */
962
+ color: string;
963
+ /** Free member seats right now. */
964
+ seatsLeft: number;
965
+ priceMin: number;
966
+ priceMax: number;
967
+ /** Per-category breakdown, cheapest first. */
968
+ categories: SectionCategory[];
682
969
  }
683
970
  interface HoldResponse {
684
971
  holdId: string;
@@ -697,10 +984,12 @@ interface PickerTransport {
697
984
  salesClosed?: boolean;
698
985
  venue?: string | null;
699
986
  startsAt?: number | null;
987
+ currency?: string;
700
988
  };
701
989
  }>;
702
990
  objects(key: string): Promise<{
703
991
  seats: Record<string, string>;
992
+ hidden?: string[];
704
993
  }>;
705
994
  hold(key: string, labels: string[]): Promise<HoldResponse>;
706
995
  bestAvailable(key: string, qty: number, categoryKey?: string): Promise<BestAvailableResponse>;
@@ -722,6 +1011,14 @@ interface PickerCallbacks extends RendererCallbacks {
722
1011
  onStatusChange?: () => void;
723
1012
  /** The server declared the event closed (a 409 event_closed). */
724
1013
  onSalesClosed?: () => void;
1014
+ /**
1015
+ * A section block was tapped at the far/zone rung — the controller has glided
1016
+ * the camera in and passes the computed summary (or null when cleared, e.g.
1017
+ * overview() / zoom-to-zones) so the host can show/hide the summary card.
1018
+ */
1019
+ onSectionFocus?: (summary: SectionSummary | null) => void;
1020
+ /** A deck was tapped in the 3D all-floors overview — host enters that floor in 2D. */
1021
+ onDeckTap?: (floorId: string) => void;
725
1022
  onError?: (err: unknown) => void;
726
1023
  }
727
1024
  interface PickerOptions extends PickerCallbacks {
@@ -730,6 +1027,8 @@ interface PickerOptions extends PickerCallbacks {
730
1027
  maxSelection?: number;
731
1028
  /** Renderer confirm-card mode (host shows a confirm popover instead of instant cart add). */
732
1029
  confirmSelection?: boolean;
1030
+ /** ISO 4217 currency for on-map prices (default from money.DEFAULT_CURRENCY). */
1031
+ currency?: string;
733
1032
  /** Flash a pulse when a seat we didn't touch goes free→taken (live-activity cue). */
734
1033
  flashOnLiveChange?: boolean;
735
1034
  }
@@ -740,9 +1039,15 @@ declare class PickerController {
740
1039
  private readonly maxSelection;
741
1040
  private renderer;
742
1041
  private _doc;
1042
+ /** Section/zone ids hidden from buyers this event (3.3) — seats vanish, not grey. */
1043
+ private hidden;
743
1044
  /** label ⇄ id maps — backend speaks labels, the engine speaks ids. */
744
1045
  private labelToId;
745
1046
  private labelToSeat;
1047
+ /** seatId → chosen ticket-tier id (absent ⇒ the category's first/default tier). */
1048
+ private seatTiers;
1049
+ /** id → seat, for the section-summary breakdown (renderer members are ids). */
1050
+ private seatById;
746
1051
  private allIds;
747
1052
  private ws;
748
1053
  private reconnectTimer;
@@ -752,6 +1057,10 @@ declare class PickerController {
752
1057
  private expiryTimer;
753
1058
  constructor(options: PickerOptions);
754
1059
  get doc(): ChartDoc | null;
1060
+ /** The chart with hidden sections' seats removed — what buyers actually see. */
1061
+ private visibleDoc;
1062
+ /** Adopt a new hidden set; rebuild the visible chart only if it differs. */
1063
+ private syncHidden;
755
1064
  currentHold(): HoldInfo | null;
756
1065
  getRenderer(): ISeatmapRenderer | null;
757
1066
  seatByLabel(label: string): ExpandedSeat | undefined;
@@ -763,6 +1072,7 @@ declare class PickerController {
763
1072
  eventName: string;
764
1073
  venue?: string | null;
765
1074
  startsAt?: number | null;
1075
+ currency?: string;
766
1076
  } | null>;
767
1077
  getSelection(): PickerSeat[];
768
1078
  clearSelection(): void;
@@ -806,9 +1116,51 @@ declare class PickerController {
806
1116
  y: number;
807
1117
  };
808
1118
  setAccessibilityFilter(types: string[] | null): void;
1119
+ /** Floors for the buyer's switcher (>1 ⇒ show it). Single-floor ⇒ one entry. */
1120
+ getFloors(): {
1121
+ id: string;
1122
+ name: string;
1123
+ }[];
1124
+ getActiveFloorId(): string;
1125
+ /** Switch the shown floor (2D), then re-apply live seat statuses onto it. */
1126
+ setFloor(id: string): void;
1127
+ /** 3D all-floors stacked overview ⇄ active floor. Re-applies statuses after. */
1128
+ setStacked(on: boolean): void;
1129
+ isMultiFloor(): boolean;
1130
+ /** Tap-a-deck-to-enter: leave the 3D stack, drop to flat 2D on `floorId`, and
1131
+ * tell the host so it can sync its 2D/3D toggle + floor-switcher state. */
1132
+ private handleDeckTap;
1133
+ /** Switch the map projection (2D flat ⇄ 3D isometric). No-op on a flat renderer. */
1134
+ setViewMode(mode: 'flat' | 'isometric'): void;
1135
+ getViewMode(): 'flat' | 'isometric';
1136
+ /** Current LOD rung (for the ZONES/SECTIONS/SEATS pill). */
1137
+ getRung(): LodRung;
1138
+ /** Jump to a rung; ZONES clears any focused summary (back to overview). */
1139
+ setRung(rung: LodRung): void;
1140
+ /** Glide in on a section and surface its summary (same path as a section tap). */
1141
+ focusSection(id: string): void;
1142
+ /** Zoom back out to the whole chart and clear the section-summary card. */
1143
+ overview(): void;
1144
+ /** Glide the camera into a tapped section and emit its computed summary. */
1145
+ private handleSectionTap;
1146
+ /**
1147
+ * Build a section summary from the renderer's spatial membership: section +
1148
+ * zone labels, live seats-left, price range, and the per-category breakdown.
1149
+ */
1150
+ private sectionSummary;
809
1151
  destroy(): void;
810
1152
  private toSeat;
811
1153
  private priceFor;
1154
+ private tiersFor;
1155
+ /**
1156
+ * Choose a ticket tier for a selected seat (e.g. Adult → Child). Re-emits the
1157
+ * selection so the host's cart + the eventual hold carry the new tier + price.
1158
+ * `tierId = null` reverts to the category's first (default) tier. No-op if the
1159
+ * seat's category has no tiers or the id isn't one of them.
1160
+ */
1161
+ setSeatTier(seatId: string, tierId: string | null): void;
1162
+ /** PickerSeats (with chosen tier) for a set of held labels. */
1163
+ private seatsForLabels;
812
1164
  private emitSelectionChange;
813
1165
  private emitError;
814
1166
  private holdCovers;
@@ -823,4 +1175,76 @@ declare class PickerController {
823
1175
  private scheduleReconnect;
824
1176
  }
825
1177
 
826
- export { ACCESSIBILITY_TYPES, type AccessibilityMeta, type AccessibilityType, type BoothObject, CHART_STORAGE_KEY, type Category, type ChartDoc, type ChartObject, type ChartTheme, type ExpandedSeat, type GAAreaObject, type HoldConflict, type HoldInfo, type ISeatmapRenderer, type PickerCallbacks, PickerController, type PickerOptions, type PickerSeat, type PickerTransport, type Point, type RendererCallbacks, type RendererOptions, type RowObject, type RowSeatSlot, type SeatOverride, type SeatStatus, SeatmapRenderer, type SectionObject, type SelectionLayer, type ShapeObject, type TableObject, type TextObject, type ZoneDef, accessibilityMeta, chartBounds, createRenderer, expandBooth, expandChart, expandRow, expandRowSlots, expandTable, layerOf, objectCenter, pointInPolygon };
1178
+ /**
1179
+ * i18n core — deliberately tiny and framework-free so the embed SDK can share
1180
+ * it without dragging in a runtime library (the 60KB-gzipped SDK budget is a
1181
+ * product contract).
1182
+ *
1183
+ * Scope (docs/design-port-plan.md): the buyer surface (picker, public event
1184
+ * page, SDK) ships fully translated in en/es/de/fr; dashboard pages route
1185
+ * their strings through t() as they are rebuilt but ship English this round.
1186
+ *
1187
+ * Keys are flat dot-namespaced strings ("picker.holdSeats"). Interpolation
1188
+ * uses {name} placeholders. Missing keys fall back to English, then to the
1189
+ * key itself — a page never crashes over a translation.
1190
+ */
1191
+ type Locale = 'en' | 'es' | 'de' | 'fr';
1192
+ declare const SUPPORTED_LOCALES: Locale[];
1193
+ type Dict = Record<string, string>;
1194
+ /** explicit setting → stored preference → browser language → en */
1195
+ declare function resolveLocale(explicit?: string | null, stored?: string | null): Locale;
1196
+ declare function getLocale(): Locale;
1197
+ /**
1198
+ * Set the active locale. Locale bundles other than English are registered by
1199
+ * the surface that needs them (the picker imports its own es/de/fr bundles;
1200
+ * the dashboard stays English until its translations ship).
1201
+ */
1202
+ declare function setLocale(locale: Locale, bundle?: Dict): void;
1203
+ declare function setStringOverrides(next: Dict): void;
1204
+ declare function t(key: string, vars?: Record<string, string | number>): string;
1205
+ /** "1 seat" / "3 seats" without hand-rolled concatenation. */
1206
+ declare function tCount(key: string, count: number, vars?: Record<string, string | number>): string;
1207
+ declare function formatDate(value: number | Date, opts?: Intl.DateTimeFormatOptions): string;
1208
+
1209
+ /**
1210
+ * Locale bundle loader — keeps non-English translations OUT of the initial
1211
+ * bundle (the 60 KB SDK budget is a product contract) and code-splits each
1212
+ * locale so a page/SDK only downloads the language it actually uses.
1213
+ *
1214
+ * `loadLocale('de')` dynamic-imports the German dictionary, registers it via
1215
+ * setLocale(), and resolves once it's active. English is built in, so
1216
+ * `loadLocale('en')` is synchronous and never fetches. Unknown/unsupported
1217
+ * codes fall back to English without throwing.
1218
+ */
1219
+
1220
+ /**
1221
+ * Resolve `code` to a supported locale, load its bundle if needed, and make it
1222
+ * active. Returns the locale that ended up active (English on any failure).
1223
+ */
1224
+ declare function loadLocale(code?: string | null): Promise<Locale>;
1225
+
1226
+ /**
1227
+ * Money formatting — the ONE place currency rendering happens.
1228
+ *
1229
+ * Ticket money is multi-currency: the org sets a default and each event can
1230
+ * override it (ISO 4217 code delivered with the event/chart payload). Until
1231
+ * those backend fields land, callers fall back to DEFAULT_CURRENCY, which is
1232
+ * kept at EUR so live buyer pages render exactly what they rendered when the
1233
+ * symbol was hardcoded. Flipping an org to USD/INR/… later is data, not code.
1234
+ *
1235
+ * Amounts are in MAJOR units (45 === €45) matching Category.price in
1236
+ * src/core/types.ts. If/when backend money fields arrive in minor units,
1237
+ * convert at the API boundary, not here.
1238
+ */
1239
+ declare const DEFAULT_CURRENCY = "USD";
1240
+ declare function setMoneyLocale(locale: string | undefined): void;
1241
+ /**
1242
+ * "€45" / "$1,500" / "45 €" (locale-dependent placement).
1243
+ * Whole amounts render without ".00" (design shows "$45", "$120"); pass
1244
+ * `fractionDigits` for fixed precision (e.g. 3 for "$0.045 / credit").
1245
+ */
1246
+ declare function formatMoney(amount: number, currency?: string, fractionDigits?: number): string;
1247
+ /** Bare symbol for input adornments ("€", "$", "₹"). */
1248
+ declare function currencySymbol(currency?: string): string;
1249
+
1250
+ export { ACCESSIBILITY_TYPES, type AccessibilityMeta, type AccessibilityType, type AvailabilityRule, type BoothObject, CHART_STORAGE_KEY, type Category, type CategoryTier, type ChartDoc, type ChartObject, type ChartTheme, DEFAULT_CURRENCY, type Dict, type ExpandedSeat, type Floor, type GAAreaObject, type HoldConflict, type HoldInfo, type ISeatmapRenderer, type Locale, type LodRung, type PickerCallbacks, PickerController, type PickerOptions, type PickerSeat, type PickerTransport, type Point, type RendererCallbacks, type RendererOptions, type RowObject, type RowSeatSlot, SUPPORTED_LOCALES, type SeatOverride, type SeatStatus, SeatmapRenderer, type SectionCategory, type SectionNode, type SectionObject, type SectionSummary, type SelectionLayer, type ShapeObject, type TableObject, type TextObject, UNGROUPED_ID, type ZoneDef, accessibilityMeta, allObjects, applyHidden, chartBounds, computeSections, createRenderer, currencySymbol, expandBooth, expandChart, expandRow, expandRowSlots, expandTable, floorObjects, floorsOf, formatDate, formatMoney, getLocale, hiddenObjectIds, isSectionHidden, layerOf, loadLocale, objectCenter, pointInPolygon, resolveLocale, setLocale, setMoneyLocale, setStringOverrides, stackFloors, t, tCount };