forgecad 0.9.6 → 0.9.7

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 (76) hide show
  1. package/dist/assets/{AdminPage-Da6hhpJx.js → AdminPage-DX0mpSZT.js} +1 -1
  2. package/dist/assets/{BlogPage-Bl_sKeWb.js → BlogPage-CI_P0_Pf.js} +1 -1
  3. package/dist/assets/{DocsPage-Blz3Tp4j.js → DocsPage-DLhIIZyJ.js} +3 -3
  4. package/dist/assets/{EditorApp-CuiPbtn5.js → EditorApp-BujZvuwX.js} +140 -20
  5. package/dist/assets/{EditorApp-DS0AIUrZ.css → EditorApp-DfFT2Dn8.css} +1 -0
  6. package/dist/assets/{EmbedViewer-BFG6-Ufm.js → EmbedViewer-0S0qXKog.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-DB9fQd5P.js → LandingPageProofDriven-O_yMtAri.js} +1 -1
  8. package/dist/assets/{PricingPage-BMxYT_F0.js → PricingPage-DGkX3Ahr.js} +1 -1
  9. package/dist/assets/{SettingsPage-VVQNrCAg.js → SettingsPage-DBsqTB_y.js} +82 -22
  10. package/dist/assets/{app-Dl9ymBWC.js → app-BE2nD6Yz.js} +1056 -258
  11. package/dist/assets/cli/{render-CFtwKCCY.js → render-iP9qh475.js} +1533 -207
  12. package/dist/assets/{evalWorker-CRvbzTXm.js → evalWorker-Ds5U4xtN.js} +2178 -30
  13. package/dist/assets/inspectWorker-Dll4eVyD.js +12620 -0
  14. package/dist/assets/{manifold-DpBXFS2K.js → manifold-Bk26ViCr.js} +1 -1
  15. package/dist/assets/{manifold-DzZ4VRPs.js → manifold-DjYsd7A_.js} +2 -2
  16. package/dist/assets/{manifold-B9QSr-qP.js → manifold-sJ-axdXM.js} +1 -1
  17. package/dist/assets/{renderSceneState-BuAXF2jh.js → renderSceneState-Bngp5MrQ.js} +1 -1
  18. package/dist/assets/{reportWorker-BNWEnRg1.js → reportWorker-CU8RZ4O0.js} +2161 -30
  19. package/dist/assets/{distance-BEC2RjJi.js → sectionPlaneMath-BdTjyVfs.js} +2539 -1187
  20. package/dist/cli/render.html +1 -1
  21. package/dist/docs/index.html +1 -1
  22. package/dist/docs-raw/AI/usage.md +7 -2
  23. package/dist/docs-raw/CLI.md +82 -53
  24. package/dist/docs-raw/beta-operations.md +5 -0
  25. package/dist/docs-raw/coding.md +1 -1
  26. package/dist/docs-raw/generated/concepts.md +59 -2
  27. package/dist/docs-raw/generated/core.md +206 -1
  28. package/dist/docs-raw/generated/lib.md +17 -1
  29. package/dist/docs-raw/generated/viewport.md +1 -1
  30. package/dist/docs-raw/guides/inspection-bundles.md +36 -13
  31. package/dist/docs-raw/platform/auth.md +2 -0
  32. package/dist/docs-raw/platform/google-oauth-setup.md +4 -0
  33. package/dist/docs-raw/skills/forgecad-make-a-model.md +87 -8
  34. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +14 -6
  35. package/dist/docs-raw/skills/forgecad-render-inspect.md +1 -1
  36. package/dist/docs-raw/skills/index.md +2 -2
  37. package/dist/index.html +1 -1
  38. package/dist/sitemap.xml +6 -6
  39. package/dist-cli/forgecad.js +7975 -4528
  40. package/dist-cli/forgecad.js.map +1 -1
  41. package/dist-skill/CONTEXT.md +260 -16
  42. package/dist-skill/docs/CLI.md +82 -53
  43. package/dist-skill/docs/generated/core.md +206 -1
  44. package/dist-skill/docs/generated/lib.md +17 -1
  45. package/dist-skill/docs/generated/viewport.md +1 -1
  46. package/dist-skill/docs/guides/inspection-bundles.md +36 -13
  47. package/dist-skill/docs-dev/CLI.md +82 -53
  48. package/dist-skill/docs-dev/coding.md +1 -1
  49. package/dist-skill/docs-dev/generated/core.md +206 -1
  50. package/dist-skill/docs-dev/generated/lib.md +17 -1
  51. package/dist-skill/docs-dev/generated/viewport.md +1 -1
  52. package/dist-skill/docs-dev/guides/inspection-bundles.md +36 -13
  53. package/dist-skill/library/forgecad-make-a-model/SKILL.md +87 -8
  54. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +14 -6
  55. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +5 -3
  56. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +7 -5
  57. package/dist-skill/library/forgecad-render-inspect/SKILL.md +1 -1
  58. package/examples/api/bolted-service-cover.forge.js +17 -0
  59. package/examples/api/cable-gland-anchor.forge.js +14 -0
  60. package/examples/api/captured-cartridge-guide.forge.js +14 -0
  61. package/examples/api/captured-linear-slide.forge.js +13 -0
  62. package/examples/api/clevis-pin-joint.forge.js +13 -0
  63. package/examples/api/datum-enclosure.forge.js +16 -0
  64. package/examples/api/hose-barb-port.forge.js +14 -0
  65. package/examples/api/intentional-overlap-overmold.forge.js +16 -0
  66. package/examples/api/knuckled-hinge-assembly.forge.js +15 -0
  67. package/examples/api/living-hinge-cover.forge.js +14 -0
  68. package/examples/api/pcb-terminal-block.forge.js +22 -0
  69. package/examples/api/pinned-lever-pivot-stack.forge.js +14 -0
  70. package/examples/api/retained-shaft-knob-stack.forge.js +15 -0
  71. package/examples/api/routed-tube-clip.forge.js +15 -0
  72. package/examples/api/seated-bearing-stack.forge.js +30 -0
  73. package/examples/api/snap-latch-cover.forge.js +14 -0
  74. package/examples/api/static-assembly-connectors.forge.js +14 -16
  75. package/examples/api/thumb-screw-clamp.forge.js +15 -0
  76. package/package.json +1 -1
