forgecad 0.6.3 → 0.8.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.
Files changed (234) hide show
  1. package/README.md +3 -12
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
  3. package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
  4. package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
  5. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
  6. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
  8. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  9. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  10. package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
  11. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
  12. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
  14. package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
  15. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  16. package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
  17. package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
  18. package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
  19. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
  20. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  21. package/dist/docs/index.html +2 -2
  22. package/dist/docs-raw/CLI.md +341 -718
  23. package/dist/docs-raw/generated/assembly.md +699 -112
  24. package/dist/docs-raw/generated/concepts.md +1834 -1346
  25. package/dist/docs-raw/generated/core.md +1012 -1059
  26. package/dist/docs-raw/generated/curves.md +759 -116
  27. package/dist/docs-raw/generated/lib.md +43 -748
  28. package/dist/docs-raw/generated/output.md +139 -245
  29. package/dist/docs-raw/generated/sdf.md +208 -0
  30. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  31. package/dist/docs-raw/generated/sketch.md +1518 -362
  32. package/dist/docs-raw/generated/viewport.md +368 -299
  33. package/dist/docs-raw/generated/wood.md +104 -0
  34. package/dist/index.html +2 -2
  35. package/dist/landing/proof-ams-adapter.png +0 -0
  36. package/dist/landing/proof-bolt-and-nut.png +0 -0
  37. package/dist/landing/proof-fillet-enclosure.png +0 -0
  38. package/dist/landing/proof-glasses.png +0 -0
  39. package/dist/landing/proof-gyroid.png +0 -0
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/forgecad.js +12321 -5700
  42. package/dist-cli/forgecad.js.map +1 -0
  43. package/dist-cli/solver-46FFSK2U.js +363 -0
  44. package/dist-cli/solver-46FFSK2U.js.map +1 -0
  45. package/dist-skill/CONTEXT.md +4890 -6302
  46. package/dist-skill/SKILL-dev.md +22 -66
  47. package/dist-skill/SKILL.md +20 -59
  48. package/dist-skill/docs/API/core/concepts.md +37 -92
  49. package/dist-skill/docs/CLI.md +341 -718
  50. package/dist-skill/docs/generated/assembly.md +699 -112
  51. package/dist-skill/docs/generated/core.md +1012 -1059
  52. package/dist-skill/docs/generated/curves.md +759 -116
  53. package/dist-skill/docs/generated/lib.md +43 -748
  54. package/dist-skill/docs/generated/output.md +139 -245
  55. package/dist-skill/docs/generated/sdf.md +208 -0
  56. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  57. package/dist-skill/docs/generated/sketch.md +1518 -362
  58. package/dist-skill/docs/generated/viewport.md +368 -299
  59. package/dist-skill/docs/generated/wood.md +104 -0
  60. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  61. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  62. package/dist-skill/docs/guides/joint-design.md +78 -0
  63. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  64. package/dist-skill/docs/guides/positioning.md +88 -147
  65. package/dist-skill/docs-dev/API/core/concepts.md +78 -0
  66. package/dist-skill/docs-dev/CLI.md +488 -0
  67. package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
  68. package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
  69. package/dist-skill/{docs → docs-dev}/coding.md +2 -4
  70. package/dist-skill/docs-dev/component-model.md +164 -0
  71. package/dist-skill/docs-dev/generated/assembly.md +779 -0
  72. package/dist-skill/docs-dev/generated/core.md +1676 -0
  73. package/dist-skill/docs-dev/generated/curves.md +855 -0
  74. package/dist-skill/docs-dev/generated/lib.md +55 -0
  75. package/dist-skill/docs-dev/generated/output.md +234 -0
  76. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  77. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  78. package/dist-skill/docs-dev/generated/sketch.md +1753 -0
  79. package/dist-skill/docs-dev/generated/viewport.md +513 -0
  80. package/dist-skill/docs-dev/generated/wood.md +104 -0
  81. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  82. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  83. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  84. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  85. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  86. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  87. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  88. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  89. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  90. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  91. package/examples/api/attachTo-basics.forge.js +8 -8
  92. package/examples/api/bill-of-materials.forge.js +9 -9
  93. package/examples/api/bolt-pattern.forge.js +5 -5
  94. package/examples/api/boolean-operations.forge.js +5 -5
  95. package/examples/api/bounding-box-visualizer.forge.js +3 -3
  96. package/examples/api/clone-duplicate.forge.js +2 -2
  97. package/examples/api/colors-union-vs-array.forge.js +6 -6
  98. package/examples/api/connector-assembly.forge.js +8 -6
  99. package/examples/api/connector-basics.forge.js +7 -7
  100. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  101. package/examples/api/elbow-test.forge.js +3 -3
  102. package/examples/api/extrude-options.forge.js +8 -14
  103. package/examples/api/feature-created-faces.forge.js +6 -10
  104. package/examples/api/fillet-showcase.forge.js +2 -2
  105. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  106. package/examples/api/gears-tier1.forge.js +5 -5
  107. package/examples/api/group-test.forge.js +3 -3
  108. package/examples/api/group-vs-union.forge.js +1 -1
  109. package/examples/api/highlight-debug.forge.js +4 -0
  110. package/examples/api/js-module-pillars.js +1 -1
  111. package/examples/api/js-module-scene.js +2 -2
  112. package/examples/api/mesh-import-slats.forge.js +4 -4
  113. package/examples/api/patterns.forge.js +3 -3
  114. package/examples/api/pointAlong-orientation.forge.js +3 -3
  115. package/examples/api/profile-2020-b-slot6.forge.js +4 -5
  116. package/examples/api/route-perimeter-flange.forge.js +1 -1
  117. package/examples/api/sdf-rover-demo.forge.js +10 -10
  118. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  119. package/examples/api/sketch-regions.forge.js +4 -4
  120. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  121. package/examples/api/smooth-curve-connections.forge.js +1 -1
  122. package/examples/api/transition-curves.forge.js +4 -4
  123. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  124. package/examples/api/variable-sweep-test.forge.js +2 -2
  125. package/examples/api/wood-joinery.forge.js +60 -0
  126. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  127. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  128. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  129. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  130. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  131. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  132. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  133. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  134. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  135. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  136. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  137. package/examples/constraints/11-surface-grid.forge.js +2 -2
  138. package/examples/constraints/12-surface-nested.forge.js +4 -4
  139. package/examples/constraints/13-surface-complex.forge.js +1 -1
  140. package/examples/exact-arc-housing.forge.js +12 -0
  141. package/examples/experiments/drone-arm.forge.js +53 -0
  142. package/examples/furniture/adjustable-table.forge.js +15 -15
  143. package/examples/furniture/bathroom.forge.js +26 -26
  144. package/examples/furniture/chair.forge.js +13 -13
  145. package/examples/furniture/picture-frame.forge.js +6 -6
  146. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  147. package/examples/furniture/shoe-rack.forge.js +7 -7
  148. package/examples/furniture/table-lamp.forge.js +8 -8
  149. package/examples/gcode/lissajous-vase.forge.js +4 -4
  150. package/examples/gcode/math-surface.forge.js +3 -3
  151. package/examples/gcode/parametric-vase.forge.js +4 -4
  152. package/examples/gcode/spiral-tower.forge.js +4 -4
  153. package/examples/generative/crystal-growth.forge.js +9 -9
  154. package/examples/generative/frost-spires.forge.js +9 -9
  155. package/examples/generative/golden-spiral-tower.forge.js +11 -11
  156. package/examples/generative/molten-forge.forge.js +6 -6
  157. package/examples/generative/neon-coral.forge.js +7 -7
  158. package/examples/mechanical/3d-printer.forge.js +37 -37
  159. package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
  160. package/examples/mechanical/airplane-propeller.forge.js +9 -9
  161. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  162. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  163. package/examples/mechanical/fillet-enclosure.forge.js +15 -11
  164. package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
  165. package/examples/mechanical/robot_hand.forge.js +24 -24
  166. package/examples/mechanical/robot_hand_2.forge.js +26 -26
  167. package/examples/nurbs-surface.forge.js +8 -0
  168. package/examples/nurbs-tube.forge.js +7 -0
  169. package/examples/products/bottle.forge.js +8 -8
  170. package/examples/products/chess-set.forge.js +25 -25
  171. package/examples/products/classical-piano.forge.js +20 -20
  172. package/examples/products/clock.forge.js +33 -33
  173. package/examples/products/cup.forge.js +5 -5
  174. package/examples/products/iphone.forge.js +20 -20
  175. package/examples/products/laptop.forge.js +24 -24
  176. package/examples/products/laser-cut-box.forge.js +6 -6
  177. package/examples/products/laser-cut-tray.forge.js +6 -6
  178. package/examples/products/liquid-soap-dispenser.forge.js +23 -23
  179. package/examples/products/origami-fish.forge.js +14 -12
  180. package/examples/products/spiderman-cake.forge.js +6 -6
  181. package/examples/shelf/container.forge.js +5 -5
  182. package/examples/shelf/shelf-unit.forge.js +6 -6
  183. package/examples/toolbox/bolted-joint.forge.js +7 -7
  184. package/package.json +9 -4
  185. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  186. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  187. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  188. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  189. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  190. package/dist/assets/index-1CYp3zUp.js +0 -1455
  191. package/dist-skill/docs/API/API.md +0 -1666
  192. package/dist-skill/docs/API/README.md +0 -37
  193. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  194. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  195. package/dist-skill/docs/API/core/parameters.md +0 -122
  196. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  197. package/dist-skill/docs/API/core/sdf.md +0 -326
  198. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  199. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  200. package/dist-skill/docs/API/core/specs.md +0 -186
  201. package/dist-skill/docs/API/core/topology.md +0 -372
  202. package/dist-skill/docs/API/entities.md +0 -268
  203. package/dist-skill/docs/API/output/bom.md +0 -58
  204. package/dist-skill/docs/API/output/brep-export.md +0 -87
  205. package/dist-skill/docs/API/output/dimensions.md +0 -67
  206. package/dist-skill/docs/API/output/export.md +0 -110
  207. package/dist-skill/docs/API/output/gcode.md +0 -195
  208. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  209. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  210. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  211. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  212. package/dist-skill/docs/API/sketch/core.md +0 -73
  213. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  214. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  215. package/dist-skill/docs/API/sketch/operations.md +0 -78
  216. package/dist-skill/docs/API/sketch/path.md +0 -75
  217. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  218. package/dist-skill/docs/API/sketch/regions.md +0 -80
  219. package/dist-skill/docs/API/sketch/text.md +0 -108
  220. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  221. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  222. package/dist-skill/docs/INDEX.md +0 -94
  223. package/dist-skill/docs/RELEASING.md +0 -55
  224. package/dist-skill/docs/cli-monetization.md +0 -111
  225. package/dist-skill/docs/deployment.md +0 -281
  226. package/dist-skill/docs/generated/concepts.md +0 -2112
  227. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  228. package/dist-skill/docs/platform/admin.md +0 -45
  229. package/dist-skill/docs/platform/architecture.md +0 -79
  230. package/dist-skill/docs/platform/auth.md +0 -110
  231. package/dist-skill/docs/platform/email.md +0 -67
  232. package/dist-skill/docs/platform/projects.md +0 -111
  233. package/dist-skill/docs/platform/sharing.md +0 -90
  234. package/dist-skill/docs/runbook.md +0 -345
