@flyfish-dev/cad-viewer 0.5.3

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.
Files changed (58) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +11 -0
  3. package/README.md +411 -0
  4. package/README.zh-CN.md +411 -0
  5. package/dist/assets/DwgWorker-CuZJ5EUe.js +349 -0
  6. package/dist/assets/DwgWorker-CuZJ5EUe.js.map +1 -0
  7. package/dist/assets/index-C365l3i9.js +4161 -0
  8. package/dist/assets/index-C365l3i9.js.map +1 -0
  9. package/dist/cad-viewer.es.js +2992 -0
  10. package/dist/cad-viewer.es.js.map +1 -0
  11. package/dist/cad-viewer.umd.cjs +30 -0
  12. package/dist/cad-viewer.umd.cjs.map +1 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/style.css +1 -0
  16. package/dist/types/core/color.d.ts +21 -0
  17. package/dist/types/core/color.d.ts.map +1 -0
  18. package/dist/types/core/entity.d.ts +34 -0
  19. package/dist/types/core/entity.d.ts.map +1 -0
  20. package/dist/types/core/format.d.ts +8 -0
  21. package/dist/types/core/format.d.ts.map +1 -0
  22. package/dist/types/core/geometry.d.ts +17 -0
  23. package/dist/types/core/geometry.d.ts.map +1 -0
  24. package/dist/types/core/transform.d.ts +20 -0
  25. package/dist/types/core/transform.d.ts.map +1 -0
  26. package/dist/types/core/types.d.ts +195 -0
  27. package/dist/types/core/types.d.ts.map +1 -0
  28. package/dist/types/index.d.ts +12 -0
  29. package/dist/types/index.d.ts.map +1 -0
  30. package/dist/types/loaders/CadLoaderRegistry.d.ts +15 -0
  31. package/dist/types/loaders/CadLoaderRegistry.d.ts.map +1 -0
  32. package/dist/types/loaders/dwf/DwfLoader.d.ts +15 -0
  33. package/dist/types/loaders/dwf/DwfLoader.d.ts.map +1 -0
  34. package/dist/types/loaders/dwg/DwgLoader.d.ts +15 -0
  35. package/dist/types/loaders/dwg/DwgLoader.d.ts.map +1 -0
  36. package/dist/types/loaders/dwg/DwgParser.d.ts +24 -0
  37. package/dist/types/loaders/dwg/DwgParser.d.ts.map +1 -0
  38. package/dist/types/loaders/dwg/DwgWorker.d.ts +2 -0
  39. package/dist/types/loaders/dwg/DwgWorker.d.ts.map +1 -0
  40. package/dist/types/loaders/dwg/DwgWorkerClient.d.ts +18 -0
  41. package/dist/types/loaders/dwg/DwgWorkerClient.d.ts.map +1 -0
  42. package/dist/types/loaders/dwg/dwgVersion.d.ts +9 -0
  43. package/dist/types/loaders/dwg/dwgVersion.d.ts.map +1 -0
  44. package/dist/types/loaders/dwg/libredwgWasmModuleProxy.d.ts +14 -0
  45. package/dist/types/loaders/dwg/libredwgWasmModuleProxy.d.ts.map +1 -0
  46. package/dist/types/loaders/dxf/DxfLoader.d.ts +12 -0
  47. package/dist/types/loaders/dxf/DxfLoader.d.ts.map +1 -0
  48. package/dist/types/loaders/index.d.ts +8 -0
  49. package/dist/types/loaders/index.d.ts.map +1 -0
  50. package/dist/types/viewer/CadCanvasRenderer.d.ts +142 -0
  51. package/dist/types/viewer/CadCanvasRenderer.d.ts.map +1 -0
  52. package/dist/types/viewer/CadViewer.d.ts +55 -0
  53. package/dist/types/viewer/CadViewer.d.ts.map +1 -0
  54. package/dist/types/viewer/CadWebGLRenderer.d.ts +69 -0
  55. package/dist/types/viewer/CadWebGLRenderer.d.ts.map +1 -0
  56. package/dist/wasm/libredwg-web.js +15 -0
  57. package/dist/wasm/libredwg-web.wasm +0 -0
  58. package/package.json +103 -0
