three-rb 0.1.0 → 0.2.0

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +64 -3
  4. data/docs/browser-runtime.md +90 -24
  5. data/docs/next-work.md +9 -5
  6. data/docs/publishing.md +116 -23
  7. data/docs/release-readiness.md +5 -3
  8. data/docs/standalone-browser-app.md +102 -0
  9. data/examples/browser/README.md +6 -0
  10. data/examples/browser/composition/main.rb +41 -61
  11. data/examples/browser/cube/main.rb +4 -34
  12. data/examples/browser/cubemap/main.rb +16 -39
  13. data/examples/browser/gltf/main.rb +28 -48
  14. data/examples/browser/picking/main.rb +27 -53
  15. data/examples/browser/postprocessing/main.rb +23 -42
  16. data/examples/browser/primitives/main.rb +18 -41
  17. data/examples/browser/ruby/README.md +22 -0
  18. data/examples/browser/ruby/boot.mjs +6 -0
  19. data/examples/browser/ruby/index.html +142 -0
  20. data/examples/browser/ruby/main.rb +313 -0
  21. data/examples/browser/ruby/smoke_test.mjs +126 -0
  22. data/examples/browser/serialization/main.rb +19 -41
  23. data/examples/browser/shared/boot.mjs +37 -5
  24. data/examples/browser/textures/main.rb +21 -39
  25. data/exe/three-rb +55 -0
  26. data/lib/three/backends/threejs/materialization.rb +6 -0
  27. data/lib/three/backends/threejs/parameters.rb +17 -0
  28. data/lib/three/backends/threejs/ruby_wasm_adapter.rb +166 -59
  29. data/lib/three/backends/threejs/synchronization.rb +38 -4
  30. data/lib/three/backends/threejs.rb +24 -0
  31. data/lib/three/browser.rb +389 -0
  32. data/lib/three/constants.rb +6 -0
  33. data/lib/three/core/buffer_attribute.rb +5 -1
  34. data/lib/three/core/buffer_geometry.rb +29 -1
  35. data/lib/three/core/object3d.rb +39 -1
  36. data/lib/three/exporters/three_json_exporter.rb +3 -0
  37. data/lib/three/generators/browser_example.rb +315 -0
  38. data/lib/three/geometries/text_geometry.rb +41 -0
  39. data/lib/three/loaders/font_loader.rb +29 -0
  40. data/lib/three/loaders/three_json_loader.rb +92 -46
  41. data/lib/three/materials/material.rb +2 -1
  42. data/lib/three/math/matrix4.rb +27 -0
  43. data/lib/three/renderers/threejs_renderer.rb +19 -0
  44. data/lib/three/scenes/fog.rb +86 -0
  45. data/lib/three/scenes/scene.rb +19 -1
  46. data/lib/three/textures/texture.rb +2 -1
  47. data/lib/three/version.rb +1 -1
  48. data/lib/three.rb +4 -0
  49. data/package.json +2 -1
  50. metadata +16 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b66b39cafbc0163bffb3b177eeaf1d397ac87fbed27046ca6f5673675fe001b9
4
- data.tar.gz: 93dfc4ba66293d5090912b3082b2a84f87ba1d73d284606c9896d9a4b7f9d794
3
+ metadata.gz: ab46f2291f0b0adcb818d9a079c341333adeec066588ed09e8c189b798819725
4
+ data.tar.gz: 3ca4184c327a2abf28dc6b081a5f0e4df8b31a183942ed82da94103146ef3922
5
5
  SHA512:
6
- metadata.gz: 14776a59074de5a790244fc27c698988e7b98fb5c54abd7c8837368d8b029c1a5f3410814377aab663f466aa4e619228622590804d4b9fd261e9ced68877ce0d
7
- data.tar.gz: 8292752e152f1aa24751e5b2b7468ca641c0beaaa36c17cfc8439466408214b7fca6fae65b3e0e8226c679586fd0aa532140b6d06fa22eda0205f7abbd57699a
6
+ metadata.gz: e2c3710079194f6860b05827679bb8e5797b52164e33016f2dcf95feabffd22da475edd2640e6f76e854606785be92c27772c679b969f73808ac0821b5b8b7ea
7
+ data.tar.gz: 9edb5c3dac961c7798e45e9901f408a3896c36e7db50ddedc1cd439ec7d3e5d54fdbeface42df108f679517fed14d81e23fe9e0d0730b24147ee37aecd430f82
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 - 2026-05-17
4
+
5
+ ### Added
6
+
7
+ - Expanded `Three::Browser` helpers so browser examples can stay in Ruby without direct application-level `require "js"` or `JS.global` usage.
8
+ - Added the `three-rb browser` generator for creating standalone browser apps with Ruby-only entrypoints.
9
+ - Added a Ruby gemstone browser example with deterministic smoke coverage.
10
+ - Added scene fog and override material support.
11
+ - Added manual `Object3D` matrix support.
12
+ - Added text geometry and font loader APIs.
13
+ - Added JSON resource metadata preservation for exported and loaded scenes.
14
+
15
+ ### Changed
16
+
17
+ - Converted browser examples to the Ruby browser helper API and shared browser boot path.
18
+ - Improved browser addon error messages with more actionable guidance.
19
+ - Strengthened release checks for installed gem behavior, the installed executable, and generated browser apps.
20
+ - Expanded browser runtime and standalone app documentation.
21
+
3
22
  ## 0.1.0 - 2026-05-15
