lineadraw 0.7.0 → 0.9.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/README.md +276 -203
- package/bin/lineadraw.mjs +35 -115
- package/dist/bridge.d.ts +102 -0
- package/dist/bridge.js +16 -0
- package/dist/{chunk-NCNZ7A6M.js → chunk-BQCSFVAF.js} +3061 -678
- package/dist/chunk-BQCSFVAF.js.map +1 -0
- package/dist/chunk-L2CWZAKW.js +141 -0
- package/dist/chunk-L2CWZAKW.js.map +1 -0
- package/dist/exportDxf-T2RTSTH5.js +10 -0
- package/dist/exportDxf-T2RTSTH5.js.map +1 -0
- package/dist/{node-2RH2EKXG.js → node-PLO3I6OZ.js} +39 -1
- package/dist/node-PLO3I6OZ.js.map +1 -0
- package/dist/pkg/linea_dwg_bg.wasm +0 -0
- package/dist/sdk.d.ts +1338 -592
- package/dist/sdk.js +3499 -1622
- package/dist/sdk.js.map +1 -1
- package/lineadraw.d.ts +481 -1424
- package/package.json +2 -2
- package/templates/sample.block.ts +2 -2
- package/dist/chunk-NCNZ7A6M.js.map +0 -1
- package/dist/exportDxf-2HC6UIBS.js +0 -8
- package/dist/node-2RH2EKXG.js.map +0 -1
- /package/dist/{exportDxf-2HC6UIBS.js.map → bridge.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,177 +1,29 @@
|
|
|
1
1
|
# lineadraw
|
|
2
2
|
|
|
3
|
-
[Linea](https://lineadraw.com) for Node
|
|
4
|
-
typings for blocks and commands, and the marketplace CLI — the one
|
|
5
|
-
dependency a script, an agent, or a block/command repository needs.
|
|
3
|
+
[Linea](https://lineadraw.com) for Node — one package, four jobs:
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- **
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- **CLI** — `lineadraw manifest | check | init` turns any repository into
|
|
14
|
-
a Linea **marketplace**: a git repo the editor can browse and install
|
|
15
|
-
blocks/commands from.
|
|
16
|
-
|
|
17
|
-
## Quick start
|
|
18
|
-
|
|
19
|
-
Scaffold a fresh marketplace repository:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm create lineadraw my-blocks
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Or add the structure to an existing project:
|
|
5
|
+
| Job | What you get | Entry point |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| [**Drawing SDK**](#1-drawing-sdk) | Open, create, edit, render and export `.linea` / DXF / DWG / PDF / SVG / PNG headlessly — for scripts, agents and batch tools. Also drives drawings open in the desktop app. | `import { openDrawing } from "lineadraw/sdk"` |
|
|
8
|
+
| [**Extension development**](#2-extension-development) | Typings for parametric **blocks** and macro **commands**: `defineBlock` / `defineCommand` type-check in any editor, with parameter types inferred. | `import { defineBlock } from "lineadraw"` |
|
|
9
|
+
| [**Marketplace management**](#3-marketplace-management) | Turn a git repo into a Linea **marketplace** the editor can browse and install from: scaffold it, validate every module, generate the catalog, check it in CI. | `lineadraw init` · `manifest` · `check` |
|
|
10
|
+
| [**CLI for the desktop app**](#4-cli-for-the-running-desktop-app) | Talk to the running Linea desktop app from a terminal: list open drawings, open and close files, run any `linea_*` operation. | `lineadraw status` · `open` · `close` · `call` · `tools` |
|
|
26
11
|
|
|
27
12
|
```bash
|
|
28
|
-
npm i
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Either way you get `blocks/` and `commands/` folders, a `tsconfig.json`
|
|
33
|
-
wired to the typings, a sample block, a CI workflow, and a generated
|
|
34
|
-
catalog (`.lineadraw/marketplace.json`) — the repo is addable in the
|
|
35
|
-
editor from the first push.
|
|
36
|
-
|
|
37
|
-
## Writing a block
|
|
38
|
-
|
|
39
|
-
A block is one module with a single `defineBlock` default export:
|
|
40
|
-
typed parameters in, geometry out.
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
// blocks/surface.block.ts
|
|
44
|
-
import { defineBlock } from "lineadraw";
|
|
45
|
-
|
|
46
|
-
export default defineBlock({
|
|
47
|
-
// The id is the block's identity everywhere: @<collection>/<slug>.
|
|
48
|
-
// Re-publishing the same id updates installed copies in place.
|
|
49
|
-
id: "@my-blocks/surface",
|
|
50
|
-
name: "Surface",
|
|
51
|
-
version: "1.0.0",
|
|
52
|
-
params: [
|
|
53
|
-
{ name: "kind", type: "enum", default: "rock", options: ["rock", "ground"] },
|
|
54
|
-
],
|
|
55
|
-
place: ["Start point", "End point"], // one point pick per label, in order
|
|
56
|
-
draw: ({ params, inputs: [start, end] }) => {
|
|
57
|
-
params.kind; // "rock" | "ground" — inferred from the params table
|
|
58
|
-
return [{ type: "line", a: start, b: end }];
|
|
59
|
-
},
|
|
60
|
-
});
|
|
13
|
+
npm i lineadraw # the SDK in a script or tool
|
|
14
|
+
npm i -D lineadraw # typings + CLI in a block/command repository
|
|
15
|
+
npm create lineadraw my-blocks # a fresh marketplace repository
|
|
61
16
|
```
|
|
62
17
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`dependencies` lists ids of blocks your `draw` emits instances of (installs
|
|
66
|
-
pull them along); `paramVisibility({ params })` hides property rows;
|
|
67
|
-
`previewInputs` curates the inputs used for thumbnails. Units are
|
|
68
|
-
millimeters, Y-up, angles in radians CCW.
|
|
69
|
-
|
|
70
|
-
Parameter types (the same rows serve a command's `params`):
|
|
18
|
+
Requires Node.js >= 20. Units everywhere are millimeters, Y-up, angles in
|
|
19
|
+
radians CCW.
|
|
71
20
|
|
|
72
|
-
|
|
73
|
-
"mm"), `presets` (standard values in the field's dropdown).
|
|
74
|
-
- `string` — `placeholder`, `multiline`, `presets`.
|
|
75
|
-
- `boolean` (`display: "toggle"` — a toggle button showing the label,
|
|
76
|
-
compact in a row), `enum` (`options`, each optionally with a `swatch:
|
|
77
|
-
"#rrggbb"` color square; `display: "buttons"` — a segmented toggle group
|
|
78
|
-
for short choices, falling back to a dropdown when it doesn't fit),
|
|
79
|
-
`color` (`"#rrggbb"`, `presets` = a palette).
|
|
80
|
-
- `numberList` (`number[]`, typed Tekla-style: `"0 2*7200"` →
|
|
81
|
-
`[0, 7200, 7200]`) and `stringList` (`string[]`: `'A "B 2"'` →
|
|
82
|
-
`["A", "B 2"]`), both with `presets` and `placeholder` (shown while
|
|
83
|
-
empty).
|
|
84
|
-
- Pickers over the document's resources, valued by id / name:
|
|
85
|
-
`textStyle`, `dimStyle`, `font`, `lineType`, `hatchPattern`.
|
|
86
|
-
- `label` — a section heading (no value); `row` — 2–3 compact params on one
|
|
87
|
-
panel row (`items`, each label a short prefix like "W"); its items are
|
|
88
|
-
ordinary flat params (`params.w`).
|
|
89
|
-
|
|
90
|
-
Every param may carry a `hint` (the label's tooltip). An instance that
|
|
91
|
-
overrides a param shows "Reset" beside it in Properties, back to your
|
|
92
|
-
default. `draw` also receives
|
|
93
|
-
`styles`: `styles.text(id)` / `styles.dim(id)` give a document style (an
|
|
94
|
-
empty or unknown id → the first), for blocks that lay out with real text
|
|
95
|
-
sizes.
|
|
96
|
-
|
|
97
|
-
## Writing a command
|
|
98
|
-
|
|
99
|
-
A command is a macro with a single `defineCommand` default export. Its
|
|
100
|
-
whole run is one undo step; Esc during a pick reverts cleanly. A
|
|
101
|
-
`params` table (the same rows a block's `params` uses) is asked for in a
|
|
102
|
-
dialog before the run and arrives typed as `ctx.params`; headless callers
|
|
103
|
-
pass it by name.
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
// commands/circle-here.cmd.ts
|
|
107
|
-
import { defineCommand } from "lineadraw";
|
|
108
|
-
|
|
109
|
-
export default defineCommand({
|
|
110
|
-
id: "@my-blocks/circle-here",
|
|
111
|
-
name: "Circle here",
|
|
112
|
-
run: async ({ document, pickPoint, prompt, showToast }) => {
|
|
113
|
-
const center = await pickPoint("Center point");
|
|
114
|
-
const r = await prompt("Radius");
|
|
115
|
-
document.add([{ type: "circle", center, radius: Number.parseFloat(r) }]);
|
|
116
|
-
showToast("Done", "success");
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Imports are limited to `"lineadraw"` and `"lineadraw/helpers"` (geometry
|
|
122
|
-
helpers) — the editor resolves both when executing your module.
|
|
123
|
-
|
|
124
|
-
## The CLI
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
lineadraw manifest Validate all modules and write .lineadraw/marketplace.json
|
|
128
|
-
lineadraw check Validate without writing; fail if the committed manifest is stale
|
|
129
|
-
lineadraw init Add the marketplace structure to the current npm project
|
|
130
|
-
|
|
131
|
-
lineadraw status App version, port, the open drawings: id, name, path, kind, dirty
|
|
132
|
-
lineadraw open <file.linea> Open a file in a new window
|
|
133
|
-
lineadraw close [--editor <x>] [--discard]
|
|
134
|
-
lineadraw call <operation> ['<json>'] [--editor <x>] Run one linea_* tool
|
|
135
|
-
lineadraw tools The operations the bridge offers
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
The bridge commands talk to the running Linea desktop app on
|
|
139
|
-
`127.0.0.1:7431` (the app's `bridge.json` supplies the port and the agent
|
|
140
|
-
token; `--port`, `--token` and `LINEA_BRIDGE_TOKEN` override) — see
|
|
141
|
-
docs/desktop-bridge.md in the linea repo.
|
|
142
|
-
|
|
143
|
-
The manifest is deterministic (repo-relative paths, no git metadata), so
|
|
144
|
-
`check` is a byte-for-byte diff — the included CI workflow runs it on every
|
|
145
|
-
push and PR. `manifest`/`check` also **dry-run every block** through
|
|
146
|
-
`lineadraw/sdk` and render thumbnails.
|
|
147
|
-
|
|
148
|
-
Collection name and owner come from your package.json:
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{ "lineadraw": { "marketplaceName": "My blocks", "marketplaceOwner": "Me" } }
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Using a marketplace in the editor
|
|
155
|
-
|
|
156
|
-
In [the editor](https://lineadraw.com): **Library → Marketplace tab → the
|
|
157
|
-
gear button (Manage marketplaces…) → Add a marketplace** and enter
|
|
158
|
-
`owner/repo` (or `owner/repo@branch`). For a private repository,
|
|
159
|
-
create a fine-grained personal access token with read-only Contents access
|
|
160
|
-
to just that repository and paste it in the add dialog — it is stored only
|
|
161
|
-
on that device. Installing embeds the module (and its declared dependency
|
|
162
|
-
closure) in the document, so drawings keep working offline and travel with
|
|
163
|
-
the file.
|
|
164
|
-
|
|
165
|
-
## Headless SDK (`lineadraw/sdk`)
|
|
21
|
+
## 1. Drawing SDK
|
|
166
22
|
|
|
167
23
|
A script-friendly API over the Linea CAD kernel: open, create, query, edit,
|
|
168
24
|
render, and import/export `.linea` documents headlessly in Node — no browser,
|
|
169
25
|
no server. Built for agents, CLIs, and batch tooling.
|
|
170
26
|
|
|
171
|
-
```bash
|
|
172
|
-
npm i lineadraw
|
|
173
|
-
```
|
|
174
|
-
|
|
175
27
|
```js
|
|
176
28
|
import { newDrawing, openDrawing } from "lineadraw/sdk";
|
|
177
29
|
|
|
@@ -180,7 +32,7 @@ const walls = doc.addLayer({ name: "walls", color: "#e8e8e8", lineWidth: 0.5 });
|
|
|
180
32
|
doc.add([
|
|
181
33
|
{ type: "polyline", layerId: walls, closed: true,
|
|
182
34
|
points: [[0, 0], [6000, 0], [6000, 3000], [0, 3000]] },
|
|
183
|
-
{ type: "dimension", points: [[0, 0], [6000, 0]], offset:
|
|
35
|
+
{ type: "dimension", points: [[0, 0], [6000, 0]], offset: -500,
|
|
184
36
|
scale: 50 }, // annotations × drawing scale
|
|
185
37
|
]);
|
|
186
38
|
doc.addLayout("Plan 1:50", { sheet: "A3", viewport: { scale: "1:50" } });
|
|
@@ -221,7 +73,11 @@ await doc.exportPdf("plan.pdf");
|
|
|
221
73
|
|
|
222
74
|
### API surface
|
|
223
75
|
|
|
224
|
-
`newDrawing(name?)` / `drawingFromJson(text)` / `openDrawing(path)`
|
|
76
|
+
`newDrawing(name?, options?)` / `drawingFromJson(text)` / `openDrawing(path, options?)`
|
|
77
|
+
→ `Drawing`; `options.marketplaces` lists the marketplaces to search and
|
|
78
|
+
install from (`{ kind: "github", spec: "owner/repo[@branch]", token? }` or
|
|
79
|
+
`{ kind: "folder", folder }`, priority order; the public collection is
|
|
80
|
+
appended when missing):
|
|
225
81
|
|
|
226
82
|
- **Name**: `drawing.name` is the file name (set on `openDrawing`, updated
|
|
227
83
|
by `save(path)`); `newDrawing(name)` names it until it is saved. The
|
|
@@ -238,11 +94,23 @@ await doc.exportPdf("plan.pdf");
|
|
|
238
94
|
a throw rolls back), `undo()`, `redo()`, `applyOps(ops)`, `ready()`
|
|
239
95
|
- **Styles** (by name or id): `listTextStyles()`, `addTextStyle({name, …})`,
|
|
240
96
|
`updateTextStyle(nameOrId, patch)`, `removeTextStyle(nameOrId)` (refused
|
|
241
|
-
while objects use it) — the same four for `DimStyle
|
|
242
|
-
|
|
97
|
+
while objects use it) — the same four for `DimStyle`
|
|
98
|
+
- **Line types / hatch patterns** (the document's own definitions, by
|
|
99
|
+
name, resolved ahead of the built-in catalogue): `listLineTypes()`,
|
|
100
|
+
`addLineTypes([{name, dashesMm}])`, `updateLineType(name, patch)`,
|
|
101
|
+
`removeLineType(name)`; `listHatchPatterns()`,
|
|
102
|
+
`addHatchPatterns([{name, lines}])`, `updateHatchPattern(name, patch)`,
|
|
103
|
+
`removeHatchPattern(name)`
|
|
243
104
|
- **Modules**: `await addBlockDefinitions(sources)` / `removeBlockDefinition(id)`,
|
|
244
105
|
`await addUserCommands(sources)` / `removeUserCommand(id)`,
|
|
245
106
|
`await setBlockDefinitions(sources)`, `await setCommands(sources)`
|
|
107
|
+
- **Marketplaces**: `listMarketplaces()`, `await addMarketplaces(list)`
|
|
108
|
+
(verifies each catalog), `removeMarketplaces(names)`,
|
|
109
|
+
`await searchMarketplaces({ query?, kind?, marketplace? })` (entries with
|
|
110
|
+
install status), `await installMarketplaceEntries(ids, marketplace?)`
|
|
111
|
+
(declared block dependencies first, one undo step) — then insert a block
|
|
112
|
+
with `add([{ type: "block", definitionId }])`. Marketplaces are process
|
|
113
|
+
configuration, never written to the file.
|
|
246
114
|
- **Scripts**: `await runScript(source, inputs?, params?)` runs a
|
|
247
115
|
`defineCommand` module (the same one the editor palette runs) headlessly
|
|
248
116
|
as ONE undo step — its declared `inputs` (points) are supplied up front,
|
|
@@ -253,8 +121,10 @@ await doc.exportPdf("plan.pdf");
|
|
|
253
121
|
- **Layers**: `addLayer`, `updateLayer(nameOrId, patch)`,
|
|
254
122
|
`removeLayer(nameOrId)` (must be empty and not current),
|
|
255
123
|
`reorderLayers(order)`, `setCurrentLayer(nameOrId)`
|
|
256
|
-
- **Assets**: `addAsset(dataUrl) → assetId` (
|
|
257
|
-
|
|
124
|
+
- **Assets**: `addAsset(dataUrl) → assetId` (content-keyed; an embedded
|
|
125
|
+
reference names it), `listAssets()`, `removeAsset(id)` (refused while
|
|
126
|
+
referenced)
|
|
127
|
+
- **Settings**: `updateSettings(patch)` (document settings, e.g. units)
|
|
258
128
|
- **Transform**: `move`, `rotate` (radians), `scale`, `mirror`, `copy`, `array`
|
|
259
129
|
- **Layouts**: `addLayout(name, {sheet, scale, viewport, index})`,
|
|
260
130
|
`updateLayout(nameOrId, {name, sheet, printPalette})`, `removeLayout`,
|
|
@@ -268,57 +138,85 @@ await doc.exportPdf("plan.pdf");
|
|
|
268
138
|
`unitOverride`), `importDwg(path)`/`importDwgData(bytes)` (DWG R13–R2018),
|
|
269
139
|
`importPdfData(arrayBuffer)`
|
|
270
140
|
|
|
271
|
-
Full parameter docs are in the bundled `dist/
|
|
141
|
+
Full parameter docs are in the bundled `dist/sdk.d.ts` JSDoc.
|
|
272
142
|
|
|
273
143
|
### Object types
|
|
274
144
|
|
|
275
145
|
`line` (a, b) · `polyline` (points with optional bulge, closed) · `circle` ·
|
|
276
146
|
`arc` (CCW start→end) · `ellipse` (center, majorAxis vector, ratio) ·
|
|
277
|
-
`hatch` (loops — outer + holes, even-odd; fill solid/pattern/lines) ·
|
|
147
|
+
`point` (p) · `hatch` (loops — outer + holes, even-odd; fill solid/pattern/lines) ·
|
|
278
148
|
`text` (position, content; **default alignment center/center**; optional
|
|
279
149
|
leaderLines with the arrowhead at index 0) · `dimension` (linear/angular/
|
|
280
|
-
radial/diameter; `offset` is
|
|
281
|
-
|
|
282
|
-
`
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
`
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
150
|
+
radial/diameter; linear: `offset` is the signed distance of the dimension
|
|
151
|
+
line from points[0] (left of the measuring direction positive) and
|
|
152
|
+
`rotation` an optional locked measuring direction; angular: `offset` is the
|
|
153
|
+
signed arc radius; `labels[i]` place and word each segment's text —
|
|
154
|
+
`along`/`across` in mm, unset = automatic, `content` with `{value}` for the
|
|
155
|
+
measurement; linear with >2 points = chain) · `block` (instances of script definitions) ·
|
|
156
|
+
`table` (columns, rows, cells) · `comment` (a review bubble, never
|
|
157
|
+
printed) · `reference` (a linked or embedded file: `.linea`, DXF, DWG,
|
|
158
|
+
PDF, PNG/JPEG — a picture is a reference whose `source` is
|
|
159
|
+
`{ kind: "asset", assetId }`) · `viewport` (layouts only).
|
|
160
|
+
|
|
161
|
+
### Block definitions in a document
|
|
162
|
+
|
|
163
|
+
`document.blockDefinitions` is an array of ES-module **source strings** —
|
|
164
|
+
the same `defineBlock` modules described under
|
|
165
|
+
[Extension development](#2-extension-development). Load them with
|
|
166
|
+
`await doc.setBlockDefinitions([...sources])` or `addBlockDefinitions`
|
|
167
|
+
(async, so the script transpiler is loaded and block geometry is
|
|
168
|
+
definitive when it resolves), then place instances with
|
|
169
|
+
`add([{ type: "block", definitionId }])`. An instance's first point input
|
|
170
|
+
is its pivot: `draw` sees inputs localized to it, and the instance's
|
|
171
|
+
`rotation`/`scale` apply about it.
|
|
172
|
+
|
|
173
|
+
### Running editors (the desktop bridge)
|
|
174
|
+
|
|
175
|
+
`Drawing` works on a document in your own process. To work on a drawing
|
|
176
|
+
that is OPEN in the Linea desktop app (or a web tab paired with it), connect
|
|
177
|
+
to the app's bridge on `127.0.0.1:7431`:
|
|
290
178
|
|
|
291
179
|
```ts
|
|
292
|
-
import {
|
|
293
|
-
import { polar, add } from "lineadraw/helpers"; // vector-math helpers
|
|
180
|
+
import { connectEditors } from "lineadraw/sdk";
|
|
294
181
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
],
|
|
182
|
+
const linea = await connectEditors(); // port + agent token from the app's bridge.json
|
|
183
|
+
const [first] = await linea.editors(); // { id, name, path, kind, readOnly, dirty }
|
|
184
|
+
const plan = linea.editor(first.id); // by document id, file path or unique name
|
|
185
|
+
|
|
186
|
+
// One linea_* operation = one undo step in that editor.
|
|
187
|
+
await plan.call("linea_add_objects", {
|
|
188
|
+
objects: [{ type: "circle", center: [0, 0], radius: 500 }],
|
|
303
189
|
});
|
|
190
|
+
|
|
191
|
+
// A whole command module, run INSIDE the editor against the full Drawing
|
|
192
|
+
// API: one round trip, one undo step. `inputs` / `params` answer the
|
|
193
|
+
// module's declared ones.
|
|
194
|
+
const result = await plan.runScript(
|
|
195
|
+
`import { defineCommand } from "lineadraw";
|
|
196
|
+
export default defineCommand({
|
|
197
|
+
id: "count-lines",
|
|
198
|
+
name: "Count lines",
|
|
199
|
+
run: ({ document }) => document.listObjects({ type: "line" }).length,
|
|
200
|
+
});`,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const other = await linea.open("details.linea"); // a window of its own
|
|
204
|
+
await linea.editor(other.id).close({ discard: true });
|
|
304
205
|
```
|
|
305
206
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`
|
|
310
|
-
|
|
311
|
-
`
|
|
312
|
-
|
|
313
|
-
`rotation`/`scale` apply about it. (Plain named exports — incl. the
|
|
314
|
-
pre-rename `main`/`defineInput` names — remain evaluatable as the frozen
|
|
315
|
-
wire form.)
|
|
207
|
+
The operations are the same `linea_*` catalog the MCP server and the CLI
|
|
208
|
+
offer (`await linea.tools()` lists them). Failures throw a `BridgeError`
|
|
209
|
+
whose `code` says why: `unreachable` (the app isn't running),
|
|
210
|
+
`ambiguous` (several editors are open and none was named — `details`
|
|
211
|
+
lists them), `noSuchEditor`, `readOnly`, `validation`… The connection
|
|
212
|
+
options (`port`, `token`, `bridgeFile`) override what bridge.json says;
|
|
213
|
+
`LINEA_BRIDGE_TOKEN` supplies a token too.
|
|
316
214
|
|
|
317
215
|
### Interchange notes
|
|
318
216
|
|
|
319
217
|
- DXF/DWG export flattens semantics: block instances are exported as their
|
|
320
|
-
evaluated geometry (no `INSERT` reuse) and
|
|
321
|
-
(DXF
|
|
218
|
+
evaluated geometry (no `INSERT` reuse), and embedded references are
|
|
219
|
+
skipped (DXF can't embed a file). The export result reports both in
|
|
322
220
|
`skipped`.
|
|
323
221
|
- PDF import extracts vector strokes and text runs (dimensions arrive as
|
|
324
222
|
exploded geometry). pdf.js loads lazily on first use, as do the PNG
|
|
@@ -330,10 +228,185 @@ wire form.)
|
|
|
330
228
|
- Dimensions validate their point counts per kind (linear ≥ 2, angular ≥ 3,
|
|
331
229
|
radial/diameter ≥ 2).
|
|
332
230
|
|
|
231
|
+
## 2. Extension development
|
|
232
|
+
|
|
233
|
+
Blocks and commands are plain TypeScript (or JavaScript) modules. The
|
|
234
|
+
package's `lineadraw.d.ts` types the two module names a script may import —
|
|
235
|
+
`"lineadraw"` (`defineBlock`, `defineCommand`) and `"lineadraw/helpers"`
|
|
236
|
+
(geometry helpers) — so authoring type-checks in VS Code, any other
|
|
237
|
+
TypeScript-aware editor, and CI. The editor resolves both names when it
|
|
238
|
+
runs your module; nothing else can be imported. `lineadraw init` writes a
|
|
239
|
+
`tsconfig.json` wired to the typings.
|
|
240
|
+
|
|
241
|
+
A module works on its own too: drop a `.block.ts` / `.cmd.ts` file onto the
|
|
242
|
+
editor, or exercise it headlessly with the SDK
|
|
243
|
+
(`await doc.addBlockDefinitions([source])`, `await doc.runScript(source)`).
|
|
244
|
+
|
|
245
|
+
### Writing a block
|
|
246
|
+
|
|
247
|
+
A block is one module with a single `defineBlock` default export:
|
|
248
|
+
typed parameters in, geometry out.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// blocks/surface.block.ts
|
|
252
|
+
import { defineBlock } from "lineadraw";
|
|
253
|
+
|
|
254
|
+
export default defineBlock({
|
|
255
|
+
// The id is the block's identity everywhere: @<collection>/<slug>.
|
|
256
|
+
// Re-publishing the same id updates installed copies in place.
|
|
257
|
+
id: "@my-blocks/surface",
|
|
258
|
+
name: "Surface",
|
|
259
|
+
version: "1.0.0",
|
|
260
|
+
params: [
|
|
261
|
+
{ name: "kind", type: "enum", default: "rock", options: ["rock", "ground"] },
|
|
262
|
+
],
|
|
263
|
+
place: ["Start point", "End point"], // one point pick per label, in order
|
|
264
|
+
draw: ({ params, inputs: [start, end] }) => {
|
|
265
|
+
params.kind; // "rock" | "ground" — inferred from the params table
|
|
266
|
+
return [{ type: "line", a: start, b: end }];
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Notable optional members: `place` may instead be an interactive
|
|
272
|
+
`({ params, pickPoint, pickObject })` function run once at insertion;
|
|
273
|
+
`dependencies` lists ids of blocks your `draw` emits instances of (installs
|
|
274
|
+
pull them along); `paramVisibility({ params })` hides property rows;
|
|
275
|
+
`previewInputs` curates the inputs used for thumbnails. Units are
|
|
276
|
+
millimeters, Y-up, angles in radians CCW.
|
|
277
|
+
|
|
278
|
+
Parameter types (the same rows serve a command's `params`):
|
|
279
|
+
|
|
280
|
+
- `number` — `min`/`max`, `integer`, `step`, `prefix`/`suffix` ("1:",
|
|
281
|
+
"mm"), `presets` (standard values in the field's dropdown).
|
|
282
|
+
- `string` — `placeholder`, `multiline`, `presets`.
|
|
283
|
+
- `boolean` (`display: "toggle"` — a toggle button showing the label,
|
|
284
|
+
compact in a row), `enum` (`options`, each optionally with a `swatch:
|
|
285
|
+
"#rrggbb"` color square; `display: "buttons"` — a segmented toggle group
|
|
286
|
+
for short choices, falling back to a dropdown when it doesn't fit),
|
|
287
|
+
`color` (`"#rrggbb"`, `presets` = a palette).
|
|
288
|
+
- `numberList` (`number[]`, typed Tekla-style: `"0 2*7200"` →
|
|
289
|
+
`[0, 7200, 7200]`) and `stringList` (`string[]`: `'A "B 2"'` →
|
|
290
|
+
`["A", "B 2"]`), both with `presets` and `placeholder` (shown while
|
|
291
|
+
empty).
|
|
292
|
+
- Pickers over the document's resources, valued by id / name:
|
|
293
|
+
`textStyle`, `dimStyle`, `font`, `lineType`, `hatchPattern`.
|
|
294
|
+
- `label` — a section heading (no value); `row` — 2–3 compact params on one
|
|
295
|
+
panel row (`items`, each label a short prefix like "W"); its items are
|
|
296
|
+
ordinary flat params (`params.w`).
|
|
297
|
+
|
|
298
|
+
Every param may carry a `hint` (the label's tooltip). An instance that
|
|
299
|
+
overrides a param shows "Reset" beside it in Properties, back to your
|
|
300
|
+
default. `draw` also receives
|
|
301
|
+
`styles`: `styles.text(id)` / `styles.dim(id)` give a document style (an
|
|
302
|
+
empty or unknown id → the first), for blocks that lay out with real text
|
|
303
|
+
sizes.
|
|
304
|
+
|
|
305
|
+
### Writing a command
|
|
306
|
+
|
|
307
|
+
A command is a macro with a single `defineCommand` default export. Its
|
|
308
|
+
whole run is one undo step; Esc during a pick reverts cleanly. A
|
|
309
|
+
`params` table (the same rows a block's `params` uses) is asked for in a
|
|
310
|
+
dialog before the run and arrives typed as `ctx.params`; headless callers
|
|
311
|
+
pass it by name.
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
// commands/circle-here.cmd.ts
|
|
315
|
+
import { defineCommand } from "lineadraw";
|
|
316
|
+
|
|
317
|
+
export default defineCommand({
|
|
318
|
+
id: "@my-blocks/circle-here",
|
|
319
|
+
name: "Circle here",
|
|
320
|
+
inputs: ["Center point"],
|
|
321
|
+
params: [{ name: "radius", type: "number", default: 10, min: 0.1 }],
|
|
322
|
+
// `inputs` are picked in the editor (or passed by a headless caller),
|
|
323
|
+
// `params` asked for in a dialog; `editor` is absent when headless.
|
|
324
|
+
run: async ({ document, editor, inputs: [center], params }) => {
|
|
325
|
+
document.add([{ type: "circle", center, radius: params.radius }]);
|
|
326
|
+
editor?.toast("Done", "success");
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## 3. Marketplace management
|
|
332
|
+
|
|
333
|
+
A marketplace is a git repository of blocks and commands plus a generated
|
|
334
|
+
catalog (`.lineadraw/marketplace.json`) that the editor reads.
|
|
335
|
+
|
|
336
|
+
Scaffold a fresh one:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
npm create lineadraw my-blocks
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Or add the structure to an existing npm project:
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
npm i -D lineadraw
|
|
346
|
+
npx lineadraw init
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Either way you get `blocks/` and `commands/` folders, a `tsconfig.json`
|
|
350
|
+
wired to the typings, a sample block, a CI workflow, the catalog, and
|
|
351
|
+
`npm run manifest` / `npm run check` scripts — the repo is addable in the
|
|
352
|
+
editor from the first push.
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
lineadraw manifest [--dir <path>] Validate all modules and write .lineadraw/marketplace.json
|
|
356
|
+
lineadraw check [--dir <path>] Validate without writing; fail if the committed manifest is stale
|
|
357
|
+
lineadraw init [--dir <path>] Add the marketplace structure to the current npm project
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`manifest` and `check` **dry-run every block** through `lineadraw/sdk` and
|
|
361
|
+
bake preview thumbnails into the catalog. The manifest is deterministic
|
|
362
|
+
(repo-relative paths, no git metadata), so `check` compares the parsed JSON
|
|
363
|
+
— formatting, a trailing newline or CRLF line endings never make it stale.
|
|
364
|
+
The included CI workflow runs it on every push and PR.
|
|
365
|
+
|
|
366
|
+
Collection name and owner come from your package.json:
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{ "lineadraw": { "marketplaceName": "My blocks", "marketplaceOwner": "Me" } }
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Using a marketplace in the editor
|
|
373
|
+
|
|
374
|
+
In [the editor](https://lineadraw.com): **Library → Marketplace tab → the
|
|
375
|
+
gear button (Manage marketplaces…) → Add a marketplace** and enter
|
|
376
|
+
`owner/repo` (or `owner/repo@branch`). For a private repository,
|
|
377
|
+
create a fine-grained personal access token with read-only Contents access
|
|
378
|
+
to just that repository and paste it in the add dialog — it is stored only
|
|
379
|
+
on that device. Installing embeds the module (and its declared dependency
|
|
380
|
+
closure) in the document, so drawings keep working offline and travel with
|
|
381
|
+
the file.
|
|
382
|
+
|
|
383
|
+
## 4. CLI for the running desktop app
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
lineadraw status App version, port, the open drawings: id, name, path, kind, dirty
|
|
387
|
+
lineadraw open <file.linea> Open a file in a new window
|
|
388
|
+
lineadraw close [--editor <x>] [--discard]
|
|
389
|
+
lineadraw call <operation> ['<json>'] [--editor <x>] Run one linea_* operation
|
|
390
|
+
lineadraw tools The operations the bridge offers
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
These commands talk to the running Linea desktop app on `127.0.0.1:7431`
|
|
394
|
+
(the app's `bridge.json` supplies the port and the agent token; `--port`,
|
|
395
|
+
`--token` and `LINEA_BRIDGE_TOKEN` override). `--editor` names a drawing by
|
|
396
|
+
document id, file path or unique name, and may be left out while only one
|
|
397
|
+
is open. Each `call` is one undo step in that editor. From code, use the
|
|
398
|
+
SDK's [`connectEditors()`](#running-editors-the-desktop-bridge).
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
npx lineadraw call linea_add_objects '{"objects":[{"type":"circle","center":[0,0],"radius":500}]}'
|
|
402
|
+
```
|
|
403
|
+
|
|
333
404
|
## Ecosystem
|
|
334
405
|
|
|
335
|
-
- [Linea
|
|
336
|
-
these modules.
|
|
406
|
+
- [Linea](https://lineadraw.com) — the CAD editor, on the desktop and on
|
|
407
|
+
the web, that runs these modules.
|
|
408
|
+
- [`create-lineadraw`](https://www.npmjs.com/package/create-lineadraw) —
|
|
409
|
+
`npm create lineadraw`, the marketplace scaffolder.
|
|
337
410
|
- [`lineadraw/lineadraw`](https://github.com/lineadraw/lineadraw) — the
|
|
338
411
|
public collection: real blocks/commands to learn from, agent skill, and
|
|
339
412
|
guides.
|