@@ -1,80 +0,0 @@
1
- ---
2
- skill-group: sketch
3
- skill-order: 11
4
- ---
5
-
6
- # Sketch Regions
7
-
8
- Decompose complex sketches into their individual filled areas. This is essential when a sketch operation produces multiple disconnected regions and you need to work with them independently.
9
-
10
- ## `sketch.regions()`
11
-
12
- Decompose a sketch into its distinct filled regions, returned largest-first by area.
13
-
14
- A single sketch can contain several disconnected filled areas (e.g., two separate rectangles, a ring shape, or the result of a boolean that leaves islands). This method enumerates all top-level connected regions as independent `Sketch` objects.
15
-
16
- **Returns:** `Sketch[]` — Array of region sketches, sorted by area (largest first)
17
-
18
- ```javascript
19
- // Two disconnected rectangles — get each one separately
20
- const pair = union2d(rect(40, 40), rect(40, 40).translate(60, 0));
21
- const [larger, smaller] = pair.regions();
22
- larger.extrude(10);
23
- smaller.extrude(5);
24
-
25
- // Ring shape — one region containing the ring
26
- const ring = circle2d(50).subtract(circle2d(30));
27
- const [ringRegion] = ring.regions();
28
- ringRegion.extrude(8);
29
- ```
30
-
31
- ## `sketch.region(seed)`
32
-
33
- Select the single filled region that contains a given 2D point. This lets you pick any enclosed area by pointing at it instead of sorting through all regions.
34
-
35
- **Parameters:**
36
- - `seed` (`[number, number]`) — A 2D point `[x, y]` strictly inside the desired region
37
-
38
- **Returns:** `Sketch` — The region containing the seed point
39
-
40
- **Throws:** If the seed is outside all regions, on a boundary edge, or inside a hole.
41
-
42
- ```javascript
43
- // Donut — select the ring area at radius 40
44
- const donut = circle2d(50).subtract(circle2d(30));
45
- const ring = donut.region([40, 0]);
46
- ring.extrude(10);
47
-
48
- // Complex boolean result — pick a specific island
49
- const complex = union2d(rect(40, 40), rect(40, 40).translate(60, 0));
50
- const rightBox = complex.region([80, 20]); // seed inside right box
51
- ```
52
-
53
- > **Callout:** The seed point must be strictly inside the filled area — not on the boundary. If you're unsure where the regions are, use `.regions()` first to enumerate them. Each returned region has a `.bounds()` you can inspect.
54
-
55
- ## Constrained Sketch Regions
56
-
57
- `ConstraintSketch` (from `constrainedSketch().solve()`) provides two additional methods for working with the line arrangement formed by its edges:
58
-
59
- ### `cs.detectArrangement()`
60
-
61
- Enumerate all bounded regions formed by the non-construction line arrangement. Returns `Sketch[]` sorted largest-first.
62
-
63
- ```javascript
64
- const sk = constrainedSketch();
65
- // ... add geometry and constraints ...
66
- const cs = sk.solve();
67
- const regions = cs.detectArrangement();
68
- regions[0].extrude(5); // extrude the largest region
69
- ```
70
-
71
- ### `cs.detectArrangementRegion(seed)`
72
-
73
- Select a single arrangement region by seed point. Same semantics as `sketch.region(seed)` but operates on the constraint sketch's line arrangement (DCEL face detection).
74
-
75
- ```javascript
76
- const region = cs.detectArrangementRegion([10, 10]);
77
- region.extrude(3);
78
- ```
79
-
80
- > **Callout:** `detectArrangement()` and `detectArrangementRegion()` use the raw line/arc geometry from the constraint solver, not the boolean profile. This means construction lines are excluded and the regions are formed by the geometric arrangement of edges — useful when you need to select specific enclosed areas from a complex constrained sketch.
@@ -1,108 +0,0 @@
1
- ---
2
- skill-group: sketch
3
- skill-order: 10
4
- ---
5
-
6
- # 2D Text
7
-
8
- Create filled text geometry from strings. Supports both the built-in **Forge Mono** geometric font and any **TTF/OTF font file** for professional typography. Text sketches can be extruded, engraved, or used anywhere a normal `Sketch` is accepted.
9
-
10
- ## `text2d(content, options?)`
11
-
12
- Build a filled 2D `Sketch` from a text string.
13
-
14
- **Parameters:**
15
- - `content` (string) — The text to render
16
- - `options` (TextOptions, optional):
17
- - `size` (number) — Cap height in model units. Default: `10`. All proportions scale with this.
18
- - `letterSpacing` (number) — Extra space between characters in model units. Negative tightens. Default: `0`
19
- - `align` (`'left' | 'center' | 'right'`) — Horizontal alignment relative to x = 0. Default: `'left'`
20
- - `baseline` (`'baseline' | 'center' | 'top'`) — Vertical alignment relative to y = 0. Default: `'baseline'`
21
- - `font` (string | Font) — Path to a TTF/OTF font file, or a pre-loaded Font object from `loadFont()`. When omitted, uses the built-in Forge Mono font.
22
- - `flattenTolerance` (number) — Bezier curve flattening tolerance in model units (font mode only). Smaller = smoother curves. Default: 0.5% of size.
23
-
24
- **Returns:** `Sketch` — Filled 2D text geometry
25
-
26
- ```javascript
27
- // Built-in geometric font (default)
28
- text2d('FORGE CAD', { size: 8 }).extrude(1.2);
29
-
30
- // Using a real font — professional typography with proper curves
31
- text2d('Hello World', { size: 10, font: '/path/to/Arial.ttf' }).extrude(1);
32
-
33
- // Pre-load font for reuse across multiple text calls
34
- const font = loadFont('/path/to/Arial Bold.ttf');
35
- text2d('Title', { size: 12, font }).extrude(1.5);
36
- text2d('Subtitle', { size: 8, font, align: 'center' }).extrude(0.8);
37
-
38
- // Centered label
39
- text2d('V 2.0', { size: 6, align: 'center', baseline: 'center' });
40
-
41
- // Engraved text on top face of a box
42
- const label = text2d('REV A', { size: 5, align: 'center', baseline: 'center' });
43
- const plate = box(60, 20, 5);
44
- return plate.subtract(label.onFace(plate, 'top', { protrude: -0.5 }).extrude(1));
45
- ```
46
-
47
- ### Font Options
48
-
49
- **Real fonts (TTF/OTF)** — Pass a file path or pre-loaded Font to the `font` option:
50
- - Professional typography with proper bezier curves
51
- - Full character set: uppercase, lowercase, accented characters, symbols
52
- - Automatic kerning between character pairs
53
- - Any TTF or OTF font file works
54
-
55
- **Built-in: Forge Mono** (default when no `font` option) —
56
- - **Style:** Geometric monoline sans-serif, squared-off and futuristic
57
- - **Inspired by:** Eurostile, Chakra Petch
58
- - **Characteristics:** Uniform stroke weight, flat open ends, no serifs
59
- - **Character set:** Uppercase A–Z, digits 0–9, punctuation. Lowercase maps to uppercase
60
- - No external font files needed — every character is constructed from geometric primitives
61
-
62
- ## `loadFont(source, cacheKey?)`
63
-
64
- Pre-load and cache a font for reuse across multiple `text2d()` calls.
65
-
66
- **Parameters:**
67
- - `source` (string | ArrayBuffer) — File path to a TTF/OTF font, or raw font data as ArrayBuffer
68
- - `cacheKey` (string, optional) — Cache key when passing ArrayBuffer
69
-
70
- **Returns:** Font object (pass to `text2d`'s `font` option)
71
-
72
- ```javascript
73
- const font = loadFont('/System/Library/Fonts/Supplemental/Arial.ttf');
74
- text2d('Line 1', { size: 8, font }).extrude(1);
75
- text2d('Line 2', { size: 8, font }).extrude(1);
76
- ```
77
-
78
- ## `textWidth(content, options?)`
79
-
80
- Measure the rendered width of a string without creating geometry. Useful for layout calculations.
81
-
82
- **Parameters:**
83
- - `content` (string) — The text to measure
84
- - `options` (object, optional):
85
- - `size` (number) — Cap height in model units. Default: `10`
86
- - `letterSpacing` (number) — Extra spacing. Default: `0`
87
-
88
- **Returns:** `number` — Width of the rendered text in model units
89
-
90
- ```javascript
91
- const label = 'SERIAL: 001';
92
- const w = textWidth(label, { size: 6 });
93
-
94
- // Create a plate that fits the text with padding
95
- const plate = box(w + 10, 12, 2);
96
- const text = text2d(label, { size: 6, align: 'center', baseline: 'center' })
97
- .translate(w / 2 + 5, 6, 0);
98
- ```
99
-
100
- ## Alignment Quick Reference
101
-
102
- | `align` | `baseline` | Origin position |
103
- |---------|-----------|----------------|
104
- | `'left'` | `'baseline'` | Bottom-left of first character (default) |
105
- | `'center'` | `'center'` | Dead center of text block |
106
- | `'right'` | `'top'` | Top-right corner |
107
-
108
- The origin is at `(0, 0)` — alignment controls where the text sits relative to that point.
@@ -1,65 +0,0 @@
1
- ---
2
- skill-group: sketch
3
- skill-order: 4
4
- ---
5
-
6
- # Sketch Transforms
7
-
8
- 2D transformations for sketches. All transforms are **chainable** and **immutable** (return new sketches). Colors are preserved through all transforms.
9
-
10
- ## Methods
11
-
12
- ### `.clone()` / `.duplicate()`
13
- Create an explicit copy handle of a sketch (same profile/color) so variants are easy to branch.
14
-
15
- ```javascript
16
- const profile = rect(40, 20);
17
- const left = profile.clone().translate(-30, 0);
18
- const right = profile.duplicate().translate(30, 0);
19
- ```
20
-
21
- ### `.translate(x, y?)`
22
- Moves the sketch.
23
-
24
- ```javascript
25
- const moved = rect(50, 30).translate(100, 50);
26
- ```
27
-
28
- ### `.rotate(degrees)`
29
- Rotates around the sketch's bounding-box center.
30
-
31
- ```javascript
32
- const rotated = rect(50, 30).rotate(45);
33
- ```
34
-
35
- ### `.rotateAround(degrees, pivot)`
36
- Rotates around a specific point instead of the default center pivot.
37
-
38
- **Parameters:**
39
- - `degrees` (number) — Rotation angle
40
- - `pivot` ([number, number]) — Point to rotate around
41
-
42
- ```javascript
43
- const hook = rect(4, 20).rotateAround(-35, [2, 0]);
44
- ```
45
-
46
- ### `.scale(v)`
47
- Scales the sketch from its bounding-box center.
48
-
49
- **Parameters:**
50
- - `v` (number | [number, number]) — Uniform scale or per-axis scale
51
-
52
- ```javascript
53
- const bigger = circle2d(10).scale(2);
54
- const stretched = rect(10, 10).scale([2, 0.5]);
55
- ```
56
-
57
- ### `.mirror(normal)`
58
- Mirrors across a line defined by its normal vector, passing through the sketch's bounding-box center.
59
-
60
- **Parameters:**
61
- - `normal` ([number, number]) — Line normal (doesn't need to be unit length)
62
-
63
- ```javascript
64
- const mirrored = sketch.mirror([1, 0]); // Mirror across Y axis
65
- ```
@@ -1,129 +0,0 @@
1
- ---
2
- skill-group: toolbox
3
- skill-order: 1
4
- ---
5
-
6
- # Toolbox — Fastener Library
7
-
8
- Pre-built ISO metric fastener geometry available via `lib.*` in any ForgeCAD script.
9
-
10
- ## Supported catalog
11
-
12
- This is the initial, intentionally small catalog. Coverage outside these items is not guaranteed.
13
-
14
- | Family | Sizes | Standard |
15
- |--------|-------|----------|
16
- | Hex bolt (`lib.bolt`) | M4 – M10 (parametric) | ISO 4762 lookalike |
17
- | Hex nut (`lib.nut`) | M4 – M10 (parametric) | ISO 4032 lookalike |
18
- | Flat washer (`lib.washer`) | M2, M2.5, M3, M4, M5, M6, M8, M10 | DIN 125-A |
19
- | Fastener hole (`lib.fastenerHole`) | M2, M2.5, M3, M4, M5, M6, M8, M10 | ISO metric fits |
20
- | Fastener set (`lib.fastenerSet`) | M2 – M10 | Combines all of the above |
21
-
22
- Sizes outside the table will throw. Extend `METRIC_HOLE_TABLE` / `WASHER_TABLE` in `library.ts` when adding new sizes.
23
-
24
- ## `lib.washer(size, options?)`
25
-
26
- Returns a flat ring washer (DIN 125-A by default) centered at the origin, thickness along Z.
27
-
28
- ```javascript
29
- const w = lib.washer("M5");
30
- // outer dia 10 mm, inner dia 5.3 mm, thickness 1 mm
31
- ```
32
-
33
- **Parameters**
34
-
35
- | Name | Type | Default | Description |
36
- |------|------|---------|-------------|
37
- | `size` | `MetricSize` | required | ISO metric thread size string, e.g. `"M6"` |
38
- | `options.standard` | `'din-125-a'` | `'din-125-a'` | Washer standard (only DIN 125-A for now) |
39
- | `options.segments` | `number` | `48` | Circle segment count |
40
-
41
- **DIN 125-A dimensions**
42
-
43
- | Size | Inner dia (mm) | Outer dia (mm) | Thickness (mm) |
44
- |------|---------------|----------------|----------------|
45
- | M2 | 2.2 | 5.0 | 0.3 |
46
- | M2.5 | 2.7 | 6.0 | 0.5 |
47
- | M3 | 3.2 | 7.0 | 0.5 |
48
- | M4 | 4.3 | 9.0 | 0.8 |
49
- | M5 | 5.3 | 10.0 | 1.0 |
50
- | M6 | 6.4 | 12.0 | 1.6 |
51
- | M8 | 8.4 | 17.0 | 1.6 |
52
- | M10 | 10.5 | 21.0 | 2.0 |
53
-
54
- ## `lib.fastenerSet(size, boltLength, options?)`
55
-
56
- Returns all geometry needed for one complete bolted joint: bolt, nut, washers, and hole cutters — un-positioned so you can place them freely.
57
-
58
- ```javascript
59
- const hw = lib.fastenerSet("M5", 20);
60
-
61
- // Cut holes in two plates
62
- const topPlate = box(60, 40, 8, true)
63
- .subtract(hw.clearanceHole.translate(15, 10, 0));
64
- const botPlate = box(60, 40, 8, true).translate(0, 0, -16)
65
- .subtract(hw.tappedHole.translate(15, 10, -8));
66
-
67
- // Place hardware
68
- return [
69
- { name: "Top Plate", shape: topPlate, color: "#9ab4cc" },
70
- { name: "Bot Plate", shape: botPlate, color: "#b0b8c8" },
71
- { name: "Bolt", shape: hw.bolt.translate(15, 10, 4), color: "#aaaaaa" },
72
- { name: "Nut", shape: hw.nut.translate(15, 10, -19), color: "#888888" },
73
- ];
74
- ```
75
-
76
- **Parameters**
77
-
78
- | Name | Type | Default | Description |
79
- |------|------|---------|-------------|
80
- | `size` | `MetricSize` | required | ISO metric thread size |
81
- | `boltLength` | `number` | required | Shaft length in mm (head excluded) |
82
- | `options.washerUnderHead` | `boolean` | `true` | Include a washer shape for under the head |
83
- | `options.washerUnderNut` | `boolean` | `true` | Include a washer shape for under the nut |
84
- | `options.fit` | `FastenerFit` | `'normal'` | Clearance hole fit: `close`, `normal`, `loose`, or `tap` |
85
- | `options.segments` | `number` | `36` | Thread/circle segment count |
86
-
87
- **Result fields**
88
-
89
- | Field | Type | Description |
90
- |-------|------|-------------|
91
- | `bolt` | `Shape` | Head top at z=0, shaft along −Z by `boltLength` |
92
- | `nut` | `Shape` | Hex nut centered at z=0 |
93
- | `washerUnderHead` | `Shape \| null` | Flat washer centered at z=0 |
94
- | `washerUnderNut` | `Shape \| null` | Flat washer centered at z=0 |
95
- | `clearanceHole` | `Shape` | Cutter cylinder for through-plate clearance, centered at z=0 |
96
- | `tappedHole` | `Shape` | Cutter cylinder for tap-drill hole, centered at z=0 |
97
- | `dims` | `FastenerSetDimensions` | Reference dimensions for placement and BOM |
98
-
99
- **`FastenerSetDimensions` fields**
100
-
101
- | Field | Description |
102
- |-------|-------------|
103
- | `size` | Thread size string |
104
- | `nominalDiameter` | Numeric thread diameter (mm) |
105
- | `boltLength` | As specified |
106
- | `clearanceDia` | Clearance hole diameter for chosen fit (mm) |
107
- | `tapDia` | Tap-drill diameter (mm) |
108
- | `nutAcrossFlats` | Hex nut width across flats (mm) |
109
- | `nutHeight` | Nut height (mm) |
110
- | `washerOuterDia` / `washerInnerDia` / `washerThickness` | DIN 125-A washer dimensions (mm) |
111
-
112
- ## `lib.fastenerHole(opts)`
113
-
114
- Lower-level helper that returns only a hole cutter (cylinder ± counterbore/countersink). Supports M2–M10 with four fit classes and optional counterbore/countersink geometry. See the full API reference for details.
115
-
116
- ## Pairing table
117
-
118
- Use this to pick the right cutter fit for your workflow:
119
-
120
- | Fit | Hole diameter | Use when |
121
- |-----|--------------|----------|
122
- | `close` | ≈ nominal + 0.2 mm | Press-location or close-tolerance slotting |
123
- | `normal` | ≈ nominal + 0.5 mm | Standard through-bolt clearance (default) |
124
- | `loose` | ≈ nominal + 1–2 mm | Adjustment slots or misaligned patterns |
125
- | `tap` | ISO tap drill | Tapped hole in the mating part |
126
-
127
- ## Example
128
-
129
- See [`examples/toolbox/bolted-joint.forge.js`](../../../../examples/toolbox/bolted-joint.forge.js) for a complete two-plate bolted assembly with BOM and exploded view.
@@ -1,94 +0,0 @@
1
- # ForgeCAD Documentation
2
-
3
- Central entry point for all ForgeCAD documentation. Start here.
4
-
5
- ---
6
-
7
- ## Operations & Runbook
8
-
9
- The first place to look when debugging, deploying, or onboarding.
10
-
11
- - **[Runbook](project/runbook.md)** — Dev server commands, local stack setup, health checks, production SSH, API endpoint reference, troubleshooting
12
- - **[Deployment & Environment](project/deployment.md)** — Docker Compose architecture, environment variables, Coolify setup, database migrations
13
- - **[Releasing](RELEASING.md)** — npm version, publish workflow, GitHub Releases
14
-
15
- ---
16
-
17
- ## Platform (Web App)
18
-
19
- Documentation for the hosted service at forgecad.io.
20
-
21
- - **[System Architecture](platform/architecture.md)** — Stack overview, URL routes, Docker topology, security
22
- - **[Authentication](platform/auth.md)** — JWT tokens, OAuth (GitHub/Google), registration, password reset, session management
23
- - **[Projects & Files](platform/projects.md)** — Project CRUD, member roles, file storage, storage quotas, SSE watching
24
- - **[Model Sharing](platform/sharing.md)** — Publishing, public URLs, embeds, gist/URL/inline sharing
25
- - **[Admin Dashboard](platform/admin.md)** — Admin panel, audit log, user management
26
- - **[Email Delivery](platform/email.md)** — Resend setup, verification emails, password reset emails
27
-
28
- ---
29
-
30
- ## CLI
31
-
32
- - **[CLI Reference](CLI.md)** — Full command reference: dev/studio servers, export (STL/STEP/SVG/G-code/SDF), render, capture, notebooks, invariant checks
33
- - **[Monetization & Licensing](project/cli-monetization.md)** — Free vs Pro tiers, license activation, feature gating strategy
34
-
35
- ---
36
-
37
- ## API Reference (Model Authoring)
38
-
39
- For users writing `.forge.js` scripts.
40
-
41
- - **[API Overview](API/README.md)** — Reading plan and orientation
42
- - **[Core API](API/API.md)** — Shapes, primitives, transforms, booleans, patterns
43
- - **[Entities](API/entities.md)** — 2D/3D entity-based API
44
- - **Sketches**: [Core](API/sketch/core.md) | [Primitives](API/sketch/primitives.md) | [Paths](API/sketch/path.md) | [Booleans](API/sketch/booleans.md) | [Operations](API/sketch/operations.md) | [Extrude/Revolve](API/sketch/extrude.md) | [Transforms](API/sketch/transforms.md) | [Regions](API/sketch/regions.md) | [Text](API/sketch/text.md) | [Anchoring](API/sketch/anchor.md) | [On-Face](API/sketch/on-face.md)
45
- - **Assembly**: [Assembly & Kinematics](API/assembly/assembly.md)
46
- - **Output**: [Export](API/output/export.md) | [BREP/STEP](API/output/brep-export.md) | [G-code](API/output/gcode.md) | [Dimensions](API/output/dimensions.md) | [BOM](API/output/bom.md)
47
- - **Other**: [Sheet Metal](API/sheet-metal/sheet-metal.md) | [Fasteners](API/toolbox/fasteners.md) | [Viewport](API/runtime/viewport.md)
48
- - **Core Concepts**: [Concepts](API/core/concepts.md) | [Parameters](API/core/parameters.md) | [Topology](API/core/topology.md) | [Edge Queries](API/core/edge-queries.md) | [Specs](API/core/specs.md) | [SDF](API/core/sdf.md)
49
- - **AI Skill**: [Skill Guide](API/core/skill-guide.md) | [Skill CLI](API/core/skill-cli.md)
50
- - **[Auto-Generated API Docs](generated/)** — Machine-generated from `forge-public-api.ts`
51
-
52
- ---
53
-
54
- ## Guides (Modeling)
55
-
56
- Techniques and conventions for model authors.
57
-
58
- - **[Coordinate System](guides/coordinate-system.md)** — Z-up convention, axis orientation
59
- - **[Geometry Conventions](guides/geometry-conventions.md)** — Winding order, depth, normals
60
- - **[Positioning](guides/positioning.md)** — Connectors, `matchTo()`, and placement strategies
61
- - **[Modeling Recipes](guides/modeling-recipes.md)** — Common patterns, iteration, multi-file composition
62
- - **[Skill Maintenance](guides/skill-maintenance.md)** — AI skill build process, `npm run refresh`
63
-
64
- ---
65
-
66
- ## Engine Internals
67
-
68
- For contributors working on ForgeCAD's core engine.
69
-
70
- - **[Compiler](internals/compiler.md)** — Multi-backend architecture (Manifold + OCCT), compile plans, lowering
71
- - **[Constraint Solver](internals/constraint-solver.md)** — Solver pipeline, architecture, Levenberg-Marquardt
72
- - **[Solver Quality](internals/constraint-solver-quality.md)** — Tunable vs architectural parameters
73
- - **[2D Sketch Pipeline](internals/sketch-2d-pipeline.md)** — Two-track export, ProfileCompilePlan
74
-
75
- ---
76
-
77
- ## Development Standards
78
-
79
- For contributors developing ForgeCAD itself.
80
-
81
- - **[Coding Guidelines](project/coding.md)** — Project structure, workflow, adding new features
82
- - **[Coding Best Practices](project/coding-best-practices.md)** — TypeScript, React, performance, self-review
83
- - **[Blueprint-First Design](project/blueprint-first.md)** — No trigonometry tax, intent-driven API design
84
-
85
- ---
86
-
87
- ## Processes
88
-
89
- Operational procedures for multi-agent development and investigations.
90
-
91
- - **[Multi-Agent Development](../processes/MULTI_AGENT_DEVELOPMENT.md)**
92
- - **[Program Lead](../processes/PROGRAM-LEAD.md)**
93
- - **[AI Investigation Projects](../processes/AI_INVESTIGATION_PROJECTS.md)**
94
- - **[Benchmark SOP](../processes/README_BENCHMARK_SOP.md)**
@@ -1,55 +0,0 @@
1
- ---
2
- skill-group: dev-conventions
3
- skill-order: 4
4
- skill-tiers: [dev]
5
- ---
6
-
7
- # Releasing ForgeCAD
8
-
9
- ## Quick Release
10
-
11
- ```bash
12
- npm version patch # or minor / major
13
- git push && git push --tags
14
- ```
15
-
16
- That's it. GitHub Actions handles the rest:
17
- - Builds everything (solver WASM + TypeScript + Vite + CLI + skills)
18
- - Runs the full test suite
19
- - Publishes to npm
20
- - Creates a GitHub Release with auto-generated notes
21
- - Production (forgecad.io) deploys automatically via Coolify on push to mainline
22
-
23
- ## Version Levels
24
-
25
- | Command | When | Example |
26
- |---------|------|---------|
27
- | `npm version patch` | Bug fixes, small changes | 0.1.5 → 0.1.6 |
28
- | `npm version minor` | New features | 0.1.5 → 0.2.0 |
29
- | `npm version major` | Breaking changes | 0.1.5 → 1.0.0 |
30
-
31
- `npm version` automatically:
32
- 1. Updates `version` in `package.json`
33
- 2. Creates a git commit (`v0.1.6`)
34
- 3. Creates a git tag (`v0.1.6`)
35
-
36
- ## Dry Run
37
-
38
- To test the publish workflow without actually releasing:
39
-
40
- 1. Go to **Actions → Publish to npm & GitHub Release → Run workflow**
41
- 2. Check "Dry run" and run
42
-
43
- ## Prerequisites
44
-
45
- - `NPM_TOKEN` must be set as a GitHub repo secret (Settings → Secrets → Actions)
46
- - Create a **Granular Access Token** at npmjs.com → Account → Access Tokens
47
- - Scope it to read+write on the `forgecad` package only
48
-
49
- ## What Gets Published
50
-
51
- - `dist-cli/forgecad.js` — CLI binary
52
- - `dist/` — web app bundles
53
- - `dist-skill/` — AI skill documentation and context
54
- - `examples/` — sample models
55
- - `README.md`, `LICENSE`
@@ -1,111 +0,0 @@
1
- # CLI Monetization Strategy
2
-
3
- ## The Model: Sublime Text Now, Cursor Later
4
-
5
- ForgeCAD follows a phased monetization strategy informed by how real software companies make money — not by how they try to prevent piracy.
6
-
7
- ### Phase 1: Honor System (Now)
8
-
9
- Ship a compiled binary with a local license gate. Pro commands show a clear upgrade message. No enforcement beyond that.
10
-
11
- **Why this works:**
12
- - Sublime Text makes $5-25M/year with zero enforcement (nag popup only)
13
- - WinRAR makes $20-40M/year with zero enforcement (40-day trial that never expires)
14
- - JetBrains makes $593M/year despite being cracked constantly
15
- - The pattern: individuals who crack it weren't going to pay. Enterprises must pay (legal compliance, audit risk, IT procurement). Pirates who learn ForgeCAD become advocates inside companies that buy licenses.
16
-
17
- **What we ship:**
18
- - Compiled native binary via `bun build --compile --minify` (source not trivially readable)
19
- - Local JWT license validation at `~/.forgecad/license.json`
20
- - Pro commands blocked with helpful message + free alternatives
21
- - `forgecad license activate/deactivate/status` commands
22
-
23
- **What we accept:**
24
- - A developer can bypass the local check in 30 minutes
25
- - String literals (error messages, API names) are visible via `strings`
26
- - This is fine. The gate converts honest users; it doesn't stop determined pirates.
27
-
28
- ### Phase 2: Enterprise Features (Soon)
29
-
30
- Add features that enterprises need and individuals don't care about:
31
- - License server (floating seats, team management, usage reporting)
32
- - Audit log (who exported what, when)
33
- - SSO/SAML integration
34
- - Priority support channel
35
-
36
- These features don't need anti-piracy because they're inherently organizational. A solo developer doesn't need a license server.
37
-
38
- ### Phase 3: Server-Side Pro Features (Later)
39
-
40
- Move high-value exports to run on the ForgeCAD server. The CLI uploads the model, the server processes it, the CLI downloads the result.
41
-
42
- **Why this is piracy-proof:** The code for STEP export, Blender rendering, and gcode computation never ships to the user. You can't crack what you don't have.
43
-
44
- **Proven by:**
45
- - Cursor: $2B ARR — the editor is free, AI calls are server-side
46
- - Figma: $1B revenue — all rendering is server-side
47
- - Onshape: acquired for $470M — 100% browser-based, nothing local to crack
48
-
49
- ## Feature Tiers
50
-
51
- | Tier | Price | Features |
52
- |------|-------|----------|
53
- | **Free** | $0 | Editor, dev server, run scripts, render PNG, export STL/3MF/SVG, all checks, debug tools |
54
- | **Pro** | $15-25/mo | STEP/BREP export, render-hq (Blender), capture GIF/MP4, gcode, report PDF, cutting layout, SDF/URDF, sketch PDF |
55
- | **Team** | $40-60/seat/mo | Everything in Pro + license server, floating seats, audit log, SSO, priority support |
56
-
57
- ### Why this split?
58
-
59
- **Free tier** includes everything needed to learn ForgeCAD, build models, and 3D print. This is the funnel — hobbyists, students, and evaluators use this. It's genuinely useful, not a crippled demo.
60
-
61
- **Pro tier** gates the features professionals need: exact CAD interchange (STEP), manufacturing outputs (gcode, cutting layouts), and presentation (render-hq, animations). These are the commands that save hours of work and justify a subscription.
62
-
63
- **Team tier** gates organizational features that individuals don't need.
64
-
65
- ## What NOT to Do
66
-
67
- ### Don't rewrite in a compiled language for code protection
68
- SolidWorks is C++ and it's cracked (SolidSquad). AutoCAD is C++ and it's cracked. The language doesn't matter — Ghidra/IDA Pro can reverse-engineer any native binary. The cost (rewriting years of work, losing the JS API that makes ForgeCAD special) far outweighs the marginal protection gain.
69
-
70
- ### Don't add aggressive DRM
71
- Dassault embeds forensic signatures in SolidWorks files and sues pirates — this generates "many millions" in settlements but also generates resentment. For a small product trying to build a community, aggressive enforcement is counterproductive.
72
-
73
- ### Don't make the free tier useless
74
- Ondsel (commercial FreeCAD) died in 2 years. Open-source CAD has near-zero commercial traction. But the opposite extreme — locking everything behind a paywall — kills adoption. The free tier must be genuinely useful.
75
-
76
- ### Don't ship source maps
77
- Claude Code accidentally shipped `.map` files in npm v2.1.88. Within hours, the entire 1,900-file TypeScript codebase was extracted, mirrored to GitHub (1,100+ stars), and reverse-engineered. `.npmignore` must exclude `*.map`, `*.ts` source, and `tsconfig.json`.
78
-
79
- ## The Gabe Newell Principle
80
-
81
- > "Piracy is almost always a service problem, not a pricing problem."
82
-
83
- Steam proved this — Russia went from "don't bother, too much piracy" to Valve's largest European market once they localized and shipped same-day.
84
-
85
- **Applied to ForgeCAD:** Make the paid version more convenient than the cracked one:
86
- - Automatic updates (the binary self-updates, cracked copies don't)
87
- - Cloud project sync (your models available everywhere)
88
- - Share links (one-click sharing from the editor)
89
- - Support (access to help when stuck)
90
- - Team features (collaboration, permissions)
91
-
92
- People pay for convenience and reliability, not because they can't find a crack.
93
-
94
- ## Distribution
95
-
96
- | Channel | Format | Audience |
97
- |---------|--------|----------|
98
- | **npm** (`npm install -g forgecad`) | Minified `cli.js` (no source maps) | Primary channel |
99
- | **forgecad.io** | Web editor (no install) | Evaluators, casual users |
100
-
101
- ForgeCAD users write `.forge.js` files — they already have Node.js. npm is the only CLI distribution channel we need to maintain.
102
-
103
- A compiled native binary build script exists (`npm run build:binary`) for potential future Homebrew distribution, but compiled binaries offer no additional source protection over minified JS (the source is stored as plain text in the binary's `__BUN` section). The binary is not worth maintaining as a separate channel until user volume justifies it.
104
-
105
- ### Build
106
-
107
- ```
108
- TypeScript source → tsup (ESM bundle, 2.5MB) → dist-cli/forgecad.js
109
- ```
110
-
111
- **Never ship source maps.** `.npmignore` must exclude `*.map`, `*.ts` source, and `tsconfig.json`.