@@ -0,0 +1,13 @@
1
+ const clevis = lib.clevisPinJointAssembly({
2
+ pinDiameter: 4,
3
+ linkThickness: 6,
4
+ linkArmLength: 38,
5
+ earThickness: 4,
6
+ });
7
+
8
+ verify.equal('clevis joint has three retained parts', clevis.parts.length, 3);
9
+ verify.greaterThan('link eye has material around pin bore', clevis.dims.eyeOuterRadius - clevis.dims.boreDiameter / 2, 4);
10
+ verify.greaterThan('clevis gap clears the link eye', clevis.dims.clevisGap, clevis.dims.linkThickness);
11
+ verify.minClearance('link eye clears clevis yoke', clevis.link, clevis.clevis, 0.05);
12
+
13
+ return clevis.parts;
@@ -0,0 +1,16 @@
1
+ const enclosure = lib.datumEnclosureAssembly({
2
+ width: 96,
3
+ depth: 64,
4
+ height: 18,
5
+ screwSize: 'M3',
6
+ });
7
+
8
+ verify.equal('datum enclosure has base gasket cover and four screws', enclosure.parts.length, 7);
9
+ verify.greaterThan('internal cavity keeps usable width', enclosure.dims.innerWidth, 80);
10
+ verify.greaterThan('standoff wall remains around screw envelope', enclosure.dims.standoffDiameter - enclosure.dims.threadEnvelopeDia, 4);
11
+ verify.greaterThan('service port leaves top wall material', enclosure.dims.height - enclosure.dims.baseThickness - enclosure.dims.portHeight, 6);
12
+ verify.notColliding('cover clears seated gasket', enclosure.cover, enclosure.gasket);
13
+ verify.notColliding('first screw clears standoff thread envelope', enclosure.screws[0], enclosure.base);
14
+ verify.inRange('cover stack has small seating clearance', enclosure.dims.faceClearance, 0.01, 0.08);
15
+
16
+ return enclosure.parts;
@@ -0,0 +1,14 @@
1
+ const hosePort = lib.hoseBarbPortAssembly({
2
+ hoseInnerDiameter: 6,
3
+ hoseOuterDiameter: 10,
4
+ barbCount: 3,
5
+ });
6
+
7
+ verify.equal('hose barb port has receiver fitting hose and clamp', hosePort.parts.length, 4);
8
+ verify.greaterThan('barb peaks retain nominal hose ID', hosePort.dims.barbPeakDiameter, hosePort.dims.hoseInnerDiameter);
9
+ verify.greaterThan('hose has wall around installed barb envelope', hosePort.dims.hoseOuterDiameter - hosePort.dims.installedHoseBoreDiameter, 1.2);
10
+ verify.notColliding('hose clears barb peaks', hosePort.hose, hosePort.fitting);
11
+ verify.notColliding('clamp clears hose outside', hosePort.clamp, hosePort.hose);
12
+ verify.inRange('fitting shoulder seats near boss face', hosePort.dims.faceClearance, 0.01, 0.08);
13
+
14
+ return hosePort.parts;
@@ -0,0 +1,16 @@
1
+ // Intentional overlap: an overmolded grip shares volume with its rigid handle core.
2
+ const handleCore = box(64, 12, 8).color('#64748b');
3
+ const rubberGrip = box(42, 18, 10).translate(6, 0, 0).color('#111827');
4
+
5
+ verify.intentionalOverlap(
6
+ 'rubber grip is overmolded on handle core',
7
+ rubberGrip,
8
+ handleCore,
9
+ 'overmolded bonded grip around rigid insert',
10
+ );
11
+ verify.greaterThan('rubber grip wraps beyond core width', rubberGrip.boundingBox().max[1], handleCore.boundingBox().max[1]);
12
+
13
+ return [
14
+ { name: 'Rigid handle core', shape: handleCore },
15
+ { name: 'Overmolded rubber grip', shape: rubberGrip },
16
+ ];
@@ -0,0 +1,15 @@
1
+ const hinge = lib.knuckledHingeAssembly({
2
+ length: 70,
3
+ leafLength: 28,
4
+ leafThickness: 1.8,
5
+ barrelOuterRadius: 3.2,
6
+ pinDiameter: 2,
7
+ openAngleDeg: 42,
8
+ });
9
+
10
+ verify.equal('hinge has two leaves and one retained pin', hinge.parts.length, 3);
11
+ verify.greaterThan('barrel wall remains around pin bore', hinge.dims.barrelOuterRadius - hinge.dims.boreDiameter / 2, 1.5);
12
+ verify.greaterThan('hinge has meaningful knuckle length', hinge.dims.knuckleLength, hinge.dims.pinDiameter * 2);
13
+ verify.minClearance('moving leaf clears fixed leaf', hinge.movingLeaf, hinge.fixedLeaf, 0.05);
14
+
15
+ return hinge.parts;
@@ -0,0 +1,14 @@
1
+ const livingCover = lib.livingHingeCoverAssembly({
2
+ width: 64,
3
+ coverDepth: 42,
4
+ fixedLeafDepth: 18,
5
+ });
6
+
7
+ verify.equal('living hinge cover is one molded part', livingCover.parts.length, 1);
8
+ verify.equal('one-piece living hinge cover has one body', livingCover.cover.numBodies(), 1);
9
+ verify.greaterThan('living hinge web is much thinner than rigid leaves', livingCover.dims.flexRatio, 3.5);
10
+ verify.greaterThan('hinge web spans the full cover width', livingCover.dims.width, 60);
11
+ verify.greaterThan('snap barb has positive retention height', livingCover.dims.snapBarbHeight, 1);
12
+ verify.greaterThan('catch land is present on fixed leaf', livingCover.dims.catchLandDepth, 2);
13
+
14
+ return livingCover.parts;
@@ -0,0 +1,22 @@
1
+ const terminalStack = lib.pcbTerminalBlockAssembly({
2
+ terminalCount: 5,
3
+ terminalPitch: 5.08,
4
+ screwSize: 'M3',
5
+ });
6
+
7
+ verify.equal('terminal block exposes one pin per terminal', terminalStack.pinPositions.length, terminalStack.dims.terminalCount);
8
+ verify.greaterThan(
9
+ 'PCB leaves mounting margin around terminal block',
10
+ terminalStack.dims.boardWidth - terminalStack.dims.terminalBlockWidth,
11
+ 20,
12
+ );
13
+ verify.notColliding('terminal pins clear PCB holes', terminalStack.terminalBlock, terminalStack.pcb);
14
+ verify.notColliding('mounting screws clear PCB and standoff holes', union(...terminalStack.screws), union(terminalStack.pcb, terminalStack.backplate));
15
+ verify.greaterThan(
16
+ 'standoffs retain wall around screw envelopes',
17
+ terminalStack.dims.standoffDiameter - terminalStack.dims.standoffThreadEnvelopeDiameter,
18
+ 3,
19
+ );
20
+ verify.greaterThan('pin holes leave PCB web between terminals', terminalStack.dims.terminalPitch - terminalStack.dims.pinHoleDiameter, 3);
21
+
22
+ return terminalStack.parts;
@@ -0,0 +1,14 @@
1
+ const lever = lib.pinnedLeverAssembly({
2
+ armLength: 56,
3
+ armWidth: 10,
4
+ leverThickness: 5,
5
+ pinDiameter: 5,
6
+ supportThickness: 8,
7
+ });
8
+
9
+ verify.equal('pinned lever stack has five retained parts', lever.parts.length, 5);
10
+ verify.greaterThan('thrust washers have real thickness', lever.dims.washerThickness, 0);
11
+ verify.greaterThan('pivot hub has material around the bore', lever.dims.hubRadius - lever.dims.boreDiameter / 2, 3);
12
+ verify.notColliding('lower washer does not collide with support', lever.washers.lower, lever.support);
13
+
14
+ return lever.parts;
@@ -0,0 +1,15 @@
1
+ const trunnion = lib.retainedShaftAssembly({
2
+ supportSpacing: 96,
3
+ shaftDiameter: 8,
4
+ supportThickness: 6,
5
+ supportHeight: 42,
6
+ knobDiameter: 24,
7
+ knobThickness: 8,
8
+ });
9
+
10
+ verify.equal('retained shaft stack has seven parts', trunnion.parts.length, 7);
11
+ verify.greaterThan('support cheeks leave material around bore', trunnion.dims.supportHeight - trunnion.dims.boreDiameter, 20);
12
+ verify.greaterThan('shaft is long enough to retain knobs', trunnion.dims.shaftLength, trunnion.dims.supportSpacing);
13
+ verify.notColliding('left washer does not collide with support cheek', trunnion.washers.left, trunnion.supports.left);
14
+
15
+ return trunnion.parts;
@@ -0,0 +1,15 @@
1
+ const route = lib.routedTubeClipAssembly({
2
+ tubeDiameter: 6,
3
+ tubeLength: 130,
4
+ clipCount: 3,
5
+ screwSize: 'M3',
6
+ });
7
+
8
+ verify.equal('tube route has three retaining clips', route.clips.length, 3);
9
+ verify.equal('each retaining clip has two installed screws', route.screws.length, route.clips.length * 2);
10
+ verify.notColliding('tube clears saddle clip bores', route.tube, union(...route.clips));
11
+ verify.notColliding('clip screws clear panel and clip holes', union(...route.screws), union(route.panel, ...route.clips));
12
+ verify.greaterThan('clip wall leaves material around tube bore', route.dims.clipWallThickness, route.dims.screwHeadDiameter);
13
+ verify.greaterThan('tube bore clears tube outside diameter', route.dims.tubeBoreDiameter - route.dims.tubeDiameter, 0.3);
14
+
15
+ return route.parts;
@@ -0,0 +1,30 @@
1
+ const bearingStack = lib.seatedBearingAssembly({
2
+ bearingOuterDiameter: 22,
3
+ bearingInnerDiameter: 8,
4
+ bearingWidth: 7,
5
+ shaftDiameter: 7.4,
6
+ housingWidth: 54,
7
+ housingDepth: 42,
8
+ housingThickness: 10,
9
+ });
10
+
11
+ verify.equal('seated bearing stack has three retained parts', bearingStack.parts.length, 3);
12
+ verify.greaterThan(
13
+ 'housing boss leaves material around bearing pocket',
14
+ bearingStack.dims.bossOuterDiameter - bearingStack.dims.pocketDiameter,
15
+ 5,
16
+ );
17
+ verify.greaterThan(
18
+ 'bearing bore clears the shaft',
19
+ bearingStack.dims.bearingInnerDiameter,
20
+ bearingStack.dims.shaftDiameter + bearingStack.dims.runningClearance,
21
+ );
22
+ verify.clearanceBetween(
23
+ 'bearing is seated in counterbore',
24
+ bearingStack.bearing,
25
+ bearingStack.housing,
26
+ -0.01,
27
+ bearingStack.dims.runningClearance + 0.1,
28
+ );
29
+
30
+ return bearingStack.parts;
@@ -0,0 +1,14 @@
1
+ const snapCover = lib.snapLatchCoverAssembly({
2
+ width: 72,
3
+ depth: 44,
4
+ ledgeWidth: 8,
5
+ parentThickness: 6,
6
+ });
7
+
8
+ verify.equal('snap latch cover has receiver and cover parts', snapCover.parts.length, 2);
9
+ verify.greaterThan('snap hooks tuck under catch land', snapCover.dims.hookThrow, snapCover.dims.latchThickness);
10
+ verify.greaterThan('receiver leaves a usable service opening', snapCover.dims.openingWidth, 48);
11
+ verify.notColliding('snap hooks clear receiver windows', snapCover.cover, snapCover.parent);
12
+ verify.inRange('snap cover has small seating clearance', snapCover.dims.faceClearance, 0.01, 0.08);
13
+
14
+ return snapCover.parts;
@@ -14,7 +14,7 @@ const railThickness = 12;
14
14
  const legSize = 14;
