lineadraw 0.9.0 → 0.11.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.
Files changed (36) hide show
  1. package/README.md +154 -20
  2. package/bin/lineadraw.mjs +60 -10
  3. package/dist/bridge.d.ts +1 -0
  4. package/dist/bridge.js +1 -1
  5. package/dist/{chunk-BQCSFVAF.js → chunk-7UFXC7OI.js} +5570 -1174
  6. package/dist/chunk-7UFXC7OI.js.map +1 -0
  7. package/dist/chunk-FDLANLIC.js +1161 -0
  8. package/dist/chunk-FDLANLIC.js.map +1 -0
  9. package/dist/chunk-M3EDRSHW.js +81 -0
  10. package/dist/chunk-M3EDRSHW.js.map +1 -0
  11. package/dist/chunk-MYP7RUYK.js +9 -0
  12. package/dist/chunk-MYP7RUYK.js.map +1 -0
  13. package/dist/{chunk-L2CWZAKW.js → chunk-RNA2RDGL.js} +13 -6
  14. package/dist/chunk-RNA2RDGL.js.map +1 -0
  15. package/dist/dist-56K6CNML.js +188 -0
  16. package/dist/dist-56K6CNML.js.map +1 -0
  17. package/dist/dist-IEN64QSV.js +10 -0
  18. package/dist/dist-IEN64QSV.js.map +1 -0
  19. package/dist/emscripten-module-Q67P5WYC-ITCU5LXL.js +2424 -0
  20. package/dist/emscripten-module-Q67P5WYC-ITCU5LXL.js.map +1 -0
  21. package/dist/{exportDxf-T2RTSTH5.js → exportDxf-6XTXQ4XN.js} +2 -2
  22. package/dist/ffi-SHMEF5YV.js +82 -0
  23. package/dist/ffi-SHMEF5YV.js.map +1 -0
  24. package/dist/module-ES6BEMUI-BJFWC656.js +14 -0
  25. package/dist/module-ES6BEMUI-BJFWC656.js.map +1 -0
  26. package/dist/module-asyncify-2EFITU5U-7F5WZXRA.js +9 -0
  27. package/dist/module-asyncify-2EFITU5U-7F5WZXRA.js.map +1 -0
  28. package/dist/sdk.d.ts +1118 -128
  29. package/dist/sdk.js +742 -620
  30. package/dist/sdk.js.map +1 -1
  31. package/lineadraw.d.ts +666 -44
  32. package/package.json +1 -1
  33. package/templates/README.md +6 -4
  34. package/dist/chunk-BQCSFVAF.js.map +0 -1
  35. package/dist/chunk-L2CWZAKW.js.map +0 -1
  36. /package/dist/{exportDxf-T2RTSTH5.js.map → exportDxf-6XTXQ4XN.js.map} +0 -0
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,scriptCurves}.ts.
4
+ // Regenerate with:
4
5
  // npm run gen:lineadraw-dts -w @lineadraw/core
5
6
 
