fluidcad 0.0.33 → 0.0.35

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 (363) hide show
  1. package/README.md +72 -2
  2. package/bin/commands/init.js +55 -0
  3. package/bin/commands/login.js +120 -0
  4. package/bin/commands/mcp.js +33 -0
  5. package/bin/commands/pack.js +49 -0
  6. package/bin/commands/publish.js +136 -0
  7. package/bin/commands/serve.js +77 -0
  8. package/bin/fluidcad.js +21 -107
  9. package/bin/lib/api-client.js +40 -0
  10. package/bin/lib/browser.js +16 -0
  11. package/bin/lib/config.js +39 -0
  12. package/bin/lib/model-config.js +38 -0
  13. package/bin/lib/workspace.js +57 -0
  14. package/lib/dist/common/scene-object.d.ts +2 -1
  15. package/lib/dist/common/scene-object.js +3 -2
  16. package/lib/dist/common/shape-factory.d.ts +2 -1
  17. package/lib/dist/common/shape-factory.js +4 -0
  18. package/lib/dist/common/transformable-primitive.d.ts +6 -5
  19. package/lib/dist/common/transformable-primitive.js +8 -7
  20. package/lib/dist/common/vertex.js +0 -1
  21. package/lib/dist/core/2d/aline.d.ts +4 -3
  22. package/lib/dist/core/2d/aline.js +3 -2
  23. package/lib/dist/core/2d/arc.d.ts +3 -2
  24. package/lib/dist/core/2d/arc.js +4 -3
  25. package/lib/dist/core/2d/bezier.d.ts +8 -6
  26. package/lib/dist/core/2d/circle.d.ts +4 -3
  27. package/lib/dist/core/2d/circle.js +3 -2
  28. package/lib/dist/core/2d/ellipse.d.ts +5 -4
  29. package/lib/dist/core/2d/ellipse.js +5 -4
  30. package/lib/dist/core/2d/hline.d.ts +4 -3
  31. package/lib/dist/core/2d/hline.js +5 -3
  32. package/lib/dist/core/2d/line.js +1 -0
  33. package/lib/dist/core/2d/offset.d.ts +3 -2
  34. package/lib/dist/core/2d/offset.js +6 -5
  35. package/lib/dist/core/2d/polygon.d.ts +5 -4
  36. package/lib/dist/core/2d/polygon.js +10 -9
  37. package/lib/dist/core/2d/rect.d.ts +4 -3
  38. package/lib/dist/core/2d/rect.js +10 -9
  39. package/lib/dist/core/2d/slot.d.ts +14 -6
  40. package/lib/dist/core/2d/slot.js +19 -8
  41. package/lib/dist/core/2d/tarc.d.ts +20 -2
  42. package/lib/dist/core/2d/tarc.js +24 -0
  43. package/lib/dist/core/2d/vline.d.ts +4 -3
  44. package/lib/dist/core/2d/vline.js +5 -3
  45. package/lib/dist/core/chamfer.d.ts +5 -4
  46. package/lib/dist/core/chamfer.js +7 -6
  47. package/lib/dist/core/color.d.ts +3 -2
  48. package/lib/dist/core/color.js +2 -1
  49. package/lib/dist/core/cut.d.ts +4 -3
  50. package/lib/dist/core/cut.js +5 -4
  51. package/lib/dist/core/cylinder.d.ts +2 -1
  52. package/lib/dist/core/cylinder.js +2 -1
  53. package/lib/dist/core/draft.d.ts +3 -2
  54. package/lib/dist/core/draft.js +3 -2
  55. package/lib/dist/core/extrude.d.ts +4 -3
  56. package/lib/dist/core/extrude.js +5 -4
  57. package/lib/dist/core/fillet.d.ts +5 -4
  58. package/lib/dist/core/fillet.js +6 -5
  59. package/lib/dist/core/index.d.ts +1 -0
  60. package/lib/dist/core/index.js +1 -0
  61. package/lib/dist/core/interfaces.d.ts +55 -25
  62. package/lib/dist/core/param.d.ts +74 -0
  63. package/lib/dist/core/param.js +147 -0
  64. package/lib/dist/core/repeat.d.ts +2 -1
  65. package/lib/dist/core/repeat.js +72 -54
  66. package/lib/dist/core/revolve.d.ts +2 -1
  67. package/lib/dist/core/revolve.js +3 -2
  68. package/lib/dist/core/rib.d.ts +3 -2
  69. package/lib/dist/core/rib.js +6 -2
  70. package/lib/dist/core/rotate.d.ts +5 -4
  71. package/lib/dist/core/rotate.js +4 -3
  72. package/lib/dist/core/shell.d.ts +3 -2
  73. package/lib/dist/core/shell.js +3 -2
  74. package/lib/dist/core/sphere.d.ts +3 -2
  75. package/lib/dist/core/sphere.js +2 -1
  76. package/lib/dist/core/translate.d.ts +7 -6
  77. package/lib/dist/core/translate.js +6 -5
  78. package/lib/dist/features/2d/arc.d.ts +8 -2
  79. package/lib/dist/features/2d/arc.js +94 -17
  80. package/lib/dist/features/2d/back.js +3 -2
  81. package/lib/dist/features/2d/bezier.js +16 -16
  82. package/lib/dist/features/2d/circle.js +4 -0
  83. package/lib/dist/features/2d/ellipse.js +4 -0
  84. package/lib/dist/features/2d/hline.d.ts +3 -0
  85. package/lib/dist/features/2d/hline.js +9 -2
  86. package/lib/dist/features/2d/line.d.ts +3 -0
  87. package/lib/dist/features/2d/line.js +11 -3
  88. package/lib/dist/features/2d/sketch.d.ts +4 -0
  89. package/lib/dist/features/2d/sketch.js +25 -0
  90. package/lib/dist/features/2d/slot.d.ts +5 -0
  91. package/lib/dist/features/2d/slot.js +52 -7
  92. package/lib/dist/features/2d/tarc-constrained.d.ts +2 -0
  93. package/lib/dist/features/2d/tarc-constrained.js +8 -0
  94. package/lib/dist/features/2d/tarc-radius-to-object.d.ts +16 -0
  95. package/lib/dist/features/2d/tarc-radius-to-object.js +58 -0
  96. package/lib/dist/features/2d/tarc-to-object.d.ts +18 -0
  97. package/lib/dist/features/2d/tarc-to-object.js +66 -0
  98. package/lib/dist/features/2d/tarc-to-point-tangent.d.ts +2 -0
  99. package/lib/dist/features/2d/tarc-to-point-tangent.js +6 -0
  100. package/lib/dist/features/2d/tarc-to-point.d.ts +2 -0
  101. package/lib/dist/features/2d/tarc-to-point.js +6 -0
  102. package/lib/dist/features/2d/tarc-with-tangent.d.ts +2 -0
  103. package/lib/dist/features/2d/tarc-with-tangent.js +6 -0
  104. package/lib/dist/features/2d/tarc.d.ts +2 -0
  105. package/lib/dist/features/2d/tarc.js +6 -0
  106. package/lib/dist/features/2d/vline.d.ts +3 -0
  107. package/lib/dist/features/2d/vline.js +9 -2
  108. package/lib/dist/features/copy-circular.d.ts +4 -3
  109. package/lib/dist/features/copy-circular.js +16 -9
  110. package/lib/dist/features/copy-circular2d.js +16 -9
  111. package/lib/dist/features/copy-linear.d.ts +4 -3
  112. package/lib/dist/features/copy-linear.js +18 -12
  113. package/lib/dist/features/copy-linear2d.js +18 -12
  114. package/lib/dist/features/extrude-base.d.ts +13 -3
  115. package/lib/dist/features/extrude-base.js +32 -3
  116. package/lib/dist/features/extrude-to-face.js +1 -5
  117. package/lib/dist/features/extrude-two-distances.js +1 -2
  118. package/lib/dist/features/extrude.js +1 -2
  119. package/lib/dist/features/mirror-feature.d.ts +3 -2
  120. package/lib/dist/features/mirror-feature.js +1 -1
  121. package/lib/dist/features/mirror-shape2d.js +2 -2
  122. package/lib/dist/features/repeat-base.d.ts +13 -0
  123. package/lib/dist/features/repeat-base.js +21 -0
  124. package/lib/dist/features/repeat-circular.d.ts +8 -7
  125. package/lib/dist/features/repeat-circular.js +7 -3
  126. package/lib/dist/features/repeat-linear.d.ts +9 -7
  127. package/lib/dist/features/repeat-linear.js +9 -3
  128. package/lib/dist/features/repeat-matrix.d.ts +3 -1
  129. package/lib/dist/features/repeat-matrix.js +7 -2
  130. package/lib/dist/features/shell.d.ts +4 -1
  131. package/lib/dist/features/shell.js +14 -3
  132. package/lib/dist/helpers/clone-transform.d.ts +2 -1
  133. package/lib/dist/index.d.ts +12 -1
  134. package/lib/dist/index.js +11 -4
  135. package/lib/dist/io/file-import.d.ts +7 -0
  136. package/lib/dist/io/file-import.js +30 -10
  137. package/lib/dist/math/lazy-matrix.d.ts +36 -0
  138. package/lib/dist/math/lazy-matrix.js +134 -0
  139. package/lib/dist/oc/boolean-ops.d.ts +2 -2
  140. package/lib/dist/oc/constraints/constraint-solver-adaptor.d.ts +5 -0
  141. package/lib/dist/oc/constraints/constraint-solver-adaptor.js +16 -0
  142. package/lib/dist/oc/constraints/constraint-solver.d.ts +4 -0
  143. package/lib/dist/oc/constraints/curve/curve-constraint-solver.d.ts +4 -0
  144. package/lib/dist/oc/constraints/curve/curve-constraint-solver.js +3 -0
  145. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.d.ts +6 -1
  146. package/lib/dist/oc/constraints/geometric/geometric-constraint-solver.js +4 -0
  147. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.d.ts +8 -0
  148. package/lib/dist/oc/constraints/geometric/tangent-arc-from-point-tangent.js +111 -0
  149. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.d.ts +8 -0
  150. package/lib/dist/oc/constraints/geometric/tangent-arc-radius-to-object.js +161 -0
  151. package/lib/dist/oc/mesh.d.ts +9 -4
  152. package/lib/dist/oc/mesh.js +14 -13
  153. package/lib/dist/oc/shell-ops.d.ts +2 -1
  154. package/lib/dist/oc/shell-ops.js +5 -2
  155. package/lib/dist/param-registry.d.ts +34 -0
  156. package/lib/dist/param-registry.js +60 -0
  157. package/lib/dist/rendering/mesh-builder.d.ts +3 -0
  158. package/lib/dist/rendering/mesh-builder.js +10 -5
  159. package/lib/dist/rendering/render-edge.d.ts +2 -1
  160. package/lib/dist/rendering/render-edge.js +2 -2
  161. package/lib/dist/rendering/render-face.d.ts +2 -1
  162. package/lib/dist/rendering/render-face.js +2 -2
  163. package/lib/dist/rendering/render-solid.d.ts +2 -1
  164. package/lib/dist/rendering/render-solid.js +2 -2
  165. package/lib/dist/rendering/render-wire.d.ts +2 -1
  166. package/lib/dist/rendering/render-wire.js +2 -2
  167. package/lib/dist/rendering/render.d.ts +3 -0
  168. package/lib/dist/rendering/render.js +7 -2
  169. package/lib/dist/scene-manager.d.ts +4 -2
  170. package/lib/dist/scene-manager.js +12 -4
  171. package/lib/dist/tests/features/2d/arc.test.js +64 -0
  172. package/lib/dist/tests/features/2d/back.test.js +17 -1
  173. package/lib/dist/tests/features/2d/tarc.test.js +157 -0
  174. package/lib/dist/tests/features/copy-circular.test.js +1 -1
  175. package/lib/dist/tests/features/copy-linear.test.js +10 -10
  176. package/lib/dist/tests/features/repeat-user-repro-cache.test.d.ts +1 -0
  177. package/lib/dist/tests/features/repeat-user-repro-cache.test.js +97 -0
  178. package/lib/dist/tests/features/repeat-user-repro.test.d.ts +1 -0
  179. package/lib/dist/tests/features/repeat-user-repro.test.js +60 -0
  180. package/lib/dist/tests/features/shell.test.js +36 -0
  181. package/lib/dist/tests/global-setup.js +2 -1
  182. package/lib/dist/tests/helpers/extract-blocks.d.ts +9 -0
  183. package/lib/dist/tests/helpers/extract-blocks.js +56 -0
  184. package/lib/dist/tests/llm-docs-examples.test.d.ts +1 -0
  185. package/lib/dist/tests/llm-docs-examples.test.js +62 -0
  186. package/lib/dist/tests/setup.js +2 -1
  187. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  188. package/llm-docs/.coverage-allowlist.txt +9 -0
  189. package/llm-docs/api/arc.md +48 -0
  190. package/llm-docs/api/axis.md +42 -0
  191. package/llm-docs/api/bezier.md +41 -0
  192. package/llm-docs/api/booleans.md +44 -0
  193. package/llm-docs/api/chamfer.md +40 -0
  194. package/llm-docs/api/circle.md +36 -0
  195. package/llm-docs/api/color.md +34 -0
  196. package/llm-docs/api/connect.md +41 -0
  197. package/llm-docs/api/constraint-qualifiers.md +48 -0
  198. package/llm-docs/api/copy.md +63 -0
  199. package/llm-docs/api/cursor-lines.md +50 -0
  200. package/llm-docs/api/cursor-move.md +61 -0
  201. package/llm-docs/api/cut.md +55 -0
  202. package/llm-docs/api/draft.md +36 -0
  203. package/llm-docs/api/edge-filter.md +57 -0
  204. package/llm-docs/api/ellipse.md +34 -0
  205. package/llm-docs/api/extrude.md +74 -0
  206. package/llm-docs/api/face-filter.md +61 -0
  207. package/llm-docs/api/fillet.md +51 -0
  208. package/llm-docs/api/index.json +139 -0
  209. package/llm-docs/api/line.md +42 -0
  210. package/llm-docs/api/load.md +37 -0
  211. package/llm-docs/api/local.md +38 -0
  212. package/llm-docs/api/loft.md +37 -0
  213. package/llm-docs/api/mirror.md +44 -0
  214. package/llm-docs/api/offset.md +36 -0
  215. package/llm-docs/api/part.md +40 -0
  216. package/llm-docs/api/plane.md +44 -0
  217. package/llm-docs/api/polygon.md +37 -0
  218. package/llm-docs/api/primitive-solids.md +39 -0
  219. package/llm-docs/api/project-intersect.md +48 -0
  220. package/llm-docs/api/rect.md +48 -0
  221. package/llm-docs/api/remove.md +32 -0
  222. package/llm-docs/api/repeat.md +79 -0
  223. package/llm-docs/api/revolve.md +38 -0
  224. package/llm-docs/api/rib.md +40 -0
  225. package/llm-docs/api/rotate.md +37 -0
  226. package/llm-docs/api/select.md +41 -0
  227. package/llm-docs/api/shell.md +41 -0
  228. package/llm-docs/api/sketch.md +76 -0
  229. package/llm-docs/api/slot.md +36 -0
  230. package/llm-docs/api/split-trim.md +42 -0
  231. package/llm-docs/api/sweep.md +43 -0
  232. package/llm-docs/api/tarc.md +45 -0
  233. package/llm-docs/api/tcircle.md +38 -0
  234. package/llm-docs/api/tline.md +42 -0
  235. package/llm-docs/api/translate.md +40 -0
  236. package/llm-docs/api/types/aline.md +35 -0
  237. package/llm-docs/api/types/arc-angles.md +29 -0
  238. package/llm-docs/api/types/arc-points.md +48 -0
  239. package/llm-docs/api/types/axis-like.md +38 -0
  240. package/llm-docs/api/types/axis.md +21 -0
  241. package/llm-docs/api/types/boolean-operation.md +50 -0
  242. package/llm-docs/api/types/circular-repeat-options.md +31 -0
  243. package/llm-docs/api/types/common.md +32 -0
  244. package/llm-docs/api/types/cut.md +125 -0
  245. package/llm-docs/api/types/draft.md +21 -0
  246. package/llm-docs/api/types/extrudable-geometry.md +23 -0
  247. package/llm-docs/api/types/extrude.md +194 -0
  248. package/llm-docs/api/types/geometry.md +51 -0
  249. package/llm-docs/api/types/hline.md +35 -0
  250. package/llm-docs/api/types/linear-repeat-options.md +31 -0
  251. package/llm-docs/api/types/loft.md +154 -0
  252. package/llm-docs/api/types/mirror.md +35 -0
  253. package/llm-docs/api/types/offset.md +31 -0
  254. package/llm-docs/api/types/plane-like.md +35 -0
  255. package/llm-docs/api/types/plane-transform-options.md +29 -0
  256. package/llm-docs/api/types/plane.md +21 -0
  257. package/llm-docs/api/types/point-like.md +22 -0
  258. package/llm-docs/api/types/point2dlike.md +26 -0
  259. package/llm-docs/api/types/polygon.md +46 -0
  260. package/llm-docs/api/types/rect.md +128 -0
  261. package/llm-docs/api/types/revolve.md +102 -0
  262. package/llm-docs/api/types/rib.md +133 -0
  263. package/llm-docs/api/types/scene-object.md +33 -0
  264. package/llm-docs/api/types/select.md +21 -0
  265. package/llm-docs/api/types/shell.md +54 -0
  266. package/llm-docs/api/types/slot.md +43 -0
  267. package/llm-docs/api/types/sweep.md +189 -0
  268. package/llm-docs/api/types/tangent-arc-two-objects.md +46 -0
  269. package/llm-docs/api/types/transformable.md +93 -0
  270. package/llm-docs/api/types/trim.md +27 -0
  271. package/llm-docs/api/types/two-objects-tangent-line.md +46 -0
  272. package/llm-docs/api/types/vertex.md +17 -0
  273. package/llm-docs/api/types/vline.md +35 -0
  274. package/llm-docs/concepts/coordinate-system.md +45 -0
  275. package/llm-docs/concepts/history-and-rollback.md +40 -0
  276. package/llm-docs/concepts/last-selection.md +49 -0
  277. package/llm-docs/concepts/scene-graph.md +37 -0
  278. package/llm-docs/index.json +1750 -0
  279. package/mcp/dist/client.d.ts +65 -0
  280. package/mcp/dist/client.js +255 -0
  281. package/mcp/dist/discovery.d.ts +11 -0
  282. package/mcp/dist/discovery.js +78 -0
  283. package/mcp/dist/docs-index.d.ts +81 -0
  284. package/mcp/dist/docs-index.js +261 -0
  285. package/mcp/dist/resources.d.ts +4 -0
  286. package/mcp/dist/resources.js +115 -0
  287. package/mcp/dist/server.d.ts +12 -0
  288. package/mcp/dist/server.js +502 -0
  289. package/mcp/dist/tools/coordination.d.ts +9 -0
  290. package/mcp/dist/tools/coordination.js +46 -0
  291. package/mcp/dist/tools/docs.d.ts +66 -0
  292. package/mcp/dist/tools/docs.js +122 -0
  293. package/mcp/dist/tools/engine.d.ts +72 -0
  294. package/mcp/dist/tools/engine.js +190 -0
  295. package/mcp/dist/tools/inspection.d.ts +75 -0
  296. package/mcp/dist/tools/inspection.js +121 -0
  297. package/mcp/dist/tools/screenshot.d.ts +63 -0
  298. package/mcp/dist/tools/screenshot.js +263 -0
  299. package/mcp/dist/tools/source.d.ts +84 -0
  300. package/mcp/dist/tools/source.js +434 -0
  301. package/mcp/dist/tools/workspaces.d.ts +13 -0
  302. package/mcp/dist/tools/workspaces.js +33 -0
  303. package/mcp/dist/types.d.ts +18 -0
  304. package/mcp/dist/types.js +11 -0
  305. package/package.json +27 -7
  306. package/server/dist/api.d.ts +37 -0
  307. package/server/dist/api.js +44 -0
  308. package/server/dist/code-editor.d.ts +100 -0
  309. package/server/dist/code-editor.js +528 -2
  310. package/server/dist/fluidcad-server.d.ts +118 -1
  311. package/server/dist/fluidcad-server.js +350 -62
  312. package/server/dist/global-registry.d.ts +30 -0
  313. package/server/dist/global-registry.js +126 -0
  314. package/server/dist/host/blocked-imports.d.ts +8 -0
  315. package/server/dist/host/blocked-imports.js +30 -0
  316. package/server/dist/{vite-manager.d.ts → host/local-scene-host.d.ts} +3 -1
  317. package/server/dist/{vite-manager.js → host/local-scene-host.js} +6 -26
  318. package/server/dist/host/scene-host.d.ts +19 -0
  319. package/server/dist/host/scene-host.js +1 -0
  320. package/server/dist/index.js +175 -123
  321. package/server/dist/instance-file.d.ts +31 -0
  322. package/server/dist/instance-file.js +73 -0
  323. package/server/dist/lint-fluid-js.d.ts +15 -0
  324. package/server/dist/lint-fluid-js.js +271 -0
  325. package/server/dist/model-package/capture-params.d.ts +19 -0
  326. package/server/dist/model-package/capture-params.js +42 -0
  327. package/server/dist/model-package/pack.d.ts +23 -0
  328. package/server/dist/model-package/pack.js +229 -0
  329. package/server/dist/model-package/types.d.ts +78 -0
  330. package/server/dist/model-package/types.js +17 -0
  331. package/server/dist/routes/editor.d.ts +24 -0
  332. package/server/dist/routes/editor.js +44 -0
  333. package/server/dist/routes/export.d.ts +1 -1
  334. package/server/dist/routes/export.js +45 -8
  335. package/server/dist/routes/health.d.ts +7 -0
  336. package/server/dist/routes/health.js +14 -0
  337. package/server/dist/routes/hit-test.d.ts +3 -0
  338. package/server/dist/routes/hit-test.js +17 -0
  339. package/server/dist/routes/lint.d.ts +10 -0
  340. package/server/dist/routes/lint.js +28 -0
  341. package/server/dist/routes/pack.d.ts +10 -0
  342. package/server/dist/routes/pack.js +47 -0
  343. package/server/dist/routes/params.d.ts +3 -0
  344. package/server/dist/routes/params.js +75 -0
  345. package/server/dist/routes/render.d.ts +33 -0
  346. package/server/dist/routes/render.js +34 -0
  347. package/server/dist/routes/scene.d.ts +5 -0
  348. package/server/dist/routes/scene.js +48 -0
  349. package/server/dist/routes/screenshot.js +68 -1
  350. package/server/dist/routes/sketch-edits.d.ts +3 -0
  351. package/server/dist/routes/sketch-edits.js +542 -0
  352. package/server/dist/routes/timeline.d.ts +3 -0
  353. package/server/dist/routes/timeline.js +49 -0
  354. package/server/dist/server-core.d.ts +53 -0
  355. package/server/dist/server-core.js +147 -0
  356. package/server/dist/ws-protocol.d.ts +156 -3
  357. package/ui/dist/assets/index-CDJmUpFI.css +2 -0
  358. package/ui/dist/assets/index-MRqwG9Vh.js +5417 -0
  359. package/ui/dist/index.html +2 -2
  360. package/server/dist/routes/actions.d.ts +0 -3
  361. package/server/dist/routes/actions.js +0 -309
  362. package/ui/dist/assets/index-CFi9p7wR.js +0 -4946
  363. package/ui/dist/assets/index-DR7c2Qk9.css +0 -2
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { setupOC, render } from "../setup.js";
3
+ import { getSceneManager } from "../../scene-manager.js";
4
+ import { SceneCompare } from "../../rendering/scene-compare.js";
5
+ import sketch from "../../core/sketch.js";
6
+ import extrude from "../../core/extrude.js";
7
+ import axis from "../../core/axis.js";
8
+ import repeat from "../../core/repeat.js";
9
+ import sweep from "../../core/sweep.js";
10
+ import cut from "../../core/cut.js";
11
+ import remove from "../../core/remove.js";
12
+ import { rect, circle, vLine, tArc, tLine, slot, move, hMove } from "../../core/2d/index.js";
13
+ import copy from "../../core/copy.js";
14
+ // Regression: re-rendering a scene whose `repeat("circular", ...)` axis is an
15
+ // AxisFromEdge derived from a sketch line used to crash inside
16
+ // SceneCompare.compare with "Cannot read properties of undefined (reading
17
+ // 'origin')". RepeatCircular.compareTo eagerly called getAxis() on the
18
+ // new-scene axis source, which hadn't been built yet (compare runs pre-render).
19
+ describe("repeat circular cache-compare on unbuilt axis source", () => {
20
+ setupOC();
21
+ function buildScene(includeRemove) {
22
+ const spine = sketch("front", () => {
23
+ vLine(1.5);
24
+ tArc(-4, 45);
25
+ const topSegment = tLine(1.5);
26
+ return { topSegment };
27
+ }).reusable();
28
+ const profile = sketch("top", () => {
29
+ const innerPipe = circle(1.5);
30
+ const outerPipe = circle(2);
31
+ return { innerPipe, outerPipe };
32
+ });
33
+ const pipe = sweep(spine, profile.regions.outerPipe);
34
+ sketch("top", () => {
35
+ rect(3.5).centered().radius(0.5);
36
+ move([-2.5 / 2, -2.5 / 2]);
37
+ const c = circle(0.5);
38
+ copy("circular", [0, 0], { count: 4, angle: 360 }, c);
39
+ });
40
+ extrude(0.375);
41
+ sketch(pipe.endFaces(), () => {
42
+ circle(4);
43
+ });
44
+ const upperFlange = extrude(-0.625);
45
+ sweep(spine, profile.regions.innerPipe).remove();
46
+ const slots = sketch(upperFlange.endFaces(), () => {
47
+ hMove(3.25 / 2);
48
+ const outerSlot = slot(1, 0.75 / 2);
49
+ const innerSlot = slot(1, 0.45 / 2);
50
+ return { outerSlot, innerSlot };
51
+ });
52
+ const s1 = cut(slots.regions.innerSlot);
53
+ const s2 = cut(0.25, slots.regions.outerSlot);
54
+ const a = axis(spine.regions.topSegment);
55
+ repeat("circular", a, { count: 4, angle: 360 }, s1, s2);
56
+ if (includeRemove) {
57
+ remove(spine);
58
+ }
59
+ }
60
+ function currentScene() {
61
+ const mgr = getSceneManager();
62
+ if (!mgr) {
63
+ throw new Error("Scene manager not initialized");
64
+ }
65
+ return mgr.currentScene;
66
+ }
67
+ function startNewScene() {
68
+ const mgr = getSceneManager();
69
+ if (!mgr) {
70
+ throw new Error("Scene manager not initialized");
71
+ }
72
+ return mgr.startScene();
73
+ }
74
+ it("re-parsing the same script with cache compare does not throw", () => {
75
+ buildScene(false);
76
+ render();
77
+ const previousScene = currentScene();
78
+ expect(previousScene.getAllSceneObjects().some(o => o.getError())).toBe(false);
79
+ const newScene = startNewScene();
80
+ buildScene(false);
81
+ SceneCompare.compare(previousScene, newScene);
82
+ const rendered = render();
83
+ const errored = rendered.getAllSceneObjects().filter(o => o.getError());
84
+ expect(errored.map(o => `${o.getUniqueType()}: ${o.getError()}`)).toEqual([]);
85
+ });
86
+ it("appending remove(spine) after a cached render does not throw", () => {
87
+ buildScene(false);
88
+ render();
89
+ const previousScene = currentScene();
90
+ const newScene = startNewScene();
91
+ buildScene(true);
92
+ SceneCompare.compare(previousScene, newScene);
93
+ const rendered = render();
94
+ const errored = rendered.getAllSceneObjects().filter(o => o.getError());
95
+ expect(errored.map(o => `${o.getUniqueType()}: ${o.getError()}`)).toEqual([]);
96
+ });
97
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { setupOC, render } from "../setup.js";
3
+ import sketch from "../../core/sketch.js";
4
+ import extrude from "../../core/extrude.js";
5
+ import axis from "../../core/axis.js";
6
+ import repeat from "../../core/repeat.js";
7
+ import sweep from "../../core/sweep.js";
8
+ import cut from "../../core/cut.js";
9
+ import { rect, circle, vLine, tArc, tLine, slot, move, hMove } from "../../core/2d/index.js";
10
+ import copy from "../../core/copy.js";
11
+ import { countShapes } from "../utils.js";
12
+ // Regression: a circular repeat whose axis is an AxisFromEdge derived from
13
+ // a sketch line, whose targets are cuts on sketch-region slots. Exercises:
14
+ // - LazyMatrix.rotation with an unbuilt AxisObjectBase source
15
+ // - Extrude.getSourceDependencies pulling the containing Sketch into the
16
+ // clone set so Sketch.build's clone-mode path carries shapes for cloned
17
+ // geometry primitives (Slot here) that would otherwise lose their
18
+ // Sketch parent context after clone.
19
+ describe("repeat circular with axis-from-sketch-line and cut-of-slot targets", () => {
20
+ setupOC();
21
+ it("renders without errors and produces the expected cut instances", () => {
22
+ const spine = sketch("front", () => {
23
+ vLine(1.5);
24
+ tArc(-4, 45);
25
+ const topSegment = tLine(1.5);
26
+ return { topSegment };
27
+ }).reusable();
28
+ const profile = sketch("top", () => {
29
+ const innerPipe = circle(1.5);
30
+ const outerPipe = circle(2);
31
+ return { innerPipe, outerPipe };
32
+ });
33
+ const pipe = sweep(spine, profile.regions.outerPipe);
34
+ sketch("top", () => {
35
+ rect(3.5).centered().radius(0.5);
36
+ move([-2.5 / 2, -2.5 / 2]);
37
+ const c = circle(0.5);
38
+ copy("circular", [0, 0], { count: 4, angle: 360 }, c);
39
+ });
40
+ extrude(0.375);
41
+ sketch(pipe.endFaces(), () => {
42
+ circle(4);
43
+ });
44
+ const upperFlange = extrude(-0.625);
45
+ sweep(spine, profile.regions.innerPipe).remove();
46
+ const slots = sketch(upperFlange.endFaces(), () => {
47
+ hMove(3.25 / 2);
48
+ const outerSlot = slot(1, 0.75 / 2);
49
+ const innerSlot = slot(1, 0.45 / 2);
50
+ return { outerSlot, innerSlot };
51
+ });
52
+ const s1 = cut(slots.regions.innerSlot);
53
+ const s2 = cut(0.25, slots.regions.outerSlot);
54
+ const a = axis(spine.regions.topSegment);
55
+ repeat("circular", a, { count: 4, angle: 360 }, s1, s2);
56
+ const scene = render();
57
+ expect(countShapes(scene)).toBeGreaterThan(0);
58
+ expect(scene.getAllSceneObjects().some(o => o.getError())).toBe(false);
59
+ });
60
+ });
@@ -6,6 +6,7 @@ import shell from "../../core/shell.js";
6
6
  import select from "../../core/select.js";
7
7
  import cylinder from "../../core/cylinder.js";
8
8
  import { rect } from "../../core/2d/index.js";
9
+ import { Shell } from "../../features/shell.js";
9
10
  import { countShapes } from "../utils.js";
10
11
  import { ShapeProps } from "../../oc/props.js";
11
12
  import { face } from "../../filters/index.js";
@@ -198,6 +199,41 @@ describe("shell", () => {
198
199
  expect(first.getShapes()[0].isSame(allShapes[0])).toBe(true);
199
200
  });
200
201
  });
