nodehammer 0.2.0rc1__tar.gz → 0.2.0rc3__tar.gz

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 (447) hide show
  1. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.gitignore +1 -0
  2. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/CMakeLists.txt +142 -24
  3. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/Justfile +71 -2
  4. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/PKG-INFO +7 -5
  5. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/README.md +4 -4
  6. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/build.sh +1 -0
  7. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/Dependencies.cmake +26 -2
  8. nodehammer-0.2.0rc3/cmake/Version.cmake +88 -0
  9. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/conanfile.py +13 -1
  10. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/cli.hpp +22 -0
  11. nodehammer-0.2.0rc3/packaging/plugins/nh_web_pin.py +92 -0
  12. nodehammer-0.2.0rc3/packaging/web/.gitignore +7 -0
  13. nodehammer-0.2.0rc3/packaging/web/LICENSE +21 -0
  14. nodehammer-0.2.0rc3/packaging/web/README.md +32 -0
  15. nodehammer-0.2.0rc3/packaging/web/pyproject.toml +63 -0
  16. nodehammer-0.2.0rc3/packaging/web/src/nodehammer_web/__init__.py +79 -0
  17. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/pyproject.toml +49 -1
  18. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/python/nodehammer/cli.py +48 -1
  19. nodehammer-0.2.0rc3/recipes/cli11/conandata.yml +6 -0
  20. nodehammer-0.2.0rc3/recipes/cli11/conanfile.py +71 -0
  21. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/build_pages_site.sh +7 -4
  22. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/check_em_js_closure.py +1 -1
  23. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/check_pages_site.py +20 -2
  24. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/check_wasm_module_linkage.py +3 -3
  25. nodehammer-0.2.0rc3/scripts/check_web_serve.py +149 -0
  26. nodehammer-0.2.0rc3/scripts/check_wheel_pair.py +110 -0
  27. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/stage_wasm_viewer.sh +48 -10
  28. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_viewer_native.cpp +37 -27
  29. nodehammer-0.2.0rc3/src/cli/cmd_viewer_web.cpp +303 -0
  30. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/run.cpp +6 -0
  31. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/run_internal.hpp +48 -2
  32. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/diagnostic_codes.hpp +13 -0
  33. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/python/bindings.cpp +15 -2
  34. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/app.cpp +4 -4
  35. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform.hpp +1 -1
  36. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform_web.cpp +1 -1
  37. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/menu_bar.cpp +1 -1
  38. nodehammer-0.2.0rc3/src/web/executable_path.cpp +76 -0
  39. nodehammer-0.2.0rc3/src/web/open_browser.cpp +68 -0
  40. nodehammer-0.2.0rc3/src/web/open_browser.hpp +23 -0
  41. nodehammer-0.2.0rc3/src/web/runtime_locator.cpp +267 -0
  42. nodehammer-0.2.0rc3/src/web/runtime_locator.hpp +122 -0
  43. nodehammer-0.2.0rc3/src/web/stage.cpp +188 -0
  44. nodehammer-0.2.0rc3/src/web/stage.hpp +62 -0
  45. nodehammer-0.2.0rc3/src/web/static_server.cpp +140 -0
  46. nodehammer-0.2.0rc3/src/web/static_server.hpp +73 -0
  47. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/CMakeLists.txt +18 -17
  48. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/python/test_cli.py +68 -0
  49. nodehammer-0.2.0rc3/tests/web/test_runtime_locator.cpp +382 -0
  50. nodehammer-0.2.0rc3/tests/web/test_stage.cpp +171 -0
  51. nodehammer-0.2.0rc3/tests/web/test_static_server.cpp +220 -0
  52. nodehammer-0.2.0rc1/cmake/Version.cmake +0 -51
  53. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.clang-format +0 -0
  54. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.clang-tidy +0 -0
  55. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.gitattributes +0 -0
  56. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.pre-commit-config.yaml +0 -0
  57. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.vscode/launch.json +0 -0
  58. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.vscode/settings.json +0 -0
  59. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/.vscode/tasks.json +0 -0
  60. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/LICENSE +0 -0
  61. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/LICENSES/MIT.txt +0 -0
  62. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/LICENSES/OFL-1.1.txt +0 -0
  63. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/LICENSES/Zlib.txt +0 -0
  64. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/REUSE.toml +0 -0
  65. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/STYLE.md +0 -0
  66. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/bench-summary.py +0 -0
  67. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/bench.sh +0 -0
  68. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/check_shared_exports.py +0 -0
  69. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/shared-build.sh +0 -0
  70. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/shared_consumer/CMakeLists.txt +0 -0
  71. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/shared_consumer/main.cpp +0 -0
  72. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/ci/verify-shared-install.sh +0 -0
  73. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/CompilerOptions.cmake +0 -0
  74. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/PicProbe.cmake +0 -0
  75. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/Sokol.cmake +0 -0
  76. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/ThirdPartyLicenses.cmake +0 -0
  77. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/nodehammer-config.cmake.in +0 -0
  78. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/patch_sokol_wgpu.cmake +0 -0
  79. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/pic_probe.cpp +0 -0
  80. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/validate_shader_hlsl.cmake +0 -0
  81. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/cmake/validate_shader_wgsl.cmake +0 -0
  82. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/full_example.toml +0 -0
  83. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/include_bad_path.toml +0 -0
  84. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/include_basic.toml +0 -0
  85. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/include_cycle.toml +0 -0
  86. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/include_diamond.toml +0 -0
  87. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/include_nested.toml +0 -0
  88. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/cycle_a.toml +0 -0
  89. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/cycle_b.toml +0 -0
  90. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/diamond_base.toml +0 -0
  91. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/diamond_left.toml +0 -0
  92. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/diamond_right.toml +0 -0
  93. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/materials.toml +0 -0
  94. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/nested_inner.toml +0 -0
  95. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/nested_outer.toml +0 -0
  96. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/includes/rules.toml +0 -0
  97. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/invalid_bad_tolerance.toml +0 -0
  98. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/invalid_missing_material_ref.toml +0 -0
  99. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/calorimeters.lua +0 -0
  100. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/kitchen_sink.lua +0 -0
  101. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/lib/constants.lua +0 -0
  102. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/materials.lua +0 -0
  103. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/muon.lua +0 -0
  104. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd/base.lua +0 -0
  105. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd/calorimeters.lua +0 -0
  106. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd/materials.lua +0 -0
  107. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd/muon.lua +0 -0
  108. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd/tracker.lua +0 -0
  109. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/odd.lua +0 -0
  110. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/parity.lua +0 -0
  111. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/parity.toml +0 -0
  112. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/lua/tracker.lua +0 -0
  113. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/minimal.toml +0 -0
  114. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/base.toml +0 -0
  115. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/calorimeters.toml +0 -0
  116. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/calorimeters_simple.toml +0 -0
  117. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/materials.toml +0 -0
  118. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/muon.toml +0 -0
  119. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/muon_simple.toml +0 -0
  120. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/simple_materials.toml +0 -0
  121. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd/tracker.toml +0 -0
  122. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd.toml +0 -0
  123. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_drop_coincident_faces.toml +0 -0
  124. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_merged.toml +0 -0
  125. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_simple.toml +0 -0
  126. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_ecal_barrel_stave.toml +0 -0
  127. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_hcal_barrel_stave.toml +0 -0
  128. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_long_strip_barrel_module.toml +0 -0
  129. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_long_strip_barrel_stave.toml +0 -0
  130. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_muon_barrel_chamber.toml +0 -0
  131. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_pixel_barrel_module.toml +0 -0
  132. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_pixel_barrel_stave.toml +0 -0
  133. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_short_strip_barrel_module.toml +0 -0
  134. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_single_short_strip_barrel_stave.toml +0 -0
  135. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/configs/odd_tracker.toml +0 -0
  136. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/dd4hep/sensitive_detector.xml +0 -0
  137. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/fixtures/dd4hep/simple_box.xml +0 -0
  138. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/build.hpp +0 -0
  139. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/config.hpp +0 -0
  140. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/diagnostics.hpp +0 -0
  141. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/render_scene.hpp +0 -0
  142. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/semantic_scene.hpp +0 -0
  143. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/version.hpp.in +0 -0
  144. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/include/nodehammer/visibility.hpp +0 -0
  145. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/profiles/emscripten +0 -0
  146. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/python/nodehammer/__init__.py +0 -0
  147. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/python/nodehammer/__main__.py +0 -0
  148. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/python/nodehammer/py.typed +0 -0
  149. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/imgui/CMakeLists.txt +0 -0
  150. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/imgui/conandata.yml +0 -0
  151. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/imgui/conanfile.py +0 -0
  152. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/implot/CMakeLists.txt +0 -0
  153. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/implot/conandata.yml +0 -0
  154. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/implot/conanfile.py +0 -0
  155. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/nfd/conandata.yml +0 -0
  156. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/nfd/conanfile.py +0 -0
  157. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/sokol/conandata.yml +0 -0
  158. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/sokol/conanfile.py +0 -0
  159. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/sokol-shdc/conandata.yml +0 -0
  160. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/recipes/sokol-shdc/conanfile.py +0 -0
  161. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/schemas/render.fbs +0 -0
  162. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/schemas/semantic.fbs +0 -0
  163. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/check_cli_no_exit.py +0 -0
  164. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/make_nhproj.py +0 -0
  165. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/serve_nocache.py +0 -0
  166. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/wasm_compute_smoke.js +0 -0
  167. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/scripts/windows-msvc-cmake.ps1 +0 -0
  168. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/CMakeLists.txt +0 -0
  169. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/ao.glsl +0 -0
  170. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/ao_denoise.glsl +0 -0
  171. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/blit.glsl +0 -0
  172. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/composite.glsl +0 -0
  173. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/depth_helpers.glsl.h +0 -0
  174. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/ibl_bake.glsl +0 -0
  175. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/shaders/scene.glsl +0 -0
  176. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/build.cpp +0 -0
  177. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/config.cpp +0 -0
  178. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/diagnostics.cpp +0 -0
  179. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/handles.hpp +0 -0
  180. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/render_scene.cpp +0 -0
  181. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/api/semantic_scene.cpp +0 -0
  182. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cli_common.hpp +0 -0
  183. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_config_flatten.cpp +0 -0
  184. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_config_lua.cpp +0 -0
  185. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_convert.cpp +0 -0
  186. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_dump_render.cpp +0 -0
  187. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_dump_semantic.cpp +0 -0
  188. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_inspect.cpp +0 -0
  189. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/cmd_validate_config.cpp +0 -0
  190. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/main.cpp +0 -0
  191. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/pager.cpp +0 -0
  192. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/cli/pager.hpp +0 -0
  193. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/color_parse.cpp +0 -0
  194. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/color_parse.hpp +0 -0
  195. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_ast.hpp +0 -0
  196. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_enums.hpp +0 -0
  197. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_keys.hpp +0 -0
  198. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_loader.cpp +0 -0
  199. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_loader.hpp +0 -0
  200. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_validator.cpp +0 -0
  201. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_validator.hpp +0 -0
  202. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_writer.cpp +0 -0
  203. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/config_writer.hpp +0 -0
  204. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/predicate_parser.cpp +0 -0
  205. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/config/predicate_parser.hpp +0 -0
  206. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/file_io.hpp +0 -0
  207. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/glm_json.hpp +0 -0
  208. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/markup.cpp +0 -0
  209. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/markup.hpp +0 -0
  210. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/overloaded.hpp +0 -0
  211. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/timing.hpp +0 -0
  212. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/detail/zstd_io.hpp +0 -0
  213. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/diagnostics.hpp +0 -0
  214. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/export_resolve.cpp +0 -0
  215. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/export_resolve.hpp +0 -0
  216. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/dd4hep/semantic/importer.cpp +0 -0
  217. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/dd4hep/semantic/importer.hpp +0 -0
  218. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/render/flatbuffer.cpp +0 -0
  219. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/render/flatbuffer.hpp +0 -0
  220. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/exporter.cpp +0 -0
  221. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/exporter.hpp +0 -0
  222. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/flatbuffer.cpp +0 -0
  223. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/flatbuffer.hpp +0 -0
  224. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/importer.cpp +0 -0
  225. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/fb/semantic/importer.hpp +0 -0
  226. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/gltf/render/exporter.cpp +0 -0
  227. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/gltf/render/exporter.hpp +0 -0
  228. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/json/semantic/exporter.cpp +0 -0
  229. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/json/semantic/exporter.hpp +0 -0
  230. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/json/semantic/importer.cpp +0 -0
  231. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/json/semantic/importer.hpp +0 -0
  232. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/obj/render/exporter.cpp +0 -0
  233. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/obj/render/exporter.hpp +0 -0
  234. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/provenance.hpp +0 -0
  235. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/render/exporter.hpp +0 -0
  236. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/render/exporter_registry.cpp +0 -0
  237. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/render.hpp +0 -0
  238. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/render_json.cpp +0 -0
  239. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/render_json.hpp +0 -0
  240. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic/exporter.hpp +0 -0
  241. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic/exporter_registry.cpp +0 -0
  242. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic/importer.hpp +0 -0
  243. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic/importer_registry.cpp +0 -0
  244. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic.cpp +0 -0
  245. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic.hpp +0 -0
  246. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic_json.cpp +0 -0
  247. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/semantic_json.hpp +0 -0
  248. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/synthetic/semantic/importer.cpp +0 -0
  249. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/synthetic/semantic/importer.hpp +0 -0
  250. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/tgeo/semantic/importer.cpp +0 -0
  251. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/tgeo/semantic/importer.hpp +0 -0
  252. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/tgeo/semantic/shape_dispatch.cpp +0 -0
  253. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/ir/tgeo/semantic/shape_dispatch.hpp +0 -0
  254. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/lua/lua_config.cpp +0 -0
  255. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/lua/lua_config.hpp +0 -0
  256. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/python/CMakeLists.txt +0 -0
  257. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/scene_build.cpp +0 -0
  258. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/scene_build.hpp +0 -0
  259. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/selection/predicate.cpp +0 -0
  260. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/selection/predicate.hpp +0 -0
  261. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/selection/selector.cpp +0 -0
  262. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/selection/selector.hpp +0 -0
  263. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/sokol/sokol_imgui_impl.cc +0 -0
  264. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/sokol/sokol_imgui_impl.mm +0 -0
  265. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/sokol/sokol_impl.c +0 -0
  266. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/sokol/sokol_impl.mm +0 -0
  267. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/boolean_tessellator.cpp +0 -0
  268. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/boolean_tessellator.hpp +0 -0
  269. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/build_pipeline.cpp +0 -0
  270. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/build_pipeline.hpp +0 -0
  271. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/primitive_tessellator.cpp +0 -0
  272. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/primitive_tessellator.hpp +0 -0
  273. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/tessellation_job.hpp +0 -0
  274. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/tessellation_pass.cpp +0 -0
  275. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/tessellation_pass.hpp +0 -0
  276. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/tessellator.hpp +0 -0
  277. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/wedge_cut.cpp +0 -0
  278. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tessellation/wedge_cut.hpp +0 -0
  279. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/tools/imgui_kitchen_sink_main.cpp +0 -0
  280. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/version.cpp +0 -0
  281. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/CMakeLists.txt +0 -0
  282. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_denoise_pass.cpp +0 -0
  283. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_denoise_pass.hpp +0 -0
  284. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_pass.cpp +0 -0
  285. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_pass.hpp +0 -0
  286. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_render_target.cpp +0 -0
  287. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ao_render_target.hpp +0 -0
  288. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/app.hpp +0 -0
  289. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/app_state.cpp +0 -0
  290. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/app_state.hpp +0 -0
  291. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/archive_export.cpp +0 -0
  292. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/archive_export.hpp +0 -0
  293. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/archive_project_fs.cpp +0 -0
  294. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/archive_project_fs.hpp +0 -0
  295. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/backend_caps.hpp +0 -0
  296. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/bag_project_fs.cpp +0 -0
  297. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/bag_project_fs.hpp +0 -0
  298. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/bench_runner.cpp +0 -0
  299. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/bench_runner.hpp +0 -0
  300. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/blit_pass.cpp +0 -0
  301. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/blit_pass.hpp +0 -0
  302. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/build_controller.cpp +0 -0
  303. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/build_controller.hpp +0 -0
  304. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/build_session.cpp +0 -0
  305. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/build_session.hpp +0 -0
  306. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/byte_buffer.hpp +0 -0
  307. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/camera.cpp +0 -0
  308. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/camera.hpp +0 -0
  309. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/composite_pass.cpp +0 -0
  310. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/composite_pass.hpp +0 -0
  311. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/config.hpp +0 -0
  312. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/dynamic_render_scale.cpp +0 -0
  313. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/dynamic_render_scale.hpp +0 -0
  314. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/filesystem_project_fs.cpp +0 -0
  315. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/filesystem_project_fs.hpp +0 -0
  316. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/gpu_pass_timer.hpp +0 -0
  317. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/gpu_pass_timer_d3d11.cpp +0 -0
  318. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/gpu_pass_timer_noop.cpp +0 -0
  319. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ibl.cpp +0 -0
  320. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ibl.hpp +0 -0
  321. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ibl_baker.cpp +0 -0
  322. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ibl_baker.hpp +0 -0
  323. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/imgui_backend.cpp +0 -0
  324. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/imgui_backend.hpp +0 -0
  325. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/log_sink.hpp +0 -0
  326. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/native_bag_project_fs.cpp +0 -0
  327. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/native_bag_project_fs.hpp +0 -0
  328. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform_macos.mm +0 -0
  329. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform_native_common.cpp +0 -0
  330. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform_native_common.hpp +0 -0
  331. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/platform_native_default.cpp +0 -0
  332. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export.cpp +0 -0
  333. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export.hpp +0 -0
  334. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export_readback.hpp +0 -0
  335. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export_readback_d3d11.cpp +0 -0
  336. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export_readback_gl.cpp +0 -0
  337. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export_readback_metal.mm +0 -0
  338. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_export_readback_wgpu.cpp +0 -0
  339. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_exporter.cpp +0 -0
  340. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/png_exporter.hpp +0 -0
  341. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/project_fs.hpp +0 -0
  342. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/project_manifest.cpp +0 -0
  343. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/project_manifest.hpp +0 -0
  344. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/render_quality.hpp +0 -0
  345. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job.hpp +0 -0
  346. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_common.cpp +0 -0
  347. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_internal.hpp +0 -0
  348. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_native.cpp +0 -0
  349. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_web.cpp +0 -0
  350. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_web_backend.hpp +0 -0
  351. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_build_job_web_worker.cpp +0 -0
  352. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_render_target.cpp +0 -0
  353. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_render_target.hpp +0 -0
  354. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_renderer.cpp +0 -0
  355. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/scene_renderer.hpp +0 -0
  356. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/shared_image.hpp +0 -0
  357. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/stb_image_write_impl.cpp +0 -0
  358. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/debug_panel.cpp +0 -0
  359. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/debug_panel.hpp +0 -0
  360. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/icon_font.cpp +0 -0
  361. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/icon_font.hpp +0 -0
  362. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/menu_bar.hpp +0 -0
  363. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/notifications.cpp +0 -0
  364. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/notifications.hpp +0 -0
  365. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/perf_history.hpp +0 -0
  366. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/project_panel.cpp +0 -0
  367. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/project_panel.hpp +0 -0
  368. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/status_ui.cpp +0 -0
  369. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/status_ui.hpp +0 -0
  370. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/ui_context.hpp +0 -0
  371. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/view_panel.cpp +0 -0
  372. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/view_panel.hpp +0 -0
  373. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/viewer_ui.cpp +0 -0
  374. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/ui/viewer_ui.hpp +0 -0
  375. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/watched_filesystem_project_fs.cpp +0 -0
  376. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/watched_filesystem_project_fs.hpp +0 -0
  377. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/zip_working_set.cpp +0 -0
  378. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/viewer/zip_working_set.hpp +0 -0
  379. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/web/compute_worker.js +0 -0
  380. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/web/compute_worker_main.cpp +0 -0
  381. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/src/web/viewer_main.cpp +0 -0
  382. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/api/test_api_equivalence.cpp +0 -0
  383. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/api/test_api_surface.cpp +0 -0
  384. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/api/test_config_wrapper.cpp +0 -0
  385. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/bench_main.cpp +0 -0
  386. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/cli/test_cli_run.cpp +0 -0
  387. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_config_fixtures.cpp +0 -0
  388. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_config_loader.cpp +0 -0
  389. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_config_validator.cpp +0 -0
  390. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_config_writer.cpp +0 -0
  391. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_lua_config.cpp +0 -0
  392. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/config/test_predicate_parser.cpp +0 -0
  393. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/export/test_export_resolve.cpp +0 -0
  394. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/export/test_gltf_exporter.cpp +0 -0
  395. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/export/test_render_exporter_registry.cpp +0 -0
  396. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/export/test_semantic_exporter_registry.cpp +0 -0
  397. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/import/test_dd4hep_importer.cpp +0 -0
  398. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/import/test_synthetic_importer.cpp +0 -0
  399. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/import/test_tgeo_importer.cpp +0 -0
  400. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/ir/test_flatbuffer_roundtrip.cpp +0 -0
  401. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/ir/test_json_roundtrip.cpp +0 -0
  402. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/ir/test_render_flatbuffer_roundtrip.cpp +0 -0
  403. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/ir/test_semantic_ir.cpp +0 -0
  404. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/ir/test_shape_variant.cpp +0 -0
  405. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/public_fixture.hpp +0 -0
  406. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_build.cpp +0 -0
  407. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_cli.cpp +0 -0
  408. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_config.cpp +0 -0
  409. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_dd4hep.cpp +0 -0
  410. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_diagnostics.cpp +0 -0
  411. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_render_scene.cpp +0 -0
  412. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_semantic_scene.cpp +0 -0
  413. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/public/test_public_tgeo.cpp +0 -0
  414. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/python/test_build.py +0 -0
  415. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/python/test_config_and_diagnostics.py +0 -0
  416. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/python/test_semantic_scene.py +0 -0
  417. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/selection/test_predicate.cpp +0 -0
  418. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/selection/test_selector.cpp +0 -0
  419. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_boolean_tessellator.cpp +0 -0
  420. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_build_pipeline.cpp +0 -0
  421. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_obj_writer.hpp +0 -0
  422. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_primitive_tessellator.cpp +0 -0
  423. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_tessellation_pass.cpp +0 -0
  424. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_tgeo_tessellation.cpp +0 -0
  425. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/tessellation/test_wedge_cut.cpp +0 -0
  426. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/test_compute_pipeline.cpp +0 -0
  427. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/test_main.cpp +0 -0
  428. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/test_markup.cpp +0 -0
  429. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/test_scene_build.cpp +0 -0
  430. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/test_timing.cpp +0 -0
  431. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_app_state.cpp +0 -0
  432. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_archive_export.cpp +0 -0
  433. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_archive_project_fs.cpp +0 -0
  434. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_bag_project_fs.cpp +0 -0
  435. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_build_session.cpp +0 -0
  436. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_byte_buffer.cpp +0 -0
  437. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_dynamic_render_scale.cpp +0 -0
  438. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_filesystem_project_fs.cpp +0 -0
  439. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_native_bag_project_fs.cpp +0 -0
  440. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_project_manifest.cpp +0 -0
  441. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_watched_filesystem_project_fs.cpp +0 -0
  442. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/viewer/test_zip_working_set.cpp +0 -0
  443. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/tests/wasm_lua_smoke.cpp +0 -0
  444. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/third_party/fontawesome7/IconsFontAwesome7.h +0 -0
  445. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/third_party/fontawesome7/README.md +0 -0
  446. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/third_party/fontawesome7/fa-solid-900.h +0 -0
  447. {nodehammer-0.2.0rc1 → nodehammer-0.2.0rc3}/third_party/stb/stb_image_write.h +0 -0
