forgecad 0.9.4 → 0.9.5

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 (82) hide show
  1. package/dist/assets/{AdminPage-jwoEgwE_.js → AdminPage-uTtcSXtn.js} +1 -1
  2. package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-DYJMjWx3.js} +1 -1
  3. package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-C58f0K5v.js} +1 -6
  4. package/dist/assets/{EditorApp-Dja2jMmW.js → EditorApp-DNH1TEz1.js} +282 -62
  5. package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-CMXWA2LX.js} +2 -2
  6. package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-CAu2OZFn.js} +1 -1
  7. package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BIgW7m3X.js} +1 -1
  8. package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-N1l1tMXO.js} +1 -1
  9. package/dist/assets/{app-CwI02pTA.js → app-CFy7g5WP.js} +74 -12
  10. package/dist/assets/cli/{render-Kw5hLEcL.js → render-BrVVdj_T.js} +453 -41
  11. package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-c_SB9gg3.js} +2057 -446
  12. package/dist/assets/{manifold-lru0jwVw.js → manifold-CRoBhJKH.js} +2 -2
  13. package/dist/assets/{manifold-CwDdMKyc.js → manifold-Cjk7WhRs.js} +1 -1
  14. package/dist/assets/{manifold-DTvmxSDf.js → manifold-Dp6pvFr6.js} +1 -1
  15. package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-3DfsSASX.js} +1 -1
  16. package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BLkuIoS8.js} +2052 -443
  17. package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → sectionPlaneMath-CykEnkvQ.js} +2258 -518
  18. package/dist/cli/render.html +1 -1
  19. package/dist/docs/index.html +2 -2
  20. package/dist/docs-raw/AI/usage.md +0 -1
  21. package/dist/docs-raw/API/core/concepts.md +11 -1
  22. package/dist/docs-raw/CLI.md +64 -13
  23. package/dist/docs-raw/generated/assembly.md +8 -3
  24. package/dist/docs-raw/generated/concepts.md +44 -41
  25. package/dist/docs-raw/generated/core.md +97 -47
  26. package/dist/docs-raw/generated/curves.md +6 -580
  27. package/dist/docs-raw/generated/lib.md +40 -3
  28. package/dist/docs-raw/generated/output.md +6 -1
  29. package/dist/docs-raw/generated/sdf.md +50 -4
  30. package/dist/docs-raw/generated/viewport.md +1 -9
  31. package/dist/docs-raw/guides/inspection-bundles.md +31 -6
  32. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
  33. package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
  34. package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
  35. package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
  36. package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
  37. package/dist/docs-raw/skills/forgecad.md +2 -1
  38. package/dist/docs-raw/skills/index.md +0 -1
  39. package/dist/index.html +1 -1
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/blender/render.py +43 -8
  42. package/dist-cli/forgecad.js +4941 -1758
  43. package/dist-cli/forgecad.js.map +1 -1
  44. package/dist-skill/CONTEXT.md +255 -656
  45. package/dist-skill/SKILL-dev.md +2 -1
  46. package/dist-skill/SKILL.md +2 -1
  47. package/dist-skill/docs/API/core/concepts.md +11 -1
  48. package/dist-skill/docs/CLI.md +64 -13
  49. package/dist-skill/docs/generated/assembly.md +8 -3
  50. package/dist-skill/docs/generated/core.md +97 -47
  51. package/dist-skill/docs/generated/curves.md +6 -580
  52. package/dist-skill/docs/generated/lib.md +40 -3
  53. package/dist-skill/docs/generated/output.md +6 -1
  54. package/dist-skill/docs/generated/sdf.md +50 -4
  55. package/dist-skill/docs/generated/viewport.md +1 -9
  56. package/dist-skill/docs/guides/inspection-bundles.md +31 -6
  57. package/dist-skill/docs-dev/API/core/concepts.md +11 -1
  58. package/dist-skill/docs-dev/CLI.md +64 -13
  59. package/dist-skill/docs-dev/generated/assembly.md +8 -3
  60. package/dist-skill/docs-dev/generated/core.md +97 -47
  61. package/dist-skill/docs-dev/generated/curves.md +6 -580
  62. package/dist-skill/docs-dev/generated/lib.md +40 -3
  63. package/dist-skill/docs-dev/generated/output.md +6 -1
  64. package/dist-skill/docs-dev/generated/sdf.md +50 -4
  65. package/dist-skill/docs-dev/generated/viewport.md +1 -9
  66. package/dist-skill/docs-dev/guides/inspection-bundles.md +31 -6
  67. package/dist-skill/library/README.md +0 -1
  68. package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
  69. package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
  70. package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
  71. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  72. package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
  73. package/examples/api/drive-wheel-regions.forge.js +43 -0
  74. package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
  75. package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
  76. package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
  77. package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
  78. package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
  79. package/examples/api/sector-gear-body.forge.js +34 -0
  80. package/package.json +1 -1
  81. package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
  82. package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
@@ -0,0 +1,72 @@
1
+ // Native Pattern2D relief for a tactile grip: diamond tread, metal collars, no JS callbacks.
2
+
3
+ scene({
4
+ background: { top: '#eef4f7', bottom: '#ccd8df' },
5
+ camera: { position: [82, -118, 54], target: [0, 0, 0], fov: 32 },
6
+ environment: { preset: 'studio', intensity: 1.35 },
7
+ lights: [
8
+ { type: 'ambient', color: '#ffffff', intensity: 0.24 },
9
+ { type: 'point', position: [56, -78, 78], color: '#ffffff', intensity: 2.1, distance: 260, decay: 1.12 },
10
+ { type: 'point', position: [-62, 48, 46], color: '#73d8ff', intensity: 1.25, distance: 230, decay: 1.2 },
11
+ { type: 'directional', position: [36, -52, 130], color: '#fff0cf', intensity: 0.62 },
12
+ ],
13
+ postProcessing: {
14
+ toneMappingExposure: 1.08,
15
+ bloom: { intensity: 0.1, threshold: 0.82, radius: 0.36 },
16
+ vignette: { darkness: 0.13, offset: 0.58 },
17
+ },
18
+ });
19
+
20
+ const p = sdf.pattern2d();
21
+
22
+ const diamondTread = p
23
+ .stripes({ direction: [1, 1], spacing: 5.2, width: 1.05, depth: 0.42 })
24
+ .max(p.stripes({ direction: [1, -1], spacing: 5.2, width: 1.05, depth: 0.42 }))
25
+ .add(p.sineWave({ direction: [0, 1], wavelength: 19, amplitude: 0.06 }))
26
+ .clamp(-0.54, 0.04);
27
+
28
+ const collarGrooves = p
29
+ .stripes({ direction: [0, 1], spacing: 2.4, width: 0.45, depth: 0.09 })
30
+ .add(p.sineWave({ direction: [1, 0], wavelength: 8, amplitude: 0.018 }))
31
+ .clamp(-0.12, 0.02);
32
+
33
+ const rubber = (shape) =>
34
+ shape.color('#1f2227').material({
35
+ roughness: 0.86,
36
+ metalness: 0.02,
37
+ clearcoat: 0.08,
38
+ clearcoatRoughness: 0.72,
39
+ });
40
+
41
+ const satinMetal = (shape) =>
42
+ shape.color('#c3cbd4').material({
43
+ roughness: 0.34,
44
+ metalness: 0.78,
45
+ clearcoat: 0.42,
46
+ clearcoatRoughness: 0.18,
47
+ });
48
+
49
+ const grip = rubber(
50
+ sdf.cylinder(78, 13)
51
+ .shell(1.7)
52
+ .surfaceDisplace(diamondTread, { uv: 'cylinder' })
53
+ .subtract(sdf.cylinder(88, 9.0)),
54
+ );
55
+
56
+ const core = satinMetal(sdf.cylinder(86, 6.8));
57
+ const leftCollar = satinMetal(
58
+ sdf.torus(13.2, 1.45).surfaceDisplace(collarGrooves, { uv: 'torus' }).translate(0, 0, -39),
59
+ );
60
+ const rightCollar = satinMetal(
61
+ sdf.torus(13.2, 1.45).surfaceDisplace(collarGrooves, { uv: 'torus' }).translate(0, 0, 39),
62
+ );
63
+ const endLips = satinMetal(sdf.box(1.1, 1.1, 5.0).circularArray(44, 15.2));
64
+
65
+ return {
66
+ core,
67
+ grip,
68
+ leftCollar,
69
+ rightCollar,
70
+ leftEndKnurl: endLips.translate(0, 0, -41.5),
71
+ rightEndKnurl: endLips.translate(0, 0, 41.5),
72
+ };
@@ -0,0 +1,62 @@
1
+ // Native Pattern2D relief plus folded circular array accents for an ornamental SDF piece.
2
+
3
+ scene({
4
+ background: { top: '#101420', bottom: '#223242' },
5
+ camera: { position: [74, -104, 58], target: [0, 0, 4], fov: 31 },
6
+ environment: { preset: 'night', intensity: 1.18 },
7
+ lights: [
8
+ { type: 'ambient', color: '#172030', intensity: 0.18 },
9
+ { type: 'point', position: [48, -66, 72], color: '#ffd36f', intensity: 2.35, distance: 250, decay: 1.08 },
10
+ { type: 'point', position: [-52, 48, 58], color: '#58e5ff', intensity: 1.85, distance: 230, decay: 1.14 },
11
+ { type: 'point', position: [0, 36, 76], color: '#ff7ab8', intensity: 1.15, distance: 220, decay: 1.2 },
12
+ { type: 'directional', position: [28, -48, 138], color: '#ffffff', intensity: 0.58 },
13
+ ],
14
+ fog: { color: '#101420', near: 130, far: 330 },
15
+ postProcessing: {
16
+ toneMappingExposure: 1.32,
17
+ bloom: { intensity: 0.44, threshold: 0.56, radius: 0.56 },
18
+ vignette: { darkness: 0.42, offset: 0.44 },
19
+ },
20
+ });
21
+
22
+ const p = sdf.pattern2d();
23
+
24
+ const braidedGold = p
25
+ .overUnderWeave({ spacing: 1.6, threadWidth: 0.72, depth: 0.18 })
26
+ .add(p.sineWave({ direction: [1, 0], wavelength: 7.2, amplitude: 0.035 }))
27
+ .clamp(-0.23, 0.04);
28
+
29
+ const opalSkin = p
30
+ .sineWave({ direction: [1, 0], wavelength: 6, amplitude: 0.18 })
31
+ .add(p.sineWave({ direction: [0, 1], wavelength: 4.2, amplitude: 0.12 }))
32
+ .add(p.stripes({ direction: [1, 1], spacing: 5.5, width: 0.7, depth: 0.08 }))
33
+ .clamp(-0.2, 0.22);
34
+
35
+ const gold = (shape) =>
36
+ shape.color('#e8b95f').material({
37
+ roughness: 0.25,
38
+ metalness: 0.92,
39
+ clearcoat: 0.45,
40
+ clearcoatRoughness: 0.08,
41
+ reflectivity: 0.72,
42
+ });
43
+
44
+ const opal = (shape) =>
45
+ shape.color('#9be9dd').material({
46
+ roughness: 0.12,
47
+ metalness: 0,
48
+ clearcoat: 1,
49
+ clearcoatRoughness: 0.03,
50
+ opacity: 0.86,
51
+ transmission: 0.22,
52
+ reflectivity: 0.84,
53
+ ior: 1.42,
54
+ });
55
+
56
+ const braidedBand = gold(sdf.torus(24, 2.9).surfaceDisplace(braidedGold, { uv: 'torus' }));
57
+ const centerStone = opal(sdf.sphere(8.8).surfaceDisplace(opalSkin).translate(0, 0, 0.8));
58
+ const beadHalo = gold(sdf.sphere(1.8).circularArray(32, 31).translate(0, 0, 0.8));
59
+ const northStar = opal(sdf.sphere(3.3).surfaceDisplace(opalSkin).translate(0, 31, 1.1));
60
+ const southStar = opal(sdf.sphere(3.3).surfaceDisplace(opalSkin).translate(0, -31, 1.1));
61
+
62
+ return { braidedBand, centerStone, beadHalo, northStar, southStar };
@@ -0,0 +1,67 @@
1
+ // Native SDF surface relief — user-composed Pattern2D IR lowers to shader + CPU.
2
+ // These raw SDF leaves raymarch directly; call .toShape() only for export.
3
+
4
+ scene({
5
+ background: { top: '#f6fbff', bottom: '#dfe9f3' },
6
+ camera: { position: [92, -128, 72], target: [0, 0, 3], fov: 34 },
7
+ environment: { preset: 'studio', intensity: 1.2 },
8
+ lights: [
9
+ { type: 'ambient', color: '#ffffff', intensity: 0.22 },
10
+ { type: 'point', position: [56, -72, 86], color: '#fff0cf', intensity: 2.0, distance: 260, decay: 1.15 },
11
+ { type: 'point', position: [-70, 52, 64], color: '#79d9ff', intensity: 1.45, distance: 240, decay: 1.2 },
12
+ { type: 'directional', position: [36, -68, 150], color: '#ffffff', intensity: 0.7 },
13
+ ],
14
+ postProcessing: {
15
+ toneMappingExposure: 1.12,
16
+ bloom: { intensity: 0.18, threshold: 0.74, radius: 0.42 },
17
+ vignette: { darkness: 0.18, offset: 0.58 },
18
+ },
19
+ });
20
+
21
+ const textile = (shape, color) =>
22
+ shape.color(color).material({
23
+ roughness: 0.72,
24
+ metalness: 0,
25
+ clearcoat: 0.22,
26
+ clearcoatRoughness: 0.58,
27
+ });
28
+
29
+ const p = sdf.pattern2d();
30
+ const weave = p.overUnderWeave({ spacing: 2.8, threadWidth: 1.2, depth: 0.55 });
31
+ const diagonalRipple = p.sineWave({ direction: [1, 1], wavelength: 11, amplitude: 0.08 });
32
+ const bowlPattern = weave.add(diagonalRipple).clamp(-0.68, 0.08);
33
+
34
+ const bowl = textile(
35
+ sdf.sphere(24)
36
+ .shell(1.8)
37
+ .surfaceDisplace(bowlPattern)
38
+ .subtract(sdf.box(70, 70, 36).translate(0, 0, 20))
39
+ .translate(-46, 0, 0),
40
+ '#d9826b',
41
+ );
42
+
43
+ const sleevePattern = p
44
+ .overUnderWeave({ spacing: 2.15, threadWidth: 0.95, depth: 0.45 })
45
+ .add(p.stripes({ direction: [1, 0], spacing: 7, width: 0.7, depth: 0.18 }));
46
+
47
+ const sleeve = textile(
48
+ sdf.cylinder(38, 13)
49
+ .shell(1.4)
50
+ .surfaceDisplace(sleevePattern, { uv: 'cylinder' })
51
+ .subtract(sdf.cylinder(46, 9.8))
52
+ .translate(0, 0, 0),
53
+ '#6fa6a3',
54
+ );
55
+
56
+ const ringPattern = p
57
+ .overUnderWeave({ spacing: 1.75, threadWidth: 0.78, depth: 0.28 })
58
+ .add(p.sineWave({ direction: [0, 1], wavelength: 5.4, amplitude: 0.05 }));
59
+
60
+ const ring = textile(
61
+ sdf.torus(18, 3.4)
62
+ .surfaceDisplace(ringPattern, { uv: 'torus' })
63
+ .translate(46, 0, 0),
64
+ '#c6a15b',
65
+ );
66
+
67
+ return { bowl, sleeve, ring };
@@ -0,0 +1,34 @@
1
+ // Sector gear demo: partial tooth window composed with a visual/structural body.
2
+
3
+ const moduleSize = Param.number("Module", 1.2, { min: 0.8, max: 2.5, step: 0.05, unit: "mm" });
4
+ const teethOnFullCircle = Param.number("Pitch Teeth", 36, { min: 24, max: 72, integer: true });
5
+ const toothCount = Param.number("Active Teeth", 11, { min: 2, max: 24, integer: true });
6
+ const firstTooth = Param.number("First Tooth", 2, { min: 0, max: 35, integer: true });
7
+ const faceWidth = Param.number("Face Width", 8, { min: 4, max: 16, unit: "mm" });
8
+
9
+ const pitchRadius = (moduleSize * teethOnFullCircle) / 2;
10
+ const rootRadius = pitchRadius - moduleSize * 1.25;
11
+ const rimWidth = Math.max(2.4, moduleSize * 2);
12
+ const hubDiameter = Math.max(8, moduleSize * 8);
13
+
14
+ const body = lib.gearBodies.spoked({
15
+ outerRadius: rootRadius,
16
+ rimWidth,
17
+ hubDiameter,
18
+ boreDiameter: 5,
19
+ spokeCount: 5,
20
+ spokeWidth: Math.max(2, moduleSize * 1.8),
21
+ faceWidth,
22
+ });
23
+
24
+ const sector = lib.sectorGear({
25
+ module: moduleSize,
26
+ teethOnFullCircle,
27
+ firstTooth: Math.min(firstTooth, teethOnFullCircle - 1),
28
+ toothCount: Math.min(toothCount, teethOnFullCircle),
29
+ pressureAngleDeg: 20,
30
+ faceWidth,
31
+ body,
32
+ });
33
+
34
+ return [{ name: "Sector Gear", shape: sector.color("#d5a15f") }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecad",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
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",
@@ -1,130 +0,0 @@
1
- # forgecad-api-dogfood
2
-
3
- Build a ForgeCAD model while actively hunting for API friction — missing helpers, awkward patterns, bad defaults, verbose boilerplate. Use when asked to dogfood, stress-test the API, or build a model with the goal of improving ForgeCAD.
4
-
5
- | Field | Value |
6
- | --- | --- |
7
- | Installed by | `forgecad skill install` |
8
- | Source | `skills/forgecad-api-dogfood/SKILL.md` |
9
-
10
- ---
11
-
12
- ## API Dogfood: Build to Find Friction
13
-
14
- Build a real ForgeCAD model **and** systematically surface every point where the API fights the developer. The model is the vehicle; the real output is a prioritized list of API improvements with concrete proposals.
15
-
16
- ### Mindset
17
-
18
- You are Steve Jobs reviewing a developer API. Every extra line of code is a failure of the framework. Every time you think "I wish I could just..." — that's a finding. The API should make the obvious thing trivial, the common thing easy, and the hard thing possible.
19
-
20
- ### Workflow
21
-
22
- #### 1. Pick or receive a model to build
23
-
24
- Choose something that exercises real geometry: curves, patterns, assemblies, boolean operations, fillets, parametric dimensions. Simple boxes won't surface friction. Good candidates:
25
- - Mechanical parts (gears, hinges, enclosures)
26
- - Organic shapes (vases, sculptures, ergonomic grips)
27
- - Multi-part assemblies (furniture, toys, mechanisms)
28
- - Patterns and repetition (tiles, lattices, arrays)
29
-
30
- #### 2. Load the ForgeCAD skill
31
-
32
- Invoke the `forgecad` skill to get the current API docs. Read the Core API reference thoroughly — you need to know what exists before you can identify what's missing.
33
-
34
- #### 3. Build bottom-up, journal friction as you go
35
-
36
- Follow the same decomposition strategy as forgecad-make-a-model (smallest piece first, verify, compose upward). But at every step, maintain a **friction journal** — a running list of issues encountered.
37
-
38
- For each friction point, capture:
39
- - **What you were trying to do** (intent)
40
- - **What you had to write** (actual code)
41
- - **What you wish you could write** (dream API)
42
- - **Category** (see below)
43
-
44
- #### 4. Friction categories
45
-
46
- | Category | Description | Example |
47
- |----------|-------------|---------|
48
- | **Missing primitive** | A common shape/op that doesn't exist | No `torus()`, no `wedge()` |
49
- | **Verbose boilerplate** | Too many lines for a simple intent | 5 lines to create a circular pattern |
50
- | **Bad defaults** | Default behavior is rarely what you want | `center` defaulting to `false` when `true` is almost always better |
51
- | **Missing transform** | A transform that should be chainable but isn't | No `.mirrorX()` shorthand |
52
- | **Naming friction** | Name doesn't match mental model | Function named differently than what you'd search for |
53
- | **Composition gap** | Hard to combine things that should compose naturally | Can't easily array along a curve |
54
- | **Error unhelpfulness** | Error message doesn't help you fix the problem | "Invalid geometry" with no indication of where or why |
55
- | **Discovery gap** | Feature exists but you couldn't find it | Had to read source to discover a useful helper |
56
- | **Parameter awkwardness** | Argument order or types feel wrong | Height before radius when you think radius-first |
57
-
58
- #### 5. Validate the model
59
-
60
- Run `forgecad run <file>` after each piece. Note any errors — confusing error messages are themselves friction findings.
61
-
62
- #### 6. Write the friction report
63
-
64
- After the model is complete, produce a structured report. Place it alongside the model file as `<model-name>.friction.md`.
65
-
66
- ```markdown
67
- # API Friction Report: <Model Name>
68
-
69
- ## Summary
70
- - Model: <what was built>
71
- - Lines of code: <count>
72
- - Friction points found: <count>
73
- - Estimated lines with dream API: <count>
74
-
75
- ## Critical (API should not ship without fixing)
76
- ### <Title>
77
- - **Intent**: What I wanted to do
78
- - **Actual**: ```js\n<what I had to write>\n```
79
- - **Dream**: ```js\n<what I wish I could write>\n```
80
- - **Proposed fix**: Concrete API change
81
-
82
- ## High (Common operation, significant friction)
83
- ...
84
-
85
- ## Medium (Nice to have)
86
- ...
87
-
88
- ## Low (Paper cuts)
89
- ...
90
- ```
91
-
92
- #### 7. Propose API changes
93
-
94
- For the top 3 friction points, go beyond the report:
95
- - Draft the actual API signature
96
- - Show how it would simplify the model code
97
- - Check if it conflicts with existing API surface
98
- - Estimate implementation complexity (trivial / moderate / significant)
99
-
100
- If a fix is trivial (< 20 lines, no breaking changes), offer to implement it right now.
101
-
102
- ### What makes a great friction finding
103
-
104
- - **Specific**: "I needed 4 lines to round-robin colors on an array" not "coloring is hard"
105
- - **Comparative**: Show the actual code vs. the dream code side by side
106
- - **Prioritized**: Based on how often other users would hit this, not just your annoyance
107
- - **Actionable**: Includes a concrete proposal, not just a complaint
108
-
109
- ### Anti-patterns to avoid
110
-
111
- - Don't invent API surface nobody would use just because you can imagine it
112
- - Don't confuse "I didn't know about this feature" with "this feature is missing" — check the docs first
113
- - Don't propose breaking changes for minor ergonomic gains
114
- - Don't let the friction hunt derail the model — build the model first, journal as you go
115
- - Don't propose thin wrappers that add no real value (`.moveRight(x)` vs `.translate([x,0,0])`)
116
-
117
- ### File Placement
118
-
119
- Place model files in the ForgeCAD project under:
120
- ```
121
- docs/temporary/dogfood/YYYY-MM-DD/<model-name>.forge.js
122
- docs/temporary/dogfood/YYYY-MM-DD/<model-name>.friction.md
123
- ```
124
-
125
- ### Tips
126
-
127
- - The most valuable friction is the kind you almost didn't notice — the "oh I always have to do this" patterns
128
- - Compare with other CAD APIs (OpenSCAD, CadQuery, JSCAD) — what do they make easy that we don't?
129
- - Pay special attention to the first 5 minutes of building — onboarding friction compounds
130
- - If you find yourself copy-pasting code between parts, that's a composition gap
@@ -1,125 +0,0 @@
1
- ---
2
- name: forgecad-api-dogfood
3
- description: Build a ForgeCAD model while actively hunting for API friction — missing helpers, awkward patterns, bad defaults, verbose boilerplate. Use when asked to dogfood, stress-test the API, or build a model with the goal of improving ForgeCAD.
4
- forgecad-public: true
5
- ---
6
-
7
- # API Dogfood: Build to Find Friction
8
-
9
- Build a real ForgeCAD model **and** systematically surface every point where the API fights the developer. The model is the vehicle; the real output is a prioritized list of API improvements with concrete proposals.
10
-
11
- ## Mindset
12
-
13
- You are Steve Jobs reviewing a developer API. Every extra line of code is a failure of the framework. Every time you think "I wish I could just..." — that's a finding. The API should make the obvious thing trivial, the common thing easy, and the hard thing possible.
14
-
15
- ## Workflow
16
-
17
- ### 1. Pick or receive a model to build
18
-
19
- Choose something that exercises real geometry: curves, patterns, assemblies, boolean operations, fillets, parametric dimensions. Simple boxes won't surface friction. Good candidates:
20
- - Mechanical parts (gears, hinges, enclosures)
21
- - Organic shapes (vases, sculptures, ergonomic grips)
22
- - Multi-part assemblies (furniture, toys, mechanisms)
23
- - Patterns and repetition (tiles, lattices, arrays)
24
-
25
- ### 2. Load the ForgeCAD skill
26
-
27
- Invoke the `forgecad` skill to get the current API docs. Read the Core API reference thoroughly — you need to know what exists before you can identify what's missing.
28
-
29
- ### 3. Build bottom-up, journal friction as you go
30
-
31
- Follow the same decomposition strategy as forgecad-make-a-model (smallest piece first, verify, compose upward). But at every step, maintain a **friction journal** — a running list of issues encountered.
32
-
33
- For each friction point, capture:
34
- - **What you were trying to do** (intent)
35
- - **What you had to write** (actual code)
36
- - **What you wish you could write** (dream API)
37
- - **Category** (see below)
38
-
39
- ### 4. Friction categories
40
-
41
- | Category | Description | Example |
42
- |----------|-------------|---------|
43
- | **Missing primitive** | A common shape/op that doesn't exist | No `torus()`, no `wedge()` |
44
- | **Verbose boilerplate** | Too many lines for a simple intent | 5 lines to create a circular pattern |
45
- | **Bad defaults** | Default behavior is rarely what you want | `center` defaulting to `false` when `true` is almost always better |
46
- | **Missing transform** | A transform that should be chainable but isn't | No `.mirrorX()` shorthand |
47
- | **Naming friction** | Name doesn't match mental model | Function named differently than what you'd search for |
48
- | **Composition gap** | Hard to combine things that should compose naturally | Can't easily array along a curve |
49
- | **Error unhelpfulness** | Error message doesn't help you fix the problem | "Invalid geometry" with no indication of where or why |
50
- | **Discovery gap** | Feature exists but you couldn't find it | Had to read source to discover a useful helper |
51
- | **Parameter awkwardness** | Argument order or types feel wrong | Height before radius when you think radius-first |
52
-
53
- ### 5. Validate the model
54
-
55
- Run `forgecad run <file>` after each piece. Note any errors — confusing error messages are themselves friction findings.
56
-
57
- ### 6. Write the friction report
58
-
59
- After the model is complete, produce a structured report. Place it alongside the model file as `<model-name>.friction.md`.
60
-
61
- ```markdown
62
- # API Friction Report: <Model Name>
63
-
64
- ## Summary
65
- - Model: <what was built>
66
- - Lines of code: <count>
67
- - Friction points found: <count>
68
- - Estimated lines with dream API: <count>
69
-
70
- ## Critical (API should not ship without fixing)
71
- ### <Title>
72
- - **Intent**: What I wanted to do
73
- - **Actual**: ```js\n<what I had to write>\n```
74
- - **Dream**: ```js\n<what I wish I could write>\n```
75
- - **Proposed fix**: Concrete API change
76
-
77
- ## High (Common operation, significant friction)
78
- ...
79
-
80
- ## Medium (Nice to have)
81
- ...
82
-
83
- ## Low (Paper cuts)
84
- ...
85
- ```
86
-
87
- ### 7. Propose API changes
88
-
89
- For the top 3 friction points, go beyond the report:
90
- - Draft the actual API signature
91
- - Show how it would simplify the model code
92
- - Check if it conflicts with existing API surface
93
- - Estimate implementation complexity (trivial / moderate / significant)
94
-
95
- If a fix is trivial (< 20 lines, no breaking changes), offer to implement it right now.
96
-
97
- ## What makes a great friction finding
98
-
99
- - **Specific**: "I needed 4 lines to round-robin colors on an array" not "coloring is hard"
100
- - **Comparative**: Show the actual code vs. the dream code side by side
101
- - **Prioritized**: Based on how often other users would hit this, not just your annoyance
102
- - **Actionable**: Includes a concrete proposal, not just a complaint
103
-
104
- ## Anti-patterns to avoid
105
-
106
- - Don't invent API surface nobody would use just because you can imagine it
107
- - Don't confuse "I didn't know about this feature" with "this feature is missing" — check the docs first
108
- - Don't propose breaking changes for minor ergonomic gains
109
- - Don't let the friction hunt derail the model — build the model first, journal as you go
110
- - Don't propose thin wrappers that add no real value (`.moveRight(x)` vs `.translate([x,0,0])`)
111
-
112
- ## File Placement
113
-
114
- Place model files in the ForgeCAD project under:
115
- ```
116
- docs/temporary/dogfood/YYYY-MM-DD/<model-name>.forge.js
117
- docs/temporary/dogfood/YYYY-MM-DD/<model-name>.friction.md
118
- ```
119
-
120
- ## Tips
121
-
122
- - The most valuable friction is the kind you almost didn't notice — the "oh I always have to do this" patterns
123
- - Compare with other CAD APIs (OpenSCAD, CadQuery, JSCAD) — what do they make easy that we don't?
124
- - Pay special attention to the first 5 minutes of building — onboarding friction compounds
125
- - If you find yourself copy-pasting code between parts, that's a composition gap