202
+ describe("join type", () => {
203
+ const buildShelledBox = (apply) => {
204
+ sketch("xy", () => {
205
+ rect(100, 100);
206
+ });
207
+ extrude(50);
208
+ select(face().onPlane("xy", 50));
209
+ const s = shell(5);
210
+ apply(s);
211
+ const scene = render();
212
+ return scene.getAllSceneObjects()
213
+ .flatMap(o => o.getShapes())
214
+ .find(sh => sh.getType() === "solid");
215
+ };
216
+ it("should produce a valid solid with default join type (arc)", () => {
217
+ const solid = buildShelledBox(() => { });
218
+ expect(solid).toBeDefined();
219
+ expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
220
+ });
221
+ it("should produce a valid solid with join('intersection')", () => {
222
+ const solid = buildShelledBox(s => s.join('intersection'));
223
+ expect(solid).toBeDefined();
224
+ expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
225
+ });
226
+ it("should produce a valid solid with join('tangent')", () => {
227
+ const solid = buildShelledBox(s => s.join('tangent'));
228
+ expect(solid).toBeDefined();
229
+ expect(ShapeProps.getProperties(solid.getShape()).volumeMm3).toBeGreaterThan(0);
230
+ });
231
+ it("should treat shells with different join types as not equal", () => {
232
+ const a = new Shell(5);
233
+ const b = new Shell(5).join('intersection');
234
+ expect(a.compareTo(b)).toBe(false);
235
+ });
236
+ });
201
237
  describe("shell with multiple selections", () => {
202
238
  it("should shell a box by removing two faces", () => {
203
239
  sketch("xy", () => {
@@ -1,5 +1,6 @@
1
1
  import { beforeAll } from "vitest";
2
2
  import { init } from "../index.js";
3
3
  beforeAll(async () => {
4
- await init("/tmp/fluidcad-test");
4
+ process.env.FLUIDCAD_WORKSPACE_PATH = "/tmp/fluidcad-test";
5
+ await init();
5
6
  });
@@ -0,0 +1,9 @@
1
+ export type FluidBlock = {
2
+ /** Path relative to the docs root, forward-slash. */
3
+ file: string;
4
+ /** 1-based line number where the block body starts (the line after the opening fence). */
5
+ line: number;
6
+ /** Block body — no fences. */
7
+ block: string;
8
+ };
9
+ export declare function extractFluidJsBlocks(docsRootRel: string): FluidBlock[];
@@ -0,0 +1,56 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ const FENCE_RE = /^(\s*)```([A-Za-z0-9_.-]*)\s*$/;
4
+ const RUNNABLE_LANGS = new Set(["fluid.js", "fluid", "fluidjs"]);
5
+ function listMarkdown(dir) {
6
+ const out = [];
7
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
8
+ if (entry.name.startsWith(".") || entry.name.startsWith("_")) {
9
+ continue;
10
+ }
11
+ const full = path.join(dir, entry.name);
12
+ if (entry.isDirectory()) {
13
+ out.push(...listMarkdown(full));
14
+ }
15
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
16
+ out.push(full);
17
+ }
18
+ }
19
+ return out;
20
+ }
21
+ function extractFromFile(file, docsRoot) {
22
+ const text = fs.readFileSync(file, "utf8");
23
+ const lines = text.split("\n");
24
+ const rel = path.relative(docsRoot, file).split(path.sep).join("/");
25
+ const blocks = [];
26
+ let i = 0;
27
+ while (i < lines.length) {
28
+ const openMatch = lines[i].match(FENCE_RE);
29
+ if (!openMatch || !RUNNABLE_LANGS.has(openMatch[2])) {
30
+ i++;
31
+ continue;
32
+ }
33
+ const startLine = i + 2;
34
+ const body = [];
35
+ i++;
36
+ while (i < lines.length) {
37
+ const closeMatch = lines[i].match(FENCE_RE);
38
+ if (closeMatch && closeMatch[2] === "") {
39
+ break;
40
+ }
41
+ body.push(lines[i]);
42
+ i++;
43
+ }
44
+ blocks.push({ file: rel, line: startLine, block: body.join("\n") });
45
+ i++;
46
+ }
47
+ return blocks;
48
+ }
49
+ export function extractFluidJsBlocks(docsRootRel) {
50
+ const docsRoot = path.resolve(docsRootRel);
51
+ const blocks = [];
52
+ for (const file of listMarkdown(docsRoot).sort()) {
53
+ blocks.push(...extractFromFile(file, docsRoot));
54
+ }
55
+ return blocks;
56
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ // Runs every fluid.js fenced block in llm-docs/**/*.md against the real
2
+ // FluidCAD runtime and asserts the scene compiles and produces ≥1 rendered
3
+ // object. Catches three classes of doc rot:
4
+ //
5
+ // 1. Syntax errors in examples (renamed args, dropped commas, etc.).
6
+ // 2. API drift — an example calls a method that no longer exists.
7
+ // 3. Geometry-empty examples — the snippet runs but yields nothing visible,
8
+ // which usually means the user-facing intent broke.
9
+ //
10
+ // The runner reuses the global OCC init from global-setup.ts and the
11
+ // startScene/render plumbing from setup.ts, so it sits on the same OCC
12
+ // initialisation the rest of the test suite uses.
13
+ import { describe, it, expect } from "vitest";
14
+ import { setupOC, render } from "./setup.js";
15
+ import { extractFluidJsBlocks } from "./helpers/extract-blocks.js";
16
+ import * as core from "../core/index.js";
17
+ import * as filters from "../filters/index.js";
18
+ import * as constraints from "../features/2d/constraints/geometry-qualifier.js";
19
+ import * as math from "../math/index.js";
20
+ import { countShapes } from "./utils.js";
21
+ function buildGlobals() {
22
+ return {
23
+ ...core,
24
+ ...filters,
25
+ ...constraints,
26
+ ...math,
27
+ };
28
+ }
29
+ // Doc examples often show the `import { foo } from "fluidcad/..."` line a
30
+ // real user would write at the top of a .fluid.js file. The runner injects
31
+ // every public export as a Function() parameter, so those imports are dead
32
+ // weight here — and `new Function` can't parse them anyway. Strip them out
33
+ // before executing.
34
+ const IMPORT_LINE_RE = /^\s*import\s[\s\S]*?from\s+['"][^'"]+['"]\s*;?\s*$/gm;
35
+ function stripImports(block) {
36
+ return block.replace(IMPORT_LINE_RE, "");
37
+ }
38
+ function runBlock(block, file, line) {
39
+ const globals = buildGlobals();
40
+ const paramNames = Object.keys(globals);
41
+ const paramValues = paramNames.map((n) => globals[n]);
42
+ const wrapped = `"use strict";\n${stripImports(block)}\n//# sourceURL=llm-docs/${file}:${line}`;
43
+ const fn = new Function(...paramNames, wrapped);
44
+ fn(...paramValues);
45
+ }
46
+ const blocks = extractFluidJsBlocks("llm-docs");
47
+ describe("llm-docs fluid.js examples", () => {
48
+ setupOC();
49
+ if (blocks.length === 0) {
50
+ it("found at least one fluid.js block", () => {
51
+ expect(blocks.length).toBeGreaterThan(0);
52
+ });
53
+ return;
54
+ }
55
+ for (const { file, line, block } of blocks) {
56
+ it(`${file}:${line} compiles and produces ≥1 rendered object`, () => {
57
+ runBlock(block, file, line);
58
+ const scene = render();
59
+ expect(countShapes(scene)).toBeGreaterThan(0);
60
+ });
61
+ }
62
+ });
@@ -1,7 +1,8 @@
1
1
  import { beforeEach } from "vitest";
2
2
  import { getSceneManager, getCurrentScene } from "../scene-manager.js";
3
3
  import { SceneRenderer } from "../rendering/render.js";
4
- const renderer = new SceneRenderer();
4
+ import { DEFAULT_MESH_CONFIG } from "../oc/mesh.js";
5
+ const renderer = new SceneRenderer(DEFAULT_MESH_CONFIG);
5
6
  export function setupOC() {
6
7
  beforeEach(() => {
7
8
  getSceneManager().startScene();