4
23
 
5
24
  ### Added
@@ -8,11 +27,12 @@
8
27
  - Mesh, instanced mesh, line, points, and sprite object support.
9
28
  - Common material APIs including basic, Lambert, Phong, standard, physical, matcap, toon, normal, shadow, line, points, and sprite materials.
10
29
  - Browser rendering through ruby.wasm and a delegated three.js backend.
30
+ - `Three::Browser` helpers and a `three-rb browser` generator for Ruby-only browser entrypoints that avoid application-level `require "js"` and `JS.global` calls.
11
31
  - Browser examples and Playwright smoke tests for cube, composition, textures, cubemap, glTF/DRACO, serialization, picking, primitives, and postprocessing.
12
32
  - Texture loading, cube textures, RGBE environment textures, glTF loading, DRACO decoder configuration, animation mixers, OrbitControls, instancing, picking, shadows, and loaded-asset traversal/disposal helpers.
13
33
  - Initial postprocessing wrappers for `EffectComposer`, `RenderPass`, `UnrealBloomPass`, `DotScreenPass`, and `OutputPass`.
14
34
  - Deterministic JSON fixture regression coverage for exporter/loader compatibility.
15
- - Release install smoke and preflight tasks for validating the built gem outside the repository load path.
35
+ - Release install smoke and preflight tasks for validating the built gem, installed executable, and generated browser app outside the repository load path.
16
36
 
17
37
  ### Notes
18
38
 
data/README.md CHANGED
@@ -24,7 +24,7 @@ Out of scope for the first public release:
24
24
 
25
25
  ## Installation
26
26
 
27
- After a release is published:
27
+ Install the released gem from RubyGems:
28
28
 
29
29
  ```sh
30
30
  gem install three-rb
@@ -36,11 +36,41 @@ Or add it to a Gemfile:
36
36
  gem "three-rb", "~> 0.1"
37
37
  ```
38
38
 
39
+ Browser rendering examples also need `pnpm`, because ruby.wasm, three.js, and three.js addons are installed from `package.json`.
40
+
39
41
  ## Quick Start
40
42
 
43
+ three-rb is browser-first. The fastest way to try the released gem is to unpack a writable copy of the gem and run one of the included browser examples:
44
+
45
+ ```sh
46
+ gem install three-rb
47
+ mkdir hello-three-rb
48
+ cd hello-three-rb
49
+ gem unpack three-rb
50
+ cd three-rb-*/
51
+ pnpm install
52
+ ruby -run -e httpd . -p 8000
53
+ ```
54
+
55
+ If Ruby reports that `webrick` is not found, install it once with `gem install webrick`. Ruby 3.0 and later no longer include WEBrick as a standard library.
56
+
57
+ ```text
58
+ http://localhost:8000/examples/browser/ruby/
59
+ ```
60
+
61
+ This page runs the Ruby entrypoint at `examples/browser/ruby/main.rb` through ruby.wasm and renders a transparent red gemstone and extruded `three-rb` title with three.js. Use `http://localhost:8000/...`; do not open the files with `file://`, because the browser runtime loads ES modules, wasm, and assets over HTTP.
62
+
63
+ After the ruby example works, inspect `examples/browser/ruby/main.rb` to see the Ruby scene code. For the smallest possible browser scene, see `examples/browser/cube/main.rb`. For a standalone app directory with your own Ruby entrypoint, see [Standalone Browser App](docs/standalone-browser-app.md).
64
+
65
+ ## Plain Ruby Check
66
+
67
+ If you only want to confirm the Ruby API loads without browser rendering, create `hello_three.rb`:
68
+
41
69
  ```ruby
42
70
  require "three"
43
71
 
72
+ puts "three-rb #{Three::VERSION}"
73
+
44
74
  scene = Three::Scene.new
45
75
  camera = Three::PerspectiveCamera.new(75, aspect: 16.0 / 9.0)
46
76
  camera.position.z = 5
@@ -50,20 +80,40 @@ material = Three::MeshBasicMaterial.new(color: 0x00ff00)
50
80
  cube = Three::Mesh.new(geometry, material)
51
81
 
52
82
  scene.add(cube)
83
+
84
+ puts scene.class
85
+ puts cube.class
86
+ ```
87
+
88
+ Run it:
89
+
90
+ ```sh
91
+ ruby hello_three.rb
92
+ ```
93
+
94
+ You should see the library load and create Ruby scene objects:
95
+
96
+ ```text
97
+ three-rb 0.1.0
98
+ Three::Scene
99
+ Three::Mesh
53
100
  ```