@@ -21,6 +21,7 @@ CMakeUserPresets.json
21
21
  # Python: the venv the wheel workflow builds against, and wheel output.
22
22
  /.venv/
23
23
  /dist/
24
+ /dist-web/
24
25
  *.egg-info/
25
26
  .idea
26
27
 
@@ -134,6 +134,32 @@ include(cmake/CompilerOptions.cmake)
134
134
  # into the shared target's INSTALL_INTERFACE include path.
135
135
  include(GNUInstallDirs)
136
136
 
137
+ # ── The web runtime's contract ───────────────────────────────────────────────
138
+ # The wasm viewer is delivered as a directory, not a program: a shell, a worker
139
+ # script, three bundles and the stamp below. `nodehammer viewer --web` locates
140
+ # one through a ladder of candidate directories (a flag, an environment
141
+ # variable, this install path, later a download cache), which means it can be
142
+ # handed a directory that nobody validated — an old checkout's build tree is
143
+ # exactly the mistake a person makes while debugging, and no wheel metadata or
144
+ # version pin can see it. So the runtime says what it is and the library checks
145
+ # before it serves anything.
146
+ #
147
+ # What is compared is the schema id, not the version. What actually has to
148
+ # agree between the two halves is the .nhproj + nh_manifest.json shape and the
149
+ # compute worker's postMessage protocol — the last of which has already broken
150
+ # once, silently, under Closure (dcc4a06, and scripts/check_wasm_module_
151
+ # linkage.py exists because of it). An integer is at once stricter than a
152
+ # version, because it catches same-version skew from a hand-pointed directory,
153
+ # and looser, because it does not cry wolf on a patch release where nothing
154
+ # about the contract moved. The version rides along in the stamp only so the
155
+ # refusal can name it.
156
+ #
157
+ # BUMP THIS when the manifest/archive shape or the worker protocol changes.
158
+ # Monotonic; never reused.
159
+ set(NH_WEB_RUNTIME_SCHEMA 1)
160
+ set(NH_WEB_RUNTIME_STAMP "nh_runtime.json")
161
+ set(NH_WEB_RUNTIME_DIR "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/web")
162
+
137
163
  # ── FlatBuffers code generation ──────────────────────────────────────────────