6
7
  declare module "lineadraw" {
@@ -96,11 +97,64 @@ declare module "lineadraw" {
96
97
  startAngle?: number;
97
98
  endAngle?: number;
98
99
  }>;
100
+ /**
101
+ * A NURBS curve (DXF SPLINE). `knots` number controlPoints + degree + 1,
102
+ * non-decreasing; `weights` (one per control point) make it rational. With
103
+ * `fitPoints` the curve was drawn through them. Easiest to create through
104
+ * points: `splineThrough(points)` from "lineadraw/helpers".
105
+ */
106
+ export type SplineInput = ObjectInputShape<{
107
+ type: "spline";
108
+ degree: number;
109
+ controlPoints: readonly Vec2Like[];
110
+ knots: readonly number[];
111
+ weights?: readonly number[];
112
+ fitPoints?: readonly Vec2Like[];
113
+ /** Fit splines: the direction the curve leaves its first fit point. */
114
+ startTangent?: Vec2Like;
115
+ /** Fit splines: the direction the curve arrives at its last fit point. */
116
+ endTangent?: Vec2Like;
117
+ closed?: boolean;
118
+ }>;
119
+ /**
120
+ * A hatch boundary edge — the geometry of a line, an arc, an elliptic arc or
121
+ * a spline. An arc runs CCW from startAngle to endAngle (equal = a full
122
+ * circle); an ellipse in DXF form (parameters, 0 → 2π = whole). Edges are
123
+ * chained by their ends, stored in either direction. A polygon or bulged
124
+ * outline: `hatchLoop(points)` from "lineadraw/helpers".
125
+ */
126
+ export type HatchEdgeInput = {
127
+ kind: "line";
128
+ a: Vec2Like;
129
+ b: Vec2Like;
130
+ } | {
131
+ kind: "arc";
132
+ center: Vec2Like;
133
+ radius: number;
134
+ startAngle: number;
135
+ endAngle: number;
136
+ } | {
137
+ kind: "ellipse";
138
+ center: Vec2Like;
139
+ majorAxis: Vec2Like;
140
+ ratio: number;
141
+ startAngle?: number;
142
+ endAngle?: number;
143
+ } | {
144
+ kind: "spline";
145
+ degree: number;
146
+ controlPoints: readonly Vec2Like[];
147
+ knots: readonly number[];
148
+ weights?: readonly number[];
149
+ /** Drawn through points: those points, as on a spline. */
150
+ fitPoints?: readonly Vec2Like[];
151
+ };
99
152
  export type HatchInput = ObjectInputShape<{
100
153
  type: "hatch";
101
- /** loops[0] = outer boundary, the rest are holes (even-odd). */
154
+ /** loops[0] = outer boundary, the rest are holes (even-odd); each a
155
+ * closed chain of edges (a circle, ellipse or closed spline is one). */
102
156
  loops: readonly {
103
- points: readonly PolylineVertexInput[];
157
+ edges: readonly HatchEdgeInput[];
104
158
  }[];
105
159
  fill?: {
106
160
  kind: "solid";
@@ -204,9 +258,12 @@ declare module "lineadraw" {
204
258
  /** Applied last, about the pivot (the first input point). */
205
259
  rotation?: number;
206
260
  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[];
261
+ params?: Record<string, number | string | boolean | readonly number[] | readonly string[] | JsonValue>;
262
+ /** Input points in the PARENT's local coords (the first is the pivot):
263
+ * in pick order, or by input name for a block declaring an `inputs`
264
+ * table. Object ids (a `host`, picked objects) only on instances a
265
+ * script ADDS to the document — a block's own children can't reference. */
266
+ inputs?: readonly Vec2Like[] | Readonly<Record<string, Vec2Like | readonly Vec2Like[] | string | readonly string[]>>;
210
267
  }>;
211
268
  /** A table cell: plain text, or text with merge / alignment / style. */
212
269
  export type TableCellInput = string | {
@@ -302,7 +359,7 @@ declare module "lineadraw" {
302
359
  modelCenter?: Vec2Like;
303
360
  /** Drawing-scale denominator (model mm per paper mm); 1:50 => 50. */
304
361
  scale?: number;
305
- /** Pinned: no pan/zoom-through, no move/resize grips. */
362
+ /** Pinned: no pan/zoom-through, no move/resize handles. */
306
363
  locked?: boolean;
307
364
  /** Layer ids hidden in this viewport only. */
308
365
  hiddenLayerIds?: readonly string[];
@@ -317,7 +374,7 @@ declare module "lineadraw" {
317
374
  }>;
318
375
  /** What a script WRITES — the union of the authoring DTOs above (what
319
376
  * `document.add` takes). Reads return `ModelObject`. */
320
- export type ModelObjectInput = LineInput | PointInput | PolylineInput | CircleInput | ArcInput | EllipseInput | HatchInput | TextInput | DimensionInput | BlockInput | TableInput | ViewportInput | CommentInput | ReferenceInput;
377
+ export type ModelObjectInput = LineInput | PointInput | PolylineInput | CircleInput | ArcInput | EllipseInput | SplineInput | HatchInput | TextInput | DimensionInput | BlockInput | TableInput | ViewportInput | CommentInput | ReferenceInput;
321
378
  /** One child a block's `draw` may return: every input except viewports and
322
379
  * comments (both are drawn only as a space's own objects — a block's would
323
380
  * never show). An `asset` reference resolves in the document the instance
@@ -380,6 +437,17 @@ declare module "lineadraw" {
380
437
  startAngle: number;
381
438
  endAngle: number;
382
439
  }>;
440
+ export type Spline = ObjectShape<{
441
+ type: "spline";
442
+ degree: number;
443
+ controlPoints: readonly Vec2[];
444
+ knots: readonly number[];
445
+ weights?: readonly number[];
446
+ fitPoints?: readonly Vec2[];
447
+ startTangent?: Vec2;
448
+ endTangent?: Vec2;
449
+ closed: boolean;
450
+ }>;
383
451
  export type HatchFill = {
384
452
  kind: "solid";
385
453
  } | {
@@ -400,11 +468,37 @@ declare module "lineadraw" {
400
468
  color2: string;
401
469
  angle: number;
402
470
  };
471
+ /** Stored hatch boundary edge — see HatchEdgeInput. */
472
+ export type HatchEdge = {
473
+ kind: "line";
474
+ a: Vec2;
475
+ b: Vec2;
476
+ } | {
477
+ kind: "arc";
478
+ center: Vec2;
479
+ radius: number;
480
+ startAngle: number;
481
+ endAngle: number;
482
+ } | {
483
+ kind: "ellipse";
484
+ center: Vec2;
485
+ majorAxis: Vec2;
486
+ ratio: number;
487
+ startAngle: number;
488
+ endAngle: number;
489
+ } | {
490
+ kind: "spline";
491
+ degree: number;
492
+ controlPoints: readonly Vec2[];
493
+ knots: readonly number[];
494
+ weights?: readonly number[];
495
+ fitPoints?: readonly Vec2[];
496
+ };
403
497
  export type Hatch = ObjectShape<{
404
498
  type: "hatch";
405
499
  /** loops[0] = outer boundary, the rest are holes (even-odd). */
406
500
  loops: readonly {
407
- points: readonly PolylineVertex[];
501
+ edges: readonly HatchEdge[];
408
502
  }[];
409
503
  fill: HatchFill;
410
504
  scale: number;
@@ -448,9 +542,10 @@ declare module "lineadraw" {
448
542
  definitionId: string;
449
543
  rotation: number;
450
544
  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)[];
545
+ params: Readonly<Record<string, number | string | boolean | readonly number[] | readonly string[] | JsonValue>>;
546
+ /** World points and/or referenced object ids: in pick order, or by
547
+ * input name for a block declaring an `inputs` table. */
548
+ inputs: readonly (Vec2 | string)[] | Readonly<Record<string, Vec2 | string | readonly Vec2[] | readonly string[]>>;
454
549
  }>;
455
550
  /** Paper-space window into model space; exists only in layout spaces. */
456
551
  export type Viewport = ObjectShape<{
@@ -540,7 +635,7 @@ declare module "lineadraw" {
540
635
  styleOverride?: TextStyleOverride;
541
636
  }>;
542
637
  /** Any object as stored in the document — what reads hand to scripts. */
543
- export type ModelObject = Line | Point | Polyline | Circle | Arc | Ellipse | Hatch | Text | Dimension | Block | Viewport | Comment | Table | Reference;
638
+ export type ModelObject = Line | Point | Polyline | Circle | Arc | Ellipse | Spline | Hatch | Text | Dimension | Block | Viewport | Comment | Table | Reference;
544
639
  /**
545
640
  * One choice of a "Select" parameter: the value (what scripts receive) with
546
641
  * an optional properties-panel label. A plain string is shorthand for
@@ -641,6 +736,12 @@ declare module "lineadraw" {
641
736
  * value: it never appears in the `params` record `draw` receives.
642
737
  * `name` stays the unique key (paramVisibility can hide it by name). */
643
738
  | (ParamBase & {
739
+ /** Structured data the block owns (any JSON value) — no panel row;
740
+ * edited through handles, handle menus and actions. Typed by its
741
+ * default: `default: {} as Record<string, Wall>`. */
742
+ type: "data";
743
+ default: JsonValue;
744
+ }) | (ParamBase & {
644
745
  type: "label";
645
746
  })
646
747
  /** Layout only: 2–3 compact params on one panel row, each item's label a
@@ -711,12 +812,147 @@ declare module "lineadraw" {
711
812
  * declares (or a block's function form returns).
712
813
  */
713
814
  export type ParamDefs = readonly ParamDef[];
815
+ /**
816
+ * One declared input — a row of the `inputs` table a block or command
817
+ * spec declares. Inputs are read by `name` (`inputs.<name>`); `label` is
818
+ * the pick prompt. `types` limits object picks to those object types
819
+ * (empty / absent = any). Optional inputs are skipped with Enter.
820
+ */
821
+ export type InputDef = {
822
+ name: string;
823
+ label?: string;
824
+ description?: string;
825
+ type: "point";
826
+ optional?: boolean;
827
+ } | {
828
+ name: string;
829
+ label?: string;
830
+ description?: string;
831
+ type: "points";
832
+ /** Default 1. */
833
+ min?: number;
834
+ max?: number;
835
+ /** A polygon (closing segment previewed) rather than an open chain. */
836
+ closed?: boolean;
837
+ } | {
838
+ name: string;
839
+ label?: string;
840
+ description?: string;
841
+ type: "object";
842
+ types?: readonly ModelObject["type"][];
843
+ optional?: boolean;
844
+ } | {
845
+ name: string;
846
+ label?: string;
847
+ description?: string;
848
+ type: "objects";
849
+ types?: readonly ModelObject["type"][];
850
+ /** Default 1. */
851
+ min?: number;
852
+ max?: number;
853
+ } | {
854
+ name: string;
855
+ label?: string;
856
+ description?: string;
857
+ /** The block instance this one LIVES ON (a door on a floor plan): it
858
+ * draws in the host's frame and reads the host's `expose` as `host`;
859
+ * the host's `draw` sees it in `hosted`. Picked by clicking the host;
860
+ * `attach` turns the click into params. */
861
+ type: "host";
862
+ /** Accepted host DEFINITION ids (empty = any block). */
863
+ types?: readonly string[];
864
+ };
865
+ /** A custom handle's marker shape (cosmetic: triangle / arrow = "pull this
866
+ * way", diamond = "options here", plus / minus = "add / remove"). */
867
+ export type HandleShape = "square" | "circle" | "diamond" | "triangle" | "arrow" | "double-arrow" | "plus" | "minus";
868
+ /** Icons a button handle may show (lucide glyphs). */
869
+ 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";
870
+ /** An inputs table. */
871
+ export type InputDefs = readonly InputDef[];
872
+ /** A command's inputs table: a `host` is a block-only input. */
873
+ export type CommandInputDefs = readonly Exclude<InputDef, {
874
+ type: "host";
875
+ }>[];
876
+ /** The object type an object input's `types` filter admits. */
877
+ type PickedObject<D> = D extends {
878
+ types: readonly (infer T)[];
879
+ } ? [T] extends [never] ? ModelObject : Extract<ModelObject, {
880
+ type: T;
881
+ }> : ModelObject;
882
+ type DrawInputValue<D> = D extends {
883
+ type: "point";
884
+ } ? D extends {
885
+ optional: true;
886
+ } ? Vec2 | undefined : Vec2 : D extends {
887
+ type: "points";
888
+ } ? readonly Vec2[] : D extends {
889
+ type: "object";
890
+ } ? PickedObject<D> | null : readonly PickedObject<D>[];
891
+ /**
892
+ * What a block's `draw` receives as `inputs` for a table: points
893
+ * pivot-localized, object inputs as the referenced object (pivot-local) or
894
+ * null once deleted, lists as arrays (deleted objects dropped).
895
+ */
896
+ export type DrawInputs<I extends InputDefs> = {
897
+ readonly [D in I[number] as D extends {
898
+ type: "host";
899
+ } ? never : D["name"]]: DrawInputValue<D>;
900
+ };
901
+ type StoredInputValue<D> = D extends {
902
+ type: "host";
903
+ } ? string : D extends {
904
+ type: "point";
905
+ } ? Vec2Like : D extends {
906
+ type: "points";
907
+ } ? readonly Vec2Like[] : D extends {
908
+ type: "object";
909
+ } ? string : readonly string[];
910
+ /**
911
+ * Inputs as STORED for a table (what `place` returns and `previewInputs`
912
+ * holds): points in world coords ([x, y] accepted), objects as ids.
913
+ * Optional inputs may be left out.
914
+ */
915
+ export type StoredInputs<I extends InputDefs> = {
916
+ readonly [D in I[number] as D extends {
917
+ optional: true;
918
+ } ? never : D["name"]]: StoredInputValue<D>;
919
+ } & {
920
+ readonly [D in I[number] as D extends {
921
+ optional: true;
922
+ } ? D["name"] : never]?: StoredInputValue<D>;
923
+ };
924
+ type CommandInputValue<D> = D extends {
925
+ type: "point";
926
+ } ? D extends {
927
+ optional: true;
928
+ } ? Vec2 | undefined : Vec2 : D extends {
929
+ type: "points";
930
+ } ? readonly Vec2[] : D extends {
931
+ type: "object";
932
+ } ? D extends {
933
+ optional: true;
934
+ } ? string | undefined : string : readonly string[];
935
+ /**
936
+ * What a command's `ctx.inputs` is: the points in label order for a
937
+ * labels array, a record by name for a table (objects as ids — read them
938
+ * with `document.getObject`).
939
+ */
940
+ export type CommandInputs<I> = I extends readonly string[] ? readonly Vec2[] : I extends CommandInputDefs ? {
941
+ readonly [D in I[number] as D["name"]]: CommandInputValue<D>;
942
+ } : readonly Vec2[];
714
943
  /** The value an enum option denotes (its string, or `value` of the record form). */
715
944
  type OptionValue<O extends ParamOption> = O extends string ? O : O extends {
716
945
  value: infer V extends string;
717
946
  } ? V : never;
947
+ /** Any JSON value — what a `data` param holds. */
948
+ export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
949
+ readonly [key: string]: JsonValue;
950
+ };
718
951
  /** The runtime value type of one `params` table entry. */
719
952
  type ParamValue<Q extends ParamDef> = Q extends {
953
+ type: "data";
954
+ default: infer D;
955
+ } ? D : Q extends {
720
956
  type: "enum";
721
957
  } ? OptionValue<Q["options"][number]> : Q extends {
722
958
  type: "number";
@@ -812,6 +1048,7 @@ declare module "lineadraw" {
812
1048
  }[];
813
1049
  settings: {
814
1050
  units: "mm" | "in";
1051
+ modelScale: number;
815
1052
  };
816
1053
  };
817
1054
  /** What `renderSvg` draws: model space or one layout. */
@@ -889,6 +1126,9 @@ declare module "lineadraw" {
889
1126
  authors?: readonly string[];
890
1127
  tags?: readonly string[];
891
1128
  dependencies?: readonly string[];
1129
+ /** The declared inputs table, when the block has one (instances then
1130
+ * store their inputs by name). */
1131
+ inputs?: InputDefs;
892
1132
  };
893
1133
  /** A command module's identity/metadata (from its exports). */
894
1134
  export type CommandInfo = {
@@ -898,8 +1138,11 @@ declare module "lineadraw" {
898
1138
  version?: string;
899
1139
  authors?: readonly string[];
900
1140
  tags?: readonly string[];
901
- /** Declared point inputs (labels), picked or supplied before `run`. */
902
- inputs?: readonly string[];
1141
+ /** Declared inputs, picked or supplied before `run`: point labels
1142
+ * (positional) or an inputs table (by name). */
1143
+ inputs?: readonly string[] | CommandInputDefs;
1144
+ /** Asks during the run, so headless runners refuse it. */
1145
+ requiresEditor?: boolean;
903
1146
  /** Declared parameters (same table as a block's), prompted or supplied
904
1147
  * before `run`. */
905
1148
  params?: readonly ParamDef[];
@@ -973,8 +1216,8 @@ declare module "lineadraw" {
973
1216
  /** A sheet: a bare preset (orientation from context — landscape for a new
974
1217
  * layout, the current one when patching), a preset with its orientation, or
975
1218
  * explicit millimetres. */
976
- export type SheetSpec = "A4" | "A3" | "A2" | "A1" | "A0" | {
977
- name: "A4" | "A3" | "A2" | "A1" | "A0";
1219
+ export type SheetSpec = "A5" | "A4" | "A3" | "A2" | "A1" | "A0" | "Letter" | {
1220
+ name: "A5" | "A4" | "A3" | "A2" | "A1" | "A0" | "Letter";
978
1221
  orientation: "portrait" | "landscape";
979
1222
  } | {
980
1223
  width: number;
@@ -1012,6 +1255,7 @@ declare module "lineadraw" {
1012
1255
  version: number;
1013
1256
  settings: {
1014
1257
  units: "mm" | "in";
1258
+ modelScale: number;
1015
1259
  };
1016
1260
  layers: readonly Layer[];
1017
1261
  currentLayerId: string;
@@ -1271,10 +1515,43 @@ declare module "lineadraw" {
1271
1515
  label?: string;
1272
1516
  referencePoint?: Vec2Like;
1273
1517
  }): Promise<Vec2>;
1274
- /** Await one object click; resolves the object's id. */
1518
+ /** Await one object click; resolves the object's id. `types` limits
1519
+ * what can be picked (empty / absent = any). */
1275
1520
  pickObject(opts?: {
1276
1521
  label?: string;
1522
+ types?: readonly ModelObject["type"][];
1277
1523
  }): Promise<string>;
1524
+ /** Await a chain of snapped points (rubber band from the previous);
1525
+ * Enter / Esc finishes once `min` (default 1) is met, `max` ends it,
1526
+ * `closed` previews the closing segment. Rejects on cancel below `min`. */
1527
+ pickPoints(opts?: {
1528
+ label?: string;
1529
+ min?: number;
1530
+ max?: number;
1531
+ closed?: boolean;
1532
+ referencePoint?: Vec2Like;
1533
+ }): Promise<Vec2[]>;
1534
+ /** Await several object picks (each once); Enter / Esc finishes once
1535
+ * `min` (default 1) is met. Rejects on cancel below `min`. */
1536
+ pickObjects(opts?: {
1537
+ label?: string;
1538
+ types?: readonly ModelObject["type"][];
1539
+ min?: number;
1540
+ max?: number;
1541
+ }): Promise<string[]>;
1542
+ /**
1543
+ * Ask several values in ONE dialog — the same form a command's `params`
1544
+ * table shows, from a params table (number / string / boolean / enum …):
1545
+ * const { count, label } = await editor.ask(
1546
+ * [{ name: "count", type: "number", default: 3 },
1547
+ * { name: "label", type: "string", default: "A" }],
1548
+ * { title: "Array" });
1549
+ * Resolves the values typed by the table; rejects on cancel.
1550
+ */
1551
+ ask<const P extends ParamDefs>(params: P, opts?: {
1552
+ title?: string;
1553
+ description?: string;
1554
+ }): Promise<ParamValues<P>>;
1278
1555
  /** Await a typed value from a small input dialog. */
1279
1556
  prompt(message: string, opts?: {
1280
1557
  initial?: string;
@@ -1286,20 +1563,21 @@ declare module "lineadraw" {
1286
1563
  /** Trigger a command by id — like the user pressing its hotkey. A macro
1287
1564
  * takes its declared `inputs` / `params` up front (params passed skip its
1288
1565
  * params dialog). */
1289
- runCommand(id: string, inputs?: readonly Vec2Like[], params?: Readonly<Record<string, unknown>>): Promise<void>;
1566
+ runCommand(id: string, inputs?: readonly Vec2Like[] | Readonly<Record<string, Vec2Like | readonly Vec2Like[] | string | readonly string[]>>, params?: Readonly<Record<string, unknown>>): Promise<void>;
1290
1567
  }
1291
1568
  /**
1292
1569
  * 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).
1570
+ * surface when there is one; `inputs` what the command's declared `inputs`
1571
+ * produced (picked in the editor — object inputs take the selection first
1572
+ * — or supplied by the caller): the points in label order for labels, a
1573
+ * record by name for a table (see CommandInputs); `params` the values of
1574
+ * the declared `params` table (asked for in a dialog in the editor, or
1575
+ * passed by the caller; omitted ones take their defaults).
1298
1576
  */
1299
- export interface CommandContext<P = Readonly<Record<string, unknown>>> {
1577
+ export interface CommandContext<P = Readonly<Record<string, unknown>>, I = readonly Vec2[]> {
1300
1578
  document: Drawing;
1301
1579
  editor?: EditorSurface;
1302
- inputs: readonly Vec2[];
1580
+ inputs: I;
1303
1581
  params: P;
1304
1582
  }
1305
1583
 
@@ -1371,9 +1649,164 @@ declare module "lineadraw" {
1371
1649
  export interface PlaceProps<P = BlockParams> {
1372
1650
  params: ParamValues<P>;
1373
1651
  pickPoint: (label?: string) => Promise<Vec2>;
1374
- pickObject: (label?: string) => Promise<string>;
1652
+ pickObject: (
1653
+ label?: string,
1654
+ opts?: { types?: readonly ModelObject["type"][] },
1655
+ ) => Promise<string>;
1656
+ /** A chain of points; Enter / Esc finishes once `min` (default 1) is met. */
1657
+ pickPoints: (
1658
+ label?: string,
1659
+ opts?: { min?: number; max?: number; closed?: boolean },
1660
+ ) => Promise<Vec2[]>;
1661
+ /** Several objects; Enter / Esc finishes once `min` (default 1) is met. */
1662
+ pickObjects: (
1663
+ label?: string,
1664
+ opts?: {
1665
+ types?: readonly ModelObject["type"][];
1666
+ min?: number;
1667
+ max?: number;
1668
+ },
1669
+ ) => Promise<string[]>;
1375
1670
  }
1376
1671
 
1672
+ // ——— Custom handles ———
1673
+
1674
+ /** The names of a params table's NUMBER parameters. */
1675
+ export type NumberParamName<P> = {
1676
+ [K in keyof ParamValues<P>]: ParamValues<P>[K] extends number ? K : never;
1677
+ }[keyof ParamValues<P>] &
1678
+ string;
1679
+
1680
+ /**
1681
+ * What a handle's drag / click / action changes: params by name (numbers
1682
+ * are clamped to the param's min / max), and — for a block with an
1683
+ * `inputs` table — inputs by name in block-LOCAL coordinates.
1684
+ */
1685
+ export interface HandlePatch<P = BlockParams, S = never> {
1686
+ params?: Partial<ParamValues<P>>;
1687
+ inputs?: S;
1688
+ }
1689
+
1690
+ /** What a `drag` function receives — the values at drag START (not
1691
+ * compounding) plus where the cursor is, already constrained. */
1692
+ export interface HandleDragEvent<P = BlockParams, D = unknown> {
1693
+ params: ParamValues<P>;
1694
+ inputs: D;
1695
+ /** The cursor, block-local, after the handle's constraint. */
1696
+ point: Vec2;
1697
+ /** The handle's position when the drag started. */
1698
+ start: Vec2;
1699
+ /** `along`: signed distance from its origin; `around`: the angle
1700
+ * (radians); free: undefined. */
1701
+ value: number | undefined;
1702
+ }
1703
+
1704
+ /**
1705
+ * One custom handle on a selected instance, in block-LOCAL coordinates
1706
+ * (pivot at the origin, before the instance's rotation / scale). Give it
1707
+ * ONE drag behaviour — `param` (the constrained value → a number param),
1708
+ * `input` (moves a point input), or `drag` (anything else) — or mark it
1709
+ * `fixed`; `click` makes it a button (a press without a move).
1710
+ */
1711
+ /**
1712
+ * A button in a handle's hover menu, or in the instance's context menu
1713
+ * (the block's top-level `actions`). One undo step.
1714
+ */
1715
+ export interface BlockAction<P = BlockParams, D = unknown, S = never> {
1716
+ label: string;
1717
+ icon?: HandleIcon;
1718
+ /** Pick a point first (snapping as usual); `run` receives it,
1719
+ * block-local, as `point`. Esc cancels. */
1720
+ pick?: "point";
1721
+ run: (s: {
1722
+ params: ParamValues<P>;
1723
+ inputs: D;
1724
+ point?: Vec2;
1725
+ }) => HandlePatch<P, S> | undefined;
1726
+ }
1727
+
1728
+ /** A field in a handle's hover menu: a parameter by name, or a field
1729
+ * bound to any value (one wall's thickness inside a `data` param), with
1730
+ * `set` returning what an edit changes. Each edit is one undo step. */
1731
+ export type MenuField<P = BlockParams, S = never> =
1732
+ | (keyof ParamValues<P> & string)
1733
+ | {
1734
+ /** The field: any value parameter row without name / default
1735
+ * (`{ type: "number", label: "Thickness", min: 50 }`). */
1736
+ field: Record<string, unknown> & { type: string; label?: string };
1737
+ value: unknown;
1738
+ set: (value: never) => HandlePatch<P, S> | undefined;
1739
+ };
1740
+
1741
+ interface HandleBase<P, D, S> {
1742
+ /** Stable within the instance (hover / drag continuity, tests). */
1743
+ id: string;
1744
+ /** Tooltip, and the hover menu's title. */
1745
+ label?: string;
1746
+ /** Not draggable (a button, or menu-only). */
1747
+ fixed?: boolean;
1748
+ /** The constrained value → this NUMBER param. */
1749
+ param?: NumberParamName<P>;
1750
+ drag?: (e: HandleDragEvent<P, D>) => HandlePatch<P, S> | undefined;
1751
+ /** Button behaviour: one undo step; the selection doesn't change. */
1752
+ click?: (s: {
1753
+ params: ParamValues<P>;
1754
+ inputs: D;
1755
+ }) => HandlePatch<P, S> | undefined;
1756
+ /** Fields shown in the hover menu (see MenuField). */
1757
+ menu?: readonly MenuField<P, S>[];
1758
+ /** Buttons in the hover menu. */
1759
+ actions?: readonly BlockAction<P, D, S>[];
1760
+ }
1761
+
1762
+ /**
1763
+ * One custom point handle on a selected instance, in block-LOCAL
1764
+ * coordinates (pivot at the origin, before the instance's rotation /
1765
+ * scale). Give it ONE drag behaviour — `param` (the constrained value →
1766
+ * a number param), `input` (moves a point input), or `drag` (anything
1767
+ * else) — or mark it `fixed`; `click` makes it a button.
1768
+ */
1769
+ export interface PointHandleDef<P = BlockParams, D = unknown, S = never>
1770
+ extends HandleBase<P, D, S> {
1771
+ kind?: "point";
1772
+ at: Vec2Like;
1773
+ shape?: HandleShape;
1774
+ /** Radians — points a triangle / arrow (default: the constraint axis). */
1775
+ direction?: number;
1776
+ /** Constrain to a line through `origin`; value = signed distance. */
1777
+ along?: {
1778
+ origin: Vec2Like;
1779
+ direction: Vec2Like;
1780
+ min?: number;
1781
+ max?: number;
1782
+ };
1783
+ /** Constrain to the circle through `at` around `center`; value =
1784
+ * the angle (radians). */
1785
+ around?: { center: Vec2Like };
1786
+ /** Move a point input: `"hinge"`, or one point of a list
1787
+ * (`{ name: "path", index: 2 }`). Replaces that point's default handle. */
1788
+ input?: string | { name: string; index: number };
1789
+ /** A lucide glyph drawn in the handle — the button look. */
1790
+ icon?: HandleIcon;
1791
+ }
1792
+
1793
+ /**
1794
+ * A line handle (a wall face, an offset): drawn while hovered, dragged
1795
+ * along its NORMAL by default (value = the signed offset, left of
1796
+ * from → to positive); `along` gives another direction.
1797
+ */
1798
+ export interface LineHandleDef<P = BlockParams, D = unknown, S = never>
1799
+ extends HandleBase<P, D, S> {
1800
+ kind: "line";
1801
+ from: Vec2Like;
1802
+ to: Vec2Like;
1803
+ along?: { direction: Vec2Like; min?: number; max?: number };
1804
+ }
1805
+
1806
+ export type HandleDef<P = BlockParams, D = unknown, S = never> =
1807
+ | PointHandleDef<P, D, S>
1808
+ | LineHandleDef<P, D, S>;
1809
+
1377
1810
  // ——— defineBlock ———
1378
1811
 
1379
1812
  /**
@@ -1423,6 +1856,7 @@ declare module "lineadraw" {
1423
1856
  export interface BlockSpec<
1424
1857
  P extends ParamDefs = readonly [],
1425
1858
  Pl extends PlaceSpec<P> | undefined = undefined,
1859
+ I extends InputDefs | undefined = undefined,
1426
1860
  > {
1427
1861
  id: string;
1428
1862
  name: string;
@@ -1447,16 +1881,58 @@ declare module "lineadraw" {
1447
1881
  paramVisibility?: (props: {
1448
1882
  params: ParamValues<P>;
1449
1883
  }) => Partial<Record<keyof ParamValues<P> & string, boolean>>;
1450
- /** Point labels (fixed picks) or an interactive function (see PlaceSpec). */
1451
- place?: Pl;
1884
+ /**
1885
+ * The block's declared inputs, by name — picked in table order at
1886
+ * insertion (`points` until Enter, `objects` from the selection when
1887
+ * it fits), and read in `draw` as `inputs.<name>`. Instances store
1888
+ * them by name, so reordering or adding inputs never breaks existing
1889
+ * instances. Replaces `place` labels.
1890
+ */
1891
+ inputs?: I;
1892
+ /**
1893
+ * Point labels (fixed picks) or an interactive function (see
1894
+ * PlaceSpec). With an `inputs` table, only the function form: it
1895
+ * returns the inputs by name (for conditional or chained picks).
1896
+ */
1897
+ place?: [I] extends [InputDefs]
1898
+ ? (props: PlaceProps<P>) => StoredInputs<I> | Promise<StoredInputs<I>>
1899
+ : Pl;
1452
1900
  /**
1453
1901
  * Optional inputs used for the block's PREVIEW (panel/Library
1454
1902
  * thumbnails, marketplace cards, save-time dry run) instead of the
1455
1903
  * 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.
1904
+ * at its best. Same shape `place` produces (by name with an
1905
+ * `inputs` table); points accept [x, y]; object-id strings resolve to
1906
+ * a stand-in line in previews.
1907
+ */
1908
+ previewInputs?: [I] extends [InputDefs] ? StoredInputs<I> : PreviewInputs<Pl>;
1909
+ /**
1910
+ * Custom handles on a selected instance — pure, block-local, re-run
1911
+ * whenever params / inputs change, so positions follow the values (a
1912
+ * width handle sits at the width). See HandleDef.
1913
+ */
1914
+ handles?: (props: {
1915
+ params: ParamValues<P>;
1916
+ inputs: [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>;
1917
+ /** Hosted blocks: the host's `expose` result (null when this block
1918
+ * has no host). Shape it as the host documents it. */
1919
+ host: any;
1920
+ /** Hosting: what lives on this instance — each one's `expose`. */
1921
+ hosted: readonly HostedBlock[];
1922
+ }) => readonly HandleDef<
1923
+ P,
1924
+ [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>,
1925
+ [I] extends [InputDefs] ? Partial<StoredInputs<I>> : never
1926
+ >[];
1927
+ /**
1928
+ * Actions in the instance's context menu (right-click) — e.g. "Add
1929
+ * window" with `pick: "point"`. Static records; one undo step each.
1458
1930
  */
1459
- previewInputs?: PreviewInputs<Pl>;
1931
+ actions?: readonly BlockAction<
1932
+ P,
1933
+ [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>,
1934
+ [I] extends [InputDefs] ? Partial<StoredInputs<I>> : never
1935
+ >[];
1460
1936
  /**
1461
1937
  * Children in block-LOCAL coordinates (pure, runs per evaluation).
1462
1938
  * The first point input is the pivot; `inputs` arrive localized
@@ -1466,12 +1942,51 @@ declare module "lineadraw" {
1466
1942
  */
1467
1943
  draw: (props: {
1468
1944
  params: ParamValues<P>;
1469
- inputs: PlaceInputs<Pl>;
1945
+ inputs: [I] extends [InputDefs] ? DrawInputs<I> : PlaceInputs<Pl>;
1470
1946
  /** The document's text / dimension styles by id — for blocks that
1471
1947
  * lay out with real text sizes. Pass a picked id on to children as
1472
1948
  * `styleId` to let them follow the style. */
1473
1949
  styles: BlockStyles;
1950
+ /** Hosted blocks: the host's `expose` result (null when this block
1951
+ * has no host). Shape it as the host documents it. */
1952
+ host: any;
1953
+ /** Hosting: what lives on this instance — each one's `expose`. */
1954
+ hosted: readonly HostedBlock[];
1474
1955
  }) => BlockChildInput[];
1956
+ /**
1957
+ * Hosting (blocks living on blocks — a door on a floor plan): JSON
1958
+ * this block PUBLISHES for others to read — a host its `walls`, a
1959
+ * door its `cut`. It sees only its own params, which is what keeps
1960
+ * a host and what lives on it from looping.
1961
+ */
1962
+ expose?: (props: { params: ParamValues<P> }) => JsonValue;
1963
+ /**
1964
+ * A hosted block (one with a `host` input): turns the click on the
1965
+ * host — host-local, with the host's exposed data — into this
1966
+ * instance's params (`{ wall: "B/1-2", at: 1200 }`).
1967
+ */
1968
+ attach?: (props: {
1969
+ host: any;
1970
+ point: Vec2;
1971
+ params: ParamValues<P>;
1972
+ }) => Partial<ParamValues<P>>;
1973
+ /**
1974
+ * A hosted block's stand-in host for previews, Library / marketplace
1975
+ * thumbnails and the save-time dry run: the JSON a real host's
1976
+ * `expose` would return. The preview places the block as if its host
1977
+ * were clicked at the origin (`attach`), and `draw` gets this as
1978
+ * `host`. Add a flag (`preview: true`) to draw extra context there,
1979
+ * e.g. a stretch of wall around a door.
1980
+ */
1981
+ previewHost?: JsonValue;
1982
+ }
1983
+
1984
+ /** One block living on a host, as the host's `draw` sees it. */
1985
+ export interface HostedBlock {
1986
+ id: string;
1987
+ definitionId: string;
1988
+ /** Its `expose` result (null without one). */
1989
+ exposed: any;
1475
1990
  }
1476
1991
 
1477
1992
  /**
@@ -1510,13 +2025,21 @@ declare module "lineadraw" {
1510
2025
  >(
1511
2026
  spec: BlockSpec<P, (props: PlaceProps<P>) => R>,
1512
2027
  ): BlockSpec<P, (props: PlaceProps<P>) => R>;
2028
+ // An `inputs` table: `draw`'s inputs are typed by name from it.
2029
+ export function defineBlock<
2030
+ const P extends ParamDefs = readonly [],
2031
+ const I extends InputDefs = readonly [],
2032
+ >(spec: BlockSpec<P, undefined, I>): BlockSpec<P, undefined, I>;
1513
2033
 
1514
2034
  // ——— Commands (macro scripts) ———
1515
2035
  // Drawing / EditorSurface / CommandContext are emitted from scriptTypes.ts
1516
2036
  // above (the script-facing mirror of core's real interfaces).
1517
2037
 
1518
2038
  /** One user command (macro): identity, metadata, and the `run` body. */
1519
- export interface CommandSpec<P extends ParamDefs = readonly []> {
2039
+ export interface CommandSpec<
2040
+ P extends ParamDefs = readonly [],
2041
+ I extends readonly string[] | CommandInputDefs = readonly [],
2042
+ > {
1520
2043
  id: string;
1521
2044
  name: string;
1522
2045
  description?: string;
@@ -1524,13 +2047,24 @@ declare module "lineadraw" {
1524
2047
  authors?: readonly string[];
1525
2048
  tags?: readonly string[];
1526
2049
  /**
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.
2050
+ * The inputs the command needs: an inputs TABLE
2051
+ * (`[{ name: "doors", type: "objects", types: ["block"] }]`, arriving
2052
+ * as `ctx.inputs.doors`), or point labels (`["Start", "End"]`,
2053
+ * arriving as `ctx.inputs` in order). In the editor they are acquired
2054
+ * before `run` — object inputs take the current selection when it
2055
+ * fits, the rest are picked; a headless or remote caller supplies them
2056
+ * up front (by name for a table). This is what lets one script run in
2057
+ * the editor, from a shell, and from an agent alike.
1532
2058
  */
1533
- inputs?: readonly string[];
2059
+ inputs?: I;
2060
+ /**
2061
+ * The command ASKS during the run (`editor.pickPoint`, `prompt`,
2062
+ * `ask` — the next question depends on the last answer), so it can't
2063
+ * run headless: CLI / agent / SDK runners refuse it up front instead of
2064
+ * failing halfway. Leave it off for commands whose `inputs` /
2065
+ * `params` cover everything.
2066
+ */
2067
+ requiresEditor?: boolean;
1534
2068
  /**
1535
2069
  * Parameters the command needs — the same table a block's `params`
1536
2070
  * declares (number / string / boolean / enum / label rows), or a
@@ -1546,7 +2080,7 @@ declare module "lineadraw" {
1546
2080
  * uncaught error rolls everything back. A return value is handed to
1547
2081
  * the caller (the agent tool / CLI) — the editor ignores it.
1548
2082
  */
1549
- run(ctx: CommandContext<ParamValues<P>>): unknown;
2083
+ run(ctx: CommandContext<ParamValues<P>, CommandInputs<I>>): unknown;
1550
2084
  }
1551
2085
 
1552
2086
  /**
@@ -1572,9 +2106,10 @@ declare module "lineadraw" {
1572
2106
  * },
1573
2107
  * });
1574
2108
  */
1575
- export function defineCommand<const P extends ParamDefs = readonly []>(
1576
- spec: CommandSpec<P>,
1577
- ): CommandSpec<P>;
2109
+ export function defineCommand<
2110
+ const P extends ParamDefs = readonly [],
2111
+ const I extends readonly string[] | CommandInputDefs = readonly [],
2112
+ >(spec: CommandSpec<P, I>): CommandSpec<P, I>;
1578
2113
  }
1579
2114
 
1580
2115
  declare module "lineadraw/helpers" {
@@ -1607,4 +2142,91 @@ declare module "lineadraw/helpers" {
1607
2142
  export const dot: (a: Vec2, b: Vec2) => number;
1608
2143
  /** Rotate vector by `angle` (radians, CCW). */
1609
2144
  export const rotate: (p: Vec2, angle: number) => Vec2;
2145
+ /** A point as scripts write it: [x, y] or { x, y }. */
2146
+ export type PointLike = readonly [number, number] | {
2147
+ readonly x: number;
2148
+ readonly y: number;
2149
+ };
2150
+ /** A polygon: one ring of points (implicitly closed), or several rings —
2151
+ * the first the outer boundary, the rest holes. */
2152
+ export type PolygonLike = readonly PointLike[] | readonly (readonly PointLike[])[];
2153
+ /** A result ring: closed implicitly (the first point is not repeated). */
2154
+ export type Ring = {
2155
+ x: number;
2156
+ y: number;
2157
+ }[];
2158
+ /**
2159
+ * The union of polygons: overlapping walls become one outline. Returns
2160
+ * every ring of the result — outer boundaries and holes alike. Draw them as
2161
+ * closed polylines, or hand them all to ONE hatch (`loops: rings.map((r) =>
2162
+ * ({ points: r }))`): hatches fill even-odd, so holes stay holes.
2163
+ */
2164
+ export const union: (...polygons: PolygonLike[]) => Ring[];
2165
+ /** `subject` minus every clip polygon (openings cut from a wall). Same
2166
+ * result rings as `union`. */
2167
+ export const difference: (subject: PolygonLike, ...clips: PolygonLike[]) => Ring[];
2168
+ /** Where ALL the polygons overlap. Same result rings as `union`. */
2169
+ export const intersection: (...polygons: PolygonLike[]) => Ring[];
2170
+ /** A point as scripts write it: [x, y] or { x, y }. */
2171
+ export type CurvePointLike = readonly [number, number] | {
2172
+ readonly x: number;
2173
+ readonly y: number;
2174
+ };
2175
+ /** A `spline` object input, ready for `document.add` or a block's `draw`. */
2176
+ export type SplineThrough = {
2177
+ type: "spline";
2178
+ degree: number;
2179
+ controlPoints: {
2180
+ x: number;
2181
+ y: number;
2182
+ }[];
2183
+ knots: number[];
2184
+ fitPoints: {
2185
+ x: number;
2186
+ y: number;
2187
+ }[];
2188
+ };
2189
+ /**
2190
+ * The smooth spline passing THROUGH `points` (in order) — what the Spline
2191
+ * tool draws. `degree` 3 (default) or 2; fewer points lower it. Throws on
2192
+ * fewer than two distinct points.
2193
+ */
2194
+ export const splineThrough: (points: readonly CurvePointLike[], degree?: number) => SplineThrough;
2195
+ /** A loop point: [x, y, bulge?] or { x, y, bulge? } — the bulge (tan of a
2196
+ * quarter of the sweep, as on a polyline) bends the edge to the NEXT point
2197
+ * into an arc. */
2198
+ export type LoopPointLike = readonly [number, number, number?] | {
2199
+ readonly x: number;
2200
+ readonly y: number;
2201
+ readonly bulge?: number;
2202
+ };
2203
+ /** A hatch loop of lines and arcs, ready for a hatch's `loops`. */
2204
+ export type HatchLoopEdges = {
2205
+ edges: ({
2206
+ kind: "line";
2207
+ a: {
2208
+ x: number;
2209
+ y: number;
2210
+ };
2211
+ b: {
2212
+ x: number;
2213
+ y: number;
2214
+ };
2215
+ } | {
2216
+ kind: "arc";
2217
+ center: {
2218
+ x: number;
2219
+ y: number;
2220
+ };
2221
+ radius: number;
2222
+ startAngle: number;
2223
+ endAngle: number;
2224
+ })[];
2225
+ };
2226
+ /**
2227
+ * The closed hatch loop through `points`: straight edges between them, an
2228
+ * arc where a point carries a bulge — the way to hatch a polygon or a
2229
+ * polyline outline (`loops: rings.map(hatchLoop)` for polygon booleans).
2230
+ */
2231
+ export const hatchLoop: (points: readonly LoopPointLike[]) => HatchLoopEdges;
1610
2232
  }