54
101
 
55
102
  Browser rendering is available through `Three::Renderers::ThreeJSRenderer`, which targets three.js from ruby.wasm.
56
103
 
57
- ## Browser Example
104
+ ## Browser Examples In This Repository
58
105
 
59
- Install browser dependencies, serve the repository root, and open one of the browser examples:
106
+ When developing this repository, install browser dependencies, serve the repository root, and open one of the browser examples:
60
107
 
61
108
  ```sh
62
109
  pnpm install
63
110
  ruby -run -e httpd . -p 8000
64
111
  ```
65
112
 
113
+ If `ruby -run -e httpd` reports that `webrick` is not found, run `gem install webrick` and retry. Ruby 3.0 and later require WEBrick to be installed separately.
114
+
66
115
  ```text
116
+ http://localhost:8000/examples/browser/ruby/
67
117
  http://localhost:8000/examples/browser/cube/
68
118
  http://localhost:8000/examples/browser/composition/
69
119
  http://localhost:8000/examples/browser/textures/
@@ -79,6 +129,16 @@ See [Browser Examples](examples/browser/README.md) for the feature coverage map
79
129
 
80
130
  The examples load pnpm-managed browser packages from `node_modules/`: `@ruby/3.4-wasm-wasi@2.9.4-2026-05-11-a`, `@ruby/wasm-wasi@2.9.4-2026-05-11-a`, and `three@0.184.0`.
81
131
 
132
+ To start a standalone browser app from the installed gem:
133
+
134
+ ```sh
135
+ three-rb browser examples/browser/quickstart
136
+ pnpm install
137
+ ruby -run -e httpd . -p 8000
138
+ ```
139
+
140
+ The generated Ruby entrypoint uses `Three::Browser.run`, so ordinary scene code does not need `require "js"` or `JS.global`.
141
+
82
142
  Run the optional browser smoke tests:
83
143
 
84
144
  ```sh
@@ -148,6 +208,7 @@ bundle exec rake release:preflight
148
208
  - [Next Work](docs/next-work.md)
149
209
  - [Browser Runtime](docs/browser-runtime.md)
150
210
  - [Browser Examples](examples/browser/README.md)
211
+ - [Standalone Browser App](docs/standalone-browser-app.md)
151
212
  - [Loaded Asset Traversal and Disposal Design](docs/loaded-assets-design.md)
152
213
  - [Release Readiness](docs/release-readiness.md)
153
214
  - [Publishing](docs/publishing.md)
@@ -11,7 +11,7 @@ A browser page using three-rb needs four pieces:
11
11
  1. An HTTP-served page with a canvas element.
12
12
  2. An import map or bundler setup that resolves ruby.wasm, three.js, and three.js addon modules.
13
13
  3. A JavaScript boot module that exposes the required three.js constructors to Ruby and starts ruby.wasm.
14
- 4. A Ruby entrypoint that waits for three.js, requires `three`, creates a scene, and renders through `Three::Renderers::ThreeJSRenderer`.
14
+ 4. A Ruby entrypoint that requires `three`, creates a scene inside `Three::Browser.run`, and renders through `Three::Renderers::ThreeJSRenderer`.
15
15
 
16
16
  Do not use `file://` for browser runs. The examples assume an HTTP server because browser module loading, wasm loading, and asset loading all need stable URL resolution.
17
17
 
@@ -84,45 +84,45 @@ The shared helper also installs optional render helpers used by smoke tests to d
84
84
 
85
85
  Write a custom boot module when the application has different asset paths, a bundler output path, a smaller addon set, a custom status/error UI, or a deployment layout that does not match the repository root. Keep the same global constructor contract unless the Ruby backend gains a different injection API.
86
86
 
87
+ If a Ruby wrapper needs an addon constructor that the boot module did not register, three-rb raises an error naming the missing `globalThis.THREE_*` value and the import/assignment lines to add to `boot.mjs`.
88
+
87
89
  ## Ruby Entrypoint
88
90
 
89
- The Ruby side should wait for the JavaScript boot module, require the library, create a scene, and attach the renderer to the existing canvas:
91
+ The Ruby side should require the library, create a scene inside `Three::Browser.run`, and attach the renderer to the existing canvas. `Three::Browser.run` waits for the JavaScript boot module and handles the example status/error UI, so application scene code does not need to call `JS.global` directly:
90
92
 
