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/dist/sdk.js CHANGED
@@ -17,7 +17,6 @@ import {
17
17
  EXTRACTED_EXTRAS,
18
18
  EXTRACTED_RANGES,
19
19
  FONTS,
20
- HEX_COLOR,
21
20
  HatchPatternSchema,
22
21
  LineTypeSchema,
23
22
  MAX_HATCH_SEGMENTS,
@@ -28,6 +27,7 @@ import {
28
27
  TextStyleSchema,
29
28
  Vec2Schema,
30
29
  aciToHex,
30
+ acquireInputTable,
31
31
  add,
32
32
  angle,
33
33
  approximateTextMeasurer,
@@ -37,6 +37,7 @@ import {
37
37
  blockPivot,
38
38
  blockScriptId,
39
39
  bytesToDataUrl,
40
+ checkNamedInputs,
40
41
  cleanRichText,
41
42
  cleanText,
42
43
  closestPointOnSegment,
@@ -50,9 +51,10 @@ import {
50
51
  dimensionRawToLabels,
51
52
  dimensionRawToOffset,
52
53
  dist,
54
+ documentBlocks,
53
55
  documentObjectResolver,
54
56
  dot,
55
- dryRunBlockInputs,
57
+ dryRunBlockPlacement,
56
58
  dryRunObjectResolver,
57
59
  effectiveTextFont,
58
60
  ellipseArcToBeziers,
@@ -73,14 +75,20 @@ import {
73
75
  hashAsset,
74
76
  hatchPatternKey,
75
77
  hexToRgb,
78
+ hostedClosure,
76
79
  imagePixelSize,
80
+ inputLabel,
81
+ inputPoints,
82
+ inputRefs,
77
83
  invalidateBlockCache,
84
+ isRequiredInput,
78
85
  len,
79
86
  lineSegments,
80
87
  lineTypeKey,
81
88
  lineWeightToMm,
82
89
  linearAxis,
83
90
  listBlockMeta,
91
+ mapInputValues,
84
92
  matApply,
85
93
  matIdentity,
86
94
  matMultiply,
@@ -92,7 +100,9 @@ import {
92
100
  norm,
93
101
  normAngle,
94
102
  normalizeSpans,
103
+ paramMismatch,
95
104
  parseFontFace,
105
+ parseInputTable,
96
106
  parseParamsExport,
97
107
  perp,
98
108
  realFace,
@@ -107,7 +117,9 @@ import {
107
117
  rotateObject,
108
118
  scale,
109
119
  scaleObjectUniform,
120
+ scanScriptId,
110
121
  scriptTranspilerReady,
122
+ selectionFits,
111
123
  signedArcSweep,
112
124
  sub,
113
125
  tableLayout,
@@ -121,8 +133,9 @@ import {
121
133
  textStyleFor,
122
134
  textWidth,
123
135
  translateObject,
124
- valueParams
125
- } from "./chunk-BQCSFVAF.js";
136
+ valueParams,
137
+ withEmptyLists
138
+ } from "./chunk-YYJAXJOY.js";
126
139
  import {
127
140
  embeddedPdfFonts
128
141
  } from "./chunk-L4FNH35X.js";
@@ -1682,10 +1695,8 @@ var buildEntry = (obj, ctx) => {
1682
1695
  };
1683
1696
  }
1684
1697
  case "block": {
1685
- const inputPoints = obj.inputs.filter(
1686
- (v) => typeof v !== "string"
1687
- );
1688
- 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" }));
1689
1700
  const segments = [];
1690
1701
  const arcs = [];
1691
1702
  const ellipses = [];
@@ -4983,7 +4994,10 @@ var contentFromDocument = (doc, refId, bounds = emptyBounds()) => {
4983
4994
  (o) => o.type === "block" ? {
4984
4995
  ...o,
4985
4996
  id: prefix + o.id,
4986
- 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
+ )
4987
5001
  } : { ...o, id: prefix + o.id }
4988
5002
  );
4989
5003
  const byId = new Map(objects.map((o) => [o.id, o]));
@@ -5344,7 +5358,7 @@ var renderBlockThumbnailSvg = async (script, {
5344
5358
  } = {}) => {
5345
5359
  await ensureScriptTranspiler();
5346
5360
  const defId = getBlockMeta(script).id;
5347
- const inputs = await dryRunBlockInputs(script);
5361
+ const { inputs, params } = await dryRunBlockPlacement(script);
5348
5362
  const defs = [script];
5349
5363
  const present = /* @__PURE__ */ new Set([defId]);
5350
5364
  for (const extra of definitions) {
@@ -5360,7 +5374,7 @@ var renderBlockThumbnailSvg = async (script, {
5360
5374
  definitionId: defId,
5361
5375
  rotation: 0,
5362
5376
  scale: 1,
5363
- params: {},
5377
+ params,
5364
5378
  inputs
5365
5379
  };
5366
5380
  const env = {
@@ -5526,8 +5540,23 @@ var OptionalMetaSchema = z3.object({
5526
5540
  version: z3.string().optional(),
5527
5541
  authors: z3.array(z3.string()).optional(),
5528
5542
  tags: z3.array(z3.string()).optional(),
5529
- inputs: z3.array(z3.string().min(1)).optional()
5543
+ requiresEditor: z3.boolean().optional()
5530
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
+ };
5531
5560
  var extractCommandMeta = (mod) => {
5532
5561
  if (typeof mod.id !== "string" || mod.id.trim() === "")
5533
5562
  throw new Error("Command script must export a non-empty string `id`");
@@ -5541,10 +5570,12 @@ var extractCommandMeta = (mod) => {
5541
5570
  );
5542
5571
  }
5543
5572
  const params = parseParamsExport(mod.params);
5573
+ const inputs = parseCommandInputs(mod.inputs, params);
5544
5574
  return {
5545
5575
  id: mod.id,
5546
5576
  name: mod.name,
5547
5577
  ...parsed.data,
5578
+ ...inputs ? { inputs } : {},
5548
5579
  ...params.length > 0 ? { params } : {}
5549
5580
  };
5550
5581
  };
@@ -5583,16 +5614,11 @@ var getCommandMetaResult = (script) => {
5583
5614
  };
5584
5615
  var listCommandMeta = (scripts) => scripts.map(getCommandMetaResult);
5585
5616
  var getCommandRun = (script) => compileCommand(script).run;
5586
- var ID_EXPORT_RE = /export\s+const\s+id\s*=\s*["']([^"']+)["']/;
5587
5617
  var commandScriptId = (script) => {
5588
5618
  try {
5589
5619
  return compileCommand(script).meta.id;
5590
5620
  } catch {
5591
- const named = ID_EXPORT_RE.exec(script);
5592
- if (named) return named[1];
5593
- const call = script.search(/defineCommand\s*\(|export\s+default\s*\{/);
5594
- if (call === -1) return void 0;
5595
- return /\bid\s*:\s*["']([^"']+)["']/.exec(script.slice(call))?.[1];
5621
+ return scanScriptId(script, /defineCommand\s*\(|export\s+default\s*\{/);
5596
5622
  }
5597
5623
  };
5598
5624
  var mergeCommandModules = (existing, incoming) => mergeScriptModules(existing, incoming, {
@@ -8171,7 +8197,9 @@ var invalidationIds = (op) => {
8171
8197
  switch (op.type) {
8172
8198
  case "add-objects":
8173
8199
  case "update-objects":
8174
- 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
+ );
8175
8203
  case "remove-objects":
8176
8204
  return op.ids;
8177
8205
  case "set-current-layer":
@@ -9312,7 +9340,12 @@ var LocalDrawing = class {
9312
9340
  /** Delete objects by id (unknown ids are skipped); returns how many went. */
9313
9341
  remove(ids) {
9314
9342
  const entries = [];
9315
- 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) {
9316
9349
  try {
9317
9350
  entries.push({ space: this.findObject(id).space, item: id });
9318
9351
  } catch {
@@ -9614,7 +9647,7 @@ var LocalDrawing = class {
9614
9647
  }
9615
9648
  /** Export the document to a DXF string. */
9616
9649
  async toDxf() {
9617
- const { buildDxf } = await import("./exportDxf-T2RTSTH5.js");
9650
+ const { buildDxf } = await import("./exportDxf-C4CLQCC3.js");
9618
9651
  return buildDxf(this.state, this.measurer).content;
9619
9652
  }
9620
9653
  /** Export the document to DWG bytes (the DXF content, wasm-transcoded).
@@ -9623,7 +9656,7 @@ var LocalDrawing = class {
9623
9656
  * exploded to plain geometry (DWG_DXF_OPTIONS — the transcoder's DWG
9624
9657
  * output for them is not accepted by AutoCAD). */
9625
9658
  async toDwg(opts) {
9626
- const { buildDxf, DWG_DXF_OPTIONS } = await import("./exportDxf-T2RTSTH5.js");
9659
+ const { buildDxf, DWG_DXF_OPTIONS } = await import("./exportDxf-C4CLQCC3.js");
9627
9660
  const dxf = buildDxf(this.state, this.measurer, DWG_DXF_OPTIONS).content;
9628
9661
  return dxfToDwg(dxf, opts?.version ?? "AC1027");
9629
9662
  }
@@ -9642,46 +9675,6 @@ var makeContext = (opts, inputs, params) => {
9642
9675
  params
9643
9676
  };
9644
9677
  };
9645
- var paramMismatch = (def, value) => {
9646
- switch (def.type) {
9647
- case "number":
9648
- if (typeof value !== "number" || !Number.isFinite(value))
9649
- return "expected a number";
9650
- if (def.integer && !Number.isInteger(value))
9651
- return "expected a whole number";
9652
- if (def.min !== void 0 && value < def.min)
9653
- return `below the minimum ${def.min}`;
9654
- if (def.max !== void 0 && value > def.max)
9655
- return `above the maximum ${def.max}`;
9656
- return null;
9657
- case "string":
9658
- case "textStyle":
9659
- case "dimStyle":
9660
- case "font":
9661
- case "lineType":
9662
- case "hatchPattern":
9663
- return typeof value === "string" ? null : "expected a string";
9664
- case "boolean":
9665
- return typeof value === "boolean" ? null : "expected true or false";
9666
- case "numberList": {
9667
- if (!Array.isArray(value) || !value.every((v) => typeof v === "number" && Number.isFinite(v)))
9668
- return "expected an array of numbers";
9669
- const low = value.find((v) => def.min !== void 0 && v < def.min);
9670
- if (low !== void 0) return `${low} is below the minimum ${def.min}`;
9671
- const high = value.find((v) => def.max !== void 0 && v > def.max);
9672
- if (high !== void 0) return `${high} is above the maximum ${def.max}`;
9673
- return null;
9674
- }
9675
- case "color":
9676
- return typeof value === "string" && HEX_COLOR.test(value) ? null : 'expected a "#rrggbb" color';
9677
- case "stringList":
9678
- return Array.isArray(value) && value.every((v) => typeof v === "string") ? null : "expected an array of strings";
9679
- case "enum":
9680
- return def.options.some((o) => o.value === value) ? null : `expected one of ${def.options.map((o) => JSON.stringify(o.value)).join(", ")}`;
9681
- default:
9682
- return "a label carries no value";
9683
- }
9684
- };
9685
9678
  var resolveCommandParams = (meta, given = {}) => {
9686
9679
  const defs = valueParams(meta.params ?? []);
9687
9680
  const params = /* @__PURE__ */ Object.create(null);
@@ -9699,8 +9692,57 @@ var resolveCommandParams = (meta, given = {}) => {
9699
9692
  }
9700
9693
  return params;
9701
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
+ };
9702
9738
  var acquireCommandInputs = async (meta, opts) => {
9739
+ if (isInputTable(meta.inputs))
9740
+ return acquireNamedCommandInputs(meta, meta.inputs, opts);
9703
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
+ );
9704
9746
  const given = (opts.inputs ?? []).map((p, i) => {
9705
9747
  try {
9706
9748
  return Vec2Schema.parse(p);
@@ -9732,11 +9774,29 @@ var runCommandModule = async (source, opts) => {
9732
9774
  const result = getCommandMetaResult(source);
9733
9775
  if (!result.ok) throw new Error(`Command failed to compile: ${result.error}`);
9734
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
+ );
9735
9781
  const params = resolveCommandParams(result.meta, opts.params);
9736
9782
  const inputs = await acquireCommandInputs(result.meta, opts);
9737
- return opts.document.transaction(
9738
- () => run(makeContext(opts, inputs, params)),
9739
- 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 }
9740
9800
  );
9741
9801
  };
9742
9802
  var runDocumentCommand = async (id, opts) => {
@@ -9982,7 +10042,8 @@ var Drawing = class extends LocalDrawing {
9982
10042
  }
9983
10043
  // --- scripts ---------------------------------------------------------------
9984
10044
  /** Run a command module source (`defineCommand`) against this drawing as
9985
- * 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
9986
10047
  * `params` take their defaults unless given by name (unknown names and
9987
10048
  * wrong types throw). Resolves with the script's return value. */
9988
10049
  runScript(source, inputs, params) {