fluidcad 0.0.32 → 0.0.34

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 (279) hide show
  1. package/README.md +3 -2
  2. package/bin/commands/init.js +55 -0
  3. package/bin/commands/mcp.js +33 -0
  4. package/bin/commands/serve.js +77 -0
  5. package/bin/fluidcad.js +15 -107
  6. package/lib/dist/common/scene-object.d.ts +4 -1
  7. package/lib/dist/common/scene-object.js +9 -2
  8. package/lib/dist/common/solid.d.ts +4 -1
  9. package/lib/dist/common/solid.js +13 -0
  10. package/lib/dist/core/2d/tarc.d.ts +20 -2
  11. package/lib/dist/core/2d/tarc.js +24 -0
  12. package/lib/dist/core/index.d.ts +2 -1
  13. package/lib/dist/core/index.js +1 -0
  14. package/lib/dist/core/interfaces.d.ts +107 -2
  15. package/lib/dist/core/load.d.ts +2 -2
  16. package/lib/dist/core/repeat.js +62 -46
  17. package/lib/dist/core/rib.d.ts +18 -0
  18. package/lib/dist/core/rib.js +37 -0
  19. package/lib/dist/features/2d/arc.d.ts +8 -2
  20. package/lib/dist/features/2d/arc.js +94 -17
  21. package/lib/dist/features/2d/back.js +3 -2
  22. package/lib/dist/features/2d/sketch.d.ts +4 -0
  23. package/lib/dist/features/2d/sketch.js +21 -0
  24. package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
  25. package/lib/dist/features/2d/tarc-constrained.js +8 -0
  26. package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
  27. package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
  28. package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
  29. package/lib/dist/features/2d/tarc-to-object.js +66 -0
  30. package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
  31. package/lib/dist/features/2d/tarc-to-point-tangent.js +3 -0
  32. package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
  33. package/lib/dist/features/2d/tarc-to-point.js +3 -0
  34. package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
  35. package/lib/dist/features/2d/tarc-with-tangent.js +3 -0
  36. package/lib/dist/features/2d/tarc.d.ts +2 -0
  37. package/lib/dist/features/2d/tarc.js +3 -0
  38. package/lib/dist/features/extrude-base.d.ts +9 -0
  39. package/lib/dist/features/extrude-base.js +22 -0
  40. package/lib/dist/features/extrude-to-face.js +1 -5
  41. package/lib/dist/features/extrude-two-distances.js +1 -2
  42. package/lib/dist/features/extrude.js +1 -2
  43. package/lib/dist/features/load.d.ts +6 -0
  44. package/lib/dist/features/load.js +53 -1
  45. package/lib/dist/features/mirror-feature.d.ts +3 -2
  46. package/lib/dist/features/mirror-feature.js +1 -1
  47. package/lib/dist/features/repeat-circular.d.ts +3 -3
  48. package/lib/dist/features/repeat-circular.js +8 -1
  49. package/lib/dist/features/repeat-linear.d.ts +4 -2
  50. package/lib/dist/features/repeat-linear.js +10 -1
  51. package/lib/dist/features/repeat-matrix.d.ts +3 -1
  52. package/lib/dist/features/repeat-matrix.js +7 -2
  53. package/lib/dist/features/rib.d.ts +31 -0
  54. package/lib/dist/features/rib.js +321 -0
  55. package/lib/dist/features/select.d.ts +1 -0
  56. package/lib/dist/features/select.js +81 -10
  57. package/lib/dist/features/shell.d.ts +4 -1
  58. package/lib/dist/features/shell.js +14 -3
  59. package/lib/dist/filters/edge/belongs-to-face.d.ts +12 -9
  60. package/lib/dist/filters/edge/belongs-to-face.js +64 -15
  61. package/lib/dist/filters/filter-builder-base.d.ts +25 -0
  62. package/lib/dist/filters/filter-builder-base.js +47 -0
  63. package/lib/dist/filters/filter.js +39 -14
  64. package/lib/dist/filters/from-object.d.ts +4 -0
  65. package/lib/dist/filters/from-object.js +10 -0
  66. package/lib/dist/helpers/clone-transform.d.ts +2 -1
  67. package/lib/dist/helpers/scene-helpers.d.ts +1 -1
  68. package/lib/dist/helpers/scene-helpers.js +146 -12
  69. package/lib/dist/index.d.ts +7 -1
  70. package/lib/dist/index.js +3 -3
  71. package/lib/dist/io/file-import.d.ts +5 -1
  72. package/lib/dist/io/file-import.js +29 -18
  73. package/lib/dist/math/lazy-matrix.d.ts +31 -0
  74. package/lib/dist/math/lazy-matrix.js +66 -0
  75. package/lib/dist/oc/color-transfer.d.ts +19 -8
  76. package/lib/dist/oc/color-transfer.js +70 -12
  77. package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
  78. package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
  79. package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
  80. package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
  81. package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
  82. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
  83. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
  84. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
  85. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
  86. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
  87. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
  88. package/lib/dist/oc/extrude-ops.d.ts +2 -1
  89. package/lib/dist/oc/extrude-ops.js +51 -2
  90. package/lib/dist/oc/mesh.d.ts +9 -4
  91. package/lib/dist/oc/mesh.js +14 -13
  92. package/lib/dist/oc/rib-ops.d.ts +35 -0
  93. package/lib/dist/oc/rib-ops.js +619 -0
  94. package/lib/dist/oc/shell-ops.d.ts +2 -1
  95. package/lib/dist/oc/shell-ops.js +5 -2
  96. package/lib/dist/oc/topology-index.d.ts +6 -0
  97. package/lib/dist/oc/topology-index.js +36 -0
  98. package/lib/dist/rendering/mesh-builder.d.ts +3 -0
  99. package/lib/dist/rendering/mesh-builder.js +8 -4
  100. package/lib/dist/rendering/render-edge.d.ts +2 -1
  101. package/lib/dist/rendering/render-edge.js +2 -2
  102. package/lib/dist/rendering/render-face.d.ts +2 -1
  103. package/lib/dist/rendering/render-face.js +2 -2
  104. package/lib/dist/rendering/render-solid.d.ts +2 -1
  105. package/lib/dist/rendering/render-solid.js +3 -5
  106. package/lib/dist/rendering/render-wire.d.ts +2 -1
  107. package/lib/dist/rendering/render-wire.js +2 -2
  108. package/lib/dist/rendering/render.d.ts +4 -0
  109. package/lib/dist/rendering/render.js +50 -2
  110. package/lib/dist/rendering/scene-compare.js +3 -0
  111. package/lib/dist/rendering/scene.d.ts +1 -0
  112. package/lib/dist/rendering/scene.js +4 -0
  113. package/lib/dist/scene-manager.d.ts +4 -2
  114. package/lib/dist/scene-manager.js +12 -4
  115. package/lib/dist/tests/features/2d/arc.test.js +64 -0
  116. package/lib/dist/tests/features/2d/back.test.js +17 -1
  117. package/lib/dist/tests/features/2d/tarc.test.js +157 -0
  118. package/lib/dist/tests/features/color-lineage.test.js +18 -0
  119. package/lib/dist/tests/features/filter-positional.test.d.ts +1 -0
  120. package/lib/dist/tests/features/filter-positional.test.js +129 -0
  121. package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
  122. package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
  123. package/lib/dist/tests/features/rib.test.d.ts +1 -0
  124. package/lib/dist/tests/features/rib.test.js +598 -0
  125. package/lib/dist/tests/features/shell.test.js +36 -0
  126. package/lib/dist/tests/global-setup.js +2 -1
  127. package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
  128. package/lib/dist/tests/helpers/extract-blocks.js +56 -0
  129. package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
  130. package/lib/dist/tests/llm-docs-examples.test.js +62 -0
  131. package/lib/dist/tests/scene-compare.test.d.ts +1 -0
  132. package/lib/dist/tests/scene-compare.test.js +77 -0
  133. package/lib/dist/tests/setup.js +2 -1
  134. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  135. package/llm-docs/.coverage-allowlist.txt +9 -0
  136. package/llm-docs/api/arc.md +48 -0
  137. package/llm-docs/api/axis.md +42 -0
  138. package/llm-docs/api/bezier.md +42 -0
  139. package/llm-docs/api/booleans.md +44 -0
  140. package/llm-docs/api/chamfer.md +40 -0
  141. package/llm-docs/api/circle.md +36 -0
  142. package/llm-docs/api/color.md +34 -0
  143. package/llm-docs/api/connect.md +41 -0
  144. package/llm-docs/api/constraint-qualifiers.md +48 -0
  145. package/llm-docs/api/copy.md +63 -0
  146. package/llm-docs/api/cursor-lines.md +50 -0
  147. package/llm-docs/api/cursor-move.md +61 -0
  148. package/llm-docs/api/cut.md +55 -0
  149. package/llm-docs/api/draft.md +36 -0
  150. package/llm-docs/api/edge-filter.md +57 -0
  151. package/llm-docs/api/ellipse.md +34 -0
  152. package/llm-docs/api/extrude.md +74 -0
  153. package/llm-docs/api/face-filter.md +61 -0
  154. package/llm-docs/api/fillet.md +51 -0
  155. package/llm-docs/api/index.json +139 -0
  156. package/llm-docs/api/line.md +42 -0
  157. package/llm-docs/api/load.md +37 -0
  158. package/llm-docs/api/local.md +38 -0
  159. package/llm-docs/api/loft.md +37 -0
  160. package/llm-docs/api/mirror.md +44 -0
  161. package/llm-docs/api/offset.md +36 -0
  162. package/llm-docs/api/part.md +40 -0
  163. package/llm-docs/api/plane.md +44 -0
  164. package/llm-docs/api/polygon.md +37 -0
  165. package/llm-docs/api/primitive-solids.md +39 -0
  166. package/llm-docs/api/project-intersect.md +48 -0
  167. package/llm-docs/api/rect.md +48 -0
  168. package/llm-docs/api/remove.md +32 -0
  169. package/llm-docs/api/repeat.md +79 -0
  170. package/llm-docs/api/revolve.md +38 -0
  171. package/llm-docs/api/rib.md +40 -0
  172. package/llm-docs/api/rotate.md +37 -0
  173. package/llm-docs/api/select.md +41 -0
  174. package/llm-docs/api/shell.md +41 -0
  175. package/llm-docs/api/sketch.md +76 -0
  176. package/llm-docs/api/slot.md +36 -0
  177. package/llm-docs/api/split-trim.md +42 -0
  178. package/llm-docs/api/sweep.md +43 -0
  179. package/llm-docs/api/tarc.md +45 -0
  180. package/llm-docs/api/tcircle.md +38 -0
  181. package/llm-docs/api/tline.md +42 -0
  182. package/llm-docs/api/translate.md +40 -0
  183. package/llm-docs/api/types/aline.md +35 -0
  184. package/llm-docs/api/types/arc-angles.md +29 -0
  185. package/llm-docs/api/types/arc-points.md +48 -0
  186. package/llm-docs/api/types/axis-like.md +38 -0
  187. package/llm-docs/api/types/axis.md +21 -0
  188. package/llm-docs/api/types/boolean-operation.md +50 -0
  189. package/llm-docs/api/types/circular-repeat-options.md +31 -0
  190. package/llm-docs/api/types/common.md +32 -0
  191. package/llm-docs/api/types/cut.md +125 -0
  192. package/llm-docs/api/types/draft.md +21 -0
  193. package/llm-docs/api/types/extrudable-geometry.md +23 -0
  194. package/llm-docs/api/types/extrude.md +194 -0
  195. package/llm-docs/api/types/geometry.md +51 -0
  196. package/llm-docs/api/types/hline.md +35 -0
  197. package/llm-docs/api/types/linear-repeat-options.md +31 -0
  198. package/llm-docs/api/types/loft.md +154 -0
  199. package/llm-docs/api/types/mirror.md +35 -0
  200. package/llm-docs/api/types/offset.md +31 -0
  201. package/llm-docs/api/types/plane-like.md +35 -0
  202. package/llm-docs/api/types/plane-transform-options.md +29 -0
  203. package/llm-docs/api/types/plane.md +21 -0
  204. package/llm-docs/api/types/point-like.md +22 -0
  205. package/llm-docs/api/types/point2dlike.md +26 -0
  206. package/llm-docs/api/types/polygon.md +46 -0
  207. package/llm-docs/api/types/rect.md +128 -0
  208. package/llm-docs/api/types/revolve.md +102 -0
  209. package/llm-docs/api/types/rib.md +133 -0
  210. package/llm-docs/api/types/scene-object.md +33 -0
  211. package/llm-docs/api/types/select.md +21 -0
  212. package/llm-docs/api/types/shell.md +54 -0
  213. package/llm-docs/api/types/slot.md +43 -0
  214. package/llm-docs/api/types/sweep.md +189 -0
  215. package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
  216. package/llm-docs/api/types/transformable.md +93 -0
  217. package/llm-docs/api/types/trim.md +27 -0
  218. package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
  219. package/llm-docs/api/types/vertex.md +17 -0
  220. package/llm-docs/api/types/vline.md +35 -0
  221. package/llm-docs/concepts/coordinate-system.md +45 -0
  222. package/llm-docs/concepts/history-and-rollback.md +40 -0
  223. package/llm-docs/concepts/last-selection.md +49 -0
  224. package/llm-docs/concepts/scene-graph.md +37 -0
  225. package/llm-docs/index.json +1750 -0
  226. package/mcp/dist/client.d.ts +64 -0
  227. package/mcp/dist/client.js +248 -0
  228. package/mcp/dist/discovery.d.ts +11 -0
  229. package/mcp/dist/discovery.js +78 -0
  230. package/mcp/dist/docs-index.d.ts +81 -0
  231. package/mcp/dist/docs-index.js +261 -0
  232. package/mcp/dist/resources.d.ts +4 -0
  233. package/mcp/dist/resources.js +115 -0
  234. package/mcp/dist/server.d.ts +12 -0
  235. package/mcp/dist/server.js +489 -0
  236. package/mcp/dist/tools/coordination.d.ts +9 -0
  237. package/mcp/dist/tools/coordination.js +46 -0
  238. package/mcp/dist/tools/docs.d.ts +66 -0
  239. package/mcp/dist/tools/docs.js +122 -0
  240. package/mcp/dist/tools/engine.d.ts +56 -0
  241. package/mcp/dist/tools/engine.js +145 -0
  242. package/mcp/dist/tools/inspection.d.ts +75 -0
  243. package/mcp/dist/tools/inspection.js +121 -0
  244. package/mcp/dist/tools/screenshot.d.ts +63 -0
  245. package/mcp/dist/tools/screenshot.js +263 -0
  246. package/mcp/dist/tools/source.d.ts +84 -0
  247. package/mcp/dist/tools/source.js +434 -0
  248. package/mcp/dist/tools/workspaces.d.ts +13 -0
  249. package/mcp/dist/tools/workspaces.js +33 -0
  250. package/mcp/dist/types.d.ts +18 -0
  251. package/mcp/dist/types.js +11 -0
  252. package/package.json +19 -5
  253. package/server/dist/code-editor.d.ts +36 -0
  254. package/server/dist/code-editor.js +8 -0
  255. package/server/dist/fluidcad-server.d.ts +50 -0
  256. package/server/dist/fluidcad-server.js +153 -1
  257. package/server/dist/global-registry.d.ts +30 -0
  258. package/server/dist/global-registry.js +126 -0
  259. package/server/dist/index.js +171 -26
  260. package/server/dist/instance-file.d.ts +31 -0
  261. package/server/dist/instance-file.js +73 -0
  262. package/server/dist/lint-fluid-js.d.ts +15 -0
  263. package/server/dist/lint-fluid-js.js +271 -0
  264. package/server/dist/routes/editor.d.ts +24 -0
  265. package/server/dist/routes/editor.js +44 -0
  266. package/server/dist/routes/export.d.ts +1 -1
  267. package/server/dist/routes/export.js +45 -8
  268. package/server/dist/routes/health.d.ts +7 -0
  269. package/server/dist/routes/health.js +14 -0
  270. package/server/dist/routes/lint.d.ts +10 -0
  271. package/server/dist/routes/lint.js +28 -0
  272. package/server/dist/routes/render.d.ts +33 -0
  273. package/server/dist/routes/render.js +34 -0
  274. package/server/dist/routes/scene.d.ts +5 -0
  275. package/server/dist/routes/scene.js +48 -0
  276. package/server/dist/routes/screenshot.js +68 -1
  277. package/server/dist/ws-protocol.d.ts +56 -2
  278. package/ui/dist/assets/{index-DMw0OYCF.js → index-BdqrMDRu.js} +30 -30
  279. package/ui/dist/index.html +1 -1