91
93
  ```ruby
92
- require "js"
93
-
94
- JS.global[:__threeReady].await
95
-
96
94
  require_relative "../../../lib/three"
97
95
 
98
- scene = Three::Scene.new
99
- camera = Three::PerspectiveCamera.new(70, aspect: 1.0, near: 0.1, far: 100)
100
- camera.position.z = 3
96
+ Three::Browser.run(starting: "Starting Ruby scene") do |app|
97
+ scene = Three::Scene.new
98
+ camera = Three::PerspectiveCamera.new(70, aspect: 1.0, near: 0.1, far: 100)
99
+ camera.position.z = 3
101
100
 
102
- mesh = Three::Mesh.new(
103
- Three::BoxGeometry.new(1, 1, 1),
104
- Three::MeshBasicMaterial.new(color: 0x4ed08f)
105
- )
106
- scene.add(mesh)
101
+ mesh = Three::Mesh.new(
102
+ Three::BoxGeometry.new(1, 1, 1),
103
+ Three::MeshBasicMaterial.new(color: 0x4ed08f)
104
+ )
105
+ scene.add(mesh)
107
106
 
108
- renderer = Three::Renderers::ThreeJSRenderer.new(
109
- canvas: "#scene",
110
- antialias: true,
111
- alpha: false
112
- )
107
+ renderer = Three::Renderers::ThreeJSRenderer.new(
108
+ canvas: "#scene",
109
+ antialias: true,
110
+ alpha: false
111
+ )
113
112
 
114
- renderer.set_size(640, 480)
115
- renderer.render(scene, camera)
113
+ app.resize_renderer(renderer, camera)
114
+ renderer.render(scene, camera)
115
+ end
116
116
  ```
117
117
 
118
118
  `canvas:` may be a CSS selector string or a JavaScript canvas object. Selector strings are resolved with `document.querySelector`.
119
119
 
120
- For responsive layouts, follow the browser examples: read the containing element's client width and height, update the camera aspect/projection matrix, call `renderer.set_size(width, height)`, and listen for `resize`.
120
+ For responsive layouts, use `app.resize_renderer(renderer, camera)` for perspective cameras. For custom orthographic sizing, pass a block and update the camera before the helper sets the renderer size.
121
121
 
122
- For animation, call `renderer.animation_loop`:
122
+ For animation, prefer `app.animation_loop(renderer)`. It delegates to the renderer's browser animation loop and keeps the entrypoint on the `Three::Browser` API surface:
123
123
 
124
124
  ```ruby
125
- renderer.animation_loop do
125
+ app.animation_loop(renderer) do
126
126
  mesh.rotation.x += 0.01
127
127
  mesh.rotation.y += 0.015
128
128
  renderer.render(scene, camera)
@@ -131,6 +131,71 @@ end
131
131
 
132
132
  For postprocessing, render through `composer.render(scene, camera)` instead of `renderer.render(scene, camera)` after configuring `Three::Postprocessing::EffectComposer` and its passes.
133
133
 
134
+ ## Three::Browser API
135
+
136
+ `Three::Browser` is the public convenience layer for browser entrypoints. It keeps ordinary scene code Ruby-only while the JavaScript boot module handles ruby.wasm and ES module setup. Treat these methods as the first-choice browser API before reaching for direct JS bridge calls.
137
+
138
+ Lifecycle and sizing:
139
+
140
+ - `Three::Browser.run(starting: "...") { |app| ... }` waits for the boot module, creates an application helper, updates the status UI, and reports boot failures.
141
+ - `app.resize_renderer(renderer, camera)` reads the default `#viewport`, updates a perspective camera's aspect/projection matrix, calls `renderer.set_size`, and registers a resize listener.
142
+ - `app.resize_renderer(renderer, camera) { |width, height, aspect| ... }` supports custom camera sizing such as orthographic views.
143
+ - `app.on_resize { |width, height, aspect| ... }` registers a lower-level resize callback and runs it once immediately.
144
+ - `app.animation_loop(renderer) { ... }` delegates to `renderer.animation_loop`.
145
+ - `app.animation_loop { |time| ... }` uses `requestAnimationFrame` directly when a renderer loop is not appropriate.
146
+
147
+ DOM and events:
148
+
149
+ - `app.element(renderer.dom_element)` wraps a browser element for event handling and pointer coordinate helpers.
150
+ - `element.on("click") { |event| ... }` registers a DOM event without exposing `JS.global`.
151
+ - `element.pointer_ndc(event)` converts pointer events to normalized device coordinates for `Raycaster#set_from_camera`.
152
+ - `app.pointer_ndc(event, target: "#viewport")` performs the same conversion against a selector, raw element, or `Three::Browser::Element`.
153
+ - `app.on_pointer("click", target: renderer.dom_element) { |event, x, y| ... }` registers a pointer event and yields normalized device coordinates.
154
+ - `app.on_key("keydown", key: "Escape") { |event| ... }` registers keyboard events on `window` by default. Omit `key:` to receive every event.
155
+
156
+ State and diagnostics:
157
+
158
+ - `app.storage` wraps `localStorage`; `app.storage(:session)` wraps `sessionStorage`.
159
+ - `storage.set(:name, value)`, `storage.get(:name)`, `storage.delete(:name)`, `storage.clear`, `storage.length`, and `storage.key(index)` cover simple browser storage access.
160
+ - `app.expose(...)`, `app.set`, `app.get`, and `app.increment` are intended for diagnostics, smoke tests, and small browser-visible state values.
161
+
162
+ Escape hatch:
163
+
164
+ - `Three::Browser.js` returns `JS.global` and is the explicit escape hatch for advanced browser integrations that three-rb does not wrap yet. Keep usage isolated and prefer adding a Ruby helper when the pattern is reusable.
165
+
166
+ Example pointer picking:
167
+
168
+ ```ruby
169
+ pointer = Three::Vector2.new
170
+ raycaster = Three::Raycaster.new(backend: renderer.backend)
171
+
172
+ app.on_pointer("click", target: renderer.dom_element) do |_event, x, y|
173
+ pointer.set(x, y)
174
+ raycaster.set_from_camera(pointer, camera)
175
+ end
176
+ ```
177
+
178
+ ## Generator
179
+
180
+ For standalone projects, install the gem and generate a Ruby-only browser example:
181
+
182
+ ```sh
183
+ three-rb browser examples/browser/quickstart
184
+ ```
185
+
186
+ The generator copies the browser runtime files, `package.json`, `pnpm-lock.yaml`, and `lib/` into the served project root, then creates `index.html`, `boot.mjs`, `main.rb`, and `README.md` for the example.
187
+
188
+ ## JavaScript Escape Hatch
189
+
190
+ Most browser scene code should not need `require "js"` or `JS.global`. JavaScript is still the right layer for:
191
+
192
+ - The boot module that imports ES modules and starts ruby.wasm.
193
+ - Custom HTML UI outside the helpers in `Three::Browser`.
194
+ - Browser APIs that three-rb does not wrap yet, such as WebSocket, drag/drop, pointer lock, fullscreen, WebXR session setup, or application-specific JavaScript callbacks.
195
+ - three.js addons that do not yet have Ruby wrappers.
196
+
197
+ When a feature needs direct JavaScript access, prefer adding a small Ruby wrapper or `Three::Browser` helper first. Use `Three::Browser.js` as the explicit escape hatch for application-specific integrations, and keep it isolated from scene construction code.
198
+
134
199
  ## Current Limits
