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.
- belfryscad-1.0.1/.claude/commands/build-and-run.md +13 -0
- belfryscad-1.0.1/.github/workflows/release.yml +181 -0
- belfryscad-1.0.1/.github/workflows/test.yml +53 -0
- belfryscad-1.0.1/.gitignore +60 -0
- belfryscad-1.0.1/CLAUDE.md +214 -0
- belfryscad-1.0.1/LICENSE +21 -0
- belfryscad-1.0.1/PKG-INFO +15 -0
- belfryscad-1.0.1/PRD.md +677 -0
- belfryscad-1.0.1/README.md +72 -0
- belfryscad-1.0.1/TODO.md +17 -0
- belfryscad-1.0.1/docs/debugger.md +152 -0
- belfryscad-1.0.1/docs/docsgen.md +713 -0
- belfryscad-1.0.1/docs/editor.md +1025 -0
- belfryscad-1.0.1/docs/rendering.md +219 -0
- belfryscad-1.0.1/docs/wysiwyg.md +198 -0
- belfryscad-1.0.1/pyproject.toml +93 -0
- belfryscad-1.0.1/resources/BelfrySCAD.png +0 -0
- belfryscad-1.0.1/resources/belfryscad.icns +0 -0
- belfryscad-1.0.1/resources/belfryscad.ico +0 -0
- belfryscad-1.0.1/resources/belfryscad.scad +6 -0
- belfryscad-1.0.1/scripts/compare_docs_images.py +206 -0
- belfryscad-1.0.1/scripts/update_library_versions.py +164 -0
- belfryscad-1.0.1/scripts/validate_against_openscad.py +460 -0
- belfryscad-1.0.1/scripts/verify_3mf_interop.py +137 -0
- belfryscad-1.0.1/scripts/verify_ai_ask_user.py +349 -0
- belfryscad-1.0.1/scripts/verify_ai_attachments.py +155 -0
- belfryscad-1.0.1/scripts/verify_ai_cli_paths.py +255 -0
- belfryscad-1.0.1/scripts/verify_ai_echo.py +144 -0
- belfryscad-1.0.1/scripts/verify_ai_followups.py +98 -0
- belfryscad-1.0.1/scripts/verify_ai_render_tools.py +1131 -0
- belfryscad-1.0.1/scripts/verify_auto_reload.py +178 -0
- belfryscad-1.0.1/scripts/verify_bracket_colors.py +190 -0
- belfryscad-1.0.1/scripts/verify_diff_contrast.py +126 -0
- belfryscad-1.0.1/scripts/verify_escape_cancel.py +118 -0
- belfryscad-1.0.1/scripts/verify_examples_menu.py +146 -0
- belfryscad-1.0.1/scripts/verify_export_check.py +278 -0
- belfryscad-1.0.1/scripts/verify_find_bar.py +275 -0
- belfryscad-1.0.1/scripts/verify_followup_attribution.py +111 -0
- belfryscad-1.0.1/scripts/verify_gestures.py +138 -0
- belfryscad-1.0.1/scripts/verify_indent_guides.py +186 -0
- belfryscad-1.0.1/scripts/verify_measure_snap.py +162 -0
- belfryscad-1.0.1/scripts/verify_measure_tools.py +338 -0
- belfryscad-1.0.1/scripts/verify_modifier_display.py +160 -0
- belfryscad-1.0.1/scripts/verify_move_lines.py +248 -0
- belfryscad-1.0.1/scripts/verify_no_save_prompts.py +145 -0
- belfryscad-1.0.1/scripts/verify_no_stray_dock_tab.py +143 -0
- belfryscad-1.0.1/scripts/verify_profile_export.py +145 -0
- belfryscad-1.0.1/scripts/verify_profile_goto.py +89 -0
- belfryscad-1.0.1/scripts/verify_profile_report.py +56 -0
- belfryscad-1.0.1/scripts/verify_profile_tree.py +141 -0
- belfryscad-1.0.1/scripts/verify_reset_layout.py +155 -0
- belfryscad-1.0.1/scripts/verify_review_button.py +126 -0
- belfryscad-1.0.1/scripts/verify_show_vertices_toggle.py +161 -0
- belfryscad-1.0.1/scripts/verify_string_continuation.py +119 -0
- belfryscad-1.0.1/scripts/verify_tool_buttons.py +146 -0
- belfryscad-1.0.1/scripts/verify_unmatched_brackets.py +195 -0
- belfryscad-1.0.1/scripts/verify_use_library_dialog.py +205 -0
- belfryscad-1.0.1/scripts/verify_viewport_clear.py +130 -0
- belfryscad-1.0.1/scripts/verify_vnf_validate_button.py +210 -0
- belfryscad-1.0.1/src/belfryscad/__init__.py +0 -0
- belfryscad-1.0.1/src/belfryscad/__main__.py +2 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/__init__.py +252 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/blocks.py +1087 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/errorlog.py +46 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/filehashes.py +72 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/imagemanager.py +425 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/logmanager.py +110 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/mdimggen.py +265 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/parser.py +1089 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/preview.py +371 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/runner.py +271 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/target.py +14 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/target_githubwiki.py +45 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/target_wiki.py +223 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/unicode_math.py +277 -0
- belfryscad-1.0.1/src/belfryscad/docsgen/utils.py +19 -0
- belfryscad-1.0.1/src/belfryscad/engine/__init__.py +0 -0
- belfryscad-1.0.1/src/belfryscad/engine/renderer.py +2485 -0
- belfryscad-1.0.1/src/belfryscad/export_name.py +105 -0
- belfryscad-1.0.1/src/belfryscad/exporters.py +147 -0
- belfryscad-1.0.1/src/belfryscad/headless.py +363 -0
- belfryscad-1.0.1/src/belfryscad/headless_render.py +486 -0
- belfryscad-1.0.1/src/belfryscad/main.py +500 -0
- belfryscad-1.0.1/src/belfryscad/png_writer.py +148 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/children.scad +30 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/data/heightmap.dat +26 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/data/knob.stl +0 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/gyroid.scad +29 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/hull.scad +30 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/import.scad +26 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/intersection_for.scad +18 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/minkowski.scad +24 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/multmatrix.scad +29 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/offset.scad +21 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/polyhedron.scad +26 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/projection.scad +32 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Advanced/surface.scad +22 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/color.scad +19 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/csg.scad +30 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/linear_extrude.scad +21 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/mirror.scad +20 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/modules.scad +22 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/polygon.scad +19 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/resize.scad +15 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/rotate_extrude.scad +18 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/text.scad +22 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Basics/transforms.scad +16 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/functions.scad +21 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/list_comprehensions.scad +19 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/Functions/recursion.scad +22 -0
- belfryscad-1.0.1/src/belfryscad/resources/examples/examples.json +32 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-first.svg +5 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-last.svg +5 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-next.svg +4 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-pause.svg +4 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-play.svg +3 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/anim-prev.svg +4 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-continue.svg +4 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-pause.svg +4 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-restart.svg +49 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-into.svg +66 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-out.svg +66 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-over.svg +60 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-step-to-child.svg +82 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/debug-stop.svg +3 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/eyedropper.svg +79 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/tool-rotate.svg +57 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/tool-scale.svg +75 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/tool-translate.svg +46 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-animate.svg +11 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-debug.svg +16 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-export.svg +67 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-measure-angle.svg +9 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-measure-linear.svg +10 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-new.svg +62 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-open.svg +42 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-redo.svg +6 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-render.svg +13 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/toolbar-undo.svg +6 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/view-orthographic.svg +9 -0
- belfryscad-1.0.1/src/belfryscad/resources/icons/view-perspective.svg +57 -0
- belfryscad-1.0.1/src/belfryscad/resources/libraries.json +2478 -0
- belfryscad-1.0.1/src/belfryscad/scad_deps.py +167 -0
- belfryscad-1.0.1/src/belfryscad/scad_literals.py +91 -0
- belfryscad-1.0.1/src/belfryscad/scad_temp.py +117 -0
- belfryscad-1.0.1/src/belfryscad/settings.py +72 -0
- belfryscad-1.0.1/src/belfryscad/vnf_validate.py +410 -0
- belfryscad-1.0.1/src/belfryscad/window/__init__.py +0 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_chat.py +1275 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_cli.py +194 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_copilot_cli.py +232 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_mcp.py +149 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_providers.py +451 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_question_dialog.py +239 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_secrets.py +27 -0
- belfryscad-1.0.1/src/belfryscad/window/ai_tools.py +1712 -0
- belfryscad-1.0.1/src/belfryscad/window/animate.py +386 -0
- belfryscad-1.0.1/src/belfryscad/window/bosl2_docs.py +169 -0
- belfryscad-1.0.1/src/belfryscad/window/color_themes.py +142 -0
- belfryscad-1.0.1/src/belfryscad/window/console.py +250 -0
- belfryscad-1.0.1/src/belfryscad/window/customizer.py +1200 -0
- belfryscad-1.0.1/src/belfryscad/window/customizer_param_dialog.py +417 -0
- belfryscad-1.0.1/src/belfryscad/window/data_viewers.py +8715 -0
- belfryscad-1.0.1/src/belfryscad/window/debugger.py +921 -0
- belfryscad-1.0.1/src/belfryscad/window/docs_pane.py +1075 -0
- belfryscad-1.0.1/src/belfryscad/window/document_manager.py +46 -0
- belfryscad-1.0.1/src/belfryscad/window/editor.py +2122 -0
- belfryscad-1.0.1/src/belfryscad/window/library_manager.py +954 -0
- belfryscad-1.0.1/src/belfryscad/window/main_window.py +4670 -0
- belfryscad-1.0.1/src/belfryscad/window/orientation_cube.py +486 -0
- belfryscad-1.0.1/src/belfryscad/window/preferences.py +1018 -0
- belfryscad-1.0.1/src/belfryscad/window/scad_format.py +518 -0
- belfryscad-1.0.1/src/belfryscad/window/ui_colors.py +318 -0
- belfryscad-1.0.1/src/belfryscad/window/viewport.py +1388 -0
- belfryscad-1.0.1/tests/conftest.py +20 -0
- belfryscad-1.0.1/tests/test_ai_chat.py +69 -0
- belfryscad-1.0.1/tests/test_ai_cli.py +189 -0
- belfryscad-1.0.1/tests/test_ai_mcp.py +125 -0
- belfryscad-1.0.1/tests/test_ai_providers.py +478 -0
- belfryscad-1.0.1/tests/test_ai_secrets.py +73 -0
- belfryscad-1.0.1/tests/test_ai_tools.py +453 -0
- belfryscad-1.0.1/tests/test_bosl2_docs.py +165 -0
- belfryscad-1.0.1/tests/test_clipboard_target.py +113 -0
- belfryscad-1.0.1/tests/test_color_themes.py +115 -0
- belfryscad-1.0.1/tests/test_compare_docs_images.py +91 -0
- belfryscad-1.0.1/tests/test_customizer.py +349 -0
- belfryscad-1.0.1/tests/test_customizer_ast_scan.py +88 -0
- belfryscad-1.0.1/tests/test_data_viewers.py +3102 -0
- belfryscad-1.0.1/tests/test_debug_session.py +102 -0
- belfryscad-1.0.1/tests/test_debugger.py +1158 -0
- belfryscad-1.0.1/tests/test_docsgen.py +1615 -0
- belfryscad-1.0.1/tests/test_editor_brackets.py +163 -0
- belfryscad-1.0.1/tests/test_editor_indent.py +298 -0
- belfryscad-1.0.1/tests/test_editor_min_width.py +74 -0
- belfryscad-1.0.1/tests/test_error_prefix.py +78 -0
- belfryscad-1.0.1/tests/test_examples.py +50 -0
- belfryscad-1.0.1/tests/test_export_format_choice.py +110 -0
- belfryscad-1.0.1/tests/test_export_model.py +131 -0
- belfryscad-1.0.1/tests/test_export_modifiers.py +64 -0
- belfryscad-1.0.1/tests/test_export_name.py +198 -0
- belfryscad-1.0.1/tests/test_headless.py +344 -0
- belfryscad-1.0.1/tests/test_headless_render.py +317 -0
- belfryscad-1.0.1/tests/test_library_catalog.py +140 -0
- belfryscad-1.0.1/tests/test_main.py +141 -0
- belfryscad-1.0.1/tests/test_message_labels.py +98 -0
- belfryscad-1.0.1/tests/test_png_writer.py +75 -0
- belfryscad-1.0.1/tests/test_read_only_tab.py +17 -0
- belfryscad-1.0.1/tests/test_render_reframe.py +97 -0
- belfryscad-1.0.1/tests/test_render_worker.py +75 -0
- belfryscad-1.0.1/tests/test_renderer.py +610 -0
- belfryscad-1.0.1/tests/test_scad_deps.py +197 -0
- belfryscad-1.0.1/tests/test_scad_format.py +290 -0
- belfryscad-1.0.1/tests/test_scad_format_ast.py +73 -0
- belfryscad-1.0.1/tests/test_scad_temp.py +169 -0
- belfryscad-1.0.1/tests/test_settings_isolation.py +109 -0
- belfryscad-1.0.1/tests/test_tab_tooltip.py +97 -0
- belfryscad-1.0.1/tests/test_ui_colors.py +121 -0
- belfryscad-1.0.1/tests/test_unicode_math.py +102 -0
- belfryscad-1.0.1/tests/test_use_statement.py +134 -0
- belfryscad-1.0.1/tests/test_viewport.py +56 -0
- belfryscad-1.0.1/tests/test_vnf_validate.py +250 -0
- belfryscad-1.0.1/tests/test_working_dir.py +96 -0
- 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).
|
belfryscad-1.0.1/LICENSE
ADDED
|
@@ -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
|