package/README.md ADDED
@@ -0,0 +1,411 @@
1
+ # @flyfish-dev/cad-viewer
2
+
3
+ A professional, lightweight, extensible **frontend CAD viewer** for modern browsers.
4
+
5
+ [![License: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-b31b1b.svg)](LICENSE)
6
+ [![npm](https://img.shields.io/npm/v/@flyfish-dev/cad-viewer.svg)](https://www.npmjs.com/package/@flyfish-dev/cad-viewer)
7
+
8
+ **Live demo:** [cad-viewer-iys.pages.dev](https://cad-viewer-iys.pages.dev)
9
+ **Source:** [github.com/flyfish-dev/cad-viewer](https://github.com/flyfish-dev/cad-viewer)
10
+
11
+ The project provides a clean loader architecture for **DWG**, **DXF** and **DWFx/XPS-compatible DWF preview**, normalizes format-specific data into a common `CadDocument`, and renders it through a retained WebGL pipeline with a lightweight Canvas overlay for text and images. Files are read locally in the browser; the viewer does not upload drawings to a backend.
12
+
13
+ > DWG support uses `@mlightcad/libredwg-web` / LibreDWG WebAssembly. DXF support uses JavaScript parsing plus a built-in fallback parser. DWFx support parses XPS `FixedPage` vector pages. Classic `.dwf` WHIP/W2D/W3D streams are detected and reported clearly, but full classic DWF decoding still requires a dedicated WHIP decoder or DWF Toolkit/WASM implementation.
14
+
15
+
16
+ ## What changed in 0.5.3
17
+
18
+ - Fixed DWG color fidelity for layer-indexed drawings. Indexed LibreDWG layers now resolve from ACI instead of the converter placeholder `0xffffff`, preventing monochrome-white output.
19
+ - Preserved DWG true-color values even when the RGB integer is in the ACI range, such as `0x0000ff`.
20
+ - Added BYBLOCK color inheritance for expanded INSERT/block entities.
21
+
22
+ ## What changed in 0.5.2
23
+
24
+ - Fixed the noisy Vite build output where `@mlightcad/libredwg-web` printed a multi-megabyte `data:application/wasm;base64,...` warning.
25
+ - DWG worker builds now use the lean LibreDWG ESM wrapper and load `/wasm/libredwg-web.js` + `/wasm/libredwg-web.wasm` at runtime, avoiding duplicated inline wasm assets.
26
+ - `build:lib` now creates `dist/index.js` as a compatibility re-export for integrations that still request `/dist/index.js`.
27
+ - Vite dev mode now serves a small `/dist/index.js` compatibility shim that forwards stale demo pages to `/demo/main.ts`.
28
+ - `npm run preview` now builds the demo first, so clean checkouts do not fail because `dist-demo` is missing.
29
+
30
+ ## What changed in 0.5
31
+
32
+ - The default rendering backend is now a **retained WebGL renderer**. CAD primitives are flattened once and uploaded to GPU buffers; pan/zoom updates only view uniforms.
33
+ - Added spatial indexing and viewport culling. Line, triangle and point batches are bucketed across the drawing bounds, so zoomed-in views submit only visible batches.
34
+ - Added large-drawing memory controls: coordinates are stored relative to the drawing center in `Float32Array`, colors are stored in `Uint8Array`, and temporary CPU arrays are released after upload.
35
+ - Text and images render in a separate overlay with minimum screen-height and maximum visible-label limits.
36
+ - `CadViewer` now supports `renderer: 'auto' | 'webgl' | 'canvas2d'`; `auto` prefers WebGL and falls back to Canvas2D.
37
+ - Demo now reports renderer backend, visible primitives and estimated GPU memory.
38
+
39
+ ## What changed in 0.4
40
+
41
+ - DWG parsing now runs in a **dedicated Web Worker by default**, so LibreDWG WASM initialization and binary decoding no longer block pan/zoom/UI interactions.
42
+ - The worker keeps the LibreDWG WASM instance warm and reuses it across DWG loads.
43
+ - Added cancellable loading through `AbortSignal`, worker timeout support, progress events, and explicit worker asset configuration.
44
+ - Reduced DWG memory pressure by stripping raw parser objects from worker payloads unless `keepRaw: true` is explicitly enabled.
45
+ - Demo now includes a loading overlay, progress bar, cancel action and loader mode indicator.
46
+ - `viewer.destroy()` disposes canvas listeners and terminates owned DWG workers, which is safe for SPA route changes.
47
+ - Library exports `supportsDwgWorker` and `DwgWorkerClient` for advanced integrations.
48
+
49
+ ## Features
50
+
51
+ - **Pure frontend viewer component**: `new CadViewer({ container })` or `new CadViewer({ canvas })`.
52
+ - **Loader registry**: DWG, DXF and DWF loaders are independent and replaceable.
53
+ - **DWG preview**: browser-local parsing through LibreDWG WebAssembly, executed in a Web Worker by default.
54
+ - **DXF preview**: JavaScript parser path with fallback support for common ASCII DXF `ENTITIES`.
55
+ - **DWF/DWFx preview**: DWFx/XPS 2D `FixedPage` rendering for paths, glyphs and images.
56
+ - **CAD color handling**: ACI, BYLAYER, BYBLOCK inheritance, DWG layer colors, true color, fill color, opacity and adaptive contrast.
57
+ - **High-performance WebGL viewport controls**: retained GPU buffers, spatial culling, zoom, pan, fit-to-view, cursor world coordinates and zoom percentage.
58
+ - **Professional demo UI**: drag-and-drop, compact toolbar, status strip, parse/render timing, entity summary and warnings.
59
+ - **Library + demo builds**: publishable npm package and Cloudflare Pages demo.
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ npm install @flyfish-dev/cad-viewer
65
+ ```
66
+
67
+ For local development from this repository:
68
+
69
+ ```bash
70
+ npm install
71
+ npm run dev
72
+ ```
73
+
74
+ The DWG loader needs LibreDWG WASM assets in a public directory. This repository copies and validates them for the demo:
75
+
76
+ ```bash
77
+ npm run copy:wasm
78
+ npm run check:wasm
79
+ ```
80
+
81
+ The demo resolves `wasmPath` to an absolute URL before sending it to the worker. In your own app, prefer an absolute path or URL, for example `/wasm` or `new URL('wasm/', document.baseURI).href`. Avoid passing a worker-relative path such as `./wasm` unless it is resolved on the UI thread first.
82
+
83
+ When publishing the npm package, `build:lib` also copies these files into `dist/wasm` and exposes them as package subpaths. Applications still need to serve the `.wasm` from a public URL and pass that directory as `wasmPath`.
84
+
85
+
86
+ ## Demo startup notes
87
+
88
+ Use Vite for the source demo:
89
+
90
+ ```bash
91
+ npm install
92
+ npm run dev
93
+ ```
94
+
95
+ For a production preview, use:
96
+
97
+ ```bash
98
+ npm run preview
99
+ ```
100
+
101
+ Do not serve the source directory with a plain static server and expect TypeScript entries to run. If a stale page requests `/dist/index.js`, run `npm run build:lib` to create the compatibility entry, or use the Vite dev server above.
102
+
103
+ ## Basic usage
104
+
105
+ ```ts
106
+ import { CadViewer } from '@flyfish-dev/cad-viewer';
107
+ import '@flyfish-dev/cad-viewer/style.css';
108
+
109
+ const viewer = new CadViewer({
110
+ container: document.querySelector('#viewer')!,
111
+ renderer: 'auto', // WebGL first, Canvas2D fallback
112
+ wasmPath: new URL('wasm/', document.baseURI).href,
113
+ canvasOptions: {
114
+ background: '#05070d',
115
+ foreground: '#f8fafc',
116
+ contrastMode: 'adaptive',
117
+ minColorContrast: 2.45
118
+ }
119
+ });
120
+
121
+ const input = document.querySelector<HTMLInputElement>('input[type=file]')!;
122
+ input.addEventListener('change', async () => {
123
+ const file = input.files?.[0];
124
+ if (!file) return;
125
+ await viewer.loadFile(file);
126
+ });
127
+ ```
128
+
129
+
130
+ ## WebGL rendering and performance model
131
+
132
+ The default `renderer: 'auto'` path attempts to create `CadWebGLRenderer` first. Instead of traversing every entity and rebuilding Canvas2D paths on every zoom, the renderer builds a retained scene once in `setDocument()`:
133
+
134
+ ```text
135
+ CadDocument
136
+
137
+ flatten blocks / curves / fills
138
+
139
+ Float32Array positions + Uint8Array colors
140
+
141
+ spatial GPU batches
142
+
143
+ WebGL drawArrays with viewport culling
144
+ ```
145
+
146
+ Tunable options:
147
+
148
+ ```ts
149
+ new CadViewer({
150
+ container,
151
+ renderer: 'auto', // force 'webgl' or 'canvas2d' when needed
152
+ canvasOptions: {
153
+ enableSpatialIndex: true,
154
+ spatialIndexCellCount: 96,
155
+ maxVerticesPerBatch: 32768,
156
+ maxCurveSegments: 72,
157
+ textMinPixelHeight: 4,
158
+ maxVisibleTextLabels: 2400,
159
+ powerPreference: 'high-performance',
160
+ preserveDrawingBuffer: false
161
+ },
162
+ onRenderStats(stats) {
163
+ console.log(stats.backend, stats.visiblePrimitiveCount, stats.gpuMemoryBytes);
164
+ }
165
+ });
166
+ ```
167
+
168
+ For very large drawings, lower `maxCurveSegments`, increase `spatialIndexCellCount`, and cap `maxVisibleTextLabels`.
169
+
170
+ ## Worker-backed DWG parsing
171
+
172
+ `DwgLoader` uses a module Web Worker by default in browsers. The worker imports `@mlightcad/libredwg-web`, initializes LibreDWG WASM inside the worker thread, caches that WASM instance, decodes DWG bytes, normalizes the result into a structured-clone-safe `CadDocument`, and sends only the normalized scene back to the UI thread. Canvas rendering remains on the main thread.
173
+
174
+ ```ts
175
+ const controller = new AbortController();
176
+
177
+ const viewer = new CadViewer({
178
+ container,
179
+ wasmPath: new URL('wasm/', document.baseURI).href,
180
+ useWorker: true,
181
+ workerTimeoutMs: 120_000,
182
+ onLoadProgress(progress) {
183
+ console.log(progress.phase, progress.message, progress.percent);
184
+ }
185
+ });
186
+
187
+ await viewer.preloadDwg(); // optional: warm the worker before the first file
188
+ await viewer.loadFile(file, { signal: controller.signal });
189
+
190
+ // cancel a large DWG load
191
+ controller.abort();
192
+ ```
193
+
194
+ Advanced deployments can override the worker constructor when the bundler or CDN has a custom asset layout:
195
+
196
+ ```ts
197
+ new CadViewer({
198
+ container,
199
+ wasmPath: new URL('wasm/', document.baseURI).href,
200
+ workerUrl: new URL('/assets/dwg-worker.js', window.location.origin)
201
+ });
202
+ ```
203
+
204
+ The default package is worker-first. For non-browser runtimes, register a custom DWG loader instead of disabling workers.
205
+
206
+ ## Component API
207
+
208
+ ```ts
209
+ const viewer = new CadViewer({
210
+ container, // HTMLElement; creates a canvas inside
211
+ canvas, // optional existing HTMLCanvasElement
212
+ renderer: 'auto', // 'auto' | 'webgl' | 'canvas2d'
213
+ wasmPath: '/wasm', // LibreDWG WebAssembly asset path. Absolute URL/path recommended for workers
214
+ autoFit: true,
215
+ canvasOptions: {
216
+ background: '#05070d',
217
+ foreground: '#ffffff',
218
+ contrastMode: 'adaptive', // 'adaptive' | 'preserve'
219
+ minColorContrast: 2.45,
220
+ showPageBounds: true,
221
+ showUnsupportedMarkers: false,
222
+ trueColorByteOrder: 'rgb',
223
+ enableSpatialIndex: true,
224
+ spatialIndexCellCount: 96,
225
+ maxVerticesPerBatch: 32768,
226
+ maxCurveSegments: 72,
227
+ textMinPixelHeight: 4,
228
+ maxVisibleTextLabels: 2400
229
+ },
230
+ useWorker: true, // default for DWG
231
+ workerTimeoutMs: 0, // 0 = disabled
232
+ onLoadProgress(progress) {},
233
+ onLoad(result) {},
234
+ onError(error) {},
235
+ onRenderStats(stats) {},
236
+ onViewChange(event) {}
237
+ });
238
+
239
+ await viewer.loadFile(file);
240
+ await viewer.loadBuffer(arrayBuffer, 'drawing.dxf');
241
+ viewer.fit();
242
+ viewer.zoomIn();
243
+ viewer.zoomOut();
244
+ await viewer.preloadDwg(); // optional DWG worker/WASM warmup
245
+ viewer.setCanvasOptions({ background: '#f7f8fb', foreground: '#111827' });
246
+ viewer.clear();
247
+ viewer.destroy();
248
+ ```
249
+
250
+ ## Loader architecture
251
+
252
+ ```text
253
+ File / ArrayBuffer
254
+
255
+ CadLoaderRegistry
256
+
257
+ DwgLoader | DxfLoader | DwfLoader | custom loader
258
+
259
+ CadDocument
260
+
261
+ CadWebGLRenderer | CadCanvasRenderer fallback
262
+
263
+ WebGL preview + Canvas overlay
264
+ ```
265
+
266
+ Each loader returns a normalized `CadDocument`:
267
+
268
+ ```ts
269
+ interface CadDocument {
270
+ format: 'dwg' | 'dxf' | 'dwf' | 'dwfx' | 'xps' | 'unknown';
271
+ layers: Record<string, CadLayer>;
272
+ blocks: Record<string, CadBlock>;
273
+ entities: CadEntity[];
274
+ pages?: CadPage[];
275
+ warnings: string[];
276
+ raw?: unknown;
277
+ }
278
+ ```
279
+
280
+ Register a custom loader:
281
+
282
+ ```ts
283
+ viewer.registerLoader({
284
+ id: 'my-cad-format',
285
+ label: 'My CAD Format',
286
+ formats: ['unknown'],
287
+ accepts(input) {
288
+ return input.fileName?.endsWith('.cad') ?? false;
289
+ },
290
+ async load(input) {
291
+ return {
292
+ document: {
293
+ format: 'unknown',
294
+ layers: {},
295
+ blocks: {},
296
+ entities: [],
297
+ metadata: {},
298
+ warnings: []
299
+ },
300
+ bytes: input.buffer instanceof Uint8Array ? input.buffer.byteLength : 0,
301
+ elapsedMs: 0,
302
+ format: 'unknown',
303
+ warnings: []
304
+ };
305
+ }
306
+ });
307
+ ```
308
+
309
+ ## Format support
310
+
311
+ | Format | Loader | Coverage |
312
+ |---|---|---|
313
+ | DWG | `DwgLoader` | Uses LibreDWG WebAssembly. Rendering coverage depends on the entities exposed by LibreDWG conversion. |
314
+ | DXF | `DxfLoader` | Uses `dxf-parser` plus fallback parsing. Supports core entities, blocks/inserts, colors/layers, polylines, text, hatch boundaries and splines as preview polylines. |
315
+ | DWFx / XPS | `DwfLoader` | Parses ZIP/OPC packages and renders 2D `FixedPage` path/glyph/image content. |
316
+ | Classic DWF | `DwfLoader` detection | Detects WHIP/W2D/W3D package content and returns a clear unsupported error. Full classic DWF requires a dedicated WHIP decoder or DWF Toolkit/WASM. |
317
+
318
+ ## Color handling
319
+
320
+ The color resolver follows CAD semantics instead of treating all numbers as RGB:
321
+
322
+ 1. explicit CSS color or true color object,
323
+ 2. explicit DWG true-color integer, including low RGB values such as `0x0000ff`,
324
+ 3. entity ACI (`colorIndex`, `colorNumber`, `color` in `1..255`),
325
+ 4. BYBLOCK inheritance (`0`) when expanding INSERT/block geometry,
326
+ 5. BYLAYER lookup (`256` / unset),
327
+ 6. viewer foreground fallback.
328
+
329
+ Layer colors prefer a valid ACI index when a converter also exposes a placeholder RGB value. ACI `7` is foreground-dependent: it renders light on dark canvas and dark on light canvas. With `contrastMode: 'adaptive'`, colors too close to the current canvas background are adjusted just enough to stay readable. Use `contrastMode: 'preserve'` when exact plotted colors are more important than screen readability.
330
+
331
+ If a particular converter exposes true-color integers in BGR order:
332
+
333
+ ```ts
334
+ new CadViewer({ canvasOptions: { trueColorByteOrder: 'bgr' } });
335
+ ```
336
+
337
+ ## Development
338
+
339
+ ```bash
340
+ npm install
341
+ npm run dev # run the demo
342
+ npm run typecheck # TypeScript validation
343
+ npm run build # library + demo
344
+ npm run preview # preview built demo
345
+ ```
346
+
347
+ ## npm publishing
348
+
349
+ 1. Build and inspect the package:
350
+
351
+ ```bash
352
+ npm run build:lib
353
+ npm run pack:dry
354
+ ```
355
+
356
+ 2. Publish:
357
+
358
+ ```bash
359
+ npm login
360
+ npm publish --access public
361
+ ```
362
+
363
+ The package also exposes:
364
+
365
+ ```bash
366
+ npm run release:npm
367
+ ```
368
+
369
+ ## Cloudflare Pages publishing
370
+
371
+ Direct upload with Wrangler. The repository includes `public/_headers`, so Cloudflare Pages serves `.wasm` as `application/wasm` and caches it long-term:
372
+
373
+ ```bash
374
+ npm install
375
+ npm run build:demo
376
+ npx wrangler pages deploy dist-demo --project-name cad-viewer
377
+ ```
378
+
379
+ Or use the included script:
380
+
381
+ ```bash
382
+ npm run deploy:pages
383
+ ```
384
+
385
+ For GitHub Actions, configure repository secrets:
386
+
387
+ ```text
388
+ CLOUDFLARE_API_TOKEN
389
+ CLOUDFLARE_ACCOUNT_ID
390
+ ```
391
+
392
+ The workflow is included at `.github/workflows/pages.yml`.
393
+
394
+ ## Repository layout
395
+
396
+ ```text
397
+ src/
398
+ core/ shared format detection, colors, geometry, transforms, normalized types
399
+ loaders/ DwgLoader, DxfLoader, DwfLoader and CadLoaderRegistry
400
+ viewer/ CadViewer component and Canvas renderer
401
+ demo/ professional Vite demo UI
402
+ docs/ English and Chinese architecture / format notes
403
+ scripts/ clean and LibreDWG WASM copy helpers
404
+ public/wasm/ demo WASM asset output directory
405
+ ```
406
+
407
+ ## License
408
+
409
+ AGPL-3.0-only. This is a strict copyleft license: if you distribute modified versions or offer modified versions over a network, review your source-code disclosure obligations carefully.
410
+
411
+ The default DWG loader depends on `@mlightcad/libredwg-web`, which is GPL-3.0-only. For closed-source commercial use, replace the DWG loader with a properly licensed parser/converter and review all dependency licenses.