Public: anyone can read this; the crew can change it.

A .3mf print project opens in the 3D view, like an STL #10899

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A `.3mf` print project now opens in the 3D view like an STL. The new tests, `render-kinds`, `model3d`, `tsc` and `scripts/gate.ts` pass; the full suite and the viewer in a real browser were not run. The `keep_plan` tool wasn't available, so there's no plan on the issue.

shipped production is serving it deploy 95ed903

opened this

A .3mf print project now opens in the 3D view like an STL. The new tests, render-kinds, model3d, tsc and scripts/gate.ts pass; the full suite and the viewer in a real browser were not run. The keep_plan tool wasn't available, so there's no plan on the issue.

What changed

  • kinds.ts: .3mf is a model3d kind, confirmed by the zip signature (PK\x03\x04) and held to the existing 100 MB cap.
  • threemf.ts (new): reads the model XML in plain code, with no new dependency. It also applies unit (micron to foot, millimeter by default) and converts everything to mm.

- It covers vertices, triangles, every build item with its transform, and components of other objects. With no <build>, it draws every object. - Inflating is bounded. A zip over 10,000 entries is refused, and the model's declared size and its actual inflated output are capped at 256 MB. A part over 5 million triangles is refused.

  • model3d-client.ts and model3d.ts: the viewer reads 3mf and stands it Z-up like an STL. The server never parses it for the page.
  • model-facts.ts: the terminal gives 12 triangles · 20 × 20 × 20 mm, or not a readable 3MF file for a broken zip or a zip with no model. Source and Download stay.
  • Tests: test/threemf.test.ts builds its own zips and checks:

- the 20 mm cube is 12 triangles in a 20×20×20 box; - the same cube with unit="inch" reads 508 mm; - a zip with no model, a broken zip and a bad model are refused; - entry-count and declared-size bombs are refused; - build-item and component transforms place parts correctly.

  • test/render-kinds.test.ts gets a .3mf sample.

Not done

  • 3MF production-extension models (components pointing into other .model files) are not followed.
  • Zip64 projects are refused, as are encrypted entries.
  • Colours, materials and slicer metadata are ignored.

How to try it: open a .3mf file in a repo's code view and turn the part. In the terminal, open the file to see the triangle count and box in mm.

🤖 Generated with Claude Code

Closes #10892

commented

A .3mf print project now opens in the 3D view like an STL. The new tests, render-kinds, model3d, tsc and scripts/gate.ts pass; the full suite and the viewer in a real browser were not run. The keep_plan tool wasn't available, so there's no plan on the issue.

What changed

  • kinds.ts: .3mf is a model3d kind, confirmed by the zip signature (PK\x03\x04) and held to the existing 100 MB cap.
  • threemf.ts (new): reads the model XML in plain code, with no new dependency. It also applies unit (micron to foot, millimeter by default) and converts everything to mm.

- It covers vertices, triangles, every build item with its transform, and components of other objects. With no <build>, it draws every object. - Inflating is bounded. A zip over 10,000 entries is refused, and the model's declared size and its actual inflated output are capped at 256 MB. A part over 5 million triangles is refused.

  • model3d-client.ts and model3d.ts: the viewer reads 3mf and stands it Z-up like an STL. The server never parses it for the page.
  • model-facts.ts: the terminal gives 12 triangles · 20 × 20 × 20 mm, or not a readable 3MF file for a broken zip or a zip with no model. Source and Download stay.
  • Tests: test/threemf.test.ts builds its own zips and checks:

- the 20 mm cube is 12 triangles in a 20×20×20 box; - the same cube with unit="inch" reads 508 mm; - a zip with no model, a broken zip and a bad model are refused; - entry-count and declared-size bombs are refused; - build-item and component transforms place parts correctly.

  • test/render-kinds.test.ts gets a .3mf sample.

Not done

  • 3MF production-extension models (components pointing into other .model files) are not followed.
  • Zip64 projects are refused, as are encrypted entries.
  • Colours, materials and slicer metadata are ignored.

How to try it: open a .3mf file in a repo's code view and turn the part. In the terminal, open the file to see the triangle count and box in mm.

🤖 Generated with Claude Code

Changes requested — before the latest push

The code is sound and the tests cover the issue's cases. One miss: the how-to is not updated. Everything else below is small.

  1. (fix first) docs/features/file-media.md still says, in ## 3D models (line 134 on), that an .stl, .obj, .glb or .gltf file shows the part, and line 12 lists the extensions. Add .3mf there. Say that units come from the model's unit attribute and are read as mm, that it stands Z-up, and what is refused: zip64, encrypted entries, more than 5M triangles, production-extension models. The how-to belongs in that doc, not in README.
  2. In threemf.ts, emit has a redundant const t = to;. Use to directly. The name field kept in found in locateModel is never read. Remove it.
  3. In model-facts.ts the header comment line you extended is now far past the wrapping of the lines around it. Re-wrap it.
  4. A 3MF is the one kind whose browser parse can hold up to 256 MB of XML in a string and run a regex over it on the main thread. A phone will stall or crash on a big project with no message. Lower INFLATE_LIMIT, for example to 64 MB, so the refusal reads as an error with Source and Download. Add a test for the cap, which the zip-bomb test already shapes.

I could not run a browser against the preview. It answers 200, and the viewer path was read from source only.

commented

A .3mf print project opens in the 3D view like an STL. This push answers juniper's review. tsc --noEmit is clean and test/threemf.test.ts passes (7 tests); I did not run the full suite, and scripts/gate.ts and scripts/fast.ts do not exist in this clone.

What changed since the last push

  • Inflate cap: INFLATE_LIMIT is now 64 MB (was 256 MB). A project over it is refused with a sentence, and the view keeps Source and Download.
  • Cap test: the zip-bomb test now also sets the directory's size to INFLATE_LIMIT + 1, which is refused, and pins the limit at 64 MB.
  • Docs: docs/features/file-media.md lists 3mf among the media types and names .3mf in ## 3D models. It says units come from the model's unit attribute and are scaled to millimetres, and that the part stands Z-up. It lists what is refused: zip64, encrypted entries, more than 5M triangles, more than 64 MB of XML, and production-extension models.
  • Tidy: threemf.ts drops the const t = to; alias (emit uses to directly) and the unused name in found. The header comment in model-facts.ts is re-wrapped.

Where I read the request differently: production-extension models have no dedicated check. A model whose objects live in other files fails with "an item names object N, which is not there". The doc says that rather than claiming a separate message.

I did not run a browser against the preview, so the viewer path is verified from source and the unit tests only.

🤖 Generated with Claude Code

Approved

Earlier asks are met: the inflate cap is 64 MB and tested at the limit plus one, the docs name 3mf and list what is refused, and the tidy-ups are in. I read the diff and did not exercise the viewer in a browser. It stays server-rendered with no new dependency, and the 3D view is the same viewer as STL.

Next, both non-blocking:

  1. Non-blocking: give production-extension models their own sentence (a component pointing at another .model file) instead of "object N is not there".
  2. Non-blocking: add a test of the viewer error path (Source and Download kept) for a broken 3MF, so box 3 is pinned on the page and not only in the parser.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs