forgecad 0.9.2 → 0.9.3

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 (79) hide show
  1. package/LICENSE +7 -5
  2. package/README.md +1 -1
  3. package/README.public.md +24 -2
  4. package/dist/assets/{AdminPage-Bs4PiK00.js → AdminPage-4jihcEk_.js} +1 -1
  5. package/dist/assets/{BlogPage-DVmgN0ma.js → BlogPage-BvzruKtw.js} +1 -1
  6. package/dist/assets/{DocsPage-BP6wlsBN.js → DocsPage-DHbd-WS-.js} +13 -13
  7. package/dist/assets/{EditorApp-Arw2NnGJ.js → EditorApp-C5P2rBfh.js} +433 -84
  8. package/dist/assets/{EditorApp-VY9lXx0N.css → EditorApp-DS0AIUrZ.css} +25 -0
  9. package/dist/assets/{EmbedViewer-qgQiOahL.js → EmbedViewer-B70wQwlE.js} +2 -2
  10. package/dist/assets/{LandingPageProofDriven-DvhtmWOz.js → LandingPageProofDriven-DIsYTnep.js} +1 -1
  11. package/dist/assets/{PricingPage-Ck3CP2ti.css → PricingPage-BMedqFef.css} +48 -0
  12. package/dist/assets/{PricingPage-657oLvWh.js → PricingPage-YPOr12pP.js} +34 -6
  13. package/dist/assets/{SettingsPage-wNy3_2yn.js → SettingsPage-rntoyJ3b.js} +10 -13
  14. package/dist/assets/{app-BdBoMQeO.js → app-CWucmnLZ.js} +801 -1208
  15. package/dist/assets/cli/{render-Ci3jjyT1.js → render-DZHmUySW.js} +214 -23
  16. package/dist/assets/copy-CQKQppF-.js +8 -0
  17. package/dist/assets/{evalWorker-CMCAbK8r.js → evalWorker-C3dKxi9Y.js} +1117 -95
  18. package/dist/assets/{manifold-BMn-8Vf8.js → manifold-CQ3FhfWB.js} +1 -1
  19. package/dist/assets/{manifold-jlYQ6E5R.js → manifold-CU0G1yYL.js} +1 -1
  20. package/dist/assets/{manifold-DbyILno4.js → manifold-CYWZMfjB.js} +2 -2
  21. package/dist/assets/{renderSceneState-DAnqvxSt.js → renderSceneState-BBUrnsUN.js} +1 -1
  22. package/dist/assets/{reportWorker-BcRVMHK-.js → reportWorker-BhZ7DjxQ.js} +1091 -95
  23. package/dist/assets/{sectionPlaneMath-DXJ_TdIW.js → sectionPlaneMath-BxfokaJE.js} +1091 -95
  24. package/dist/cli/render.html +1 -1
  25. package/dist/docs/index.html +2 -2
  26. package/dist/docs-raw/AI/usage.md +182 -89
  27. package/dist/docs-raw/API/core/concepts.md +26 -0
  28. package/dist/docs-raw/CLI.md +58 -37
  29. package/dist/docs-raw/INDEX.md +81 -64
  30. package/dist/docs-raw/cli-monetization.md +9 -8
  31. package/dist/docs-raw/generated/concepts.md +111 -4
  32. package/dist/docs-raw/generated/core.md +2 -0
  33. package/dist/docs-raw/generated/curves.md +480 -1
  34. package/dist/docs-raw/generated/output.md +1 -0
  35. package/dist/docs-raw/generated/sketch.md +2 -0
  36. package/dist/docs-raw/generated/viewport.md +81 -3
  37. package/dist/docs-raw/product/user-outreach-email-templates.md +159 -0
  38. package/dist/docs-raw/skills/forgecad-image-replicator.md +1 -1
  39. package/dist/docs-raw/skills/forgecad-make-a-model.md +33 -4
  40. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +1 -1
  41. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  42. package/dist/docs-raw/skills/forgecad-render-inspect.md +1 -1
  43. package/dist/docs-raw/skills/forgecad.md +2 -1
  44. package/dist/docs-raw/welcome.md +85 -137
  45. package/dist/index.html +1 -1
  46. package/dist/llms.txt +4 -3
  47. package/dist/sitemap.xml +6 -6
  48. package/dist-cli/forgecad.js +1413 -219
  49. package/dist-cli/forgecad.js.map +1 -1
  50. package/dist-skill/CONTEXT.md +594 -5
  51. package/dist-skill/SKILL-dev.md +2 -1
  52. package/dist-skill/SKILL.md +2 -1
  53. package/dist-skill/docs/API/core/concepts.md +26 -0
  54. package/dist-skill/docs/CLI.md +58 -37
  55. package/dist-skill/docs/generated/core.md +2 -0
  56. package/dist-skill/docs/generated/curves.md +480 -1
  57. package/dist-skill/docs/generated/output.md +1 -0
  58. package/dist-skill/docs/generated/sketch.md +2 -0
  59. package/dist-skill/docs/generated/viewport.md +81 -3
  60. package/dist-skill/docs-dev/API/core/concepts.md +26 -0
  61. package/dist-skill/docs-dev/CLI.md +58 -37
  62. package/dist-skill/docs-dev/generated/core.md +2 -0
  63. package/dist-skill/docs-dev/generated/curves.md +480 -1
  64. package/dist-skill/docs-dev/generated/output.md +1 -0
  65. package/dist-skill/docs-dev/generated/sketch.md +2 -0
  66. package/dist-skill/docs-dev/generated/viewport.md +81 -3
  67. package/dist-skill/library/README.md +0 -1
  68. package/dist-skill/library/forgecad-image-replicator/SKILL.md +1 -1
  69. package/dist-skill/library/forgecad-make-a-model/SKILL.md +33 -4
  70. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  71. package/dist-skill/library/forgecad-project/SKILL.md +1 -1
  72. package/dist-skill/library/forgecad-render-inspect/SKILL.md +1 -1
  73. package/examples/api/conformal-product-ribbon.forge.js +77 -0
  74. package/examples/api/render-labels.forge.js +33 -0
  75. package/examples/api/text2d-basics.forge.js +6 -3
  76. package/package.json +1 -1
  77. package/dist-skill/library/forgecad-deep-dive/SKILL.md +0 -120
  78. package/dist-skill/library/forgecad-deep-dive/agents/openai.yaml +0 -4
  79. package/dist-skill/library/forgecad-deep-dive/references/output-shape.md +0 -64
@@ -27,7 +27,8 @@ Prefer documented primitives, import rules, and placement strategies over invent
27
27
 
28
28
  ### Import and composition
29
29
 
30
- - `require("./file.forge.js", { Param: value })` for any model file, with optional param overrides.
30
+ - Always include the extension in relative imports: `require("./file.forge.js", { Param: value })` for model files and `require("./helpers.js")` for plain helper modules. Do not write extensionless imports such as `require("./file")`; ForgeCAD resolves project imports by exact path.
31
+ - ForgeCAD APIs are injected globals in `.forge.js` files. Use `bom()`, `box()`, `scene()`, `Shape`, etc. directly; do not destructure those names from helpers with patterns like `const { bom } = require("./bom.js")`. If a helper file is needed, import it under a project-specific name such as `const bomHelpers = require("./bom.js")`.
31
32
  - `importSvgSketch()` for SVG files (file format loader, not a module import).
32
33
  - `.placeReference('bottom', [0,0,0])` to align any built-in anchor to a world coordinate; also works with custom `.withReferences()`.
33
34
  - Plain `.js` modules for shared helpers/constants (not model imports).
@@ -102,6 +103,24 @@ const width = param("Width", 50, { min: 20, max: 100, unit: "mm" });
102
103
  return box(width, 30, 10);
103
104
  ```
104
105
 
106
+ ## Injected Runtime Names
107
+
108
+ ForgeCAD API functions and classes are injected into every `.forge.js` script. Use them directly; do not import or destructure ForgeCAD API names from helper files.
109
+
110
+ ```javascript
111
+ // BAD — `bom` and `bomToCsv` are already built-in runtime names.
112
+ const { bom, bomToCsv } = require("./bom.js");
113
+
114
+ // GOOD — use the built-in directly.
115
+ bom(4, "M4 bolt");
116
+
117
+ // GOOD — keep project helpers under their own local name.
118
+ const bomHelpers = require("./bom.js");
119
+ bomHelpers.addFasteners(...);
120
+ ```
121
+
122
+ Top-level declarations such as `const bom = ...`, `let scene = ...`, or `class Shape {}` collide with the injected runtime names. If you need a local helper, choose a project-specific name like `projectBom`, `sceneConfig`, or `makeShape`.
123
+
105
124
  ## Execution Model
106
125
 
107
126
  - Scripts re-execute on every parameter change (400ms debounce)
@@ -114,6 +133,8 @@ Top-level assembly scripts can return an unsolved `Assembly` directly; ForgeCAD
114
133
 
115
134
  A script can return a plain object whose values include renderable geometry alongside non-renderable metadata. All renderable entries (Shape, Sketch, ShapeGroup, Assembly, SolvedAssembly, SdfShape, or Array of named objects) are rendered; non-renderable entries are silently skipped. This is useful for multi-file projects where a part needs to publish interface data (bolt positions, dimensions) to other files:
116
135
 
136
+ When importing project files, include the full extension in every relative path: `require('./motor-mount.forge.js')` for model files and `require('./helpers.js')` for plain helper modules. ForgeCAD resolves project imports by exact path and does not infer `.forge.js` or `.js` from `require('./motor-mount')`.
137
+
117
138
  ```javascript
118
139
  // motor-mount.forge.js — renders standalone, exports metadata via require()
119
140
  const holePositions = [[17, 15], [-29, 15], [17, -15], [-29, -15]];
@@ -165,6 +186,12 @@ Shapes carry semantic face labels through their lifecycle. The flow is:
165
186
 
166
187
  You resolve labels to geometry with `.face(name)` or `.face(query)` — see the Shape class docs for the full query API. Operations like `.pocket()`, `.boss()`, `.hole()`, and `faceProfile()` all consume face references.
167
188
 
189
+ ## Text vs Viewport Labels
190
+
191
+ Use `text2d()` only when the letters are part of the model: raised text, engraving, cut labels, serial plates, exported markings, or geometry that should survive into STL/STEP output. `text2d()` builds filled sketch geometry from font outlines, so it can make exact/OCCT workflows slower.
192
+
193
+ Use `Viewport.label(text, [x, y, z], options)` when the goal is to explain the model in the viewport. Render labels are annotations only: they do not create meshes, do not export, do not enter the B-rep path, and do not add face labels.
194
+
168
195
  ## SDF Modeling
169
196
 
170
197
  For organic shapes, smooth blending, TPMS lattices, and surface deformations. Return `SdfShape` values directly, or return a plain object/array tree of SDF leaves, for native raymarch preview. Use `.toShape()` or `toShape(...)` only when you need mesh-backed CAD/export behavior. See [sdf-primitives.md](sdf-primitives.md).
@@ -584,6 +611,8 @@ coalesceEdges(segments: EdgeSegment[], tolerance?: number): EdgeSegment[]
584
611
 
585
612
  When importing a `.forge.js` file, the return value is what the script returns. If the script returns a metadata object (e.g. `{ shape: myShape, bolts: {...} }`), the caller receives the full object — renderable values and metadata together.
586
613
 
614
+ **Path rule:** Always include the file extension in relative imports: use `require("./part.forge.js")` for model files and `require("./helpers.js")` for plain helper modules. ForgeCAD does not apply Node-style extension inference, so `require("./part")` will not find `part.forge.js` or `part.js`.
615
+
587
616
  **Parameter scoping:** Parameters declared in required files are automatically namespaced with a `"filename#N / "` prefix (e.g. `"bracket.forge.js#1 / Width"`). This prevents collisions when multiple files declare same-named params. Each file's params appear as separate sliders.
588
617
 
589
618
  **Parameter overrides:** When passing overrides, use the bare param name (not the scoped name). Overrides are type-checked — unrecognized keys throw an error with typo suggestions.
@@ -2571,6 +2600,8 @@ loadFont(source: string | ArrayBuffer, cacheKey?: string): opentype.Font
2571
2600
 
2572
2601
  The Sketch origin is at the left end of the text baseline by default. Use `align` and `baseline` options to adjust placement. Text is rendered using the bundled Inter font by default, or any TTF/OTF/WOFF font you provide.
2573
2602
 
2603
+ `text2d()` creates real geometry. For non-exported explanatory labels in the viewport, prefer `Viewport.label()` so the text stays off the geometry and OCCT compile paths.
2604
+
2574
2605
  Alignment reference table:
2575
2606
 
2576
2607
  | `align` | `baseline` | Origin |
@@ -4086,9 +4117,11 @@ Smooth curves, lofted surfaces, swept solids, splines, and high-level product sk
4086
4117
  - [QuinticHermiteCurve3D](#quintichermitecurve3d)
4087
4118
  - [ProductSkin](#productskin)
4088
4119
  - [ProductSurfaceRef](#productsurfaceref)
4120
+ - [ProductSurfaceBuilder](#productsurfacebuilder)
4089
4121
  - [ProductSkinBuilder](#productskinbuilder)
4090
4122
  - [ProductStationBuilder](#productstationbuilder)
4091
4123
  - [ProductPanelBuilder](#productpanelbuilder)
4124
+ - [ProductRibbonBuilder](#productribbonbuilder)
4092
4125
  - [ProductSpoutBuilder](#productspoutbuilder)
4093
4126
  - [ProductHandleBuilder](#producthandlebuilder)
4094
4127
  - [ProductHandleFeature](#producthandlefeature)
@@ -4969,12 +5002,31 @@ integrate(...details: Shape[]): Shape
4969
5002
  diagnostics(): ProductSkinDiagnostics
4970
5003
  ```
4971
5004
 
5005
+ **`ProductSkinDiagnostics`**: `representation: ProductSkinRepresentation`, `lowering: string[]`, `warnings: string[]`, `stationNames: string[]`, `railNames: string[]`
5006
+
5007
+ **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5008
+
5009
+ `"exact" | "sampled" | "mixed" | "fallback"`
5010
+
4972
5011
  #### `uv()` — Create a side/u/v surface-ref query on this skin.
4973
5012
 
4974
5013
  ```ts
4975
5014
  uv(side: ProductSkinSide, u?: number, v?: number): ProductSkinRefQuery
4976
5015
  ```
4977
5016
 
5017
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5018
+
5019
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5020
+
5021
+ **`ProductSkinRefQuery`**
5022
+
5023
+ | Option | Type | Description |
5024
+ |--------|------|-------------|
5025
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5026
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5027
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5028
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5029
+
4978
5030
  #### `ref()` — Resolve a named ref published with Product.skin().refs(...).
4979
5031
 
4980
5032
  ```ts
@@ -4987,18 +5039,32 @@ ref(name: string): ProductSurfaceRef
4987
5039
  curveOnSurface(name: string, points: Array<Partial<ProductSkinRefQuery> & { side: ProductSkinSide; }>): ProductSurfaceRef[]
4988
5040
  ```
4989
5041
 
5042
+ #### `surface()` — Create a fluent surface helper for refs and conformal features on one side of this skin.
5043
+
5044
+ Use this when several refs or ribbons share the same skin side; side-local helpers keep path points concise and make it harder to mix sides accidentally.
5045
+
5046
+ ```ts
5047
+ surface(side: ProductSkinSide): ProductSurfaceBuilder
5048
+ ```
5049
+
4990
5050
  #### `stationAt()` — Interpolate center, width, and depth at a normalized v or absolute axis value.
4991
5051
 
4992
5052
  ```ts
4993
- stationAt(vOrAxis: number): { center: Vec3; width: number; depth: number; dWidth: number; dDepth: number; axisValue: number; }
5053
+ stationAt(vOrAxis: number): { ... }
4994
5054
  ```
4995
5055
 
5056
+ **`ProductProfileKind`**
5057
+
5058
+ `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5059
+
4996
5060
  #### `frame()` — Build a local surface frame from a side/u/v query.
4997
5061
 
4998
5062
  ```ts
4999
5063
  frame(query: ProductSkinRefQuery): ProductSurfaceFrame
5000
5064
  ```
5001
5065
 
5066
+ **`ProductSurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentU: Vec3`, `tangentV: Vec3`, `matrix: Mat4`, `skin: string`, `representation: ProductSkinRepresentation`
5067
+
5002
5068
  ### `ProductSurfaceRef`
5003
5069
 
5004
5070
  **Properties:**
@@ -5015,6 +5081,25 @@ frame(query: ProductSkinRefQuery): ProductSurfaceFrame
5015
5081
  frame(overrides?: Partial<ProductSkinRefQuery>): ProductSurfaceFrame
5016
5082
  ```
5017
5083
 
5084
+ **`ProductSkinRefQuery`**
5085
+
5086
+ | Option | Type | Description |
5087
+ |--------|------|-------------|
5088
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5089
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5090
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5091
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5092
+
5093
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5094
+
5095
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5096
+
5097
+ **`ProductSurfaceFrame`**: `point: Vec3`, `normal: Vec3`, `tangentU: Vec3`, `tangentV: Vec3`, `matrix: Mat4`, `skin: string`, `representation: ProductSkinRepresentation`
5098
+
5099
+ **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5100
+
5101
+ `"exact" | "sampled" | "mixed" | "fallback"`
5102
+
5018
5103
  #### `with()` — Return a copy of this ref with side/u/v/offset overrides.
5019
5104
 
5020
5105
  ```ts
@@ -5027,12 +5112,98 @@ with(overrides: Partial<ProductSkinRefQuery>): ProductSurfaceRef
5027
5112
  attach(detail: Shape | ShapeGroup, options?: ProductAttachOptions): Shape | ShapeGroup
5028
5113
  ```
5029
5114
 
5115
+ `ProductAttachOptions`: `{ offset?: number, inset?: number }`
5116
+
5030
5117
  #### `querySpec()` — Return the serializable side/u/v query behind this ref.
5031
5118
 
5032
5119
  ```ts
5033
5120
  querySpec(): ProductSkinRefQuery
5034
5121
  ```
5035
5122
 
5123
+ ### `ProductSurfaceBuilder`
5124
+
5125
+ Fluent helper bound to one ProductSkin side for refs and side-local conformal features.
5126
+
5127
+ **Properties:**
5128
+
5129
+ | Property | Type | Description |
5130
+ |----------|------|-------------|
5131
+ | `side` | `ProductSkinSide` | — |
5132
+
5133
+ **Methods:**
5134
+
5135
+ #### `ref()` — Create a ref on this skin side.
5136
+
5137
+ ```ts
5138
+ ref(u?: number, v?: number, offset?: number): ProductSurfaceRef
5139
+ ```
5140
+
5141
+ #### `uv()` — Create a side/u/v query on this skin side.
5142
+
5143
+ ```ts
5144
+ uv(u?: number, v?: number, offset?: number): ProductSkinRefQuery
5145
+ ```
5146
+
5147
+ **`ProductSkinRefQuery`**
5148
+
5149
+ | Option | Type | Description |
5150
+ |--------|------|-------------|
5151
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5152
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5153
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5154
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5155
+
5156
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5157
+
5158
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5159
+
5160
+ #### `ribbon()` — Start a conformal ribbon on this skin side.
5161
+
5162
+ Path points use side-local `u`/`v` coordinates; this builder supplies the side. The returned ProductRibbonBuilder is already bound to the source skin and can be further configured before build(). Use `widthSamples` >= 3 when the ribbon must visibly wrap over curved product sections instead of behaving like a flat strip.
5163
+
5164
+ ```ts
5165
+ ribbon(name: string, points: ProductSurfacePathPoint[], options?: ProductRibbonBuildOptions): ProductRibbonBuilder
5166
+ ```
5167
+
5168
+ **`ProductSurfacePathPoint`**
5169
+ - `u?: number` — Across-side parameter on the bound side. Defaults to 0.5.
5170
+ - `v?: number` — Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5.
5171
+ - `offset?: number` — Positive distance away from the surface along the resolved normal.
5172
+
5173
+ **`ProductRibbonBuildOptions`** — Options shared by Product.ribbon() builders and Product.surface(...).ribbon(...).
5174
+
5175
+ | Option | Type | Description |
5176
+ |--------|------|-------------|
5177
+ | `width?` | `number` | Width across the surface in millimeters. |
5178
+ | `thickness?` | `number` | Solid thickness outward from the source surface in millimeters. |
5179
+ | `offset?` | `number` | Positive clearance between the source surface and the ribbon's inner face. |
5180
+ | `samples?` | `number` | Samples along the ribbon path. Higher values bend more smoothly. |
5181
+ | `widthSamples?` | `number` | Samples across the ribbon width. Use 3+ to visibly wrap over curved cross-sections. |
5182
+ | `resolution?` | `number` | Tessellation resolution passed to the lowered NURBS surface. |
5183
+ | `material?` | `ProductMaterial` | Apply a product material preset to the ribbon. |
5184
+ | `color?` | `string` | Apply a simple color override. |
5185
+
5186
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5187
+
5188
+ **`ShapeMaterialProps`**
5189
+
5190
+ | Option | Type | Description |
5191
+ |--------|------|-------------|
5192
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5193
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5194
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5195
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5196
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5197
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5198
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5199
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5200
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5201
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5202
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5203
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5204
+ | `specularColor?` | `string` | Specular highlight tint. |
5205
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5206
+
5036
5207
  ### `ProductSkinBuilder`
5037
5208
 
5038
5209
  **Properties:**
@@ -5049,24 +5220,55 @@ querySpec(): ProductSkinRefQuery
5049
5220
  axis(axis: ProductSkinAxis): this
5050
5221
  ```
5051
5222
 
5223
+ **`ProductSkinAxis`** — Primary world axis used to order ProductSkin loft stations.
5224
+
5225
+ `"X" | "Y" | "Z"`
5226
+
5052
5227
  #### `stations()` — Set named cross-section stations for the product skin.
5053
5228
 
5054
5229
  ```ts
5055
5230
  stations(stations: Array<ProductStationBuilder | ProductStationSpec>): this
5056
5231
  ```
5057
5232
 
5233
+ `ProductStationSpec`: `{ name: string, center: Vec3, profile: ProductStationProfile, crown?: number }`
5234
+
5235
+ `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5236
+
5237
+ **`ProductProfileKind`**
5238
+
5239
+ `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5240
+
5058
5241
  #### `rails()` — Attach guide rails as ProductSkin IR metadata and diagnostics.
5059
5242
 
5060
5243
  ```ts
5061
5244
  rails(rails: Record<string, ProductRailSpec>): this
5062
5245
  ```
5063
5246
 
5247
+ `ProductRailSpec`: `{ kind: ProductRailKind, points: Vec3[], degree?: number, name?: string }`
5248
+
5249
+ **`ProductRailKind`**
5250
+
5251
+ `"bezier" | "nurbs" | "polyline"`
5252
+
5064
5253
  #### `ref()` — Publish a named semantic surface ref on the skin.
5065
5254
 
5066
5255
  ```ts
5067
5256
  ref(name: string, query: ProductSkinRefQuery): this
5068
5257
  ```
5069
5258
 
5259
+ **`ProductSkinRefQuery`**
5260
+
5261
+ | Option | Type | Description |
5262
+ |--------|------|-------------|
5263
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5264
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5265
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5266
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5267
+
5268
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5269
+
5270
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5271
+
5070
5272
  #### `refs()` — Publish multiple named semantic surface refs on the skin.
5071
5273
 
5072
5274
  ```ts
@@ -5085,6 +5287,27 @@ uv(side: ProductSkinSide, u?: number, v?: number): ProductSkinRefQuery
5085
5287
  material(material: ProductMaterial): this
5086
5288
  ```
5087
5289
 
5290
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5291
+
5292
+ **`ShapeMaterialProps`**
5293
+
5294
+ | Option | Type | Description |
5295
+ |--------|------|-------------|
5296
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5297
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5298
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5299
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5300
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5301
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5302
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5303
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5304
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5305
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5306
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5307
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5308
+ | `specularColor?` | `string` | Specular highlight tint. |
5309
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5310
+
5088
5311
  #### `color()` — Apply a simple color override to the lowered skin.
5089
5312
 
5090
5313
  ```ts
@@ -5155,6 +5378,8 @@ oval(width: number, depth: number, options?: { segments?: number; }): this
5155
5378
  superEllipse(width: number, depth: number, options?: ProductStationSuperEllipseOptions): this
5156
5379
  ```
5157
5380
 
5381
+ `ProductStationSuperEllipseOptions`: `{ segments?: number, exponent?: number }`
5382
+
5158
5383
  #### [`roundedRect()`](/docs/sketch#roundedrect) — Use a rounded-rectangle cross-section with the given corner radius.
5159
5384
 
5160
5385
  ```ts
@@ -5185,6 +5410,14 @@ crown(amount: number): this
5185
5410
  toSpec(): ProductStationSpec
5186
5411
  ```
5187
5412
 
5413
+ `ProductStationSpec`: `{ name: string, center: Vec3, profile: ProductStationProfile, crown?: number }`
5414
+
5415
+ `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5416
+
5417
+ **`ProductProfileKind`**
5418
+
5419
+ `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5420
+
5188
5421
  ### `ProductPanelBuilder`
5189
5422
 
5190
5423
  **Properties:**
@@ -5225,6 +5458,27 @@ thickness(thickness: number): this
5225
5458
  material(material: ProductMaterial): this
5226
5459
  ```
5227
5460
 
5461
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5462
+
5463
+ **`ShapeMaterialProps`**
5464
+
5465
+ | Option | Type | Description |
5466
+ |--------|------|-------------|
5467
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5468
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5469
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5470
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5471
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5472
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5473
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5474
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5475
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5476
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5477
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5478
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5479
+ | `specularColor?` | `string` | Specular highlight tint. |
5480
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5481
+
5228
5482
  #### `color()` — Apply a simple color override to the panel.
5229
5483
 
5230
5484
  ```ts
@@ -5243,6 +5497,202 @@ build(): Shape
5243
5497
  attachTo(ref: ProductRefInput, options?: ProductPanelAttachOptions): Shape
5244
5498
  ```
5245
5499
 
5500
+ **`ProductRefInput`**
5501
+
5502
+ `ProductSurfaceRef`
5503
+
5504
+ `ProductAttachOptions`: `{ offset?: number, inset?: number }`
5505
+
5506
+ `ProductPanelAttachOptions`: `{ at?: Partial<ProductSkinRefQuery>, thickness?: number, material?: ProductMaterial, color?: string }`
5507
+
5508
+ **`ProductSkinRefQuery`**
5509
+
5510
+ | Option | Type | Description |
5511
+ |--------|------|-------------|
5512
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5513
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5514
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5515
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5516
+
5517
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5518
+
5519
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5520
+
5521
+ ### `ProductRibbonBuilder`
5522
+
5523
+ Builder for thin trim, label, grip, and split-line features that bend with a ProductSkin surface.
5524
+
5525
+ **Properties:**
5526
+
5527
+ | Property | Type | Description |
5528
+ |----------|------|-------------|
5529
+ | `name` | `string` | — |
5530
+
5531
+ **Methods:**
5532
+
5533
+ #### `on()` — Follow a ProductSkin with side/u/v path queries or refs.
5534
+
5535
+ This is the highest-fidelity mode because every interpolated sample is resolved through ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes. All query path points must stay on one side; split side transitions into separate ribbons.
5536
+
5537
+ ```ts
5538
+ on(skin: ProductSkin, points: ProductRibbonPathPoint[], options?: ProductRibbonBuildOptions): this
5539
+ ```
5540
+
5541
+ **`ProductRibbonPathPoint`** — Path point for Product.ribbon().on(...): either a side/u/v query or a resolved surface ref.
5542
+
5543
+ `ProductSkinRefQuery | ProductSurfaceRef`
5544
+
5545
+ **`ProductSkinRefQuery`**
5546
+
5547
+ | Option | Type | Description |
5548
+ |--------|------|-------------|
5549
+ | `side` | `ProductSkinSide` | Side of the product skin. `front` is the minimum axis cap, `rear`/`back` is the maximum axis cap. |
5550
+ | `u?` | `number` | Across-side parameter for side refs. Defaults to 0.5. |
5551
+ | `v?` | `number` | Along-axis parameter, 0 at the first cap and 1 at the rear/back cap. Defaults to 0.5. |
5552
+ | `offset?` | `number` | Positive distance away from the surface along the resolved normal. |
5553
+
5554
+ **`ProductSkinSide`** — Semantic side of a ProductSkin. `back` is accepted as an alias for `rear`.
5555
+
5556
+ `"left" | "right" | "top" | "bottom" | "front" | "rear" | "back"`
5557
+
5558
+ **`ProductRibbonBuildOptions`** — Options shared by Product.ribbon() builders and Product.surface(...).ribbon(...).
5559
+
5560
+ | Option | Type | Description |
5561
+ |--------|------|-------------|
5562
+ | `width?` | `number` | Width across the surface in millimeters. |
5563
+ | `thickness?` | `number` | Solid thickness outward from the source surface in millimeters. |
5564
+ | `offset?` | `number` | Positive clearance between the source surface and the ribbon's inner face. |
5565
+ | `samples?` | `number` | Samples along the ribbon path. Higher values bend more smoothly. |
5566
+ | `widthSamples?` | `number` | Samples across the ribbon width. Use 3+ to visibly wrap over curved cross-sections. |
5567
+ | `resolution?` | `number` | Tessellation resolution passed to the lowered NURBS surface. |
5568
+ | `material?` | `ProductMaterial` | Apply a product material preset to the ribbon. |
5569
+ | `color?` | `string` | Apply a simple color override. |
5570
+
5571
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5572
+
5573
+ **`ShapeMaterialProps`**
5574
+
5575
+ | Option | Type | Description |
5576
+ |--------|------|-------------|
5577
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5578
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5579
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5580
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5581
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5582
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5583
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5584
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5585
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5586
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5587
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5588
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5589
+ | `specularColor?` | `string` | Specular highlight tint. |
5590
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5591
+
5592
+ #### `fromRefs()` — Follow explicit surface refs.
5593
+
5594
+ Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and interpolates between those frames; use on(skin, points) when you need full skin-side sampling between sparse control points.
5595
+
5596
+ ```ts
5597
+ fromRefs(points: ProductSurfaceRef[], options?: ProductRibbonBuildOptions): this
5598
+ ```
5599
+
5600
+ #### `width()` — Set ribbon width in millimeters.
5601
+
5602
+ ```ts
5603
+ width(width: number): this
5604
+ ```
5605
+
5606
+ #### `thickness()` — Set solid thickness outward from the source surface in millimeters.
5607
+
5608
+ ```ts
5609
+ thickness(thickness: number): this
5610
+ ```
5611
+
5612
+ #### `offset()` — Set positive clearance between the source surface and the ribbon's inner face.
5613
+
5614
+ ```ts
5615
+ offset(offset: number): this
5616
+ ```
5617
+
5618
+ #### `samples()` — Set samples along the path.
5619
+
5620
+ ```ts
5621
+ samples(samples: number): this
5622
+ ```
5623
+
5624
+ #### `widthSamples()` — Set samples across the width. Use 3+ to bend over curved cross-sections.
5625
+
5626
+ ```ts
5627
+ widthSamples(samples: number): this
5628
+ ```
5629
+
5630
+ #### `resolution()` — Set NURBS tessellation resolution.
5631
+
5632
+ ```ts
5633
+ resolution(resolution: number): this
5634
+ ```
5635
+
5636
+ #### `material()` — Apply a product material preset.
5637
+
5638
+ ```ts
5639
+ material(material: ProductMaterial): this
5640
+ ```
5641
+
5642
+ #### `color()` — Apply a simple color override.
5643
+
5644
+ ```ts
5645
+ color(color: string): this
5646
+ ```
5647
+
5648
+ #### `build()` — Build a conformal ribbon as a thin NURBS surface solid.
5649
+
5650
+ ```ts
5651
+ build(options?: ProductRibbonBuildOptions): Shape
5652
+ ```
5653
+
5654
+ #### `buildWithDiagnostics()` — Build a conformal ribbon and return surface-feature diagnostics.
5655
+
5656
+ Use this while validating API usage or model fidelity; diagnostics report sampling counts, side-span clamping, lowering mode, and warnings that should be visible in reviews.
5657
+
5658
+ ```ts
5659
+ buildWithDiagnostics(options?: ProductRibbonBuildOptions): ProductRibbonResult
5660
+ ```
5661
+
5662
+ **`ProductRibbonResult`** — Shape plus diagnostics returned by ProductRibbonBuilder.buildWithDiagnostics().
5663
+ - `shape: Shape` — Lowered conformal ribbon shape.
5664
+ - `diagnostics: ProductRibbonDiagnostics` — Sampling and lowering diagnostics for the returned shape.
5665
+
5666
+ **`ProductRibbonDiagnostics`** — Diagnostics describing how a conformal ribbon was sampled and lowered.
5667
+
5668
+ | Option | Type | Description |
5669
+ |--------|------|-------------|
5670
+ | `name` | `string` | Ribbon shape name. |
5671
+ | `skin?` | `string` | Source skin name when the ribbon follows a ProductSkin directly. |
5672
+ | `side?` | `ProductSkinSide` | Source skin side when all path points are on one semantic side. |
5673
+ | `pathPointCount` | `number` | Number of control path points supplied before interpolation. |
5674
+ | `width` | `number` | Final ribbon width in millimeters. |
5675
+ | `thickness` | `number` | Final ribbon solid thickness in millimeters. |
5676
+ | `offset` | `number` | Final normal offset from the source surface in millimeters. |
5677
+ | `samples` | `number` | Final sample count along the ribbon path. |
5678
+ | `widthSamples` | `number` | Final sample count across the ribbon width. |
5679
+ | `resolution` | `number` | NURBS tessellation resolution used for the lowered surface. |
5680
+ | `lowering` | `"nurbsSurface"` | Lowering primitive used for the ribbon shape. |
5681
+ | `expectedFidelity` | `ProductSkinRepresentation` | Expected fidelity inherited from the source skin/ref sampling mode. |
5682
+ | `clampedUCount` | `number` | Number of generated width samples clamped to the valid side span. |
5683
+ | `maxUClampDistance` | `number` | Largest absolute u-distance lost to side-span clamping. |
5684
+ | `warnings` | `string[]` | Non-fatal sampling and lowering warnings. |
5685
+
5686
+ **`ProductSkinRepresentation`** — Reported lowering mode for ProductSkin and conformal feature diagnostics.
5687
+
5688
+ `"exact" | "sampled" | "mixed" | "fallback"`
5689
+
5690
+ #### `diagnostics()` — Return diagnostics from the most recent build, if this builder has been built.
5691
+
5692
+ ```ts
5693
+ diagnostics(): ProductRibbonDiagnostics | undefined
5694
+ ```
5695
+
5246
5696
  ### `ProductSpoutBuilder`
5247
5697
 
5248
5698
  **Properties:**
@@ -5265,6 +5715,14 @@ from(ref: ProductSurfaceRef): this
5265
5715
  sections(sections: Array<Sketch | ProductStationBuilder | ProductStationSpec>): this
5266
5716
  ```
5267
5717
 
5718
+ `ProductStationSpec`: `{ name: string, center: Vec3, profile: ProductStationProfile, crown?: number }`
5719
+
5720
+ `ProductStationProfile`: `{ sketch: Sketch, width: number, depth: number, kind: ProductProfileKind, radius?: number, exponent?: number }`
5721
+
5722
+ **`ProductProfileKind`**
5723
+
5724
+ `"oval" | "roundedRect" | "circle" | "superEllipse" | "custom"`
5725
+
5268
5726
  #### `projection()` — Set the projection length along the source ref normal.
5269
5727
 
5270
5728
  ```ts
@@ -5283,6 +5741,27 @@ edgeLength(value: number): this
5283
5741
  material(material: ProductMaterial): this
5284
5742
  ```
5285
5743
 
5744
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5745
+
5746
+ **`ShapeMaterialProps`**
5747
+
5748
+ | Option | Type | Description |
5749
+ |--------|------|-------------|
5750
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5751
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5752
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5753
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5754
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5755
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5756
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5757
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5758
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5759
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5760
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5761
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5762
+ | `specularColor?` | `string` | Specular highlight tint. |
5763
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5764
+
5286
5765
  #### `color()` — Apply a simple color override to the spout.
5287
5766
 
5288
5767
  ```ts
@@ -5301,6 +5780,8 @@ build(): Shape
5301
5780
  attach(options?: ProductAttachOptions): Shape
5302
5781
  ```
5303
5782
 
5783
+ `ProductAttachOptions`: `{ offset?: number, inset?: number }`
5784
+
5304
5785
  ### `ProductHandleBuilder`
5305
5786
 
5306
5787
  **Properties:**
@@ -5323,6 +5804,12 @@ between(upper: ProductSurfaceRef, lower: Vec3): this
5323
5804
  spine(points: Vec3[] | ProductRailSpec): this
5324
5805
  ```
5325
5806
 
5807
+ `ProductRailSpec`: `{ kind: ProductRailKind, points: Vec3[], degree?: number, name?: string }`
5808
+
5809
+ **`ProductRailKind`**
5810
+
5811
+ `"bezier" | "nurbs" | "polyline"`
5812
+
5326
5813
  #### `grip()` — Set the grip cross-section profile.
5327
5814
 
5328
5815
  ```ts
@@ -5335,6 +5822,27 @@ grip(profile: Sketch): this
5335
5822
  material(material: ProductMaterial): this
5336
5823
  ```
5337
5824
 
5825
+ `ProductMaterial`: `{ color?: string, material?: ShapeMaterialProps }`
5826
+
5827
+ **`ShapeMaterialProps`**
5828
+
5829
+ | Option | Type | Description |
5830
+ |--------|------|-------------|
5831
+ | `metalness?` | `number` | Metalness factor (0 = dielectric, 1 = metal). Default: 0.05 |
5832
+ | `roughness?` | `number` | Roughness factor (0 = mirror, 1 = fully diffuse). Default: 0.35 |
5833
+ | `emissive?` | `string` | Emissive glow color (hex string, e.g. "#ff6b35"). |
5834
+ | `emissiveIntensity?` | `number` | Emissive intensity multiplier. Default: 1 |
5835
+ | `opacity?` | `number` | Opacity (0 = fully transparent, 1 = fully opaque). Default: 1 |
5836
+ | `wireframe?` | `boolean` | Render as wireframe. Default: false |
5837
+ | `clearcoat?` | `number` | Clearcoat intensity (0–1). Default: 0.1 |
5838
+ | `clearcoatRoughness?` | `number` | Clearcoat roughness (0–1). Default: 0.4 |
5839
+ | `transmission?` | `number` | Glass/translucency transmission factor (0–1). Renderer support depends on target. |
5840
+ | `ior?` | `number` | Index of refraction for transmissive materials. Typical glass is ~1.45. |
5841
+ | `thickness?` | `number` | Approximate transmissive volume thickness in model units. |
5842
+ | `specularIntensity?` | `number` | Specular highlight intensity (0–1). |
5843
+ | `specularColor?` | `string` | Specular highlight tint. |
5844
+ | `reflectivity?` | `number` | Reflection strength for supported renderers (0–1). |
5845
+
5338
5846
  #### `padMaterial()` — Apply a product material preset to handle landing pads.
5339
5847
 
5340
5848
  ```ts
@@ -5431,7 +5939,9 @@ toGroup(): ShapeGroup
5431
5939
  - `describeProfile(sketch: Sketch, kind?: ProductProfileKind, radius?: number): ProductProfileDescriptor` — Describe a custom sketch as a product profile.
5432
5940
  - `scaleProfileTo(sketch: Sketch, width: number, depth: number): Sketch` — Scale an existing profile sketch to a target width/depth.
5433
5941
  - `ref(skin: ProductSkin, query: ProductSkinRefQuery): ProductSurfaceRef` — Create an ad-hoc ProductSurfaceRef from a skin and side/u/v query.
5942
+ - `surface(skin: ProductSkin, side: ProductSkinSide): ProductSurfaceBuilder` — Create a fluent surface helper for refs and conformal features on one side of a skin. Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
5434
5943
  - `panel(name: string): ProductPanelBuilder` — Start a panel feature builder.
5944
+ - `ribbon(name: string): ProductRibbonBuilder` — Start a conformal ribbon/trim builder for details that should bend with a ProductSkin. Call .on(skin, points) for side/u/v sampling or .fromRefs(points) for explicit surface refs, then configure width, thickness, offset, sampling, material, and color before build().
5435
5945
  - `spout(name: string): ProductSpoutBuilder` — Start a spout/nozzle feature builder.
5436
5946
  - `handle(name: string): ProductHandleBuilder` — Start a handle feature builder.
5437
5947
  - `place(detail: Shape | ShapeGroup, ref: ProductRefInput, options?: ProductAttachOptions): Shape | ShapeGroup` — Place a shape or group on a ProductSurfaceRef.
@@ -6754,6 +7264,7 @@ BOM entries are accumulated during script execution and exported alongside the m
6754
7264
  - `quantity` must be a finite number `>= 0`. A quantity of `0` is silently ignored (useful for conditional scripting with `param()`-driven counts).
6755
7265
  - `unit` defaults to `"pieces"` when omitted or empty.
6756
7266
  - The assembly `solved.bom()` / `solved.bomCsv()` API is separate and covers per-part assembly metadata; this function is for free-form purchased-item annotation.
7267
+ - `bom()` is injected into every `.forge.js` script. Call it directly; do not write `const { bom } = require(...)`, because top-level declarations named `bom` collide with the built-in runtime name.
6757
7268
 
6758
7269
  ```ts
6759
7270
  const tubeLen = param("Tube Length", 1200, { min: 300, max: 4000, unit: "mm" });
@@ -7205,7 +7716,7 @@ Cut planes, exploded views, joint animations, and scene configuration.
7205
7716
 
7206
7717
  ## Contents
7207
7718
 
7208
- - [Viewport & Runtime](#viewport-runtime) — `scene`, `viewConfig`, `explodeView`, `jointsView`, `cutPlane`, `mock`, `showLabels`, `highlight`
7719
+ - [Viewport & Runtime](#viewport-runtime) — `Viewport.label`, `scene`, `viewConfig`, `explodeView`, `jointsView`, `cutPlane`, `mock`, `showLabels`, `highlight`
7209
7720
  - [RouteBuilder](#routebuilder)
7210
7721
  - [route](#route)
7211
7722
 
@@ -7213,14 +7724,50 @@ Cut planes, exploded views, joint animations, and scene configuration.
7213
7724
 
7214
7725
  ### Viewport & Runtime
7215
7726
 
7727
+ #### `Viewport.label()` — Add a render-only viewport label at a world-space point.
7728
+
7729
+ `Viewport.label()` is for explanatory text that helps a viewer understand the model. It does not create sketches, meshes, B-rep topology, exported text, or face labels, so it stays off the OCCT path. Use [`text2d()`](/docs/sketch#text2d) only when the letters should become manufactured geometry, such as raised lettering, engraved serial numbers, or exported nameplates.
7730
+
7731
+ Labels are collected during script execution and rendered by the viewport as lightweight overlay annotations. They are ignored by exports and do not appear in `objects`.
7732
+
7733
+ ```js
7734
+ Viewport.label('Bearing bore', [0, 0, 18], {
7735
+ color: '#f8fafc',
7736
+ background: '#0f172acc',
7737
+ offset: [0, 0, 8],
7738
+ anchor: 'bottom',
7739
+ });
7740
+
7741
+ return box(40, 30, 12);
7742
+ ```
7743
+
7744
+ ```ts
7745
+ Viewport.label(text: string, at: [ number, number, number ], options?: RenderLabelOptions): void
7746
+ ```
7747
+
7748
+ **`RenderLabelOptions`**
7749
+
7750
+ | Option | Type | Description |
7751
+ |--------|------|-------------|
7752
+ | `color?` | `string` | Text color as any CSS color string. |
7753
+ | `background?` | `string` | Background color as any CSS color string. Use `'transparent'` for no pill background. |
7754
+ | `size?` | `number` | Font size in CSS pixels. Defaults to 12. |
7755
+ | `offset?` | `[ number, number, number ]` | Additional world-space offset from `at`. |
7756
+ | `anchor?` | `RenderLabelAnchor` | Which point of the label box is anchored to `at`. Defaults to `'center'`. |
7757
+ | `alwaysOnTop?` | `boolean` | When false, the label is hidden when occluded by scene geometry. Defaults to true. |
7758
+
7216
7759
  #### `scene()` — Configure the scene environment for the current script execution.
7217
7760
 
7218
- Controls camera position, lighting rig, background color or gradient, atmospheric fog, environment maps, post-processing effects, and capture parameters for the `forgecad capture` command. Multiple calls merge — later values override earlier ones on a per-key basis, so you can split configuration across multiple `scene()` calls.
7761
+ Controls camera position, named render views, optional model journeys, lighting rig, background color or gradient, atmospheric fog, environment maps, post-processing effects, and capture parameters for the `forgecad capture` command. Multiple calls merge — later values override earlier ones on a per-key basis, so you can split configuration across multiple `scene()` calls.
7219
7762
 
7220
7763
  When `lights` is specified, **all** default lights are removed. You must include your own ambient light or the scene will be fully dark.
7221
7764
 
7222
7765
  Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
7223
7766
 
7767
+ Named render views let scripts check in repeatable cameras next to the model code. The canonical shape is `{ camera: { position, target } }`, and a direct camera shorthand `{ position, target }` is also accepted. Use the canonical shape when you may add view metadata later. Use it from the CLI with `forgecad render 3d model.forge.js --view hero`.
7768
+
7769
+ Model journeys let scripts check in a compact guided path through named objects. Each journey has ordered `steps`; each step can name a `focus` target by object name/tree path, provide a caption, and optionally provide an explicit camera. In the viewer, journeys are opt-in: they appear as a small Explore control and do not move the camera until the user starts them. Use `forgecad run model.forge.js --journeys` or `--journeys-json` to inspect resolved targets.
7770
+
7224
7771
  Post-processing effects (`bloom`, `vignette`, `grain`) work in the browser viewport only. The CLI applies camera, lights, background, fog, and `toneMappingExposure` but skips shader effects.
7225
7772
 
7226
7773
  All numeric values accept `param()` expressions.
@@ -7229,6 +7776,22 @@ All numeric values accept `param()` expressions.
7229
7776
  scene({
7230
7777
  background: { top: '#000814', bottom: '#001d3d' },
7231
7778
  camera: { position: [160, -120, 100], target: [0, 0, 50], fov: 52 },
7779
+ views: {
7780
+ hero: {
7781
+ camera: { position: [180, -140, 90], target: [0, 0, 25], up: [0, 0, 1], fov: 38 },
7782
+ },
7783
+ side: { position: [240, 0, 70], target: [0, 0, 25], fov: 34 },
7784
+ },
7785
+ journeys: {
7786
+ grandTour: {
7787
+ title: 'Grand Tour',
7788
+ startsAt: 'overview',
7789
+ steps: [
7790
+ { id: 'overview', focus: 'Solar System', caption: 'Start with the whole model.' },
7791
+ { id: 'earth', focus: 'Earth', caption: 'Fit and inspect Earth.' },
7792
+ ],
7793
+ },
7794
+ },
7232
7795
  lights: [
7233
7796
  { type: 'ambient', color: '#001233', intensity: 0.08 },
7234
7797
  { type: 'point', position: [120, -80, 130], color: '#00f5d4', intensity: 4, distance: 400, decay: 1 },
@@ -7255,12 +7818,38 @@ scene(options: SceneOptions): void
7255
7818
  | Option | Type | Description |
7256
7819
  |--------|------|-------------|
7257
7820
  | `capture?` | `SceneCaptureConfig` | Default capture parameters for `forgecad capture` — CLI flags override these. |
7258
- | `background?`, `camera?`, `lights?`, `environment?`, `fog?`, `postProcessing?`, `ground?` | | — |
7821
+ | `background?`, `camera?`, `views?`, `journeys?`, `lights?`, `environment?`, `fog?`, `postProcessing?`, `ground?` | | — |
7259
7822
 
7260
7823
  `SceneBackgroundGradient`: `{ top: string, bottom: string }`
7261
7824
 
7262
7825
  **`SceneCameraConfig`**: `position?: [ number, number, number ]`, `target?: [ number, number, number ]`, `up?: [ number, number, number ]`, `fov?: number`, `type?: "perspective" | "orthographic"`
7263
7826
 
7827
+ **`SceneJourneyConfig`**
7828
+
7829
+ | Option | Type | Description |
7830
+ |--------|------|-------------|
7831
+ | `title?` | `string` | Viewer-facing journey title. Defaults to the journey id. |
7832
+ | `startsAt?` | `string` | Optional starting step id. Defaults to the first step. |
7833
+ | `behavior?` | `"opt-in" \| "auto"` | Whether the viewer should offer or auto-open the journey. First slice supports opt-in. |
7834
+ | `steps` | `SceneJourneyStepConfig[]` | Ordered journey spine. Branches can be added later without changing this core contract. |
7835
+ | `valid?` | `boolean` | True unless any journey or step diagnostic has level "error". |
7836
+ | `diagnostics?` | `SceneJourneyDiagnostic[]` | Whole-journey diagnostics, including unresolved startsAt and step target diagnostics. |
7837
+
7838
+ **`SceneJourneyStepConfig`**
7839
+
7840
+ | Option | Type | Description |
7841
+ |--------|------|-------------|
7842
+ | `id` | `string` | Stable step id used by viewer links and Next/Back state. |
7843
+ | `title?` | `string` | Viewer-facing title. Defaults to the step id. |
7844
+ | `focus?` | `string` | Object name or slash-separated tree path to focus. |
7845
+ | `caption?` | `string` | Short optional viewer caption. |
7846
+ | `camera?` | `SceneViewCameraConfig` | Optional explicit camera for this step. When omitted, the viewer fits `focus`. |
7847
+ | `resolvedFocusId?` | `string \| null` | Resolved object id after script execution, when `focus` matched exactly one object. |
7848
+ | `resolvedFocusPath?` | `string \| null` | Resolved object tree path or name after script execution. |
7849
+ | `diagnostics?` | `SceneJourneyDiagnostic[]` | Resolution diagnostics for this step. |
7850
+
7851
+ `SceneJourneyDiagnostic`: `{ level: SceneJourneyDiagnosticLevel, message: string, stepId?: string, suggestions?: string[] }`
7852
+
7264
7853
  **`SceneLightConfig`**
7265
7854
 
7266
7855
  | Option | Type | Description |