Public: anyone can read this; the crew can change it.
A .3mf print project opens in the 3D view, like an STL #10899
Check · Review · Land · Live done — Landed and live in production.
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
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:.3mfis amodel3dkind, 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 appliesunit(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.tsandmodel3d.ts: the viewer reads3mfand stands it Z-up like an STL. The server never parses it for the page.model-facts.ts: the terminal gives12 triangles · 20 × 20 × 20 mm, ornot a readable 3MF filefor a broken zip or a zip with no model. Source and Download stay.- Tests:
test/threemf.test.tsbuilds 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.tsgets a.3mfsample.
Not done
- 3MF production-extension models (components pointing into other
.modelfiles) 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
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.
- (fix first) docs/features/file-media.md still says, in
## 3D models(line 134 on), that an.stl,.obj,.glbor.gltffile shows the part, and line 12 lists the extensions. Add.3mfthere. Say that units come from the model'sunitattribute 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. - In
threemf.ts,emithas a redundantconst t = to;. Usetodirectly. Thenamefield kept infoundinlocateModelis never read. Remove it. - In
model-facts.tsthe header comment line you extended is now far past the wrapping of the lines around it. Re-wrap it. - 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.
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_LIMITis 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.mdlists3mfamong the media types and names.3mfin## 3D models. It says units come from the model'sunitattribute 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.tsdrops theconst t = to;alias (emitusestodirectly) and the unusednameinfound. The header comment inmodel-facts.tsis 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
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:
- Non-blocking: give production-extension models their own sentence (a component pointing at another .model file) instead of "object N is not there".
- 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.
A
.3mfprint project now opens in the 3D view like an STL. The new tests,render-kinds,model3d,tscandscripts/gate.tspass; the full suite and the viewer in a real browser were not run. Thekeep_plantool wasn't available, so there's no plan on the issue.What changed
kinds.ts:.3mfis amodel3dkind, 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 appliesunit(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.tsandmodel3d.ts: the viewer reads3mfand stands it Z-up like an STL. The server never parses it for the page.model-facts.ts: the terminal gives12 triangles · 20 × 20 × 20 mm, ornot a readable 3MF filefor a broken zip or a zip with no model. Source and Download stay.test/threemf.test.tsbuilds 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.tsgets a.3mfsample.Not done
.modelfiles) are not followed.How to try it: open a
.3mffile 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