138
164
  set(NH_FBS_SCHEMAS
139
165
  ${CMAKE_CURRENT_SOURCE_DIR}/schemas/semantic.fbs
@@ -231,6 +257,11 @@ set(NH_CORE_SOURCES
231
257
  src/ir/fb/semantic/exporter.cpp
232
258
  src/ir/semantic/exporter_registry.cpp
233
259
  src/detail/markup.cpp
260
+ # Host-side, despite the directory: locating and vetting the wasm runtime is
261
+ # something the *serving* process does. src/web/'s other two TUs are the
262
+ # Emscripten entry points and are not core.
263
+ src/web/runtime_locator.cpp
264
+ src/web/executable_path.cpp
234
265
  src/scene_build.cpp
235
266
  src/export_resolve.cpp
236
267
  src/version.cpp
@@ -348,6 +379,11 @@ set(NH_CORE_PRIVATE_DEPS
348
379
 
349
380
  set(NH_CORE_DEFINES "")
350
381
 
382
+ # The web runtime's schema id, compiled in so `web::compiledSchema()` and the
383
+ # `nh_runtime.json` the install rules stamp come from one `set()` and cannot
384
+ # drift. See the contract block above GNUInstallDirs.
385
+ list(APPEND NH_CORE_DEFINES NH_WEB_RUNTIME_SCHEMA=${NH_WEB_RUNTIME_SCHEMA})
386
+
351
387
  # Extra include directories for dependencies that ship variables instead of
352
388
  # imported targets — Geant4 is the only one today. Everything else propagates
353
389
  # its includes through the link.
@@ -429,11 +465,30 @@ list(APPEND NH_CORE_DEPS manifold::manifold)
429
465
  # the web *runtime* no longer references BagProjectFs (static-lib dead-strip
430
466
  # keeps it out of the wasm viewer), but the native CLI (cmd_viewer) and the
431
467
  # unit tests on both platforms still use it.
468
+ #
469
+ # The web viewer's server and staging are native-only for a plainer reason:
470
+ # they exist to *serve* the wasm runtime to a browser, and a wasm module is the
471
+ # thing being served. Sockets are the giveaway -- there are none in the sandbox.
472
+ # The locator above is not gated, because "where would the runtime be" is a
473
+ # question the wasm build can be asked and answer "nowhere" to.
432
474
  if(NOT EMSCRIPTEN)
433
475
  list(APPEND NH_CORE_SOURCES
434
476
  src/viewer/filesystem_project_fs.cpp
435
477
  src/viewer/native_bag_project_fs.cpp
478
+ src/web/static_server.cpp
479
+ src/web/stage.cpp
480
+ src/web/open_browser.cpp
481
+ # `viewer --web` itself. Excluded here rather than #ifdef'd inside:
482
+ # every line of it calls something in the three files above, so a wasm
483
+ # build of this TU would be a shell around a refusal. `run.cpp` skips
484
+ # the registration to match, and a wasm module simply has no `viewer`
485
+ # subcommand — which is right, since it *is* the viewer.
486
+ src/cli/cmd_viewer_web.cpp
436
487
  )
488
+ # Private: httplib.h is included by static_server.cpp and by nothing else,
489
+ # least of all a header. Header-only, so the shared library gains no link
490
+ # dependency and its package config gains no requirement.
491
+ list(APPEND NH_CORE_PRIVATE_DEPS httplib::httplib)
437
492
  endif()
438
493
 
439
494
  # WatchedFilesystemProjectFs (strategy doc step 4) decorates FilesystemProjectFs
@@ -712,7 +767,7 @@ endif()
712
767
  # executable per backend. Native picks the right backend for the host
713
768
  # (Metal on Apple, GLCORE on Linux/Windows). Emscripten builds BOTH GLES3
714
769
  # and WGPU executables from a single configure so the wasm shell can pick
715
- # at runtime via navigator.gpu — see web/viewer.html.
770
+ # at runtime via navigator.gpu — see web/index.html.
716
771
  if(NODEHAMMER_WITH_VIEWER)
717
772
  # Defines nodehammer_shaders custom target + NH_SHADER_INCLUDE_DIR.
718
773
  add_subdirectory(shaders)
@@ -723,7 +778,7 @@ if(NODEHAMMER_WITH_VIEWER)
723
778
  # dispatcher (`nodehammer viewer …` alongside the other subcommands);
724
779
  # the wasm build skips CLI11 entirely and uses src/web/viewer_main.cpp,
725
780
  # which exposes a single `nh_viewer_start(opts_json)` C export driven
726
- # by web/viewer.html.
781
+ # by web/index.html.
727
782
  function(nh_add_viewer_exe name viewer_lib)
728
783
  if(EMSCRIPTEN)
729
784
  add_executable(${name} src/web/viewer_main.cpp)
@@ -837,37 +892,100 @@ include(cmake/PicProbe.cmake)
837
892
  # (GNUInstallDirs is included near the top — the core-library helper needs
838
893
  # CMAKE_INSTALL_INCLUDEDIR for the shared target's INSTALL_INTERFACE.)
839
894
 
840
- # Native: single nodehammer executable. Emscripten + viewer: per-backend
841
- # wasm bundles named nodehammer-{gles3,wgpu}.{js,wasm}. Emscripten + no
842
- # viewer: headless nodehammer (just the .js + .wasm).
843
- if(EMSCRIPTEN AND NODEHAMMER_WITH_VIEWER)
844
- set(NH_INSTALL_TARGETS nodehammer-gles3 nodehammer-wgpu nodehammer-compute)
845
- else()
846
- set(NH_INSTALL_TARGETS nodehammer)
847
- endif()
848
-
849
895
  # Every install rule names a COMPONENT: Runtime to run nodehammer, Development
850
- # to build against it. A plain `cmake --install` still installs everything, so
851
- # existing callers are unaffected; what this adds is `--component Runtime` for a
852
- # release artifact that wants the CLI and its licences but not the headers.
896
+ # to build against it, WebRuntime for the servable wasm payload. A plain
897
+ # `cmake --install` still installs everything, so existing callers are
898
+ # unaffected; what this adds is `--component Runtime` for a release artifact
899
+ # that wants the CLI and its licences but not the headers.
853
900
  #
854
901
  # Tag every rule, not just the new ones — an untagged rule lands in a default
855
902
  # component named Unspecified and would be silently missing from *both* halves.
856
- install(TARGETS ${NH_INSTALL_TARGETS}
857
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
858
- COMPONENT Runtime
859
- )
903
+ if(EMSCRIPTEN AND NODEHAMMER_WITH_VIEWER)
904
+ # This configuration does not install a *program*. What it installs is a
905
+ # directory a web server can point at: the shell, the worker script, the
906
+ # per-backend bundles and a stamp saying what they are. Six blobs under
907
+ # bin/ were never that — `scripts/build_pages_site.sh` only worked because
908
+ # it read the shell out of the source tree it happened to be running in, so
909
+ # the artifact alone could not be served by anything that did not also have
910
+ # a checkout.
911
+ #
912
+ # share/nodehammer/web is also the rung the asset ladder looks for relative
913
+ # to the executable (docs/cli-and-web-viewer-plan.md Part 1), which is why
914
+ # it is a datadir path and not an opaque one: a native install tree and a
915
+ # wasm install tree merge into one directory that `nodehammer viewer --web`
916
+ # can resolve without being told where anything is.
917
+ install(TARGETS nodehammer-gles3 nodehammer-wgpu nodehammer-compute
918
+ RUNTIME DESTINATION "${NH_WEB_RUNTIME_DIR}"
919
+ COMPONENT WebRuntime
920
+ )
860
921
 
861
- # Emscripten produces a sidecar .wasm next to each .js loader; CMake's
862
- # install(TARGETS RUNTIME) only picks up the primary output, so install
863
- # the blobs explicitly.
864
- if(EMSCRIPTEN)
865
- foreach(_t ${NH_INSTALL_TARGETS})
922
+ # Emscripten produces a sidecar .wasm next to each .js loader; CMake's
923
+ # install(TARGETS RUNTIME) only picks up the primary output, so install
924
+ # the blobs explicitly.
925
+ foreach(_t nodehammer-gles3 nodehammer-wgpu nodehammer-compute)
866
926
  install(FILES "$<TARGET_FILE_DIR:${_t}>/${_t}.wasm"
927
+ DESTINATION "${NH_WEB_RUNTIME_DIR}"
928
+ COMPONENT WebRuntime
929
+ )
930
+ endforeach()
931
+
932
+ # The two files the browser loads that no compiler produced. They are as
933
+ # much a part of the runtime as the bundles — index.html is what picks a
934
+ # backend at all, and a bundle without its worker script tessellates on the
935
+ # main thread or not at all.
936
+ install(FILES
937
+ "${CMAKE_CURRENT_SOURCE_DIR}/web/index.html"
938
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/web/compute_worker.js"
939
+ DESTINATION "${NH_WEB_RUNTIME_DIR}"
940
+ COMPONENT WebRuntime
941
+ )
942
+
943
+ # The stamp, written beside the bundles in the build tree so that a build
944
+ # directory and an install tree are the same shape — `just wasm-serve` and
945
+ # `just pages-site` stage straight out of the former, and `viewer --web
946
+ # --web-assets build/emscripten/Release` is the first thing any developer
947
+ # reaches for.
948
+ file(GENERATE
949
+ OUTPUT "$<TARGET_FILE_DIR:nodehammer-gles3>/${NH_WEB_RUNTIME_STAMP}"
950
+ CONTENT "{\n \"schema\": ${NH_WEB_RUNTIME_SCHEMA},\n \"version\": \"${NODEHAMMER_VERSION}\"\n}\n"
951
+ )
952
+ install(FILES "$<TARGET_FILE_DIR:nodehammer-gles3>/${NH_WEB_RUNTIME_STAMP}"
953
+ DESTINATION "${NH_WEB_RUNTIME_DIR}"
954
+ COMPONENT WebRuntime
955
+ )
956
+
957
+ # "The same shape" has to include the two files no compiler produces, or the
958
+ # claim above is false in exactly the way that bites: a build tree carrying a
959
+ # stamp and bundles but no shell passes every check a stamp can make and then
960
+ # serves 404 on `/`.
961
+ #
962
+ # Symlinks, not copies, and that is the whole point: an edit to web/index.html
963
+ # is live in a served build tree with no rebuild, which is what `just
964
+ # wasm-serve`'s link mode has always given. COPY_ON_ERROR covers Windows,
965
+ # where an unprivileged symlink fails and a stale copy is better than an
966
+ # incomplete directory.
967
+ foreach(_nh_shell_file
968
+ "${CMAKE_CURRENT_SOURCE_DIR}/web/index.html"
969
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/web/compute_worker.js")
970
+ get_filename_component(_nh_shell_name "${_nh_shell_file}" NAME)
971
+ file(CREATE_LINK "${_nh_shell_file}"
972
+ "${CMAKE_CURRENT_BINARY_DIR}/${_nh_shell_name}"
973
+ COPY_ON_ERROR SYMBOLIC)
974
+ endforeach()
975
+ else()
976
+ # Native: the single nodehammer executable. Emscripten with no viewer: the
977
+ # headless nodehammer bundle (just the .js + .wasm).
978
+ install(TARGETS nodehammer
979
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
980
+ COMPONENT Runtime
981
+ )
982
+
983
+ if(EMSCRIPTEN)
984
+ install(FILES "$<TARGET_FILE_DIR:nodehammer>/nodehammer.wasm"
867
985
  DESTINATION ${CMAKE_INSTALL_BINDIR}
868
986
  COMPONENT Runtime
869
987
  )
870
- endforeach()
988
+ endif()
871
989
  endif()
872
990
 
873
991
  install(FILES "${CMAKE_SOURCE_DIR}/LICENSE"
@@ -12,6 +12,7 @@ build_nodehammer_cmd := if os_family() == "windows" { "powershell -NoProfile -Ex
12
12
 
13
13
  # Cheap to re-export every time; conan dedupes by recipe revision.
14
14
  recipes:
15
+ conan export recipes/cli11 --version=2.7.2
15
16
  conan export recipes/sokol --version=2026.07.02
16
17
  conan export recipes/imgui --version=1.92.8
17
18
  conan export recipes/implot --version=1.0.0
@@ -328,7 +329,9 @@ wheel *args:
328
329
  # the only visible difference is the filename. Catch that here rather than
329
330
  # discovering it as a CI matrix that grew from one wheel per platform to one
330
331
  # per interpreter.
331
- for whl in "$root"/dist/*.whl; do
332
+ # dist/ also holds nodehammer_web-*.whl, which is py3-none-any by design, so
333
+ # the glob names this distribution rather than every wheel in the directory.
334
+ for whl in "$root"/dist/nodehammer-*.whl; do
332
335
  case "$whl" in
333
336
  *-abi3-*) ;;
334
337
  *) echo "not an abi3 wheel: $(basename "$whl") -- STABLE_ABI did not engage" >&2
@@ -336,6 +339,65 @@ wheel *args:
336
339
  esac
337
340
  done
338
341
 
342
+ # ── the web runtime wheel ─────────────────────────────────────────────────────
343
+ # `nodehammer-web` is the other half of the Python story: a py3-none-any wheel
344
+ # holding the Emscripten build, because no native toolchain can produce one and
345
+ # a headless install has no use for 7 MB of it. See packaging/web/pyproject.toml.
346
+ #
347
+ # `src` is anything the runtime locator would accept -- a wasm build directory or
348
+ # an install tree's share/nodehammer/web -- and it is staged by the same script
349
+ # the Pages site uses, in application mode, so the payload is the one CI deploys.
350
+
351
+ # Build the nodehammer-web wheel from a wasm build (run `just wasm-release` first)
352
+ wheel-web src="build/emscripten/Release":
353
+ #!/usr/bin/env bash
354
+ set -euo pipefail
355
+ root="{{justfile_directory()}}"
356
+ src="{{src}}"
357
+ case "$src" in /*) ;; *) src="$root/$src" ;; esac
358
+ runtime="$root/packaging/web/src/nodehammer_web/runtime"
359
+ # Both sides emptied rather than overlaid. The staging directory is the
360
+ # obvious one; setuptools' build/lib is the one that bites, because
361
+ # `build_py` copies into it and never prunes -- a file dropped from the
362
+ # payload survives there and is added to the wheel from a previous run.
363
+ # Caught doing exactly that with a stale nodehammer-wgpu.wasm.zst.
364
+ rm -rf "$runtime" "$root/packaging/web/build"
365
+ "$root/scripts/stage_wasm_viewer.sh" copy none "$runtime" "$src"
366
+ uv build --wheel --out-dir "$root/dist" "$root/packaging/web"
367
+ # The mirror of the abi3 check above, and the same kind of silent failure:
368
+ # a wheel that picked up a platform tag would install nowhere it is needed.
369
+ for whl in "$root"/dist/nodehammer_web-*.whl; do
370
+ case "$whl" in
371
+ *-py3-none-any.whl) ;;
372
+ *) echo "not a pure wheel: $(basename "$whl")" >&2
373
+ exit 1 ;;
374
+ esac
375
+ done
376
+
377
+ # The only check that reaches the rung a wheel fills. `--web-assets` and
378
+ # NODEHAMMER_WEB_ASSETS are cleared out of the way on purpose: both outrank the
379
+ # package, so leaving either set would test the wrong rung and pass anyway.
380
+ #
381
+ # The pair check reads dist/, so it fails on a directory holding two versions of
382
+ # either distribution -- which is the stale-artifact case it exists to catch, and
383
+ # locally usually just means `rm -rf dist` first.
384
+
385
+ # Install both wheels from local files and serve the viewer out of them
386
+ wheel-web-test: wheel wheel-web
387
+ #!/usr/bin/env bash
388
+ set -euo pipefail
389
+ root="{{justfile_directory()}}"
390
+ uv run --no-project --with packaging "$root/scripts/check_wheel_pair.py" "$root/dist"
391
+ venv=$(mktemp -d)/venv
392
+ uv venv --python 3.12 "$venv"
393
+ # --no-index: the pair under test is the one just built, never whatever an
394
+ # index happens to resolve. Only `nodehammer` is named -- pulling the runtime
395
+ # in is the dependency's job, and naming it here would pass even if the
396
+ # generated pin had gone missing.
397
+ VIRTUAL_ENV="$venv" uv pip install --quiet --no-index \
398
+ --find-links "$root/dist" nodehammer
399
+ env -u NODEHAMMER_WEB_ASSETS "$root/scripts/check_web_serve.py" "$venv/bin/nodehammer"
400
+
339
401
  # The Python counterpart of ci/shared_consumer: the build tree resolves
340
402
  # libnodehammer through CMake's own rpath, so only an installed wheel proves the
341
403
  # relative rpath and the bundled library are right.
@@ -347,7 +409,14 @@ wheel-test: wheel
347
409
  root="{{justfile_directory()}}"
348
410
  venv=$(mktemp -d)/venv
349
411
  uv venv --python 3.12 "$venv"
350
- VIRTUAL_ENV="$venv" uv pip install --quiet pytest tomli-w "$root"/dist/nodehammer-*.whl
412
+ VIRTUAL_ENV="$venv" uv pip install --quiet pytest tomli-w
413
+ # --no-deps, in its own command so the test tools above still resolve.
414
+ # `nodehammer` requires `nodehammer-web`, which is built by a separate
415
+ # Emscripten toolchain -- resolving it here would either reach an index for
416
+ # something this recipe is not testing, or fail on a machine with no emsdk.
417
+ # What this recipe is for is the platform wheel's own payload and its
418
+ # relative rpath; `just wheel-web-test` is the one that exercises the pair.
419
+ VIRTUAL_ENV="$venv" uv pip install --quiet --no-deps "$root"/dist/nodehammer-*.whl
351
420
  cd "$root" && "$venv/bin/python" -m pytest tests/python -q
352
421
 
353
422
  # Build the Linux wheels the way CI does, in a manylinux container. Needs Docker.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nodehammer
3
- Version: 0.2.0rc1
3
+ Version: 0.2.0rc3
4
4
  Summary: Detector geometry tessellation and export
5
5
  Author: Paul Gessinger
6
6
  License-Expression: MIT
@@ -11,8 +11,10 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
11
11
  Classifier: Topic :: Scientific/Engineering :: Physics
12
12
  Project-URL: Homepage, https://github.com/paulgessinger/nodehammer
13
13
  Requires-Python: >=3.12
14
+ Requires-Dist: nodehammer-web==0.2.*
14
15
  Provides-Extra: dict
15
16
  Requires-Dist: tomli-w>=1.0; extra == "dict"
17
+ Provides-Extra: web
16
18
  Description-Content-Type: text/markdown
17
19
 
18
20
  <p align="center">
@@ -137,7 +139,7 @@ natively, in the browser, and behind a published link.
137
139
  | **Working set** | the live, editable in-memory project (`ZipWorkingSet`) — what the viewer edits |
138
140
  | **Archive** | a serialized `.nhproj` of a working set — the portable, publishable unit |
139
141
  | **Project manifest** | root `nodehammer.toml` *inside* the archive: `[project]` entry keys + `[view]` initial steer |
140
- | **Sidecar** | `nh_manifest.json` next to `viewer.html` — points at archive(s), carries deployment presentation (lock, steer overrides) |
142
+ | **Sidecar** | `nh_manifest.json` next to `index.html` — points at archive(s), carries deployment presentation (lock, steer overrides) |
141
143
  | **Steer** | view-state (camera, angle cut, rotation, toggles) — the ephemeral per-link layer, committed to the URL query |
142
144
  | **Provenance** | `Empty \| Local(name) \| Remote(url)` — where the working set came from; drives persistence and posture |
143
145
  | **Package** | the self-contained static folder emitted by **Publish** — drop it on any static host, zero server code |
@@ -244,9 +246,9 @@ web build supports:
244
246
 
245
247
  | | |
246
248
  |---|---|
247
- | [`/app/`](https://paulgessinger.github.io/nodehammer/app/viewer.html) | the open application — empty start; drop a `.nhproj` or loose config/geometry files onto it |
248
- | [`/odd/`](https://paulgessinger.github.io/nodehammer/odd/viewer.html) | the full Open Data Detector, preconfigured and content-locked |
249
- | [`/odd-simple/`](https://paulgessinger.github.io/nodehammer/odd-simple/viewer.html) | the simplified ODD — same, lighter to build |
249
+ | [`/app/`](https://paulgessinger.github.io/nodehammer/app/) | the open application — empty start; drop a `.nhproj` or loose config/geometry files onto it |
250
+ | [`/odd/`](https://paulgessinger.github.io/nodehammer/odd/) | the full Open Data Detector, preconfigured and content-locked |
251
+ | [`/odd-simple/`](https://paulgessinger.github.io/nodehammer/odd-simple/) | the simplified ODD — same, lighter to build |
250
252
 
251
253
  Each directory is a self-contained static package of the kind the viewer's
252
254
  **Publish** command emits. Reproduce the whole site locally with
@@ -120,7 +120,7 @@ natively, in the browser, and behind a published link.
120
120
  | **Working set** | the live, editable in-memory project (`ZipWorkingSet`) — what the viewer edits |
121
121
  | **Archive** | a serialized `.nhproj` of a working set — the portable, publishable unit |
122
122
  | **Project manifest** | root `nodehammer.toml` *inside* the archive: `[project]` entry keys + `[view]` initial steer |
123
- | **Sidecar** | `nh_manifest.json` next to `viewer.html` — points at archive(s), carries deployment presentation (lock, steer overrides) |
123
+ | **Sidecar** | `nh_manifest.json` next to `index.html` — points at archive(s), carries deployment presentation (lock, steer overrides) |
124
124
  | **Steer** | view-state (camera, angle cut, rotation, toggles) — the ephemeral per-link layer, committed to the URL query |
125
125
  | **Provenance** | `Empty \| Local(name) \| Remote(url)` — where the working set came from; drives persistence and posture |
126
126
  | **Package** | the self-contained static folder emitted by **Publish** — drop it on any static host, zero server code |
@@ -227,9 +227,9 @@ web build supports:
227
227
 
228
228
  | | |
229
229
  |---|---|
230
- | [`/app/`](https://paulgessinger.github.io/nodehammer/app/viewer.html) | the open application — empty start; drop a `.nhproj` or loose config/geometry files onto it |
231
- | [`/odd/`](https://paulgessinger.github.io/nodehammer/odd/viewer.html) | the full Open Data Detector, preconfigured and content-locked |
232
- | [`/odd-simple/`](https://paulgessinger.github.io/nodehammer/odd-simple/viewer.html) | the simplified ODD — same, lighter to build |
230
+ | [`/app/`](https://paulgessinger.github.io/nodehammer/app/) | the open application — empty start; drop a `.nhproj` or loose config/geometry files onto it |
231
+ | [`/odd/`](https://paulgessinger.github.io/nodehammer/odd/) | the full Open Data Detector, preconfigured and content-locked |
232
+ | [`/odd-simple/`](https://paulgessinger.github.io/nodehammer/odd-simple/) | the simplified ODD — same, lighter to build |
233
233
 
234
234
  Each directory is a self-contained static package of the kind the viewer's
235
235
  **Publish** command emits. Reproduce the whole site locally with
@@ -50,6 +50,7 @@ esac
50
50
 
51
51
  case "$cmd" in
52
52
  conan-install)
53
+ conan export recipes/cli11 --version=2.7.2
53
54
  conan export recipes/sokol --version=2026.07.02
54
55
  conan export recipes/imgui --version=1.92.8
55
56
  conan export recipes/implot --version=1.0.0
@@ -39,11 +39,14 @@ FetchContent_Declare(Catch2
39
39
  FetchContent_MakeAvailable(Catch2)
40
40
 
41
41
  # ── CLI11 ─────────────────────────────────────────────────────────────────────
42
+ # 2.7.2 is a floor, not a preference. Up to and including 2.6.2 CLI11's character
43
+ # tables were namespace-scope references bound to temporaries, which Emscripten
44
+ # cannot run an atexit chain over -- see recipes/cli11/conanfile.py and issue #76.
42
45
  FetchContent_Declare(CLI11
43
46
  SYSTEM
44
47
  GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
45
- GIT_TAG v2.6.2
46
- FIND_PACKAGE_ARGS 2.6.2
48
+ GIT_TAG v2.7.2
49
+ FIND_PACKAGE_ARGS 2.7.2
47
50
  )
48
51
  FetchContent_MakeAvailable(CLI11)
49
52
 
@@ -56,6 +59,27 @@ FetchContent_Declare(nlohmann_json
56
59
  )
57
60
  FetchContent_MakeAvailable(nlohmann_json)
58
61
 
62
+ # ── cpp-httplib ───────────────────────────────────────────────────────────────
63
+ # The static server behind `viewer --web`. Native only: the wasm build has no
64
+ # sockets, and the module it would serve is the thing being served.
65
+ #
66
+ # Chosen over the ~370 hand-rolled lines it replaces because it is *less* code
67
+ # for today's behaviour, not only because of what comes next. What comes next
68
+ # does matter though: `HttpProjectFs` needs request bodies and routing, and an
69
+ # experiment framework pushing events needs a parser that has been attacked. The
70
+ # `set_mount_point` + `set_default_headers` + `bind_to_any_port` trio covers the
71
+ # static case in a few lines, and WebSocket (since 0.33.0) plus chunked content
72
+ # providers cover the rest without a second dependency.
73
+ if(NOT EMSCRIPTEN)
74
+ FetchContent_Declare(httplib
75
+ SYSTEM
76
+ GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
77
+ GIT_TAG v0.47.0
78
+ FIND_PACKAGE_ARGS 0.47.0
79
+ )
80
+ FetchContent_MakeAvailable(httplib)
81
+ endif()
82
+
59
83
  # ── GLM ───────────────────────────────────────────────────────────────────────
60
84
  FetchContent_Declare(glm
61
85
  SYSTEM
@@ -0,0 +1,88 @@
1
+ # The version of record is a git tag, read here with `git describe`. There is one
2
+ # exception, and it comes first.
3
+ #
4
+ # Under scikit-build-core the version is decided on the Python side — setuptools_scm,
5
+ # which speaks PEP 440 — and handed down as SKBUILD_PROJECT_VERSION. Prefer it
6
+ # there, for two reasons. `git describe`'s spelling is not PEP 440, so the two
7
+ # would disagree by construction; and a wheel built from an sdist has no .git to
8
+ # describe at all, which would fall through to the branch below and ship a
9
+ # libnodehammer whose nodehammer::version() reports 0.0.0 from inside a wheel
10
+ # whose metadata says otherwise.
11
+ #
12
+ # The numeric prefix is what project(VERSION) and SOVERSION need; the full string
13
+ # is what version.hpp reports, so a development wheel says
14
+ # "0.1.3.post1.dev398+g920e6c5" — the same string pip shows, and strictly more
15
+ # information than the "0.1.3-398-g920e6c5" it replaces. The prefix match has no
16
+ # anchor at its end on purpose: a pre-release version keeps its numeric prefix
17
+ # and everything after it rides in the full string.
18
+ if(DEFINED SKBUILD_PROJECT_VERSION
19
+ AND SKBUILD_PROJECT_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
20
+ set(NODEHAMMER_VERSION_MAJOR "${CMAKE_MATCH_1}")
21
+ set(NODEHAMMER_VERSION_MINOR "${CMAKE_MATCH_2}")
22
+ set(NODEHAMMER_VERSION_PATCH "${CMAKE_MATCH_3}")
23
+ set(NODEHAMMER_VERSION "${SKBUILD_PROJECT_VERSION}")
24
+ return()
25
+ endif()
26
+
27
+ execute_process(
28
+ COMMAND git describe --tags --long --always
29
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
30
+ OUTPUT_VARIABLE _git_describe
31
+ OUTPUT_STRIP_TRAILING_WHITESPACE
32
+ ERROR_QUIET
33
+ RESULT_VARIABLE _git_result
34
+ )
35
+
36
+ # The `(.*)` between the patch number and the commit distance is what makes a
37
+ # pre-release tag a version rather than a fallthrough. `v0.2.0rc1` is spelled the
38
+ # way setuptools_scm and PEP 440 want it, so the two halves of one release agree,
39
+ # and it does not match a triple-only pattern — which is how tagging v0.2.0rc1
40
+ # silently rewrote nodehammer::version(), the SOVERSION and the package config
41
+ # version to 0.0.0 for every native and wasm build off that tag, while wheels
42
+ # went on being right because they take the branch above.
43
+ #
44
+ # Greedy, and it backtracks to the last `-<digits>-g<hash>`, so both spellings
45
+ # work: "v0.2.0rc1-3-gabc1234" and "v0.2.0-rc1-3-gabc1234" both yield the triple
46
+ # 0.2.0 with the pre-release riding in the full string. The suffix is passed
47
+ # through, not validated — this file's job is to report the tag that exists, not
48
+ # to have an opinion about how it is spelled.
49
+ if(_git_result EQUAL 0 AND _git_describe MATCHES
50
+ "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)(.*)-([0-9]+)-g([0-9a-f]+)$")
51
+ # Named up front: six groups read as six groups, and CMAKE_MATCH_* is global
52
+ # state that the next `if(... MATCHES ...)` anywhere would overwrite.
53
+ set(NODEHAMMER_VERSION_MAJOR "${CMAKE_MATCH_1}")
54
+ set(NODEHAMMER_VERSION_MINOR "${CMAKE_MATCH_2}")
55
+ set(NODEHAMMER_VERSION_PATCH "${CMAKE_MATCH_3}")
56
+ set(_nh_pre "${CMAKE_MATCH_4}")
57
+ set(_nh_distance "${CMAKE_MATCH_5}")
58
+ set(_nh_hash "${CMAKE_MATCH_6}")
59
+
60
+ set(_nh_base
61
+ "${NODEHAMMER_VERSION_MAJOR}.${NODEHAMMER_VERSION_MINOR}.${NODEHAMMER_VERSION_PATCH}${_nh_pre}")
62
+ if(_nh_distance EQUAL 0)
63
+ set(NODEHAMMER_VERSION "${_nh_base}")
64
+ else()
65
+ set(NODEHAMMER_VERSION "${_nh_base}-${_nh_distance}-g${_nh_hash}")
66
+ endif()
67
+ else()
68
+ # Loud, because it is silent failure that made this branch worth fixing: the
69
+ # build carries on and every version it reports is a lie, in four places at
70
+ # once, and nothing says so until someone reads one of them.
71
+ if(NOT _git_result EQUAL 0)
72
+ set(_nh_why "`git describe` failed — not a git checkout, or git is not installed")
73
+ else()
74
+ set(_nh_why "`git describe` returned \"${_git_describe}\", which names no vMAJOR.MINOR.PATCH tag")
75
+ endif()
76
+ message(WARNING
77
+ "nodehammer: could not derive a version — ${_nh_why}.\n"
78
+ "Falling back to 0.0.0, which is what nodehammer --version, "
79
+ "nodehammer::version(), the shared library SOVERSION and "
80
+ "nodehammer-config-version.cmake will all report — so "
81
+ "find_package(nodehammer <any version>) against this build will fail.\n"
82
+ "A tagless or shallow clone is the usual cause; `git fetch --tags` fixes it.")
83
+
84
+ set(NODEHAMMER_VERSION_MAJOR 0)
85
+ set(NODEHAMMER_VERSION_MINOR 0)
86
+ set(NODEHAMMER_VERSION_PATCH 0)
87
+ set(NODEHAMMER_VERSION "0.0.0")
88
+ endif()
@@ -30,7 +30,7 @@ class Nodehammer(ConanFile):
30
30
  def requirements(self):
31
31
  self.requires("zstd/1.5.7")
32
32
  self.requires("catch2/3.15.1")
33
- self.requires("cli11/2.6.2")
33
+ self.requires("cli11/2.7.2")
34
34
  self.requires("nlohmann_json/3.12.0")
35
35
  self.requires("glm/1.0.3")
36
36
  self.requires("tomlplusplus/3.4.0")
@@ -71,6 +71,18 @@ class Nodehammer(ConanFile):
71
71
  # never calls the `.lua` path, which today is both wasm bundles.
72
72
  self.requires("lua/5.4.6")
73
73
  self.requires("sol2/3.5.0")
74
+ # The static server behind `viewer --web`. Gated on the platform rather
75
+ # than unconditional, unlike miniz and lua above, because this one is not
76
+ # a capability of the library: it exists to *serve* the wasm runtime to a
77
+ # browser, and under Emscripten the wasm module is the thing being
78
+ # served. There are no sockets in the sandbox, and src/web/static_server.cpp
79
+ # is excluded from NH_CORE_SOURCES there for the same reason.
80
+ #
81
+ # Header-only, so it adds no link dependency beyond the platform's own
82
+ # sockets, and no OpenSSL unless CPPHTTPLIB_OPENSSL_SUPPORT is defined,
83
+ # which nothing here defines.
84
+ if self.settings.os != "Emscripten":
85
+ self.requires("cpp-httplib/0.47.0")
74
86
  # Viewer-only runtime dep. PlatformFolders resolves the OS-appropriate
75
87
  # cache / config directory on native; not needed on web. Skip under
76
88
  # Emscripten — the wasm viewer never links it and the package fails to
@@ -20,6 +20,7 @@
20
20
 
21
21
  #include <nodehammer/visibility.hpp>
22
22
 
23
+ #include <filesystem>
23
24
  #include <span>
24
25
  #include <string_view>
25
26
 
@@ -39,6 +40,27 @@ struct RunOptions {
39
40
  /// typed the command, wrong when a program called a function. A TTY alone
40
41
  /// cannot tell those apart: an interactive interpreter has one.
41
42
  bool pager = false;
43
+
44
+ /// Where the wasm viewer runtime is, for callers that know.
45
+ ///
46
+ /// `viewer --web` needs a directory of Emscripten output that this library
47
+ /// cannot build and, in an embedded process, cannot find: under the Python
48
+ /// wheel the running executable is the interpreter, so the install-tree
49
+ /// guess resolves to the wrong prefix entirely. The `nodehammer-web` package
50
+ /// knows exactly where its own payload is, so it says.
51
+ ///
52
+ /// Empty means "I do not know of one", which is not an error — it costs one
53
+ /// rung of the search. It is a *default*, not an override: `--web-assets`
54
+ /// and `NODEHAMMER_WEB_ASSETS` both outrank it, so a person can point at a
55
+ /// locally built runtime without uninstalling anything.
56
+ ///
57
+ /// A `path` rather than a `string`, like every other file-taking member of
58
+ /// this API — and here it is not only consistency. On Windows a `path` holds
59
+ /// `wchar_t`, and building one from a narrow string decodes it in the active
60
+ /// code page, so a runtime under a directory with a non-ASCII name would
61
+ /// arrive mangled. Callers that have the real encoding — nanobind's
62
+ /// `stl/filesystem.h` caster above all — can hand it over intact.
63
+ std::filesystem::path webAssets;
42
64
  };
43
65
 
44
66
  /// Run one command line. Returns the exit code the executable would have.