Appends objects. IDs must not already exist; validate afterwards if they come from user input.
function addObjects(
layout: LayoutDocument,
objects: readonly LayoutObject[],
): LayoutDocument;@openseat/core/src/operations.ts
Schema, validation, generators · Browser + Node
Framework-independent layout schema, validation, geometry and generators for OpenSeat.
Layout schema, validation, serialisation, migrations, geometry, generators and layout operations for OpenSeat. No React, Konva, DOM or network dependencies; runs in browsers, Node and workers.
npm i @openseat/coreInstall commands show the public npm names. The first npm release is pending; the packages are free and MIT licensed.
depends on zod
import {
validateLayout,
generateStraightRows,
generateCurvedRows,
arenaPreset,
createLayout,
} from "@openseat/core";Appends objects. IDs must not already exist; validate afterwards if they come from user input.
function addObjects(
layout: LayoutDocument,
objects: readonly LayoutObject[],
): LayoutDocument;@openseat/core/src/operations.ts
Moves seats into a row, detaching them from other rows and tables first. They are inserted at atIndex of seatObjectIds, or by their position along the row when omitted. The row is refitted (no seat moves); it stays freeform when no line or arc fits.
function addSeatsToRow(
layout: LayoutDocument,
rowId: string,
seatIds: readonly string[],
atIndex?: number,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
seatIds | readonly string[] | |
atIndexoptional | number |
@openseat/core/src/row-ops.ts
Aligns objects to the matching edge or centre of their combined bounds.
function alignObjects(
layout: LayoutDocument,
ids: readonly string[],
edge: AlignEdge,
): LayoutDocument;@openseat/core/src/operations.ts
Ancestors of an object, nearest first (e.g. a grouped seat: [row, section]).
function ancestorsOf(index: LayoutIndex, id: string): string[];@openseat/core/src/membership.ts
The outline of a ring segment between two radii and two angles: the outer arc from startAngle to endAngle, then the inner arc back from endAngle to startAngle. An inner radius of 0 or less closes the wedge at the centre instead.
function annularSectorPoints(
options: AnnularSectorOptions,
): Point[];| Parameter | Type | Description |
|---|---|---|
options | AnnularSectorOptions |
options takes AnnularSectorOptions:
PointnumbernumbernumbernumbernumberPoint[]@openseat/core/src/generators/shapes.ts
(Re)generates a section's rows and seats from parametric seating, keeping identities:
rowOrder, else geometrically) and row r keeps its object ID, other fields and label (unless relabel, or the row count changes while every row is still labelled as the section's rowLabeling numbers it: then all rows follow it). Its seats keep their object and public IDs by slot; holes (deleted seats) below the new slot count stay holes; labels follow the seat labeling except for labelLocked seats (or with keepLabels).seating, its rowOrder, and, when its outline is "auto", an outline recomputed from the seats.Without seating, the section's current seating is re-applied. Throws a RangeError when the section is missing or the seating is invalid.
function applySectionSeating(
layout: LayoutDocument,
sectionId: string,
seating?: SectionSeating,
opts?: ApplySectionSeatingOptions,
): SectionSeatingResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
sectionId | string | |
seatingoptional | SectionSeating | |
optsoptional | ApplySectionSeatingOptions | Default |
@openseat/core/src/section-ops.ts
Ready-made arenas, each with its ARENA_PRESET_TITLES title:
function arenaPreset(name: ArenaPresetName): ArenaPreset;import { arenaPreset, createArenaLayout, estimateArenaSeats } from "@openseat/core";
const options = { ...arenaPreset("hockey"), idPrefix: "a", center: { x: 0, y: 0 } };
console.log(estimateArenaSeats(options)); // 9758, exact and effectively free
const arena = createArenaLayout({ ...options, layoutId: "arena" });
// Presets: "hockey" | "basketball" | "concert-in-round" | "football"@openseat/core/src/generators/arena.ts
Assigns objects inside a section's boundary to it. Rows and tables move as a whole: one joins when at least half of its seats are inside (an exact half is decided by the seats' centroid). Loose seats go by their centre and GA areas by their centroid. Seat caches are updated. With mode: "sync", members of this section that now lie fully outside it leave it. count is the number of seats whose centre is inside the boundary.
function assignSeatsToSection(
layout: LayoutDocument,
sectionId: string,
opts?: { readonly mode?: "add" | "sync" },
): { layout: LayoutDocument; count: number };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
sectionId | string | |
optsoptional | { readonly mode?: "add" | "sync"; } | Default |
{ layout: LayoutDocument; count: number; }@openseat/core/src/operations.ts
Tags untagged rows, tables, loose seats and GA areas among ids with the section under their centre (seat centroid for groups, boundary centroid for GA areas). Objects already in a section are left alone.
function autoAssignSections(
layout: LayoutDocument,
ids: readonly string[],
): LayoutDocument;@openseat/core/src/operations.ts
The exact curvature range applySectionSeating accepts for a block seating, row by row: per-row lengths (rowSeats), aisles and the row's own radius are all taken into account, so it is never stricter or looser than the check itself. min is in [-100, 0] and max in [0, 100]; bound a block's curvature slider with them. minBlockCurvature and maxBlockCurvature are the uniform-row shortcuts for a block that does not exist yet.
function blockCurvatureRange(
seating: Pick<
BlockSeating,
| "rows"
| "rowSpacing"
| "seatSpacing"
| "seatsPerRow"
| "rowSeats"
| "aisles"
| "aisleWidth"
>,
): { readonly min: number; readonly max: number };| Parameter | Type | Description |
|---|---|---|
seating | Pick<BlockSeating, "rows" | "rowSpacing" | "seatSpacing" | "seatsPerRow" | "rowSeats" | "aisles" | "aisleWidth"> |
{ readonly min: number; readonly max: number; }@openseat/core/src/section-ops.ts
True when inner lies entirely inside outer (edges may touch).
@openseat/core/src/geometry.ts
True when the rectangles overlap or touch.
@openseat/core/src/geometry.ts
@openseat/core/src/geometry.ts
Strictly overlapping by more than tolerance on both axes; touching boxes do not overlap.
@openseat/core/src/label-layout.ts
Builds an arc (wedge) section without adding it: concentric rows between two angles around center, outer rows holding more seats. The section's origin is the area centroid of its ring-segment outline (rotation 0), and the seating stores the centre relative to it; the outline is "auto".
function buildArcSection(
layout: LayoutDocument,
params: ArcSectionParams,
): { objects: LayoutObject[]; sectionId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
params | ArcSectionParams |
{ objects: LayoutObject[]; sectionId: string; }@openseat/core/src/section-ops.ts
Builds a parametric block section (rows x seats, optionally curved, ragged or staggered) without adding it: the section followed by each row's seats and the row. IDs are unique against layout. Used for drawing previews; createBlockSection adds the result.
function buildBlockSection(
layout: LayoutDocument,
params: BlockSectionParams,
): { objects: LayoutObject[]; sectionId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
params | BlockSectionParams |
{ objects: LayoutObject[]; sectionId: string; }@openseat/core/src/section-ops.ts
Builds the inventory manifest of a layout.
sectionId (row or table; the source of truth), else its own; only sections that exist count. Row and table come from the group that owns the seat.inventoryId (capacity 1); tables with bookAsWhole (kind "table", capacity = seats); zone sections (showSeats: false) as kind "ga" with objectType: "section", seated: true, inventory ID = the section ID and their zone capacity. The seats, and whole tables, inside a zone section are not sold on their own and are left out of seats and areas.Optional fields are omitted rather than undefined, so the manifest serialises compactly and compares equal after a JSON round trip.
function buildInventoryManifest(
layout: LayoutDocument,
): InventoryManifest;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument |
layout takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstringimport { buildInventoryManifest, diffManifests, validateLayout } from "@openseat/core";
const result = validateLayout(body.layout); // never trust the client's layout
if (!result.valid || !result.layout) throw new Error("invalid layout"); // result.errors says why
const next = buildInventoryManifest(result.layout);
const { removedSeatIds } = diffManifests(previousManifest, next);
// Refuse the save if any removed seat is held or sold for a live event.Your backend: the REST contract
@openseat/core/src/manifest.ts
Generates the rows and seats of a section from its seating, without touching any document. Row r (0 = front) gets the ID ${prefix}:r${r+1}, its slot k the seat object ID ${prefix}:r${r+1}:s${k+1} and the public seat ID ${section.label}-${rowLabel}-${seatLabel} (made unique against existing). Row labels follow section.rowLabeling (A, B, C… by default), seat labels the seating's seatLabeling. Seats take the section's category. Throws a RangeError when the section has no seating or the seating is invalid.
function buildSectionContents(
section: Section,
opts?: BuildSectionContentsOptions,
): SectionContents;| Parameter | Type | Description |
|---|---|---|
section | Section | |
optsoptional | BuildSectionContentsOptions | Default |
@openseat/core/src/section-ops.ts
Corners of a width × height rectangle centred on the object's origin, in world space.
| Parameter | Type | Description |
|---|---|---|
width | number | |
height | number | |
placement | { position: Point; rotation: number; } |
Point[]@openseat/core/src/object-geometry.ts
Reports structural problems: shared or repeated group members, stale seat caches, duplicate labels, empty rows, uncategorised seats, and invalid parametric fields (row layout, section row order and seating).
Reference checks (dangling IDs) and geometry checks stay in validateLayout; this function assumes nothing about them and skips references it cannot resolve.
function checkStructure(doc: LayoutDocument): {
errors: LayoutIssue[];
warnings: LayoutIssue[];
};| Parameter | Type | Description |
|---|---|---|
doc | LayoutDocument |
doc takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstring{ errors: LayoutIssue[]; warnings: LayoutIssue[]; }@openseat/core/src/structure-checks.ts
Deep-copies the objects of ids (expanded recursively) and the categories they reference into a clipboard payload. Null when nothing is copied.
function cloneForClipboard(
layout: LayoutDocument,
ids: readonly string[],
): ClipboardPayload | null;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] |
ClipboardPayload | null@openseat/core/src/operations.ts
Turns a polygon into a section in place: same ID, outline, placement, layer flags, category, fill and stroke; the label is the next free section label. Throws a RangeError when polygonId is not a polygon.
function convertToSection(
layout: LayoutDocument,
polygonId: string,
): { layout: LayoutDocument; sectionId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
polygonId | string |
{ layout: LayoutDocument; sectionId: string; }@openseat/core/src/section-ops.ts
Convex hull by Andrew's monotone chain. Returns the hull vertices without a repeated closing point and without collinear points along edges. Fewer than three distinct input points are returned as they are (deduplicated).
@openseat/core/src/geometry.ts
buildArcSection plus adding the objects to the layout.
function createArcSection(
layout: LayoutDocument,
params: ArcSectionParams,
): { layout: LayoutDocument; sectionId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
params | ArcSectionParams |
{ layout: LayoutDocument; sectionId: string; }@openseat/core/src/section-ops.ts
A complete arena layout document: generateArena's objects, venue and categories in a new layout whose focal point is the field centre. When the venue would reach past the top or left canvas edge, everything is moved (by whole grid cells) to make room, and the canvas grows to fit the venue and every object.
function createArenaLayout(
options: CreateArenaLayoutOptions,
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
options | CreateArenaLayoutOptions |
options takes CreateArenaLayoutOptions:
stringstringstringPointArenaFieldreadonly ArenaTier[]ArenaNumberingSeatLabeling{ readonly margin: number; readonly shape?: "rect" | "ellipse"; }ArenaFieldKind = "rink"stringboolean@openseat/core/src/generators/arena.ts
buildBlockSection plus adding the objects to the layout.
function createBlockSection(
layout: LayoutDocument,
params: BlockSectionParams,
): { layout: LayoutDocument; sectionId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
params | BlockSectionParams |
{ layout: LayoutDocument; sectionId: string; }import { createLayout, createBlockSection, updateSectionSeating, validateLayout } from "@openseat/core";
const empty = createLayout({
id: "hall",
name: "Main hall",
width: 1200,
height: 800,
categories: [{ id: "general", name: "General", colour: "#2563EB" }], // "colour", not "color"
});
const { layout, sectionId } = createBlockSection(empty, {
position: { x: 600, y: 400 },
categoryId: "general",
seating: {
kind: "block",
rows: 10,
seatsPerRow: 20,
seatSpacing: 24,
rowSpacing: 30,
curvature: 20, // -100..100; > 0 arcs around the stage in front
align: "center",
aisles: [10],
aisleWidth: 30,
seatShape: { kind: "circle", radius: 9 },
seatLabeling: { format: "numeric", start: 1, step: 1, direction: "ltr" },
},
});
// Change a parameter later: seats that still have a slot keep their IDs.
const { layout: bigger, report } = updateSectionSeating(layout, sectionId, { rows: 14, curvature: 35 });
console.log(validateLayout(bigger).valid, report.removedSeatIds); // true []@openseat/core/src/section-ops.ts
A new floor divider (not yet added): an open path through the world points (at least 2), 1 unit wide, with caption (trimmed; left out when empty) drawn on it as "▾ CAPTION ▾". Throws a RangeError for fewer than 2 points.
function createDivider(
layout: LayoutDocument,
points: readonly Point[],
caption: string,
): CustomPath;@openseat/core/src/zone-ops.ts
A new footnote label (not yet added) centred at at: 12 units, bold, letter-spaced 0.08 em, in muted grey (#9e9e9e), such as "ALL THE SECTIONS ARE SEATED". Throws a RangeError for empty text.
function createFootnote(
layout: LayoutDocument,
at: Point,
text: string,
): Label;@openseat/core/src/zone-ops.ts
Creates a random object ID. Does not depend on DOM typings.
function createId(prefix?: string): string;| Parameter | Type | Description |
|---|---|---|
prefixoptional | string | Default |
string@openseat/core/src/ids.ts
Creates an empty layout at the current schema version. The result is not validated; call validateLayout after adding objects.
function createLayout(options: CreateLayoutOptions): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
options | CreateLayoutOptions |
options takes CreateLayoutOptions:
import { createLayout, createBlockSection, updateSectionSeating, validateLayout } from "@openseat/core";
const empty = createLayout({
id: "hall",
name: "Main hall",
width: 1200,
height: 800,
categories: [{ id: "general", name: "General", colour: "#2563EB" }], // "colour", not "color"
});
const { layout, sectionId } = createBlockSection(empty, {
position: { x: 600, y: 400 },
categoryId: "general",
seating: {
kind: "block",
rows: 10,
seatsPerRow: 20,
seatSpacing: 24,
rowSpacing: 30,
curvature: 20, // -100..100; > 0 arcs around the stage in front
align: "center",
aisles: [10],
aisleWidth: 30,
seatShape: { kind: "circle", radius: 9 },
seatLabeling: { format: "numeric", start: 1, step: 1, direction: "ltr" },
},
});
// Change a parameter later: seats that still have a slot keep their IDs.
const { layout: bigger, report } = updateSectionSeating(layout, sectionId, { rows: 14, curvature: 35 });
console.log(validateLayout(bigger).valid, report.removedSeatIds); // true []@openseat/core/src/factory.ts
Wraps the selected rows, tables, loose seats and GA areas (a selected seat stands for its row or table) in a new section with a custom outline around them, and makes them its members. The rows are ordered front to back (nearest the layout's focal point first when it has one). Returns sectionId: null and the same layout when the selection holds nothing that can join a section.
function createSectionFromSelection(
layout: LayoutDocument,
ids: readonly string[],
opts?: SectionFromSelectionOptions,
): { layout: LayoutDocument; sectionId: string | null };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
optsoptional | SectionFromSelectionOptions | Default |
{ layout: LayoutDocument; sectionId: string | null; }@openseat/core/src/section-ops.ts
A new GA zone (not yet added to the layout) with an object ID and an inventory ID unused in layout. rect, rounded and trapezoid zones are built from rect and centred on it; a polygon zone from points, its origin at their centroid. A rounded zone gets cornerRadius = min(w, h) × 0.12; a trapezoid keeps one edge w wide and insets the other by 15% on each side (bottom inset by default, top with flip). Throws a RangeError for a degenerate shape, a bad capacity or an empty label.
function createZone(
opts: CreateZoneOptions,
layout: LayoutDocument,
): GaArea;@openseat/core/src/zone-ops.ts
Everything below an object: a section's rows, tables, GA areas and loose seats, each group followed by its seats; a row's or table's seats. Empty for other objects.
function descendantsOf(index: LayoutIndex, id: string): string[];@openseat/core/src/membership.ts
Turns a parametric row into a freeform one. Seats are untouched.
function detachRowLayout(
layout: LayoutDocument,
rowId: string,
): RegenerationResult;@openseat/core/src/row-ops.ts
Compares two manifests by public seat ID. A seat ID listed twice in one manifest (an invalid layout) is compared by its first entry.
function diffManifests(
prev: InventoryManifest,
next: InventoryManifest,
): ManifestDiff;@openseat/core/src/manifest.ts
Removes a section but keeps its contents, which lose their sectionId.
function dissolveSection(
layout: LayoutDocument,
sectionId: string,
): LayoutDocument;@openseat/core/src/section-ops.ts
@openseat/core/src/geometry.ts
Shortest distance from p to the segment ab.
@openseat/core/src/geometry.ts
Spaces objects so their centres are evenly spread between the first and last. Needs three or more.
function distributeObjects(
layout: LayoutDocument,
ids: readonly string[],
axis: "x" | "y",
): LayoutDocument;@openseat/core/src/operations.ts
Spreads count seats over the four edges of a width x height table in proportion to the edge lengths (largest remainder method; ties go to the first edge clockwise from the top).
function distributeSeatsOnEdges(
count: number,
width: number,
height: number,
): TableEdgeCounts;@openseat/core/src/table-ops.ts
The text a divider draws for caption: upper-cased between two markers ("▾ GROUND FLOOR ▾").
function dividerCaptionText(caption: string): string;| Parameter | Type | Description |
|---|---|---|
caption | string |
string@openseat/core/src/object-geometry.ts
function drawBand(type: LayoutObject["type"]): number;@openseat/core/src/object-geometry.ts
Copies objects, offset by offset. Sections are copied with their contents and rows and tables with their seats; the copies reference each other. Every copy gets a new object ID and every copied seat a new public seat ID (<old>-copy, then <old>-copy-2, …), so the result never has duplicates. Copies whose section was not copied keep it, unless opts.sectionId says otherwise.
function duplicateObjects(
layout: LayoutDocument,
ids: readonly string[],
offset: Point,
opts?: DuplicateOptions,
): DuplicateResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
offset | Point | |
optsoptional | DuplicateOptions | Default |
@openseat/core/src/operations.ts
An ellipse of width x height centred on center, clockwise on screen from its rightmost point.
| Parameter | Type | Description |
|---|---|---|
width | number | |
height | number | |
segmentsoptional | number | Default |
centeroptional | Point | Default |
Point[]@openseat/core/src/generators/shapes.ts
Writes a fitted layout to a row that has none, when the fit reproduces its seats within ROW_LAYOUT_FIT_TOLERANCE. Never moves seats. fitted is true when the row has a layout afterwards; residual is the fit error (0 for rows that already had a layout, Infinity for rows without seats).
function ensureRowLayout(
layout: LayoutDocument,
rowId: string,
): { layout: LayoutDocument; fitted: boolean; residual: number };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string |
{ layout: LayoutDocument; fitted: boolean; residual: number; }@openseat/core/src/row-ops.ts
Seats generateArena would create for options, computed from the section plans without generating any seat (milliseconds even for invalid sizes). Exact for the generator's default sections; use it to warn about or reject oversized arenas before generating. Throws a GeneratorInputError for invalid options, including a row longer than ARENA_LIMITS.maxSeatsPerRow seats.
function estimateArenaSeats(options: ArenaOptions): number;| Parameter | Type | Description |
|---|---|---|
options | ArenaOptions |
options takes ArenaOptions:
stringPointArenaFieldreadonly ArenaTier[]ArenaNumberingSeatLabeling{ readonly margin: number; readonly shape?: "rect" | "ellipse"; }ArenaFieldKind = "rink"stringbooleannumber@openseat/core/src/generators/arena.ts
Sections generateArena would create for options, without generating any seat. Sides too short for their sections are left empty, so this can be well below the requested counts (a round field has no straight sides). Throws a GeneratorInputError for invalid options.
function estimateArenaSections(options: ArenaOptions): number;| Parameter | Type | Description |
|---|---|---|
options | ArenaOptions |
options takes ArenaOptions:
stringPointArenaFieldreadonly ArenaTier[]ArenaNumberingSeatLabeling{ readonly margin: number; readonly shape?: "rect" | "ellipse"; }ArenaFieldKind = "rink"stringbooleannumber@openseat/core/src/generators/arena.ts
Conservative box of one line of text: `n × fontSize × (bold ? 0.68 : 0.62)
wide and1.2 × fontSizehigh, forn` characters. Wider than real text in common UI fonts, so a renderer that shrinks its measured text to fit this box can guarantee that what it draws stays inside what the label layout reserved.function estimateTextBox(
text: string,
fontSize: number,
opts?: TextBoxOptions,
): { width: number; height: number };| Parameter | Type | Description |
|---|---|---|
text | string | |
fontSize | number | |
optsoptional | TextBoxOptions | Default |
{ width: number; height: number; }@openseat/core/src/object-geometry.ts
Approximate text box for a label. Exact metrics need a font engine, which core does not have; renderers measure real text, but core needs a stable estimate for hit testing and bounds that does not depend on the browser.
function estimateTextSize(
text: string,
fontSize: number,
): { width: number; height: number };| Parameter | Type | Description |
|---|---|---|
text | string | |
fontSize | number |
{ width: number; height: number; }@openseat/core/src/object-geometry.ts
Expands bounds by amount on every side.
@openseat/core/src/geometry.ts
Adds everything that moves with the given objects, recursively. A section brings every row, table, GA area and loose seat (a seat in no row or table) whose sectionId is the section; a row or table brings its seats. Moving a table moves its chairs and moving a section moves its contents: this is the single place that rule is defined.
Output order: the known input IDs first (deduplicated), then the added members in document order (the iteration order of objectsById). Unknown IDs are ignored. One pass over the objects, so O(n) per call.
function expandWithMembers(
ids: readonly string[],
objectsById: ReadonlyMap<string, LayoutObject>,
): string[];| Parameter | Type | Description |
|---|---|---|
ids | readonly string[] | |
objectsById | ReadonlyMap<string, LayoutObject> |
string[]@openseat/core/src/object-geometry.ts
Adds seats at one end of a row (count > 0) or trims them (count < 0). Seats already in the row stay where they are and keep their labels and seat IDs. Extending or trimming the start of a left-to-right numbered row shifts labeling.start so the new seats continue the numbers downwards when that stays at 1 or above; otherwise the new seats continue after the row's highest number, the row's labeling is removed (it keeps custom numbers) and the result's message says so. New seat IDs follow the row's pattern (prefix + label). Never produces label 0, a negative number or a duplicate.
function extendRow(
layout: LayoutDocument,
rowId: string,
end: "start" | "end",
count: number,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
end | "start" | "end" | |
count | number |
@openseat/core/src/row-ops.ts
Takes seats out of their rows and tables (parametric rows keep holes, so nothing else moves). With asRow, the seats become a new row (ID row-N, label rowLabel or the next free label) with a fitted layout when one fits. Seats never move.
function extractSeats(
layout: LayoutDocument,
seatIds: readonly string[],
opts?: { readonly asRow?: boolean; readonly rowLabel?: string },
): { layout: LayoutDocument; rowId: string | null };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
seatIds | readonly string[] | |
optsoptional | { readonly asRow?: boolean; readonly rowLabel?: string; } | Default |
{ layout: LayoutDocument; rowId: string | null; }@openseat/core/src/row-ops.ts
Text boxes (layoutTextBoxes) that overlap drawn seats (seatBoxes), one entry per text and seat, in text order. At "overview" no seats are drawn (the map shows filled sections), so only "detail" can report overlaps. The check behind the "no text over seats" gate.
function findLabelSeatOverlaps(
layout: LayoutDocument,
opts: LabelSeatOverlapOptions,
): LabelSeatOverlap[];@openseat/core/src/label-layout.ts
Rows whose stored seat positions deviate from their layout by more than tolerance, with the largest deviation. Rows without a layout are skipped.
function findRowLayoutDrift(
layout: LayoutDocument,
tolerance?: number,
): { rowId: string; maxDeviation: number }[];| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
toleranceoptional | number | Default |
{ rowId: string; maxDeviation: number; }[]@openseat/core/src/row-geometry.ts
Grows the canvas until every object fits inside it, with margin to spare. Content above or left of the origin is shifted (all objects together, locked ones included, with the venue outline and focal point) by a whole number of grid cells, so grid alignment survives. The canvas never shrinks; a layout that already fits is returned unchanged.
function fitCanvasToContent(
layout: LayoutDocument,
margin?: number,
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
marginoptional | number | Default |
@openseat/core/src/operations.ts
Fits a parametric layout to seats given in row order. Returns null for no seats. Tries a principal-axis line and a Kasa circle: the line wins when it fits within max(0.5, 2% of the median step) or the circle is nearly flat (radius above 50 times the extent); otherwise the circle wins when it fits within the same tolerance; otherwise the better of the two is returned with its residual. The placement is anchored at the first seat ("start") with the tangent pointing towards the second seat.
function fitRowLayout(seats: readonly Seat[]): RowLayoutFit | null;@openseat/core/src/row-geometry.ts
Formats a label index. numeric: n >= 0; alpha: n >= 1 in spreadsheet order (1 = A, 26 = Z, 27 = AA); alpha-lower: the same in lowercase; roman: 1..3999, uppercase. Throws a RangeError when n is out of range.
function formatLabelIndex(n: number, format: LabelFormat): string;@openseat/core/src/labeling.ts
Generates a concentric arena: the field and every tier's sections with their rows and seats, a venue outline and per-tier categories. Section k of a tier (in numbering order) is labelled firstNumber + k; its ID is ${idPrefix}-sec-${label} and its rows and seats follow buildSectionContents (${sectionId}:r1, ${sectionId}:r1:s1, seat IDs ${label}-${row}-${seat}). The field is a stage with the field kind's style and markings. Throws a GeneratorInputError for invalid options, beyond ARENA_LIMITS, or when estimateArenaSeats exceeds ARENA_LIMITS.maxSeats.
function generateArena(options: ArenaOptions): GeneratedArena;| Parameter | Type | Description |
|---|---|---|
options | ArenaOptions |
options takes ArenaOptions:
stringPointArenaFieldreadonly ArenaTier[]ArenaNumberingSeatLabeling{ readonly margin: number; readonly shape?: "rect" | "ellipse"; }ArenaFieldKind = "rink"stringboolean@openseat/core/src/generators/arena.ts
Generates a grid of booths numbered left to right, top to bottom. Deterministic IDs.
function generateBoothGrid(options: BoothGridOptions): Booth[];| Parameter | Type | Description |
|---|---|---|
options | BoothGridOptions |
options takes BoothGridOptions:
stringPointnumbernumbernumbernumbernumbernumberstring = "Booth "stringstringnumber = 1Booth[]@openseat/core/src/generators/booths.ts
Generates concentric arc rows. Seats are spaced evenly by angle within each row, or by arc length when seatSpacing is given. Each row gets a parametric layout whose signed radius reproduces the generated seats.
function generateCurvedRows(
options: CurvedRowsOptions,
): GeneratedRows;| Parameter | Type | Description |
|---|---|---|
options | CurvedRowsOptions |
options takes CurvedRowsOptions:
numbernumberPointnumbernumbernumbernumberreadonly string[]RowLabelingbooleannumberstringstringstringSeatShapeSeatNumberingimport { createLayout, generateCurvedRows, validateLayout } from "@openseat/core";
const layout = createLayout({ id: "amphi", name: "Amphitheatre", width: 1200, height: 900 });
layout.objects = generateCurvedRows({
idPrefix: "bowl", // unique per generated block
rowCount: 8,
seatsPerRow: 24,
center: { x: 600, y: 120 }, // the stage
innerRadius: 220,
rowSpacing: 34,
startAngle: 30, // degrees: 0 points right, 90 points down
endAngle: 150,
tangentRotation: true, // seats face the centre
}).objects;
validateLayout(layout).valid; // true: 192 seats@openseat/core/src/generators/rows.ts
Generates a rectangular table with seats along selected edges. Seats are numbered clockwise starting from the top edge; on each edge the n seats sit at k / (n + 1) of the edge length. The table records the parameters as its arrangement.
function generateRectTable(
options: RectTableOptions,
): GeneratedRows;| Parameter | Type | Description |
|---|---|---|
options | RectTableOptions |
options takes RectTableOptions:
stringPointnumbernumberRectTableSeatCountsnumberstringstringstringSeatShapeSeatNumbering@openseat/core/src/generators/tables.ts
Generates a round table with seats spread evenly around it, clockwise from startAngle. The table records the parameters as its arrangement.
function generateRoundTable(
options: RoundTableOptions,
): GeneratedRows;| Parameter | Type | Description |
|---|---|---|
options | RoundTableOptions |
options takes RoundTableOptions:
stringPointnumbernumbernumbernumberstringstringstringSeatShapeSeatNumbering@openseat/core/src/generators/tables.ts
Fills a polygon with rows of seats, as for a stadium or arena section.
The rows come from planFillRows: every seat lies fully inside the boundary, and a concave section can give a row several spans. Each row gets a straight parametric layout (span breaks and aisles become gaps), and the section records the fill as seating (kind "fill") with its rowOrder, so it can be refilled later without losing seat IDs.
function generateSectionSeating(
options: SectionSeatingOptions,
): GeneratedRows & { section: Section };| Parameter | Type | Description |
|---|---|---|
options | SectionSeatingOptions |
options takes SectionSeatingOptions:
stringstringreadonly Point[]numbernumbernumbernumber = 2numbernumber = seatSpacingnumber = 1booleanreadonly string[]readonly RowCategoryBand[]string{ kind: "circle"; radius: number; } | { kind: "rect"; width: number; height: number; }stringSeatNumberingGeneratedRows & { section: Section; }@openseat/core/src/generators/section.ts
Generates straight, optionally staggered rows with aisle gaps. Seat positions are absolute layout coordinates. Output is deterministic for the same options. Each row gets a parametric layout (start anchor at its first seat, aisle gaps, seat rotation "row", labeling from the numbering).
function generateStraightRows(
options: StraightRowsOptions,
): GeneratedRows;| Parameter | Type | Description |
|---|---|---|
options | StraightRowsOptions |
options takes StraightRowsOptions:
numbernumberPointnumbernumberreadonly string[]RowLabelingnumberreadonly number[]number = seatSpacingstringstringstringSeatShapeSeatNumberingimport { useState } from "react";
import { createLayout, generateStraightRows } from "@openseat/core";
import { createRestAdapter } from "@openseat/adapter-rest";
import { BookingMap, useInventory } from "@openseat/react";
const layout = createLayout({ id: "hall", name: "Main hall", width: 1000, height: 800 });
layout.objects = generateStraightRows({
idPrefix: "stalls",
rowCount: 10,
seatsPerRow: 20,
origin: { x: 100, y: 100 },
seatSpacing: 28,
rowSpacing: 32,
aisleAfter: [10],
}).objects;
const api = createRestAdapter({ baseUrl: "https://api.example.com" });
export function Venue() {
const { statuses } = useInventory(api, "show-2026-11-20");
const [selection, setSelection] = useState<readonly string[]>([]);
return (
<BookingMap
layout={layout}
statuses={statuses}
selectedSeatIds={selection}
onSelectionChange={(next) => setSelection(next)}
rules={{ maxSeats: 8, preventOrphans: true }}
/>
);
}@openseat/core/src/generators/rows.ts
The seats of a table arrangement, unrotated around center, with the same IDs and labels as generateRoundTable and generateRectTable. Unlike those, an arrangement without seats gives an empty list.
function generateTableSeats(options: TableSeatsOptions): Seat[];| Parameter | Type | Description |
|---|---|---|
options | TableSeatsOptions |
options takes TableSeatsOptions:
stringPointTableShapeTableArrangementnumberstringstringstringSeatShapeSeatNumberingSeat[]@openseat/core/src/generators/tables.ts
@openseat/core/src/geometry.ts
Returns object IDs in paint order (bottom first). Hit testing walks this order in reverse so the topmost object wins.
function getDrawOrder(objects: readonly LayoutObject[]): string[];@openseat/core/src/object-geometry.ts
Seat object IDs owned by a row or table. Empty for other objects.
function getMemberSeatObjectIds(
obj: LayoutObject,
): readonly string[];@openseat/core/src/object-geometry.ts
World-space axis-aligned bounds of an object, or null for objects with no geometry of their own (rows).
function getObjectBounds(obj: LayoutObject): Bounds | null;@openseat/core/src/object-geometry.ts
function getObjectHitShape(obj: LayoutObject): HitShape;@openseat/core/src/object-geometry.ts
Bounds of the transformed points, computed without a second pass over the input.
@openseat/core/src/geometry.ts
Makes a row from loose or grouped seats. Seats are ordered along their principal axis (oriented left to right, or top to bottom when vertical) and detached from any row or table they were in (a parametric source row keeps their slots as holes; a table's capacity follows). Source rows left empty are removed. The row is rotated along its first-to-last seat direction and joins the seats' common section; seat caches (rowLabel, sectionId) are updated. Returns the new row's ID.
function groupSeatsIntoRow(
layout: LayoutDocument,
seatObjectIds: readonly string[],
rowLabel: string,
): { layout: LayoutDocument; rowId: string | null };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
seatObjectIds | readonly string[] | |
rowLabel | string |
{ layout: LayoutDocument; rowId: string | null; }@openseat/core/src/operations.ts
@openseat/core/src/object-geometry.ts
True when point hits the shape. tolerance widens the target, which helps touch input.
boolean@openseat/core/src/object-geometry.ts
Builds (or returns the memoised) membership index of a layout. Memoised by the objects array, so every operation on the same immutable document shares one O(n) build (for the most recently used documents).
function indexLayout(layout: {
readonly objects: readonly LayoutObject[];
}): LayoutIndex;@openseat/core/src/membership.ts
Converts a zero-based index to a spreadsheet-style label: 0 → A, 25 → Z, 26 → AA. Used for default row labels.
function indexToLetters(index: number): string;| Parameter | Type | Description |
|---|---|---|
index | number |
string@openseat/core/src/ids.ts
The shared prefix of a group's public seat IDs, for numbering new seats in the same pattern. Returns the longest common prefix that ends in a non-digit when every seat ID is that prefix followed by digits (so c:A:1…c:A:5 gives c:A:), and fallback otherwise.
function inferSeatIdPrefix(
seatIds: readonly string[],
fallback: string,
): string;| Parameter | Type | Description |
|---|---|---|
seatIds | readonly string[] | |
fallback | string |
string@openseat/core/src/ids.ts
Infers a table's arrangement from where its seats are, for tables drawn before arrangements existed or edited seat by seat.
seatObjectIds[0]).seatOffset).A table without seats gives the generator defaults (seat radius + 4 clear of the edge, first seat at the top). The table's stored arrangement is ignored; see setTableArrangement for the one in effect.
function inferTableArrangement(
layout: LayoutDocument,
tableId: string,
): TableArrangement;@openseat/core/src/table-ops.ts
Inserts count new seats before slot atSlot (0..slotCount). Holes and gaps at or after atSlot shift with the seats; the first retained seat stays where it is.
Existing seats keep their labels and seat IDs. New seats take the labeling's label for their slot when it is free (inserting at slot 0 of a left-to-right row shifts the labeling start down, as extendRow does), else the next number after the row's highest; their seat IDs follow the row's pattern (prefix + label). When the labels then no longer follow the row's labeling, the labeling is removed (the row keeps custom numbers) and the result's message says so.
function insertSeats(
layout: LayoutDocument,
rowId: string,
atSlot: number,
count?: number,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
atSlot | number | |
countoptional | number | Default |
@openseat/core/src/row-ops.ts
Inserts a vertex on edge edgeIndex (from vertex edgeIndex to the next; closed outlines wrap around), at world or the edge midpoint.
function insertVertex(
layout: LayoutDocument,
id: string,
edgeIndex: number,
world?: Point,
): VertexEditResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
id | string | |
edgeIndex | number | |
worldoptional | Point |
@openseat/core/src/section-ops.ts
Result for an unexpected exception inside validation. Validation never throws: a bug or a hostile input that trips one becomes an error instead.
function internalValidationFailure(
cause: unknown,
): ValidationResult;@openseat/core/src/validate.ts
True when the object or any ancestor is hidden. Unknown IDs are not hidden.
function isEffectivelyHidden(
index: LayoutIndex,
id: string,
): boolean;@openseat/core/src/membership.ts
True when the object or any ancestor is locked. Unknown IDs are not locked.
function isEffectivelyLocked(
index: LayoutIndex,
id: string,
): boolean;@openseat/core/src/membership.ts
A polygon is simple when no two non-adjacent edges touch. Adjacent edges always share a vertex, so they are excluded from the check. See polygonSimplicity for a version with a work budget.
function isSimplePolygon(points: readonly Point[]): boolean;@openseat/core/src/geometry.ts
True for a section shown as a zone (showSeats: false): sold by quantity, seats not drawn.
function isZoneSection(
obj: LayoutObject | undefined | null,
): obj is Section;@openseat/core/src/zone-ops.ts
Seat distance for a round table whose radius changes from oldRadius to newRadius: the clear gap oldSeatDistance - oldRadius is kept, floored at the seat radius so seats never slide under the table edge.
function keptRoundSeatDistance(
oldRadius: number,
oldSeatDistance: number,
newRadius: number,
seatRadius: number,
): number;| Parameter | Type | Description |
|---|---|---|
oldRadius | number | |
oldSeatDistance | number | |
newRadius | number | |
seatRadius | number |
number@openseat/core/src/table-ops.ts
Derived places of a row's seats, in seatObjectIds order. Throws when the row has no layout.
function layoutRowSeats(row: Row): RowSlotPose[];| Parameter | Type | Description |
|---|---|---|
row | Row |
row takes Row:
"row"stringstring[]stringbooleanbooleannumberPointnumberstringRowLayout"start" | "end" | "none" | "both"string@openseat/core/src/row-geometry.ts
World positions and seat rotations of slotCount slots. Rotation is placement.rotation + tangent for "tangent" (the default), placement.rotation for "row", and NaN for "keep".
function layoutRowSlots(
placement: Placement,
spec: RowLayout,
slotCount: number,
): RowSlotPose[];@openseat/core/src/row-geometry.ts
Every piece of text the layout draws at a level of detail, with its reserved world box: section titles (sectionTitlePlacement), row labels (detail only; resolveRowLabelPlacements, hidden ones left out), zone labels (GA zones and zone sections), stage labels, free labels, divider captions, and table and booth labels (objectLabelPlacement). Hidden objects and the contents of zone sections are left out, and so are seat numbers, which sit inside their seats by design. Memoised per layout object.
function layoutTextBoxes(
layout: LayoutDocument,
opts: { readonly lod: LabelLod },
): readonly LayoutTextBox[];| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
opts | { readonly lod: LabelLod; } |
readonly LayoutTextBox[]@openseat/core/src/label-layout.ts
Maps a point from an object's local space into layout space. Local points are rotated around the local origin, then translated by position.
@openseat/core/src/geometry.ts
Creates a seat object. Optional references are omitted rather than set to undefined, so JSON round trips compare equal.
| Parameter | Type | Description |
|---|---|---|
input | SeatInput |
input takes SeatInput:
stringstringnumberstringPointnumberstringstringstringSeatShape@openseat/core/src/generators/common.ts
The strongest positive block curvature at which the longest row (longestRow seats) still fits on the tightest (front) row's radius, with the same limits as minBlockCurvature. Rows grow outwards with positive curvature, so the row count does not matter. 100 when any curvature works.
function maxBlockCurvature(
seatSpacing: number,
longestRow?: number,
): number;| Parameter | Type | Description |
|---|---|---|
seatSpacing | number | |
longestRowoptional | number | Default |
number@openseat/core/src/section-ops.ts
The most seats a table can take without neighbouring seats coming closer than minGap. Round tables: the seats sit on a circle of radius seatDistance (from a round arrangement, else the radius plus the rect offset), so n seats fit while the chord between neighbours is at least 2 * seatRadius + minGap. Rect tables: the sum over the edges of the seats whose spacing edgeLength / (n + 1) stays at least that pitch (at least one per edge, centred).
function maxSeatsForTable(
shape: TableShape,
arrangement: TableArrangement,
seatShape: SeatShape,
minGap?: number,
): number;| Parameter | Type | Description |
|---|---|---|
shape | TableShape | |
arrangement | TableArrangement | |
seatShape | SeatShape | |
minGapoptional | number | Default |
number@openseat/core/src/table-ops.ts
The regeneration strategy, in one place:
slots, each generated seat takes over the listed existing seat; otherwise the i-th new seat takes over the i-th existing seat. A retained seat keeps its object ID, public seatId, accessibility metadata, category, name, tags and locked/visible flags. Its sectionId and rowLabel follow the group. Geometry and shape come from the generator. Labels are regenerated unless keepLabels is set or the seat is labelLocked.${group.id}:s${slot + 1} and a seat ID in the pattern of the group's existing seat IDs (see inferSeatIdPrefix), numbered slot + 1 plus the offset the retained seats share (so a row numbered from 5 continues 9, 10…), made unique against the whole layout.Nothing outside the group is changed. Unchanged seats and an unchanged group keep their object references; when nothing changes at all, the input layout is returned.
function mergeRegeneratedSeats(
layout: LayoutDocument,
group: Row | Table,
generated: readonly Seat[],
options?: RetentionOptions,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
group | Row | Table | |
generated | readonly Seat[] | |
optionsoptional | RetentionOptions | Default |
@openseat/core/src/regenerate.ts
Merges rows of one section into the first one. Rows are ordered along the first row's direction and their seats concatenated (a row running the other way is reversed); the layout is refitted with gaps for the distance between rows. The other rows are removed (their seats are kept). Throws a RangeError when the rows belong to different sections, or when their seats do not lie on one line or arc (parallel rows cannot be merged). When the merged seats would repeat a label, the row is numbered afresh from its labeling and renumbered is true.
function mergeRows(
layout: LayoutDocument,
rowIds: readonly string[],
opts?: { readonly rowLabel?: string },
): { layout: LayoutDocument; rowId: string; renumbered: boolean };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowIds | readonly string[] | |
optsoptional | { readonly rowLabel?: string; } | Default |
{ layout: LayoutDocument; rowId: string; renumbered: boolean; }@openseat/core/src/row-ops.ts
Upgrades a document through the registered migration chain until it reaches SCHEMA_VERSION. Documents from a newer schema are rejected rather than silently downgraded.
function migrateLayout(
input: unknown,
migrations?: readonly LayoutMigration[],
currentVersion?: number,
): MigrationResult;| Parameter | Type | Description |
|---|---|---|
input | unknown | |
migrationsoptional | readonly LayoutMigration[] | Override for tests or custom build pipelines. Default |
currentVersionoptional | number | Override for tests; defaults to the built-in schema version. Default |
@openseat/core/src/migrate.ts
The most negative block curvature that still gives every row a radius larger than the seat spacing and lets the longest row (longestRow seats, default 1) fit on the tightest (back) row's radius: it bends through at most 162 degrees and neighbouring seats stay at least 0.95 x the seat spacing apart. Curvature below it fails applySectionSeating; use it to bound a curvature slider. -100 when any curvature works.
function minBlockCurvature(
rows: number,
rowSpacing: number,
seatSpacing: number,
longestRow?: number,
): number;| Parameter | Type | Description |
|---|---|---|
rows | number | |
rowSpacing | number | |
seatSpacing | number | |
longestRowoptional | number | Default |
number@openseat/core/src/section-ops.ts
The smallest round-table radius at which maxSeatsForTable is at least seatCount, with the seats kept gap outside the table edge (the gap setTableArrangement keeps when the radius changes). At least 1. For clamping radius inputs so a table never shrinks below its seats.
function minRoundTableRadiusForSeats(
seatCount: number,
opts?: MinRoundTableRadiusOptions,
): number;| Parameter | Type | Description |
|---|---|---|
seatCount | number | |
optsoptional | MinRoundTableRadiusOptions | Default |
number@openseat/core/src/table-ops.ts
Moves objects by delta. Sections bring their contents and rows and tables their seats. Effectively locked objects (locked themselves or inside a locked group or section) do not move. Moving part of a parametric row or section turns it freeform (see reconcileMoved).
function moveObjects(
layout: LayoutDocument,
ids: readonly string[],
delta: Point,
): LayoutDocument;@openseat/core/src/operations.ts
Moves vertex index of a section, polygon, GA area, aisle or path outline to a layout-space point.
function moveVertex(
layout: LayoutDocument,
id: string,
index: number,
world: Point,
): VertexEditResult;@openseat/core/src/section-ops.ts
The first label of spec's sequence not used by any row with the same sectionId.
function nextFreeRowLabel(
layout: LayoutDocument,
sectionId: string | undefined,
spec?: RowLabeling,
): string;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
sectionId | string | undefined | |
specoptional | RowLabeling | Default |
string@openseat/core/src/labeling.ts
The next section label: one more than the largest label when every section label is a number, otherwise the first free Section N (N = section count + 1 and up).
function nextFreeSectionLabel(layout: LayoutDocument): string;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument |
layout takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstringstring@openseat/core/src/section-ops.ts
Where a table's or booth's label goes: centred on it and turned with it, at 12 units, shrunk (down to 4) so it fits 0.9 × the width of the booth or rect table, or 0.8 × a round table's diameter, and never spills onto the chairs.
function objectLabelPlacement(obj: Table | Booth): TextPlacement;@openseat/core/src/label-layout.ts
The direct parent of an object: a grouped seat gives its row or table; a row, table, GA area or loose seat gives its section when that section exists. Sections and free-standing objects have no parent.
function parentOf(
index: LayoutIndex,
id: string,
): string | undefined;@openseat/core/src/membership.ts
Parses the first label a user typed: "12" is numeric 12, "A" alpha 1, "AA" alpha 27, "c" alpha-lower 3, "IV" roman 4 (only valid roman numerals longer than one character; "I" is alpha 9). Null when unrecognised.
function parseLabelStart(
text: string,
): { format: LabelFormat; start: number } | null;@openseat/core/src/labeling.ts
Parses and validates JSON text from an untrusted source.
Failure modes are reported as validation errors, not exceptions:
INVALID_DOCUMENT at path "" with the parser messagevalidateLayoutINVALID_DOCUMENT "Internal validation error: ..."; this function never throwsfunction parseLayout(
json: string,
options?: ParseOptions,
): ValidationResult;@openseat/core/src/serialize.ts
Pastes a clipboard payload into a layout (the same one or another). Objects are re-identified as by duplicateObjects. A sectionId is kept only when that section is part of the payload; otherwise it is dropped. Missing categories are added; a category ID that exists with a different name gets a fresh ID. Returns the pasted top-level IDs and the copied row IDs.
function pasteObjects(
layout: LayoutDocument,
payload: ClipboardPayload,
opts?: PasteOptions,
): { layout: LayoutDocument; ids: string[]; rowIds: string[] };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
payload | ClipboardPayload | |
optsoptional | PasteOptions | Default |
{ layout: LayoutDocument; ids: string[]; rowIds: string[]; }@openseat/core/src/operations.ts
Placement of a path's caption: centred at the arc-length midpoint of the (world) path, turned along the segment there and kept upright, with the reserved text box. fontSize defaults to captionSize, else DIVIDER_CAPTION_SIZE. Null when the path has no caption or no length.
function pathCaptionPlacement(
path: Pick<
CustomPath,
| "points"
| "position"
| "rotation"
| "closed"
| "caption"
| "captionSize"
>,
fontSize?: number,
): PathCaptionPlacement | null;| Parameter | Type | Description |
|---|---|---|
path | Pick<CustomPath, "points" | "position" | "rotation" | "closed" | "caption" | "captionSize"> | |
fontSizeoptional | number | Default |
PathCaptionPlacement | null@openseat/core/src/object-geometry.ts
Plans the rows that fill a polygon, without creating objects. The boundary is rotated so rows run horizontally, each row is a scanline across the polygon, and seats are spaced along every span the line crosses (a concave outline can give a row several spans, which become row gaps). Seats too close to an edge are dropped, so every seat lies fully inside. Rows run along angle and are returned front (first scanline) to back, or the other way round with reverse.
Throws a GeneratorInputError before doing any work when the fill would exceed MAX_FILL_ROWS rows, when a span would exceed MAX_FILL_SEATS_PER_SPAN seats, or when a boundary coordinate is beyond LAYOUT_LIMITS.maxCoordinate.
function planFillRows(options: FillPlanOptions): {
pivot: Point;
angle: number;
rows: FillRowPlan[];
};| Parameter | Type | Description |
|---|---|---|
options | FillPlanOptions |
options takes FillPlanOptions:
readonly Point[]numbernumbernumber = 0number = 2numbernumber = seatSpacingnumber = 1booleanSeatShape{ pivot: Point; angle: number; rows: FillRowPlan[]; }@openseat/core/src/generators/section.ts
Point-in-polygon test using ray casting. Points on the boundary count as inside, so a seat sitting exactly on a section edge is not flagged.
@openseat/core/src/geometry.ts
The interior point farthest from the polygon outline (the "visual centre"), found with the polylabel quadtree search. Better than the centroid for placing a label in a concave shape such as a C or an L: the result always lies inside the polygon. precision is the tolerance in layout units. Degenerate polygons (zero area) return the vertex mean.
@openseat/core/src/geometry.ts
function polygonArea(points: readonly Point[]): number;@openseat/core/src/geometry.ts
Area-weighted centroid of a simple polygon. Falls back to the mean of the vertices when the area is zero (collinear or degenerate input), and to the origin for an empty list.
@openseat/core/src/geometry.ts
Whether a polygon is simple (no two non-adjacent edges touch; adjacent edges always share a vertex, so they are excluded). Edges are sorted by their left x, and only pairs whose bounding boxes overlap are tested exactly, so real outlines cost about O(n log n) (about two candidate pairs per edge). Pathological shapes (many long edges whose x extents overlap) can still visit O(n^2) candidate pairs, whether or not their y extents overlap too: every candidate pair uses up one unit of budget.remaining, and when it runs out the answer is "unknown".
function polygonSimplicity(
points: readonly Point[],
budget?: SimplicityBudget,
): "simple" | "self-intersecting" | "unknown";| Parameter | Type | Description |
|---|---|---|
points | readonly Point[] | |
budgetoptional | SimplicityBudget |
"simple" | "self-intersecting" | "unknown"@openseat/core/src/geometry.ts
Inverse of rowCurvatureToRadius: sign(r) * min(100, 13500 / |r|), 0 when straight.
function radiusToRowCurvature(r?: number): number;| Parameter | Type | Description |
|---|---|---|
roptional | number |
number@openseat/core/src/row-geometry.ts
Seats along the edges of a width x height rectangle, clockwise from the top edge: top left to right, right top to bottom, bottom right to left, left bottom to top. The n seats of an edge sit at k / (n + 1) of its length, seatOffset outside it.
function rectTableSeatPlacements(
width: number,
height: number,
seatsPerEdge: RectTableSeatCounts,
seatOffset: number,
): TableSeatPlacement[];| Parameter | Type | Description |
|---|---|---|
width | number | |
height | number | |
seatsPerEdge | RectTableSeatCounts | |
seatOffset | number |
@openseat/core/src/generators/tables.ts
Recomputes the outline of every section whose outline is "auto" (block seating, or no seating; arc seating gets its ring segment; fill seating is driven by its outline and is skipped) from its actual seats. With previous, only sections whose members (or the section itself) changed by reference are recomputed. Returns the same layout when nothing changes.
function refreshAutoOutlines(
layout: LayoutDocument,
previous?: LayoutDocument,
): LayoutDocument;@openseat/core/src/section-ops.ts
Regenerates a row with seatCount seats seatSpacing apart, never flattening it:
layout keeps its line or arc, anchor, gaps and holes (setRowLayout);fitRowLayout) and regenerated along the fitted line or arc.The row always ends up with a layout. IDs follow mergeRegeneratedSeats.
function regenerateRow(
layout: LayoutDocument,
rowId: string,
options: RowRegenerationOptions,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
options | RowRegenerationOptions |
@openseat/core/src/regenerate.ts
Regenerates a table's seats (and optionally its shape) around its current position and rotation, and records the parameters as the table's arrangement. A round table keeps the start angle of its current round arrangement (default -90, the top) and, without a seatDistance, the clear gap between its edge and its seats (floored at the seat radius). A seat count of 0, or no seats on any edge, empties the table. IDs follow mergeRegeneratedSeats: the i-th generated seat takes over the i-th existing seat.
function regenerateTable(
layout: LayoutDocument,
tableId: string,
options: TableRegenerationOptions,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
tableId | string | |
options | TableRegenerationOptions |
@openseat/core/src/regenerate.ts
Labels rows with a RowLabeling sequence, front row first.
"auto" order uses the section's rowOrder when every row shares a section that has one; otherwise rows are ordered by their seat centroid projected on the normal of the mean row direction (front = row-local -y side). Writes row.rowLabel, the seats' rowLabel caches, and row.name when it is the default Row X. When rowIds are exactly the rows of one section, also stores section.rowLabeling and section.rowOrder (front to back).
function relabelRows(
layout: LayoutDocument,
rowIds: readonly string[],
spec: RowLabeling,
opts?: { readonly order?: "auto" | "given" },
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowIds | readonly string[] | |
spec | RowLabeling | |
optsoptional | { readonly order?: "auto" | "given"; } | Default |
@openseat/core/src/labeling.ts
Renumbers the seats of a row or table.
spec as layout.labeling, and every seat takes the label of its slot.seatIds (a subset): only those seats are labelled, numbered in slot order from start; the spec is not stored. The new labels are locked (labelLocked), so regenerating the row or table keeps them.seatObjectIds order.Seats with labelLocked keep their label unless respectLocks is false, in which case they are relabelled and unlocked.
function relabelSeats(
layout: LayoutDocument,
target: {
readonly groupId: string;
readonly seatIds?: readonly string[];
},
spec?: SeatLabeling,
opts?: { readonly respectLocks?: boolean },
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
target | { readonly groupId: string; readonly seatIds?: readonly string[]; } | |
specoptional | SeatLabeling | |
optsoptional | { readonly respectLocks?: boolean; } | Default |
@openseat/core/src/labeling.ts
Removes objects without leaving dangling references:
withContents is false, in which case they stay and lose the sectionId;force is set;pruneEmptyRows is false);function removeObjects(
layout: LayoutDocument,
ids: readonly string[],
opts?: RemoveOptions,
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
optsoptional | RemoveOptions | Default |
@openseat/core/src/operations.ts
Removes seats from a row and deletes them.
function removeSeatsFromRow(
layout: LayoutDocument,
rowId: string,
seatIds: readonly string[],
mode: "close-gap" | "keep-positions",
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
seatIds | readonly string[] | |
mode | "close-gap" | "keep-positions" |
@openseat/core/src/row-ops.ts
Removes vertex index of an outline. Closed outlines keep at least 3 points, open ones 2.
function removeVertex(
layout: LayoutDocument,
id: string,
index: number,
): VertexEditResult;@openseat/core/src/section-ops.ts
Relabels the tables and booths among ids as ${prefix}${start + k} in reading order. Objects are grouped into rows (or columns) with a tolerance: sorted by their centre across the reading direction, a new row starts when an object lies more than rowToleranceUnits beyond the first object of the current row. Within a row they are ordered along it. Other object types, unknown and repeated IDs are ignored. count is the number of labels that changed; the same layout object is returned when none does. Throws a RangeError for a non-integer or negative start or a negative tolerance.
function renumberObjects(
layout: LayoutDocument,
ids: Iterable<string>,
opts: RenumberOptions,
): { layout: LayoutDocument; count: number };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | Iterable<string> | |
opts | RenumberOptions |
{ layout: LayoutDocument; count: number; }@openseat/core/src/renumber-ops.ts
Changes the paint order of objects (and what they carry) within their draw band. "front" / "back" lift the selection above / below every other object of the band, keeping its internal order; "forward" / "backward" swap each selected object with its neighbour and renumber the band's zIndex densely.
function reorderObjects(
layout: LayoutDocument,
ids: readonly string[],
direction: ReorderDirection,
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
direction | ReorderDirection |
@openseat/core/src/operations.ts
Fixes structural corruption in a valid document. Not a migration: the schema version is unchanged and only the broken relationships are edited.
Level "errors" (the default):
seatObjectIds are removed;seatObjectIds entries that are not seats are removed;sectionId / categoryId pointing at nothing is deleted;rowOrder entries are removed;Level "all" additionally syncs seat rowLabel / sectionId caches to the owning group (a row without a section adopts the one all its seats share), sets table capacity to the seat count, and removes empty rows that no section's rowOrder references.
Unchanged objects keep their identity, and the input layout is returned as is when nothing needed fixing.
function repairStructure(
layout: LayoutDocument,
options?: RepairOptions,
): { layout: LayoutDocument; repairs: StructureRepair[] };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
optionsoptional | RepairOptions | Default |
{ layout: LayoutDocument; repairs: StructureRepair[]; }@openseat/core/src/structure-checks.ts
function requireFinitePoint(value: Point, name: string): void;@openseat/core/src/generators/common.ts
function requireNonNegativeFinite(
value: number,
name: string,
): void;| Parameter | Type | Description |
|---|---|---|
value | number | |
name | string |
void@openseat/core/src/generators/common.ts
function requirePositiveFinite(value: number, name: string): void;| Parameter | Type | Description |
|---|---|---|
value | number | |
name | string |
void@openseat/core/src/generators/common.ts
function requirePositiveInteger(value: number, name: string): void;| Parameter | Type | Description |
|---|---|---|
value | number | |
name | string |
void@openseat/core/src/generators/common.ts
Changes a table's shape, keeping the clear gap between table and seats (round: seatDistance = newRadius + (oldSeatDistance - oldRadius); rect: the same seatOffset and edge counts). Grows the table when its seats no longer fit. Switching kind converts the arrangement as in setTableArrangement.
function resizeTable(
layout: LayoutDocument,
tableId: string,
shape: TableShape,
): TableEditResult;@openseat/core/src/table-ops.ts
Returns labels if given, otherwise A, B, C… for count groups. Labels must be unique.
function resolveGroupLabels(
count: number,
labels: readonly string[] | undefined,
): string[];| Parameter | Type | Description |
|---|---|---|
count | number | |
labels | readonly string[] | undefined |
string[]@openseat/core/src/generators/common.ts
Maps the object under the pointer to the object a click selects:
function resolveHitTarget(
index: LayoutIndex,
hitId: string,
ctx: HitContext,
): string | null;@openseat/core/src/membership.ts
A row's labels, pushed clear of seats. Starts from core rowLabelPlacements (gap 8, font 11, bold); a label whose box overlaps any drawn seat in the layout moves outward along the row in steps of half its font size, at most 4 times. A label still blocked comes back with hidden: true and must not be drawn, so a drawn row label never overlaps a seat. seats are the row's seats in row order (as for rowLabelPlacements). Rows of zone sections and hidden rows get the same treatment here; layoutTextBoxes leaves them out.
function resolveRowLabelPlacements(
layout: LayoutDocument,
row: Row,
seats: readonly Seat[],
): ResolvedRowLabel[];@openseat/core/src/label-layout.ts
Reverses a row's direction: seatObjectIds are reversed and the layout is recomputed (row.position moves to the old last slot for a start anchor, rotation turns by 180 degrees and the radius sign flips), so no seat moves. Labels re-flow from the labeling. Seats that follow the row's direction ("tangent" or "row") turn by 180 degrees with it.
function reverseRow(
layout: LayoutDocument,
rowId: string,
): LayoutDocument;@openseat/core/src/row-ops.ts
Rotates point around pivot by degrees. Same as rotatePoint with the pivot made explicit.
@openseat/core/src/geometry.ts
World AABB of a width × height text box centred on at and turned by rotation degrees about it.
@openseat/core/src/object-geometry.ts
Rotates point around origin by degrees.
@openseat/core/src/geometry.ts
A closed polygon with its corners rounded by radius, as line and arc segments in drawing order (each segment starts where the previous one ends; the last one ends where the first starts). Each corner's radius is clamped so its tangent points stay within half of each adjacent edge; straight (collinear) and degenerate corners are left sharp. With radius <= 0 the result is the plain polygon as lines. Used to draw cornerRadius on GA zones and sections, in Konva and SVG alike.
function roundedPolygonPath(
points: readonly Point[],
radius: number,
): RoundedPathSegment[];@openseat/core/src/object-geometry.ts
roundedPolygonPath flattened to points: each arc becomes stepsPerArc chords. Useful for hit testing and bounds of rounded zones.
| Parameter | Type | Description |
|---|---|---|
points | readonly Point[] | |
radius | number | |
stepsPerArcoptional | number | Default |
Point[]@openseat/core/src/object-geometry.ts
A rectangle with rounded corners centred on center, clockwise on screen starting at the top-right corner. cornerRadius is clamped to min(width, height) / 2; 0 gives the four corners. Each corner is drawn with segmentsPerCorner segments.
| Parameter | Type | Description |
|---|---|---|
width | number | |
height | number | |
cornerRadius | number | |
segmentsPerCorneroptional | number | Default |
centeroptional | Point | Default |
Point[]@openseat/core/src/generators/shapes.ts
Seats evenly spread on a circle of radius seatDistance, clockwise (screen convention) from startAngle degrees. Each seat faces the centre.
function roundTableSeatPlacements(
seatDistance: number,
startAngle: number,
seatCount: number,
): TableSeatPlacement[];| Parameter | Type | Description |
|---|---|---|
seatDistance | number | |
startAngle | number | |
seatCount | number |
@openseat/core/src/generators/tables.ts
Block curvature (-100..100) to a signed row radius: 0 is straight, otherwise sign(c) * max(100, 13500 / |c|).
function rowCurvatureToRadius(c: number): number | undefined;| Parameter | Type | Description |
|---|---|---|
c | number |
number | undefined@openseat/core/src/row-geometry.ts
Row-local point at arc length t for a straight row (radius absent) or an arc.
@openseat/core/src/row-geometry.ts
Row-local tangent angle at arc length t, in degrees (0 for straight rows).
function rowCurveTangentDeg(
spec: Pick<RowLayout, "radius">,
t: number,
): number;@openseat/core/src/row-geometry.ts
Where to draw a row's label(s): before the first seat and/or after the last one, along the row's tangent, seatRadius + gap away from the seat centre. Follows row.labelPosition (absent = "start"; "none" gives no labels). seats must be the row's seats in row order.
function rowLabelPlacements(
row: Row,
seats: readonly Seat[],
opts: { readonly gap: number },
): RowLabelPlacement[];@openseat/core/src/row-geometry.ts
count unique row labels. Walks start, start + step, … and, for the alpha formats, skips values whose uppercase form is in skip (numeric and roman labels ignore it). "back-to-front" reverses the returned list, so index 0 is always the front row.
function rowLabels(spec: RowLabeling, count: number): string[];@openseat/core/src/labeling.ts
The placement that puts slot k of spec (with slotCount slots) at position with world tangent tangent degrees. Used to keep a seat fixed while the rest of the row changes.
@openseat/core/src/row-geometry.ts
The slot index of each seatObjectIds entry, skipping holes.
function rowSeatSlots(row: Row): number[];| Parameter | Type | Description |
|---|---|---|
row | Row |
row takes Row:
"row"stringstring[]stringbooleanbooleannumberPointnumberstringRowLayout"start" | "end" | "none" | "both"stringnumber[]@openseat/core/src/row-geometry.ts
A row's slot count: its seats plus its holes. Never stored.
function rowSlotCount(row: Row): number;| Parameter | Type | Description |
|---|---|---|
row | Row |
row takes Row:
"row"stringstring[]stringbooleanbooleannumberPointnumberstringRowLayout"start" | "end" | "none" | "both"stringnumber@openseat/core/src/row-geometry.ts
Arc-length parameter t_i of every slot, anchor shift applied. Holes do not affect the parameters.
function rowSlotParams(
spec: Pick<RowLayout, "seatSpacing" | "gaps" | "anchor">,
slotCount: number,
): number[];| Parameter | Type | Description |
|---|---|---|
spec | Pick<RowLayout, "seatSpacing" | "gaps" | "anchor"> | |
slotCount | number |
number[]@openseat/core/src/row-geometry.ts
World position and tangent (degrees) of slot k.
@openseat/core/src/row-geometry.ts
World AABBs of every drawn seat, in document order. Hidden seats (also through a hidden row, table or section) and the seats of zone sections (showSeats: false) are left out. Memoised per layout object.
function seatBoxes(layout: LayoutDocument): readonly SeatBox[];| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument |
layout takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstringreadonly SeatBox[]@openseat/core/src/label-layout.ts
Convex hull around seats, each seat taken as a circle of its enclosing radius plus pad, sampled at 8 points. Coordinates are the seats' (layout space). Empty when there are no seats.
@openseat/core/src/generators/shapes.ts
function seatIdFor(
idPrefix: string,
groupLabel: string,
seatNumber: number,
): string;| Parameter | Type | Description |
|---|---|---|
idPrefix | string | |
groupLabel | string | |
seatNumber | number |
string@openseat/core/src/generators/common.ts
Builds the displayed label for a seat number, applying the numbering prefix and suffix.
function seatLabelFor(
seatNumber: number,
numbering: SeatNumbering | undefined,
): string;@openseat/core/src/generators/common.ts
One label per slot (holes included), so deleted seats keep their number. Slot i gets start + i * step ("ltr") or start + (slotCount - 1 - i) * step ("rtl").
function seatLabels(
spec: SeatLabeling,
slotCount: number,
): string[];@openseat/core/src/labeling.ts
The label index label stands for under labeling (prefix and suffix stripped): "12" is 12 when numeric, "C" 3 when alpha, "IV" 4 when roman. Null when the label does not belong to the labeling's sequence format.
function seatLabelValue(
label: string,
labeling: SeatLabeling,
): number | null;@openseat/core/src/labeling.ts
Displayed labels for count seats. Uses seatLabels when the numbering sets a format, step or direction; otherwise exactly seatLabelFor for start + i.
function seatNumberingLabels(
numbering: SeatNumbering | undefined,
count: number,
): string[];@openseat/core/src/generators/common.ts
The SeatLabeling equivalent of a numbering, or undefined when it cannot be stored (a start or step outside the schema's range, or an over-long prefix or suffix).
function seatNumberingToLabeling(
numbering: SeatNumbering | undefined,
): SeatLabeling | undefined;@openseat/core/src/generators/common.ts
function seatObjectIdFor(
idPrefix: string,
groupLabel: string,
seatNumber: number,
): string;| Parameter | Type | Description |
|---|---|---|
idPrefix | string | |
groupLabel | string | |
seatNumber | number |
string@openseat/core/src/generators/common.ts
Half the largest extent of a seat shape: its radius for circles.
function seatShapeRadius(shape: SeatShape): number;@openseat/core/src/generators/tables.ts
The topmost visible section whose boundary contains point.
function sectionAt(
layout: LayoutDocument,
point: Point,
): string | undefined;@openseat/core/src/operations.ts
Where a section's label goes, in section-local coordinates: the outline's visual centre plus labelOffset.
| Parameter | Type | Description |
|---|---|---|
section | Section |
section takes Section:
"section"stringPoint[]{ fill?: string | undefined; stroke?: string | undefined; }stringbooleanbooleannumberPointnumberstringstring[]RowLabelingBlockSeating | ArcSeating | FillSeating"none" | "custom" | "auto"numberPointbooleannumbernumbernumber"hidden" | "left" | "right" | "auto" | "above" | "below"string@openseat/core/src/section-label.ts
The section that contains an object (through its group for grouped seats). A section is not inside itself, so a section gives undefined.
function sectionOf(
index: LayoutIndex,
id: string,
): string | undefined;@openseat/core/src/membership.ts
Seats of a section that are not hidden through a seat, row or table (the default zone capacity).
function sectionSeatCount(
layout: LayoutDocument,
sectionId: string,
): number;@openseat/core/src/zone-ops.ts
Where a section's title goes, or where: "hidden" when it is not drawn.
sectionLabelPoint (the outline's pole plus labelOffset), turned by labelRotation, upper-case, sized by the long-standing rule (its labelSize, default 16, at most 0.45 × the outline's shorter side) and shrunk to fit inside. where: "inside".labelPlacement, or above, below, left, right for "auto". Size: labelSize (default 16), at most what fits 0.95 × the block's width (above/below) or 2 × its row pitch high (left/right), never below 8. A side is rejected when its box overlaps any drawn seat in the layout, any drawn row label or an earlier title (sections in document order); when every side is rejected they are retried at size 8, then the title is hidden. A placement never overlaps a seat.labelPlacement: "hidden" hides the title in both levels of detail. Undefined when sectionId is not a visible section. Memoised per layout.
function sectionTitlePlacement(
layout: LayoutDocument,
sectionId: string,
opts: { readonly lod: LabelLod },
): SectionTitlePlacement | undefined;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
sectionId | string | |
opts | { readonly lod: LabelLod; } |
SectionTitlePlacement | undefined@openseat/core/src/label-layout.ts
Places a section sells as a zone: its capacity, or else the number of its seats that are not hidden (themselves or through their row or table; the section's own visibility does not count). 0 when sectionId is not a section.
function sectionZoneCapacity(
layout: LayoutDocument,
sectionId: string,
): number;@openseat/core/src/zone-ops.ts
True when segments ab and cd share at least one point, including touching endpoints.
@openseat/core/src/geometry.ts
Selection made by a marquee that fully contains containedIds. Each ID is resolved with resolveHitTarget and deduplicated. A section is only selected when its own outline is enclosed (its ID is in containedIds); a row or table is selected when at least one of its seats is.
function selectionFromMarquee(
index: LayoutIndex,
containedIds: readonly string[],
ctx: HitContext,
): string[];| Parameter | Type | Description |
|---|---|---|
index | LayoutIndex | |
containedIds | readonly string[] | |
ctx | HitContext |
string[]@openseat/core/src/membership.ts
What a layout offers for sale, after unsellableInventory: every seat, GA place and booth with an inventory ID, minus those held back. A table booked as a whole counts its seats, so holding back a 4-seat table lowers totalSellable by 4 either way.
function sellableSummary(layout: LayoutDocument): SellableSummary;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument |
layout takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstring@openseat/core/src/category-ops.ts
Serialises a layout to pretty-printed JSON with a canonical key order, so the same layout always produces the same text, however it was built. That keeps exports stable across round trips and makes diffs in version control show only real changes.
The input must be a valid document; this function does not validate, so callers that build documents by hand should run validateLayout first.
function serializeLayout(
layout: LayoutDocument,
indent?: number | string,
): string;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
indentoptional | number | string | Default |
string@openseat/core/src/serialize.ts
Sets visibility and/or lock on exactly the given objects. What they carry (a section's rows, a row's seats) is not written: it inherits the state, read through isEffectivelyHidden and isEffectivelyLocked. So locking row B, hiding row C, then locking, hiding, unlocking and showing their section leaves B locked and C hidden.
function setFlags(
layout: LayoutDocument,
ids: readonly string[],
flags: { readonly visible?: boolean; readonly locked?: boolean },
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
flags | { readonly visible?: boolean; readonly locked?: boolean; } |
@openseat/core/src/operations.ts
Writes categoryId on the objects that carry one (seats, sections, GA areas, booths and polygons), or removes it with null. Unknown IDs and objects without a category field are skipped. With withMembers, a section's seats and a row's or table's seats are written too (the editor's "set category" on a section, row or table). The category is not required to exist. count is the number of objects changed.
function setObjectsCategory(
layout: LayoutDocument,
ids: Iterable<string>,
categoryId: string | null,
opts?: SetObjectsCategoryOptions,
): { layout: LayoutDocument; count: number };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | Iterable<string> | |
categoryId | string | null | |
optsoptional | SetObjectsCategoryOptions | Default |
{ layout: LayoutDocument; count: number; }@openseat/core/src/category-ops.ts
Records ids as not for sale (or, with notForSale false, as for sale again) in metadata.openseat.notForSale. The stored list is deduplicated, sorted and pruned of IDs that no longer name an object; malformed entries are dropped. Categories are never touched. An empty list removes the key. Returns the same layout object when nothing changes.
function setObjectsNotForSale(
layout: LayoutDocument,
ids: readonly string[],
notForSale: boolean,
): LayoutDocument;@openseat/core/src/category-ops.ts
Sets a row's label, its seats' rowLabel caches and its default name.
function setRowLabel(
layout: LayoutDocument,
rowId: string,
label: string,
): LayoutDocument;@openseat/core/src/labeling.ts
Changes a row's parametric layout and regenerates its seats.
The patch is merged into the row's layout (fitted first when it has none); radius: null makes it straight. seatCount grows the row by appending slots, or shrinks it by removing the highest-slot seats and any holes left at the end. Positions and rotations come from the layout, labels from its labeling (locked labels stay). New seats copy the category and shape of the nearest seat. Returns the same layout when nothing changes.
function setRowLayout(
layout: LayoutDocument,
rowId: string,
patch: RowLayoutPatch,
opts?: SetRowLayoutOptions,
): RegenerationResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
patch | RowLayoutPatch | |
optsoptional | SetRowLayoutOptions | Default |
@openseat/core/src/row-ops.ts
Puts objects into a section (or takes them out with null). IDs map to their membership units: rows, tables, GA areas and loose seats (a grouped seat stands for its group). Seat caches follow. When the section keeps a rowOrder, joining rows are appended to it; rows leaving a section drop out of its order. count is the number of units written.
function setSectionMembership(
layout: LayoutDocument,
ids: readonly string[],
sectionId: string | null,
): { layout: LayoutDocument; count: number };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
sectionId | string | null |
{ layout: LayoutDocument; count: number; }@openseat/core/src/operations.ts
Switches sections between seats and zone only. show false writes showSeats: false (the section is sold by quantity and its seats are not drawn); true deletes the key (seats as before). Rows, seats, tables and capacity are never touched, so switching back restores the seats exactly. Unknown IDs and non-sections are skipped.
function setSectionShowSeats(
layout: LayoutDocument,
ids: Iterable<string>,
show: boolean,
): LayoutDocument;@openseat/core/src/zone-ops.ts
Replaces an outline's points with layout-space points (stored relative to the unchanged origin).
function setShapePoints(
layout: LayoutDocument,
id: string,
worldPoints: readonly Point[],
): VertexEditResult;@openseat/core/src/section-ops.ts
Edits a table's shape and seat arrangement and regenerates its seats.
table.arrangement (or inferTableArrangement).seatCount on a rect table redistributes; seatsPerEdge sets edges.grow (the default), a table too small for its seats is enlarged; otherwise a changed seat count is clamped to maxSeatsForTable (per edge for rect tables) and clamped is set.keepLabels; locked labels are kept.Writes shape, arrangement, capacity and bookAsWhole. Returns the input layout when nothing changes.
function setTableArrangement(
layout: LayoutDocument,
tableId: string,
patch: TableEditPatch,
opts?: TableEditOptions,
): TableEditResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
tableId | string | |
patch | TableEditPatch | |
optsoptional | TableEditOptions | Default |
@openseat/core/src/table-ops.ts
Sets a zone's capacity. On a section, null deletes the override so the capacity follows the seat count again. On a GA zone, n replaces its capacity and null changes nothing (a GA zone always has one). Throws a RangeError for a capacity that is not a whole number from 1 to 1,000,000.
function setZoneCapacity(
layout: LayoutDocument,
id: string,
capacity: number | null,
): LayoutDocument;@openseat/core/src/zone-ops.ts
Shoelace signed area. Positive for counter-clockwise order in a y-up system.
function signedArea(points: readonly Point[]): number;@openseat/core/src/geometry.ts
Rounds value to the nearest multiple of grid. A grid of 0 or less disables snapping.
function snapToGrid(value: number, grid: number): number;| Parameter | Type | Description |
|---|---|---|
value | number | |
grid | number |
number@openseat/core/src/operations.ts
Splits a row after the seat at afterIndex (an index into seatObjectIds). The second half becomes a new row ${row.id}-b in the same section, labelled newRowLabel or the next free row label. No seat moves and seat labels are unchanged.
function splitRow(
layout: LayoutDocument,
rowId: string,
afterIndex: number,
newRowLabel?: string,
): { layout: LayoutDocument; newRowId: string };| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rowId | string | |
afterIndex | number | |
newRowLabeloptional | string |
{ layout: LayoutDocument; newRowId: string; }@openseat/core/src/row-ops.ts
Where a stage's label goes: centred at stageLabelPoint, upper-case, letter-spaced 0.14 em, clamp(0.28 × min(w, h), 10, 36) and shrunk until it is at most 0.8 × the width available there. Turned with the stage. Null for a stage without a label.
function stageLabelPlacement(stage: Stage): TextPlacement | null;@openseat/core/src/label-layout.ts
Where a stage's label is centred, in its local space: the centre for rect and round stages, the centre of the main body for a thrust stage, and (0, -h/2 + 0.4 h) for a semicircle (inside its widest part).
function stageLabelPoint(stage: StageGeometry): Point;@openseat/core/src/object-geometry.ts
Width available to a stage's label at its label point, in local units: the width for rect and round stages (round: the full ellipse width at the centre), the body width for thrust, and the chord at the label point for a semicircle.
function stageLabelWidth(stage: StageGeometry): number;| Parameter | Type | Description |
|---|---|---|
stage | StageGeometry |
number@openseat/core/src/object-geometry.ts
Closed outline of a stage in its local space: the width × height box centred on the origin, with the audience on +y. No point is repeated at the end.
shape): the box, its corners rounded by cornerRadius (clamped to half the shorter side) with segments / 4 steps per corner; without a radius the four corners in the order top-left, top-right, bottom-right, bottom-left.thrust.width wide (default 0.4 w) down to y = h/2 (depth default 0.45 h).segments points.segments + 1 points.function stageOutline(
stage: StageGeometry,
segments?: number,
): Point[];@openseat/core/src/object-geometry.ts
Runway size of a thrust stage after defaults and clamping (width <= stage width, depth < stage height).
function stageThrustSize(stage: StageGeometry): {
width: number;
depth: number;
};| Parameter | Type | Description |
|---|---|---|
stage | StageGeometry |
{ width: number; depth: number; }@openseat/core/src/object-geometry.ts
Makes a row straight, keeping its spacing and anchor. A curved row is laid along its chord: the straight row runs in the direction from its first slot to its last and keeps the chord's midpoint, so a row curved and then straightened again keeps its direction (it does not follow the tangent at its first seat).
function straightenRow(
layout: LayoutDocument,
rowId: string,
): RegenerationResult;@openseat/core/src/row-ops.ts
function toDegrees(radians: number): number;| Parameter | Type | Description |
|---|---|---|
radians | number |
number@openseat/core/src/geometry.ts
The selection units among ids: drops unknown IDs, duplicates and every ID whose ancestor is also in ids (a seat of a selected row, a row of a selected section). Input order is kept.
function topLevelUnits(
index: LayoutIndex,
ids: readonly string[],
): string[];@openseat/core/src/membership.ts
function toRadians(degrees: number): number;| Parameter | Type | Description |
|---|---|---|
degrees | number |
number@openseat/core/src/geometry.ts
Turns a layout into a zone map: every section with seats becomes a zone section (showSeats: false), so buyers pick a section and a quantity and no seat is drawn. Without sectionIds, rows, tables and loose seats that are in no section are first wrapped into new rectangular sections (createSectionFromSelection with shape: "bounds"): grouped by category and by proximity (units whose bounds are at most 3 × the median seat pitch apart join one section), each labelled with its category's name and an ordinal unique in the layout ("Stalls 1", "VIP 2"; "Zone N" without a category) and given that category. Seats themselves are never changed, so the sellable capacity stays the same (hidden seats aside, which zones do not count). With sectionIds, only those sections switch.
function toZoneOnlyLayout(
layout: LayoutDocument,
opts?: ZoneOnlyOptions,
): LayoutDocument;@openseat/core/src/zone-ops.ts
Sets one object's position and absolute rotation and scales its dimensions along its local axes (the single-object transformer). Everything the object carries follows rigidly in its local frame: a table's chairs (radially for a round table), a row's seats (rotating about row.position), a section's rows, tables, GA areas and loose seats. Effectively locked objects do not change. Parametric rows and sections follow the same rules as transformObjects.
function transformObject(
layout: LayoutDocument,
id: string,
transform: ObjectTransform,
): LayoutDocument;@openseat/core/src/operations.ts
Transforms objects together, as one rigid (or scaled, or mirrored) group. Selections expand recursively and effectively locked objects are skipped.
Per object: the position is mapped and the rotation follows the local +x axis. Point lists (section and GA boundaries, polygons, aisles, paths) are mapped in world space and re-localised. Booths, stages, rect tables and entrances stretch by how much the map stretches their local axes; seats keep their shape and labels their font size. Round tables scale their radius by sqrt(|sx·sy|) and their seats keep the gap to the table edge.
Parametric data: uniform scale scales a row layout and section seating; a mirror negates a row's radius and re-derives an arc section's centre and angles (block and fill seating is dropped); non-uniform scale drops both. The layout-level venue is not affected.
function transformObjects(
layout: LayoutDocument,
ids: readonly string[],
t: SelectionTransform,
): LayoutDocument;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | |
t | SelectionTransform |
@openseat/core/src/operations.ts
@openseat/core/src/geometry.ts
Returns base, or base-2, base-3…, whichever is first not in taken, and adds it to taken.
function uniqueId(base: string, taken: Set<string>): string;| Parameter | Type | Description |
|---|---|---|
base | string | |
taken | Set<string> |
string@openseat/core/src/ids.ts
The inventory a booking map must not sell: everything in a category listed in metadata.openseat.unsellableCategories, plus everything covered by an ID in metadata.openseat.notForSale:
seatId, a GA area or booth its inventoryId;showSeats: false) gives its own ID, and its seats and tables give nothing (they are not sold on their own).Category membership follows the inventory manifest: a seat, GA area or booth by its own categoryId; a table booked as a whole by its first seat's; a zone section by its categoryId, else its seats' most common one. Malformed metadata (non-arrays, non-strings) is ignored, and at most 40,000 IDs of each list are read.
function unsellableInventory(
layout: LayoutDocument,
): UnsellableInventory;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument |
layout takes LayoutDocument:
1stringstringnumber{ width: number; height: number; gridSize: number; }Record<string, unknown>Category[]LayoutObject[]VenuePointstringstring@openseat/core/src/category-ops.ts
Replaces fields on one object. Use for property-inspector edits.
function updateObject<T extends LayoutObject>(
layout: LayoutDocument,
id: string,
update: (obj: T) => T,
): LayoutDocument;T extends LayoutObject| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
id | string | |
update | (obj: T) => T |
@openseat/core/src/operations.ts
Changes a section's seating and regenerates it (applySectionSeating). A patch of the same kind is merged into the current seating (a key set to undefined is removed, e.g. { rowSeats: undefined }); a different kind, or a section without seating, needs a complete spec. Throws a RangeError when the result is not a valid seating.
function updateSectionSeating(
layout: LayoutDocument,
sectionId: string,
patch: Partial<SectionSeating>,
opts?: ApplySectionSeatingOptions,
): SectionSeatingResult;| Parameter | Type | Description |
|---|---|---|
layout | LayoutDocument | |
sectionId | string | |
patch | Partial<SectionSeating> | |
optsoptional | ApplySectionSeatingOptions | Default |
@openseat/core/src/section-ops.ts
degrees normalised into (-90, 90], so text drawn at that angle is never upside down.
function uprightAngle(degrees: number): number;| Parameter | Type | Description |
|---|---|---|
degrees | number |
number@openseat/core/src/object-geometry.ts
Validates an untrusted layout document. Returns structured errors and warnings rather than throwing, so editors can show every problem at once. Never throws: an unexpected internal failure is reported as an INVALID_DOCUMENT error.
Accepts only the current schema version. Run migrateLayout first to upgrade older documents.
options.rules re-maps the severity of semantic issues (or turns them off); valid reflects the re-mapped errors.
function validateLayout(
input: unknown,
options?: ValidateOptions,
): ValidationResult;import { buildInventoryManifest, diffManifests, validateLayout } from "@openseat/core";
const result = validateLayout(body.layout); // never trust the client's layout
if (!result.valid || !result.layout) throw new Error("invalid layout"); // result.errors says why
const next = buildInventoryManifest(result.layout);
const { removedSeatIds } = diffManifests(previousManifest, next);
// Refuse the save if any removed seat is held or sold for a live event.Your backend: the REST contract
@openseat/core/src/validate.ts
Inverse of localToWorld.
@openseat/core/src/geometry.ts
Where a GA zone's label goes: centred at the pole of inaccessibility of its outline, turned by labelRotation, upper-case and bold, sized to fit inside (16 at most, never below 8).
function zoneLabelPlacement(area: GaArea): TextPlacement;| Parameter | Type | Description |
|---|---|---|
area | GaArea |
area takes GaArea:
"ga"stringPoint[]numberstringstringbooleanbooleannumberPointnumberstringstring{ fill?: string | undefined; stroke?: string | undefined; }"standing" | "seated"numbernumberstring@openseat/core/src/label-layout.ts
Rows, tables and seats inside zone sections, mapped to their zone section's ID. These are kept in the document but never drawn, hit or sold on their own. GA areas inside a zone section are not included: they stay zones of their own. Memoised per objects array.
function zoneSectionMembers(layout: {
readonly objects: readonly LayoutObject[];
}): ReadonlyMap<string, string>;| Parameter | Type | Description |
|---|---|---|
layout | { readonly objects: readonly LayoutObject[]; } |
ReadonlyMap<string, string>@openseat/core/src/zone-ops.ts
The zone section a seat, row or table belongs to, if it is inside one.
function zoneSectionOf(
layout: LayoutDocument,
objectId: string,
): string | undefined;@openseat/core/src/zone-ops.ts
Thrown when generator input is outside its valid range. These are programmer errors, not document errors.
class GeneratorInputError extends RangeErrorExtends RangeError: its public members apply here too.
| Member | Type | Description |
|---|---|---|
constructorconstructor | (message: string) |
@openseat/core/src/generators/common.ts
Uniform-grid spatial index over axis-aligned bounds. Used for viewport culling and hit-test candidate lookup, so both stay proportional to what is on screen rather than to the size of the layout.
A grid suits seating layouts: objects are similar in size and spread evenly. Large objects (sections) are registered in every cell they cover, up to MAX_CELLS_PER_ENTRY; anything bigger is checked linearly on every query. No input makes the index throw or loop without bound.
class SpatialIndex| Member | Type | Description |
|---|---|---|
cellSize | number | |
constructorconstructor | (cellSize?: number) |
|
sizegetter | number | |
insertmethod | (id: string, bounds: Bounds) => void | |
removemethod | (id: string) => void | |
clearmethod | () => void | |
getBoundsmethod | (id: string) => Bounds | undefined | |
querymethod | (area: Bounds) => string[] | IDs whose bounds intersect |
queryPointmethod | (point: Point, radius?: number) => string[] |
Every member is readonly.
@openseat/core/src/spatial-index.ts
interface AnnularSectorOptions| Member | Type | Description |
|---|---|---|
center | Point | |
innerRadius | number | 0 or less gives a pie wedge with its tip at the centre. |
outerRadius | number | |
startAngle | number | Degrees, screen convention (clockwise on screen from +x). |
endAngle | number | |
segmentsoptional | number | Segments per arc. Defaults to one per 4 degrees of sweep, rounded up. |
Every member is readonly.
@openseat/core/src/generators/shapes.ts
interface ApplySectionSeatingOptions| Member | Type | Description |
|---|---|---|
keepLabelsoptional | boolean | Keep existing seat labels instead of applying the seating's seat labeling. Locked labels are always kept. |
relabeloptional | boolean | Relabel existing rows from |
Every member is readonly.
@openseat/core/src/section-ops.ts
interface ArcSectionParams| Member | Type | Description |
|---|---|---|
center | Point | Centre of the arc (the stage or field centre), in layout coordinates. |
innerRadius | number | Radius of the front row. |
rows | number | |
rowSpacing | number | |
startAngle | number | Degrees, screen convention; |
endAngle | number | |
seatSpacing | number | |
edgeMarginoptional | number | Clear distance from the radial edges to the outermost seat centres. Defaults to the seat radius + 4. |
seatShapeoptional | SeatShape | |
labeloptional | string | |
rowLabelingoptional | RowLabeling | |
seatLabelingoptional | SeatLabeling | |
categoryIdoptional | string | |
rowSeatsoptional | readonly number[] |
Every member is readonly.
@openseat/core/src/section-ops.ts
The playing area (or stage) in the middle of the arena.
interface ArenaField| Member | Type | Description |
|---|---|---|
width | number | |
height | number | |
cornerRadius | number | Clamped to half the smaller side. |
labeloptional | string | Text drawn on the field. Defaults to the field kind's name ("Rink", "Court", "Pitch", "Stage"). |
Every member is readonly.
@openseat/core/src/generators/arena.ts
interface ArenaNumbering| Member | Type | Description |
|---|---|---|
directionoptional | "clockwise" | "counterclockwise" | Defaults to "clockwise". Default |
startOffsetoptional | number | Rotates the numbering sequence, which by default starts at the first (left-most) section of the top side. -1 gives the first number to the last section before it (the last top-left corner fan when the corners are filled), as in a typical hockey arena. Defaults to 0. Default |
Every member is readonly.
@openseat/core/src/generators/arena.ts
interface ArenaOptions| Member | Type | Description |
|---|---|---|
idPrefix | string | Prefix of every generated object and category ID. |
center | Point | Centre of the field, in layout coordinates. |
field | ArenaField | |
tiers | readonly ArenaTier[] | Tiers from the field outwards. |
numberingoptional | ArenaNumbering | |
seatLabelingoptional | SeatLabeling | How seats are numbered within each row. Defaults to 1, 2, 3… from each spectator's left. |
venueoptional | { readonly margin: number; readonly shape?: "rect" | "ellipse"; } | Venue outline around the outermost tier. Defaults to a rounded rectangle 40 units out. |
fieldKindoptional | ArenaFieldKind | Defaults to "rink". Sets the field's default label, colours and markings. Default |
titleoptional | string | Human-readable name, e.g. "Ice hockey arena"; |
balanceSectionsoptional | boolean | Evens out the seats per section around each tier. The tier keeps its total section count (never more), but the sections are redistributed between the long sides, the short sides and the corners so the sections on every side and in every corner hold about the same number of seats: without it, a round-ended field gives huge corner fans next to thin side blocks. A side or corner left at 0 stays empty, and a side too short for any section gives its share to the others. Off by default, so existing options generate exactly what they did before. |
Every member is readonly.
@openseat/core/src/generators/arena.ts
One ring ("level") of sections around the field.
interface ArenaTier| Member | Type | Description |
|---|---|---|
labeloptional | string | Name of the tier, e.g. "100 Level". Used for its generated category. |
firstNumber | number | Number of the tier's first section in numbering order, e.g. 101. |
rows | number | |
rowSpacing | number | Distance between rows. |
seatSpacing | number | Distance between seat centres along a row. |
gap | number | Distance from the field edge (tier 0) or from the previous tier's outer edge. |
sectionsPerLongSide | number | Sections along each long (top and bottom) side; 0 leaves the side empty. |
sectionsPerShortSide | number | Sections along each short (left and right) side; 0 leaves the side empty. |
sectionsPerCorner | number | Fan sections per corner; 0 leaves the corners empty. |
extraCornerSectionsoptional | readonly [ number, number, number, number ] | Extra fan sections added to |
aisleWidth | number | Clear width of the aisles between neighbouring sections. |
categoryIdoptional | string | Category of the tier's seats. Without it the generator creates one category per tier. |
seatShapeoptional | SeatShape | Defaults to a circle of radius 0.4 x the smaller of seat and row spacing. |
rowLabelingoptional | RowLabeling | Defaults to A, B, C… front to back. |
Every member is readonly.
@openseat/core/src/generators/arena.ts
interface BlockSectionParams| Member | Type | Description |
|---|---|---|
position | Point | Centre of the block (the section's origin), in layout coordinates. |
rotationoptional | number | |
labeloptional | string | Defaults to Default |
seating | BlockSeating | |
rowLabelingoptional | RowLabeling | |
outlineoptional | "auto" | "none" | Defaults to "auto". "none" still stores the outline but view mode does not draw it. Default |
categoryIdoptional | string |
Every member is readonly.
@openseat/core/src/section-ops.ts
interface BoothGridOptions| Member | Type | Description |
|---|---|---|
idPrefix | string | Prefix for booth object IDs: |
origin | Point | Centre of the first (top-left) booth. |
columns | number | |
rows | number | |
width | number | |
height | number | |
gapX | number | Space between booths horizontally. |
gapY | number | Space between booths vertically, for example an aisle between booth rows. |
labelPrefixoptional | string | Displayed label is Default |
inventoryPrefixoptional | string | When set, each booth gets |
categoryIdoptional | string | |
startoptional | number | First booth number. Defaults to 1. Default |
Every member is readonly.
@openseat/core/src/generators/booths.ts
interface Bounds| Member | Type | Description |
|---|---|---|
minX | number | |
minY | number | |
maxX | number | |
maxY | number |
Every member is readonly.
@openseat/core/src/geometry.ts
interface BuildSectionContentsOptions| Member | Type | Description |
|---|---|---|
idPrefixoptional | string | Prefix of generated object IDs. Defaults to the section ID. |
existingoptional | LayoutDocument | Document the contents will join; generated object IDs and seat IDs are made unique against it. |
Every member is readonly.
@openseat/core/src/section-ops.ts
Serializable clipboard content: deep copies of objects with what they carry and the categories they use.
interface ClipboardPayload| Member | Type | Description |
|---|---|---|
format | "openseat/objects" | |
version | 1 | |
sourceLayoutId | string | |
objects | readonly LayoutObject[] | |
categories | readonly Category[] | |
bounds | Bounds | World bounds of the copied objects. |
Every member is readonly.
@openseat/core/src/operations.ts
Options of createArenaLayout.
interface CreateArenaLayoutOptions extends ArenaOptionsExtends ArenaOptions
| Member | Type | Description |
|---|---|---|
layoutId | string | |
nameoptional | string | Defaults to |
idPrefix | string | Prefix of every generated object and category ID. From |
center | Point | Centre of the field, in layout coordinates. From |
field | ArenaField | From |
tiers | readonly ArenaTier[] | Tiers from the field outwards. From |
numberingoptional | ArenaNumbering | From |
seatLabelingoptional | SeatLabeling | How seats are numbered within each row. Defaults to 1, 2, 3… from each spectator's left. From |
venueoptional | { readonly margin: number; readonly shape?: "rect" | "ellipse"; } | Venue outline around the outermost tier. Defaults to a rounded rectangle 40 units out. From |
fieldKindoptional | ArenaFieldKind | Defaults to "rink". Sets the field's default label, colours and markings. From Default |
titleoptional | string | Human-readable name, e.g. "Ice hockey arena"; From |
balanceSectionsoptional | boolean | Evens out the seats per section around each tier. The tier keeps its total section count (never more), but the sections are redistributed between the long sides, the short sides and the corners so the sections on every side and in every corner hold about the same number of seats: without it, a round-ended field gives huge corner fans next to thin side blocks. A side or corner left at 0 stays empty, and a side too short for any section gives its share to the others. Off by default, so existing options generate exactly what they did before. From |
Every member is readonly.
@openseat/core/src/generators/arena.ts
interface CreateLayoutOptions| Member | Type | Description |
|---|---|---|
id | string | |
name | string | |
width | number | Canvas width in layout units. |
height | number | Canvas height in layout units. |
gridSizeoptional | number | |
categoriesoptional | readonly Category[] | |
metadataoptional | Record<string, unknown> |
Every member is readonly.
@openseat/core/src/factory.ts
interface CreateZoneOptions| Member | Type | Description |
|---|---|---|
shape | "rect" | "rounded" | "polygon" | "trapezoid" | rect: plain; rounded: corners rounded by 12% of the shorter side; trapezoid: one edge inset 15% per side; polygon: |
rectoptional | ZoneRect | World rectangle for rect, rounded and trapezoid. |
pointsoptional | readonly Point[] | World corners for polygon (at least 3). |
label | string | |
capacity | number | Whole number from 1 to 1,000,000. |
seating | GaSeating | |
categoryIdoptional | string | |
inventoryIdoptional | string | Host inventory key; defaults to an unused key derived from the label. |
flipoptional | boolean | Trapezoid only: false (default) keeps the top edge full width and insets the bottom; true the reverse. |
Every member is readonly.
@openseat/core/src/zone-ops.ts
interface CurvedRowsOptions extends GeneratorBaseOptionsExtends GeneratorBaseOptions
| Member | Type | Description |
|---|---|---|
rowCount | number | |
seatsPerRow | number | |
center | Point | Centre of the arc. |
innerRadius | number | Radius of the first row. |
rowSpacing | number | Radial distance between rows. Must be positive when |
startAngle | number | Start angle in degrees. Angles follow screen convention: 0 points right, 90 points down. |
endAngle | number | End angle in degrees. Seats are spread evenly from start to end, inclusive. |
rowLabelsoptional | readonly string[] | |
rowLabelingoptional | RowLabeling | Row label sequence used when |
tangentRotationoptional | boolean | When true, each seat is rotated to follow the arc's tangent. |
seatSpacingoptional | number | Arc length between seat centres. When given, seats are spaced by this distance on every row, centred on the middle angle, instead of being spread from |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
sectionIdoptional | string | From |
categoryIdoptional | string | From |
seatShapeoptional | SeatShape | From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/rows.ts
interface DuplicateOptions| Member | Type | Description |
|---|---|---|
sectionIdoptionalreadonly | string | null | Section for copies whose section was not copied with them. Absent: keep the original section. |
@openseat/core/src/operations.ts
interface DuplicateResult| Member | Type | Description |
|---|---|---|
layout | LayoutDocument | |
ids | readonly string[] | New IDs of the duplicated top-level objects, in input order. Select these after duplicating. |
idMap | ReadonlyMap<string, string> | Old object ID -> new object ID, for every copied object. |
rowIds | string[] | IDs of the copied rows. |
Every member is readonly.
@openseat/core/src/operations.ts
Options of planFillRows: the geometric part of SectionSeatingOptions.
interface FillPlanOptions| Member | Type | Description |
|---|---|---|
boundary | readonly Point[] | Outline in layout coordinates. Concave shapes are supported. |
seatSpacing | number | |
rowSpacing | number | |
angleoptional | number | Direction rows run, in degrees. Defaults to 0. Default |
marginoptional | number | Clearance between seat edges and the boundary. Defaults to 2. Default |
aisleEveryoptional | number | Insert an aisle after every N seats in a row; 0 for none. |
aisleWidthoptional | number | Extra space of each aisle. Defaults to Default |
minSeatsPerRowoptional | number | Rows with fewer seats are skipped. Defaults to 1. Default |
reverseoptional | boolean | Fill from the far side (bottom first when angle is 0). |
seatShapeoptional | SeatShape |
Every member is readonly.
@openseat/core/src/generators/section.ts
One planned row of a filled section.
interface FillRowPlan| Member | Type | Description |
|---|---|---|
positions | readonly Point[] | Seat centres in layout coordinates, in seating order. |
gaps | readonly RowGap[] | Row gaps that reproduce the positions with a straight row layout anchored at the first seat: span breaks of concave outlines and aisles. |
Every member is readonly.
@openseat/core/src/generators/section.ts
interface GeneratedArena| Member | Type | Description |
|---|---|---|
objects | LayoutObject[] | The field (a stage object) followed by each section, its seats and rows. |
venue | Venue | |
sectionIds | string[] | Section IDs in tier order, each tier in numbering order. |
categories | Category[] | One category per tier that has no |
Every member is readonly.
@openseat/core/src/generators/arena.ts
Output of a row generator: a row object per row, plus the seat objects it references.
interface GeneratedRows| Member | Type | Description |
|---|---|---|
objects | LayoutObject[] | |
seatIds | string[] | Seat IDs in generation order (row by row, left to right). |
Every member is readonly.
@openseat/core/src/generators/rows.ts
Options shared by every generator.
interface GeneratorBaseOptions| Member | Type | Description |
|---|---|---|
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. |
sectionIdoptional | string | |
categoryIdoptional | string | |
seatShapeoptional | SeatShape | |
numberingoptional | SeatNumbering |
Every member is readonly.
@openseat/core/src/generators/common.ts
Where the user is editing and what a click selects.
interface HitContext| Member | Type | Description |
|---|---|---|
scopeSectionId | string | null | The section being edited ("entered"), or null at the top level. |
scopeGroupId | string | null | The row or table being edited, or null. |
granularity | "objects" | "seats" | "seats" selects individual seats wherever they are. |
Every member is readonly.
@openseat/core/src/membership.ts
A flat, backend-friendly list of everything sellable in a layout: one entry per seat with its effective section, row or table and category, plus the areas sold as units (GA areas, booths with an inventory ID, tables booked as a whole). Hosts store it next to the layout on save and diff it against the previous save to reconcile inventory (diffManifests).
interface InventoryManifest| Member | Type | Description |
|---|---|---|
version | 1 | |
layoutId | string | |
name | string | |
revision | number | |
categories | readonly InventoryManifestCategory[] | |
sections | readonly InventoryManifestSection[] | |
seats | readonly InventoryManifestSeat[] | |
areas | readonly InventoryManifestArea[] |
Every member is readonly.
@openseat/core/src/manifest.ts
interface InventoryManifestArea| Member | Type | Description |
|---|---|---|
inventoryId | string | GA area and booth: their |
objectId | string | |
kind | "ga" | "booth" | "table" | Zone sections are listed as "ga": they are sold by quantity like GA zones. |
objectTypeoptional | "ga" | "section" | "section" for a zone section ( |
seatedoptional | boolean | True for a seated zone: a GA zone with |
label | string | |
capacity | number | GA: its capacity; zone section: its zone capacity; booth: 1; table: its seat count. |
categoryIdoptional | string | A zone section's |
sectionIdoptional | string |
Every member is readonly.
@openseat/core/src/manifest.ts
interface InventoryManifestCategory| Member | Type | Description |
|---|---|---|
id | string | |
name | string | |
colouroptional | string |
Every member is readonly.
@openseat/core/src/manifest.ts
interface InventoryManifestSeat| Member | Type | Description |
|---|---|---|
seatId | string | Public seat ID, the inventory key. |
objectId | string | Layout object ID of the seat. |
label | string | |
sectionIdoptional | string | |
sectionLabeloptional | string | |
rowIdoptional | string | |
rowLabeloptional | string | |
tableIdoptional | string | |
tableLabeloptional | string | |
categoryIdoptional | string | |
x | number | World position, rounded to 3 decimals. |
y | number | |
wheelchair | boolean | |
companionSeat | boolean |
Every member is readonly.
@openseat/core/src/manifest.ts
interface InventoryManifestSection| Member | Type | Description |
|---|---|---|
id | string | |
label | string | |
categoryIdoptional | string | |
rowCount | number | Rows whose |
seatCount | number | Seats whose effective section is this section. |
Every member is readonly.
@openseat/core/src/manifest.ts
One piece of text over one seat, from findLabelSeatOverlaps.
interface LabelSeatOverlap| Member | Type | Description |
|---|---|---|
objectId | string | |
kind | TextBoxKind | |
seatObjectId | string |
Every member is readonly.
@openseat/core/src/label-layout.ts
interface LabelSeatOverlapOptions| Member | Type | Description |
|---|---|---|
lod | LabelLod | |
kindsoptional | readonly TextBoxKind[] | Only these kinds of text. Default: all. |
toleranceoptional | number | Boxes must overlap by more than this on both axes. Default 0 (touching is fine). |
Every member is readonly.
@openseat/core/src/label-layout.ts
The membership hierarchy of a layout, derived from the flat object list:
section ─┬─ row ── seat ├─ table ── seat ├─ GA area └─ loose seat (a seat in no row or table)
sectionId on rows, tables, GA areas and loose seats is the source of truth; on grouped seats it is only a cache. A seat belongs to the first row or table (in document order) that lists it, matching the structural checks. Every other object (stage, booth, label, ...) is top level.
interface LayoutIndex| Member | Type | Description |
|---|---|---|
byId | ReadonlyMap<string, LayoutObject> | First object with each ID, as in validation. |
groupOfSeat | ReadonlyMap<string, string> | Seat object ID -> owning row or table ID. |
contentsOfSection | ReadonlyMap<string, readonly string[]> | Section ID -> direct contents (rows, tables, GA areas and loose seats whose |
Every member is readonly.
@openseat/core/src/membership.ts
A structured validation finding. path is a dotted field path into the document.
interface LayoutIssue| Member | Type | Description |
|---|---|---|
code | LayoutIssueCode | |
severity | "error" | "warning" | |
message | string | |
path | string | Dotted path such as |
objectIdoptional | string | Set when the issue belongs to a specific object. |
Every member is readonly.
@openseat/core/src/validate.ts
A single explicit step between two adjacent schema versions. Migrations receive a plain object and return a new object; they must not mutate input.
interface LayoutMigration| Member | Type | Description |
|---|---|---|
from | number | |
to | number | |
migrate | (document: Record<string, unknown>) => Record<string, unknown> |
Every member is readonly.
@openseat/core/src/migrate.ts
One piece of text and its reserved world box.
interface LayoutTextBox| Member | Type | Description |
|---|---|---|
kind | TextBoxKind | |
objectId | string | |
box | Bounds |
Every member is readonly.
@openseat/core/src/label-layout.ts
interface ManifestDiff| Member | Type | Description |
|---|---|---|
addedSeatIds | string[] | In the next manifest only (next order). |
removedSeatIds | string[] | In the previous manifest only (previous order). |
movedSeatIds | string[] | In both, at a different position (next order). |
relabeledSeatIds | string[] | In both, with a different seat, row, table or section label (next order). |
Every member is readonly.
@openseat/core/src/manifest.ts
interface MigrationResult| Member | Type | Description |
|---|---|---|
value | unknown | The migrated document, or the original input when no migration was needed. |
errors | LayoutIssue[] | Errors encountered while migrating. Empty when the migration chain completed. |
appliedVersions | readonly number[] |
Every member is readonly.
@openseat/core/src/migrate.ts
interface MinRoundTableRadiusOptions| Member | Type | Description |
|---|---|---|
seatShapeoptional | SeatShape | Seat shape; defaults to the default seat circle. |
gapoptional | number | Table edge to seat centre ( |
minGapoptional | number | Clear space between neighbouring seats. Defaults to Default |
Every member is readonly.
@openseat/core/src/table-ops.ts
interface ObjectTransform| Member | Type | Description |
|---|---|---|
position | Point | |
rotation | number | Absolute rotation in degrees. |
scaleX | number | Scale relative to the object's current size. 1 leaves dimensions unchanged. |
scaleY | number |
Every member is readonly.
@openseat/core/src/operations.ts
interface ParseOptions| Member | Type | Description |
|---|---|---|
migrationsoptionalreadonly | readonly LayoutMigration[] | Extra migrations, merged with the built-in chain. |
@openseat/core/src/serialize.ts
interface PasteOptions| Member | Type | Description |
|---|---|---|
atoptional | Point | Centre the pasted content on this point. |
offsetoptional | Point | Offset from the copied position, used when |
Every member is readonly.
@openseat/core/src/operations.ts
Where a divider's caption is drawn, from pathCaptionPlacement.
interface PathCaptionPlacement| Member | Type | Description |
|---|---|---|
at | Point | World centre of the caption: the point halfway along the path. |
rotation | number | Degrees, the direction of the path there, normalised into (-90, 90] so the text stays upright. |
text | string | The drawn text ( |
fontSize | number | |
width | number | Unrotated size of the reserved text box ( |
height | number | |
box | Bounds | World AABB of the rotated text box. |
distance | number | Arc length from the path start to |
length | number | Total arc length of the path. |
Every member is readonly.
@openseat/core/src/object-geometry.ts
Object placement: position is the local origin, rotation is in degrees.
interface Placement| Member | Type | Description |
|---|---|---|
position | Point | |
rotation | number |
Every member is readonly.
@openseat/core/src/geometry.ts
Pure 2D geometry helpers. Coordinates are in layout units; the y axis points down, matching canvas and SVG conventions. Angles are in degrees, measured clockwise on screen (positive rotation turns +x towards +y).
interface Point| Member | Type | Description |
|---|---|---|
x | number | |
y | number |
Every member is readonly.
@openseat/core/src/geometry.ts
interface RectTableOptions extends GeneratorBaseOptionsExtends GeneratorBaseOptions
| Member | Type | Description |
|---|---|---|
label | string | |
center | Point | |
width | number | |
height | number | |
seatsPerEdge | RectTableSeatCounts | Seats per edge. Edges not listed have no seats. Counts are placed evenly along each edge. |
seatOffsetoptional | number | Distance from the table edge to seat centres. Defaults to the seat radius plus 4. |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
sectionIdoptional | string | From |
categoryIdoptional | string | From |
seatShapeoptional | SeatShape | From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/tables.ts
interface RectTableSeatCounts| Member | Type | Description |
|---|---|---|
topoptional | number | |
rightoptional | number | |
bottomoptional | number | |
leftoptional | number |
Every member is readonly.
@openseat/core/src/generators/tables.ts
What a regeneration did to a group's seats, by seat object ID. Hosts should surface removed: those public seat IDs no longer exist, and any inventory mapped to them needs attention.
interface RegenerationReport| Member | Type | Description |
|---|---|---|
retained | readonly string[] | |
added | readonly string[] | |
removed | readonly string[] | |
removedSeatIds | readonly string[] | Public seat IDs of removed seats, for reconciling inventory. |
Every member is readonly.
@openseat/core/src/regenerate.ts
interface RegenerationResult| Member | Type | Description |
|---|---|---|
layout | LayoutDocument | |
report | RegenerationReport | |
messageoptional | string | A note for the user about how the edit was carried out (e.g. how new seats were numbered). |
Every member is readonly.
@openseat/core/src/regenerate.ts
interface RemoveOptions| Member | Type | Description |
|---|---|---|
withContentsoptional | boolean | Remove a section's rows, tables, GA areas and loose seats with it. Default true. Default |
forceoptional | boolean | Remove effectively locked objects too. Default false. Default |
pruneEmptyRowsoptional | boolean | Remove rows that lost seats and now have none. Default true. Default |
Every member is readonly.
@openseat/core/src/operations.ts
Renumbering of tables and booths: rewrites their labels in reading order (front to back, then left to right, or by columns). Object IDs, inventory IDs and seat IDs are never touched, so renumbering is safe on a layout whose inventory is already on sale.
interface RenumberOptions| Member | Type | Description |
|---|---|---|
order | "rows" | "columns" | "rows": front to back (smaller y first), each row left to right. "columns": left to right, each column front to back. |
prefixoptional | string | Text placed before each number. Defaults to "". Default |
startoptional | number | First number. Defaults to 1. Default |
rowToleranceUnitsoptional | number | Objects whose centres differ by at most this much across the reading direction belong to the same row (or column). Defaults to half the smallest object depth among the targets. |
Every member is readonly.
@openseat/core/src/renumber-ops.ts
interface RepairOptions| Member | Type | Description |
|---|---|---|
leveloptionalreadonly | "errors" | "all" |
|
@openseat/core/src/structure-checks.ts
A row label from resolveRowLabelPlacements: the core placement, pushed clear of seats.
interface ResolvedRowLabel extends RowLabelPlacementExtends RowLabelPlacement
| Member | Type | Description |
|---|---|---|
fontSize | number | |
box | Bounds | World AABB of the label's reserved box. |
pushed | number | Units the label was pushed outward along the row to clear seats (0 when it was clear). |
position | Point | From |
rotation | number | Text rotation in degrees, normalised into (-90, 90] so the text is never upside down. From |
align | "left" | "right" | Text alignment relative to From |
side | "start" | "end" | From |
Every member is readonly.
@openseat/core/src/label-layout.ts
interface RetentionOptions| Member | Type | Description |
|---|---|---|
keepLabelsoptional | boolean | Keep existing seat labels instead of the newly generated ones. Default false. Default |
slotsoptional | readonly (string | null)[] | Identity mapping: |
slotIndexesoptional | readonly number[] | Slot index of each generated seat, used to name new seats ( |
respectLabelLocksoptional | boolean | Seats with Default |
newSeatIdoptional | (slot: number, label: string) => string | undefined | Public seat ID for a new seat at |
Every member is readonly.
@openseat/core/src/regenerate.ts
interface RoundTableOptions extends GeneratorBaseOptionsExtends GeneratorBaseOptions
| Member | Type | Description |
|---|---|---|
label | string | Label shown on the table and used in its generated IDs. Must be unique within |
center | Point | |
tableRadius | number | |
seatCount | number | |
seatDistanceoptional | number | Distance from the table centre to each seat centre. Defaults to the table radius plus the seat's own radius plus a small gap, so seats sit clear of the edge. |
startAngleoptional | number | Angle of the first seat in degrees, screen convention. Defaults to -90 (top of the table). |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
sectionIdoptional | string | From |
categoryIdoptional | string | From |
seatShapeoptional | SeatShape | From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/tables.ts
Assigns a category to a band of rows. Rows are numbered from 1, in generation order.
interface RowCategoryBand| Member | Type | Description |
|---|---|---|
fromRow | number | |
toRow | number | |
categoryId | string |
Every member is readonly.
@openseat/core/src/generators/section.ts
Where a row label is drawn, as computed by rowLabelPlacements.
interface RowLabelPlacement| Member | Type | Description |
|---|---|---|
position | Point | |
rotation | number | Text rotation in degrees, normalised into (-90, 90] so the text is never upside down. |
align | "left" | "right" | Text alignment relative to |
side | "start" | "end" |
Every member is readonly.
@openseat/core/src/row-geometry.ts
Result of fitRowLayout.
interface RowLayoutFit| Member | Type | Description |
|---|---|---|
placement | Placement | |
layout | RowLayout | |
residual | number | Largest distance between a stored seat and the place the fitted layout puts it. |
Every member is readonly.
@openseat/core/src/row-geometry.ts
interface RowRegenerationOptions extends RetentionOptionsExtends RetentionOptions
| Member | Type | Description |
|---|---|---|
seatCount | number | |
seatSpacing | number | |
seatShapeoptional | SeatShape | Seat shape for every seat of the row. Defaults to each seat's own shape (new seats copy their neighbour). |
numberingStartoptional | number | First seat number for generated labels. Default: the row's labeling start, or 1. |
keepLabelsoptional | boolean | Keep existing seat labels instead of the newly generated ones. Default false. From Default |
slotsoptional | readonly (string | null)[] | Identity mapping: From |
slotIndexesoptional | readonly number[] | Slot index of each generated seat, used to name new seats ( From |
respectLabelLocksoptional | boolean | Seats with From Default |
newSeatIdoptional | (slot: number, label: string) => string | undefined | Public seat ID for a new seat at From |
Every member is readonly.
@openseat/core/src/regenerate.ts
A seat place derived from a row layout.
interface RowSlotPose| Member | Type | Description |
|---|---|---|
position | Point | |
rotation | number | Seat rotation in degrees. NaN when the layout's seatRotation is "keep" (the caller keeps the old value). |
Every member is readonly.
@openseat/core/src/row-geometry.ts
World AABB of a drawn seat.
interface SeatBox| Member | Type | Description |
|---|---|---|
objectId | string | |
box | Bounds |
Every member is readonly.
@openseat/core/src/label-layout.ts
interface SeatInput| Member | Type | Description |
|---|---|---|
idPrefix | string | |
groupLabel | string | |
seatNumber | number | |
label | string | |
position | Point | |
rotation | number | |
rowLabeloptional | string | |
sectionIdoptional | string | |
categoryIdoptional | string | |
shape | SeatShape |
Every member is readonly.
@openseat/core/src/generators/common.ts
interface SeatNumbering| Member | Type | Description |
|---|---|---|
startoptional | number | First seat number in each row or table. Defaults to 1. Default |
prefixoptional | string | Text placed before the number in the displayed label. |
suffixoptional | string | Text placed after the number in the displayed label. |
formatoptional | LabelFormat | Label format. Defaults to numeric. Default |
stepoptional | number | Difference between consecutive seat numbers (2 numbers odd or even seats). Defaults to 1. Default |
directionoptional | "ltr" | "rtl" | "rtl" gives the last seat of a row the first number. Defaults to "ltr". Default |
Every member is readonly.
@openseat/core/src/generators/common.ts
interface SectionContents| Member | Type | Description |
|---|---|---|
rows | Row[] | Rows front to back. |
seats | Seat[] | |
boundaryoptional | Point[] | Section-local outline; only for outline "auto" or arc seating. |
Every member is readonly.
@openseat/core/src/section-ops.ts
interface SectionFromSelectionOptions| Member | Type | Description |
|---|---|---|
labeloptional | string | Defaults to Default |
paddingoptional | number | Clearance between the seats' edges and the outline. Defaults to 8. Default |
shapeoptional | "hull" | "bounds" | "hull" (default): convex envelope; "bounds": axis-aligned rectangle. |
Every member is readonly.
@openseat/core/src/section-ops.ts
interface SectionSeatingOptions extends Omit<
GeneratorBaseOptions,
"sectionId"
>Extends Omit<GeneratorBaseOptions, "sectionId">
| Member | Type | Description |
|---|---|---|
sectionId | string | Object ID for the generated section. Seats and rows reference it. |
label | string | |
boundary | readonly Point[] | Section outline in layout coordinates. Concave shapes are supported. |
seatSpacing | number | Distance between seat centres along a row. |
rowSpacing | number | Distance between rows. |
angleoptional | number | Direction rows run, in degrees. 0 gives horizontal rows filled top to bottom; 90 gives vertical rows. Seats are rotated to match. |
marginoptional | number | Clearance between seat edges and the boundary. Defaults to 2. Default |
aisleEveryoptional | number | Insert an aisle after every N seats in a row. |
aisleWidthoptional | number | Width of each aisle. Defaults to Default |
minSeatsPerRowoptional | number | Rows with fewer seats than this are skipped. Defaults to 1. Default |
reverseoptional | boolean | Fill from the far side of the section instead (bottom first when angle is 0). |
rowLabelsoptional | readonly string[] | |
rowCategoriesoptional | readonly RowCategoryBand[] | Category bands, for example rows 1–3 VIP and the rest standard. Falls back to |
categoryIdoptional | string | From |
seatShapeoptional | { kind: "circle"; radius: number; } | { kind: "rect"; width: number; height: number; } | From |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/section.ts
interface SectionSeatingResult| Member | Type | Description |
|---|---|---|
layout | LayoutDocument | |
report | RegenerationReport | Seat changes across all rows of the section. |
addedRowIds | string[] | |
removedRowIds | string[] |
Every member is readonly.
@openseat/core/src/section-ops.ts
Where a section's title goes.
interface SectionTitlePlacement| Member | Type | Description |
|---|---|---|
at | Point | World centre of the text. |
fontSize | number | |
rotation | number | Degrees; 0 for header bands (upright whatever the section's rotation), |
text | string | The text to draw: upper-case inside (overview and zones), the label as written in a header band. |
width | number | Unrotated size of the reserved box. |
height | number | |
box | Bounds | World AABB of the reserved box. |
where | "inside" | "above" | "below" | "left" | "right" | "hidden" |
Every member is readonly.
@openseat/core/src/label-layout.ts
A transform of a whole selection about a pivot, as produced by a group transformer: M = T(pivot + translate) · R(rotation) · S(sx·flipX, sy·flipY) · T(-pivot).
interface SelectionTransform| Member | Type | Description |
|---|---|---|
pivot | Point | |
translateoptional | Point | |
rotationoptional | number | Degrees, clockwise on screen. |
scaleXoptional | number | |
scaleYoptional | number | |
flipXoptional | boolean | |
flipYoptional | boolean |
Every member is readonly.
@openseat/core/src/operations.ts
Counts behind the editor's "Tickets for sale" figure.
interface SellableSummary| Member | Type | Description |
|---|---|---|
seats | number | Seats that can be sold, including the seats of tables booked as a whole (not those of zone sections). |
gaPlaces | number | GA and zone-section capacity that can be sold. |
booths | number | Booths with an inventory ID that can be sold. |
wholeTables | number | Tables booked as a whole that can be sold. |
notForSaleSeats | number | Seats held back (unsellable category or not for sale), including those of whole tables. |
notForSalePlaces | number | GA capacity and booths held back. |
totalSellable | number | seats + gaPlaces + booths. |
Every member is readonly.
@openseat/core/src/category-ops.ts
interface SetObjectsCategoryOptions| Member | Type | Description |
|---|---|---|
withMembersoptionalreadonly | boolean | Also write the category on the members of each unit: a section's seats, a row's or table's seats. Rows and tables carry no category themselves. |
@openseat/core/src/category-ops.ts
interface SetRowLayoutOptions| Member | Type | Description |
|---|---|---|
keepLabelsoptional | boolean | Keep existing seat labels instead of applying the layout's labeling. |
seatShapeoptional | SeatShape | Shape for every seat of the row. Default: each seat keeps its own; new seats copy their nearest neighbour. |
pinoptional | "anchor" | "first-retained" | "anchor" (default) keeps row.position; "first-retained" moves the row so its first retained seat stays put. |
Every member is readonly.
@openseat/core/src/row-ops.ts
A work allowance shared by several polygonSimplicity calls: candidate edge pairs (pairs whose x extents overlap) left to look at.
interface SimplicityBudget| Member | Type | Description |
|---|---|---|
remaining | number |
@openseat/core/src/geometry.ts
interface StraightRowsOptions extends GeneratorBaseOptionsExtends GeneratorBaseOptions
| Member | Type | Description |
|---|---|---|
rowCount | number | |
seatsPerRow | number | |
origin | Point | Position of the first seat in the first row. |
seatSpacing | number | Horizontal distance between adjacent seats in a row. |
rowSpacing | number | Vertical distance between rows. |
rowLabelsoptional | readonly string[] | Row labels. Defaults to A, B, C… and must be unique. |
rowLabelingoptional | RowLabeling | Row label sequence used when |
staggeroptional | number | Horizontal offset applied to every odd-numbered row, for staggered seating. |
aisleAfteroptional | readonly number[] | Seat numbers (1-based, within a row) after which an aisle gap is inserted. Entry |
aisleGapoptional | number | Extra horizontal space added at each aisle gap. Defaults to Default |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
sectionIdoptional | string | From |
categoryIdoptional | string | From |
seatShapeoptional | SeatShape | From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/rows.ts
One change made by repairStructure.
interface StructureRepair| Member | Type | Description |
|---|---|---|
kind | StructureRepairKind | |
objectId | string | The object that was changed or removed. |
detail | string |
Every member is readonly.
@openseat/core/src/structure-checks.ts
Seat counts on the four edges of a rectangular table.
interface TableEdgeCounts| Member | Type | Description |
|---|---|---|
top | number | |
right | number | |
bottom | number | |
left | number |
Every member is readonly.
@openseat/core/src/table-ops.ts
interface TableEditOptions| Member | Type | Description |
|---|---|---|
growoptional | boolean | When seats would come closer than Default |
keepLabelsoptional | boolean | Keep existing seat labels instead of renumbering clockwise. Default false. Default |
respectLabelLocksoptional | boolean | Seats with Default |
Every member is readonly.
@openseat/core/src/table-ops.ts
Changes to a table's shape and seat arrangement; see setTableArrangement.
interface TableEditPatch| Member | Type | Description |
|---|---|---|
shapeoptional | TableShape | New table shape. Switching kind converts the arrangement, keeping the seat count. |
seatCountoptional | number | Total seats. On a rect table it redistributes the seats over the edges ( |
seatsPerEdgeoptional | Partial<{ top: number; right: number; bottom: number; left: number; }> | Rect only. Merged into the current edge counts; takes precedence over |
seatDistanceoptional | number | Round only: table centre to seat centre. |
seatOffsetoptional | number | Rect only: table edge to seat centre. |
startAngleoptional | number | Round only: angle of the first seat in degrees, screen convention. |
seatShapeoptional | SeatShape | Shape of every seat. Defaults to the first seat's shape. |
bookAsWholeoptional | boolean | Sell the whole table as one unit. |
Every member is readonly.
@openseat/core/src/table-ops.ts
interface TableEditResult| Member | Type | Description |
|---|---|---|
layout | LayoutDocument | |
report | RegenerationReport | |
clamped | boolean | True when |
Every member is readonly.
@openseat/core/src/table-ops.ts
Where one seat of a table sits, in table-local coordinates (origin at the table centre, unrotated).
interface TableSeatPlacement| Member | Type | Description |
|---|---|---|
position | Point | |
rotation | number | Seat rotation in degrees, relative to the table. |
Every member is readonly.
@openseat/core/src/generators/tables.ts
interface TableSeatsOptions extends GeneratorBaseOptionsExtends GeneratorBaseOptions
| Member | Type | Description |
|---|---|---|
label | string | Table label, used in the generated seat IDs. |
centeroptional | Point | Table centre. Defaults to the origin (table-local coordinates). |
shape | TableShape | |
arrangement | TableArrangement | Must have the same kind as |
seatCountoptional | number | Number of seats of a round arrangement. Ignored for rect arrangements, whose edge counts decide. |
idPrefix | string | Prefix for every generated object ID and seat ID. Use a unique value per generated block so two blocks never collide. Re-running a generator with the same prefix and labels produces the same IDs. From |
sectionIdoptional | string | From |
categoryIdoptional | string | From |
seatShapeoptional | SeatShape | From |
numberingoptional | SeatNumbering | From |
Every member is readonly.
@openseat/core/src/generators/tables.ts
Options of estimateTextBox.
interface TextBoxOptions| Member | Type | Description |
|---|---|---|
boldoptional | boolean | Bold text is estimated wider (0.68 em per character instead of 0.62). |
letterSpacingoptional | number | Extra space between letters, in em. |
Every member is readonly.
@openseat/core/src/object-geometry.ts
Where a GA zone's or stage's label goes.
interface TextPlacement| Member | Type | Description |
|---|---|---|
at | Point | |
fontSize | number | |
rotation | number | |
text | string | |
width | number | |
height | number | |
box | Bounds | |
letterSpacing | number | Letter spacing in em the size was computed with. |
bold | boolean |
Every member is readonly.
@openseat/core/src/label-layout.ts
interface UnsellableInventory| Member | Type | Description |
|---|---|---|
categoryIds | string[] |
|
inventoryIds | string[] | Inventory keys that must not be sold, sorted: seat |
notForSaleIds | string[] |
|
Every member is readonly.
@openseat/core/src/category-ops.ts
interface ValidateOptions| Member | Type | Description |
|---|---|---|
rulesoptionalreadonly | Partial<Record<LayoutIssueCode, IssueRuleSetting>> | Re-maps the severity of semantic issues by code, e.g. |
@openseat/core/src/validate.ts
interface ValidationResult| Member | Type | Description |
|---|---|---|
valid | boolean | |
layout | LayoutDocument | null | The typed, default-filled document. Present only when |
errors | LayoutIssue[] | |
warnings | LayoutIssue[] |
Every member is readonly.
@openseat/core/src/validate.ts
Result of a vertex edit. A rejected edit returns the original layout.
interface VertexEditResult| Member | Type | Description |
|---|---|---|
layout | LayoutDocument | |
rejectedoptional | VertexEditRejection |
Every member is readonly.
@openseat/core/src/section-ops.ts
interface ZoneOnlyOptions| Member | Type | Description |
|---|---|---|
sectionIdsoptionalreadonly | readonly string[] | Only these sections become zones, and seats outside sections are left alone. Absent: every section with seats, after loose seats, rows and tables are wrapped into new sections. |
@openseat/core/src/zone-ops.ts
A world-space rectangle: top-left corner and size.
interface ZoneRect| Member | Type | Description |
|---|---|---|
x | number | |
y | number | |
width | number | |
height | number |
Every member is readonly.
@openseat/core/src/zone-ops.ts
// Inferred from its zod schema: z.infer<typeof accessibilitySchema>
type Accessibility = {
wheelchair: boolean;
companionSeat: boolean;
notes?: string;
};| Member | Type | Description |
|---|---|---|
wheelchair | boolean | |
companionSeat | boolean | |
notesoptional | string |
@openseat/core/src/schema.ts
| Member | Type | Description |
|---|---|---|
type | "aisle" | |
points | Point[] | |
width | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
nameoptional | string |
@openseat/core/src/schema.ts
type AlignEdge =
"left" | "centerX" | "right" | "top" | "centerY" | "bottom";@openseat/core/src/operations.ts
// Inferred from its zod schema: z.infer<typeof arcSeatingSchema>
type ArcSeating = {
seatSpacing: number;
seatShape: SeatShape;
seatLabeling: SeatLabeling;
kind: "arc";
center: Point;
innerRadius: number;
rows: number;
rowSpacing: number;
startAngle: number;
endAngle: number;
edgeMargin: number;
rowLabelPosition?: "start" | "end" | "none" | "both";
rowSeats?: number[];
};| Member | Type | Description |
|---|---|---|
seatSpacing | number | |
seatShape | SeatShape | |
seatLabeling | SeatLabeling | |
kind | "arc" | |
center | Point | |
innerRadius | number | |
rows | number | |
rowSpacing | number | |
startAngle | number | |
endAngle | number | |
edgeMargin | number | |
rowLabelPositionoptional | "start" | "end" | "none" | "both" | |
rowSeatsoptional | number[] |
@openseat/core/src/schema.ts
type ArenaFieldKind = "rink" | "court" | "pitch" | "stage";@openseat/core/src/generators/arena.ts
Arena options without placement, as returned by arenaPreset.
type ArenaPreset = Omit<ArenaOptions, "idPrefix" | "center">;| Member | Type | Description |
|---|---|---|
venueoptional | { readonly margin: number; readonly shape?: "rect" | "ellipse"; } | Venue outline around the outermost tier. Defaults to a rounded rectangle 40 units out. |
titleoptional | string | Human-readable name, e.g. "Ice hockey arena"; |
seatLabelingoptional | SeatLabeling | How seats are numbered within each row. Defaults to 1, 2, 3… from each spectator's left. |
numberingoptional | ArenaNumbering | |
field | ArenaField | |
tiers | readonly ArenaTier[] | Tiers from the field outwards. |
fieldKindoptional | "stage" | "rink" | "court" | "pitch" | Defaults to "rink". Sets the field's default label, colours and markings. Default |
balanceSectionsoptional | boolean | Evens out the seats per section around each tier. The tier keeps its total section count (never more), but the sections are redistributed between the long sides, the short sides and the corners so the sections on every side and in every corner hold about the same number of seats: without it, a round-ended field gives huge corner fans next to thin side blocks. A side or corner left at 0 stays empty, and a side too short for any section gives its share to the others. Off by default, so existing options generate exactly what they did before. |
Every member is readonly.
@openseat/core/src/generators/arena.ts
type ArenaPresetName = (typeof ARENA_PRESET_NAMES)[number];@openseat/core/src/generators/arena.ts
// Inferred from its zod schema: z.infer<typeof blockSeatingSchema>
type BlockSeating = {
seatSpacing: number;
seatShape: SeatShape;
seatLabeling: SeatLabeling;
kind: "block";
rows: number;
seatsPerRow: number;
rowSpacing: number;
curvature: number;
align: "center" | "left" | "right";
rowLabelPosition?: "start" | "end" | "none" | "both";
rowSeats?: number[];
stagger?: number;
aisles?: number[];
aisleWidth?: number;
};| Member | Type | Description |
|---|---|---|
seatSpacing | number | |
seatShape | SeatShape | |
seatLabeling | SeatLabeling | |
kind | "block" | |
rows | number | |
seatsPerRow | number | |
rowSpacing | number | |
curvature | number | |
align | "center" | "left" | "right" | |
rowLabelPositionoptional | "start" | "end" | "none" | "both" | |
rowSeatsoptional | number[] | |
staggeroptional | number | |
aislesoptional | number[] | |
aisleWidthoptional | number |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof boothSchema>
type Booth = {
type: "booth";
label: string;
width: number;
height: number;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
categoryId?: string;
inventoryId?: string;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "booth" | |
label | string | |
width | number | |
height | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
categoryIdoptional | string | |
inventoryIdoptional | string | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof categorySchema>
type Category = {
id: string;
name: string;
colour?: string;
};| Member | Type | Description |
|---|---|---|
id | string | |
name | string | |
colouroptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof pathSchema>
type CustomPath = {
type: "path";
points: Point[];
closed: boolean;
strokeWidth: number;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
caption?: string;
captionSize?: number;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "path" | |
points | Point[] | |
closed | boolean | |
strokeWidth | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
captionoptional | string | |
captionSizeoptional | number | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof entranceSchema>
type Entrance = {
type: "entrance";
width: number;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
label?: string;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "entrance" | |
width | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
labeloptional | string | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof fillSeatingSchema>
type FillSeating = {
seatSpacing: number;
seatShape: SeatShape;
seatLabeling: SeatLabeling;
kind: "fill";
rowSpacing: number;
angle: number;
margin: number;
aisleEvery: number;
minSeatsPerRow: number;
reverse: boolean;
rowLabelPosition?: "start" | "end" | "none" | "both";
aisleWidth?: number;
};| Member | Type | Description |
|---|---|---|
seatSpacing | number | |
seatShape | SeatShape | |
seatLabeling | SeatLabeling | |
kind | "fill" | |
rowSpacing | number | |
angle | number | |
margin | number | |
aisleEvery | number | |
minSeatsPerRow | number | |
reverse | boolean | |
rowLabelPositionoptional | "start" | "end" | "none" | "both" | |
aisleWidthoptional | number |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof gaAreaSchema>
type GaArea = {
type: "ga";
label: string;
boundary: Point[];
capacity: number;
inventoryId: string;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
categoryId?: string;
sectionId?: string;
style?: { fill?: string | undefined; stroke?: string | undefined };
seating?: "standing" | "seated";
cornerRadius?: number;
labelRotation?: number;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "ga" | |
label | string | |
boundary | Point[] | |
capacity | number | |
inventoryId | string | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
categoryIdoptional | string | |
sectionIdoptional | string | |
styleoptional | { fill?: string | undefined; stroke?: string | undefined; } | |
seatingoptional | "standing" | "seated" | |
cornerRadiusoptional | number | |
labelRotationoptional | number | |
nameoptional | string |
@openseat/core/src/schema.ts
Whether a GA zone is seated (buyers pick a quantity) or standing; absent means "standing".
type GaSeating = NonNullable<GaArea["seating"]>;@openseat/core/src/schema.ts
World-space hit geometry for a layout object. Renderers and editors share this so that what is drawn, what is clickable, and what is culled agree.
| Discriminant | Type | Description |
|---|---|---|
kind: "circle" | { readonly kind: "circle"; readonly center: Point; readonly radius: number; } | |
kind: "polygon" | { readonly kind: "polygon"; readonly points: readonly Point[]; } | |
kind: "polyline" | { readonly kind: "polyline"; readonly points: readonly Point[]; readonly halfWidth: number; } | |
kind: "none" | { readonly kind: "none"; } |
@openseat/core/src/object-geometry.ts
Severity override for one issue code; "off" drops the issue.
type IssueRuleSetting = "error" | "warning" | "off";@openseat/core/src/validate.ts
// Inferred from its zod schema: z.infer<typeof labelSchema>
type Label = {
type: "label";
text: string;
fontSize: number;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
style?: LabelStyle;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "label" | |
text | string | |
fontSize | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
styleoptional | LabelStyle | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof labelFormatSchema>
type LabelFormat = "numeric" | "alpha" | "alpha-lower" | "roman";@openseat/core/src/schema.ts
Level of detail: "detail" when seats are drawn, "overview" when zoomed out (no seats drawn).
type LabelLod = "detail" | "overview";@openseat/core/src/label-layout.ts
type LabelStyle = NonNullable<Label["style"]>;| Member | Type | Description |
|---|---|---|
colouroptional | string | |
boldoptional | boolean | |
italicoptional | boolean | |
letterSpacingoptional | number |
@openseat/core/src/schema.ts
A complete seating layout: the canvas, its ticket categories and every object on it (seats, rows, sections, tables, booths, stages, standing areas and more), plus a revision your backend increments on every save. It is plain JSON: store it as it is, create one with createLayout, and check one that came from outside with validateLayout.
// Inferred from its zod schema: z.infer<typeof layoutSchema>
type LayoutDocument = {
schemaVersion: 1;
id: string;
name: string;
revision: number;
canvas: { width: number; height: number; gridSize: number };
metadata: Record<string, unknown>;
categories: Category[];
objects: LayoutObject[];
venue?: Venue;
focalPoint?: Point;
createdAt?: string;
updatedAt?: string;
};| Member | Type | Description |
|---|---|---|
schemaVersion | 1 | |
id | string | |
name | string | |
revision | number | |
canvas | { width: number; height: number; gridSize: number; } | |
metadata | Record<string, unknown> | |
categories | Category[] | |
objects | LayoutObject[] | |
venueoptional | Venue | |
focalPointoptional | Point | |
createdAtoptional | string | |
updatedAtoptional | string |
@openseat/core/src/schema.ts
type LayoutIssueCode =
| "INVALID_DOCUMENT"
| "UNSUPPORTED_SCHEMA_VERSION"
| "SCHEMA_VIOLATION"
| "NON_FINITE_VALUE"
| "INVALID_DIMENSION"
| "MALFORMED_GEOMETRY"
| "DUPLICATE_OBJECT_ID"
| "DUPLICATE_SEAT_ID"
| "DUPLICATE_CATEGORY_ID"
| "UNRESOLVED_REFERENCE"
| "CAPACITY_MISMATCH"
| "SEAT_OUTSIDE_SECTION"
| "OBJECT_OUTSIDE_CANVAS"
| "DUPLICATE_SEAT_POSITION"
| "SEAT_IN_MULTIPLE_GROUPS"
| "DUPLICATE_GROUP_MEMBER"
| "GROUP_SECTION_MISMATCH"
| "ROW_LABEL_MISMATCH"
| "DUPLICATE_ROW_LABEL"
| "DUPLICATE_SEAT_LABEL"
| "DUPLICATE_SECTION_LABEL"
| "DUPLICATE_SEAT_TAG"
| "EMPTY_ROW"
| "SEAT_OVERLAP"
| "OVERLAP_CHECK_SKIPPED"
| "SEAT_WITHOUT_CATEGORY"
| "SEATING_IN_STANDING_AREA"
| "INVALID_ROW_LAYOUT"
| "INVALID_ROW_ORDER"
| "INVALID_SECTION_SEATING"
| "DUPLICATE_INVENTORY_ID"
| "LABEL_OVER_SEATS"
| "ZONE_CAPACITY_ABOVE_SEATS";@openseat/core/src/validate.ts
One object on a layout's canvas. Its type tells the variants apart (listed below); narrow with obj.type === "seat" before reading variant fields.
| Discriminant | Type | Description |
|---|---|---|
type: "seat" | Seat | |
type: "row" | Row | |
type: "section" | Section | |
type: "table" | Table | |
type: "booth" | Booth | |
type: "stage" | Stage | |
type: "aisle" | Aisle | |
type: "label" | Label | |
type: "polygon" | Polygon | |
type: "path" | CustomPath | |
type: "entrance" | Entrance | |
type: "ga" | GaArea |
@openseat/core/src/schema.ts
type LayoutObjectType = LayoutObject["type"];@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof polygonSchema>
type Polygon = {
type: "polygon";
points: Point[];
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
categoryId?: string;
style?: { fill?: string | undefined; stroke?: string | undefined };
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "polygon" | |
points | Point[] | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
categoryIdoptional | string | |
styleoptional | { fill?: string | undefined; stroke?: string | undefined; } | |
nameoptional | string |
@openseat/core/src/schema.ts
type ReorderDirection = "forward" | "backward" | "front" | "back";@openseat/core/src/operations.ts
One piece of a rounded outline from roundedPolygonPath.
type RoundedPathSegment =
| {
readonly kind: "line";
readonly from: Point;
readonly to: Point;
}
| {
readonly kind: "arc";
readonly from: Point;
readonly to: Point;
readonly center: Point;
readonly radius: number;
readonly startAngle: number;
readonly endAngle: number;
readonly clockwise: boolean;
};| Discriminant | Type | Description |
|---|---|---|
kind: "line" | { readonly kind: "line"; readonly from: Point; readonly to: Point; } | |
kind: "arc" | { readonly kind: "arc"; readonly from: Point; readonly to: Point; readonly center: Point; readonly radius: number; readonly startAngle: number; readonly endAngle: number; readonly clockwise: boolean; } |
@openseat/core/src/object-geometry.ts
// Inferred from its zod schema: z.infer<typeof rowSchema>
type Row = {
type: "row";
rowLabel: string;
seatObjectIds: string[];
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
sectionId?: string;
layout?: RowLayout;
labelPosition?: "start" | "end" | "none" | "both";
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "row" | |
rowLabel | string | |
seatObjectIds | string[] | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
sectionIdoptional | string | |
layoutoptional | RowLayout | |
labelPositionoptional | "start" | "end" | "none" | "both" | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof rowGapSchema>
type RowGap = {
after: number;
width: number;
};| Member | Type | Description |
|---|---|---|
after | number | |
width | number |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof rowLabelingSchema>
type RowLabeling = {
format: LabelFormat;
start: number;
step: number;
direction: "front-to-back" | "back-to-front";
skip?: string[];
prefix?: string;
suffix?: string;
};| Member | Type | Description |
|---|---|---|
format | LabelFormat | |
start | number | |
step | number | |
direction | "front-to-back" | "back-to-front" | |
skipoptional | string[] | |
prefixoptional | string | |
suffixoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof rowLabelPositionSchema>
type RowLabelPosition = "start" | "end" | "none" | "both";@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof rowLayoutSchema>
type RowLayout = {
seatSpacing: number;
radius?: number;
anchor?: "start" | "end" | "center";
gaps?: RowGap[];
holes?: number[];
seatRotation?: "row" | "tangent" | "keep";
labeling?: SeatLabeling;
};| Member | Type | Description |
|---|---|---|
seatSpacing | number | |
radiusoptional | number | |
anchoroptional | "start" | "end" | "center" | |
gapsoptional | RowGap[] | |
holesoptional | number[] | |
seatRotationoptional | "row" | "tangent" | "keep" | |
labelingoptional | SeatLabeling |
@openseat/core/src/schema.ts
Changes for setRowLayout. radius: null straightens the row; seatCount counts real seats.
| Member | Type | Description |
|---|---|---|
seatSpacingoptional | number | |
anchoroptional | "start" | "end" | "center" | |
gapsoptional | RowGap[] | |
holesoptional | number[] | |
seatRotationoptional | "row" | "tangent" | "keep" | |
labelingoptional | SeatLabeling | |
radiusoptional | number | null | |
seatCountoptional | number | |
positionoptional | Point | |
rotationoptional | number |
@openseat/core/src/row-ops.ts
// Inferred from its zod schema: z.infer<typeof seatSchema>
type Seat = {
type: "seat";
seatId: string;
label: string;
shape: SeatShape;
accessibility: Accessibility;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
rowLabel?: string;
sectionId?: string;
categoryId?: string;
labelLocked?: boolean;
tags?: string[];
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "seat" | |
seatId | string | |
label | string | |
shape | SeatShape | |
accessibility | Accessibility | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
rowLabeloptional | string | |
sectionIdoptional | string | |
categoryIdoptional | string | |
labelLockedoptional | boolean | |
tagsoptional | string[] | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof seatLabelingSchema>
type SeatLabeling = {
format: LabelFormat;
start: number;
step: number;
direction: "ltr" | "rtl";
prefix?: string;
suffix?: string;
};| Member | Type | Description |
|---|---|---|
format | LabelFormat | |
start | number | |
step | number | |
direction | "ltr" | "rtl" | |
prefixoptional | string | |
suffixoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof seatShapeSchema>
type SeatShape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };| Discriminant | Type | Description |
|---|---|---|
kind: "circle" | { kind: "circle"; radius: number; } | |
kind: "rect" | { kind: "rect"; width: number; height: number; } |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof sectionSchema>
type Section = {
type: "section";
label: string;
boundary: Point[];
style: { fill?: string | undefined; stroke?: string | undefined };
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
categoryId?: string;
rowOrder?: string[];
rowLabeling?: RowLabeling;
seating?: BlockSeating | ArcSeating | FillSeating;
outline?: "none" | "custom" | "auto";
labelSize?: number;
labelOffset?: Point;
showSeats?: boolean;
capacity?: number;
cornerRadius?: number;
labelRotation?: number;
labelPlacement?: "hidden" | "left" | "right" | "auto" | "above" | "below";
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "section" | |
label | string | |
boundary | Point[] | |
style | { fill?: string | undefined; stroke?: string | undefined; } | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
categoryIdoptional | string | |
rowOrderoptional | string[] | |
rowLabelingoptional | RowLabeling | |
seatingoptional | BlockSeating | ArcSeating | FillSeating | |
outlineoptional | "none" | "custom" | "auto" | |
labelSizeoptional | number | |
labelOffsetoptional | Point | |
showSeatsoptional | boolean | |
capacityoptional | number | |
cornerRadiusoptional | number | |
labelRotationoptional | number | |
labelPlacementoptional | "hidden" | "left" | "right" | "auto" | "above" | "below" | |
nameoptional | string |
@openseat/core/src/schema.ts
Where a section's title goes when its seats are drawn; absent means "auto".
type SectionLabelPlacement = NonNullable<Section["labelPlacement"]>;@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof sectionSeatingSchema>
type SectionSeating = BlockSeating | ArcSeating | FillSeating;| Discriminant | Type | Description |
|---|---|---|
kind: "block" | BlockSeating | |
kind: "arc" | ArcSeating | |
kind: "fill" | FillSeating |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof stageSchema>
type Stage = {
type: "stage";
width: number;
height: number;
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
label?: string;
cornerRadius?: number;
style?: StageStyle;
markings?: "rink" | "court" | "pitch";
shape?: "rect" | "round" | "thrust" | "semicircle";
thrust?: StageThrust;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "stage" | |
width | number | |
height | number | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
labeloptional | string | |
cornerRadiusoptional | number | |
styleoptional | StageStyle | |
markingsoptional | "rink" | "court" | "pitch" | |
shapeoptional | "rect" | "round" | "thrust" | "semicircle" | |
thrustoptional | StageThrust | |
nameoptional | string |
@openseat/core/src/schema.ts
type StageMarkings = NonNullable<Stage["markings"]>;@openseat/core/src/schema.ts
Outline shape of a stage; absent on a stage means "rect".
type StageShape = NonNullable<Stage["shape"]>;@openseat/core/src/schema.ts
type StageStyle = NonNullable<Stage["style"]>;| Member | Type | Description |
|---|---|---|
filloptional | string | |
strokeoptional | string | |
strokeWidthoptional | number |
@openseat/core/src/schema.ts
Runway of a thrust stage.
type StageThrust = NonNullable<Stage["thrust"]>;| Member | Type | Description |
|---|---|---|
widthoptional | number | |
depthoptional | number |
@openseat/core/src/schema.ts
type StructureRepairKind =
| "seat-in-multiple-groups"
| "duplicate-member"
| "dangling-member"
| "dangling-section"
| "dangling-category"
| "row-order"
| "row-layout"
| "capacity"
| "seat-cache"
| "empty-row";@openseat/core/src/structure-checks.ts
// Inferred from its zod schema: z.infer<typeof tableSchema>
type Table = {
type: "table";
label: string;
shape: TableShape;
capacity: number;
seatObjectIds: string[];
id: string;
visible: boolean;
locked: boolean;
zIndex: number;
position: Point;
rotation: number;
sectionId?: string;
arrangement?:
| { kind: "round"; seatDistance: number; startAngle: number }
| {
kind: "rect";
seatsPerEdge: {
top: number;
right: number;
bottom: number;
left: number;
};
seatOffset: number;
};
bookAsWhole?: boolean;
name?: string;
};| Member | Type | Description |
|---|---|---|
type | "table" | |
label | string | |
shape | TableShape | |
capacity | number | |
seatObjectIds | string[] | |
id | string | |
visible | boolean | |
locked | boolean | |
zIndex | number | |
position | Point | |
rotation | number | |
sectionIdoptional | string | |
arrangementoptional | { kind: "round"; seatDistance: number; startAngle: number; } | { kind: "rect"; seatsPerEdge: { top: number; right: number; bottom: number; left: number; }; seatOffset: number; } | |
bookAsWholeoptional | boolean | |
nameoptional | string |
@openseat/core/src/schema.ts
// Inferred from its zod schema: z.infer<typeof tableArrangementSchema>
type TableArrangement =
| { kind: "round"; seatDistance: number; startAngle: number }
| {
kind: "rect";
seatsPerEdge: {
top: number;
right: number;
bottom: number;
left: number;
};
seatOffset: number;
};| Discriminant | Type | Description |
|---|---|---|
kind: "round" | { kind: "round"; seatDistance: number; startAngle: number; } | |
kind: "rect" | { kind: "rect"; seatsPerEdge: { top: number; right: number; bottom: number; left: number; }; seatOffset: number; } |
@openseat/core/src/schema.ts
type TableRegenerationOptions = RetentionOptions &
(
| {
readonly shape: {
readonly kind: "round";
readonly radius: number;
};
readonly seatCount: number;
readonly seatDistance?: number;
}
| {
readonly shape: {
readonly kind: "rect";
readonly width: number;
readonly height: number;
};
readonly seatsPerEdge: RectTableSeatCounts;
readonly seatOffset?: number;
}
) & {
readonly seatShape?: SeatShape;
};@openseat/core/src/regenerate.ts
// Inferred from its zod schema: z.infer<typeof tableShapeSchema>
type TableShape =
| { kind: "round"; radius: number }
| { kind: "rect"; width: number; height: number };| Discriminant | Type | Description |
|---|---|---|
kind: "round" | { kind: "round"; radius: number; } | |
kind: "rect" | { kind: "rect"; width: number; height: number; } |
@openseat/core/src/schema.ts
What a piece of text belongs to. Seat numbers are not text boxes: they sit inside their seats by design.
type TextBoxKind =
| "section-title"
| "row-label"
| "zone-label"
| "stage-label"
| "label"
| "caption"
| "table-label"
| "booth-label";@openseat/core/src/label-layout.ts
// Inferred from its zod schema: z.infer<typeof venueSchema>
type Venue = {
shape: "rect" | "ellipse";
position: Point;
width: number;
height: number;
name?: string;
showName?: boolean;
cornerRadius?: number;
style?: { fill?: string | undefined; stroke?: string | undefined };
};| Member | Type | Description |
|---|---|---|
shape | "rect" | "ellipse" | |
position | Point | |
width | number | |
height | number | |
nameoptional | string | |
showNameoptional | boolean | |
cornerRadiusoptional | number | |
styleoptional | { fill?: string | undefined; stroke?: string | undefined; } |
@openseat/core/src/schema.ts
type VertexEditRejection =
"not-editable" | "too-few-points" | "self-intersecting" | "zero-area";@openseat/core/src/section-ops.ts
const accessibilitySchema: z.ZodObject<
{
wheelchair: z.ZodDefault<z.ZodBoolean>;
companionSeat: z.ZodDefault<z.ZodBoolean>;
notes: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>@openseat/core/src/schema.ts
const aisleSchema: z.ZodObject<
{
type: z.ZodLiteral<"aisle">;
points: z.ZodArray<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
width: z.ZodNumber;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Concentric rows on an arc between two angles.
const arcSeatingSchema: z.ZodObject<
{
seatSpacing: z.ZodNumber;
seatShape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
seatLabeling: z.ZodObject<
{
format: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
// … 40 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Bounds on ArenaOptions, enforced before anything is generated so a hostile or mistyped option cannot make the generator run for minutes.
const ARENA_LIMITS: {
readonly maxTiers: 8;
readonly maxRowsPerTier: 100;
readonly maxSectionsPerSide: 50;
readonly maxFieldSize: 100000;
readonly maxSpacing: 10000;
readonly maxSeatsPerRow: 2000;
readonly maxSeats: 200000;
}@openseat/core/src/generators/arena.ts
Names accepted by arenaPreset.
const ARENA_PRESET_NAMES: readonly [
"hockey",
"basketball",
"concert-in-round",
"football",
]@openseat/core/src/generators/arena.ts
Display titles of the presets; createArenaLayout names a preset layout after its title.
const ARENA_PRESET_TITLES: Readonly<
Record<ArenaPresetName, string>
>@openseat/core/src/generators/arena.ts
A rectangular (optionally curved, ragged or staggered) block of rows.
const blockSeatingSchema: z.ZodObject<
{
seatSpacing: z.ZodNumber;
seatShape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
seatLabeling: z.ZodObject<
{
format: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
// … 39 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
const boothSchema: z.ZodObject<
{
type: z.ZodLiteral<"booth">;
label: z.ZodString;
width: z.ZodNumber;
height: z.ZodNumber;
categoryId: z.ZodOptional<z.ZodString>;
inventoryId: z.ZodOptional<z.ZodString>;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Migrations registered for this build. Version 1 is the first published schema, so this list is empty. Add an entry here when schema version 2 ships.
Versioning policy: schema version 1 grows additively. New fields are optional and carry no Zod default, so every existing v1 document stays valid and re-serialises byte-for-byte without a migration step. Bump to version 2 (and register a migration here) only for a non-additive change (renaming, removing or re-typing a field, or changing what a stored value means) or when freezing the format for 1.0 before the first publish.
Structural corruption inside a valid v1 document (a seat in two groups, dangling references) is not a schema change; it is fixed by repairStructure in structure-checks.ts, not by a migration.
const builtinMigrations: readonly LayoutMigration[]@openseat/core/src/migrate.ts
const categorySchema: z.ZodObject<
{
id: z.ZodString;
name: z.ZodString;
colour: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Rows lettered A, B, C… from the front.
const DEFAULT_ROW_LABELING: RowLabeling@openseat/core/src/labeling.ts
Seats numbered 1, 2, 3… from the first slot.
const DEFAULT_SEAT_LABELING: SeatLabeling@openseat/core/src/labeling.ts
Default seat shape used when a generator is not given one: a 10-unit radius circle.
const DEFAULT_SEAT_SHAPE: SeatShape@openseat/core/src/generators/common.ts
Clear space kept between neighbouring seats when fitting or growing a table.
const DEFAULT_TABLE_SEAT_GAP = 2@openseat/core/src/table-ops.ts
Letter spacing (em) of a divider caption.
const DIVIDER_CAPTION_LETTER_SPACING = 0.1@openseat/core/src/object-geometry.ts
Default font size of a divider caption (a path's caption) when captionSize is absent.
const DIVIDER_CAPTION_SIZE = 9@openseat/core/src/object-geometry.ts
Entrances are drawn as a bar width long and this thick.
const ENTRANCE_DEPTH = 8@openseat/core/src/object-geometry.ts
const entranceSchema: z.ZodObject<
{
type: z.ZodLiteral<"entrance">;
label: z.ZodOptional<z.ZodString>;
width: z.ZodNumber;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Rows that fill the section boundary at an angle.
const fillSeatingSchema: z.ZodObject<
{
seatSpacing: z.ZodNumber;
seatShape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
seatLabeling: z.ZodObject<
{
format: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
// … 33 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Default colour of a footnote made by createFootnote.
const FOOTNOTE_COLOUR = "#9e9e9e"@openseat/core/src/zone-ops.ts
Font size of a footnote made by createFootnote.
const FOOTNOTE_FONT_SIZE = 12@openseat/core/src/zone-ops.ts
Letter spacing (em) of a footnote made by createFootnote.
const FOOTNOTE_LETTER_SPACING = 0.08@openseat/core/src/zone-ops.ts
A zone sold by quantity up to capacity rather than by seat: standing (general admission) or seated (seating: "seated").
const gaAreaSchema: z.ZodObject<
{
type: z.ZodLiteral<"ga">;
label: z.ZodString;
boundary: z.ZodArray<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
capacity: z.ZodNumber;
inventoryId: z.ZodString;
categoryId: z.ZodOptional<z.ZodString>;
sectionId: z.ZodOptional<z.ZodString>;
style: z.ZodOptional<
z.ZodObject<
{
fill: z.ZodOptional<z.ZodString>;
stroke: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>
>;
seating: z.ZodOptional<
z.ZodEnum<{
// … 24 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Label formats shared by seat and row labeling.
const labelFormatSchema: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
"alpha-lower": "alpha-lower";
roman: "roman";
}>@openseat/core/src/schema.ts
const labelSchema: z.ZodObject<
{
type: z.ZodLiteral<"label">;
text: z.ZodString;
fontSize: z.ZodNumber;
style: z.ZodOptional<
z.ZodObject<
{
colour: z.ZodOptional<z.ZodString>;
bold: z.ZodOptional<z.ZodBoolean>;
italic: z.ZodOptional<z.ZodBoolean>;
letterSpacing: z.ZodOptional<z.ZodNumber>;
},
z.core.$strict
>
>;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Hard size limits enforced by the schema. They bound the work validation, rendering and the generators can be made to do by an untrusted document, and sit well above anything a real venue needs (the largest stadiums have about 100,000 seats).
const LAYOUT_LIMITS: {
readonly maxObjects: 250000;
readonly maxPolygonPoints: 5000;
readonly maxTotalShapePoints: 200000;
readonly maxSectionSeats: 20000;
readonly maxGroupSeats: 5000;
readonly maxCategories: 1000;
readonly maxTableCapacity: 500;
readonly maxSeatsPerEdge: 500;
readonly maxMetadataBytes: 65536;
readonly maxMetadataDepth: 16;
readonly maxCoordinate: 10000000;
readonly maxDimension: 1000000;
readonly maxFontSize: 10000;
}@openseat/core/src/limits.ts
const layoutObjectSchema: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
type: z.ZodLiteral<"seat">;
seatId: z.ZodString;
label: z.ZodString;
rowLabel: z.ZodOptional<z.ZodString>;
sectionId: z.ZodOptional<z.ZodString>;
categoryId: z.ZodOptional<z.ZodString>;
shape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
// … 797 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
const layoutSchema: z.ZodObject<
{
schemaVersion: z.ZodLiteral<1>;
id: z.ZodString;
name: z.ZodString;
revision: z.ZodDefault<z.ZodNumber>;
canvas: z.ZodObject<
{
width: z.ZodNumber;
height: z.ZodNumber;
gridSize: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>;
metadata: z.ZodDefault<
z.ZodPipe<z.ZodUnknown, z.ZodRecord<z.ZodString, z.ZodUnknown>>
>;
categories: z.ZodDefault<
z.ZodArray<
z.ZodObject<
{
id: z.ZodString;
name: z.ZodString;
colour: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>
>
// … 874 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Most grid cells one entry (or one query) may touch. Entries that would cover more cells, or whose cell range is not a safe integer range (huge or non-finite coordinates), are kept in an "oversized" list that every query scans linearly. Without this cap a single 1e6 x 1e6 stage, or two seats at x = 1e9 with a 1e-300 radius, would make an insert loop for minutes and exhaust memory.
const MAX_CELLS_PER_ENTRY = 4096@openseat/core/src/spatial-index.ts
Most rows one fill may produce. Matches the block seating row limit.
const MAX_FILL_ROWS = 500@openseat/core/src/generators/section.ts
Most seats one scanline span may produce. Matches the per-row seat limit of block seating.
const MAX_FILL_SEATS_PER_SPAN = 2000@openseat/core/src/generators/section.ts
Largest |labelRotation| of a section or GA zone, in degrees.
const MAX_LABEL_ROTATION = 360@openseat/core/src/limits.ts
Deepest nesting serializeLayout accepts. Valid documents nest far less (metadata is capped at 16).
const MAX_SERIALIZE_DEPTH = 256@openseat/core/src/serialize.ts
Largest zone capacity: a GA zone's capacity and a zone section's capacity.
const MAX_ZONE_CAPACITY = 1000000@openseat/core/src/limits.ts
Smallest size a table or booth label is shrunk to.
const MIN_OBJECT_LABEL_SIZE = 4@openseat/core/src/label-layout.ts
Table and booth labels are drawn at least this size, shrunk to fit the table or booth. A larger table or booth gets a larger label, up to OBJECT_LABEL_SHARE of its shorter side.
const OBJECT_LABEL_SIZE = 12@openseat/core/src/label-layout.ts
const pathSchema: z.ZodObject<
{
type: z.ZodLiteral<"path">;
points: z.ZodArray<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
closed: z.ZodDefault<z.ZodBoolean>;
strokeWidth: z.ZodNumber;
caption: z.ZodOptional<z.ZodString>;
captionSize: z.ZodOptional<z.ZodNumber>;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
const pointSchema: z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>@openseat/core/src/schema.ts
const polygonSchema: z.ZodObject<
{
type: z.ZodLiteral<"polygon">;
points: z.ZodArray<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
categoryId: z.ZodOptional<z.ZodString>;
style: z.ZodOptional<
z.ZodObject<
{
fill: z.ZodOptional<z.ZodString>;
stroke: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>
>;
id: z.ZodString;
name: z.ZodOptional<z.ZodString>;
visible: z.ZodDefault<z.ZodBoolean>;
locked: z.ZodDefault<z.ZodBoolean>;
zIndex: z.ZodDefault<z.ZodNumber>;
position: z.ZodDefault<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
rotation: z.ZodDefault<z.ZodNumber>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Corner radius of a "rounded" zone, as a fraction of its shorter side.
const ROUNDED_ZONE_RADIUS_FRACTION = 0.12@openseat/core/src/zone-ops.ts
Row labels: font size, gap from the end seat's edge, push step (× font size) and most pushes.
const ROW_LABEL_LAYOUT: {
readonly fontSize: 11;
readonly gap: 8;
readonly pushStep: 0.5;
readonly maxPushes: 4;
readonly bold: true;
}@openseat/core/src/label-layout.ts
Largest distance, in layout units, a fitted layout may deviate from the stored seats and still be written.
const ROW_LAYOUT_FIT_TOLERANCE = 0.5@openseat/core/src/row-geometry.ts
Extra space after slot index after (an aisle inside a row).
const rowGapSchema: z.ZodObject<
{
after: z.ZodNumber;
width: z.ZodNumber;
},
z.core.$strict
>@openseat/core/src/schema.ts
How the rows of one section are labelled.
const rowLabelingSchema: z.ZodObject<
{
format: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
"alpha-lower": "alpha-lower";
roman: "roman";
}>;
start: z.ZodNumber;
step: z.ZodNumber;
direction: z.ZodEnum<{
"front-to-back": "front-to-back";
"back-to-front": "back-to-front";
}>;
skip: z.ZodOptional<z.ZodArray<z.ZodString>>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>@openseat/core/src/schema.ts
Where a row's label is drawn. Absent on a row means "start".
const rowLabelPositionSchema: z.ZodEnum<{
start: "start";
none: "none";
end: "end";
both: "both";
}>@openseat/core/src/schema.ts
Parametric description of a row. Seat positions are derived from it by the row geometry helpers; the stored seat positions stay authoritative for rendering and booking.
const rowLayoutSchema: z.ZodObject<
{
seatSpacing: z.ZodNumber;
radius: z.ZodOptional<z.ZodNumber>;
anchor: z.ZodOptional<
z.ZodEnum<{
start: "start";
end: "end";
center: "center";
}>
>;
gaps: z.ZodOptional<
z.ZodArray<
z.ZodObject<
{
after: z.ZodNumber;
width: z.ZodNumber;
},
z.core.$strict
>
>
>;
holes: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
seatRotation: z.ZodOptional<
z.ZodEnum<{
tangent: "tangent";
row: "row";
keep: "keep";
// … 26 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
const rowSchema: z.ZodObject<
{
type: z.ZodLiteral<"row">;
rowLabel: z.ZodString;
sectionId: z.ZodOptional<z.ZodString>;
seatObjectIds: z.ZodArray<z.ZodString>;
layout: z.ZodOptional<
z.ZodObject<
{
seatSpacing: z.ZodNumber;
radius: z.ZodOptional<z.ZodNumber>;
anchor: z.ZodOptional<
z.ZodEnum<{
start: "start";
end: "end";
center: "center";
}>
>;
gaps: z.ZodOptional<
z.ZodArray<
z.ZodObject<
{
after: z.ZodNumber;
width: z.ZodNumber;
},
z.core.$strict
>
>
// … 60 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Plain constants of the layout schema. They are re-exported from schema.ts (and so from the package root) unchanged; they live here, free of zod, so that code needing only a constant (factories, generators, the renderer) does not keep the zod schemas in a bundle that never validates a layout.
const SCHEMA_VERSION = 1@openseat/core/src/limits.ts
Seat tag that represents the reference editor's "marked" flag.
const SEAT_TAG_MARKED = "marked"@openseat/core/src/limits.ts
How the seats of one row are numbered.
const seatLabelingSchema: z.ZodObject<
{
format: z.ZodEnum<{
numeric: "numeric";
alpha: "alpha";
"alpha-lower": "alpha-lower";
roman: "roman";
}>;
start: z.ZodNumber;
step: z.ZodNumber;
direction: z.ZodEnum<{
ltr: "ltr";
rtl: "rtl";
}>;
prefix: z.ZodOptional<z.ZodString>;
suffix: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>@openseat/core/src/schema.ts
const seatSchema: z.ZodObject<
{
type: z.ZodLiteral<"seat">;
seatId: z.ZodString;
label: z.ZodString;
rowLabel: z.ZodOptional<z.ZodString>;
sectionId: z.ZodOptional<z.ZodString>;
categoryId: z.ZodOptional<z.ZodString>;
shape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
// … 30 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
const seatShapeSchema: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>@openseat/core/src/schema.ts
Section titles: default and smallest size, band gap (× font size) and the fit factors.
const SECTION_TITLE_LAYOUT: {
readonly defaultSize: 16;
readonly minSize: 8;
readonly gap: 0.5;
readonly widthFit: 0.95;
readonly pitchFit: 2;
readonly insideShorterFit: 0.45;
readonly insideWidthFit: 0.9;
}@openseat/core/src/label-layout.ts
const sectionSchema: z.ZodObject<
{
type: z.ZodLiteral<"section">;
label: z.ZodString;
categoryId: z.ZodOptional<z.ZodString>;
boundary: z.ZodArray<
z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>
>;
style: z.ZodDefault<
z.ZodObject<
{
fill: z.ZodOptional<z.ZodString>;
stroke: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>
>;
rowOrder: z.ZodOptional<z.ZodArray<z.ZodString>>;
rowLabeling: z.ZodOptional<
z.ZodObject<
{
format: z.ZodEnum<{
// … 270 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Parametric seating of a section, used to regenerate its rows.
const sectionSeatingSchema: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
seatSpacing: z.ZodNumber;
seatShape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"circle">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
seatLabeling: z.ZodObject<
{
format: z.ZodEnum<{
// … 173 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Stage labels: upper-case, letter-spaced, sized to 0.28 × the shorter side within [10, 36], at most 0.8 × the width.
const STAGE_LABEL_LAYOUT: {
readonly sizeFactor: 0.28;
readonly minSize: 10;
readonly maxSize: 36;
readonly widthFit: 0.8;
readonly letterSpacing: 0.14;
}@openseat/core/src/label-layout.ts
const stageSchema: z.ZodObject<
{
type: z.ZodLiteral<"stage">;
label: z.ZodOptional<z.ZodString>;
width: z.ZodNumber;
height: z.ZodNumber;
cornerRadius: z.ZodOptional<z.ZodNumber>;
style: z.ZodOptional<
z.ZodObject<
{
fill: z.ZodOptional<z.ZodString>;
stroke: z.ZodOptional<z.ZodString>;
strokeWidth: z.ZodOptional<z.ZodNumber>;
},
z.core.$strict
>
>;
markings: z.ZodOptional<
z.ZodEnum<{
rink: "rink";
court: "court";
pitch: "pitch";
}>
>;
shape: z.ZodOptional<
z.ZodEnum<{
rect: "rect";
round: "round";
// … 31 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
Parametric seat arrangement around a table.
const tableArrangementSchema: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"round">;
seatDistance: z.ZodNumber;
startAngle: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
seatsPerEdge: z.ZodObject<
{
top: z.ZodNumber;
right: z.ZodNumber;
bottom: z.ZodNumber;
left: z.ZodNumber;
},
z.core.$strict
>;
seatOffset: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>@openseat/core/src/schema.ts
const tableSchema: z.ZodObject<
{
type: z.ZodLiteral<"table">;
label: z.ZodString;
shape: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"round">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>;
capacity: z.ZodNumber;
sectionId: z.ZodOptional<z.ZodString>;
seatObjectIds: z.ZodArray<z.ZodString>;
arrangement: z.ZodOptional<
// … 49 more lines (type truncated)This type is long; its declaration is shortened here.
@openseat/core/src/schema.ts
const tableShapeSchema: z.ZodDiscriminatedUnion<
[
z.ZodObject<
{
kind: z.ZodLiteral<"round">;
radius: z.ZodNumber;
},
z.core.$strict
>,
z.ZodObject<
{
kind: z.ZodLiteral<"rect">;
width: z.ZodNumber;
height: z.ZodNumber;
},
z.core.$strict
>,
],
"kind"
>@openseat/core/src/schema.ts
Default runway depth of a thrust stage, as a fraction of the stage height.
const THRUST_DEPTH_FRACTION = 0.45@openseat/core/src/object-geometry.ts
Default runway width of a thrust stage, as a fraction of the stage width.
const THRUST_WIDTH_FRACTION = 0.4@openseat/core/src/object-geometry.ts
How far a trapezoid zone's short edge is inset on each side, as a fraction of its width.
const TRAPEZOID_INSET_FRACTION = 0.15@openseat/core/src/zone-ops.ts
Outline of the venue itself, drawn beneath every object.
const venueSchema: z.ZodObject<
{
name: z.ZodOptional<z.ZodString>;
showName: z.ZodOptional<z.ZodBoolean>;
shape: z.ZodEnum<{
rect: "rect";
ellipse: "ellipse";
}>;
position: z.ZodObject<
{
x: z.ZodNumber;
y: z.ZodNumber;
},
z.core.$strict
>;
width: z.ZodNumber;
height: z.ZodNumber;
cornerRadius: z.ZodOptional<z.ZodNumber>;
style: z.ZodOptional<
z.ZodObject<
{
fill: z.ZodOptional<z.ZodString>;
stroke: z.ZodOptional<z.ZodString>;
},
z.core.$strict
>
>;
},
z.core.$strict
>@openseat/core/src/schema.ts