15
15
  const slatThickness = 10;
16
16
  const slatWidth = 18;
17
- const railY = benchWidth / 2 - railThickness / 2;
17
+ const railCenterSpan = benchWidth - railThickness;
18
18
  const legInset = 26;
19
19
  const supportXs = Array.from({ length: slatCount }, (_, i) =>
20
20
  slatCount === 1
@@ -22,15 +22,14 @@ const supportXs = Array.from({ length: slatCount }, (_, i) =>
22
22
  : -benchLength / 2 + legInset + ((benchLength - legInset * 2) * i) / (slatCount - 1)
23
23
  );
24
24
 
25
- function makeRail(sign) {
25
+ function makeRail() {
26
26
  return box(benchLength, railThickness, railHeight)
27
- .translate(0, sign * railY, 0)
28
27
  .withConnectors(
29
28
  Object.fromEntries(
30
29
  supportXs.map((x, i) => [
31
30
  `leg_${i}`,
32
31
  connector.female("leg-seat", {
33
- origin: [x, sign * railY, 0],
32
+ origin: [x, 0, 0],
34
33
  axis: [0, 0, -1],
35
34
  up: [1, 0, 0],
36
35
  }),
@@ -42,7 +41,7 @@ function makeRail(sign) {
42
41
  supportXs.map((x, i) => [
43
42
  `slat_${i}`,
44
43
  connector.female("slat-seat", {
45
- origin: [x, sign * railY, railHeight],
44
+ origin: [x, 0, railHeight],
46
45
  axis: [0, 0, 1],
47
46
  up: [1, 0, 0],
48
47
  }),
@@ -64,30 +63,29 @@ function makeLeg() {
64
63
  function makeSlat() {
65
64
  return box(slatWidth, benchWidth, slatThickness).withConnectors({
66
65
  left_seat: connector.male("slat-seat", {
67
- origin: [0, -railY, 0],
66
+ origin: [0, -railCenterSpan / 2, 0],
68
67
  axis: [0, 0, -1],
69
68
  up: [1, 0, 0],
70
69
  }),
71
70
  right_seat: connector.male("slat-seat", {
72
- origin: [0, railY, 0],
71
+ origin: [0, railCenterSpan / 2, 0],
73
72
  axis: [0, 0, -1],
74
73
  up: [1, 0, 0],
75
74
  }),
76
75
  });
77
76
  }
78
77
 
79
- const leftRail = makeRail(-1).color("#8b5a2b");
80
- const rightRail = makeRail(1).color("#8b5a2b");
81
-
78
+ const leftRail = makeRail().color("#8b5a2b");
82
79
  const leftLegs = supportXs.map((_, i) =>
83
80
  makeLeg().matchTo(leftRail, "head", `leg_${i}`).color("#b57f45")
84
81
  );
85
- const rightLegs = supportXs.map((_, i) =>
86
- makeLeg().matchTo(rightRail, "head", `leg_${i}`).color("#b57f45")
87
- );
88
82
  const slats = supportXs.map((_, i) =>
89
83
  makeSlat().matchTo(leftRail, "left_seat", `slat_${i}`).color(i % 2 === 0 ? "#d9b07a" : "#c99b63")
90
84
  );
85
+ const rightRail = makeRail().matchTo(slats[0], "slat_0", "right_seat").color("#8b5a2b");
86
+ const rightLegs = supportXs.map((_, i) =>
87
+ makeLeg().matchTo(rightRail, "head", `leg_${i}`).color("#b57f45")
88
+ );
91
89
 
92
90
  const bench = group(
93
91
  { name: "LeftRail", shape: leftRail },
@@ -97,9 +95,9 @@ const bench = group(
97
95
  ...slats.map((shape, i) => ({ name: `Slat${i}`, shape })),
98
96
  );
99
97
 
100
- verify.equal("First left leg is seated", bench.connectorDistance("LeftRail.leg_0", "LeftLeg0.head"), 0, 0.01);
101
- verify.equal("First slat is seated on left rail", bench.connectorDistance("LeftRail.slat_0", "Slat0.left_seat"), 0, 0.01);
102
- verify.equal("First slat also reaches the right rail", bench.connectorDistance("RightRail.slat_0", "Slat0.right_seat"), 0, 0.01);
98
+ verify.connectorDistance("First left leg is seated", bench, "LeftRail.leg_0", "LeftLeg0.head", 0, 0.01);
99
+ verify.connectorDistance("First slat is seated on left rail", bench, "LeftRail.slat_0", "Slat0.left_seat", 0, 0.01);
100
+ verify.connectorDistance("First slat also reaches the right rail", bench, "RightRail.slat_0", "Slat0.right_seat", 0, 0.01);
103
101
 
104
102
  scene({
105
103
  background: { top: "#dce7f5", bottom: "#f7fbff" },
@@ -0,0 +1,15 @@
1
+ // Thumb-screw clamp with a real threaded boss, captive pressure pad, and clamped workpiece.
2
+ const clamp = lib.thumbScrewClampAssembly({
3
+ screwSize: 'M6',
4
+ workpieceThickness: 20,
5
+ workpieceDepth: 50,
6
+ });
7
+
8
+ verify.equal('thumb screw clamp has frame workpiece and screw', clamp.parts.length, 3);
9
+ verify.notColliding('thumb screw clears threaded boss bore', clamp.clampScrew, clamp.frame);
10
+ verify.clearanceBetween('pressure pad is seated on workpiece', clamp.clampScrew, clamp.workpiece, -0.01, 0.05);
11
+ verify.clearanceBetween('fixed anvil is seated on workpiece', clamp.frame, clamp.workpiece, -0.01, 0.05);
12
+ verify.greaterThan('threaded boss leaves wall around screw envelope', clamp.dims.bossDiameter - clamp.dims.threadEnvelopeDiameter, 4);
13
+ verify.greaterThan('frame depth captures the workpiece and pads', clamp.dims.frameDepth, clamp.dims.workpieceDepth);
14
+
15
+ return clamp.parts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecad",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "description": "Code-first parametric CAD for JavaScript/TypeScript, in the browser and CLI.",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",