belfryscad 1.0.1__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 (223) hide show
  1. belfryscad-1.0.1/.claude/commands/build-and-run.md +13 -0
  2. belfryscad-1.0.1/.github/workflows/release.yml +181 -0
  3. belfryscad-1.0.1/.github/workflows/test.yml +53 -0
  4. belfryscad-1.0.1/.gitignore +60 -0
  5. belfryscad-1.0.1/CLAUDE.md +214 -0
  6. belfryscad-1.0.1/LICENSE +21 -0
  7. belfryscad-1.0.1/PKG-INFO +15 -0
  8. belfryscad-1.0.1/PRD.md +677 -0
  9. belfryscad-1.0.1/README.md +72 -0
  10. belfryscad-1.0.1/TODO.md +17 -0
  11. belfryscad-1.0.1/docs/debugger.md +152 -0
  12. belfryscad-1.0.1/docs/docsgen.md +713 -0
  13. belfryscad-1.0.1/docs/editor.md +1025 -0
  14. belfryscad-1.0.1/docs/rendering.md +219 -0
  15. belfryscad-1.0.1/docs/wysiwyg.md +198 -0
  16. belfryscad-1.0.1/pyproject.toml +93 -0
  17. belfryscad-1.0.1/resources/BelfrySCAD.png +0 -0
  18. belfryscad-1.0.1/resources/belfryscad.icns +0 -0
  19. belfryscad-1.0.1/resources/belfryscad.ico +0 -0
  20. belfryscad-1.0.1/resources/belfryscad.scad +6 -0
  21. belfryscad-1.0.1/scripts/compare_docs_images.py +206 -0
  22. belfryscad-1.0.1/scripts/update_library_versions.py +164 -0
  23. belfryscad-1.0.1/scripts/validate_against_openscad.py +460 -0
  24. belfryscad-1.0.1/scripts/verify_3mf_interop.py +137 -0
  25. belfryscad-1.0.1/scripts/verify_ai_ask_user.py +349 -0
  26. belfryscad-1.0.1/scripts/verify_ai_attachments.py +155 -0
  27. belfryscad-1.0.1/scripts/verify_ai_cli_paths.py +255 -0
  28. belfryscad-1.0.1/scripts/verify_ai_echo.py +144 -0
  29. belfryscad-1.0.1/scripts/verify_ai_followups.py +98 -0
  30. belfryscad-1.0.1/scripts/verify_ai_render_tools.py +1131 -0
  31. belfryscad-1.0.1/scripts/verify_auto_reload.py +178 -0
  32. belfryscad-1.0.1/scripts/verify_bracket_colors.py +190 -0
  33. belfryscad-1.0.1/scripts/verify_diff_contrast.py +126 -0
  34. belfryscad-1.0.1/scripts/verify_escape_cancel.py +118 -0
  35. belfryscad-1.0.1/scripts/verify_examples_menu.py +146 -0
  36. belfryscad-1.0.1/scripts/verify_export_check.py +278 -0
  37. belfryscad-1.0.1/scripts/verify_find_bar.py +275 -0
  38. belfryscad-1.0.1/scripts/verify_followup_attribution.py +111 -0
  39. belfryscad-1.0.1/scripts/verify_gestures.py +138 -0
  40. belfryscad-1.0.1/scripts/verify_indent_guides.py +186 -0
  41. belfryscad-1.0.1/scripts/verify_measure_snap.py +162 -0
  42. belfryscad-1.0.1/scripts/verify_measure_tools.py +338 -0
  43. belfryscad-1.0.1/scripts/verify_modifier_display.py +160 -0
  44. belfryscad-1.0.1/scripts/verify_move_lines.py +248 -0
  45. belfryscad-1.0.1/scripts/verify_no_save_prompts.py +145 -0
  46. belfryscad-1.0.1/scripts/verify_no_stray_dock_tab.py +143 -0
  47. belfryscad-1.0.1/scripts/verify_profile_export.py +145 -0
  48. belfryscad-1.0.1/scripts/verify_profile_goto.py +89 -0
  49. belfryscad-1.0.1/scripts/verify_profile_report.py +56 -0
  50. belfryscad-1.0.1/scripts/verify_profile_tree.py +141 -0
  51. belfryscad-1.0.1/scripts/verify_reset_layout.py +155 -0
  52. belfryscad-1.0.1/scripts/verify_review_button.py +126 -0
  53. belfryscad-1.0.1/scripts/verify_show_vertices_toggle.py +161 -0
  54. belfryscad-1.0.1/scripts/verify_string_continuation.py +119 -0
  55. belfryscad-1.0.1/scripts/verify_tool_buttons.py +146 -0
  56. belfryscad-1.0.1/scripts/verify_unmatched_brackets.py +195 -0
  57. belfryscad-1.0.1/scripts/verify_use_library_dialog.py +205 -0
  58. belfryscad-1.0.1/scripts/verify_viewport_clear.py +130 -0
  59. belfryscad-1.0.1/scripts/verify_vnf_validate_button.py +210 -0
  60. belfryscad-1.0.1/src/belfryscad/__init__.py +0 -0
  61. belfryscad-1.0.1/src/belfryscad/__main__.py +2 -0
  62. belfryscad-1.0.1/src/belfryscad/docsgen/__init__.py +252 -0
  63. belfryscad-1.0.1/src/belfryscad/docsgen/blocks.py +1087 -0
  64. belfryscad-1.0.1/src/belfryscad/docsgen/errorlog.py +46 -0
  65. belfryscad-1.0.1/src/belfryscad/docsgen/filehashes.py +72 -0
  66. belfryscad-1.0.1/src/belfryscad/docsgen/imagemanager.py +425 -0
  67. belfryscad-1.0.1/src/belfryscad/docsgen/logmanager.py +110 -0
  68. belfryscad-1.0.1/src/belfryscad/docsgen/mdimggen.py +265 -0
  69. belfryscad-1.0.1/src/belfryscad/docsgen/parser.py +1089 -0
  70. belfryscad-1.0.1/src/belfryscad/docsgen/preview.py +371 -0
  71. belfryscad-1.0.1/src/belfryscad/docsgen/runner.py +271 -0
  72. belfryscad-1.0.1/src/belfryscad/docsgen/target.py +14 -0
  73. belfryscad-1.0.1/src/belfryscad/docsgen/target_githubwiki.py +45 -0
  74. belfryscad-1.0.1/src/belfryscad/docsgen/target_wiki.py +223 -0
  75. belfryscad-1.0.1/src/belfryscad/docsgen/unicode_math.py +277 -0
  76. belfryscad-1.0.1/src/belfryscad/docsgen/utils.py +19 -0
  77. belfryscad-1.0.1/src/belfryscad/engine/__init__.py +0 -0
  78. belfryscad-1.0.1/src/belfryscad/engine/renderer.py +2485 -0
  79. belfryscad-1.0.1/src/belfryscad/export_name.py +105 -0
  80. belfryscad-1.0.1/src/belfryscad/exporters.py +147 -0
  81. belfryscad-1.0.1/src/belfryscad/headless.py +363 -0
  82. belfryscad-1.0.1/src/belfryscad/headless_render.py +486 -0
  83. belfryscad-1.0.1/src/belfryscad/main.py +500 -0
  84. belfryscad-1.0.1/src/belfryscad/png_writer.py +148 -0
  85. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/children.scad +30 -0
  86. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/data/heightmap.dat +26 -0
  87. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/data/knob.stl +0 -0
  88. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/gyroid.scad +29 -0
  89. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/hull.scad +30 -0
  90. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/import.scad +26 -0
  91. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/intersection_for.scad +18 -0
  92. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/minkowski.scad +24 -0
  93. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/multmatrix.scad +29 -0
  94. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/offset.scad +21 -0
  95. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/polyhedron.scad +26 -0
  96. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/projection.scad +32 -0
  97. belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/surface.scad +22 -0
  98. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/color.scad +19 -0
  99. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/csg.scad +30 -0
  100. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/linear_extrude.scad +21 -0
  101. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/mirror.scad +20 -0
  102. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/modules.scad +22 -0
  103. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/polygon.scad +19 -0
  104. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/resize.scad +15 -0
  105. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/rotate_extrude.scad +18 -0
  106. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/text.scad +22 -0
  107. belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/transforms.scad +16 -0
  108. belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/functions.scad +21 -0
  109. belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/list_comprehensions.scad +19 -0
  110. belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/recursion.scad +22 -0
  111. belfryscad-1.0.1/src/belfryscad/resources/examples/examples.json +32 -0
  112. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-first.svg +5 -0
  113. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-last.svg +5 -0
  114. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-next.svg +4 -0
  115. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-pause.svg +4 -0
  116. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-play.svg +3 -0
  117. belfryscad-1.0.1/src/belfryscad/resources/icons/anim-prev.svg +4 -0
  118. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-continue.svg +4 -0
  119. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-pause.svg +4 -0
  120. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-restart.svg +49 -0
  121. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-into.svg +66 -0
  122. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-out.svg +66 -0
  123. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-over.svg +60 -0
  124. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-to-child.svg +82 -0
  125. belfryscad-1.0.1/src/belfryscad/resources/icons/debug-stop.svg +3 -0
  126. belfryscad-1.0.1/src/belfryscad/resources/icons/eyedropper.svg +79 -0
  127. belfryscad-1.0.1/src/belfryscad/resources/icons/tool-rotate.svg +57 -0
  128. belfryscad-1.0.1/src/belfryscad/resources/icons/tool-scale.svg +75 -0
  129. belfryscad-1.0.1/src/belfryscad/resources/icons/tool-translate.svg +46 -0
  130. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-animate.svg +11 -0
  131. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-debug.svg +16 -0
  132. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-export.svg +67 -0
  133. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-measure-angle.svg +9 -0
  134. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-measure-linear.svg +10 -0
  135. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-new.svg +62 -0
  136. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-open.svg +42 -0
  137. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-redo.svg +6 -0
  138. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-render.svg +13 -0
  139. belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-undo.svg +6 -0
  140. belfryscad-1.0.1/src/belfryscad/resources/icons/view-orthographic.svg +9 -0
  141. belfryscad-1.0.1/src/belfryscad/resources/icons/view-perspective.svg +57 -0
  142. belfryscad-1.0.1/src/belfryscad/resources/libraries.json +2478 -0
  143. belfryscad-1.0.1/src/belfryscad/scad_deps.py +167 -0
  144. belfryscad-1.0.1/src/belfryscad/scad_literals.py +91 -0
  145. belfryscad-1.0.1/src/belfryscad/scad_temp.py +117 -0
  146. belfryscad-1.0.1/src/belfryscad/settings.py +72 -0
  147. belfryscad-1.0.1/src/belfryscad/vnf_validate.py +410 -0
  148. belfryscad-1.0.1/src/belfryscad/window/__init__.py +0 -0
  149. belfryscad-1.0.1/src/belfryscad/window/ai_chat.py +1275 -0
  150. belfryscad-1.0.1/src/belfryscad/window/ai_cli.py +194 -0
  151. belfryscad-1.0.1/src/belfryscad/window/ai_copilot_cli.py +232 -0
  152. belfryscad-1.0.1/src/belfryscad/window/ai_mcp.py +149 -0
  153. belfryscad-1.0.1/src/belfryscad/window/ai_providers.py +451 -0
  154. belfryscad-1.0.1/src/belfryscad/window/ai_question_dialog.py +239 -0
  155. belfryscad-1.0.1/src/belfryscad/window/ai_secrets.py +27 -0
  156. belfryscad-1.0.1/src/belfryscad/window/ai_tools.py +1712 -0
  157. belfryscad-1.0.1/src/belfryscad/window/animate.py +386 -0
  158. belfryscad-1.0.1/src/belfryscad/window/bosl2_docs.py +169 -0
  159. belfryscad-1.0.1/src/belfryscad/window/color_themes.py +142 -0
  160. belfryscad-1.0.1/src/belfryscad/window/console.py +250 -0
  161. belfryscad-1.0.1/src/belfryscad/window/customizer.py +1200 -0
  162. belfryscad-1.0.1/src/belfryscad/window/customizer_param_dialog.py +417 -0
  163. belfryscad-1.0.1/src/belfryscad/window/data_viewers.py +8715 -0
  164. belfryscad-1.0.1/src/belfryscad/window/debugger.py +921 -0
  165. belfryscad-1.0.1/src/belfryscad/window/docs_pane.py +1075 -0
  166. belfryscad-1.0.1/src/belfryscad/window/document_manager.py +46 -0
  167. belfryscad-1.0.1/src/belfryscad/window/editor.py +2122 -0
  168. belfryscad-1.0.1/src/belfryscad/window/library_manager.py +954 -0
  169. belfryscad-1.0.1/src/belfryscad/window/main_window.py +4670 -0
  170. belfryscad-1.0.1/src/belfryscad/window/orientation_cube.py +486 -0
  171. belfryscad-1.0.1/src/belfryscad/window/preferences.py +1018 -0
  172. belfryscad-1.0.1/src/belfryscad/window/scad_format.py +518 -0
  173. belfryscad-1.0.1/src/belfryscad/window/ui_colors.py +318 -0
  174. belfryscad-1.0.1/src/belfryscad/window/viewport.py +1388 -0
  175. belfryscad-1.0.1/tests/conftest.py +20 -0
  176. belfryscad-1.0.1/tests/test_ai_chat.py +69 -0
  177. belfryscad-1.0.1/tests/test_ai_cli.py +189 -0
  178. belfryscad-1.0.1/tests/test_ai_mcp.py +125 -0
  179. belfryscad-1.0.1/tests/test_ai_providers.py +478 -0
  180. belfryscad-1.0.1/tests/test_ai_secrets.py +73 -0
  181. belfryscad-1.0.1/tests/test_ai_tools.py +453 -0
  182. belfryscad-1.0.1/tests/test_bosl2_docs.py +165 -0
  183. belfryscad-1.0.1/tests/test_clipboard_target.py +113 -0
  184. belfryscad-1.0.1/tests/test_color_themes.py +115 -0
  185. belfryscad-1.0.1/tests/test_compare_docs_images.py +91 -0
  186. belfryscad-1.0.1/tests/test_customizer.py +349 -0
  187. belfryscad-1.0.1/tests/test_customizer_ast_scan.py +88 -0
  188. belfryscad-1.0.1/tests/test_data_viewers.py +3102 -0
  189. belfryscad-1.0.1/tests/test_debug_session.py +102 -0
  190. belfryscad-1.0.1/tests/test_debugger.py +1158 -0
  191. belfryscad-1.0.1/tests/test_docsgen.py +1615 -0
  192. belfryscad-1.0.1/tests/test_editor_brackets.py +163 -0
  193. belfryscad-1.0.1/tests/test_editor_indent.py +298 -0
  194. belfryscad-1.0.1/tests/test_editor_min_width.py +74 -0
  195. belfryscad-1.0.1/tests/test_error_prefix.py +78 -0
  196. belfryscad-1.0.1/tests/test_examples.py +50 -0
  197. belfryscad-1.0.1/tests/test_export_format_choice.py +110 -0
  198. belfryscad-1.0.1/tests/test_export_model.py +131 -0
  199. belfryscad-1.0.1/tests/test_export_modifiers.py +64 -0
  200. belfryscad-1.0.1/tests/test_export_name.py +198 -0
  201. belfryscad-1.0.1/tests/test_headless.py +344 -0
  202. belfryscad-1.0.1/tests/test_headless_render.py +317 -0
  203. belfryscad-1.0.1/tests/test_library_catalog.py +140 -0
  204. belfryscad-1.0.1/tests/test_main.py +141 -0
  205. belfryscad-1.0.1/tests/test_message_labels.py +98 -0
  206. belfryscad-1.0.1/tests/test_png_writer.py +75 -0
  207. belfryscad-1.0.1/tests/test_read_only_tab.py +17 -0
  208. belfryscad-1.0.1/tests/test_render_reframe.py +97 -0
  209. belfryscad-1.0.1/tests/test_render_worker.py +75 -0
  210. belfryscad-1.0.1/tests/test_renderer.py +610 -0
  211. belfryscad-1.0.1/tests/test_scad_deps.py +197 -0
  212. belfryscad-1.0.1/tests/test_scad_format.py +290 -0
  213. belfryscad-1.0.1/tests/test_scad_format_ast.py +73 -0
  214. belfryscad-1.0.1/tests/test_scad_temp.py +169 -0
  215. belfryscad-1.0.1/tests/test_settings_isolation.py +109 -0
  216. belfryscad-1.0.1/tests/test_tab_tooltip.py +97 -0
  217. belfryscad-1.0.1/tests/test_ui_colors.py +121 -0
  218. belfryscad-1.0.1/tests/test_unicode_math.py +102 -0
  219. belfryscad-1.0.1/tests/test_use_statement.py +134 -0
  220. belfryscad-1.0.1/tests/test_viewport.py +56 -0
  221. belfryscad-1.0.1/tests/test_vnf_validate.py +250 -0
  222. belfryscad-1.0.1/tests/test_working_dir.py +96 -0
  223. belfryscad-1.0.1/uv.lock +1343 -0