135
200
 
136
201
  The browser runtime intentionally does not promise full three.js compatibility yet.
@@ -138,6 +203,7 @@ The browser runtime intentionally does not promise full three.js compatibility y
138
203
  - There is no Ruby-native OpenGL, Vulkan, WebGPU, or software renderer.
139
204
  - Rendering is delegated to three.js through ruby.wasm.
140
205
  - Addon wrappers only work when their JavaScript constructors are registered on `globalThis`.
206
+ - The JavaScript boot module is still required to import ES modules and start ruby.wasm, even though ordinary Ruby scene entrypoints can stay Ruby-only.
141
207
  - The first public scope does not include stable APIs for every loader, material, render target, postprocessing pass, WebGPU, or XR workflow.
142
208
  - `examples/browser/shared/boot.mjs` is a reference implementation for this repository's examples, not a separate stable package.
143
209
  - `preserveDrawingBuffer: true` is useful for deterministic canvas smoke tests, but applications do not need it by default.
data/docs/next-work.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This document is the resume point for the next implementation session. It is intentionally narrower than `docs/implementation-plan.md`: use it to decide what to do next after conversation context is lost.
4
4
 
5
- Last updated: 2026-05-14.
5
+ Last updated: 2026-05-17.
6
6
 
7
7
  ## Current Position
8
8
 
@@ -26,6 +26,9 @@ Recent completed work:
26
26
  - `Sprite` / `SpriteMaterial` support with textured billboard marker sync, JSON export/load, resource disposal, and primitives browser smoke coverage.
27
27
  - Saved JSON fixture coverage now includes `Sprite` / `SpriteMaterial` so the representative exporter/loader regression fixture matches the current primitive surface.
28
28
  - Completed public API and documentation consistency pass for the current browser-first alpha scope.
29
+ - Browser examples were moved to Ruby-only entrypoints that use `Three::Browser` helpers instead of application-level `require "js"` or `JS.global`.
30
+ - Added the `three-rb browser` executable and generator for standalone Ruby-only browser examples.
31
+ - Release install smoke now validates the installed executable and generated browser app shape outside the repository load path.
29
32
  - Local release gate and latest `main` CI are green for the `0.1.0` release candidate.
30
33
 
31
34
  Do not start Phase 9 native renderer work yet. The implementation plan still recommends keeping browser rendering delegated to three.js through ruby.wasm until the browser-first API is more stable.
