lineadraw 0.8.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/dist/sdk.js CHANGED
@@ -1,3 +1,10 @@
1
+ import {
2
+ BridgeError,
3
+ DEFAULT_BRIDGE_PORT,
4
+ bridgeFilePath,
5
+ connectEditors,
6
+ createBridge
7
+ } from "./chunk-L2CWZAKW.js";
1
8
  import {
2
9
  ADVANCE_TABLES,
3
10
  ANSI31_BASE_SPACING,
@@ -10,16 +17,17 @@ import {
10
17
  EXTRACTED_EXTRAS,
11
18
  EXTRACTED_RANGES,
12
19
  FONTS,
13
- HEX_COLOR,
14
20
  HatchPatternSchema,
15
21
  LineTypeSchema,
16
22
  MAX_HATCH_SEGMENTS,
17
23
  ModelObjectSchema,
24
+ PX_TO_MM,
18
25
  REAL_FACES,
19
26
  REFERENCE_FORMATS,
20
27
  TextStyleSchema,
21
28
  Vec2Schema,
22
29
  aciToHex,
30
+ acquireInputTable,
23
31
  add,
24
32
  angle,
25
33
  approximateTextMeasurer,
@@ -28,11 +36,14 @@ import {
28
36
  blockDefinitionExists,
29
37
  blockPivot,
30
38
  blockScriptId,
39
+ bytesToDataUrl,
40
+ checkNamedInputs,
31
41
  cleanRichText,
32
42
  cleanText,
33
43
  closestPointOnSegment,
34
44
  computeHatchLines,
35
45
  computePatternGeometry,
46
+ dataUrlBytes,
36
47
  decodeTextEscapes,
37
48
  dimFontFace,
38
49
  dimStyleFor,
@@ -40,12 +51,14 @@ import {
40
51
  dimensionRawToLabels,
41
52
  dimensionRawToOffset,
42
53
  dist,
54
+ documentBlocks,
43
55
  documentObjectResolver,
44
56
  dot,
45
- dryRunBlockInputs,
57
+ dryRunBlockPlacement,
46
58
  dryRunObjectResolver,
47
59
  effectiveTextFont,
48
60
  ellipseArcToBeziers,
61
+ embeddedAssetId,
49
62
  ensureScriptTranspiler,
50
63
  estimatePatternSegments,
51
64
  executeScriptModule,
@@ -59,17 +72,23 @@ import {
59
72
  getEvaluatedWorldChildren,
60
73
  getHatchPattern,
61
74
  getLineType,
75
+ hashAsset,
62
76
  hatchPatternKey,
63
77
  hexToRgb,
64
- imageCenter,
65
- imageCorners,
78
+ hostedClosure,
79
+ imagePixelSize,
80
+ inputLabel,
81
+ inputPoints,
82
+ inputRefs,
66
83
  invalidateBlockCache,
84
+ isRequiredInput,
67
85
  len,
68
86
  lineSegments,
69
87
  lineTypeKey,
70
88
  lineWeightToMm,
71
89
  linearAxis,
72
90
  listBlockMeta,
91
+ mapInputValues,
73
92
  matApply,
74
93
  matIdentity,
75
94
  matMultiply,
@@ -81,7 +100,9 @@ import {
81
100
  norm,
82
101
  normAngle,
83
102
  normalizeSpans,
103
+ paramMismatch,
84
104
  parseFontFace,
105
+ parseInputTable,
85
106
  parseParamsExport,
86
107
  perp,
87
108
  realFace,
@@ -96,7 +117,9 @@ import {
96
117
  rotateObject,
97
118
  scale,
98
119
  scaleObjectUniform,
120
+ scanScriptId,
99
121
  scriptTranspilerReady,
122
+ selectionFits,
100
123
  signedArcSweep,
101
124
  sub,
102
125
  tableLayout,
@@ -110,8 +133,9 @@ import {
110
133
  textStyleFor,
111
134
  textWidth,
112
135
  translateObject,
113
- valueParams
114
- } from "./chunk-VWED6V6F.js";
136
+ valueParams,
137
+ withEmptyLists
138
+ } from "./chunk-YYJAXJOY.js";
115
139
  import {
116
140
  embeddedPdfFonts
117
141
  } from "./chunk-L4FNH35X.js";
@@ -446,9 +470,10 @@ var LineaDocumentSchema = z2.preprocess(
446
470
  /** Document-defined hatch patterns (model/hatchPatterns.ts), same model. */
447
471
  hatchPatterns: z2.array(HatchPatternSchema).default([]),
448
472
  /**
449
- * Embedded binary assets for image objects: content hash → data URL.
450
- * Kept IN the document so the .linea file stays self-contained (the
451
- * cost is base64 in the JSON). Entries orphaned by deleted images just
473
+ * Embedded files (the bytes of `asset`-source references — PNG, JPEG,
474
+ * PDF, DXF, DWG, .linea): content hash → data URL. Kept IN the document
475
+ * so the .linea file stays self-contained (the cost is base64 in the
476
+ * JSON). Entries orphaned by deleted references just
452
477
  * ride along (no GC — deletion is undoable, so eager sweeps would break
453
478
  * redo).
454
479
  */
@@ -513,14 +538,6 @@ function emptyDocument() {
513
538
  });
514
539
  }
515
540
 
516
- // ../core/src/model/assets.ts
517
- var hashAsset = (data) => {
518
- let h = 5381;
519
- for (let i = 0; i < data.length; i++)
520
- h = (h << 5) + h + data.charCodeAt(i) | 0;
521
- return `${(h >>> 0).toString(16)}-${data.length.toString(16)}`;
522
- };
523
-
524
541
  // ../core/src/serialize.ts
525
542
  var documentToJson = (doc) => JSON.stringify(doc, null, 2);
526
543
  var documentFromJson = (text) => LineaDocumentSchema.parse(JSON.parse(text));
@@ -1240,7 +1257,9 @@ var paintChildren = (children, sink, env, parentLayerId) => {
1240
1257
  hatchPatterns: env.hatchPatterns,
1241
1258
  blockDefinitions: env.blockDefinitions,
1242
1259
  resolveObject: env.resolveObject,
1243
- assets: env.assets
1260
+ assets: env.assets,
1261
+ // A reference child (linked / embedded file) loads like any other.
1262
+ resolveReference: env.resolveReference
1244
1263
  }
1245
1264
  );
1246
1265
  }
@@ -1258,36 +1277,6 @@ var paintViewport = (obj, sink, env) => {
1258
1277
  sink.stroke({ color: env.style.color, widthMm: env.style.widthMm });
1259
1278
  };
1260
1279
 
1261
- // ../core/src/paint/painters/image.ts
1262
- var paintImage = (obj, sink, env) => {
1263
- const data = env.assets?.[obj.assetId];
1264
- if (data !== void 0 && !env.forceStyle) {
1265
- sink.image({
1266
- data,
1267
- origin: obj.origin,
1268
- widthMm: obj.width,
1269
- heightMm: obj.height,
1270
- rotation: obj.rotation,
1271
- opacity: env.style.opacity ?? 1
1272
- });
1273
- return;
1274
- }
1275
- const [a, b, c, d] = imageCorners(obj);
1276
- sink.beginPath();
1277
- sink.moveTo(a);
1278
- sink.lineTo(b);
1279
- sink.lineTo(c);
1280
- sink.lineTo(d);
1281
- sink.closePath();
1282
- if (data === void 0) {
1283
- sink.moveTo(a);
1284
- sink.lineTo(c);
1285
- sink.moveTo(b);
1286
- sink.lineTo(d);
1287
- }
1288
- sink.stroke(env.style);
1289
- };
1290
-
1291
1280
  // ../core/src/paint/painters/table.ts
1292
1281
  var paintTable = (obj, sink, env) => paintChildren(tableLayout(obj, env.textStyles).children, sink, env);
1293
1282
 
@@ -1706,10 +1695,8 @@ var buildEntry = (obj, ctx) => {
1706
1695
  };
1707
1696
  }
1708
1697
  case "block": {
1709
- const inputPoints = obj.inputs.filter(
1710
- (v) => typeof v !== "string"
1711
- );
1712
- const points = (inputPoints.length > 0 ? inputPoints : [blockPivot(obj)]).map((p) => ({ p, type: "endpoint" }));
1698
+ const pts = inputPoints(obj.inputs);
1699
+ const points = (pts.length > 0 ? pts : inputRefs(obj.inputs).length > 0 ? [] : [blockPivot(obj)]).map((p) => ({ p, type: "endpoint" }));
1713
1700
  const segments = [];
1714
1701
  const arcs = [];
1715
1702
  const ellipses = [];
@@ -1757,26 +1744,6 @@ var buildEntry = (obj, ctx) => {
1757
1744
  rings: [outline]
1758
1745
  };
1759
1746
  }
1760
- case "image": {
1761
- const corners = imageCorners(obj);
1762
- const segments = corners.map((c, i) => ({
1763
- a: c,
1764
- b: corners[(i + 1) % corners.length]
1765
- }));
1766
- const points = corners.map((p) => ({
1767
- p,
1768
- type: "endpoint"
1769
- }));
1770
- for (const s of segments)
1771
- points.push({ p: mid(s.a, s.b), type: "midpoint" });
1772
- points.push({ p: imageCenter(obj), type: "center" });
1773
- return {
1774
- bounds: boundsOfPoints(corners),
1775
- points,
1776
- segments,
1777
- arcs: []
1778
- };
1779
- }
1780
1747
  case "comment": {
1781
1748
  const bounds = emptyBounds();
1782
1749
  addPointToBounds(bounds, obj.position);
@@ -4300,10 +4267,9 @@ var convertAcadTable = (e, ctx) => {
4300
4267
  const cell = cells[r * cols + c];
4301
4268
  const raw = cell.text || cell.display || "";
4302
4269
  const a = cell.alignment;
4303
- const rich = cleanRichText(
4304
- raw,
4305
- cell.height && cell.height > 0 ? cell.height : 2.5
4306
- );
4270
+ const inlineHeight = Number(/\\H(\d+(?:\.\d+)?);/.exec(raw)?.[1]);
4271
+ const height = cell.height && cell.height > 0 ? cell.height : inlineHeight > 0 ? inlineHeight : void 0;
4272
+ const rich = cleanRichText(raw, height ?? 2.5);
4307
4273
  const text = rich.content.replace(/\s+$/, "");
4308
4274
  const spans = normalizeSpans(rich.spans, text).filter(
4309
4275
  (s) => s.start > 0 || s.end < text.length
@@ -4317,7 +4283,7 @@ var convertAcadTable = (e, ctx) => {
4317
4283
  vAlign: a <= 3 ? "top" : a <= 6 ? "center" : "bottom"
4318
4284
  } : {},
4319
4285
  styleOverride: {
4320
- ...cell.height && cell.height > 0 ? { textHeight: cell.height * f } : {},
4286
+ ...height !== void 0 ? { textHeight: height * f } : {},
4321
4287
  ...mtextFontFlags(raw)
4322
4288
  }
4323
4289
  };
@@ -4985,7 +4951,7 @@ var dxfToModel = (text, input) => {
4985
4951
 
4986
4952
  // ../core/src/reference/reference.ts
4987
4953
  var rebaseReferenceSource = (child, parent) => {
4988
- if (child.kind === "url" || parent.kind === "url") return child;
4954
+ if (child.kind !== "path" || parent.kind !== "path") return child;
4989
4955
  if (/^([A-Za-z]:[\\/]|[\\/])/.test(child.path)) return child;
4990
4956
  const folder = parent.path.replace(/\\/g, "/").replace(/\/?[^/]*$/, "");
4991
4957
  return { ...child, path: folder ? `${folder}/${child.path}` : child.path };
@@ -5028,7 +4994,10 @@ var contentFromDocument = (doc, refId, bounds = emptyBounds()) => {
5028
4994
  (o) => o.type === "block" ? {
5029
4995
  ...o,
5030
4996
  id: prefix + o.id,
5031
- inputs: o.inputs.map((v) => typeof v === "string" ? prefix + v : v)
4997
+ inputs: mapInputValues(
4998
+ o.inputs,
4999
+ (v) => typeof v === "string" ? prefix + v : v
5000
+ )
5032
5001
  } : { ...o, id: prefix + o.id }
5033
5002
  );
5034
5003
  const byId = new Map(objects.map((o) => [o.id, o]));
@@ -5090,39 +5059,43 @@ var documentFromDxf = (text) => documentWithObjects((doc) => {
5090
5059
  ]
5091
5060
  };
5092
5061
  });
5093
- var documentFromImage = (dataUrl, widthMm, heightMm) => {
5094
- const doc = emptyDocument();
5095
- const assetId = hashAsset(dataUrl);
5096
- doc.assets = { [assetId]: dataUrl };
5097
- doc.objects = [
5098
- {
5099
- id: "image",
5100
- type: "image",
5101
- layerId: doc.layers[0].id,
5102
- assetId,
5103
- origin: { x: 0, y: 0 },
5104
- width: widthMm,
5105
- height: heightMm,
5106
- rotation: 0
5107
- }
5062
+ var rasterSnap = (w, h, layer) => {
5063
+ const corners = [
5064
+ { x: 0, y: 0 },
5065
+ { x: w, y: 0 },
5066
+ { x: w, y: h },
5067
+ { x: 0, y: h }
5108
5068
  ];
5109
- return doc;
5069
+ return {
5070
+ points: [
5071
+ ...corners.map((p) => ({ p, type: "endpoint", layer })),
5072
+ ...corners.map((p, i) => {
5073
+ const q = corners[(i + 1) % 4];
5074
+ return {
5075
+ p: { x: (p.x + q.x) / 2, y: (p.y + q.y) / 2 },
5076
+ type: "midpoint",
5077
+ layer
5078
+ };
5079
+ }),
5080
+ { p: { x: w / 2, y: h / 2 }, type: "center", layer }
5081
+ ],
5082
+ segments: corners.map((a, i) => ({ a, b: corners[(i + 1) % 4], layer })),
5083
+ arcs: [],
5084
+ ellipses: []
5085
+ };
5110
5086
  };
5111
- var imagePixelSize = (b) => {
5112
- const u32 = (i) => (b[i] << 24 | b[i + 1] << 16 | b[i + 2] << 8 | b[i + 3]) >>> 0;
5113
- const u16 = (i) => b[i] << 8 | b[i + 1];
5114
- if (b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71)
5115
- return { width: u32(16), height: u32(20) };
5116
- if (b[0] !== 255 || b[1] !== 216) return null;
5117
- for (let i = 2; i + 9 < b.length; ) {
5118
- if (b[i] !== 255) return null;
5119
- const marker = b[i + 1];
5120
- const len2 = u16(i + 2);
5121
- if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204)
5122
- return { width: u16(i + 7), height: u16(i + 5) };
5123
- i += 2 + len2;
5124
- }
5125
- return null;
5087
+ var contentFromRaster = (refId, data, width, height) => {
5088
+ const content = contentFromDocument(emptyDocument(), refId, {
5089
+ minX: 0,
5090
+ minY: 0,
5091
+ maxX: width,
5092
+ maxY: height
5093
+ });
5094
+ return {
5095
+ ...content,
5096
+ raster: { data, width, height },
5097
+ snap: rasterSnap(width, height, content.layers[0].id)
5098
+ };
5126
5099
  };
5127
5100
  var measureContentBounds = (content, measure) => {
5128
5101
  const ctx = {
@@ -5266,6 +5239,26 @@ var paintReference = (obj, sink, env) => {
5266
5239
  w: clip.maxX - clip.minX,
5267
5240
  h: clip.maxY - clip.minY
5268
5241
  });
5242
+ const raster = content.raster;
5243
+ if (raster) {
5244
+ if (env.forceStyle) {
5245
+ out.beginPath();
5246
+ out.moveTo({ x: 0, y: 0 });
5247
+ out.lineTo({ x: raster.width, y: 0 });
5248
+ out.lineTo({ x: raster.width, y: raster.height });
5249
+ out.lineTo({ x: 0, y: raster.height });
5250
+ out.closePath();
5251
+ out.stroke(env.style);
5252
+ } else
5253
+ out.image({
5254
+ data: raster.data,
5255
+ origin: { x: 0, y: 0 },
5256
+ widthMm: raster.width,
5257
+ heightMm: raster.height,
5258
+ rotation: 0,
5259
+ opacity: fade
5260
+ });
5261
+ }
5269
5262
  for (const child of content.objects) {
5270
5263
  if (shown.get(child.layerId) === false) continue;
5271
5264
  const own = resolveStyle(
@@ -5345,7 +5338,6 @@ var painters = {
5345
5338
  dimension: paintDimension,
5346
5339
  block: paintBlockInstance,
5347
5340
  viewport: paintViewport,
5348
- image: paintImage,
5349
5341
  table: paintTable,
5350
5342
  reference: paintReference,
5351
5343
  // Screen-fixed editor chrome — painted by the editor's canvas renderer
@@ -5366,7 +5358,7 @@ var renderBlockThumbnailSvg = async (script, {
5366
5358
  } = {}) => {
5367
5359
  await ensureScriptTranspiler();
5368
5360
  const defId = getBlockMeta(script).id;
5369
- const inputs = await dryRunBlockInputs(script);
5361
+ const { inputs, params } = await dryRunBlockPlacement(script);
5370
5362
  const defs = [script];
5371
5363
  const present = /* @__PURE__ */ new Set([defId]);
5372
5364
  for (const extra of definitions) {
@@ -5382,7 +5374,7 @@ var renderBlockThumbnailSvg = async (script, {
5382
5374
  definitionId: defId,
5383
5375
  rotation: 0,
5384
5376
  scale: 1,
5385
- params: {},
5377
+ params,
5386
5378
  inputs
5387
5379
  };
5388
5380
  const env = {
@@ -5548,8 +5540,23 @@ var OptionalMetaSchema = z3.object({
5548
5540
  version: z3.string().optional(),
5549
5541
  authors: z3.array(z3.string()).optional(),
5550
5542
  tags: z3.array(z3.string()).optional(),
5551
- inputs: z3.array(z3.string().min(1)).optional()
5543
+ requiresEditor: z3.boolean().optional()
5552
5544
  });
5545
+ var isInputTable = (inputs) => inputs !== void 0 && inputs.length > 0 && typeof inputs[0] !== "string";
5546
+ var parseCommandInputs = (raw, params) => {
5547
+ if (raw === void 0) return void 0;
5548
+ if (Array.isArray(raw) && raw.every((l) => typeof l === "string")) {
5549
+ if (raw.some((l) => l === ""))
5550
+ throw new Error("Invalid command metadata \u2014 inputs: empty label");
5551
+ return raw;
5552
+ }
5553
+ const table = parseInputTable(raw, params);
5554
+ if (table?.some((d) => d.type === "host"))
5555
+ throw new Error(
5556
+ "Invalid command metadata \u2014 inputs: `host` is a block input (a command has no frame to live in)"
5557
+ );
5558
+ return table;
5559
+ };
5553
5560
  var extractCommandMeta = (mod) => {
5554
5561
  if (typeof mod.id !== "string" || mod.id.trim() === "")
5555
5562
  throw new Error("Command script must export a non-empty string `id`");
@@ -5563,10 +5570,12 @@ var extractCommandMeta = (mod) => {
5563
5570
  );
5564
5571
  }
5565
5572
  const params = parseParamsExport(mod.params);
5573
+ const inputs = parseCommandInputs(mod.inputs, params);
5566
5574
  return {
5567
5575
  id: mod.id,
5568
5576
  name: mod.name,
5569
5577
  ...parsed.data,
5578
+ ...inputs ? { inputs } : {},
5570
5579
  ...params.length > 0 ? { params } : {}
5571
5580
  };
5572
5581
  };
@@ -5605,16 +5614,11 @@ var getCommandMetaResult = (script) => {
5605
5614
  };
5606
5615
  var listCommandMeta = (scripts) => scripts.map(getCommandMetaResult);
5607
5616
  var getCommandRun = (script) => compileCommand(script).run;
5608
- var ID_EXPORT_RE = /export\s+const\s+id\s*=\s*["']([^"']+)["']/;
5609
5617
  var commandScriptId = (script) => {
5610
5618
  try {
5611
5619
  return compileCommand(script).meta.id;
5612
5620
  } catch {
5613
- const named = ID_EXPORT_RE.exec(script);
5614
- if (named) return named[1];
5615
- const call = script.search(/defineCommand\s*\(|export\s+default\s*\{/);
5616
- if (call === -1) return void 0;
5617
- return /\bid\s*:\s*["']([^"']+)["']/.exec(script.slice(call))?.[1];
5621
+ return scanScriptId(script, /defineCommand\s*\(|export\s+default\s*\{/);
5618
5622
  }
5619
5623
  };
5620
5624
  var mergeCommandModules = (existing, incoming) => mergeScriptModules(existing, incoming, {
@@ -8012,7 +8016,7 @@ var applySetAsset = (doc, op) => {
8012
8016
  };
8013
8017
  };
8014
8018
  var assetInUse = (doc, id) => {
8015
- const used = (objs) => objs.some((o) => o.type === "image" && o.assetId === id);
8019
+ const used = (objs) => objs.some((o) => embeddedAssetId(o) === id);
8016
8020
  return used(doc.objects) || doc.layouts.some((l) => used(l.objects));
8017
8021
  };
8018
8022
  var applyRemoveAsset = (doc, op) => {
@@ -8193,7 +8197,9 @@ var invalidationIds = (op) => {
8193
8197
  switch (op.type) {
8194
8198
  case "add-objects":
8195
8199
  case "update-objects":
8196
- return op.objects.map((o) => o.id);
8200
+ return op.objects.flatMap(
8201
+ (o) => o.type === "block" ? [o.id, ...inputRefs(o.inputs)] : [o.id]
8202
+ );
8197
8203
  case "remove-objects":
8198
8204
  return op.ids;
8199
8205
  case "set-current-layer":
@@ -8688,8 +8694,9 @@ var LocalDrawing = class {
8688
8694
  }
8689
8695
  // --- document meta / assets ------------------------------------------------
8690
8696
  /**
8691
- * Embed image pixels (a data URL) and return the assetId that `image`
8692
- * objects reference. Content-keyed: the same data reuses one entry.
8697
+ * Embed a file (a data URL — PNG, JPEG, PDF…) and return the assetId an
8698
+ * embedded reference's `source: { kind: "asset", assetId }` names.
8699
+ * Content-keyed: the same data reuses one entry.
8693
8700
  */
8694
8701
  addAsset(dataUrl) {
8695
8702
  const id = hashAsset(dataUrl);
@@ -8697,7 +8704,7 @@ var LocalDrawing = class {
8697
8704
  this.applyForward({ type: "set-asset", id, dataUrl });
8698
8705
  return id;
8699
8706
  }
8700
- /** Remove an asset (the kernel refuses while an image references it). */
8707
+ /** Remove an asset (the kernel refuses while a reference embeds it). */
8701
8708
  removeAsset(id) {
8702
8709
  this.applyForward({ type: "remove-asset", id });
8703
8710
  }
@@ -8707,9 +8714,10 @@ var LocalDrawing = class {
8707
8714
  this.state.objects,
8708
8715
  ...this.state.layouts.map((l) => l.objects)
8709
8716
  ])
8710
- for (const o of objs)
8711
- if (o.type === "image")
8712
- users.set(o.assetId, (users.get(o.assetId) ?? 0) + 1);
8717
+ for (const o of objs) {
8718
+ const id = embeddedAssetId(o);
8719
+ if (id !== void 0) users.set(id, (users.get(id) ?? 0) + 1);
8720
+ }
8713
8721
  return Object.entries(this.state.assets).map(([id, dataUrl]) => {
8714
8722
  const comma = dataUrl.indexOf(",");
8715
8723
  const base64 = dataUrl.slice(comma + 1);
@@ -8748,7 +8756,8 @@ var LocalDrawing = class {
8748
8756
  return this.importDxfText(await dwgToDxf(data), opts);
8749
8757
  }
8750
8758
  /** Merge a PDF: its vector/text content onto the current layer, or
8751
- * (`as: "image"`) every page as an image at true page scale. */
8759
+ * (`as: "image"`) embedded, every page a raster reference at true page
8760
+ * scale. */
8752
8761
  async importPdfData(data, opts = {}) {
8753
8762
  if (opts.as === "image") {
8754
8763
  if (!this.rasterizePdf)
@@ -8758,15 +8767,20 @@ var LocalDrawing = class {
8758
8767
  const pages = await this.rasterizePdf(data);
8759
8768
  const at = toVec(opts.origin ?? [0, 0]);
8760
8769
  const layerId2 = this.state.currentLayerId;
8770
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
8771
+ const assetId = this.addAsset(bytesToDataUrl(bytes, "application/pdf"));
8761
8772
  let x = at.x;
8762
- const placed = pages.map((p) => {
8773
+ const placed = pages.map((p, i) => {
8763
8774
  const object = {
8764
- type: "image",
8775
+ type: "reference",
8765
8776
  layerId: layerId2,
8766
- assetId: this.addAsset(p.dataUrl),
8767
- origin: { x, y: at.y },
8768
- width: p.widthMm,
8769
- height: p.heightMm
8777
+ format: "pdf",
8778
+ name: pages.length > 1 ? `PDF page ${i + 1}` : "PDF",
8779
+ source: { kind: "asset", assetId },
8780
+ pdfPage: i + 1,
8781
+ pdfMode: "raster",
8782
+ position: { x, y: at.y },
8783
+ extent: { minX: 0, minY: 0, maxX: p.widthMm, maxY: p.heightMm }
8770
8784
  };
8771
8785
  x += p.widthMm + PDF_PAGE_GAP_MM;
8772
8786
  return object;
@@ -8952,10 +8966,6 @@ var LocalDrawing = class {
8952
8966
  id: this.resolveStyle(this.state.dimStyles, nameOrId, "dim style").id
8953
8967
  });
8954
8968
  }
8955
- /** @deprecated Use updateDimStyle (default here: the first style). */
8956
- setDimStyle(patch, styleId) {
8957
- this.updateDimStyle(styleId ?? this.state.dimStyles[0].id, patch);
8958
- }
8959
8969
  /** Replace the document's dimension styles (min 1; objects reference by
8960
8970
  * id). Diffed into granular per-style ops. */
8961
8971
  setDimStyles(styles) {
@@ -8963,10 +8973,6 @@ var LocalDrawing = class {
8963
8973
  assertUniqueIds(parsed, "setDimStyles()");
8964
8974
  this.applyForwardAll(diffDimStyleOps(this.state.dimStyles, parsed));
8965
8975
  }
8966
- /** @deprecated Use updateTextStyle (default here: the first style). */
8967
- setTextStyle(patch, styleId) {
8968
- this.updateTextStyle(styleId ?? this.state.textStyles[0].id, patch);
8969
- }
8970
8976
  /** Replace the document's text styles (min 1; objects reference by id).
8971
8977
  * Diffed into granular per-style ops. */
8972
8978
  setTextStyles(styles) {
@@ -9334,7 +9340,12 @@ var LocalDrawing = class {
9334
9340
  /** Delete objects by id (unknown ids are skipped); returns how many went. */
9335
9341
  remove(ids) {
9336
9342
  const entries = [];
9337
- for (const id of ids) {
9343
+ const all = hostedClosure(
9344
+ ids,
9345
+ this.state.blockDefinitions,
9346
+ documentBlocks(this.state)
9347
+ );
9348
+ for (const id of all) {
9338
9349
  try {
9339
9350
  entries.push({ space: this.findObject(id).space, item: id });
9340
9351
  } catch {
@@ -9469,6 +9480,7 @@ var LocalDrawing = class {
9469
9480
  return {
9470
9481
  dimStyles: this.state.dimStyles,
9471
9482
  textStyles: this.state.textStyles,
9483
+ layers: this.state.layers,
9472
9484
  blockDefinitions: this.state.blockDefinitions,
9473
9485
  resolveObject: this.resolveObject(),
9474
9486
  resolveReference: this.resolveReference,
@@ -9635,19 +9647,17 @@ var LocalDrawing = class {
9635
9647
  }
9636
9648
  /** Export the document to a DXF string. */
9637
9649
  async toDxf() {
9638
- const { buildDxf } = await import("./exportDxf-2VU5475O.js");
9650
+ const { buildDxf } = await import("./exportDxf-C4CLQCC3.js");
9639
9651
  return buildDxf(this.state, this.measurer).content;
9640
9652
  }
9641
9653
  /** Export the document to DWG bytes (the DXF content, wasm-transcoded).
9642
9654
  * Defaults to AC1027 (AutoCAD 2013) — readable by every current CAD tool.
9643
- * Dimensions transcode as real DIMENSION objects; leaders are exploded to
9644
- * plain geometry (acadrust's MULTILEADER DWG output is unproven in real
9645
- * CADs — exploded geometry always displays). */
9655
+ * Dimensions transcode as real DIMENSION objects; leaders and tables are
9656
+ * exploded to plain geometry (DWG_DXF_OPTIONS — the transcoder's DWG
9657
+ * output for them is not accepted by AutoCAD). */
9646
9658
  async toDwg(opts) {
9647
- const { buildDxf } = await import("./exportDxf-2VU5475O.js");
9648
- const dxf = buildDxf(this.state, this.measurer, {
9649
- explodeLeaders: true
9650
- }).content;
9659
+ const { buildDxf, DWG_DXF_OPTIONS } = await import("./exportDxf-C4CLQCC3.js");
9660
+ const dxf = buildDxf(this.state, this.measurer, DWG_DXF_OPTIONS).content;
9651
9661
  return dxfToDwg(dxf, opts?.version ?? "AC1027");
9652
9662
  }
9653
9663
  toLinea() {
@@ -9656,66 +9666,15 @@ var LocalDrawing = class {
9656
9666
  };
9657
9667
 
9658
9668
  // ../core/src/drawing/runCommand.ts
9659
- var needsEditor = (what) => () => {
9660
- throw new Error(`${what} needs an editor (this command runs headlessly)`);
9661
- };
9662
9669
  var makeContext = (opts, inputs, params) => {
9663
9670
  const { document, editor } = opts;
9664
9671
  return {
9665
9672
  document,
9666
9673
  editor,
9667
9674
  inputs,
9668
- params,
9669
- // Deprecated shorthands (pre-`editor` wire form) — one release.
9670
- pickPoint: editor ? (label) => editor.pickPoint({ label }) : needsEditor("pickPoint"),
9671
- pickObject: editor ? (label) => editor.pickObject({ label }) : needsEditor("pickObject"),
9672
- selection: editor ? () => editor.state().selectedIds : needsEditor("selection"),
9673
- setSelection: editor ? (ids) => editor.setSelection(ids) : needsEditor("setSelection"),
9674
- prompt: editor ? (message, o) => editor.prompt(message, o) : needsEditor("prompt"),
9675
- showToast: editor ? (message, kind) => editor.toast(message, kind) : () => {
9676
- }
9675
+ params
9677
9676
  };
9678
9677
  };
9679
- var paramMismatch = (def, value) => {
9680
- switch (def.type) {
9681
- case "number":
9682
- if (typeof value !== "number" || !Number.isFinite(value))
9683
- return "expected a number";
9684
- if (def.integer && !Number.isInteger(value))
9685
- return "expected a whole number";
9686
- if (def.min !== void 0 && value < def.min)
9687
- return `below the minimum ${def.min}`;
9688
- if (def.max !== void 0 && value > def.max)
9689
- return `above the maximum ${def.max}`;
9690
- return null;
9691
- case "string":
9692
- case "textStyle":
9693
- case "dimStyle":
9694
- case "font":
9695
- case "lineType":
9696
- case "hatchPattern":
9697
- return typeof value === "string" ? null : "expected a string";
9698
- case "boolean":
9699
- return typeof value === "boolean" ? null : "expected true or false";
9700
- case "numberList": {
9701
- if (!Array.isArray(value) || !value.every((v) => typeof v === "number" && Number.isFinite(v)))
9702
- return "expected an array of numbers";
9703
- const low = value.find((v) => def.min !== void 0 && v < def.min);
9704
- if (low !== void 0) return `${low} is below the minimum ${def.min}`;
9705
- const high = value.find((v) => def.max !== void 0 && v > def.max);
9706
- if (high !== void 0) return `${high} is above the maximum ${def.max}`;
9707
- return null;
9708
- }
9709
- case "color":
9710
- return typeof value === "string" && HEX_COLOR.test(value) ? null : 'expected a "#rrggbb" color';
9711
- case "stringList":
9712
- return Array.isArray(value) && value.every((v) => typeof v === "string") ? null : "expected an array of strings";
9713
- case "enum":
9714
- return def.options.some((o) => o.value === value) ? null : `expected one of ${def.options.map((o) => JSON.stringify(o.value)).join(", ")}`;
9715
- default:
9716
- return "a label carries no value";
9717
- }
9718
- };
9719
9678
  var resolveCommandParams = (meta, given = {}) => {
9720
9679
  const defs = valueParams(meta.params ?? []);
9721
9680
  const params = /* @__PURE__ */ Object.create(null);
@@ -9733,8 +9692,57 @@ var resolveCommandParams = (meta, given = {}) => {
9733
9692
  }
9734
9693
  return params;
9735
9694
  };
9695
+ var surfacePickers = (editor, document) => {
9696
+ let selectionUsed = false;
9697
+ const fromSelection = (def) => {
9698
+ if (selectionUsed) return null;
9699
+ const selected = editor.state().selectedIds.map((id) => document.getObject(id)).filter((o) => o !== void 0);
9700
+ if (!selectionFits(def, selected)) return null;
9701
+ selectionUsed = true;
9702
+ return selected.map((o) => o.id);
9703
+ };
9704
+ return {
9705
+ point: (def, ref) => editor.pickPoint({ label: inputLabel(def), referencePoint: ref }),
9706
+ points: (def, ref) => editor.pickPoints({ ...def, referencePoint: ref }),
9707
+ object: async (def) => fromSelection({ types: def.types, min: 1, max: 1 })?.[0] ?? editor.pickObject({ label: inputLabel(def), types: def.types }),
9708
+ objects: async (def) => fromSelection(def) ?? editor.pickObjects(def)
9709
+ };
9710
+ };
9711
+ var acquireNamedCommandInputs = async (meta, table, opts) => {
9712
+ const { inputs, missing } = checkNamedInputs(
9713
+ meta.name,
9714
+ table,
9715
+ opts.inputs,
9716
+ (id) => opts.document.getObject(id)
9717
+ );
9718
+ if (!opts.editor) {
9719
+ const required = missing.filter(isRequiredInput);
9720
+ if (required.length > 0)
9721
+ throw new Error(
9722
+ `${meta.name} needs an editor for: ${required.map((d) => d.name).join(", ")} \u2014 supply inputs`
9723
+ );
9724
+ return withEmptyLists(table, inputs);
9725
+ }
9726
+ if (missing.length === 0) return inputs;
9727
+ const picked = await acquireInputTable(
9728
+ missing,
9729
+ surfacePickers(opts.editor, opts.document)
9730
+ );
9731
+ const out = {};
9732
+ for (const def of table) {
9733
+ const v = inputs[def.name] ?? picked[def.name];
9734
+ if (v !== void 0) out[def.name] = v;
9735
+ }
9736
+ return out;
9737
+ };
9736
9738
  var acquireCommandInputs = async (meta, opts) => {
9739
+ if (isInputTable(meta.inputs))
9740
+ return acquireNamedCommandInputs(meta, meta.inputs, opts);
9737
9741
  const labels = meta.inputs ?? [];
9742
+ if (opts.inputs !== void 0 && !Array.isArray(opts.inputs))
9743
+ throw new Error(
9744
+ `${meta.name}: inputs are positional (${labels.join(", ") || "none declared"}) \u2014 pass an array`
9745
+ );
9738
9746
  const given = (opts.inputs ?? []).map((p, i) => {
9739
9747
  try {
9740
9748
  return Vec2Schema.parse(p);
@@ -9766,11 +9774,29 @@ var runCommandModule = async (source, opts) => {
9766
9774
  const result = getCommandMetaResult(source);
9767
9775
  if (!result.ok) throw new Error(`Command failed to compile: ${result.error}`);
9768
9776
  const run = getCommandRun(source);
9777
+ if (result.meta.requiresEditor && !opts.editor)
9778
+ throw new Error(
9779
+ `${result.meta.name} needs the editor (it asks during the run) \u2014 run it from Linea`
9780
+ );
9769
9781
  const params = resolveCommandParams(result.meta, opts.params);
9770
9782
  const inputs = await acquireCommandInputs(result.meta, opts);
9771
- return opts.document.transaction(
9772
- () => run(makeContext(opts, inputs, params)),
9773
- result.meta.name
9783
+ try {
9784
+ return await opts.document.transaction(
9785
+ () => run(makeContext(opts, inputs, params)),
9786
+ result.meta.name
9787
+ );
9788
+ } catch (e) {
9789
+ throw opts.editor ? e : namedEditorError(result.meta.name, e);
9790
+ }
9791
+ };
9792
+ var namedEditorError = (name, e) => {
9793
+ const m = e instanceof TypeError ? /reading '(pickPoint|pickPoints|pickObject|pickObjects|prompt|ask|toast|state|setSelection|setActiveSpace|zoom|listCommands|runCommand)'/.exec(
9794
+ e.message
9795
+ ) : null;
9796
+ if (!m) return e;
9797
+ return new Error(
9798
+ `${name} used editor.${m[1]} but runs without an editor here \u2014 check \`editor\` before using it, or declare \`requiresEditor: true\``,
9799
+ { cause: e }
9774
9800
  );
9775
9801
  };
9776
9802
  var runDocumentCommand = async (id, opts) => {
@@ -9824,18 +9850,30 @@ var referenceDocument = async (ref, bytes) => {
9824
9850
  }));
9825
9851
  }
9826
9852
  case "png":
9827
- case "jpg": {
9828
- const size = imagePixelSize(bytes);
9829
- if (!size) throw new Error(`${ref.name}: not a PNG/JPEG`);
9830
- const mime = ref.format === "png" ? "image/png" : "image/jpeg";
9831
- const px = 25.4 / 96;
9832
- return documentFromImage(
9833
- `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`,
9834
- size.width * px,
9835
- size.height * px
9836
- );
9837
- }
9853
+ case "jpg":
9854
+ throw new Error("pictures load as raster content");
9855
+ }
9856
+ };
9857
+ var referenceContent = async (ref, bytes) => {
9858
+ if (ref.format === "png" || ref.format === "jpg") {
9859
+ const size = imagePixelSize(bytes);
9860
+ if (!size) throw new Error(`${ref.name}: not a PNG/JPEG`);
9861
+ const mime = ref.format === "png" ? "image/png" : "image/jpeg";
9862
+ return contentFromRaster(
9863
+ ref.id,
9864
+ `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`,
9865
+ size.width * PX_TO_MM,
9866
+ size.height * PX_TO_MM
9867
+ );
9838
9868
  }
9869
+ const content = contentFromDocument(
9870
+ await referenceDocument(ref, bytes),
9871
+ ref.id
9872
+ );
9873
+ return {
9874
+ ...content,
9875
+ bounds: measureContentBounds(content, approximateTextMeasurer)
9876
+ };
9839
9877
  };
9840
9878
  var Drawing = class extends LocalDrawing {
9841
9879
  path;
@@ -9873,22 +9911,31 @@ var Drawing = class extends LocalDrawing {
9873
9911
  ...this.layouts.flatMap((l) => l.objects)
9874
9912
  ].filter((o) => o.type === "reference");
9875
9913
  for (const ref of refs) {
9876
- const content = await this.loadReference(ref, ref.source, 0, /* @__PURE__ */ new Set());
9914
+ const content = await this.loadReference(
9915
+ ref,
9916
+ ref.source,
9917
+ this.state.assets,
9918
+ 0,
9919
+ /* @__PURE__ */ new Set()
9920
+ );
9877
9921
  if (content) this.referenceContent.set(ref.id, content);
9878
9922
  else this.referenceContent.delete(ref.id);
9879
9923
  }
9880
9924
  }
9881
9925
  /** One reference's content (null when unreadable); a .linea's own
9882
- * references load relative to it — depth-limited, never re-entering a
9883
- * file above in the chain (the editor's rules). */
9884
- async loadReference(ref, source, depth, seen) {
9885
- let doc;
9926
+ * references load relative to it — and its embedded ones from ITS assets —
9927
+ * depth-limited, never re-entering a file above in the chain (the
9928
+ * editor's rules). */
9929
+ async loadReference(ref, source, assets, depth, seen) {
9930
+ let content;
9886
9931
  try {
9887
- doc = await referenceDocument(ref, await this.readSource(source));
9932
+ content = await referenceContent(
9933
+ ref,
9934
+ await this.readSource(source, assets)
9935
+ );
9888
9936
  } catch {
9889
9937
  return null;
9890
9938
  }
9891
- const content = contentFromDocument(doc, ref.id);
9892
9939
  if (ref.format === "linea" && depth + 1 < 4) {
9893
9940
  const above = new Set(seen).add(JSON.stringify(source));
9894
9941
  const nested = /* @__PURE__ */ new Map();
@@ -9899,19 +9946,23 @@ var Drawing = class extends LocalDrawing {
9899
9946
  const c = await this.loadReference(
9900
9947
  child,
9901
9948
  childSource,
9949
+ content.assets ?? {},
9902
9950
  depth + 1,
9903
9951
  above
9904
9952
  );
9905
9953
  if (c) nested.set(child.id, c);
9906
9954
  }
9907
9955
  content.resolveReference = (r) => nested.get(r.id) ?? null;
9956
+ content.bounds = measureContentBounds(content, approximateTextMeasurer);
9908
9957
  }
9909
- return {
9910
- ...content,
9911
- bounds: measureContentBounds(content, approximateTextMeasurer)
9912
- };
9958
+ return content;
9913
9959
  }
9914
- async readSource(source) {
9960
+ async readSource(source, assets) {
9961
+ if (source.kind === "asset") {
9962
+ const data = assets[source.assetId];
9963
+ if (data === void 0) throw new Error("embedded file missing");
9964
+ return dataUrlBytes(data);
9965
+ }
9915
9966
  if (source.kind === "url") {
9916
9967
  const res = await fetch(source.url);
9917
9968
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -9991,7 +10042,8 @@ var Drawing = class extends LocalDrawing {
9991
10042
  }
9992
10043
  // --- scripts ---------------------------------------------------------------
9993
10044
  /** Run a command module source (`defineCommand`) against this drawing as
9994
- * ONE undo step, headlessly: declared `inputs` must be supplied; declared
10045
+ * ONE undo step, headlessly: declared `inputs` must be supplied (points
10046
+ * in label order, or a record by name for an inputs table); declared
9995
10047
  * `params` take their defaults unless given by name (unknown names and
9996
10048
  * wrong types throw). Resolves with the script's return value. */
9997
10049
  runScript(source, inputs, params) {
@@ -10032,7 +10084,12 @@ async function openDrawing(path, options = {}) {
10032
10084
  return doc;
10033
10085
  }
10034
10086
  export {
10087
+ BridgeError,
10088
+ DEFAULT_BRIDGE_PORT,
10035
10089
  Drawing,
10090
+ bridgeFilePath,
10091
+ connectEditors,
10092
+ createBridge,
10036
10093
  drawingFromJson,
10037
10094
  ensureScriptTranspiler,
10038
10095
  getBlockMetaResult,