@@ -0,0 +1,13 @@
1
+ Build, commit, and run BelfrySCAD locally. Steps:
2
+
3
+ 1. Review all files changed since the last commit (`git diff`). For each changed file under `docs/` or `src/`, check that the corresponding documentation in `docs/` accurately reflects the current code. Update any docs that are stale or missing coverage for the changes.
4
+
5
+ 2. Stage and commit all changes with `git commit -S`. Follow the project's commit conventions: short imperative subject line summarizing what changed, optional body explaining why. Include the `Co-Authored-By` trailer.
6
+
7
+ 3. Run `uv run python -m briefcase update macOS app -r` to sync source and requirements into the build tree.
8
+
9
+ 4. Run `uv run python -m briefcase build macOS app` to compile the app bundle.
10
+
11
+ 5. Run `uv run python -m briefcase run macOS app` to launch the app.
12
+
13
+ If any step fails, stop and report the error before continuing.
@@ -0,0 +1,181 @@
1
+ name: Release
2
+
3
+ on:
4
+ # Manually runnable so a packaging change can be proven before a tag is
5
+ # cut. The release and publish jobs are all tag-only, so a dispatch run
6
+ # builds Windows, Linux and the sdist/wheel, and publishes nothing.
7
+ workflow_dispatch:
8
+ push:
9
+ tags:
10
+ - 'v*'
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ build-windows:
17
+ runs-on: windows-latest
18
+ steps:
19
+ - uses: actions/checkout@v5
20
+
21
+ - uses: astral-sh/setup-uv@v7
22
+ with:
23
+ enable-cache: true
24
+ python-version: "3.12"
25
+
26
+ - run: uv sync --frozen --python 3.12
27
+
28
+ - name: Build Windows installer
29
+ run: |
30
+ uv run --python 3.12 briefcase create windows app
31
+ uv run --python 3.12 briefcase build windows app
32
+ uv run --python 3.12 briefcase package windows app
33
+
34
+ - name: Upload artifact
35
+ uses: actions/upload-artifact@v5
36
+ with:
37
+ name: windows-x86_64
38
+ path: dist/*.msi
39
+
40
+ build-linux:
41
+ # x86_64 only. An aarch64 AppImage was tried and does not work:
42
+ #
43
+ # * PySide6's only Linux ARM wheel is manylinux_2_39_aarch64, so the
44
+ # container has to be manylinux_2_39 -- the 2.28 image this project
45
+ # otherwise uses contains no installable PySide6 at all.
46
+ # * moderngl and glcontext publish no aarch64 wheels whatsoever, so
47
+ # that container then has to compile them, and it has no clang++.
48
+ # * briefcase's AppImage template has no system_requires step, no env
49
+ # hook and no package step, so there is no supported way to put a
50
+ # compiler in it.
51
+ #
52
+ # Linux ARM is therefore a source install needing a C++ toolchain, not
53
+ # a packaging target. Revisit if moderngl ships aarch64 wheels.
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v5
57
+
58
+ - name: Install build dependencies
59
+ run: sudo apt-get update && sudo apt-get install -y libx11-dev libegl1-mesa-dev
60
+
61
+ - uses: astral-sh/setup-uv@v7
62
+ with:
63
+ enable-cache: true
64
+ python-version: "3.12"
65
+
66
+ - run: uv sync --frozen --python 3.12
67
+
68
+ - name: Build Linux AppImage
69
+ run: |
70
+ uv run --python 3.12 briefcase create linux appimage
71
+ uv run --python 3.12 briefcase build linux appimage
72
+ uv run --python 3.12 briefcase package linux appimage
73
+
74
+ - name: Upload artifact
75
+ uses: actions/upload-artifact@v5
76
+ with:
77
+ name: linux-x86_64
78
+ path: dist/*.AppImage
79
+
80
+ build-pypi:
81
+ # Pure Python -- one sdist and one py3-none-any wheel, so no matrix.
82
+ # Runs on workflow_dispatch as well, to prove the sdist/wheel still
83
+ # build; the publish jobs below are the tag-only part.
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@v5
87
+
88
+ - uses: astral-sh/setup-uv@v7
89
+ with:
90
+ enable-cache: true
91
+ python-version: "3.12"
92
+
93
+ - run: uv build
94
+
95
+ - name: Upload artifact
96
+ uses: actions/upload-artifact@v5
97
+ with:
98
+ name: pypi-dist
99
+ path: dist/*
100
+
101
+ publish-testpypi:
102
+ needs: [build-pypi]
103
+ if: startsWith(github.ref, 'refs/tags/')
104
+ runs-on: ubuntu-latest
105
+ # The pypi environment, not a separate testpypi one: one environment to
106
+ # create, and one environment name to enter on both indexes' trusted
107
+ # publisher forms. The name is part of the OIDC subject PyPI matches
108
+ # against, so it is load-bearing even with no protection rules on it.
109
+ environment: pypi
110
+ # OIDC trusted publishing: no API token is stored anywhere. id-token
111
+ # write is what mints the short-lived OIDC credential, and job-level
112
+ # permissions replace the workflow-level block wholesale rather than
113
+ # adding to it, so contents has to be restated.
114
+ #
115
+ # openscad_cpp_evaluator uses token auth instead, because PyPI's
116
+ # trusted-publisher matcher does not yet recognise GitHub's immutable
117
+ # subject claims, emitted with no opt-out by repos created after
118
+ # 2026-07-15. That repo was created 2026-07-24; this one 2026-06-24, so
119
+ # it predates the change and matches the way PyPI still expects.
120
+ permissions:
121
+ id-token: write
122
+ contents: read
123
+ steps:
124
+ - uses: actions/download-artifact@v5
125
+ with:
126
+ name: pypi-dist
127
+ path: dist
128
+
129
+ - name: Publish to TestPyPI
130
+ uses: pypa/gh-action-pypi-publish@release/v1
131
+ with:
132
+ repository-url: https://test.pypi.org/legacy/
133
+ packages-dir: dist
134
+ # TestPyPI is a dry run we may re-upload to under one version while
135
+ # fixing a packaging problem. Never set this on publish-pypi -- a
136
+ # real PyPI filename collision must fail loudly, not skip silently.
137
+ skip-existing: true
138
+
139
+ publish-pypi:
140
+ needs: [publish-testpypi]
141
+ if: startsWith(github.ref, 'refs/tags/')
142
+ runs-on: ubuntu-latest
143
+ # The environment carries no protection rules today, so a tag runs
144
+ # straight through to PyPI with nothing to stop it -- and a PyPI upload
145
+ # cannot be undone or replaced at the same version. Adding a required
146
+ # reviewer to the environment reinstates that brake; GitHub permits
147
+ # approving your own deployment unless "Prevent self-review" is ticked.
148
+ environment: pypi
149
+ # OIDC trusted publishing -- see the comment on publish-testpypi above.
150
+ permissions:
151
+ id-token: write
152
+ contents: read
153
+ steps:
154
+ - uses: actions/download-artifact@v5
155
+ with:
156
+ name: pypi-dist
157
+ path: dist
158
+
159
+ - name: Publish to PyPI
160
+ uses: pypa/gh-action-pypi-publish@release/v1
161
+ with:
162
+ packages-dir: dist
163
+
164
+ release:
165
+ needs: [build-windows, build-linux, build-pypi]
166
+ # Only a tag publishes. A workflow_dispatch run exists to prove the
167
+ # builds still work, and must not create a release.
168
+ if: startsWith(github.ref, 'refs/tags/')
169
+ runs-on: ubuntu-latest
170
+ steps:
171
+ - uses: actions/download-artifact@v5
172
+ with:
173
+ path: artifacts
174
+ merge-multiple: true
175
+
176
+ - name: Create GitHub Release
177
+ uses: softprops/action-gh-release@v2
178
+ with:
179
+ files: artifacts/*
180
+ generate_release_notes: true
181
+ draft: true
@@ -0,0 +1,53 @@
1
+ name: Tests
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ platforms:
10
+ # macOS and Windows were only ever built by release.yml, which runs
11
+ # briefcase but never pytest -- so the suite had never run on either,
12
+ # on a project mostly developed on macOS. macos-latest is arm64, which
13
+ # also gives the .3mf export that ARM platforms gained when lib3mf was
14
+ # dropped its first real coverage.
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ # One platform failing should not hide the result on the others.
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, macos-latest, windows-latest]
21
+
22
+ steps:
23
+ - uses: actions/checkout@v5
24
+
25
+ - name: Install Qt/GL dependencies
26
+ if: runner.os == 'Linux'
27
+ run: sudo apt-get update && sudo apt-get install -y libegl1 libopengl0 libgl1-mesa-dri
28
+
29
+ - uses: astral-sh/setup-uv@v7
30
+ with:
31
+ enable-cache: true
32
+
33
+ - run: uv sync --frozen
34
+
35
+ - run: uv run pytest
36
+
37
+ # Branch protection requires a check called exactly "test". A matrix job
38
+ # reports as "test (ubuntu-latest)" and friends, so a job of that name
39
+ # would never produce the required check and every PR would sit BLOCKED
40
+ # with all three platforms green -- which is precisely what happened.
41
+ # This gate keeps that name stable however the matrix changes.
42
+ test:
43
+ needs: platforms
44
+ if: always()
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - name: Check every platform passed
48
+ run: |
49
+ if [ "${{ needs.platforms.result }}" != "success" ]; then
50
+ echo "platform matrix: ${{ needs.platforms.result }}"
51
+ exit 1
52
+ fi
53
+ echo "all platforms passed"
@@ -0,0 +1,60 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ *.egg-info/
7
+ .eggs/
8
+ dist/
9
+ build/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # uv
18
+ .uv/
19
+
20
+ # Distribution / packaging
21
+ *.whl
22
+ *.tar.gz
23
+
24
+ # IDE
25
+ .vscode/
26
+ .idea/
27
+ *.swp
28
+ *.swo
29
+
30
+ # macOS
31
+ .DS_Store
32
+
33
+ # Briefcase
34
+ logs/
35
+ dist/
36
+
37
+ # Icon build artifacts
38
+ resources/*.iconset/
39
+
40
+ # Testing
41
+ .coverage
42
+ htmlcov/
43
+
44
+ # Scratch files
45
+ scratch/
46
+ test*.scad
47
+ ntest*.scad
48
+ # A local working copy of BOSL2's, sitting next to the test-*.scad that
49
+ # includes it. Twice swept into a commit by a `git add -A`; add -f if it
50
+ # is ever meant to be tracked.
51
+ nurbs.scad
52
+ test30_mesh.off
53
+ test30_surface.dat
54
+ profile_eval.py
55
+
56
+ # graphify
57
+ graphify-out/
58
+
59
+ # Wiki content — its own git repo, pushed to BelfrySCAD.wiki.git
60
+ wiki/
@@ -0,0 +1,214 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ BelfrySCAD is a hybrid procedural CAD application combining OpenSCAD-style script-based modeling with live WYSIWYG 3D interaction. Its defining feature is **bidirectional synchronization** between source code and 3D geometry — editing code or dragging geometry keeps both views in sync.
8
+
9
+ **Status**: In active development. Core pipeline, rendering, editor, and several WYSIWYG features are implemented. Full design in `PRD.md`.
10
+
11
+ ## Technology Stack
12
+
13
+ - **UI Framework**: PySide6 (Qt)
14
+ - **Code Editor**: `QPlainTextEdit` + `QSyntaxHighlighter` (PySide6 built-ins; text layer only — not semantically aware)
15
+ - **Parser**: openscad_cpp_parser (C++, Bison `lalr1.cc`; generates an AST with file/line/col/span metadata; parses full OpenSCAD syntax but has no knowledge of built-in functions/modules — the evaluator implements all built-ins). Not a dependency of this project directly: it is vendored at `external/openscad_cpp_parser` inside openscad_cpp_evaluator and built with it.
16
+ - **Evaluator**: openscad_cpp_evaluator ≥0.30.0 (C++ with nanobind bindings; walks the parser's AST and produces Manifold geometry — the two-pass resolve/generate pipeline, built-ins, `ManifoldCache`, profiling; GUI-agnostic, callback-injection API). The only OpenSCAD-side dependency in `pyproject.toml`, fetched from PyPI as a wheel; see its own `CLAUDE.md` for the full architecture reference.
17
+ - **CSG Kernel**: Manifold (union, difference, intersection, boolean ops)
18
+ - **Renderer**: ModernGL (GPU mesh rendering, camera controls)
19
+ - **Language**: Python
20
+
21
+ ## Core Architecture
22
+
23
+ The pipeline flows strictly one direction during normal operation:
24
+
25
+ ```
26
+ Source Code → Code Editor → openscad_cpp_parser (AST) → Evaluator → Manifold (CSG/mesh) → ModernGL → PySide6 UI
27
+ ```
28
+
29
+ **The AST is the single source of truth** — not the rendered geometry, not the editor text.
30
+
31
+ ### Critical Constraint: Strict Parser
32
+
33
+ The parser produces **no partial AST** — it either succeeds fully or fails entirely. Handle the no-AST state gracefully:
34
+ - Cache the last valid AST
35
+ - Display last valid geometry while code is invalid
36
+ - Never block the UI or break the viewport
37
+
38
+ ### Bidirectional Loop (future-critical, v1 groundwork required)
39
+
40
+ Dragging geometry in the viewport:
41
+ ```
42
+ Drag event → ray cast → pick geometry ID → map ID to AST node (via span) → modify AST parameter → regenerate code + model
43
+ ```
44
+
45
+ Requires every AST node to carry both its **source span** (file/line/col) and its **geometry ID(s)** from Manifold output. This mapping is the hardest design problem in the project. See `docs/wysiwyg.md` for the full interaction design and openscad_cpp_evaluator's own `CLAUDE.md` for the AST ↔ geometry ID mapping pattern.
46
+
47
+ ## Key Design Requirements
48
+
49
+ - **Code ↔ Geometry mapping**: every geometry-producing AST node owns an `originalID`; the `originalID → AST node` table rebuilds on each render trigger.
50
+ - **Stability under invalid code**: UI must never crash or go blank.
51
+ - **Deterministic regeneration**: AST → geometry must be reproducible with no hidden rendering state. Every render trigger walks the whole tree, but unchanged subtrees skip actual Manifold work via a content-hash cache (`ManifoldCache`, see openscad_cpp_evaluator's `CLAUDE.md`) — a fresh AST/CSG tree is still built every render (no incremental *parsing*), but a node whose resolved content matches a previous render/debug pause reuses that prior result instead of recomputing it.
52
+ - **Performance**: <200ms model regeneration for small/medium models; 60 FPS viewport.
53
+
54
+ ## File Format & Export
55
+
56
+ - **File format**: `.scad` (OpenSCAD-compatible plain text)
57
+ - **Language**: Full OpenSCAD language (variables, functions, modules, loops, conditionals, all built-in primitives and transforms)
58
+ - **Language extension — `$export_name`**: seeded with the input file's basename before
59
+ the script runs, assignable by the script, and used (sanitised to `[A-Za-z0-9_+.-]`,
60
+ everything else becoming one underscore each) as the Export dialog's default filename.
61
+ Needed **no evaluator change**: `viewport_params` seeds arbitrary `$`-names and
62
+ `Evaluator.dyn` returns them all. Seeded in the CLI and debugger too, so a script
63
+ reading it never finds it undefined. See `belfryscad/export_name.py` and
64
+ `docs/rendering.md`. Not part of upstream OpenSCAD.
65
+ - **Language extension — `render()` in expression position**: `obj = render() { cube(1); };`
66
+ builds its children's geometry, measures it, and returns an `object()` with `vertices`,
67
+ `faces`, `volume`, `area`, `genus`, `boundingbox` and `dim` — then **discards the geometry**
68
+ (nothing is drawn). This is the only way a script can inspect its own geometry.
69
+ `polyhedron()` and `polygon()` accept the object directly, and `polyhedron()` also takes
70
+ BOSL2's `[vertices, faces]` 2-list, so the mesh round-trips in one call. Two consequences
71
+ worth knowing: **`render` is a reserved keyword** (it can no longer
72
+ be a variable/module/function/argument/member name — LALR(1) leaves no alternative), and
73
+ **`obj = render() cube(1);` does not parse** — a bare call's `child_statement` swallows the
74
+ `;`, so the braced form is required. Not part of upstream OpenSCAD. Full reference in
75
+ openscad_cpp_evaluator's `CLAUDE.md`; user-facing docs on the wiki's
76
+ Language-Other-Modules page.
77
+ - **Language extension — `children(separate=true)`**: the forwarded children are spliced into
78
+ the surrounding block as **real statements**, one per child, instead of arriving as the single
79
+ statement a `children()` call normally is. Everything follows from that:
80
+ `module frame() { difference() children(separate=true); }` subtracts children 1..n from
81
+ child 0; `$children` **counts** the spliced members; and `children(i)` indexes them, which is
82
+ what makes a recursive n-ary module (forwarding "all the rest" to itself) possible at all.
83
+ Accepted positionally (`children([0:2], true)`) and alongside an index. A forward that selects
84
+ nothing contributes **zero** statements. No effect on `hull()`/`minkowski()`, which read bodies
85
+ rather than operand groups.
86
+ **`$children` therefore diverges from OpenSCAD**, but only for a block that types
87
+ `separate=true` — every other shape counts identically (verified against the reference).
88
+ OpenSCAD does not reject the argument, it silently ignores it, so such a script runs there and
89
+ quietly produces different geometry; no warning we can emit changes that.
90
+ Implemented entirely in openscad_cpp_evaluator (`Evaluator::expandChildStatements` +
91
+ `Op::CsgGroupChildren`); no parser or GUI change.
92
+ - **Language extension — feature detection**: `$_SUPPORTED_FEATURE` is `true` wherever
93
+ `supported_feature()` can be called — a capability name, not a vendor one, so any evaluator
94
+ adding the function is meant to set it. Check it before calling — you cannot safely call what
95
+ you don't know exists — and write it `!is_undef($_SUPPORTED_FEATURE) && supported_feature(...)`,
96
+ which is **silent** in OpenSCAD: `is_undef()` reads an unknown variable there without a warning,
97
+ and `&&` short-circuits past the unknown call.
98
+ `$_BELFRYSCAD` holds the evaluator version as
99
+ `[major, minor, patch]`, and `supported_feature("name")` returns the level at which this build
100
+ implements a named feature (`render-expr`, `polyhedron-vnf`, `separate-children`,
101
+ `minkowski-diff`, `sphere-styles`, `export-name`, `simplify-op`, `expr-import`,
102
+ `object-function`, `roof-op` — one for every documented extension) or **0** for one it
103
+ does not — including names it has never heard of, so probing for a future feature is safe.
104
+ Both are `undef` in OpenSCAD, so the guard is portable. They exist because OpenSCAD silently
105
+ ignores unknown *arguments*: `children(separate=true)` runs there and renders the wrong shape
106
+ with no warning, so `assert(supported_feature(...))` is the only way a script can refuse.
107
+ Version comes from `pyproject.toml` via CMake, so it tracks releases with no code change.
108
+ - **Export**: 3MF (default), STL, OBJ, AMF, OFF, PLY, VRML, X3D — all written by openscad_cpp_evaluator's `export.cpp`, which owns the colour pipeline and mesh repair; `exporters.py` is just the interface. Colour is carried by 3MF, AMF, OBJ (companion `.mtl`), PLY, VRML and X3D; STL and OFF are geometry only. AMF puts colour on the `<volume>`, so a welded multi-coloured solid is written as one object of several volumes. **Never restate the format list** — the dialog (`_EXPORT_FORMATS`), the CLI (`headless._export_extensions`) and the evaluator's Python facade all read it from `exportExtensions()`. Three hand-written copies previously agreed with each other and not with the C++ writer table, so `.off` was writable by neither GUI nor CLI while every test passed (the tests compared the copies to each other). Fixed in #295 / evaluator 0.46.0. STEP under investigation (Manifold produces triangle meshes; STEP is B-rep, so any export would be a faceted solid of limited downstream value)
109
+ - **Export object split**: top level is an implicit union, so every format writes the union, never the raw body list. The evaluator's `splitBodiesForExport` cuts it into objects that never share volume — one per colour (later `color()` wins an overlap), then one per connected component — and carries per-triangle colour where a CSG merge produced it. The GUI calls `exporters.export_model(path, evaluator.geometry)` and logs the warnings it returns. See `docs/rendering.md`'s Export section.
110
+ - **Export workflow**: if no current render exists, Export triggers a render first
111
+
112
+ ## Render Triggers
113
+
114
+ No live preview. Full Manifold CSG processing runs when:
115
+
116
+ - The user selects **Render** (toolbar or Design menu)
117
+ - A **gizmo drag commits** (mouse-up)
118
+ - An **"Edit as..." literal edit is saved** (Save button in the editable Path/Grid/Matrix/Affine viewers, opened from the code editor's right-click menu)
119
+ - A **file is opened** (`open_file_by_path` triggers `_render` after the tab is created)
120
+ - A **file is saved** (`_write_file` triggers `_render` after writing)
121
+ - The user stops editing **Customizer** fields for 2 seconds (`MainWindow._customizer_render_timer`, a debounced single-shot `QTimer` restarted on every edit — see `docs/editor.md`'s CustomizerPane section)
122
+ - An **animation frame advances** (`MainWindow._on_animate_frame` renders per tick; a tick is skipped while a render is still in flight, since overlapping renders invoke the parser concurrently and can segfault)
123
+ - A **watched file changes on disk**, with **Design ▸ Automatic Reload and Render** on (`_on_watched_file_changed`; skipped for a tab with unsaved edits, which are never overwritten)
124
+ - The user **accepts an AI proposal** in the chat pane (`_on_ai_proposal_accepted` goes through `replace_span` + `source_edited_externally`, the same path "Edit as..." uses)
125
+ - The **AI calls its `render` tool** (`AIToolContext.request_render`, wired to `_render_threadsafe`) — for a script it has not itself changed
126
+
127
+ **"Render with Profiling"** (Design menu) is a separate, explicitly opt-in diagnostic trigger — not part of this automatic/WYSIWYG set — that turns on per-call-site timing instrumentation for that one render. See openscad_cpp_evaluator's `CLAUDE.md` for the profiling instrumentation.
128
+
129
+ The viewport always shows the last render's result; it stays static while the user edits code.
130
+
131
+ ## V1 Scope Boundaries
132
+
133
+ **In scope**: Script editing, real-time 3D rendering, basic WYSIWYG drag interaction, CSG operations, graceful invalid-code handling.
134
+
135
+ **Explicitly out of scope for v1**: Constraint solver, collaborative editing, cloud modeling, incremental/tolerant parsing, node-based visual programming, plugin system.
136
+
137
+ ## Versioning
138
+
139
+ Every PR bumps the version (`version` in both `[project]` and `[tool.briefcase]` in `pyproject.toml`, kept identical — then run `uv lock` to sync `uv.lock`'s pinned self-version). Patch bump at minimum; use judgment for minor/major on larger changes. Do this as part of preparing the PR, alongside the commit.
140
+
141
+ ### `briefcase update` leaves dependencies at their old versions
142
+
143
+ Plain `briefcase update` refreshes your app's own code and nothing else. A
144
+ pinned dependency stays at whatever version was last installed into the bundle,
145
+ however far `pyproject.toml`/`uv.lock` have moved on. Use **`briefcase update -r`**
146
+ (`--update-requirements`) after any dependency bump.
147
+
148
+ Nothing warns about this, and every surface lies convincingly: the build prints
149
+ `Built ... BelfrySCAD.app`, and the app reports its own bumped version, because
150
+ that comes from the app package. Caught in practice with BelfrySCAD at 0.76.1
151
+ bundling `openscad_cpp_evaluator` **0.37.0** — three releases behind, so the
152
+ bundle had none of `render()` expressions, the touching-shells weld fix,
153
+ `polyhedron(vnf)` or `object()`'s delete entry.
154
+
155
+ Check what actually landed rather than trusting the build log:
156
+
157
+ ```
158
+ find build/belfryscad/macos/app/BelfrySCAD.app -name "*.dist-info" -maxdepth 6 \
159
+ | sed 's|.*/||' | grep -iE "belfryscad|openscad_cpp"
160
+ ```
161
+
162
+ Better still, run the bundled binary against a script exercising the new
163
+ feature — `BelfrySCAD.app/Contents/MacOS/BelfrySCAD -o /tmp/out.stl probe.scad`
164
+ — since that is the only check that proves the code inside the bundle, not the
165
+ dev environment, is the code you shipped.
166
+
167
+ ### The macOS bundle's Info.plist goes stale on every bump
168
+
169
+ `briefcase update` and `briefcase build` never rewrite `build/belfryscad/macos/app/BelfrySCAD.app/Contents/Info.plist` — only `briefcase create` generates it, from the `pyproject.toml` values as they stood at scaffold time. So after any version bump the bundle keeps reporting the *old* `CFBundleShortVersionString`, and the same applies to anything else the plist bakes in (`LSMinimumSystemVersion` from `[tool.briefcase.app.belfryscad.macOS] min_os_version`, the bundle identifier, the document-type declarations).
170
+
171
+ Nothing warns about this. Both values had drifted a long way before anyone looked: the plist still said `0.1.0` and `12.0` while `pyproject.toml` said `0.68.1` and `13.3` — the app itself reported 0.68.1 correctly the whole time, since that comes from the installed package, not the plist. The stale `LSMinimumSystemVersion` was the real problem: it advertised macOS 12 support for a bundle whose `openscad_cpp_evaluator` wheel needs 13.3.
172
+
173
+ To refresh it, regenerate the scaffold rather than hand-editing the plist (a hand edit is silently discarded the next time anyone runs `create`):
174
+
175
+ ```
176
+ mv build/belfryscad/macos/app build/belfryscad/macos/app.bak # ~950MB, keep until verified
177
+ uv run briefcase create --no-input
178
+ uv run briefcase build
179
+ /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" \
180
+ build/belfryscad/macos/app/BelfrySCAD.app/Contents/Info.plist
181
+ rm -rf build/belfryscad/macos/app.bak
182
+ ```
183
+
184
+ `create` re-downloads every wheel (PySide6 alone is ~440MB), so this is a few minutes — worth doing before cutting any real release or notarized build, not on every routine local rebuild. `CFBundleVersion` stays at `1`; that is briefcase's build-number default and is unrelated to the version string.
185
+
186
+ ## Documentation Generation
187
+
188
+ BelfrySCAD replaces both `openscad-docsgen` (`belfryscad --docsgen`) and
189
+ `openscad-mdimggen` (`belfryscad --mdimggen`): the same options, the same
190
+ markdown, but Examples and Figures render through this project's own
191
+ evaluator and offscreen renderer instead of launching the OpenSCAD binary
192
+ once per image. The GUI's **Docs**
193
+ pane (View ▸ Show Docs) runs the identical code over the live editor buffer,
194
+ so a library author sees the formatted docs, the validation errors and the
195
+ rendered example images without saving or leaving the app.
196
+
197
+ `openscad_docsgen`'s parser, blocks, error log and output targets are
198
+ vendored **unchanged** under `src/belfryscad/docsgen/` — only its two
199
+ OpenSCAD-launching modules (`imagemanager.py`, `logmanager.py`) are
200
+ reimplemented, keeping the upstream names so nothing else needed editing.
201
+ Keeping the parser byte-identical is what makes the pane's verdict
202
+ trustworthy: it is the same validation a real docs build performs. Full
203
+ details, including the camera/`--viewall` semantics and the APNG animation
204
+ support, in `docs/docsgen.md`.
205
+
206
+ ## Further Documentation
207
+
208
+ Detailed implementation notes live in `docs/`. AST Evaluator internals (scope processing, assignment order, built-ins reference, 2D/3D geometry handling, error format, `$variables` scoping, `include`/`use`, implementation quirks, and the Manifold provenance / AST ↔ geometry ID mapping API) now live in the separate `openscad_cpp_evaluator` package's own `CLAUDE.md`, not here.
209
+
210
+ - **`docs/wysiwyg.md`** — Viewport camera controls, selection model, transform gizmos, value overlay, and source rewrite rules for drag-to-edit.
211
+ - **`docs/debugger.md`** — `DebugSession` signals, call stack display, per-frame variable inspection, expression-level stepping, and `DebuggerPane` states.
212
+ - **`docs/rendering.md`** — Threaded rendering (`_RenderWorker`/`_RenderCallback`), cancellation, and progress indicator.
213
+ - **`docs/docsgen.md`** — `openscad_docsgen` documentation generation: the vendored parser, the evaluator-backed image/log backends that replace running the OpenSCAD binary, `belfryscad --docsgen`, and the GUI Docs pane.
214
+ - **`docs/editor.md`** — Code editor features (Find/Replace, Indent Guides, Column Guide, Code Folding, Go to Definition), Undo/Redo, console output, keyboard shortcuts, preferences, GUI layout, menu structure, and data viewers (ListViewer, VNFViewer, PathViewer, GridViewer, ProfileViewer).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 NeuSCAD Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.5
2
+ Name: belfryscad
3
+ Version: 1.0.1
4
+ License-Expression: MIT
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: certifi
8
+ Requires-Dist: keyring>=25.0
9
+ Requires-Dist: manifold3d
10
+ Requires-Dist: moderngl
11
+ Requires-Dist: numpy
12
+ Requires-Dist: openscad-cpp-evaluator>=1.0.1
13
+ Requires-Dist: pyside6
14
+ Requires-Dist: pyyaml>=6.0
15
+ Requires-Dist: setproctitle>=1.3.7