package/README.md CHANGED
@@ -216,10 +216,10 @@ See the full list of commands in the [Neovim plugin README](extension/neovim/REA
216
216
  <details>
217
217
  <summary><strong>Any Other Editor</strong></summary>
218
218
 
219
- Run the FluidCAD server directly:
219
+ From your project directory, run the FluidCAD server directly:
220
220
 
221
221
  ```bash
222
- npx fluidcad -w ./my-app
222
+ npx fluidcad serve
223
223
  ```
224
224
 
225
225
  This starts a local server and opens a 3D viewport in your browser. Edit your `.fluid.js` files in any editor -- the viewport updates on save.
@@ -230,6 +230,7 @@ This starts a local server and opens a 3D viewport in your browser. Edit your `.
230
230
  |------|-------------|---------|
231
231
  | `-w, --workspace <path>` | Path to your project | Current directory |
232
232
  | `-p, --port <port>` | Server port | `3100` |
233
+ | `--open` | Open the viewport in your default browser when ready | _off_ |
233
234
 
234
235
  </details>
235
236
 
@@ -0,0 +1,55 @@
1
+ import { writeFileSync, existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+
4
+ const INIT_JS = `import { init } from 'fluidcad'\n\nexport default await init()\n`;
5
+
6
+ const TEST_FLUID_JS = `import { extrude, fillet, rect, shell, sketch } from "fluidcad/core";
7
+
8
+ sketch("xy", () => {
9
+ rect(100, 50).radius(10).centered();
10
+ });
11
+
12
+ const e = extrude(30);
13
+
14
+ fillet(4, e.startEdges());
15
+
16
+ shell(-2, e.endFaces());
17
+ `;
18
+
19
+ const JSCONFIG = JSON.stringify({
20
+ compilerOptions: {
21
+ checkJs: true,
22
+ module: 'node20',
23
+ },
24
+ }, null, 2) + '\n';
25
+
26
+ function runInit() {
27
+ const cwd = process.cwd();
28
+
29
+ const initPath = resolve(cwd, 'init.js');
30
+ if (existsSync(initPath)) {
31
+ console.error('init.js already exists in this directory.');
32
+ process.exit(1);
33
+ }
34
+
35
+ writeFileSync(initPath, INIT_JS);
36
+
37
+ const testPath = resolve(cwd, 'test.fluid.js');
38
+ if (!existsSync(testPath)) {
39
+ writeFileSync(testPath, TEST_FLUID_JS);
40
+ }
41
+
42
+ const jsconfigPath = resolve(cwd, 'jsconfig.json');
43
+ if (!existsSync(jsconfigPath)) {
44
+ writeFileSync(jsconfigPath, JSCONFIG);
45
+ }
46
+
47
+ console.log('FluidCAD initialized.');
48
+ }
49
+
50
+ export function registerInitCommand(program) {
51
+ program
52
+ .command('init')
53
+ .description('Scaffold init.js, test.fluid.js, and jsconfig.json in the current directory')
54
+ .action(runInit);
55
+ }
@@ -0,0 +1,33 @@
1
+ import { resolve, dirname } from 'path';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ const mcpEntry = resolve(__dirname, '..', '..', 'mcp', 'dist', 'server.js');
6
+
7
+ async function runMcp() {
8
+ // stdout is reserved for the MCP protocol. Any incidental console.log from
9
+ // FluidCAD internals would corrupt the stream — route everything to stderr.
10
+ console.log = (...args) => console.error(...args);
11
+ console.info = (...args) => console.error(...args);
12
+
13
+ const mod = await import(mcpEntry);
14
+ if (typeof mod.runStdio !== 'function') {
15
+ console.error('mcp/dist/server.js does not export runStdio.');
16
+ process.exit(1);
17
+ }
18
+ await mod.runStdio();
19
+ }
20
+
21
+ export function registerMcpCommand(program) {
22
+ program
23
+ .command('mcp')
24
+ .description('Run the FluidCAD MCP server over stdio (for Claude Desktop, Claude Code, Cursor, ...)')
25
+ .action(async () => {
26
+ try {
27
+ await runMcp();
28
+ } catch (err) {
29
+ console.error(err?.stack || err?.message || String(err));
30
+ process.exit(1);
31
+ }
32
+ });
33
+ }
@@ -0,0 +1,77 @@
1
+ import { fork } from 'child_process';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import open from 'open';
5
+ import { createFileWatcher, findFluidFiles } from '../watcher.js';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const serverEntry = resolve(__dirname, '..', '..', 'server', 'dist', 'index.js');
9
+
10
+ function runServe(opts) {
11
+ const workspacePath = resolve(opts.workspace);
12
+ const port = String(opts.port);
13
+
14
+ const server = fork(serverEntry, [], {
15
+ env: {
16
+ ...process.env,
17
+ FLUIDCAD_SERVER_PORT: port,
18
+ FLUIDCAD_WORKSPACE_PATH: workspacePath,
19
+ },
20
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
21
+ });
22
+
23
+ server.stdout.on('data', (data) => { process.stdout.write(data); });
24
+ server.stderr.on('data', (data) => { process.stderr.write(data); });
25
+
26
+ let watcher;
27
+
28
+ server.on('message', (msg) => {
29
+ if (msg.type === 'ready') {
30
+ console.log(`FluidCAD server ready at ${msg.url}`);
31
+ if (opts.open) {
32
+ open(msg.url).catch((err) => {
33
+ console.error(`Failed to open browser: ${err.message}`);
34
+ });
35
+ }
36
+ }
37
+ if (msg.type === 'init-complete') {
38
+ if (msg.success) {
39
+ console.log('FluidCAD initialized successfully.');
40
+ watcher = createFileWatcher(workspacePath, server);
41
+
42
+ const files = findFluidFiles(workspacePath);
43
+ if (files.length > 0) {
44
+ server.send({ type: 'process-file', filePath: files[0] });
45
+ }
46
+ } else {
47
+ console.error(`FluidCAD initialization failed: ${msg.error}`);
48
+ process.exit(1);
49
+ }
50
+ }
51
+ });
52
+
53
+ server.on('exit', (code) => {
54
+ if (watcher) { watcher.close(); }
55
+ process.exit(code || 0);
56
+ });
57
+
58
+ process.on('SIGINT', () => {
59
+ if (watcher) { watcher.close(); }
60
+ server.kill('SIGINT');
61
+ });
62
+
63
+ process.on('SIGTERM', () => {
64
+ if (watcher) { watcher.close(); }
65
+ server.kill('SIGTERM');
66
+ });
67
+ }
68
+
69
+ export function registerServeCommand(program) {
70
+ program
71
+ .command('serve')
72
+ .description('Start the FluidCAD server and watch .fluid.js files')
73
+ .option('-w, --workspace <path>', 'workspace directory', process.cwd())
74
+ .option('-p, --port <port>', 'server port', '3100')
75
+ .option('--open', 'open the UI in the default browser when ready', false)
76
+ .action(runServe);
77
+ }
package/bin/fluidcad.js CHANGED
@@ -1,115 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { fork } from 'child_process';
4
- import { resolve, dirname } from 'path';
3
+ import { Command } from 'commander';
4
+ import { readFileSync } from 'fs';
5
+ import { dirname, resolve } from 'path';
5
6
  import { fileURLToPath } from 'url';
6
- import { parseArgs } from 'util';
7
- import { writeFileSync, existsSync } from 'fs';
8
- import { createFileWatcher, findFluidFiles } from './watcher.js';
7
+ import { registerInitCommand } from './commands/init.js';
8
+ import { registerServeCommand } from './commands/serve.js';
9
+ import { registerMcpCommand } from './commands/mcp.js';
9
10
 
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
11
13
 
12
- const { values, positionals } = parseArgs({
13
- options: {
14
- port: { type: 'string', short: 'p', default: '3100' },
15
- workspace: { type: 'string', short: 'w', default: process.cwd() },
16
- },
17
- allowPositionals: true,
18
- });
14
+ const program = new Command()
15
+ .name('fluidcad')
16
+ .description('FluidCAD CLI')
17
+ .version(pkg.version);
19
18
 
20
- if (positionals[0] === 'init') {
21
- const cwd = process.cwd();
19
+ registerInitCommand(program);
20
+ registerServeCommand(program);
21
+ registerMcpCommand(program);
22
22
 
23
- const initPath = resolve(cwd, 'init.js');
24
- if (existsSync(initPath)) {
25
- console.error('init.js already exists in this directory.');
26
- process.exit(1);
27
- }
28
-
29
- writeFileSync(initPath, `import { init } from 'fluidcad'\n\nexport default await init()\n`);
30
-
31
- const testPath = resolve(cwd, 'test.fluid.js');
32
- if (!existsSync(testPath)) {
33
- writeFileSync(testPath, `import { extrude, fillet, rect, shell, sketch } from "fluidcad/core";
34
-
35
- sketch("xy", () => {
36
- rect(100, 50).radius(10).centered();
37
- });
38
-
39
- const e = extrude(30);
40
-
41
- fillet(4, e.startEdges());
42
-
43
- shell(-2, e.endFaces());
44
- `);
45
- }
46
-
47
- const jsconfigPath = resolve(cwd, 'jsconfig.json');
48
- if (!existsSync(jsconfigPath)) {
49
- writeFileSync(jsconfigPath, JSON.stringify({
50
- compilerOptions: {
51
- checkJs: true,
52
- module: 'node20',
53
- },
54
- }, null, 2) + '\n');
55
- }
56
-
57
- console.log('FluidCAD initialized.');
58
- process.exit(0);
59
- }
60
-
61
- const serverEntry = resolve(__dirname, '..', 'server', 'dist', 'index.js');
62
-
63
- const server = fork(serverEntry, [], {
64
- env: {
65
- ...process.env,
66
- FLUIDCAD_SERVER_PORT: values.port,
67
- FLUIDCAD_WORKSPACE_PATH: resolve(values.workspace),
68
- },
69
- stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
70
- });
71
-
72
- server.stdout.on('data', (data) => {
73
- process.stdout.write(data);
74
- });
75
-
76
- server.stderr.on('data', (data) => {
77
- process.stderr.write(data);
78
- });
79
-
80
- const workspacePath = resolve(values.workspace);
81
- let watcher;
82
-
83
- server.on('message', (msg) => {
84
- if (msg.type === 'ready') {
85
- console.log(`FluidCAD server ready at ${msg.url}`);
86
- }
87
- if (msg.type === 'init-complete') {
88
- if (msg.success) {
89
- console.log('FluidCAD initialized successfully.');
90
- watcher = createFileWatcher(workspacePath, server);
91
-
92
- const files = findFluidFiles(workspacePath);
93
- if (files.length > 0) {
94
- server.send({ type: 'process-file', filePath: files[0] });
95
- }
96
- } else {
97
- console.error(`FluidCAD initialization failed: ${msg.error}`);
98
- process.exit(1);
99
- }
100
- }
101
- });
102
-
103
- server.on('exit', (code) => {
104
- process.exit(code || 0);
105
- });
106
-
107
- process.on('SIGINT', () => {
108
- if (watcher) { watcher.close(); }
109
- server.kill('SIGINT');
110
- });
111
-
112
- process.on('SIGTERM', () => {
113
- if (watcher) { watcher.close(); }
114
- server.kill('SIGTERM');
115
- });
23
+ program.parseAsync(process.argv);
@@ -2,6 +2,7 @@ import { Shape, ShapeFilter } from "./shape.js";
2
2
  import { Face } from "./face.js";
3
3
  import { Edge } from "./edge.js";
4
4
  import { Matrix4 } from "../math/matrix4.js";
5
+ import { LazyMatrix } from "../math/lazy-matrix.js";
5
6
  import { ISceneObject } from "../core/interfaces.js";
6
7
  import { FusionScope, OperationMode } from "../features/extrude-options.js";
7
8
  import { ShapeType } from "./shape-type.js";
@@ -59,6 +60,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
59
60
  constructor();
60
61
  get id(): string;
61
62
  get parentId(): string | null;
63
+ inheritIdentityFrom(other: SceneObject): void;
62
64
  private setParent;
63
65
  protected setAlwaysVisible(): void;
64
66
  isAlwaysVisible(): boolean;
@@ -89,7 +91,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
89
91
  getDependencies(): SceneObject[];
90
92
  createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
91
93
  clone(): SceneObject[];
92
- setTransform(matrix: Matrix4): void;
94
+ setTransform(matrix: Matrix4 | LazyMatrix): void;
93
95
  getTransform(): Matrix4 | null;
94
96
  setCloneSource(source: SceneObject): void;
95
97
  getCloneSource(): SceneObject | null;
@@ -150,6 +152,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
150
152
  setOrder(order: number): void;
151
153
  getOrder(): number;
152
154
  getName(): string;
155
+ hasCustomName(): boolean;
153
156
  name(value: string): this;
154
157
  guide(): this;
155
158
  reusable(): this;
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "crypto";
2
+ import { LazyMatrix } from "../math/lazy-matrix.js";
2
3
  export class SceneObject {
3
4
  state;
4
5
  children = [];
@@ -36,6 +37,9 @@ export class SceneObject {
36
37
  get parentId() {
37
38
  return this._parent?.id || null;
38
39
  }
40
+ inheritIdentityFrom(other) {
41
+ this._id = other._id;
42
+ }
39
43
  setParent(parent) {
40
44
  this._parent = parent;
41
45
  }
@@ -197,10 +201,10 @@ export class SceneObject {
197
201
  return result;
198
202
  }
199
203
  setTransform(matrix) {
200
- this._transform = matrix;
204
+ this._transform = matrix instanceof LazyMatrix ? matrix : LazyMatrix.of(matrix);
201
205
  }
202
206
  getTransform() {
203
- return this._transform;
207
+ return this._transform ? this._transform.resolve() : null;
204
208
  }
205
209
  setCloneSource(source) {
206
210
  this._cloneSource = source;
@@ -452,6 +456,9 @@ export class SceneObject {
452
456
  getName() {
453
457
  return this._name ?? this.getType();
454
458
  }
459
+ hasCustomName() {
460
+ return this._name !== null;
461
+ }
455
462
  name(value) {
456
463
  this._name = value;
457
464
  return this;
@@ -1,4 +1,4 @@
1
- import type { TopoDS_Edge, TopoDS_Face, TopoDS_Solid } from "occjs-wrapper";
1
+ import type { TopoDS_Edge, TopoDS_Face, TopoDS_Solid, TopTools_IndexedDataMapOfShapeListOfShape } from "occjs-wrapper";
2
2
  import { ShapeType } from "./shape-type.js";
3
3
  import { Shape } from "./shape.js";
4
4
  import { Face } from "./face.js";
@@ -6,6 +6,7 @@ import { Edge } from "./edge.js";
6
6
  export declare class Solid extends Shape<TopoDS_Solid> {
7
7
  private faces;
8
8
  private edges;
9
+ private edgeToFacesIndex;
9
10
  constructor(solid: TopoDS_Solid);
10
11
  getType(): ShapeType;
11
12
  isSolid(): boolean;
@@ -15,6 +16,8 @@ export declare class Solid extends Shape<TopoDS_Solid> {
15
16
  getFace(face: TopoDS_Face): Face | null;
16
17
  hasFace(face: TopoDS_Face): boolean;
17
18
  hasEdge(edge: TopoDS_Edge): TopoDS_Edge;
19
+ getEdgeToFacesIndex(): TopTools_IndexedDataMapOfShapeListOfShape;
20
+ dispose(): void;
18
21
  copy(): Shape;
19
22
  static fromTopoDSSolid(solid: TopoDS_Solid): Solid;
20
23
  }
@@ -1,8 +1,10 @@
1
1
  import { Explorer } from "../oc/explorer.js";
2
+ import { TopologyIndex } from "../oc/topology-index.js";
2
3
  import { Shape } from "./shape.js";
3
4
  export class Solid extends Shape {
4
5
  faces = null;
5
6
  edges = null;
7
+ edgeToFacesIndex = null;
6
8
  constructor(solid) {
7
9
  super(solid);
8
10
  }
@@ -59,6 +61,17 @@ export class Solid extends Shape {
59
61
  }
60
62
  return null;
61
63
  }
64
+ getEdgeToFacesIndex() {
65
+ if (!this.edgeToFacesIndex) {
66
+ this.edgeToFacesIndex = TopologyIndex.buildEdgeToFaces(this.getShape());
67
+ }
68
+ return this.edgeToFacesIndex;
69
+ }
70
+ dispose() {
71
+ this.edgeToFacesIndex?.delete();
72
+ this.edgeToFacesIndex = null;
73
+ super.dispose();
74
+ }
62
75
  copy() {
63
76
  const copied = new Solid(this.getShape());
64
77
  for (const entry of this.colorMap) {
@@ -1,7 +1,25 @@
1
1
  import { Point2DLike } from "../../math/point.js";
2
2
  import { QualifiedSceneObject } from "../../features/2d/constraints/qualified-geometry.js";
3
- import { IGeometry, ISceneObject, ITangentArcTwoObjects } from "../interfaces.js";
3
+ import { IGeometry, ISceneObject, ITangentArcToObject, ITangentArcTwoObjects } from "../interfaces.js";
4
4
  interface TArcFunction {
5
+ /**
6
+ * Draws a tangent arc from the current position using the current tangent
7
+ * direction, ending tangent to a target line. The radius is solved
8
+ * automatically; the arc must be tangent to the target but does not need
9
+ * to touch its finite extent. By default the arc curves to the left of
10
+ * the start tangent — chain `.flip()` to curve to the right instead.
11
+ * @param target - The target line (or a qualified line)
12
+ */
13
+ (target: ISceneObject | QualifiedSceneObject): ITangentArcToObject;
14
+ /**
15
+ * Draws a tangent arc from the current position using the current tangent
16
+ * direction, with the given radius, ending at the first intersection with
17
+ * the target geometry along the arc's sweep direction. A negative radius
18
+ * flips the sweep direction. Supported targets: lines, circles, and arcs.
19
+ * @param radius - The arc radius. A negative value flips the sweep direction.
20
+ * @param target - The target geometry to intersect with
21
+ */
22
+ (radius: number, target: ISceneObject | QualifiedSceneObject): IGeometry;
5
23
  /**
6
24
  * Draws a tangent arc with a given radius and end angle.
7
25
  * @param radius - The arc radius (defaults to 100). A negative value flips the sweep direction.
@@ -34,7 +52,7 @@ interface TArcFunction {
34
52
  */
35
53
  (startPoint: Point2DLike, endPoint: Point2DLike, tangent: Point2DLike): IGeometry;
36
54
  /**
37
- * Draws a tangent arc between two geometry objects.
55
+ * Draws all possible tangent arcs between two geometry objects.
38
56
  * @param c1 - The first geometry object
39
57
  * @param c2 - The second geometry object
40
58
  * @param radius - The arc radius
@@ -3,13 +3,37 @@ import { TangentArc } from "../../features/2d/tarc.js";
3
3
  import { TangentArcToPoint } from "../../features/2d/tarc-to-point.js";
4
4
  import { TangentArcToPointTangent } from "../../features/2d/tarc-to-point-tangent.js";
5
5
  import { TangentArcWithTangent } from "../../features/2d/tarc-with-tangent.js";
6
+ import { TangentArcToObject } from "../../features/2d/tarc-to-object.js";
7
+ import { TangentArcRadiusToObject } from "../../features/2d/tarc-radius-to-object.js";
6
8
  import { Move } from "../../features/2d/move.js";
7
9
  import { normalizePoint2D } from "../../helpers/normalize.js";
8
10
  import { registerBuilder } from "../../index.js";
9
11
  import { QualifiedSceneObject } from "../../features/2d/constraints/qualified-geometry.js";
10
12
  import { TangentArcTwoObjects } from "../../features/2d/tarc-constrained.js";
13
+ import { SceneObject } from "../../common/scene-object.js";
11
14
  function build(context) {
12
15
  return function tarc() {
16
+ // tArc(target): single scene-object target, radius solved automatically.
17
+ // LazyVertex extends SceneObject but represents a point — fall through to the
18
+ // Point2DLike branch below so tArc(lazyVertex) becomes tArc(endPoint).
19
+ if (arguments.length === 1 &&
20
+ ((arguments[0] instanceof SceneObject && !isPoint2DLike(arguments[0])) ||
21
+ arguments[0] instanceof QualifiedSceneObject)) {
22
+ const target = QualifiedSceneObject.from(arguments[0]);
23
+ const arc = new TangentArcToObject(target);
24
+ context.addSceneObject(arc);
25
+ return arc;
26
+ }
27
+ // tArc(radius, target): explicit radius, end at first intersection with target
28
+ if (arguments.length === 2 &&
29
+ typeof arguments[0] === 'number' &&
30
+ (arguments[1] instanceof SceneObject || arguments[1] instanceof QualifiedSceneObject)) {
31
+ const radius = arguments[0];
32
+ const target = QualifiedSceneObject.from(arguments[1]);
33
+ const arc = new TangentArcRadiusToObject(radius, target);
34
+ context.addSceneObject(arc);
35
+ return arc;
36
+ }
13
37
  // tarc(c1, c2, radius): fillet arc tangent to two circles/points
14
38
  if ((arguments.length === 3 || arguments.length === 4) && typeof arguments[2] === 'number') {
15
39
  const o1 = isPoint2DLike(arguments[0]) ? normalizePoint2D(arguments[0]) : arguments[0];
@@ -1,4 +1,4 @@
1
- export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft } from "./interfaces.js";
1
+ export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft, IRib } from "./interfaces.js";
2
2
  export { default as axis } from "./axis.js";
3
3
  export { default as local } from "./local.js";
4
4
  export { default as plane } from "./plane.js";
@@ -23,6 +23,7 @@ export { default as repeat } from "./repeat.js";
23
23
  export { default as load } from "./load.js";
24
24
  export { default as loft } from "./loft.js";
25
25
  export { default as sweep } from "./sweep.js";
26
+ export { default as rib } from "./rib.js";
26
27
  export { default as color } from "./color.js";
27
28
  export { default as draft } from "./draft.js";
28
29
  export { default as remove } from "./remove.js";
@@ -22,6 +22,7 @@ export { default as repeat } from "./repeat.js";
22
22
  export { default as load } from "./load.js";
23
23
  export { default as loft } from "./loft.js";
24
24
  export { default as sweep } from "./sweep.js";
25
+ export { default as rib } from "./rib.js";
25
26
  export { default as color } from "./color.js";
26
27
  export { default as draft } from "./draft.js";
27
28
  export { default as remove } from "./remove.js";