@@ -39,12 +42,13 @@ This is the best next step because:
39
42
  - The representative JSON fixture covers the current primitive/material surface, including `Sprite` / `SpriteMaterial`.
40
43
  - The public docs now cover release readiness, publishing, browser example coverage, browser runtime boot contract, and the current implemented API scope.
41
44
  - `lib/three/version.rb` already contains `0.1.0`.
42
- - `CHANGELOG.md` intentionally remains `## 0.1.0 - Unreleased` until the owner confirms the release date, but its contents should continue to reflect the current release-candidate surface.
45
+ - `CHANGELOG.md` currently records `## 0.1.0 - 2026-05-15`; if the release date changes before publishing, update that heading during release metadata finalization.
46
+ - The release candidate now includes the Ruby-only browser generator and installed-generator smoke coverage.
43
47
  - Further feature scope should wait until after the `0.1.0` release unless an already-advertised example breaks.
44
48
 
45
49
  ## Scope
46
50
 
47
- For deferred-release maintenance, keep `CHANGELOG.md`, `docs/release-readiness.md`, and this resume point aligned with the implemented release-candidate surface. Do not date the changelog, tag, push, or publish until the release owner confirms the release window.
51
+ For deferred-release maintenance, keep `CHANGELOG.md`, `docs/release-readiness.md`, and this resume point aligned with the implemented release-candidate surface. Do not tag, push, or publish until the release owner confirms the release window. If the confirmed release date differs from the current changelog heading, update it as release metadata work.
48
52
 
49
53
  When the owner is ready to publish, follow `docs/publishing.md`: confirm the release date, update `CHANGELOG.md`, run `bundle exec rake release:preflight`, commit the metadata update, publish the gem, tag `v0.1.0`, push `main` and the tag, then verify installation from RubyGems.
50
54
 
@@ -60,8 +64,8 @@ Candidate targets, in recommended order when there is no stronger product signal
60
64
 
61
65
  ## Suggested Implementation Plan
62
66
 
63
- 1. While release is deferred, keep the unreleased changelog and release-readiness docs current without changing the version, date, tag, or published artifact.
64
- 2. When release is confirmed, finalize `CHANGELOG.md` with the confirmed release date.
67
+ 1. While release is deferred, keep the current changelog heading, release-readiness docs, and publishing checklist aligned with the release candidate without changing the version, tag, or published artifact.
68
+ 2. When release is confirmed, adjust `CHANGELOG.md` only if the confirmed release date differs from the current heading.
65
69
  3. Run `bundle exec rake release:preflight`.
66
70
  4. Commit the release metadata with a message that does not include co-author trailers.
67
71
  5. Publish and tag using `docs/publishing.md`.
data/docs/publishing.md CHANGED
@@ -1,64 +1,157 @@
1
1
  # Publishing
2
2
 
3
- This document records the manual steps for publishing `three-rb`. Do not run the publish step until the release owner has confirmed the version, changelog date, and RubyGems credentials.
3
+ This is the manual release checklist for publishing `three-rb` to RubyGems.
4
4
 
5
- ## Preflight
5
+ `three-rb` is the RubyGems package name. The public Ruby require path is `three`, with `three-rb` kept as a compatibility require shim.
6
6
 
7
- Start from a clean `main` branch:
7
+ Do not publish until the release owner has confirmed:
8
+
9
+ - The target version.
10
+ - The release date for `CHANGELOG.md`.
11
+ - RubyGems credentials with MFA enabled.
12
+
13
+ ## Release Checklist
14
+
15
+ Run these commands from the repository root.
8
16
 
9
17
  ```sh
18
+ cd /path/to/three-rb
19
+ ```
20
+
21
+ Choose the target version before starting. If this is not already the version in `lib/three/version.rb`, update that file first.
22
+
23
+ ```ruby
24
+ module Three
25
+ VERSION = "X.Y.Z"
26
+ end
27
+ ```
28
+
29
+ Set the version being released from the code. All later commands use this value.
30
+
31
+ ```sh
32
+ VERSION="$(ruby -Ilib -e 'require "three/version"; print Three::VERSION')"
33
+ GEM_FILE="three-rb-${VERSION}.gem"
34
+ TAG="v${VERSION}"
35
+ echo "$VERSION"
36
+ echo "$GEM_FILE"
37
+ echo "$TAG"
38
+ ```
39
+
40
+ Start from the latest clean `main`:
41
+
42
+ ```sh
43
+ git switch main
44
+ git pull --ff-only origin main
10
45
  git status --short
46
+ ```
47
+
48
+ `git status --short` should be empty before release metadata changes.
49
+
50
+ Install and audit the Node/browser dependencies:
51
+
52
+ ```sh
11
53
  pnpm install --frozen-lockfile --ignore-scripts
12
54
  pnpm audit --audit-level moderate
13
55
  pnpm audit signatures
14
56
  pnpm exec playwright install chromium
57
+ ```
58
+
59
+ Run the full non-publishing release gate:
60
+
61
+ ```sh
15
62
  bundle exec rake release:preflight
16
63
  ```
