@miclivs/cadcli 0.1.2 → 0.3.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 +70 -14
- 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/query.d.ts +7 -0
- package/dist/commands/query.js +45 -0
- 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 +16 -14
- package/dist/core/drawing.d.ts +0 -1
- package/dist/core/drawing.js +2 -1
- package/dist/core/normalize.js +4 -1
- package/dist/core/query.d.ts +17 -0
- package/dist/core/query.js +232 -0
- package/dist/core/text-normalize.d.ts +3 -0
- package/dist/core/text-normalize.js +104 -0
- package/dist/index.d.ts +2 -1
- package/dist/main.js +98 -7
- package/dist/sdk.d.ts +5 -7
- package/dist/sdk.js +6 -6
- package/dist/types.d.ts +9 -0
- package/package.json +2 -2
- package/skills/cadcli/SKILL.md +50 -9
- package/dist/core/libredwg.d.ts +0 -37
- package/dist/core/libredwg.js +0 -86
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { EXIT_UNAVAILABLE, EXIT_USER_ERROR } from "../utils/exit-codes.js";
|
|
2
|
+
import { DwgCliError } from "./errors.js";
|
|
3
|
+
const QUERY_SCHEMA_TABLES = [
|
|
4
|
+
{
|
|
5
|
+
name: "summary",
|
|
6
|
+
description: "One-row drawing summary.",
|
|
7
|
+
columns: [
|
|
8
|
+
{ name: "file", type: "text" },
|
|
9
|
+
{ name: "format", type: "text" },
|
|
10
|
+
{ name: "version", type: "text" },
|
|
11
|
+
{ name: "entity_count", type: "integer" },
|
|
12
|
+
{ name: "layer_count", type: "integer" },
|
|
13
|
+
{ name: "block_count", type: "integer" },
|
|
14
|
+
{ name: "unsupported_count", type: "integer" },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "metadata",
|
|
19
|
+
description: "Document metadata and normalization notes.",
|
|
20
|
+
columns: [
|
|
21
|
+
{ name: "key", type: "text" },
|
|
22
|
+
{ name: "value", type: "text" },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "layers",
|
|
27
|
+
description: "Drawing layers and entity counts.",
|
|
28
|
+
columns: [
|
|
29
|
+
{ name: "name", type: "text" },
|
|
30
|
+
{ name: "entity_count", type: "integer" },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "blocks",
|
|
35
|
+
description: "Block definitions and definition entity counts.",
|
|
36
|
+
columns: [
|
|
37
|
+
{ name: "name", type: "text" },
|
|
38
|
+
{ name: "entity_count", type: "integer" },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "entities",
|
|
43
|
+
description: "All normalized model-space entities, projected narrowly.",
|
|
44
|
+
columns: [
|
|
45
|
+
{ name: "id", type: "text" },
|
|
46
|
+
{ name: "type", type: "text" },
|
|
47
|
+
{ name: "layer", type: "text" },
|
|
48
|
+
{ name: "color", type: "text" },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "texts",
|
|
53
|
+
description: "TEXT and MTEXT entities with normalized text and insertion points.",
|
|
54
|
+
columns: [
|
|
55
|
+
{ name: "id", type: "text" },
|
|
56
|
+
{ name: "type", type: "text" },
|
|
57
|
+
{ name: "layer", type: "text" },
|
|
58
|
+
{ name: "text", type: "text" },
|
|
59
|
+
{ name: "x", type: "real" },
|
|
60
|
+
{ name: "y", type: "real" },
|
|
61
|
+
{ name: "text_style", type: "text" },
|
|
62
|
+
{ name: "text_style_file", type: "text" },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "inserts",
|
|
67
|
+
description: "Placed block instances with insertion points.",
|
|
68
|
+
columns: [
|
|
69
|
+
{ name: "id", type: "text" },
|
|
70
|
+
{ name: "layer", type: "text" },
|
|
71
|
+
{ name: "block_name", type: "text" },
|
|
72
|
+
{ name: "x", type: "real" },
|
|
73
|
+
{ name: "y", type: "real" },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
export function querySchema() {
|
|
78
|
+
return QUERY_SCHEMA_TABLES.map((table) => `${table.name}(${table.columns.map((column) => column.name).join(", ")})`).join("\n");
|
|
79
|
+
}
|
|
80
|
+
export function querySchemaTables() {
|
|
81
|
+
return QUERY_SCHEMA_TABLES;
|
|
82
|
+
}
|
|
83
|
+
async function openMemoryDatabase() {
|
|
84
|
+
try {
|
|
85
|
+
const { default: Database } = await import("better-sqlite3");
|
|
86
|
+
return new Database(":memory:");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new DwgCliError("cadcli query requires the optional better-sqlite3 dependency.", "QUERY_UNAVAILABLE", EXIT_UNAVAILABLE);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function sqlValue(value) {
|
|
93
|
+
if (value === undefined || value === null)
|
|
94
|
+
return null;
|
|
95
|
+
if (typeof value === "number" || typeof value === "string")
|
|
96
|
+
return value;
|
|
97
|
+
return String(value);
|
|
98
|
+
}
|
|
99
|
+
function point(entity) {
|
|
100
|
+
const value = entity.data.insertionPoint ?? entity.data.position ?? entity.data.point;
|
|
101
|
+
if (!value || typeof value !== "object")
|
|
102
|
+
return { x: null, y: null };
|
|
103
|
+
const rec = value;
|
|
104
|
+
const x = Number(rec.x ?? rec.X);
|
|
105
|
+
const y = Number(rec.y ?? rec.Y);
|
|
106
|
+
return {
|
|
107
|
+
x: Number.isFinite(x) ? x : null,
|
|
108
|
+
y: Number.isFinite(y) ? y : null,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function textValue(entity) {
|
|
112
|
+
const value = entity.data.text ?? entity.data.value ?? entity.data.Text;
|
|
113
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
114
|
+
}
|
|
115
|
+
function blockName(entity) {
|
|
116
|
+
const value = entity.data.blockName ?? entity.data.block_name ?? entity.data.name;
|
|
117
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
118
|
+
}
|
|
119
|
+
function createSchema(db) {
|
|
120
|
+
db.exec(`
|
|
121
|
+
CREATE TABLE summary (
|
|
122
|
+
file TEXT,
|
|
123
|
+
format TEXT,
|
|
124
|
+
version TEXT,
|
|
125
|
+
entity_count INTEGER,
|
|
126
|
+
layer_count INTEGER,
|
|
127
|
+
block_count INTEGER,
|
|
128
|
+
unsupported_count INTEGER
|
|
129
|
+
);
|
|
130
|
+
CREATE TABLE metadata (key TEXT PRIMARY KEY, value TEXT);
|
|
131
|
+
CREATE TABLE layers (name TEXT PRIMARY KEY, entity_count INTEGER);
|
|
132
|
+
CREATE TABLE blocks (name TEXT PRIMARY KEY, entity_count INTEGER);
|
|
133
|
+
CREATE TABLE entities (id TEXT PRIMARY KEY, type TEXT, layer TEXT, color TEXT);
|
|
134
|
+
CREATE TABLE texts (
|
|
135
|
+
id TEXT PRIMARY KEY,
|
|
136
|
+
type TEXT,
|
|
137
|
+
layer TEXT,
|
|
138
|
+
text TEXT,
|
|
139
|
+
x REAL,
|
|
140
|
+
y REAL,
|
|
141
|
+
text_style TEXT,
|
|
142
|
+
text_style_file TEXT
|
|
143
|
+
);
|
|
144
|
+
CREATE TABLE inserts (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
layer TEXT,
|
|
147
|
+
block_name TEXT,
|
|
148
|
+
x REAL,
|
|
149
|
+
y REAL
|
|
150
|
+
);
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
function registerFunctions(db) {
|
|
154
|
+
db.function("regexp", (pattern, value) => {
|
|
155
|
+
if (typeof pattern !== "string" || value === null || value === undefined) {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
return new RegExp(pattern, "u").test(String(value)) ? 1 : 0;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function insertDocument(db, doc) {
|
|
167
|
+
db.prepare("INSERT INTO summary VALUES (?, ?, ?, ?, ?, ?, ?)").run(doc.summary.file, doc.summary.format, doc.summary.version ?? null, doc.summary.counts.entities, doc.summary.counts.layers, doc.summary.counts.blocks, doc.summary.counts.unsupported);
|
|
168
|
+
const insertMetadata = db.prepare("INSERT INTO metadata VALUES (?, ?)");
|
|
169
|
+
if (doc.metadata.codePage)
|
|
170
|
+
insertMetadata.run("code_page", doc.metadata.codePage);
|
|
171
|
+
if (doc.metadata.textNormalization) {
|
|
172
|
+
insertMetadata.run("text_normalization_applied", doc.metadata.textNormalization.applied.join(","));
|
|
173
|
+
insertMetadata.run("text_normalization_entities_changed", doc.metadata.textNormalization.entitiesChanged);
|
|
174
|
+
}
|
|
175
|
+
const insertLayer = db.prepare("INSERT INTO layers VALUES (?, ?)");
|
|
176
|
+
const insertBlock = db.prepare("INSERT INTO blocks VALUES (?, ?)");
|
|
177
|
+
const insertEntity = db.prepare("INSERT INTO entities VALUES (?, ?, ?, ?)");
|
|
178
|
+
const insertText = db.prepare("INSERT INTO texts VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
179
|
+
const insertInsert = db.prepare("INSERT INTO inserts VALUES (?, ?, ?, ?, ?)");
|
|
180
|
+
db.exec("BEGIN");
|
|
181
|
+
try {
|
|
182
|
+
for (const layer of doc.layers)
|
|
183
|
+
insertLayer.run(layer.name, layer.entityCount);
|
|
184
|
+
for (const block of doc.blocks)
|
|
185
|
+
insertBlock.run(block.name, block.entityCount);
|
|
186
|
+
for (const entity of doc.entities) {
|
|
187
|
+
insertEntity.run(entity.id, entity.type, entity.layer ?? null, sqlValue(entity.color));
|
|
188
|
+
const { x, y } = point(entity);
|
|
189
|
+
const text = textValue(entity);
|
|
190
|
+
if ((entity.type === "TEXT" || entity.type === "MTEXT") && text) {
|
|
191
|
+
insertText.run(entity.id, entity.type, entity.layer ?? null, text, x, y, sqlValue(entity.data.textStyle), sqlValue(entity.data.textStyleFile));
|
|
192
|
+
}
|
|
193
|
+
if (entity.type === "INSERT") {
|
|
194
|
+
insertInsert.run(entity.id, entity.layer ?? null, blockName(entity), x, y);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
db.exec("COMMIT");
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
db.exec("ROLLBACK");
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function ensureSelectQuery(sql) {
|
|
205
|
+
const trimmed = sql.trim();
|
|
206
|
+
if (!/^(select|with)\b/i.test(trimmed)) {
|
|
207
|
+
throw new DwgCliError("Query SQL must be a SELECT statement.", "INVALID_QUERY_SQL", EXIT_USER_ERROR);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function ensureReadOnlyStatement(statement) {
|
|
211
|
+
if (statement.readonly === false) {
|
|
212
|
+
throw new DwgCliError("Query SQL must be read-only.", "INVALID_QUERY_SQL", EXIT_USER_ERROR);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export async function queryDrawing(doc, sql) {
|
|
216
|
+
ensureSelectQuery(sql);
|
|
217
|
+
const db = await openMemoryDatabase();
|
|
218
|
+
try {
|
|
219
|
+
createSchema(db);
|
|
220
|
+
registerFunctions(db);
|
|
221
|
+
insertDocument(db, doc);
|
|
222
|
+
const statement = db.prepare(sql);
|
|
223
|
+
ensureReadOnlyStatement(statement);
|
|
224
|
+
const rows = statement.all();
|
|
225
|
+
const columns = statement.columns?.().map((column) => column.name) ??
|
|
226
|
+
Object.keys(rows[0] ?? {});
|
|
227
|
+
return { columns, rows };
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
db.close();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import hebrewLayout from "convert-layout/he.js";
|
|
2
|
+
const TEXT_NORMALIZATION_ROUTES = [
|
|
3
|
+
{
|
|
4
|
+
name: "hebrew-keyboard",
|
|
5
|
+
codePages: ["ansi_1255"],
|
|
6
|
+
textStyleFiles: ["gil.shx", "narkism$.shx", "heb.shx", "oron", "oron1"],
|
|
7
|
+
fromLatinKeyboard: hebrewLayout.fromEn,
|
|
8
|
+
targetScript: "hebrew",
|
|
9
|
+
},
|
|
10
|
+
];
|
|
11
|
+
function asText(value) {
|
|
12
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
function normalizedToken(value) {
|
|
15
|
+
return value?.trim().toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
function textStyleFile(entity) {
|
|
18
|
+
return normalizedToken(asText(entity.data.textStyleFile));
|
|
19
|
+
}
|
|
20
|
+
function isTextEntity(entity) {
|
|
21
|
+
return entity.type === "TEXT" || entity.type === "MTEXT";
|
|
22
|
+
}
|
|
23
|
+
function hasTargetScript(text, script) {
|
|
24
|
+
switch (script) {
|
|
25
|
+
case "hebrew":
|
|
26
|
+
return /[\u0590-\u05ff]/u.test(text);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function targetScriptCount(text, script) {
|
|
30
|
+
switch (script) {
|
|
31
|
+
case "hebrew":
|
|
32
|
+
return text.match(/[\u0590-\u05ff]/gu)?.length ?? 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function mostlyLatinKeyboardText(text, targetScript) {
|
|
36
|
+
const latinLetters = text.match(/[A-Za-z]/g)?.length ?? 0;
|
|
37
|
+
return latinLetters > 0 && targetScriptCount(text, targetScript) === 0;
|
|
38
|
+
}
|
|
39
|
+
function routeMatches(route, doc, entity) {
|
|
40
|
+
const codePage = normalizedToken(doc.metadata.codePage);
|
|
41
|
+
const styleFile = textStyleFile(entity);
|
|
42
|
+
return Boolean(codePage &&
|
|
43
|
+
styleFile &&
|
|
44
|
+
route.codePages.includes(codePage) &&
|
|
45
|
+
route.textStyleFiles.includes(styleFile));
|
|
46
|
+
}
|
|
47
|
+
function routeFor(doc, entity) {
|
|
48
|
+
return TEXT_NORMALIZATION_ROUTES.find((route) => routeMatches(route, doc, entity));
|
|
49
|
+
}
|
|
50
|
+
function normalizeEntityText(doc, entity) {
|
|
51
|
+
if (!isTextEntity(entity))
|
|
52
|
+
return { entity };
|
|
53
|
+
const route = routeFor(doc, entity);
|
|
54
|
+
if (!route)
|
|
55
|
+
return { entity };
|
|
56
|
+
const text = asText(entity.data.text);
|
|
57
|
+
if (!text || !mostlyLatinKeyboardText(text, route.targetScript)) {
|
|
58
|
+
return { entity };
|
|
59
|
+
}
|
|
60
|
+
const normalized = route.fromLatinKeyboard(text);
|
|
61
|
+
if (normalized === text || !hasTargetScript(normalized, route.targetScript)) {
|
|
62
|
+
return { entity };
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
normalizer: route.name,
|
|
66
|
+
entity: {
|
|
67
|
+
...entity,
|
|
68
|
+
data: {
|
|
69
|
+
...entity.data,
|
|
70
|
+
text: normalized,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function normalizeHebrewKeyboardText(text) {
|
|
76
|
+
return hebrewLayout.fromEn(text);
|
|
77
|
+
}
|
|
78
|
+
export function normalizeTextAuto(doc) {
|
|
79
|
+
const applied = new Set();
|
|
80
|
+
let entitiesChanged = 0;
|
|
81
|
+
const entities = doc.entities.map((entity) => {
|
|
82
|
+
const result = normalizeEntityText(doc, entity);
|
|
83
|
+
if (!result.normalizer)
|
|
84
|
+
return entity;
|
|
85
|
+
entitiesChanged++;
|
|
86
|
+
applied.add(result.normalizer);
|
|
87
|
+
return result.entity;
|
|
88
|
+
});
|
|
89
|
+
if (entitiesChanged === 0)
|
|
90
|
+
return doc;
|
|
91
|
+
const unsupportedIds = new Set(doc.unsupported.map((entity) => entity.id));
|
|
92
|
+
return {
|
|
93
|
+
...doc,
|
|
94
|
+
entities,
|
|
95
|
+
unsupported: entities.filter((entity) => unsupportedIds.has(entity.id)),
|
|
96
|
+
metadata: {
|
|
97
|
+
...doc.metadata,
|
|
98
|
+
textNormalization: {
|
|
99
|
+
applied: [...applied],
|
|
100
|
+
entitiesChanged,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
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
|
@@ -9,6 +9,7 @@ import { info } from "./commands/info.js";
|
|
|
9
9
|
import { json } from "./commands/json.js";
|
|
10
10
|
import { layers } from "./commands/layers.js";
|
|
11
11
|
import { overview } from "./commands/overview.js";
|
|
12
|
+
import { query } from "./commands/query.js";
|
|
12
13
|
import { search } from "./commands/search.js";
|
|
13
14
|
import { svg } from "./commands/svg.js";
|
|
14
15
|
import { thumbnail } from "./commands/thumbnail.js";
|
|
@@ -27,8 +28,10 @@ Examples:
|
|
|
27
28
|
$ cadcli layers drawing.dwg --json List layers for scripts/agents
|
|
28
29
|
$ cadcli entities drawing.dwg --type LINE --limit 20
|
|
29
30
|
$ cadcli search drawing.dwg "conference" --layer A-TEXT
|
|
31
|
+
$ cadcli query drawing.dwg --sql "select text, x, y from texts"
|
|
30
32
|
$ cadcli view drawing.dwg -o drawing.svg
|
|
31
|
-
$ cadcli edit drawing.dwg --
|
|
33
|
+
$ cadcli edit drawing.dwg --set-text "Office" --text-id 2A -o edited.dwg
|
|
34
|
+
$ cadcli edit drawing.dwg --add-line 0,0:10,0 --new-layer A-WALL -o edited.dwg
|
|
32
35
|
|
|
33
36
|
Workflow: info → overview → search/entities → view/edit
|
|
34
37
|
|
|
@@ -39,16 +42,23 @@ Inspecting:
|
|
|
39
42
|
blocks <file> List blocks
|
|
40
43
|
entities <file> List/filter entities
|
|
41
44
|
search <file> Search entities by text, type, layer, and raw fields
|
|
45
|
+
query <file> Query normalized CAD tables with SQL
|
|
42
46
|
|
|
43
47
|
Viewing and editing:
|
|
44
|
-
view <file> Render
|
|
45
|
-
edit <file> Edit DWG/DXF with
|
|
48
|
+
view <file> Render an SVG preview
|
|
49
|
+
edit <file> Edit DWG/DXF with acad-ts operations
|
|
46
50
|
|
|
47
51
|
Conversion:
|
|
48
52
|
json <file> Export normalized JSON
|
|
49
53
|
svg <file> Render best-effort SVG from normalized JSON
|
|
50
54
|
thumbnail <file> Extract embedded thumbnail when available
|
|
51
55
|
|
|
56
|
+
Querying:
|
|
57
|
+
query <file> Query normalized CAD tables with SQL
|
|
58
|
+
--schema Show available query tables
|
|
59
|
+
--sql <query> Run an inline SELECT query
|
|
60
|
+
--file <path> Read a SELECT query from a file
|
|
61
|
+
|
|
52
62
|
Options:
|
|
53
63
|
--json Output as JSON
|
|
54
64
|
-q, --quiet Suppress non-essential output
|
|
@@ -118,17 +128,98 @@ program
|
|
|
118
128
|
root.query = root.query || queryWords.join(" ");
|
|
119
129
|
await search(file, root);
|
|
120
130
|
});
|
|
131
|
+
program
|
|
132
|
+
.command("query <file>")
|
|
133
|
+
.description("Query normalized CAD tables with SQL")
|
|
134
|
+
.option("--sql <query>", "SQL SELECT query to run")
|
|
135
|
+
.option("--file <path>", "Read SQL query from a file")
|
|
136
|
+
.option("--schema", "Show available query tables")
|
|
137
|
+
.action(async (file, opts, cmd) => query(file, { ...cmd.optsWithGlobals(), ...opts }));
|
|
121
138
|
program
|
|
122
139
|
.command("view <file>")
|
|
123
|
-
.description("Render
|
|
140
|
+
.description("Render an SVG preview")
|
|
124
141
|
.option("-o, --output <path>", "Output SVG file")
|
|
125
142
|
.action(async (file, opts, cmd) => view(file, { ...cmd.optsWithGlobals(), ...opts }));
|
|
126
143
|
program
|
|
127
144
|
.command("edit <file>")
|
|
128
|
-
.description("Edit DWG/DXF with
|
|
129
|
-
.
|
|
130
|
-
.option("-
|
|
145
|
+
.description("Edit DWG/DXF with acad-ts operations")
|
|
146
|
+
.option("--set-text <text>", "Set TEXT/MTEXT content")
|
|
147
|
+
.option("--text-id <id>", "Entity id/handle for --set-text")
|
|
148
|
+
.option("--set-layer <name>", "Move an entity to a layer")
|
|
149
|
+
.option("--layer-id <id>", "Entity id/handle for --set-layer")
|
|
150
|
+
.option("--set-color <color>", "Set entity color: bylayer, byblock, index, #rrggbb, or r,g,b")
|
|
151
|
+
.option("--color-id <id>", "Entity id/handle for --set-color")
|
|
152
|
+
.option("--set-linetype <name>", "Set entity line type")
|
|
153
|
+
.option("--linetype-id <id>", "Entity id/handle for --set-linetype")
|
|
154
|
+
.option("--set-lineweight <n>", "Set entity line weight enum value")
|
|
155
|
+
.option("--lineweight-id <id>", "Entity id/handle for --set-lineweight")
|
|
156
|
+
.option("--set-transparency <alpha>", "Set transparency alpha value")
|
|
157
|
+
.option("--transparency-id <id>", "Entity id/handle for --set-transparency")
|
|
158
|
+
.option("--hide <id>", "Hide an entity")
|
|
159
|
+
.option("--show <id>", "Show an entity")
|
|
160
|
+
.option("--move <id>", "Move an entity by --dx/--dy/--dz")
|
|
161
|
+
.option("--rotate <id>", "Rotate an entity around Z by --angle degrees")
|
|
162
|
+
.option("--scale <id>", "Scale an entity by --factor")
|
|
163
|
+
.option("--copy <id>", "Copy an entity, optionally offset by --dx/--dy/--dz")
|
|
164
|
+
.option("--match-properties <id>", "Copy visual properties from --source-id")
|
|
165
|
+
.option("--source-id <id>", "Source entity id for --match-properties")
|
|
166
|
+
.option("--set-attr <tag=value>", "Set a block insert attribute")
|
|
167
|
+
.option("--insert-id <id>", "Block insert id for --set-attr")
|
|
168
|
+
.option("--dx <n>", "X delta")
|
|
169
|
+
.option("--dy <n>", "Y delta")
|
|
170
|
+
.option("--dz <n>", "Z delta")
|
|
171
|
+
.option("--angle <degrees>", "Angle in degrees")
|
|
172
|
+
.option("--origin <x,y,z>", "Origin for rotate/scale")
|
|
173
|
+
.option("--factor <n>", "Scale factor")
|
|
174
|
+
.option("--add-point <x,y,z>", "Add a POINT")
|
|
175
|
+
.option("--add-line <from:to>", "Add a LINE, e.g. 0,0:10,0")
|
|
176
|
+
.option("--add-circle <center:radius>", "Add a CIRCLE, e.g. 5,5:2")
|
|
177
|
+
.option("--add-arc <center:radius:start:end>", "Add an ARC with degree angles")
|
|
178
|
+
.option("--add-text <text>", "Add TEXT at --at")
|
|
179
|
+
.option("--add-mtext <text>", "Add MTEXT at --at")
|
|
180
|
+
.option("--points <p1;p2[;p3]>", "Add an LWPOLYLINE from x,y points")
|
|
181
|
+
.option("--closed", "Close a polyline added with --points")
|
|
182
|
+
.option("--at <x,y,z>", "Insertion point for text")
|
|
183
|
+
.option("--height <n>", "Text height")
|
|
184
|
+
.option("--width <n>", "MText width")
|
|
185
|
+
.option("--new-layer <name>", "Layer for add-* entities")
|
|
186
|
+
.option("--new-color <color>", "Color for add-* entities")
|
|
187
|
+
.option("--delete <id>", "Delete an entity")
|
|
188
|
+
.option("-o, --output <path>", "Write edited copy to this file; required unless --overwrite")
|
|
131
189
|
.option("--overwrite", "Allow editing the input file in place")
|
|
190
|
+
.addHelpText("after", `
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
Find ids first:
|
|
194
|
+
$ cadcli entities drawing.dwg --type TEXT --json
|
|
195
|
+
$ cadcli search drawing.dwg "office" --json
|
|
196
|
+
|
|
197
|
+
Update existing entities:
|
|
198
|
+
$ cadcli edit drawing.dwg --set-text "Office" --text-id 2A -o edited.dwg
|
|
199
|
+
$ cadcli edit drawing.dwg --set-color '#ff0000' --color-id 2A -o edited.dwg
|
|
200
|
+
$ cadcli edit drawing.dwg --move 2A --dx 10 --dy 0 -o edited.dwg
|
|
201
|
+
$ cadcli edit drawing.dwg --rotate 2A --angle 90 --origin 0,0 -o edited.dwg
|
|
202
|
+
$ cadcli edit drawing.dwg --copy 2A --dx 10 --dy 0 -o edited.dwg
|
|
203
|
+
$ cadcli edit drawing.dwg --delete 2A -o edited.dwg
|
|
204
|
+
|
|
205
|
+
Add new entities:
|
|
206
|
+
$ cadcli edit drawing.dwg --add-line 0,0:10,0 --new-layer A-WALL -o edited.dwg
|
|
207
|
+
$ cadcli edit drawing.dwg --add-circle 5,5:2 -o edited.dwg
|
|
208
|
+
$ cadcli edit drawing.dwg --add-text "Label" --at 5,5 --height 2.5 -o edited.dwg
|
|
209
|
+
$ cadcli edit drawing.dwg --points '0,0;10,0;10,5' --closed -o edited.dwg
|
|
210
|
+
|
|
211
|
+
Formats:
|
|
212
|
+
points: x,y or x,y,z
|
|
213
|
+
line: from:to, e.g. 0,0:10,0
|
|
214
|
+
circle: center:radius, e.g. 5,5:2
|
|
215
|
+
arc: center:radius:startAngle:endAngle, e.g. 5,5:2:0:90
|
|
216
|
+
color: bylayer, byblock, AutoCAD color index, #rrggbb, or r,g,b
|
|
217
|
+
|
|
218
|
+
Safety:
|
|
219
|
+
cadcli refuses to modify the input file by default.
|
|
220
|
+
Use -o/--output to write an edited copy.
|
|
221
|
+
Use --overwrite only when you intentionally want to edit the input file in place.
|
|
222
|
+
`)
|
|
132
223
|
.action(async (file, opts, cmd) => edit(file, { ...cmd.optsWithGlobals(), ...opts }));
|
|
133
224
|
program
|
|
134
225
|
.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/dist/types.d.ts
CHANGED
|
@@ -35,8 +35,17 @@ export interface DwgSummary {
|
|
|
35
35
|
};
|
|
36
36
|
bounds?: DwgBounds;
|
|
37
37
|
}
|
|
38
|
+
export interface DwgTextNormalizationMetadata {
|
|
39
|
+
applied: string[];
|
|
40
|
+
entitiesChanged: number;
|
|
41
|
+
}
|
|
42
|
+
export interface DwgMetadata {
|
|
43
|
+
codePage?: string;
|
|
44
|
+
textNormalization?: DwgTextNormalizationMetadata;
|
|
45
|
+
}
|
|
38
46
|
export interface DwgDocument {
|
|
39
47
|
summary: DwgSummary;
|
|
48
|
+
metadata: DwgMetadata;
|
|
40
49
|
layers: DwgLayer[];
|
|
41
50
|
blocks: DwgBlock[];
|
|
42
51
|
entities: DwgEntity[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miclivs/cadcli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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"
|
|
@@ -69,6 +68,7 @@
|
|
|
69
68
|
"@node-projects/acad-ts": "2.3.0",
|
|
70
69
|
"chalk": "^5.6.2",
|
|
71
70
|
"commander": "^14.0.3",
|
|
71
|
+
"convert-layout": "^0.11.1",
|
|
72
72
|
"nanoid": "^5.1.6"
|
|
73
73
|
},
|
|
74
74
|
"optionalDependencies": {
|