/** * Hex Cluster browser API, experimental v1: the types of `window.hexAPI`. * https://academy.onethousanddrones.com/hex/api * * Generated from the public contract; do not edit. Copyright One Thousand * Drones, LLC. Use is governed by the terms at the address above. */ /** An axial cell (q, r) and its storey (level, 0 at the ground). */ export interface Cell { readonly q: number; readonly r: number; readonly level: number; } /** A cell with no storey, as edges and junctions name them. */ export interface Site { readonly q: number; readonly r: number; } /** A face of a cell. */ export type Face = 'top' | 'bottom'; /** A cell shape: full, a half or a quarter. */ export type Variant = 'full' | 'half-n' | 'half-s' | 'half-e' | 'half-w' | 'quarter-nw' | 'quarter-ne' | 'quarter-sw' | 'quarter-se'; /** The kind of a node. Other families may appear while the API is experimental. */ export type NodeFamily = 'piece' | 'snap' | 'fused' | 'spike' | 'cover' | 'cap' | 'insert' | 'handle' | 'accessory' | 'port' | 'bin' | 'binBolt' | 'connector' | 'run' | 'riser' | 'coverHub' | 'footprint' | 'drawer' | 'jointBolt' | 'notch' | 'bay' | (string & {}); /** An opaque handle for one placed part: read it, pass it back, never build one. */ export type NodeRef = string; /** An opaque handle for one placed prop: read it, pass it back, never build one. It changes when the prop does (moved, or a pot filled or planted), and the outcome names the new one. */ export type PropRef = string; /** One placed part. */ export interface HexNode { readonly ref: NodeRef; readonly family: NodeFamily; /** null for parts that span cells (runs). */ readonly at: Cell | null; /** The part name, where the node names one. */ readonly stem: string | null; /** Opaque: not part of the contract. */ readonly detail: Readonly>; } /** A build of bases and snaps only, for load(). */ export interface StarterState { readonly pieces?: readonly (Cell & { readonly variant: Variant })[]; readonly snaps?: readonly (Cell & { readonly variant: Variant })[]; } /** One part a script can name, as listParts() gives it. */ export interface PartInfo { /** The value the op takes in field. */ readonly value: string; /** The op that places it. */ readonly op: string; readonly field: string; readonly family: NodeFamily; /** The published part it prints as: the name the pack uses. */ readonly part: string; readonly name: string; /** What it is for; null where none is written. */ readonly use: string | null; /** The printed part's size in the pose it ships in, mm; null if unmeasured. */ readonly box: { readonly x: number; readonly y: number; readonly z: number } | null; /** False for a part outside this release: it places, warns UNRELEASED_PART and is not in the pack. */ readonly released: boolean; } /** One prop of the library, as listProps() gives it. */ export interface PropInfo { readonly id: string; readonly label: string; readonly group: string; /** Longest side, mm. */ readonly longMm: number; /** A plug plant: it goes in soil. */ readonly plant: boolean; /** A pot: it takes soil and a plant. */ readonly pot: boolean; /** What it is (leafy, herbs, pots, tools, ...). Ids that share a label are one crop at different stages. */ readonly kind: string; /** A plant growth stage; null for anything that is not a plant. */ readonly stage: 'seedling' | 'young' | 'harvest' | null; } /** The props on the build and the soil in its parts: staging, not parts, so not in nodes or the BOM. Positions are mm, y up. */ export interface Staging { readonly props: readonly { readonly ref: PropRef; readonly id: string; /** null for an id the library does not know. */ readonly label: string | null; readonly group: string | null; readonly posMm: readonly [number, number, number]; readonly quat: readonly [number, number, number, number]; /** The yaw alone, degrees. */ readonly yawDeg: number; readonly pot?: { readonly soil: boolean; readonly plant: string | null }; }[]; readonly soil: readonly { readonly part: NodeRef; readonly to: 'rim' | 'dividers' }[]; } /** What getState() returns. */ export interface HexState { /** Opaque beyond pieces and snaps: use nodes. */ readonly state: { readonly pieces: readonly (Cell & { readonly variant: Variant })[]; readonly snaps: readonly (Cell & { readonly variant: Variant })[]; }; readonly nodes: readonly HexNode[]; /** Props and soil: staging, not parts. */ readonly staging: Staging; } /** One change to the build. The op field names it. */ export type HexOp = | { readonly op: 'place'; readonly at: Cell; readonly variant: Variant; readonly storeys?: 1 | 2 | 3; } | { readonly op: 'remove'; readonly ref: NodeRef; readonly sweepOrphans?: boolean; } | { readonly op: 'rotate'; readonly ref: NodeRef; readonly stepDeg: number; } | { readonly op: 'addSnap'; readonly at: Cell; readonly variant: Variant; } | { readonly op: 'addBin'; readonly at: Cell; readonly stem: string; } | { readonly op: 'addSpike'; readonly level: number; readonly junction: readonly [Site, Site, Site]; readonly variant?: string; readonly facing?: 'up' | 'down'; } | { readonly op: 'setSpikeAccessory'; readonly level: number; readonly junction: readonly [Site, Site, Site]; readonly accessory: string | null; } | { readonly op: 'addCover'; readonly at: Cell; readonly variant: Variant; readonly face: Face; readonly type?: string; } | { readonly op: 'setCoverType'; readonly at: Cell; readonly variant: Variant; readonly face: Face; readonly type: string; } | { readonly op: 'setCoverMagnets'; readonly at: Cell; readonly variant: Variant; readonly face: Face; readonly on: boolean; } | { readonly op: 'addCap'; readonly at: Cell; readonly face: '*' | '#'; readonly bearingDeg: number; readonly capVariant?: string; readonly storeys?: 1 | 2; } | { readonly op: 'addInsert'; readonly at: Cell; readonly variant: Variant; readonly face: Face; readonly bearingDeg: number; readonly stem: string; readonly chain?: boolean; } | { readonly op: 'addHandle'; readonly at: Cell; readonly face: Face; readonly chain?: boolean; } | { readonly op: 'addCoverHub'; readonly at: Cell; readonly variant: Variant; readonly face: Face; } | { readonly op: 'addConnector'; readonly level: number; readonly edge: readonly [Site, Site]; readonly kind: string; } | { readonly op: 'addJointBolt'; readonly at: Cell; readonly slot: number | string; } | { readonly op: 'addAccessory'; readonly at: Cell; readonly slot: number | Face; readonly stem: string; } | { readonly op: 'addPort'; readonly at: Cell; readonly slot: number; readonly stem: string; } | { readonly op: 'addBinBolt'; readonly at: Cell; readonly slot: number | 'cut'; readonly stem: string; } | { readonly op: 'addNotch'; readonly at: Cell; readonly k: number; readonly stem: string; } | { readonly op: 'setNotchOpen'; readonly at: Cell; readonly k: number; readonly open: boolean; } | { readonly op: 'addDrawer'; readonly at: Cell; readonly key: string; readonly style?: string; readonly storeys?: 1 | 2 | 3; } | { readonly op: 'setDrawerOpen'; readonly at: Cell; readonly open: boolean; readonly west?: boolean; } | { readonly op: 'addRun'; readonly level: number; readonly start: Site; readonly entryEdge: number; readonly through: readonly Site[]; readonly mode?: string; } | { readonly op: 'setRunMode'; readonly id: string; readonly mode: string; } | { readonly op: 'addRiser'; readonly at: Cell; } | { readonly op: 'setFootBolt'; readonly mm: number; } | { readonly op: 'removeProp'; readonly ref: PropRef; } | { readonly op: 'fillPot'; readonly ref: PropRef; } | { readonly op: 'emptyPot'; readonly ref: PropRef; } | { readonly op: 'plant'; readonly id: string; readonly in: PropRef | NodeRef; readonly atMm?: { readonly x: number; readonly z: number }; } | { readonly op: 'addProp'; readonly id: string; readonly on?: NodeRef | PropRef | 'bench'; readonly atMm?: { readonly x: number; readonly z: number }; readonly yawDeg?: number; readonly pose?: 'rest'; readonly drop?: boolean; } | { readonly op: 'moveProp'; readonly ref: PropRef; readonly on?: NodeRef | PropRef | 'bench'; readonly atMm?: { readonly x: number; readonly z: number }; readonly yawDeg?: number; readonly pose?: 'rest'; readonly drop?: boolean; } | { readonly op: 'fillSoil'; readonly part: NodeRef; readonly to?: 'rim' | 'dividers'; } | { readonly op: 'emptySoil'; readonly part: NodeRef; }; /** What openSockets() looks for. */ export type PlaceableFamily = 'piece' | 'snap' | 'spike' | 'cover' | 'cap' | 'insert' | 'handle' | 'accessory' | 'port' | 'bin' | 'binBolt' | 'connector' | 'run' | 'riser' | 'coverHub' | 'drawer' | 'jointBolt' | 'notch' | 'bay'; export interface SocketQuery { readonly family: PlaceableFamily; readonly level?: number; readonly variant?: Variant; readonly stem?: string; readonly face?: Face; readonly type?: string; readonly facing?: 'up' | 'down'; readonly capVariant?: string; readonly storeys?: 1 | 2 | 3; readonly key?: string; } /** One place a part fits now. */ export interface OpenSocket { readonly family: NodeFamily; readonly at: Cell; /** Face, bearing, slot or edge, as the op takes it; null when the cell is the whole answer. */ readonly slot: string | null; /** The exact op that puts the part here. */ readonly op: HexOp; } /** Why an op was refused. Branch on it; new codes may appear while the API is experimental. */ export type RefusalCode = | 'ALREADY_FITTED' | 'BIN_IN_WAY' | 'BIN_RIM_IN_JOINT' | 'BOLT_ON_BELOW' | 'BORE_COVERED' | 'BUSY' | 'CAP_ON_FLAT' | 'CELL_EMPTY' | 'COLLAR_IN_WAY' | 'CUT_MISMATCH' | 'DECODE_FAILED' | 'FACE_NOT_FREE' | 'FLAT_BLOCKED' | 'FLAT_TAKEN' | 'FOOTPRINT_IN_WAY' | 'FOOTPRINT_MISMATCH' | 'GENDER_MISMATCH' | 'HALF_ALREADY_BINNED' | 'HAND_MISMATCH' | 'HOST_MISMATCH' | 'INTERRUPTED' | 'INVALID_INPUT' | 'INVALID_PART_ID' | 'KIND_MISMATCH_BELOW' | 'LID_IN_WAY' | 'LID_NOT_BOLT_TYPE' | 'LINK_TOO_BIG' | 'LOAD_ABOVE' | 'MESH_CLASH' | 'NEEDS_CONFIGURATOR' | 'NEEDS_TALL_BASE' | 'NODE_NOT_FOUND' | 'NOT_ADJACENT' | 'NOT_A_DRAWER' | 'NOT_A_PLANT' | 'NOT_A_POT' | 'NOT_A_SITE_PART' | 'NOT_FULL_HEX' | 'NO_BOLT_RECT' | 'NO_BORE' | 'NO_CELL' | 'NO_CELL_ACROSS' | 'NO_CHANGE' | 'NO_COLLAR_POSSIBLE' | 'NO_SOIL' | 'OUT_OF_BOUNDS' | 'OVERLAPS_NEIGHBOUR' | 'PART_WITHHELD' | 'PIPE_ON_FLAT' | 'PIPE_THROUGH_CELL' | 'POT_EMPTY' | 'POT_FULL' | 'PROP_EXISTS' | 'PROP_NOT_FOUND' | 'PROP_NO_REST' | 'PROP_UNKNOWN' | 'PROUD_PORT' | 'ROUTE_AXIS_ABSENT' | 'ROUTE_BIN_IN_WAY' | 'ROUTE_CELL_OCCUPIED' | 'ROUTE_COVER_HUB_CLEARANCE' | 'ROUTE_COVER_HUB_MOUNT' | 'ROUTE_COVER_HUB_PROVISIONAL' | 'ROUTE_FLAT_BOLTED_OVER' | 'ROUTE_FLEX_BEND' | 'ROUTE_MODE_CLASH' | 'ROUTE_NOT_A_BOUNDARY' | 'ROUTE_NO_PATH' | 'ROUTE_NO_RISER' | 'ROUTE_PROVISIONAL_FITTING' | 'ROUTE_RISER_UNDER_COVER' | 'ROUTE_SOCKET_CROWDED' | 'ROUTE_SOCKET_GONE' | 'ROUTE_TOO_MANY_ARMS' | 'RUN_OWNS_CONNECTOR' | 'SEAM_FLAT' | 'SITE_FILLED' | 'SLOT_MISMATCH' | 'SOCKET_OCCUPIED' | 'SOCKET_ON_LID' | 'SOIL_NOT_FILLABLE' | 'SOIL_NO_BED' | 'SOIL_NO_DIVIDERS' | 'SPIKE_CLASH' | 'STACK_ORDER' | 'STOREY_LIMIT' | 'TALL_FACE_MISMATCH' | 'TIMED_OUT' | 'TOO_FEW_CELLS' | 'TRIANGLE_OPEN' | 'TYPE_NOT_ALLOWED' | 'UNSUPPORTED' | 'WEDGED_LID' | 'WOULD_BREAK_RUN' | (string & {}); /** What validate() found. */ export type IssueCode = | 'UNSUPPORTED' | 'ORPHAN' | 'STRANDED' | 'LOOSE_BODY' | 'OVER_PACK_CAP' | 'UNRELEASED_PART' | 'OPEN_CAP_FACE' | 'MESH_CLASH_KEPT' | 'MESH_NOT_RUN' | 'PROPS_NOT_DRAWN' | `ROUTE_${string}` | `COVER_HUB_${string}` | (string & {}); /** One finding. */ export interface Issue { readonly severity: 'error' | 'warning' | 'info'; readonly code: IssueCode; readonly refs: readonly NodeRef[]; /** For people; may change. */ readonly message: string; } /** The collision gate's verdict: clear, not run, or the two parts that collide and by how much (mm). */ export type MeshClash = | 'not-run' | 'clear' | { readonly a: string; readonly b: string; readonly depthMm: number; readonly limitMm: number }; /** What validate() returns. */ export interface ValidationReport { /** No error issue. Fit only: not strength. Does not include meshClash: read it too. */ readonly ok: boolean; readonly issues: readonly Issue[]; readonly meshClash: MeshClash; } /** The answer to every op: what changed, or which rule said no. */ export type MutationOutcome = | { readonly ok: true; /** The op you sent. */ readonly op: HexOp | { readonly op: string }; readonly added: readonly (NodeRef | PropRef)[]; readonly removed: readonly (NodeRef | PropRef)[]; /** Issues this op newly caused. */ readonly warnings: readonly Issue[]; } | { readonly ok: false; readonly op: HexOp | { readonly op: string }; readonly code: RefusalCode; /** For people; may change. */ readonly message: string; /** Extra context; shape not guaranteed. */ readonly details: Readonly>; /** Up to 12 nearby places the part does fit. */ readonly validAlternatives: readonly OpenSocket[]; }; /** How applyAll() treats a refusal. */ export interface ApplyAllOptions { /** Take back every op of the batch if any is refused, leaving no undo step. Default true. */ readonly atomic?: boolean; /** Stop at the first refusal. Default true. */ readonly stopOnRefusal?: boolean; /** Shorthand for { atomic: false, stopOnRefusal: false }. */ readonly keepGoing?: boolean; } /** What applyAll() returns. */ export interface BatchResult { readonly outcomes: readonly MutationOutcome[]; /** True when a refusal undid the ops before it. */ readonly rolledBack: boolean; } /** One line of the parts list. */ export interface BomLine { /** The published part for printed parts (as listParts() part); a descriptive key otherwise. */ readonly key: string; readonly qty: number; readonly kind: 'printed' | 'stock' | 'unmodelled'; /** For people: a printed part's name, as listParts() gives it. */ readonly label: string; } /** What bom() returns. */ export interface BomSummary { readonly lines: readonly BomLine[]; /** Printed parts one download would pack. */ readonly printedInstances: number; /** The most one download takes. Not a limit on the build. */ readonly maxPackInstances: number; /** For people. */ readonly notes: readonly string[]; readonly cuts: { /** The pipe cut list, for people. */ readonly lines: readonly string[]; }; /** The download link for the printed parts. */ readonly pack: PackLink; } /** The print pack bom() links to. */ export interface PackOptions { /** 3MF plates (default) or loose STL files. */ readonly format?: '3mf' | 'stl'; /** The bed, whole mm, 220 to 1000 each (default 220 x 220). */ readonly bed?: { readonly x: number; readonly y: number }; /** The bed's height, whole mm, 50 to 1000 (default 250). */ readonly bedZ?: number; } /** The print pack for a build: the link, the bed and format it is for, or why there is none. */ export interface PackLink { /** The download link, or null with why. Opening it downloads the files. */ readonly url: string | null; readonly format: '3mf' | 'stl'; readonly bed: { readonly x: number; readonly y: number }; readonly bedZ: number; /** Why there is no link: no printed parts, over the limit, or options it cannot use. */ readonly why: string | null; } /** `window.hexAPI`, experimental v1. Members marked "changes the build" edit the build on screen. */ export interface HexAPI { /** The API version: 1 while the API is experimental v1. */ readonly version: 1; /** The build: every placed part as a node (and the raw state, which is opaque). */ getState(): HexState; /** The props library: every prop a build can stage, and which are plants and which are pots. */ listProps(): readonly PropInfo[]; /** Every part a script can name: the value, the op and field that take it, its name, what it is for and its printed size. */ listParts(): readonly PartInfo[]; /** Every place one kind of part fits now, each with the exact op that puts it there. */ openSockets(query: SocketQuery): readonly OpenSocket[]; /** Waits for the collision gate, then reports every issue with the build on screen. */ validate(): Promise; /** The parts list, the printed-part count against the download cap, notes, and the print pack link for a bed and format. */ bom(options?: PackOptions): BomSummary; /** The build as a share-link fragment (v2s=…), without the leading #. */ toUrl(): Promise; /** Apply one op through the same path a click takes; resolves to its outcome. Changes the build. */ apply(op: HexOp): Promise; /** Apply ops in order; atomic and stop-on-refusal by default. The ops it applies are one undo step. Changes the build. */ applyAll(ops: readonly HexOp[], options?: ApplyAllOptions): Promise; /** Replace the build with a share link, or with a starter state of bases. Changes the build. */ load(input: string | StarterState): Promise<{ ok: true } | { ok: false; code: 'DECODE_FAILED' | 'LINK_TOO_BIG'; message: string }>; /** Undo the last change, as Ctrl+Z does. False when there is nothing to undo. Changes the build. */ undo(): boolean; /** Redo, as Ctrl+Shift+Z does. False when there is nothing to redo. Changes the build. */ redo(): boolean; /** Resolves when every model is drawn and the collision gate is idle. */ settle(): Promise; } declare global { interface Window { /** Loads the API (a lazy chunk) and binds window.hexAPI. Not registered inside a frame. */ __loadHexAPI?: () => Promise; hexAPI?: HexAPI; } }