17
64
 
18
65
  `release:preflight` runs Ruby tests, builds and installs the gem into a temporary `GEM_HOME`, runs the install smoke test, runs all browser smoke tests, and builds the gem. It does not publish anything.
19
66
 
20
- ## Prepare Release Metadata
67
+ Confirm release metadata:
68
+
69
+ ```sh
70
+ test "$(ruby -Ilib -e 'require "three/version"; print Three::VERSION')" = "$VERSION"
71
+ ```
72
+
73
+ The command must exit successfully. If it fails, fix `VERSION` or `lib/three/version.rb` before continuing. Do not build or publish a gem until the code version is the intended release version.
21
74
 
22
- For version `0.1.0`:
75
+ Update `CHANGELOG.md` from an unreleased heading to the final release date. Replace `$VERSION` with the actual version value:
76
+
77
+ ```md
78
+ ## $VERSION - YYYY-MM-DD
79
+ ```
23
80
 
24
- 1. Confirm `lib/three/version.rb` contains `VERSION = "0.1.0"`.
25
- 2. Change `CHANGELOG.md` from `## 0.1.0 - Unreleased` to the release date.
26
- 3. Run `bundle exec rake release:preflight` again.
27
- 4. Commit the metadata update:
81
+ Run the full gate again after metadata changes:
28
82
 
29
83
  ```sh
30
- git add CHANGELOG.md lib/three/version.rb
31
- git commit -m "Prepare 0.1.0 release"
84
+ bundle exec rake release:preflight
32
85
  ```
33
86
 
34
- Skip `lib/three/version.rb` in the commit if the version was already correct.
87
+ Commit the release metadata, including `lib/three/version.rb` when it changed. Do not add co-author trailers.
35
88
 
36
- ## Publish
89
+ ```sh
90
+ git status --short
91
+ git add -A
92
+ git commit -m "Prepare ${VERSION} release"
93
+ ```
37
94
 
38
- Publishing requires RubyGems credentials with MFA enabled.
95
+ Build the exact gem artifact that will be pushed:
39
96
 
40
97
  ```sh
41
98
  gem build three-rb.gemspec
42
- gem push three-rb-0.1.0.gem
43
99
  ```
44
100
 
45
- After RubyGems accepts the gem, create and push the git tag:
101
+ The build output must include the selected version and gem file:
102
+
103
+ ```text
104
+ Name: three-rb
105
+ Version: $VERSION
106
+ File: $GEM_FILE
107
+ ```
108
+
109
+ Publish to RubyGems:
46
110
 
47
111
  ```sh
48
- git tag -a v0.1.0 -m "Release 0.1.0"
49
- git push origin main
50
- git push origin v0.1.0
112
+ gem push "$GEM_FILE"
51
113
  ```
52
114
 
53
- If `gem push` fails, do not create the tag until the published artifact is confirmed.
115
+ RubyGems MFA is required because the gemspec sets `rubygems_mfa_required`. If RubyGems reports that MFA must be enabled, enable MFA on the publishing account and rerun the same `gem push` command.
116
+
117
+ If `gem push` fails for any reason, stop. Do not create or push the git tag until RubyGems confirms the gem was registered.
54
118
 
55
- ## Post-publish
119
+ After RubyGems prints a successful registration message, create and push the git tag:
120
+
121
+ ```sh
122
+ git tag -a "$TAG" -m "Release ${VERSION}"
123
+ git push origin main
124
+ git push origin "$TAG"
125
+ ```
56
126
 
57
127
  Verify the public install path from a clean temporary directory:
58
128
 
59
129
  ```sh
60
- gem install three-rb -v 0.1.0
130
+ tmpdir="$(mktemp -d)"
131
+ cd "$tmpdir"
132
+ gem install three-rb -v "$VERSION"
61
133
  ruby -e 'require "three"; puts Three::VERSION'
134
+ ruby -e 'require "three-rb"; puts Three::VERSION'
135
+ three-rb --help
136
+ three-rb browser examples/browser/quickstart
137
+ test -f examples/browser/quickstart/main.rb
138
+ ruby -e 'main = File.read("examples/browser/quickstart/main.rb"); abort "generated Ruby entrypoint used JS bridge" if main.include?("require \"js\"") || main.include?("JS.global"); puts "generated Ruby entrypoint is Ruby-only"'
139
+ ```
140
+
141
+ Both Ruby commands must print:
142
+
143
+ ```text
144
+ $VERSION
145
+ ```
146
+
147
+ The `three-rb browser` command must create `examples/browser/quickstart/main.rb`, and the final Ruby command must print:
148
+
149
+ ```text
150
+ generated Ruby entrypoint is Ruby-only
62
151
  ```
