lineadraw 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lineadraw.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // GENERATED — do not edit. Typings for the virtual "lineadraw" and
2
2
  // "lineadraw/helpers" modules block scripts import from, emitted from
3
- // packages/core/src/extensions/{scriptTypes,scriptHelpers}.ts. Regenerate with:
3
+ // packages/core/src/extensions/{scriptTypes,scriptHelpers,scriptBooleans}.ts.
4
+ // Regenerate with:
4
5
  // npm run gen:lineadraw-dts -w @lineadraw/core
5
6
 
6
7
  declare module "lineadraw" {
@@ -204,9 +205,12 @@ declare module "lineadraw" {
204
205
  /** Applied last, about the pivot (the first input point). */
205
206
  rotation?: number;
206
207
  scale?: number;
207
- params?: Record<string, number | string | boolean | readonly number[] | readonly string[]>;
208
- /** Input points in the PARENT's local coords; the first is the pivot. */
209
- inputs?: readonly Vec2Like[];
208
+ params?: Record<string, number | string | boolean | readonly number[] | readonly string[] | JsonValue>;
209
+ /** Input points in the PARENT's local coords (the first is the pivot):
210
+ * in pick order, or by input name for a block declaring an `inputs`
211
+ * table. Object ids (a `host`, picked objects) only on instances a
212
+ * script ADDS to the document — a block's own children can't reference. */
213
+ inputs?: readonly Vec2Like[] | Readonly<Record<string, Vec2Like | readonly Vec2Like[] | string | readonly string[]>>;
210
214
  }>;
211
215
  /** A table cell: plain text, or text with merge / alignment / style. */
212
216
  export type TableCellInput = string | {
@@ -302,7 +306,7 @@ declare module "lineadraw" {
302
306
  modelCenter?: Vec2Like;
303
307
  /** Drawing-scale denominator (model mm per paper mm); 1:50 => 50. */
304
308
  scale?: number;
305
- /** Pinned: no pan/zoom-through, no move/resize grips. */
309
+ /** Pinned: no pan/zoom-through, no move/resize handles. */
306
310
  locked?: boolean;
307
311
  /** Layer ids hidden in this viewport only. */
308
312
  hiddenLayerIds?: readonly string[];
@@ -448,9 +452,10 @@ declare module "lineadraw" {
448
452
  definitionId: string;
449
453
  rotation: number;
450
454
  scale: number;
451
- params: Readonly<Record<string, number | string | boolean | readonly number[] | readonly string[]>>;
452
- /** World points and/or referenced object ids, in pick order. */
453
- inputs: readonly (Vec2 | string)[];
455
+ params: Readonly<Record<string, number | string | boolean | readonly number[] | readonly string[] | JsonValue>>;
456
+ /** World points and/or referenced object ids: in pick order, or by
457
+ * input name for a block declaring an `inputs` table. */
458
+ inputs: readonly (Vec2 | string)[] | Readonly<Record<string, Vec2 | string | readonly Vec2[] | readonly string[]>>;
454
459
  }>;
455
460
  /** Paper-space window into model space; exists only in layout spaces. */
456
461
  export type Viewport = ObjectShape<{
@@ -641,6 +646,12 @@ declare module "lineadraw" {
641
646
  * value: it never appears in the `params` record `draw` receives.
642
647
  * `name` stays the unique key (paramVisibility can hide it by name). */
643
648
  | (ParamBase & {
649
+ /** Structured data the block owns (any JSON value) — no panel row;
650
+ * edited through handles, handle menus and actions. Typed by its
651
+ * default: `default: {} as Record<string, Wall>`. */
652
+ type: "data";
653
+ default: JsonValue;
654
+ }) | (ParamBase & {
644
655
  type: "label";
645
656
  })
646
657
  /** Layout only: 2–3 compact params on one panel row, each item's label a
@@ -711,12 +722,147 @@ declare module "lineadraw" {
711
722
  * declares (or a block's function form returns).
712
723
  */
713
724
  export type ParamDefs = readonly ParamDef[];
725
+ /**
726
+ * One declared input — a row of the `inputs` table a block or command
727
+ * spec declares. Inputs are read by `name` (`inputs.<name>`); `label` is
728
+ * the pick prompt. `types` limits object picks to those object types
729
+ * (empty / absent = any). Optional inputs are skipped with Enter.
730
+ */
731
+ export type InputDef = {
732
+ name: string;
733
+ label?: string;
734
+ description?: string;
735
+ type: "point";
736
+ optional?: boolean;
737
+ } | {
738
+ name: string;
739
+ label?: string;
740
+ description?: string;
741
+ type: "points";
742
+ /** Default 1. */
743
+ min?: number;
744
+ max?: number;
745
+ /** A polygon (closing segment previewed) rather than an open chain. */
746
+ closed?: boolean;
747
+ } | {
748
+ name: string;
749
+ label?: string;
750
+ description?: string;
751
+ type: "object";
752
+ types?: readonly ModelObject["type"][];
753
+ optional?: boolean;
754
+ } | {
755
+ name: string;
756
+ label?: string;
757
+ description?: string;
758
+ type: "objects";
759
+ types?: readonly ModelObject["type"][];
760
+ /** Default 1. */
761
+ min?: number;
762
+ max?: number;
763
+ } | {
764
+ name: string;
765
+ label?: string;
766
+ description?: string;
767
+ /** The block instance this one LIVES ON (a door on a floor plan): it
768
+ * draws in the host's frame and reads the host's `expose` as `host`;
769
+ * the host's `draw` sees it in `hosted`. Picked by clicking the host;
770
+ * `attach` turns the click into params. */
771
+ type: "host";
772
+ /** Accepted host DEFINITION ids (empty = any block). */
773
+ types?: readonly string[];
774
+ };
775
+ /** A custom handle's marker shape (cosmetic: triangle / arrow = "pull this
776
+ * way", diamond = "options here", plus / minus = "add / remove"). */
777
+ export type HandleShape = "square" | "circle" | "diamond" | "triangle" | "arrow" | "double-arrow" | "plus" | "minus";
778
+ /** Icons a button handle may show (lucide glyphs). */
779
+ export type HandleIcon = "plus" | "minus" | "x" | "check" | "flip-horizontal" | "flip-vertical" | "rotate-cw" | "rotate-ccw" | "refresh-cw" | "arrow-left-right" | "arrow-up-down" | "settings-2" | "ellipsis";
780
+ /** An inputs table. */
781
+ export type InputDefs = readonly InputDef[];
782
+ /** A command's inputs table: a `host` is a block-only input. */
783
+ export type CommandInputDefs = readonly Exclude<InputDef, {
784
+ type: "host";
785
+ }>[];
786
+ /** The object type an object input's `types` filter admits. */
787
+ type PickedObject<D> = D extends {
788
+ types: readonly (infer T)[];
789
+ } ? [T] extends [never] ? ModelObject : Extract<ModelObject, {
790
+ type: T;
791
+ }> : ModelObject;
792
+ type DrawInputValue<D> = D extends {
793
+ type: "point";
794
+ } ? D extends {
795
+ optional: true;
796
+ } ? Vec2 | undefined : Vec2 : D extends {
797
+ type: "points";
798
+ } ? readonly Vec2[] : D extends {
799
+ type: "object";
800
+ } ? PickedObject<D> | null : readonly PickedObject<D>[];
801
+ /**
802
+ * What a block's `draw` receives as `inputs` for a table: points
803
+ * pivot-localized, object inputs as the referenced object (pivot-local) or
804
+ * null once deleted, lists as arrays (deleted objects dropped).
805
+ */
806
+ export type DrawInputs<I extends InputDefs> = {
807
+ readonly [D in I[number] as D extends {
808
+ type: "host";
809
+ } ? never : D["name"]]: DrawInputValue<D>;
810
+ };
811
+ type StoredInputValue<D> = D extends {
812
+ type: "host";
813
+ } ? string : D extends {
814
+ type: "point";
815
+ } ? Vec2Like : D extends {
816
+ type: "points";
817
+ } ? readonly Vec2Like[] : D extends {
818
+ type: "object";
819
+ } ? string : readonly string[];
820
+ /**
821
+ * Inputs as STORED for a table (what `place` returns and `previewInputs`
822
+ * holds): points in world coords ([x, y] accepted), objects as ids.
823
+ * Optional inputs may be left out.
824
+ */
825
+ export type StoredInputs<I extends InputDefs> = {
826
+ readonly [D in I[number] as D extends {
827
+ optional: true;
828
+ } ? never : D["name"]]: StoredInputValue<D>;
829
+ } & {
830
+ readonly [D in I[number] as D extends {
831
+ optional: true;
832
+ } ? D["name"] : never]?: StoredInputValue<D>;
833
+ };
834
+ type CommandInputValue<D> = D extends {
835
+ type: "point";
836
+ } ? D extends {
837
+ optional: true;
838
+ } ? Vec2 | undefined : Vec2 : D extends {
839
+ type: "points";
840
+ } ? readonly Vec2[] : D extends {
841
+ type: "object";
842
+ } ? D extends {
843
+ optional: true;
844
+ } ? string | undefined : string : readonly string[];
845
+ /**
846
+ * What a command's `ctx.inputs` is: the points in label order for a
847
+ * labels array, a record by name for a table (objects as ids — read them
848
+ * with `document.getObject`).
849
+ */
850
+ export type CommandInputs<I> = I extends readonly string[] ? readonly Vec2[] : I extends CommandInputDefs ? {
851
+ readonly [D in I[number] as D["name"]]: CommandInputValue<D>;
852
+ } : readonly Vec2[];
714
853
  /** The value an enum option denotes (its string, or `value` of the record form). */
715
854
  type OptionValue<O extends ParamOption> = O extends string ? O : O extends {
716
855
  value: infer V extends string;
717
856
  } ? V : never;
857
+ /** Any JSON value — what a `data` param holds. */
858
+ export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
859
+ readonly [key: string]: JsonValue;
860
+ };
718
861
  /** The runtime value type of one `params` table entry. */
719
862
  type ParamValue<Q extends ParamDef> = Q extends {
863
+ type: "data";
864
+ default: infer D;
865
+ } ? D : Q extends {
720
866
  type: "enum";
721
867
  } ? OptionValue<Q["options"][number]> : Q extends {
722
868
  type: "number";
@@ -889,6 +1035,9 @@ declare module "lineadraw" {
889
1035
  authors?: readonly string[];
890
1036
  tags?: readonly string[];
891
1037
  dependencies?: readonly string[];
1038
+ /** The declared inputs table, when the block has one (instances then
1039
+ * store their inputs by name). */
1040
+ inputs?: InputDefs;
892
1041
  };
893
1042
  /** A command module's identity/metadata (from its exports). */
894
1043
  export type CommandInfo = {
@@ -898,8 +1047,11 @@ declare module "lineadraw" {
898
1047
  version?: string;
899
1048
  authors?: readonly string[];
900
1049
  tags?: readonly string[];
901
- /** Declared point inputs (labels), picked or supplied before `run`. */
902
- inputs?: readonly string[];
1050
+ /** Declared inputs, picked or supplied before `run`: point labels
1051
+ * (positional) or an inputs table (by name). */
1052
+ inputs?: readonly string[] | CommandInputDefs;
1053
+ /** Asks during the run, so headless runners refuse it. */
1054
+ requiresEditor?: boolean;
903
1055
  /** Declared parameters (same table as a block's), prompted or supplied
904
1056
  * before `run`. */
905
1057
  params?: readonly ParamDef[];
@@ -1271,10 +1423,43 @@ declare module "lineadraw" {
1271
1423
  label?: string;
1272
1424
  referencePoint?: Vec2Like;
1273
1425
  }): Promise<Vec2>;
1274
- /** Await one object click; resolves the object's id. */
1426
+ /** Await one object click; resolves the object's id. `types` limits
1427
+ * what can be picked (empty / absent = any). */
1275
1428
  pickObject(opts?: {
1276
1429
  label?: string;
1430
+ types?: readonly ModelObject["type"][];
1277
1431
  }): Promise<string>;
1432
+ /** Await a chain of snapped points (rubber band from the previous);
1433
+ * Enter / Esc finishes once `min` (default 1) is met, `max` ends it,
1434
+ * `closed` previews the closing segment. Rejects on cancel below `min`. */
1435
+ pickPoints(opts?: {
1436
+ label?: string;
1437
+ min?: number;
1438
+ max?: number;
1439
+ closed?: boolean;
1440
+ referencePoint?: Vec2Like;
1441
+ }): Promise<Vec2[]>;
1442
+ /** Await several object picks (each once); Enter / Esc finishes once
1443
+ * `min` (default 1) is met. Rejects on cancel below `min`. */
1444
+ pickObjects(opts?: {
1445
+ label?: string;
1446
+ types?: readonly ModelObject["type"][];
1447
+ min?: number;
1448
+ max?: number;
1449
+ }): Promise<string[]>;
1450
+ /**
1451
+ * Ask several values in ONE dialog — the same form a command's `params`
1452
+ * table shows, from a params table (number / string / boolean / enum …):
1453
+ * const { count, label } = await editor.ask(
1454
+ * [{ name: "count", type: "number", default: 3 },
1455
+ * { name: "label", type: "string", default: "A" }],
1456
+ * { title: "Array" });
1457
+ * Resolves the values typed by the table; rejects on cancel.
1458
+ */
1459
+ ask<const P extends ParamDefs>(params: P, opts?: {
1460
+ title?: string;
1461
+ description?: string;
1462
+ }): Promise<ParamValues<P>>;
1278
1463
  /** Await a typed value from a small input dialog. */
1279
1464
  prompt(message: string, opts?: {
1280
1465
  initial?: string;
@@ -1286,20 +1471,21 @@ declare module "lineadraw" {
1286
1471
  /** Trigger a command by id — like the user pressing its hotkey. A macro
1287
1472
  * takes its declared `inputs` / `params` up front (params passed skip its
1288
1473
  * params dialog). */
1289
- runCommand(id: string, inputs?: readonly Vec2Like[], params?: Readonly<Record<string, unknown>>): Promise<void>;
1474
+ runCommand(id: string, inputs?: readonly Vec2Like[] | Readonly<Record<string, Vec2Like | readonly Vec2Like[] | string | readonly string[]>>, params?: Readonly<Record<string, unknown>>): Promise<void>;
1290
1475
  }
1291
1476
  /**
1292
1477
  * What `run` receives. `document` is the Drawing; `editor` the interactive
1293
- * surface when there is one; `inputs` the points the command's declared
1294
- * `inputs` labels produced (picked in the editor, or supplied by the
1295
- * caller), in label order; `params` the values of the declared `params`
1296
- * table (asked for in a dialog in the editor, or passed by the caller;
1297
- * omitted ones take their defaults).
1478
+ * surface when there is one; `inputs` what the command's declared `inputs`
1479
+ * produced (picked in the editor — object inputs take the selection first
1480
+ * — or supplied by the caller): the points in label order for labels, a
1481
+ * record by name for a table (see CommandInputs); `params` the values of
1482
+ * the declared `params` table (asked for in a dialog in the editor, or
1483
+ * passed by the caller; omitted ones take their defaults).
1298
1484
  */
1299
- export interface CommandContext<P = Readonly<Record<string, unknown>>> {
1485
+ export interface CommandContext<P = Readonly<Record<string, unknown>>, I = readonly Vec2[]> {
1300
1486
  document: Drawing;
1301
1487
  editor?: EditorSurface;
1302
- inputs: readonly Vec2[];
1488
+ inputs: I;
1303
1489
  params: P;
1304
1490
  }
1305
1491
 
@@ -1371,9 +1557,164 @@ declare module "lineadraw" {
1371
1557
  export interface PlaceProps<P = BlockParams> {
1372
1558
  params: ParamValues<P>;
1373
1559
  pickPoint: (label?: string) => Promise<Vec2>;
1374
- pickObject: (label?: string) => Promise<string>;
1560
+ pickObject: (
1561
+ label?: string,
1562
+ opts?: { types?: readonly ModelObject["type"][] },
1563
+ ) => Promise<string>;
1564
+ /** A chain of points; Enter / Esc finishes once `min` (default 1) is met. */
1565
+ pickPoints: (
1566
+ label?: string,
1567
+ opts?: { min?: number; max?: number; closed?: boolean },
1568
+ ) => Promise<Vec2[]>;
1569
+ /** Several objects; Enter / Esc finishes once `min` (default 1) is met. */
1570
+ pickObjects: (
1571
+ label?: string,
1572
+ opts?: {
1573
+ types?: readonly ModelObject["type"][];
1574
+ min?: number;
1575
+ max?: number;
1576
+ },
1577
+ ) => Promise<string[]>;
1578
+ }
1579
+
1580
+ // ——— Custom handles ———
1581
+
1582
+ /** The names of a params table's NUMBER parameters. */
1583
+ export type NumberParamName<P> = {
1584
+ [K in keyof ParamValues<P>]: ParamValues<P>[K] extends number ? K : never;
1585
+ }[keyof ParamValues<P>] &
1586
+ string;
1587
+
1588
+ /**
1589
+ * What a handle's drag / click / action changes: params by name (numbers
1590
+ * are clamped to the param's min / max), and — for a block with an
1591
+ * `inputs` table — inputs by name in block-LOCAL coordinates.
1592
+ */
1593
+ export interface HandlePatch<P = BlockParams, S = never> {
1594
+ params?: Partial<ParamValues<P>>;
1595
+ inputs?: S;
1596
+ }
1597
+
1598
+ /** What a `drag` function receives — the values at drag START (not
1599
+ * compounding) plus where the cursor is, already constrained. */
1600
+ export interface HandleDragEvent<P = BlockParams, D = unknown> {
1601
+ params: ParamValues<P>;
1602
+ inputs: D;
1603
+ /** The cursor, block-local, after the handle's constraint. */
1604
+ point: Vec2;
1605
+ /** The handle's position when the drag started. */
1606
+ start: Vec2;
1607
+ /** `along`: signed distance from its origin; `around`: the angle
1608
+ * (radians); free: undefined. */
1609
+ value: number | undefined;
1375
1610
  }
1376
1611
 
1612
+ /**
1613
+ * One custom handle on a selected instance, in block-LOCAL coordinates
1614
+ * (pivot at the origin, before the instance's rotation / scale). Give it
1615
+ * ONE drag behaviour — `param` (the constrained value → a number param),
1616
+ * `input` (moves a point input), or `drag` (anything else) — or mark it
1617
+ * `fixed`; `click` makes it a button (a press without a move).
1618
+ */
1619
+ /**
1620
+ * A button in a handle's hover menu, or in the instance's context menu
1621
+ * (the block's top-level `actions`). One undo step.
1622
+ */
1623
+ export interface BlockAction<P = BlockParams, D = unknown, S = never> {
1624
+ label: string;
1625
+ icon?: HandleIcon;
1626
+ /** Pick a point first (snapping as usual); `run` receives it,
1627
+ * block-local, as `point`. Esc cancels. */
1628
+ pick?: "point";
1629
+ run: (s: {
1630
+ params: ParamValues<P>;
1631
+ inputs: D;
1632
+ point?: Vec2;
1633
+ }) => HandlePatch<P, S> | undefined;
1634
+ }
1635
+
1636
+ /** A field in a handle's hover menu: a parameter by name, or a field
1637
+ * bound to any value (one wall's thickness inside a `data` param), with
1638
+ * `set` returning what an edit changes. Each edit is one undo step. */
1639
+ export type MenuField<P = BlockParams, S = never> =
1640
+ | (keyof ParamValues<P> & string)
1641
+ | {
1642
+ /** The field: any value parameter row without name / default
1643
+ * (`{ type: "number", label: "Thickness", min: 50 }`). */
1644
+ field: Record<string, unknown> & { type: string; label?: string };
1645
+ value: unknown;
1646
+ set: (value: never) => HandlePatch<P, S> | undefined;
1647
+ };
1648
+
1649
+ interface HandleBase<P, D, S> {
1650
+ /** Stable within the instance (hover / drag continuity, tests). */
1651
+ id: string;
1652
+ /** Tooltip, and the hover menu's title. */
1653
+ label?: string;
1654
+ /** Not draggable (a button, or menu-only). */
1655
+ fixed?: boolean;
1656
+ /** The constrained value → this NUMBER param. */
1657
+ param?: NumberParamName<P>;
1658
+ drag?: (e: HandleDragEvent<P, D>) => HandlePatch<P, S> | undefined;
1659
+ /** Button behaviour: one undo step; the selection doesn't change. */
1660
+ click?: (s: {
1661
+ params: ParamValues<P>;
1662
+ inputs: D;
1663
+ }) => HandlePatch<P, S> | undefined;
1664
+ /** Fields shown in the hover menu (see MenuField). */
1665
+ menu?: readonly MenuField<P, S>[];
1666
+ /** Buttons in the hover menu. */
1667
+ actions?: readonly BlockAction<P, D, S>[];
1668
+ }
1669
+
1670
+ /**
1671
+ * One custom point handle on a selected instance, in block-LOCAL
1672
+ * coordinates (pivot at the origin, before the instance's rotation /
1673
+ * scale). Give it ONE drag behaviour — `param` (the constrained value →
1674
+ * a number param), `input` (moves a point input), or `drag` (anything
1675
+ * else) — or mark it `fixed`; `click` makes it a button.
1676
+ */
1677
+ export interface PointHandleDef<P = BlockParams, D = unknown, S = never>
1678
+ extends HandleBase<P, D, S> {
1679
+ kind?: "point";
1680
+ at: Vec2Like;
1681
+ shape?: HandleShape;
1682
+ /** Radians — points a triangle / arrow (default: the constraint axis). */
1683
+ direction?: number;
1684
+ /** Constrain to a line through `origin`; value = signed distance. */
1685
+ along?: {
1686
+ origin: Vec2Like;
1687
+ direction: Vec2Like;
1688
+ min?: number;
1689
+ max?: number;
1690
+ };
1691
+ /** Constrain to the circle through `at` around `center`; value =
1692
+ * the angle (radians). */
1693
+ around?: { center: Vec2Like };
1694
+ /** Move a point input: `"hinge"`, or one point of a list
1695
+ * (`{ name: "path", index: 2 }`). Replaces that point's default handle. */
1696
+ input?: string | { name: string; index: number };
1697
+ /** A lucide glyph drawn in the handle — the button look. */
1698
+ icon?: HandleIcon;
1699
+ }
1700
+
1701
+ /**
1702
+ * A line handle (a wall face, an offset): drawn while hovered, dragged
1703
+ * along its NORMAL by default (value = the signed offset, left of
1704
+ * from → to positive); `along` gives another direction.
1705
+ */
1706
+ export interface LineHandleDef<P = BlockParams, D = unknown, S = never>
1707
+ extends HandleBase<P, D, S> {
1708
+ kind: "line";
1709
+ from: Vec2Like;
1710
+ to: Vec2Like;
1711
+ along?: { direction: Vec2Like; min?: number; max?: number };
1712
+ }
1713
+
1714
+ export type HandleDef<P = BlockParams, D = unknown, S = never> =
1715
+ | PointHandleDef<P, D, S>
1716
+ | LineHandleDef<P, D, S>;
1717
+
1377
1718
  // ——— defineBlock ———
1378
1719
 
1379
1720
  /**
@@ -1423,6 +1764,7 @@ declare module "lineadraw" {
1423
1764
  export interface BlockSpec<
1424
1765
  P extends ParamDefs = readonly [],
1425
1766
  Pl extends PlaceSpec<P> | undefined = undefined,
1767
+ I extends InputDefs | undefined = undefined,
1426
1768
  > {
1427
1769
  id: string;
1428
1770
  name: string;
@@ -1447,16 +1789,58 @@ declare module "lineadraw" {
1447
1789
  paramVisibility?: (props: {
1448
1790
  params: ParamValues<P>;
1449
1791
  }) => Partial<Record<keyof ParamValues<P> & string, boolean>>;
1450
- /** Point labels (fixed picks) or an interactive function (see PlaceSpec). */
1451
- place?: Pl;
1792
+ /**
1793
+ * The block's declared inputs, by name — picked in table order at
1794
+ * insertion (`points` until Enter, `objects` from the selection when
1795
+ * it fits), and read in `draw` as `inputs.<name>`. Instances store
1796
+ * them by name, so reordering or adding inputs never breaks existing
1797
+ * instances. Replaces `place` labels.
1798
+ */
1799
+ inputs?: I;
1800
+ /**
1801
+ * Point labels (fixed picks) or an interactive function (see
1802
+ * PlaceSpec). With an `inputs` table, only the function form: it
1803
+ * returns the inputs by name (for conditional or chained picks).
1804
+ */
1805
+ place?: [I] extends [InputDefs]
1806
+ ? (props: PlaceProps<P>) => StoredInputs<I> | Promise<StoredInputs<I>>
1807
+ : Pl;
1452
1808
  /**
1453
1809
  * Optional inputs used for the block's PREVIEW (panel/Library
1454
1810
  * thumbnails, marketplace cards, save-time dry run) instead of the
1455
1811
  * generic stand-in picks — curate them so the preview shows the block
1456
- * at its best. Same shape `place` produces; points accept [x, y];
1457
- * object-id strings resolve to a stand-in line in previews.
1812
+ * at its best. Same shape `place` produces (by name with an
1813
+ * `inputs` table); points accept [x, y]; object-id strings resolve to
1814
+ * a stand-in line in previews.
1458
1815
  */
1459
- previewInputs?: PreviewInputs<Pl>;
1816
+ previewInputs?: [I] extends [InputDefs] ? StoredInputs<I> : PreviewInputs<Pl>;
1817
+ /**
1818
+ * Custom handles on a selected instance — pure, block-local, re-run
1819
+ * whenever params / inputs change, so positions follow the values (a
1820
+ * width handle sits at the width). See HandleDef.
1821
+ */
1822
+ handles?: (props: {
1823
+ params: ParamValues<P>;
1824
+ inputs: [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>;
1825
+ /** Hosted blocks: the host's `expose` result (null when this block
1826
+ * has no host). Shape it as the host documents it. */
1827
+ host: any;
1828
+ /** Hosting: what lives on this instance — each one's `expose`. */
1829
+ hosted: readonly HostedBlock[];
1830
+ }) => readonly HandleDef<
1831
+ P,
1832
+ [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>,
1833
+ [I] extends [InputDefs] ? Partial<StoredInputs<I>> : never
1834
+ >[];
1835
+ /**
1836
+ * Actions in the instance's context menu (right-click) — e.g. "Add
1837
+ * window" with `pick: "point"`. Static records; one undo step each.
1838
+ */
1839
+ actions?: readonly BlockAction<
1840
+ P,
1841
+ [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>,
1842
+ [I] extends [InputDefs] ? Partial<StoredInputs<I>> : never
1843
+ >[];
1460
1844
  /**
1461
1845
  * Children in block-LOCAL coordinates (pure, runs per evaluation).
1462
1846
  * The first point input is the pivot; `inputs` arrive localized
@@ -1466,12 +1850,51 @@ declare module "lineadraw" {
1466
1850
  */
1467
1851
  draw: (props: {
1468
1852
  params: ParamValues<P>;
1469
- inputs: PlaceInputs<Pl>;
1853
+ inputs: [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>;
1470
1854
  /** The document's text / dimension styles by id — for blocks that
1471
1855
  * lay out with real text sizes. Pass a picked id on to children as
1472
1856
  * `styleId` to let them follow the style. */
1473
1857
  styles: BlockStyles;
1858
+ /** Hosted blocks: the host's `expose` result (null when this block
1859
+ * has no host). Shape it as the host documents it. */
1860
+ host: any;
1861
+ /** Hosting: what lives on this instance — each one's `expose`. */
1862
+ hosted: readonly HostedBlock[];
1474
1863
  }) => BlockChildInput[];
1864
+ /**
1865
+ * Hosting (blocks living on blocks — a door on a floor plan): JSON
1866
+ * this block PUBLISHES for others to read — a host its `walls`, a
1867
+ * door its `cut`. It sees only its own params, which is what keeps
1868
+ * a host and what lives on it from looping.
1869
+ */
1870
+ expose?: (props: { params: ParamValues<P> }) => JsonValue;
1871
+ /**
1872
+ * A hosted block (one with a `host` input): turns the click on the
1873
+ * host — host-local, with the host's exposed data — into this
1874
+ * instance's params (`{ wall: "B/1-2", at: 1200 }`).
1875
+ */
1876
+ attach?: (props: {
1877
+ host: any;
1878
+ point: Vec2;
1879
+ params: ParamValues<P>;
1880
+ }) => Partial<ParamValues<P>>;
1881
+ /**
1882
+ * A hosted block's stand-in host for previews, Library / marketplace
1883
+ * thumbnails and the save-time dry run: the JSON a real host's
1884
+ * `expose` would return. The preview places the block as if its host
1885
+ * were clicked at the origin (`attach`), and `draw` gets this as
1886
+ * `host`. Add a flag (`preview: true`) to draw extra context there,
1887
+ * e.g. a stretch of wall around a door.
1888
+ */
1889
+ previewHost?: JsonValue;
1890
+ }
1891
+
1892
+ /** One block living on a host, as the host's `draw` sees it. */
1893
+ export interface HostedBlock {
1894
+ id: string;
1895
+ definitionId: string;
1896
+ /** Its `expose` result (null without one). */
1897
+ exposed: any;
1475
1898
  }
1476
1899
 
1477
1900
  /**
@@ -1510,13 +1933,21 @@ declare module "lineadraw" {
1510
1933
  >(
1511
1934
  spec: BlockSpec<P, (props: PlaceProps<P>) => R>,
1512
1935
  ): BlockSpec<P, (props: PlaceProps<P>) => R>;
1936
+ // An `inputs` table: `draw`'s inputs are typed by name from it.
1937
+ export function defineBlock<
1938
+ const P extends ParamDefs = readonly [],
1939
+ const I extends InputDefs = readonly [],
1940
+ >(spec: BlockSpec<P, undefined, I>): BlockSpec<P, undefined, I>;
1513
1941
 
1514
1942
  // ——— Commands (macro scripts) ———
1515
1943
  // Drawing / EditorSurface / CommandContext are emitted from scriptTypes.ts
1516
1944
  // above (the script-facing mirror of core's real interfaces).
1517
1945
 
1518
1946
  /** One user command (macro): identity, metadata, and the `run` body. */
1519
- export interface CommandSpec<P extends ParamDefs = readonly []> {
1947
+ export interface CommandSpec<
1948
+ P extends ParamDefs = readonly [],
1949
+ I extends readonly string[] | CommandInputDefs = readonly [],
1950
+ > {
1520
1951
  id: string;
1521
1952
  name: string;
1522
1953
  description?: string;
@@ -1524,13 +1955,24 @@ declare module "lineadraw" {
1524
1955
  authors?: readonly string[];
1525
1956
  tags?: readonly string[];
1526
1957
  /**
1527
- * Point inputs the command needs, as labels (`["Start", "End"]`). In
1528
- * the editor each is picked in turn before `run` (rubber-band from the
1529
- * previous pick); a headless or remote caller supplies them up front.
1530
- * They arrive as `ctx.inputs`, in this order. This is what lets one
1531
- * script run in the editor, from a shell, and from an agent alike.
1958
+ * The inputs the command needs: an inputs TABLE
1959
+ * (`[{ name: "doors", type: "objects", types: ["block"] }]`, arriving
1960
+ * as `ctx.inputs.doors`), or point labels (`["Start", "End"]`,
1961
+ * arriving as `ctx.inputs` in order). In the editor they are acquired
1962
+ * before `run` — object inputs take the current selection when it
1963
+ * fits, the rest are picked; a headless or remote caller supplies them
1964
+ * up front (by name for a table). This is what lets one script run in
1965
+ * the editor, from a shell, and from an agent alike.
1966
+ */
1967
+ inputs?: I;
1968
+ /**
1969
+ * The command ASKS during the run (`editor.pickPoint`, `prompt`,
1970
+ * `ask` — the next question depends on the last answer), so it can't
1971
+ * run headless: CLI / agent / SDK runners refuse it up front instead of
1972
+ * failing halfway. Leave it off for commands whose `inputs` /
1973
+ * `params` cover everything.
1532
1974
  */
1533
- inputs?: readonly string[];
1975
+ requiresEditor?: boolean;
1534
1976
  /**
1535
1977
  * Parameters the command needs — the same table a block's `params`
1536
1978
  * declares (number / string / boolean / enum / label rows), or a
@@ -1546,7 +1988,7 @@ declare module "lineadraw" {
1546
1988
  * uncaught error rolls everything back. A return value is handed to
1547
1989
  * the caller (the agent tool / CLI) — the editor ignores it.
1548
1990
  */
1549
- run(ctx: CommandContext<ParamValues<P>>): unknown;
1991
+ run(ctx: CommandContext<ParamValues<P>, CommandInputs<I>>): unknown;
1550
1992
  }
1551
1993
 
1552
1994
  /**
@@ -1572,9 +2014,10 @@ declare module "lineadraw" {
1572
2014
  * },
1573
2015
  * });
1574
2016
  */
1575
- export function defineCommand<const P extends ParamDefs = readonly []>(
1576
- spec: CommandSpec<P>,
1577
- ): CommandSpec<P>;
2017
+ export function defineCommand<
2018
+ const P extends ParamDefs = readonly [],
2019
+ const I extends readonly string[] | CommandInputDefs = readonly [],
2020
+ >(spec: CommandSpec<P, I>): CommandSpec<P, I>;
1578
2021
  }
1579
2022
 
1580
2023
  declare module "lineadraw/helpers" {
@@ -1607,4 +2050,29 @@ declare module "lineadraw/helpers" {
1607
2050
  export const dot: (a: Vec2, b: Vec2) => number;
1608
2051
  /** Rotate vector by `angle` (radians, CCW). */
1609
2052
  export const rotate: (p: Vec2, angle: number) => Vec2;
2053
+ /** A point as scripts write it: [x, y] or { x, y }. */
2054
+ export type PointLike = readonly [number, number] | {
2055
+ readonly x: number;
2056
+ readonly y: number;
2057
+ };
2058
+ /** A polygon: one ring of points (implicitly closed), or several rings —
2059
+ * the first the outer boundary, the rest holes. */
2060
+ export type PolygonLike = readonly PointLike[] | readonly (readonly PointLike[])[];
2061
+ /** A result ring: closed implicitly (the first point is not repeated). */
2062
+ export type Ring = {
2063
+ x: number;
2064
+ y: number;
2065
+ }[];
2066
+ /**
2067
+ * The union of polygons: overlapping walls become one outline. Returns
2068
+ * every ring of the result — outer boundaries and holes alike. Draw them as
2069
+ * closed polylines, or hand them all to ONE hatch (`loops: rings.map((r) =>
2070
+ * ({ points: r }))`): hatches fill even-odd, so holes stay holes.
2071
+ */
2072
+ export const union: (...polygons: PolygonLike[]) => Ring[];
2073
+ /** `subject` minus every clip polygon (openings cut from a wall). Same
2074
+ * result rings as `union`. */
2075
+ export const difference: (subject: PolygonLike, ...clips: PolygonLike[]) => Ring[];
2076
+ /** Where ALL the polygons overlap. Same result rings as `union`. */
2077
+ export const intersection: (...polygons: PolygonLike[]) => Ring[];
1610
2078
  }