@miclivs/cadcli 0.1.2 → 0.2.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 +22 -13
- package/dist/commands/edit.d.ts +42 -2
- package/dist/commands/edit.js +281 -7
- package/dist/commands/json.js +0 -1
- package/dist/commands/shared.d.ts +0 -1
- package/dist/commands/shared.js +0 -1
- package/dist/commands/svg.js +0 -1
- package/dist/commands/thumbnail.js +0 -1
- package/dist/commands/view.d.ts +0 -1
- package/dist/commands/view.js +5 -7
- package/dist/core/acad-edit.d.ts +142 -0
- package/dist/core/acad-edit.js +234 -0
- package/dist/core/acad-view.d.ts +6 -0
- package/dist/core/acad-view.js +22 -0
- package/dist/core/acad.d.ts +11 -0
- package/dist/core/acad.js +82 -0
- package/dist/core/adapter.d.ts +0 -6
- package/dist/core/adapter.js +0 -13
- package/dist/core/drawing.d.ts +0 -1
- package/dist/index.d.ts +2 -1
- package/dist/main.js +82 -7
- package/dist/sdk.d.ts +5 -7
- package/dist/sdk.js +6 -6
- package/package.json +1 -2
- package/skills/cadcli/SKILL.md +21 -4
- package/dist/core/libredwg.d.ts +0 -37
- package/dist/core/libredwg.js +0 -86
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Arc, Circle, Color, Insert, Layer, Line, LineType, LineWeightType, LwPolyline, MText, Point, TextEntity, Transparency, XY, XYZ, } from "@node-projects/acad-ts";
|
|
2
|
+
import { EXIT_USER_ERROR } from "../utils/exit-codes.js";
|
|
3
|
+
import { readAcadFile, writeAcadDocument } from "./acad.js";
|
|
4
|
+
import { DwgCliError } from "./errors.js";
|
|
5
|
+
import { writeOutput } from "./files.js";
|
|
6
|
+
const degreesToRadians = (degrees) => (degrees * Math.PI) / 180;
|
|
7
|
+
function xyz(point) {
|
|
8
|
+
return new XYZ(point.x, point.y, point.z ?? 0);
|
|
9
|
+
}
|
|
10
|
+
function xy(point) {
|
|
11
|
+
return new XY(point.x, point.y);
|
|
12
|
+
}
|
|
13
|
+
function handleMatches(handle, entityId) {
|
|
14
|
+
const expected = entityId.toLowerCase();
|
|
15
|
+
const text = String(handle ?? "").toLowerCase();
|
|
16
|
+
if (text === expected)
|
|
17
|
+
return true;
|
|
18
|
+
const numeric = Number(handle);
|
|
19
|
+
return (Number.isFinite(numeric) && numeric.toString(16).toLowerCase() === expected);
|
|
20
|
+
}
|
|
21
|
+
function modelEntities(doc) {
|
|
22
|
+
return [...(doc.modelSpace?.entities ?? [])];
|
|
23
|
+
}
|
|
24
|
+
function findEntity(doc, entityId) {
|
|
25
|
+
const entity = modelEntities(doc).find((item) => handleMatches(item.handle, entityId));
|
|
26
|
+
if (!entity) {
|
|
27
|
+
throw new DwgCliError(`Entity not found: ${entityId}`, "ENTITY_NOT_FOUND", EXIT_USER_ERROR);
|
|
28
|
+
}
|
|
29
|
+
return entity;
|
|
30
|
+
}
|
|
31
|
+
function ensureLayer(doc, name) {
|
|
32
|
+
const existing = doc.layers?.tryGetValue(name);
|
|
33
|
+
if (existing)
|
|
34
|
+
return existing;
|
|
35
|
+
const layer = new Layer(name);
|
|
36
|
+
doc.layers?.add(layer);
|
|
37
|
+
return layer;
|
|
38
|
+
}
|
|
39
|
+
function ensureLineType(doc, name) {
|
|
40
|
+
const existing = doc.lineTypes?.tryGetValue(name);
|
|
41
|
+
if (existing)
|
|
42
|
+
return existing;
|
|
43
|
+
const lineType = new LineType(name);
|
|
44
|
+
doc.lineTypes?.add(lineType);
|
|
45
|
+
return lineType;
|
|
46
|
+
}
|
|
47
|
+
function acadColor(color) {
|
|
48
|
+
switch (color.mode) {
|
|
49
|
+
case "byLayer":
|
|
50
|
+
return Color.byLayer;
|
|
51
|
+
case "byBlock":
|
|
52
|
+
return Color.byBlock;
|
|
53
|
+
case "index":
|
|
54
|
+
return new Color(color.index);
|
|
55
|
+
case "rgb":
|
|
56
|
+
return new Color(color.r, color.g, color.b);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function lineWeight(weight) {
|
|
60
|
+
if (!Object.values(LineWeightType).includes(weight)) {
|
|
61
|
+
throw new DwgCliError(`Unsupported line weight: ${weight}`, "INVALID_LINE_WEIGHT", EXIT_USER_ERROR);
|
|
62
|
+
}
|
|
63
|
+
return weight;
|
|
64
|
+
}
|
|
65
|
+
function applyCommonOptions(doc, entity, options) {
|
|
66
|
+
if (options.layer)
|
|
67
|
+
entity.layer = ensureLayer(doc, options.layer);
|
|
68
|
+
if (options.color)
|
|
69
|
+
entity.color = acadColor(options.color);
|
|
70
|
+
}
|
|
71
|
+
function addEntity(doc, entity) {
|
|
72
|
+
if (!doc.modelSpace) {
|
|
73
|
+
throw new DwgCliError("Drawing has no model space to edit.", "MODEL_SPACE_NOT_FOUND", EXIT_USER_ERROR);
|
|
74
|
+
}
|
|
75
|
+
doc.modelSpace.entities.add(entity);
|
|
76
|
+
}
|
|
77
|
+
function deleteEntity(entity, entityId) {
|
|
78
|
+
const owner = entity.owner;
|
|
79
|
+
if (!owner?.entities?.remove) {
|
|
80
|
+
throw new DwgCliError(`Entity cannot be deleted: ${entityId}`, "ENTITY_NOT_DELETABLE", EXIT_USER_ERROR);
|
|
81
|
+
}
|
|
82
|
+
owner.entities.remove(entity);
|
|
83
|
+
}
|
|
84
|
+
function setAttribute(entity, tag, value, id) {
|
|
85
|
+
if (!(entity instanceof Insert)) {
|
|
86
|
+
throw new DwgCliError(`Entity is not a block insert: ${id}`, "ENTITY_NOT_INSERT", EXIT_USER_ERROR);
|
|
87
|
+
}
|
|
88
|
+
const attr = [...entity.attributes].find((item) => item.tag.toLowerCase() === tag.toLowerCase());
|
|
89
|
+
if (!attr) {
|
|
90
|
+
throw new DwgCliError(`Attribute not found on ${id}: ${tag}`, "ATTRIBUTE_NOT_FOUND", EXIT_USER_ERROR);
|
|
91
|
+
}
|
|
92
|
+
attr.value = value;
|
|
93
|
+
}
|
|
94
|
+
function applyOperation(doc, operation) {
|
|
95
|
+
switch (operation.kind) {
|
|
96
|
+
case "addPoint": {
|
|
97
|
+
const point = new Point(xyz(operation.at));
|
|
98
|
+
applyCommonOptions(doc, point, operation);
|
|
99
|
+
addEntity(doc, point);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
case "addLine": {
|
|
103
|
+
const line = new Line(xyz(operation.from), xyz(operation.to));
|
|
104
|
+
applyCommonOptions(doc, line, operation);
|
|
105
|
+
addEntity(doc, line);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
case "addCircle": {
|
|
109
|
+
const circle = new Circle();
|
|
110
|
+
circle.center = xyz(operation.center);
|
|
111
|
+
circle.radius = operation.radius;
|
|
112
|
+
applyCommonOptions(doc, circle, operation);
|
|
113
|
+
addEntity(doc, circle);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
case "addArc": {
|
|
117
|
+
const arc = new Arc(xyz(operation.center), operation.radius, degreesToRadians(operation.startAngleDegrees), degreesToRadians(operation.endAngleDegrees));
|
|
118
|
+
applyCommonOptions(doc, arc, operation);
|
|
119
|
+
addEntity(doc, arc);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
case "addText": {
|
|
123
|
+
const text = new TextEntity();
|
|
124
|
+
text.value = operation.text;
|
|
125
|
+
text.insertPoint = xyz(operation.at);
|
|
126
|
+
if (operation.height !== undefined)
|
|
127
|
+
text.height = operation.height;
|
|
128
|
+
if (operation.rotationDegrees !== undefined) {
|
|
129
|
+
text.rotation = degreesToRadians(operation.rotationDegrees);
|
|
130
|
+
}
|
|
131
|
+
applyCommonOptions(doc, text, operation);
|
|
132
|
+
addEntity(doc, text);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
case "addMText": {
|
|
136
|
+
const text = new MText();
|
|
137
|
+
text.value = operation.text;
|
|
138
|
+
text.insertPoint = xyz(operation.at);
|
|
139
|
+
if (operation.height !== undefined)
|
|
140
|
+
text.height = operation.height;
|
|
141
|
+
if (operation.width !== undefined)
|
|
142
|
+
text.rectangleWidth = operation.width;
|
|
143
|
+
if (operation.rotationDegrees !== undefined) {
|
|
144
|
+
text.rotation = degreesToRadians(operation.rotationDegrees);
|
|
145
|
+
}
|
|
146
|
+
applyCommonOptions(doc, text, operation);
|
|
147
|
+
addEntity(doc, text);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
case "addPolyline": {
|
|
151
|
+
const polyline = new LwPolyline(operation.points.map(xy));
|
|
152
|
+
polyline.isClosed = operation.closed ?? false;
|
|
153
|
+
applyCommonOptions(doc, polyline, operation);
|
|
154
|
+
addEntity(doc, polyline);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const entity = findEntity(doc, operation.entityId);
|
|
159
|
+
switch (operation.kind) {
|
|
160
|
+
case "setText":
|
|
161
|
+
if (!(entity instanceof TextEntity || entity instanceof MText)) {
|
|
162
|
+
throw new DwgCliError(`Entity is not editable text: ${operation.entityId}`, "ENTITY_NOT_TEXT", EXIT_USER_ERROR);
|
|
163
|
+
}
|
|
164
|
+
entity.value = operation.text;
|
|
165
|
+
return;
|
|
166
|
+
case "setLayer":
|
|
167
|
+
entity.layer = ensureLayer(doc, operation.layer);
|
|
168
|
+
return;
|
|
169
|
+
case "setColor":
|
|
170
|
+
entity.color = acadColor(operation.color);
|
|
171
|
+
return;
|
|
172
|
+
case "setLineType":
|
|
173
|
+
entity.lineType = ensureLineType(doc, operation.lineType);
|
|
174
|
+
return;
|
|
175
|
+
case "setLineWeight":
|
|
176
|
+
entity.lineWeight = lineWeight(operation.weight);
|
|
177
|
+
return;
|
|
178
|
+
case "setTransparency":
|
|
179
|
+
entity.transparency = Transparency.fromAlphaValue(operation.alpha);
|
|
180
|
+
return;
|
|
181
|
+
case "setVisibility":
|
|
182
|
+
entity.isInvisible = !operation.visible;
|
|
183
|
+
return;
|
|
184
|
+
case "move":
|
|
185
|
+
entity.applyTranslation(new XYZ(operation.dx, operation.dy, operation.dz ?? 0));
|
|
186
|
+
return;
|
|
187
|
+
case "rotate": {
|
|
188
|
+
const origin = operation.origin ? xyz(operation.origin) : XYZ.zero;
|
|
189
|
+
entity.applyTranslation(new XYZ(-origin.x, -origin.y, -origin.z));
|
|
190
|
+
entity.applyRotation(XYZ.axisZ, degreesToRadians(operation.angleDegrees));
|
|
191
|
+
entity.applyTranslation(origin);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
case "scale":
|
|
195
|
+
entity.applyScaling(new XYZ(operation.factor, operation.factor, operation.factor), operation.origin ? xyz(operation.origin) : XYZ.zero);
|
|
196
|
+
return;
|
|
197
|
+
case "copy": {
|
|
198
|
+
const clone = entity.clone();
|
|
199
|
+
clone.applyTranslation(new XYZ(operation.dx ?? 0, operation.dy ?? 0, operation.dz ?? 0));
|
|
200
|
+
addEntity(doc, clone);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
case "matchProperties": {
|
|
204
|
+
const source = findEntity(doc, operation.sourceId);
|
|
205
|
+
entity.matchProperties(source);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
case "setAttribute":
|
|
209
|
+
setAttribute(entity, operation.tag, operation.value, operation.entityId);
|
|
210
|
+
return;
|
|
211
|
+
case "delete":
|
|
212
|
+
deleteEntity(entity, operation.entityId);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export function editWithAcadTs(opts) {
|
|
217
|
+
if (opts.operations.length === 0) {
|
|
218
|
+
throw new DwgCliError("No acad-ts edit operation specified.", "MISSING_EDIT_OPERATION", EXIT_USER_ERROR);
|
|
219
|
+
}
|
|
220
|
+
if (opts.input === opts.output && !opts.overwrite) {
|
|
221
|
+
throw new DwgCliError("Refusing to edit in place without --overwrite. Provide --output or pass --overwrite.", "EDIT_REQUIRES_OUTPUT_OR_OVERWRITE", EXIT_USER_ERROR);
|
|
222
|
+
}
|
|
223
|
+
const { bytes, doc, format } = readAcadFile(opts.input);
|
|
224
|
+
for (const operation of opts.operations)
|
|
225
|
+
applyOperation(doc, operation);
|
|
226
|
+
writeOutput(opts.output, writeAcadDocument(doc, format, bytes.length));
|
|
227
|
+
return {
|
|
228
|
+
input: opts.input,
|
|
229
|
+
output: opts.output,
|
|
230
|
+
backend: "acad-ts",
|
|
231
|
+
operations: opts.operations,
|
|
232
|
+
changed: opts.operations.length,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Color } from "@node-projects/acad-ts";
|
|
2
|
+
import { readAcadFile, writeAcadSvg } from "./acad.js";
|
|
3
|
+
// acad-ts SvgWriter currently crashes when text resolves ByLayer/ByBlock
|
|
4
|
+
// through an index that has no RGB entry. Normalize render-only colors here;
|
|
5
|
+
// the source file is untouched because view reparses the document every time.
|
|
6
|
+
function makeEntityColorsRenderable(doc) {
|
|
7
|
+
for (const entity of doc.modelSpace?.entities ?? []) {
|
|
8
|
+
const candidate = entity;
|
|
9
|
+
if (candidate.color?.isByLayer || candidate.color?.isByBlock) {
|
|
10
|
+
candidate.color = Color.black;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function renderSvgWithAcadTs(file) {
|
|
15
|
+
const { doc } = readAcadFile(file);
|
|
16
|
+
makeEntityColorsRenderable(doc);
|
|
17
|
+
return {
|
|
18
|
+
input: file,
|
|
19
|
+
svg: writeAcadSvg(doc),
|
|
20
|
+
backend: "acad-ts",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CadDocument } from "@node-projects/acad-ts";
|
|
2
|
+
import type { DwgFormat } from "../types.js";
|
|
3
|
+
export interface AcadFile {
|
|
4
|
+
bytes: Uint8Array;
|
|
5
|
+
doc: CadDocument;
|
|
6
|
+
format: DwgFormat;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseAcadDocument(bytes: Uint8Array, format: DwgFormat): CadDocument;
|
|
9
|
+
export declare function readAcadFile(file: string): AcadFile;
|
|
10
|
+
export declare function writeAcadDocument(doc: CadDocument, format: DwgFormat, originalBytes: number): Uint8Array;
|
|
11
|
+
export declare function writeAcadSvg(doc: CadDocument): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { DwgReader, DwgWriter, DxfReader, DxfWriter, SvgWriter, } from "@node-projects/acad-ts";
|
|
2
|
+
import { EXIT_USER_ERROR } from "../utils/exit-codes.js";
|
|
3
|
+
import { arrayBufferFor } from "./adapter.js";
|
|
4
|
+
import { DwgCliError } from "./errors.js";
|
|
5
|
+
import { readCadFile } from "./files.js";
|
|
6
|
+
export function parseAcadDocument(bytes, format) {
|
|
7
|
+
try {
|
|
8
|
+
return format === "DWG"
|
|
9
|
+
? DwgReader.readFromStream(arrayBufferFor(bytes))
|
|
10
|
+
: DxfReader.readFromStream(bytes);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new DwgCliError(`Could not parse ${format}: ${error.message}`, "PARSE_FAILED", EXIT_USER_ERROR);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function readAcadFile(file) {
|
|
17
|
+
const { bytes, format } = readCadFile(file);
|
|
18
|
+
return { bytes, format, doc: parseAcadDocument(bytes, format) };
|
|
19
|
+
}
|
|
20
|
+
function writeDwg(doc, originalBytes) {
|
|
21
|
+
let size = Math.max(1024 * 1024, originalBytes * 4);
|
|
22
|
+
const maxSize = 512 * 1024 * 1024;
|
|
23
|
+
while (size <= maxSize) {
|
|
24
|
+
const buffer = new ArrayBuffer(size);
|
|
25
|
+
const writer = new DwgWriter(buffer, doc);
|
|
26
|
+
writer.write();
|
|
27
|
+
if (writer.bytesWritten <= size) {
|
|
28
|
+
return new Uint8Array(buffer).slice(0, writer.bytesWritten);
|
|
29
|
+
}
|
|
30
|
+
size *= 2;
|
|
31
|
+
}
|
|
32
|
+
throw new DwgCliError("Could not write DWG: output exceeded the maximum supported buffer size.", "ACAD_WRITE_FAILED", EXIT_USER_ERROR);
|
|
33
|
+
}
|
|
34
|
+
function writeDxf(doc) {
|
|
35
|
+
let content = "";
|
|
36
|
+
DxfWriter.writeToStream({
|
|
37
|
+
write(value) {
|
|
38
|
+
content +=
|
|
39
|
+
typeof value === "string" ? value : new TextDecoder().decode(value);
|
|
40
|
+
},
|
|
41
|
+
flush() { },
|
|
42
|
+
close() { },
|
|
43
|
+
}, doc);
|
|
44
|
+
return new TextEncoder().encode(content);
|
|
45
|
+
}
|
|
46
|
+
export function writeAcadDocument(doc, format, originalBytes) {
|
|
47
|
+
try {
|
|
48
|
+
return format === "DWG" ? writeDwg(doc, originalBytes) : writeDxf(doc);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof DwgCliError)
|
|
52
|
+
throw error;
|
|
53
|
+
throw new DwgCliError(`Could not write ${format}: ${error.message}`, "ACAD_WRITE_FAILED", EXIT_USER_ERROR);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function trimNullPadding(bytes) {
|
|
57
|
+
const end = bytes.indexOf(0);
|
|
58
|
+
return end === -1 ? bytes : bytes.slice(0, end);
|
|
59
|
+
}
|
|
60
|
+
function isOutputCapacityError(error) {
|
|
61
|
+
return /offset is out of bounds|too small|outside the bounds/i.test(error.message);
|
|
62
|
+
}
|
|
63
|
+
export function writeAcadSvg(doc) {
|
|
64
|
+
let size = 1024 * 1024;
|
|
65
|
+
const maxSize = 256 * 1024 * 1024;
|
|
66
|
+
while (size <= maxSize) {
|
|
67
|
+
const output = new Uint8Array(size);
|
|
68
|
+
try {
|
|
69
|
+
const writer = new SvgWriter(output, doc);
|
|
70
|
+
writer.write();
|
|
71
|
+
writer.dispose();
|
|
72
|
+
return new TextDecoder().decode(trimNullPadding(output));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (!isOutputCapacityError(error) || size >= maxSize) {
|
|
76
|
+
throw new DwgCliError(`Could not render SVG with acad-ts: ${error.message}`, "ACAD_SVG_FAILED", EXIT_USER_ERROR);
|
|
77
|
+
}
|
|
78
|
+
size *= 2;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new DwgCliError("Could not render SVG with acad-ts: output exceeded the maximum supported buffer size.", "ACAD_SVG_FAILED", EXIT_USER_ERROR);
|
|
82
|
+
}
|
package/dist/core/adapter.d.ts
CHANGED
|
@@ -6,9 +6,3 @@ export declare class AcadTsReader implements DrawingReader {
|
|
|
6
6
|
parse(_file: string, bytes: Uint8Array, format: DwgFormat): Promise<unknown>;
|
|
7
7
|
thumbnail(): Promise<ThumbnailResult | null>;
|
|
8
8
|
}
|
|
9
|
-
export declare class NativeLibreDwgReader implements DrawingReader {
|
|
10
|
-
private readonly toolDir?;
|
|
11
|
-
constructor(toolDir?: string | undefined);
|
|
12
|
-
parse(file: string, _bytes: Uint8Array, _format: DwgFormat): Promise<unknown>;
|
|
13
|
-
thumbnail(): Promise<ThumbnailResult | null>;
|
|
14
|
-
}
|
package/dist/core/adapter.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DwgReader, DxfReader } from "@node-projects/acad-ts";
|
|
2
2
|
import { EXIT_UNAVAILABLE, EXIT_USER_ERROR } from "../utils/exit-codes.js";
|
|
3
3
|
import { DwgCliError } from "./errors.js";
|
|
4
|
-
import { readJsonWithLibreDwg } from "./libredwg.js";
|
|
5
4
|
function asRecord(value) {
|
|
6
5
|
return value && typeof value === "object"
|
|
7
6
|
? value
|
|
@@ -151,15 +150,3 @@ export class AcadTsReader {
|
|
|
151
150
|
throw new DwgCliError("Thumbnail extraction is not available through acad-ts.", "THUMBNAIL_UNAVAILABLE", EXIT_UNAVAILABLE);
|
|
152
151
|
}
|
|
153
152
|
}
|
|
154
|
-
export class NativeLibreDwgReader {
|
|
155
|
-
toolDir;
|
|
156
|
-
constructor(toolDir) {
|
|
157
|
-
this.toolDir = toolDir;
|
|
158
|
-
}
|
|
159
|
-
async parse(file, _bytes, _format) {
|
|
160
|
-
return readJsonWithLibreDwg(file, { toolDir: this.toolDir }).json;
|
|
161
|
-
}
|
|
162
|
-
async thumbnail() {
|
|
163
|
-
throw new DwgCliError("Thumbnail extraction is not available through native LibreDWG.", "THUMBNAIL_UNAVAILABLE", EXIT_UNAVAILABLE);
|
|
164
|
-
}
|
|
165
|
-
}
|
package/dist/core/drawing.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { DrawingReader, DwgBlock, DwgDocument, DwgEntity, DwgLayer, EntityFilter, SvgResult, ThumbnailResult } from "../types.js";
|
|
2
2
|
export interface LoadOptions {
|
|
3
3
|
reader?: DrawingReader;
|
|
4
|
-
toolDir?: string;
|
|
5
4
|
}
|
|
6
5
|
export declare function loadDrawing(file: string, opts?: LoadOptions): Promise<DwgDocument>;
|
|
7
6
|
export declare function getInfo(file: string, opts?: LoadOptions): Promise<import("../types.js").DwgSummary>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { AcadColor, AcadEditOperation, AcadEditResult, AcadPoint, } from "./core/acad-edit.js";
|
|
2
|
+
export type { AcadSvgViewResult } from "./core/acad-view.js";
|
|
2
3
|
export type { DrawingOverview, DrawingOverviewOptions, OverviewBlock, OverviewEntityType, OverviewLayer, OverviewText, } from "./core/overview.js";
|
|
3
4
|
export type { DwgSearchOptions, DwgSearchResult } from "./core/search.js";
|
|
4
5
|
export { Dwg } from "./sdk.js";
|
package/dist/main.js
CHANGED
|
@@ -28,7 +28,8 @@ Examples:
|
|
|
28
28
|
$ cadcli entities drawing.dwg --type LINE --limit 20
|
|
29
29
|
$ cadcli search drawing.dwg "conference" --layer A-TEXT
|
|
30
30
|
$ cadcli view drawing.dwg -o drawing.svg
|
|
31
|
-
$ cadcli edit drawing.dwg --
|
|
31
|
+
$ cadcli edit drawing.dwg --set-text "Office" --text-id 2A -o edited.dwg
|
|
32
|
+
$ cadcli edit drawing.dwg --add-line 0,0:10,0 --new-layer A-WALL -o edited.dwg
|
|
32
33
|
|
|
33
34
|
Workflow: info → overview → search/entities → view/edit
|
|
34
35
|
|
|
@@ -41,8 +42,8 @@ Inspecting:
|
|
|
41
42
|
search <file> Search entities by text, type, layer, and raw fields
|
|
42
43
|
|
|
43
44
|
Viewing and editing:
|
|
44
|
-
view <file> Render
|
|
45
|
-
edit <file> Edit DWG/DXF with
|
|
45
|
+
view <file> Render an SVG preview
|
|
46
|
+
edit <file> Edit DWG/DXF with acad-ts operations
|
|
46
47
|
|
|
47
48
|
Conversion:
|
|
48
49
|
json <file> Export normalized JSON
|
|
@@ -120,15 +121,89 @@ program
|
|
|
120
121
|
});
|
|
121
122
|
program
|
|
122
123
|
.command("view <file>")
|
|
123
|
-
.description("Render
|
|
124
|
+
.description("Render an SVG preview")
|
|
124
125
|
.option("-o, --output <path>", "Output SVG file")
|
|
125
126
|
.action(async (file, opts, cmd) => view(file, { ...cmd.optsWithGlobals(), ...opts }));
|
|
126
127
|
program
|
|
127
128
|
.command("edit <file>")
|
|
128
|
-
.description("Edit DWG/DXF with
|
|
129
|
-
.
|
|
130
|
-
.option("-
|
|
129
|
+
.description("Edit DWG/DXF with acad-ts operations")
|
|
130
|
+
.option("--set-text <text>", "Set TEXT/MTEXT content")
|
|
131
|
+
.option("--text-id <id>", "Entity id/handle for --set-text")
|
|
132
|
+
.option("--set-layer <name>", "Move an entity to a layer")
|
|
133
|
+
.option("--layer-id <id>", "Entity id/handle for --set-layer")
|
|
134
|
+
.option("--set-color <color>", "Set entity color: bylayer, byblock, index, #rrggbb, or r,g,b")
|
|
135
|
+
.option("--color-id <id>", "Entity id/handle for --set-color")
|
|
136
|
+
.option("--set-linetype <name>", "Set entity line type")
|
|
137
|
+
.option("--linetype-id <id>", "Entity id/handle for --set-linetype")
|
|
138
|
+
.option("--set-lineweight <n>", "Set entity line weight enum value")
|
|
139
|
+
.option("--lineweight-id <id>", "Entity id/handle for --set-lineweight")
|
|
140
|
+
.option("--set-transparency <alpha>", "Set transparency alpha value")
|
|
141
|
+
.option("--transparency-id <id>", "Entity id/handle for --set-transparency")
|
|
142
|
+
.option("--hide <id>", "Hide an entity")
|
|
143
|
+
.option("--show <id>", "Show an entity")
|
|
144
|
+
.option("--move <id>", "Move an entity by --dx/--dy/--dz")
|
|
145
|
+
.option("--rotate <id>", "Rotate an entity around Z by --angle degrees")
|
|
146
|
+
.option("--scale <id>", "Scale an entity by --factor")
|
|
147
|
+
.option("--copy <id>", "Copy an entity, optionally offset by --dx/--dy/--dz")
|
|
148
|
+
.option("--match-properties <id>", "Copy visual properties from --source-id")
|
|
149
|
+
.option("--source-id <id>", "Source entity id for --match-properties")
|
|
150
|
+
.option("--set-attr <tag=value>", "Set a block insert attribute")
|
|
151
|
+
.option("--insert-id <id>", "Block insert id for --set-attr")
|
|
152
|
+
.option("--dx <n>", "X delta")
|
|
153
|
+
.option("--dy <n>", "Y delta")
|
|
154
|
+
.option("--dz <n>", "Z delta")
|
|
155
|
+
.option("--angle <degrees>", "Angle in degrees")
|
|
156
|
+
.option("--origin <x,y,z>", "Origin for rotate/scale")
|
|
157
|
+
.option("--factor <n>", "Scale factor")
|
|
158
|
+
.option("--add-point <x,y,z>", "Add a POINT")
|
|
159
|
+
.option("--add-line <from:to>", "Add a LINE, e.g. 0,0:10,0")
|
|
160
|
+
.option("--add-circle <center:radius>", "Add a CIRCLE, e.g. 5,5:2")
|
|
161
|
+
.option("--add-arc <center:radius:start:end>", "Add an ARC with degree angles")
|
|
162
|
+
.option("--add-text <text>", "Add TEXT at --at")
|
|
163
|
+
.option("--add-mtext <text>", "Add MTEXT at --at")
|
|
164
|
+
.option("--points <p1;p2[;p3]>", "Add an LWPOLYLINE from x,y points")
|
|
165
|
+
.option("--closed", "Close a polyline added with --points")
|
|
166
|
+
.option("--at <x,y,z>", "Insertion point for text")
|
|
167
|
+
.option("--height <n>", "Text height")
|
|
168
|
+
.option("--width <n>", "MText width")
|
|
169
|
+
.option("--new-layer <name>", "Layer for add-* entities")
|
|
170
|
+
.option("--new-color <color>", "Color for add-* entities")
|
|
171
|
+
.option("--delete <id>", "Delete an entity")
|
|
172
|
+
.option("-o, --output <path>", "Write edited copy to this file; required unless --overwrite")
|
|
131
173
|
.option("--overwrite", "Allow editing the input file in place")
|
|
174
|
+
.addHelpText("after", `
|
|
175
|
+
|
|
176
|
+
Examples:
|
|
177
|
+
Find ids first:
|
|
178
|
+
$ cadcli entities drawing.dwg --type TEXT --json
|
|
179
|
+
$ cadcli search drawing.dwg "office" --json
|
|
180
|
+
|
|
181
|
+
Update existing entities:
|
|
182
|
+
$ cadcli edit drawing.dwg --set-text "Office" --text-id 2A -o edited.dwg
|
|
183
|
+
$ cadcli edit drawing.dwg --set-color '#ff0000' --color-id 2A -o edited.dwg
|
|
184
|
+
$ cadcli edit drawing.dwg --move 2A --dx 10 --dy 0 -o edited.dwg
|
|
185
|
+
$ cadcli edit drawing.dwg --rotate 2A --angle 90 --origin 0,0 -o edited.dwg
|
|
186
|
+
$ cadcli edit drawing.dwg --copy 2A --dx 10 --dy 0 -o edited.dwg
|
|
187
|
+
$ cadcli edit drawing.dwg --delete 2A -o edited.dwg
|
|
188
|
+
|
|
189
|
+
Add new entities:
|
|
190
|
+
$ cadcli edit drawing.dwg --add-line 0,0:10,0 --new-layer A-WALL -o edited.dwg
|
|
191
|
+
$ cadcli edit drawing.dwg --add-circle 5,5:2 -o edited.dwg
|
|
192
|
+
$ cadcli edit drawing.dwg --add-text "Label" --at 5,5 --height 2.5 -o edited.dwg
|
|
193
|
+
$ cadcli edit drawing.dwg --points '0,0;10,0;10,5' --closed -o edited.dwg
|
|
194
|
+
|
|
195
|
+
Formats:
|
|
196
|
+
points: x,y or x,y,z
|
|
197
|
+
line: from:to, e.g. 0,0:10,0
|
|
198
|
+
circle: center:radius, e.g. 5,5:2
|
|
199
|
+
arc: center:radius:startAngle:endAngle, e.g. 5,5:2:0:90
|
|
200
|
+
color: bylayer, byblock, AutoCAD color index, #rrggbb, or r,g,b
|
|
201
|
+
|
|
202
|
+
Safety:
|
|
203
|
+
cadcli refuses to modify the input file by default.
|
|
204
|
+
Use -o/--output to write an edited copy.
|
|
205
|
+
Use --overwrite only when you intentionally want to edit the input file in place.
|
|
206
|
+
`)
|
|
132
207
|
.action(async (file, opts, cmd) => edit(file, { ...cmd.optsWithGlobals(), ...opts }));
|
|
133
208
|
program
|
|
134
209
|
.command("json <file>")
|
package/dist/sdk.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type AcadEditOperation, type AcadEditResult } from "./core/acad-edit.js";
|
|
2
|
+
import { type AcadSvgViewResult } from "./core/acad-view.js";
|
|
1
3
|
import { type LoadOptions } from "./core/drawing.js";
|
|
2
|
-
import { type LibreDwgEditResult, type LibreDwgViewResult } from "./core/libredwg.js";
|
|
3
4
|
import { type DrawingOverview, type DrawingOverviewOptions } from "./core/overview.js";
|
|
4
5
|
import { type DwgSearchOptions, type DwgSearchResult } from "./core/search.js";
|
|
5
6
|
import type { DwgBlock, DwgDocument, DwgEntity, DwgLayer, DwgSummary, EntityFilter, SvgResult, ThumbnailResult } from "./types.js";
|
|
@@ -16,15 +17,12 @@ export declare class Dwg {
|
|
|
16
17
|
overview(opts?: DrawingOverviewOptions): Promise<DrawingOverview>;
|
|
17
18
|
json(): Promise<DwgDocument>;
|
|
18
19
|
svg(): Promise<SvgResult>;
|
|
19
|
-
view(
|
|
20
|
-
toolDir?: string;
|
|
21
|
-
}): LibreDwgViewResult;
|
|
20
|
+
view(): AcadSvgViewResult;
|
|
22
21
|
edit(opts: {
|
|
23
22
|
output: string;
|
|
24
|
-
|
|
23
|
+
operations: AcadEditOperation[];
|
|
25
24
|
overwrite?: boolean;
|
|
26
|
-
|
|
27
|
-
}): LibreDwgEditResult;
|
|
25
|
+
}): AcadEditResult;
|
|
28
26
|
thumbnail(): Promise<ThumbnailResult>;
|
|
29
27
|
static open(file: string, opts?: LoadOptions): Dwg;
|
|
30
28
|
}
|
package/dist/sdk.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { editWithAcadTs, } from "./core/acad-edit.js";
|
|
2
|
+
import { renderSvgWithAcadTs, } from "./core/acad-view.js";
|
|
1
3
|
import { getBlocks, getEntities, getInfo, getLayers, getThumbnail, loadDrawing, toJson, toSvg, } from "./core/drawing.js";
|
|
2
|
-
import { editWithLibreDwgFilter, renderSvgWithLibreDwg, } from "./core/libredwg.js";
|
|
3
4
|
import { getOverview, } from "./core/overview.js";
|
|
4
5
|
import { searchDrawing, } from "./core/search.js";
|
|
5
6
|
export class Dwg {
|
|
@@ -36,16 +37,15 @@ export class Dwg {
|
|
|
36
37
|
svg() {
|
|
37
38
|
return toSvg(this.file, this.opts);
|
|
38
39
|
}
|
|
39
|
-
view(
|
|
40
|
-
return
|
|
40
|
+
view() {
|
|
41
|
+
return renderSvgWithAcadTs(this.file);
|
|
41
42
|
}
|
|
42
43
|
edit(opts) {
|
|
43
|
-
return
|
|
44
|
+
return editWithAcadTs({
|
|
44
45
|
input: this.file,
|
|
45
46
|
output: opts.output,
|
|
46
|
-
|
|
47
|
+
operations: opts.operations,
|
|
47
48
|
overwrite: opts.overwrite,
|
|
48
|
-
toolDir: opts.toolDir,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
thumbnail() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miclivs/cadcli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Agent-friendly CAD inspection, search, viewing, and editing for DWG/DXF files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"cad",
|
|
50
50
|
"cli",
|
|
51
51
|
"sdk",
|
|
52
|
-
"libredwg",
|
|
53
52
|
"svg",
|
|
54
53
|
"conversion",
|
|
55
54
|
"pi-package"
|
package/skills/cadcli/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cadcli
|
|
3
|
-
description: Inspect, search, summarize, preview, and safely edit DWG/DXF CAD drawings with the cadcli CLI. Use when the user asks about CAD files, floorplans, DWG/DXF contents, layers, blocks, entities, text, previews, or
|
|
3
|
+
description: Inspect, search, summarize, preview, and safely edit DWG/DXF CAD drawings with the cadcli CLI. Use when the user asks about CAD files, floorplans, DWG/DXF contents, layers, blocks, entities, text, previews, or CAD edits.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# cadcli
|
|
@@ -15,7 +15,7 @@ cadcli entities <file> --limit 20 --json
|
|
|
15
15
|
cadcli search <file> "query" --json
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
For visual checks,
|
|
18
|
+
For visual checks, render an SVG preview:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
cadcli view <file> -o preview.svg
|
|
@@ -27,8 +27,25 @@ For lightweight previews from the normalized model:
|
|
|
27
27
|
cadcli svg <file> -o preview.svg
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
For edits,
|
|
30
|
+
For edits, find handles first, then write an edited copy. Never overwrite by default; use `--overwrite` only when the user explicitly asks for in-place edits:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
cadcli
|
|
33
|
+
cadcli entities <file> --type TEXT --json
|
|
34
|
+
cadcli search <file> "office" --json
|
|
35
|
+
|
|
36
|
+
cadcli edit <file> --set-text 'New label' --text-id <id> -o edited.dwg
|
|
37
|
+
cadcli edit <file> --set-layer A-TEXT --layer-id <id> -o edited.dwg
|
|
38
|
+
cadcli edit <file> --set-color '#ff0000' --color-id <id> -o edited.dwg
|
|
39
|
+
cadcli edit <file> --move <id> --dx 10 --dy 0 -o edited.dwg
|
|
40
|
+
cadcli edit <file> --rotate <id> --angle 90 --origin 0,0 -o edited.dwg
|
|
41
|
+
cadcli edit <file> --scale <id> --factor 2 -o edited.dwg
|
|
42
|
+
cadcli edit <file> --copy <id> --dx 10 --dy 0 -o edited.dwg
|
|
43
|
+
cadcli edit <file> --delete <id> -o edited.dwg
|
|
44
|
+
|
|
45
|
+
cadcli edit <file> --add-line 0,0:10,0 --new-layer A-WALL -o edited.dwg
|
|
46
|
+
cadcli edit <file> --add-circle 5,5:2 -o edited.dwg
|
|
47
|
+
cadcli edit <file> --add-text 'Label' --at 5,5 --height 2.5 -o edited.dwg
|
|
48
|
+
cadcli edit <file> --points '0,0;10,0;10,5' --closed -o edited.dwg
|
|
34
49
|
```
|
|
50
|
+
|
|
51
|
+
Edit formats: points are `x,y` or `x,y,z`; line is `from:to`; circle is `center:radius`; arc is `center:radius:startAngle:endAngle`; color is `bylayer`, `byblock`, an AutoCAD color index, `#rrggbb`, or `r,g,b`.
|