63
152
 
64
- Then update issue tracker or release notes with the published RubyGems URL.
153
+ Then update issue tracker or release notes with the published RubyGems URL:
154
+
155
+ ```text
156
+ https://rubygems.org/gems/three-rb
157
+ ```
@@ -16,12 +16,13 @@ The `0.1.0` target is complete when all of these are true:
16
16
 
17
17
  - Scope is frozen to the browser-first alpha surface in this document and `README.md`; no additional material, loader, render-target, postprocessing, WebGPU, XR, native-renderer, or broad compatibility work is required for the release unless an already-advertised example is broken.
18
18
  - Public API documentation matches the implemented, tested Ruby API surface loaded by `require "three"`.
19
+ - Ruby-authored browser examples use `Three::Browser` helpers instead of application-level `require "js"` and `JS.global`, and the `three-rb browser` generator can create the same Ruby-only entrypoint shape for standalone apps.
19
20
  - Snake-case Ruby methods are the documented API style. Broad camelCase three.js compatibility aliases are deferred until after `0.1.0`.
20
21
  - `Three::Exporters::ThreeJSONExporter` and `Three::Loaders::ThreeJSONLoader` continue to round-trip Ruby-authored scenes covered by the saved `test/fixtures/scene_export_v1.json` regression fixture.
21
22
  - JavaScript-loaded assets remain opaque `ExternalObject3D` roots for `0.1.0`; transform-level use, renderer traversal helpers, animation mixer usage, and explicit subtree disposal are in scope, while Ruby child mutation inside loaded external roots is out of scope.
22
23
  - Every browser example advertised in `README.md` and `examples/browser/README.md` has a matching deterministic Playwright smoke command, and `pnpm test:browser` runs them all.
23
24
  - The required gate below passes locally before tagging, and the CI workflow passes on the release commit.
24
- - Release metadata is final: `lib/three/version.rb` contains `0.1.0`, `CHANGELOG.md` has either the unreleased heading during development or the final release date before publishing, and `docs/publishing.md` has the manual publish steps.
25
+ - Release metadata is final: `lib/three/version.rb` contains the exact target version, `CHANGELOG.md` has either the unreleased heading during development or the final release date before publishing, and `docs/publishing.md` has the manual publish steps.
25
26
 
26
27
  ## Public Scope
27
28
 
@@ -29,6 +30,7 @@ Included in the first public scope:
29
30
 
30
31
  - Ruby object model for scenes, groups, transforms, cameras, lights, geometries, non-mesh primitives including sprites, common materials including matcap, toon, sprite, and shadow materials, textures, and common math primitives.
31
32
  - Browser rendering through `Three::Renderers::ThreeJSRenderer`, ruby.wasm, and `three@0.184.0`.
33
+ - `Three::Browser` helpers and the `three-rb browser` executable for generating standalone Ruby-only browser examples.
32
34
  - Dirty-tracked synchronization from Ruby objects into three.js handles.
33
35
  - JSON export/load for Ruby-authored scenes.
34
36
  - JavaScript-delegated browser integrations for textures, cube maps, RGBE environment maps, glTF, DRACO, animation mixers, OrbitControls, raycasting, instancing, shadows, and initial postprocessing with composer/render/bloom/dot-screen/output passes.
@@ -67,7 +69,7 @@ The release is acceptable when:
67
69
  - The required gate passes locally and in CI.
68
70
  - `CHANGELOG.md` describes the release as unreleased or tagged with the final date.
69
71
  - README documents the browser-first alpha scope and unsupported areas.
70
- - `bundle exec rake release:gem_smoke` proves the built gem can be installed into a temporary `GEM_HOME` and used without the repository `lib/` path.
72
+ - `bundle exec rake release:gem_smoke` proves the built gem can be installed into a temporary `GEM_HOME`, used without the repository `lib/` path, and run the installed `three-rb browser` generator.
71
73
  - `bundle exec rake release:preflight` proves the Ruby tests, install smoke, browser smoke tests, and gem build pass without publishing.
72
74
  - Browser smoke tests cover every advertised browser example.
73
75
 
@@ -77,7 +79,7 @@ Before expanding feature scope, prefer:
77
79
 
78
80
  1. Keep release checks fast and deterministic.
79
81
  2. Keep fixture-based JSON export/load regression tests current.
80
- 3. Keep `CHANGELOG.md` accurate while it remains under the `0.1.0 - Unreleased` heading.
82
+ 3. Keep `CHANGELOG.md` accurate while the next release remains under an unreleased heading.
81
83
  4. Improve public docs around browser examples, browser boot, and unsupported APIs; see `docs/next-work.md`.
82
84
  5. Add new material classes, postprocessing passes, render targets, or loaders only when a dedicated example and smoke test need them.
83
85
  6. Treat KTX2, WebGPU, WebXR, and native rendering as post-0.1 planning items.