Skip to main content

3D Viewer - Releases

← App details

Nextcloud 35

3D Viewer 3.9.0
Release Details
UpdatedOct. 28, 2026, 11:08 a.m.
Changelog

Three new tools. Compare a model with an earlier version of the same file, side by side or as a see-through ghost over the current one. Print check tells you whether a model fits your printer, where it overhangs, roughly how much filament it needs and which walls are too thin, or, for G-code, what the slicer worked out. Record turntable makes a short video of the model turning, to download or save next to it.

Added

  • Print check, in the tools panel's Analyze section: see whether a model fits your printer's build volume (pick from common printers or enter your own size), which surfaces overhang past an angle you choose, and roughly how much filament it needs. For G-code files it shows the print time and filament the slicer worked out. A wall thickness check colours walls thinner than a minimum you set.
  • Compare a model with an earlier version of the same file: pick one of the versions Nextcloud kept, and see the two side by side with what changed, or the earlier one as a see-through ghost over the current one. Supporting files such as textures are loaded as they are now.
  • Record a turntable video, from the tools panel's Export section: one full turn of the camera around the model, 4 to 15 seconds at 720p, 1080p or your window's size, as the viewer draws it, with its shading, annotations and measurements (face labels aren't included). Download it as WebM (MP4 in browsers that can't record WebM, such as older Safari), or save it next to the model in your Nextcloud when you can add files to that folder.

Fixed

  • While a dialog such as Help or Send to Slicer was open, the tool shortcuts (R, F, M, S, P) and Escape still acted on the viewer behind it: a key could reset the view or take a screenshot behind the dialog, and Escape closed the tools panel along with the dialog. Keys now belong to the open dialog, wherever the focus is; ? still closes Help.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturerZEJPDWtDI4c/UZUEa/CwOYHXSyql3DtZoZiStkj8GahJ7fwXaLzAMFgvz16luTKgh/rekkOpfdoi+M9NVK8XV696vG6PUNB//y7dPbANbyAntQyDIVGmkTMtCTu4uMZrsCa227zTJecbKdsrHhiVCsFqWs2CcLM4Ih0UfNwzknlgXpQ21AM+L6FaNWFnrhEtURXWcSgQvwz/bcm2b+mb/Qp5khws78GIU3i/Z4a6LpMuhUJxHMQ8PjfNQyzTA1vr4ehd8UmSZJ6P4IcF1/qlXA3PkDaD2fhnELwdfy2s4Y5B74sX76I97AICTOehnoEEOIoMdU7aLeAfezuoZA6BSupy0dwp5JtyTzFdcFf3I7iMWTY/HXtQ90vXIs0uV2pg0oLPSPbsnXMZ6B6nUs3KWzma7aFppkWYTSCB4RXUEXZ5e6WA3WtGYMpos3SXF6TaFS0H0QTH74DXO0p9UXzuT04JP3VaFiK+B2lnHt6mz/YOKhDZ3TmJmmfe8yZZm1ail6F8jI/kVoePQ2X4JeTxkN6cijqp0aLhExXhuX5B1xNeUNuQMhOVTglfQMPSYwVc3OVcn5yO86j0gD72MWDUP6i9+SPM0V2N5av8i/ccnPwXLYBR6ygjsOHKsHoASesGxtmSZ628PrrsR9nRQg/c4mBMUmkAPqdrpWQpKWiB/U=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.1
Release Details
UpdatedOct. 28, 2026, 2:45 a.m.
Changelog

Thumbnails made in the viewer now show the model as its file has it, whatever wireframe, exploded view, transform or animation state the viewer was in, and exports from exploded view have the parts together. The top bar stays usable while a model loads, and every control in the tools panel works from the keyboard and is announced to screen readers. Textures named by a path above the model's folder, such as ..\..\textures\wood.jpg, are found beside the model. Every route that changes data now checks Nextcloud's request token.

Fixed

  • A thumbnail made in the viewer could show how one user was viewing the model, and it is shown to everyone who can see the file. The viewer makes the thumbnail from the model it has open, half a second after it opens, and several tools change that model: with Wireframe on, the thumbnail was a wireframe; in Exploded view, its parts were flung apart and framed small; after the transform gizmo had turned or stretched the model, it was drawn that way; while a clipping plane or box was open, the back of an open surface showed; an animated model was caught at whatever frame its animation had reached; and a G-code file opened with Single colour picked was drawn in that colour, where the same file got the gradient when Files made its thumbnail. Each tool now keeps a record of what it changed, and the thumbnail is drawn from the model as the file has it, as it already was for Clay, Normals and X-ray: no wireframe (a material the file itself makes wireframe stays one), the parts together, the model as loaded, the pose the file opens in, and the G-code gradient. The viewer keeps showing what you chose. Thumbnails already stored are not remade; one goes when its file changes.
  • Exports made in Exploded view wrote the parts where the view had moved them, so an STL, OBJ, PLY, 3MF, GLB or USDZ file, and the file Send to Slicer sends, had the parts flung apart. Exports now have the parts together, in every format; a model turned or moved with the transform gizmo is still exported as you placed it, and an animated model in the pose it is in. Exporting a GLB, USDZ or 3MF no longer drops a paused animation's parts back to where the file puts them.
  • With Wireframe on, the next model you opened was drawn solid while the switch still read on, and Exploded view stayed "Active" with the next model's parts together. Both now carry on to the next model, as the Clay, Normals and X-ray shading does; Exploded view turns off for a model it cannot take apart, and the camera frames the parts as they are shown. The model statistics are those of the model with its parts together, also when it opens exploded. Wireframe also reaches the parts of a model that have more than one material, which it skipped.
  • Switching G-code from Single colour back to Gradient tinted the gradient with the single colour, and a toolpath opened while Single colour was picked turned black on the switch. The gradient now comes back as it was.
  • While a model loaded, the loading screen covered the top bar as well as the 3D view, so Help, the Tools button and the view actions could not be pressed until the model arrived, which for a large file is the whole wait. The loading screen now covers only the 3D view, and the bar stays usable, with Cancel loading still on the loading screen.
  • Models whose textures are named by a path relative to the project rather than to the model, such as ..\..\textures\wood.jpg or ../maps/wood.png, opened untextured even with the texture right beside them, and got no thumbnail in Files. 3ds Max and other exporters write such paths in FBX, 3DS, DAE, X3D, OBJ/MTL and glTF files, and the app refused any path that leads out of the model's folder. Such a texture is now looked for by its file name, as one named by a full path already is since 3.8.0: beside the model, for background thumbnails and on public shares, and in the viewer also in the model's subfolders, such as textures or maps, where the viewer looks for any texture. The folders the path names are never followed, and a texture that is not beside the model still leaves the thumbnail to the viewer. Background thumbnails also read an MTL's maps relative to the MTL, as the viewer on a public share already did, so an OBJ whose MTL sits in a subfolder now gets its textures in its thumbnail.
  • A DAE, X3D or glTF model whose texture path has its slashes percent-encoded, such as ..%2F..%2Fmaps%2Fchecker.png or C:%5Cx%5Cwood.png, had the texture fetched but drew without it, and got no thumbnail in Files: the loaders looked for a file called by the whole path. They now find it by its file name.
  • A folder holding an OBJ or MTL file made largely of blank lines could stall the Files page while background thumbnails read it. The file is now read in a time proportional to its size.
  • The tools panel's Grid, Axes and Wireframe switches and the animation Loop switch could not be reached with the keyboard: Tab skipped them, and a screen reader read out neither that they were switches nor whether they were on. They are now real switches. Tab reaches each one, Space or Enter turns it on or off, a focus ring shows where you are, and a screen reader says "Grid, switch, on". They look as before, High Contrast mode included. The same pass named controls that a screen reader read as "×" or by a symbol (removing a bookmark, resetting a colour, stepping the animation), gave the animation and cross-section sliders names, and made the cross-section plane, box and axis buttons, the transform Move, Rotate and Scale buttons and the lighting presets say which one is chosen; in High Contrast mode the chosen axis and transform button are now marked. The T shortcut that opens and closes the panel no longer fires while a modifier such as Ctrl is held. Choosing a format in the Export section's format list no longer starts the export: moving through the list with the arrow keys used to write a GLB file with the first key, in Chrome and Firefox on Windows and Linux. The row is now a format list that starts on GLB and only chooses, plus an Export button that exports in the chosen format.

Security

  • Four actions could be triggered by another website while you were signed in to Nextcloud, because their routes skipped Nextcloud's CSRF check, the request token that proves a request came from Nextcloud's own pages: sending a model to the slicer (which uploads a temporary copy of it and makes a share link for that copy), deleting that temporary copy, re-indexing your 3D files, and saving the viewer's settings. Your browser's SameSite cookies, and the preflight a browser makes before it sends a PUT or DELETE from another site, stopped most of this, but they do not cover every case, such as a request from another page on the same site, so these routes now require the token like the rest of Nextcloud. The viewer sends it itself, so nothing changes in use; anything outside the viewer that called these routes with a session cookie and no token now gets a 412 and has to send the requesttoken header. Saving the viewer's settings also accepted any setting name and value; it now accepts only the two the viewer stores, the file-browser sort and the selected file, and refuses the whole request if either is unknown or invalid.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureIm8PqU0r/Nr8aRKa1jh6a+lsP8E3b6u1GCxKzIqtb1C4vUZNgKeSdD/it/SABY5O0GBwso8NdFBD3XHV5jgg/yriggt78tRHo7pvVEo/J4CyRhKEWNRChjWfoOMuxRZF5sBJCOacuCQoMyofcHBCSIPjDils6gZ2CW5H3xZ0atGx5hgjcmQKS7TGxaq/3tymVcGmgl17DNbiSULcfFeIL9b0uBQs0HpLzIBGskp0dffLean9atDu5WAzm8CB9ASVCl+m2DyWkE0mcgQpFKDijWCohaNJXKA+P37KSnBOZ1zuQbDV+8B49hoKQr4hxUgfKhM/tVrbZ/F0Fo2FerTrLpi+H1eWYFvPFXicooi51yLVi2vn+OA8cwBcIrvViUCNiYiI0luZOzNiYRGk17WrZWow7suGL810YJk5eGzkEIj5HQ7dsSoU9Alaz6ubEFRc/Fqw4A0wOjXJIcFm3Gw2uFkjRGA7hUIY0Bhbnwsahi1K2DQMv1nr/pUZKwYK1mTsxJjE2+UwPV6ayLxk2+aMSDPxtNYG/fJrEoCeNUaDTljxsT3P8/EXko8/6e6teSUHTKDBFNBM52HSt7Mzl82R/9bBvXHLCmJk8fg1Td/+QebTbtqW4u1RUio946WlErrk75Ku/WmvhDH4RKeyGWdvJz1j2BKYSzxvk2YHAlEMrBA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.0
Release Details
UpdatedOct. 27, 2026, 4:59 p.m.
Changelog

New inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ. Measurements, statistics and exports now use each model's real unit, so a glTF figure measures in metres and an STL part in millimetres. FBX readings change: they follow the file's own unit, centimetres by default, so most FBX models read 10× larger than in 3.7.x, which is their correct size. Textures a model names by a full path from the machine that exported it, such as C:\…\wood.jpg, are found beside the model, and a texture that is not one of the model's files is never requested.

Four inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ.

Added

  • Angle measurement. The measurement panel has a Distance / Angle switch. In Angle mode, three clicks measure the angle at the second one, in degrees, and changing the length unit leaves it alone. A second click on the point just picked, usually a double click, is not taken, since it would leave the angle without a side. Angles are shared like distances: everyone who can open the model sees them, with their author, and anyone who can edit it can delete them. They are stored as a note type of their own, so a viewer older than this one skips them instead of drawing them as a distance. The annotations JSON export includes angles, and import restores them, whichever mode the panel is in.
  • A layer slider for G-code, in the Analyze section of the tools panel, for files with more than one layer. It shows the print up to the chosen layer, with that layer's height, and "Only this layer" shows that layer alone. A Z-hop counts as part of its layer rather than as a layer of its own. Each step changes only the layers it passes, so dragging across a vase-mode print with tens of thousands of layers stays smooth. The slider and the switch work from the keyboard.
  • Clay, Normals and X-ray shading, in the Scene section of the tools panel. Clay shows the shape of the surface in each part's colour, Normals shows which way each face points, which makes dents and flipped faces stand out, and X-ray makes the parts see-through to show the inside of an assembly. The model's own materials are not changed: exports use them, Send to Slicer included, and so do thumbnails, so a model opened while X-ray is on still gets a normal thumbnail. The shading stays on when you open the next model.
  • Export to 3MF, PLY and USDZ. 3MF, which slicers open, is written with Z up, and its parts are closed solids, so a slicer does not have to repair them. PLY is binary and keeps vertex colours. USDZ opens in AR Quick Look on iPhone and iPad. Both 3MF and USDZ come out at the model's real size: glTF, IFC, COLLADA, X3D, VRML and dotbim models are taken to be in metres, Rhino, 3MF and FBX models in the unit their file says, and STL, OBJ, CAD and the other formats in millimetres. A model with no surfaces, such as a G-code toolpath, gets a message instead of an empty file. The exporters themselves load only when an export starts.

Fixed

  • Measurements and model statistics took every model to be in millimetres, so a 1.8 m glTF figure measured "1.800 mm", and its surface area and volume were a million and a billion times too small. They now use the model's own unit, the same one the 3MF and USDZ exports use: a distance, the bounding box and Copy Measurements read in metres for a glTF, IFC or COLLADA model, and areas and volumes follow as square and cubic units. STL, OBJ, STEP and the other millimetre formats read exactly as before, and angles are unchanged. Both unit pickers now start in the model's own unit (metres for a glTF, millimetres for an STL) until you pick one. Your pick is kept as you open other models in the 3D Viewer app, and is not saved: reloading the page starts again from the model's own unit. "Generic Units" shows the numbers in the model's file, unconverted. The comparison table converts each model from its own unit, so a glTF and an STL of the same part compare as the same size. Shared measurements are stored as points on the model, so they need no change and show the right reading.
  • 3MF files written in a unit other than millimetres opened at the wrong size: three.js reads a 3MF file's unit and never applies it, so a file in metres was a thousandth of its size, measured wrong and exported wrong. The unit (micron, millimeter, centimeter, inch, foot or meter) is now read from the file, so measurements are right and a metre 3MF exports and opens again at its real size. The model is shown as before. Only the start of the file's model part is read for it, and in linear time, so a crafted file cannot stall the page.
  • FBX readings now follow the file's own unit, UnitScaleFactor, which is centimetres when a file does not say. In 3.7.x every FBX was read as millimetres, so every FBX model, small ones included, now reads 10× larger than it did (more for a file in metres or inches): that is its correct size. FBX models larger than 50 units also measured and exported at a fraction of their size, because the viewer shrinks such a model to fit its lighting; that is now undone for measurements, statistics and export. FBX models are shown exactly as before.
  • OBJ models larger than 1000 units, a metre for an OBJ in millimetres, measured, reported and exported at 1000 mm whatever their size: the viewer shrinks such a model to fit, and the shrinking was not undone. A 5 m OBJ now reads 5000 mm. Such models read differently from 3.7.x, and correctly; they are shown as before.
  • G-code toolpaths drew the travel moves the parser leaves out. Each layer was drawn as a single connected line through all its moves, so a line joined the end of each move to the start of the next. Layers are now drawn as separate moves.
  • Closing the measurement panel with one point picked left that point's marker in the scene. Markers were matched to measurements by their position in a list of pairs, so every later pair was off by one, and deleting a measurement could remove another one's markers. Each measurement now keeps track of what it drew, and an unused point's marker goes when the panel closes or the mode changes.
  • Send to Slicer's PLY option always sent ASCII PLY: the binary option was passed where the exporter expects its callback. It now sends binary PLY.
  • In Windows High Contrast mode, the tools panel's switches (Grid, Axes, Wireframe and the others) showed no difference between on and off, because their track was filled with a colour that the system palette replaces. They now have an outline, and a switch that is on is filled with the highlight colour.
  • Models whose textures are named by a full path from the machine that exported them, such as C:\Users\anna\textures\wood.jpg, file:///C:/textures/wood.jpg, \\server\share\wood.jpg or /home/anna/wood.jpg, opened untextured even with the texture right beside them, and got no thumbnail in Files. SketchUp, 3ds Max and some Blender exports write such paths in DAE, FBX, 3DS, OBJ/MTL and X3D files, and the app took them for files that are not beside the model. The texture is now looked for by its file name in the model's own folder, in the viewer, for background thumbnails and on public shares. This also covers glTF, an FBX that names its texture only in a video's Filename, and a material map written after options such as -bm 0.3. The folders the path names are never searched, and a texture that is not beside the model still leaves the thumbnail to the viewer.

Security

  • Opening a DAE, 3DS, glTF or VRML model in the viewer requested any texture the model named that was not one of its own files, at the address the model gave. A texture written as https://…, or as a Windows network path such as \\server\share\x.png, which the browser turns into http://server/…, made your browser contact a server the model's author chose, and only Nextcloud's Content Security Policy stood in the way. Such a texture is now never requested: the model is drawn without it. Thumbnails made in the background already refused these.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCmm5ayL+RHBF5+iiN7ZD5hk7WWM1pgz+KPfzKS6H+3ZnGg8ZyORrq7C6H+eaNxd54PoTbGonDOSIomtizZ/I/udluAu1lRW0ZA6ZS4olC6t+iylmvWU2lc5CaCLqBSXHt7+uE5RSSHbXCZl1MdYWlyYQkJo+SVPCbupSBQu/EenOXfNOMsBJhjdtyGmNzTOhqil+IGmPGRf+YiPa9cW9X0ll+sreqKvbICTxtrfw0OhEbA801v1R2ecXns2RL1iqiWxn/gGcAM4ibCEd+RdsrdUgs3QKSBBgtsZEzJNYKDeH15WoPGHnThDY13aVv7Qv446rHYcn8V3ZIk/vR4Znu0EVLPoRHW6ltJppSi0aeNE62WWCd5KWNfY0Q0K5oykwbtl2szFx+AEYgvbRdRFyOylExNZUcoigYFmlkrQLvPTKtvivYVUi9NIQIbCgSHHygjZEdWy1otFuRs9+W7pqtqH++SJ0cXwZp/ruIikfAJ3/JnQdAuNlWelNErjj2DJVa7cScS8zdiYINzXoToPnDYG9vrNTftnYNwTEvLOQBaHFVDBEC/XdwnPk0ZqEXseJMid632xpWxLdQny2nuVnzjXkG3Nw5klYZ1wWbKOzwUuRHKWatOCJxRiQIXlIs07SgNqV2HlcelYUJ9GG5pX8R21zQBbEKqAsRrT/0WsEtBw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.1
Release Details
UpdatedOct. 27, 2026, 1:58 a.m.
Changelog

3MF, AMF, DAE and X3D files now get thumbnails while folders are browsed, like every other model. FreeCAD files show only the part FreeCAD itself shows, instead of every step of its history on top of each other.

Added

  • 3MF, AMF, DAE (COLLADA) and X3D files get a thumbnail while you browse Files, like every other model, instead of only once opened in the viewer. Their loaders read XML with the browser's DOMParser, which the background worker does not have; the worker now loads its own XML parser (@xmldom/xmldom) for these formats only, and the viewer keeps the browser's. The parser fetches nothing a file names, such as an external DTD or entity, and expands no entity a file declares, so a crafted file can neither make a request nor multiply itself. Parsing still takes memory in proportion to the file, about 31 bytes per character for a 3MF mesh, so XML longer than 16 Mi characters (roughly a 250,000-triangle 3MF, measured unzipped) is not parsed in the background: the file is skipped for its version, like one that runs out of time, and gets its thumbnail when opened in the viewer. As with OBJ, glTF and FBX, a DAE or X3D texture the file names is downloaded with it, and a file whose named texture cannot be found is left for the viewer rather than given an untextured thumbnail; a 3MF carries its textures inside it. Only .nc and .g files, which may not be G-code at all, are still left to the viewer.

Fixed

  • FreeCAD files (.fcstd) showed every step of a part's history on top of each other, in the viewer and in their thumbnail: a PartDesign part with a Pad and a Pocket drew the Pad's uncut solid around the finished part, and the tool the Pocket cut away sticking out of it. FreeCAD saves a shape for every feature and shows only some of them; the loader drew them all. It now draws only the objects GuiDocument.xml marks visible, and nothing inside a hidden Body or App::Part, which FreeCAD hides as a whole while their members stay marked visible (the tips of two Bodies combined by a Part Cut, for one). A file saved without it (by FreeCADCmd), or one where nothing visible can be drawn, is drawn whole as before. In FreeCAD's own PartDesign example this is 1 solid instead of 18; in its Part workbench engine block, 10 shapes instead of 35.

Security

  • Finding the texture and material files a COLLADA, X3D, OBJ or MTL upload names took time that grew with the square of the file's size, so a crafted upload of a few megabytes could keep the server busy for minutes. These files are now read in a single pass, on the server and in the browser.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5P5xxH9yvWf8Au302k0c7SEVJMaiS+KTOElYVIGw4KinRBHTSr3Lc/tmNF0aTDJaecbjcKuz5zrzimSYyQhtuWYIjyBqDew8qfJRoek/6+rycT2u7zvYyzyjmuWGHA+Wns13LLoCk6GsRxeya03ZyWvdwfyJ5eCGmJfAFN+ukZl3VTUmeADIqaQbKiYnHzj3lYQljacH3x0ebkFQ8Q1TPbCcRrUTjEVOAheWM3g7k5knbYstBo0EGUCr84TtsNvyenqCnr0uH8g10NPBt+mIaQRIWlK2xua6RoPtCGY33JmoGVzOGppM8rhyaVQ40wCdY2Sp1Mm+itA1ViyqjF/OcumS4LL5bAE+J0Y5J+4zzCELNWPQIOXGaA6X+YwWXEEN8Q+TB5UlqNOqyUliRVa3JsXcnmfUpwBRQPGEYe5UN48bXEUNRzodZ9J97SclDVApc8wayuf7kZNe3q8HOkjj8euEUXw1QZeSSfPv1cFWm0iy72SYqlrBfylMs4+L32u0B3nloFACcrgVgi0+erNSNGyzKJS2AwwFLXo1MEwTT6IGAx2Lmquq5sT427HDavOlwRpsAeGQ5IwZF+2rxJyhAYl+PkLHCeHH7UwyjHteTxbIYmsUSwzSjPd81sVuI/O/UUfFVhAQbH2etMRBSdQj19Ub0+frTScX32NKmmvYjG8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.0
Release Details
UpdatedOct. 26, 2026, 11:33 a.m.
Changelog

Models get thumbnails in Files without anyone opening them: the browser of whoever is viewing a folder renders them in the background, within limits each user sets. A thumbnail is kept once per file version and shown to everyone who can see the file, including share recipients and public links. VRML files, FreeCAD files saved by FreeCAD and FBX files with embedded textures open correctly again. Existing thumbnails turn back into icons after the upgrade and are made again as folders are browsed.

Added

  • Thumbnails are made while you browse Files. Models in the folder you are viewing get a thumbnail without being opened: one file at a time, while the browser is idle and the tab is visible, and not while Save-Data is on or a model is open in the viewer. Folders without models load none of this code. Each model is rendered in its own Web Worker, which is stopped the moment it runs out of time, so a large CAD file never freezes the page. Personal Settings → 3D Viewer → Thumbnails has the new options: "Create thumbnails while browsing Files" (on by default, and separate from making thumbnails in the viewer), "Skip files larger than" (50 MB), "Give up on a file after" (30 seconds) and "Download at most, per folder visit" (500 MB). A file that runs out of time or cannot be read is skipped by this browser until the file changes or you press "Retry skipped files"; network and server errors never skip a file, and a browser that cannot render in a worker downloads nothing at all. Only the companion files a model names (materials, textures, glTF buffers) are downloaded, and only while they fit in the folder budget, so a model whose textures are not named gets an untextured thumbnail. A model is never rendered without a companion it names, since a thumbnail is kept for the file's version: one that does not fit in what is left of the budget waits for your next visit to the folder, and one that cannot be found beside the model (or at the path it names) is left for the viewer, which looks further, to make the first thumbnail. In Favorites, Shares and Tags, thumbnails are made once you open a folder, not on their top-level lists. Only files you can edit get a thumbnail. 3MF, AMF, DAE and X3D files, whose loaders cannot run in a worker, and .nc and .g files, which may not be G-code at all, still get theirs when opened in the viewer.

Changed

  • One thumbnail per file version, shared by everyone who can see the file. Thumbnails were stored per user, keyed by file id alone, so each user made their own and an edited file kept its old picture. They are now stored once per version in the app data (thumbnails-v2/<fileId>/), dropped when the file is edited or deleted, and the old per-user thumbnails are removed on upgrade. Models that showed a thumbnail before the upgrade show their file icon again until a new thumbnail is made, by browsing their folder or opening them.
  • The viewer captures a model's thumbnail with the same fixed look as every other thumbnail (transparent background, default lighting and camera framing) instead of a crop of the live canvas, and only once per file version: it asks the server first and skips files you cannot edit, files that already have a thumbnail for their current version, public shares, and opens that took a material or texture from the browser's dependency cache, which may be older than the file. It now covers every format the viewer opens, not a fixed list of 12.
  • The CAD runtime chunk is 63 KB instead of 776 KB. The OCCT glue's require("crypto"), reached only when it runs under Node, pulled the whole browser polyfill for Node's crypto into the bundle: crypto-browserify, bn.js, elliptic, asn1.js and readable-stream. crypto is no longer polyfilled, which also removes the vm-browserify eval warning from the build.
  • The GLB, STL and OBJ exporters load when an export starts instead of with the viewer, taking 40 KB off the main chunk. The slicer's dynamic imports of the same exporters had no effect while the export composable imported them statically.
  • webdav 5.10.0 → 5.11.0 and fast-xml-parser 5.10.1 → 5.11.2, which drop a duplicate key the build warned about.
  • "Retry skipped files" replaces "Clear thumbnails" in the thumbnail settings. Thumbnails are now shared, so clearing them would have removed other people's too; a thumbnail goes away when its file changes instead.
  • roave/security-advisories is no longer a dev dependency. Its latest release declares a conflict with every PHP below 8.2.35, PHP 8.1 included, so Composer could not resolve the dev tree for the app's minimum PHP and every PHP job in CI failed at install. Advisories are still checked by composer audit in the security scan.

Fixed

  • IFC, FCSTD and G-code files never showed a thumbnail in Files. The preview provider registered for model/* MIME types only, and these three have application/x-step, application/x-fcstd and text/x-gcode. It now registers for the MIME type of every format that gets a thumbnail.
  • Thumbnails never showed to share recipients or on public links. They were looked up for the logged-in user, so a recipient saw their own (usually none) and a public link, with no user, saw none at all. Anyone who can see a file now sees its thumbnail.
  • FreeCAD files (.fcstd) saved by FreeCAD failed to open with "FCSTD archive contains no .brep geometry entries". FreeCAD stores each body as a .brp file inside the document, and only .brep was read; both are now.
  • VRML files (.wrl, .vrml) spun for 30 seconds and then failed to open in the viewer. The loader waited for a callback three.js's VRML loader never calls; it now uses the scene the parser returns, so they open.
  • FBX files with embedded textures opened untextured. The images the file carries were looked up by name among the companion files, matched nothing, and got a blank placeholder; they are now used as they are.

Security

  • A model rendered in the background loads only the files it was given. A texture or buffer it named but that was not among them was requested from the URL the model wrote, so a model in a shared folder could make the browser of anyone browsing that folder request an address of its author's choosing. The worker now refuses every URL except the model's own files and the app's decoders, before any request is made, and skips the model.
  • Storing a thumbnail requires the request token (CSRF protection) and edit rights on the file. The upload route opted out of CSRF checks, and anyone who could read a file could set its thumbnail; read-only share recipients now get 403. The browser also sends the version it rendered, which it learns before it starts downloading the model, and an upload for a version that is no longer current is refused, so a thumbnail can never be filed under a newer version of the file.
  • Dependencies updated within their existing ranges to clear npm audit: follow-redirects 1.16.0 → 1.16.2 (credential headers kept across cross-origin redirects), reached through axios's Node adapter, which the browser bundle does not use, and vite 7.3.6 → 7.3.7 (dev-server advisories). Dev-only fast-uri, brace-expansion, js-yaml, moment, pbkdf2 and qs move to patched releases as well.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureHW+gyQT9pWQ7mEMg5wkO6BOlN33tSE+lpY33kDjIvJ0XIvGh3JY9P7KW64TxfSUb5xG+sArQE8CsPqTDbwGjKPy+sMK+iPQrKdbIMKA/xmrW6ISonhqxt1IN9siV0EiPhGVlLsigL76kuWbzAzaxiHiISofv79xZt2hR0YwD/vHqn3IRNQXREf7gtSnNDGXF5HmQVELaR43ccFlJK4husCsCGSxnQ0Fdbt6I3OZd2+bp1l53cG2EVOTbNFn5Azc4krpiy1jP8oNO6PVKveZPHObTBiXqYMNm6seHkEuATm0vw+aq5F9u/HCVitQyzXo+0oANQFoYW1+FqtWT1ueSgTaz4++lsn5Z5b7aDtubDWISj5AJL/kERXdfPvux6klusP4UPb1YP1yLkO/tWBQWn3is4SsSvC9f3UGBTKch/IEAcHjnL2c5PSqml491KCHF3/4jqQUu8ozKuG6n/09yImtQDmBvuyGlEuBCZT4rANZ8Pr9Q29zbDkFqQCNR/qe+tveEQzKDKO0ECaDp4bjxydNvnff3+m6ceWp5/KpHXLUfs228XoJqBVuyY5jI3+i/ltEnXYp8l3coQFBVKmKiLvuE8aGbP92EmTZXc1sv2Q8RHBSMt3Mxd/78cMIF4pMYjGsKOIHWl4R9hvctcntXkiB4BD2ALVTCZ2ypvBihqjE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.6.0
Release Details
UpdatedOct. 19, 2026, 10:51 a.m.
Changelog

Annotations and measurements become shared notes that everyone who can open a model sees, and the app supports Nextcloud 35. CAD, BIM and 3DM files open again on App Store installs, Rhino 3DM files open at all, and the Favorites view lists starred models.

Added

  • Annotations and measurements are shared with everyone who can open the model. They were saved privately per user, and measurements were not saved at all. Now each model has one set of notes: share recipients and public-link visitors see it, and anyone who can edit the model can add, change or delete notes. Each note saves on its own, so two people editing at once never overwrite each other. Each note shows its author, except on public links. Existing private annotations move into the shared set the first time their author opens the model, if that user can edit it; otherwise they stay visible to that user only, marked "Private, not shared". Notes follow a model through moves and renames within the same storage; moving it to another storage, such as a group folder or external storage, gives it a new file id, and its notes are removed by the daily cleanup.
  • JSON export includes measurements, and import restores them.
  • Nextcloud 35 support. The app declared max-version="34", so the updater listed 3D Viewer under "Apps missing compatible version" and an upgrade to 35 would have disabled it. Checked on 35.0.2 by upgrading a 34 instance in place: the app stays enabled, and models open from the Files app, the standalone viewer, the file picker and public shares, with nothing logged. Nextcloud 35 types the schema and file APIs more strictly, which surfaced three changes: the folder_path migration calls modifyColumn(), the only form 35's ITable declares (changeColumn() was a deprecated alias of it in every supported server); the slicer download endpoint no longer treats a folder ID as a file, which deleted any folder older than a day inside its temp directory; and the temp-file cleanup job skips a non-folder at the temp path instead of failing on it.

Changed

  • three.js r185 → r186. The Babel config moves from package.json to a project-wide babel.config.json: r186's CommonJS entry only re-exports the ES module build, and a package.json config never reaches files inside node_modules, so Jest could no longer load three.
  • Babel 7 → 8 for the Jest transform. @babel/plugin-proposal-object-rest-spread, @babel/plugin-syntax-dynamic-import and @babel/plugin-transform-runtime are dropped: no config referenced them, and the first had no Babel 8 release. Babel 8 needs Node 22.18 or later, so engines.node and .nvmrc move to 22.18.

Removed

  • The /api/annotations/{fileId} endpoints, replaced by /api/notes/{fileId}.

Fixed

  • STEP, IGES, BREP, FCSTD, IFC and 3DM files never opened when the app was installed from the App Store, failing with both async and sync fetching of the wasm failed. The loaders fetched their WebAssembly runtimes from /apps/threedviewer/…, which exists only when the app sits in the shipped apps root; App Store installs land in the writable root, custom_apps/ on the official image. The OCCT, web-ifc, rhino3dm, DRACO and Basis paths now resolve against the app's actual web root, which also covers instances served from a sub-directory (#188).
  • The file browser's Favorites view was always empty. The controller read favorites through OCP\Files\Tags\ISystemTagManager, which does not exist, so Nextcloud injected null; it also looked for system tag 10, while favorites are per-user tags. It now reads the user's starred files from ITagManager, and the browser renders the flat Favorites list, which no view state reached before (#205).
  • Rhino 3DM files never opened, for three reasons. The published app shipped rhino3dm.js but no rhino3dm.wasm: the wasm is git-ignored and its old source in three.js's examples/ no longer exists, and the copy step skipped missing files silently. The runtime now comes from the rhino3dm package (8.32.1, the version three's loader names), and the build fails if either file is missing. Its emscripten glue compiled code with new Function, which Nextcloud's CSP blocks; it is rewritten as it is copied, the same repair #169 made for the CAD runtime. And a worker whose runtime failed to start never answered, so the viewer spun with no error: the decode now has a deadline, worker errors are reported by their message, and each load terminates its worker (#204).

Security

  • Production dependencies updated within their existing ranges to clear npm audit: vue 3.5.41 → 3.5.44 (@vue/server-renderer XSS), axios 1.19.0 → 1.20.0 (prototype-pollution gadgets), brace-expansion 5.0.9 → 5.0.12 (ReDoS), source-map-js 1.2.1 → 1.2.2 (event-loop DoS) and DOMPurify 3.4.15 → 3.4.17 (IN_PLACE sanitizer bypasses).
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewgF9SWWbzLH22o0y9lzTkAUX33vlRdY37uuPVanE2nMuer3V8gTzxkFnfGn2m6iXOWfHvlTAM1xMXbMIiFOCvTTw5rA2GMq+T+QVh8kAyeDAOtQVU7Rr0J0+x2PK0HNkmwlJstOc7xGiYEiVLXEu6mEKbb99Q/o7xlkUsgvmAuK6Xxu1EZvWJi30bx8pJwJC3jbRMaZX5CRWYpoPkeoOCqglgXTm/j8wCqNP8W4ofmWZlOdqM5VQoApIv3Iv9Bvtp7lzau+r+saUNcgShEHuMhiODLeWDdznd5XUrQNLp7yLiYM2tk/brB04F9qIL0nr1BJtA1qhOe2nlIHtmvyp1Yy8bEryIPLKgWHubSyMYV8qUt5pJP3b5ZOmlDfNYBpru1mNJVNFOPlEyWw/74lRAWRadFY3dOASyyUfzcr0rMraMcvKg3Kg881XEAPiizNfTCHi5trQR4+gfbd7e42gT2Z5KGuZLGllAntMzP9B/ZwMl5VuFZLaWJNosjkOdry5TRH8ZOn1hyEj+vP4fdi+a81Wcq2yRjuER+LG6SL3LKkVIjXLCnat1N0nEzxOfPEi3VPNDy6jNJhoX8aaDAQJklXFc/TcD7+6MwLXR/ZXLlxRZ7dvpu/tO3WQF75kxCo94Z6fILOS5EhjDf0JZb6iFscELiwK9R7A8XaEiqHKFY8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32

Nextcloud 34

3D Viewer 3.9.0
Release Details
UpdatedOct. 28, 2026, 11:08 a.m.
Changelog

Three new tools. Compare a model with an earlier version of the same file, side by side or as a see-through ghost over the current one. Print check tells you whether a model fits your printer, where it overhangs, roughly how much filament it needs and which walls are too thin, or, for G-code, what the slicer worked out. Record turntable makes a short video of the model turning, to download or save next to it.

Added

  • Print check, in the tools panel's Analyze section: see whether a model fits your printer's build volume (pick from common printers or enter your own size), which surfaces overhang past an angle you choose, and roughly how much filament it needs. For G-code files it shows the print time and filament the slicer worked out. A wall thickness check colours walls thinner than a minimum you set.
  • Compare a model with an earlier version of the same file: pick one of the versions Nextcloud kept, and see the two side by side with what changed, or the earlier one as a see-through ghost over the current one. Supporting files such as textures are loaded as they are now.
  • Record a turntable video, from the tools panel's Export section: one full turn of the camera around the model, 4 to 15 seconds at 720p, 1080p or your window's size, as the viewer draws it, with its shading, annotations and measurements (face labels aren't included). Download it as WebM (MP4 in browsers that can't record WebM, such as older Safari), or save it next to the model in your Nextcloud when you can add files to that folder.

Fixed

  • While a dialog such as Help or Send to Slicer was open, the tool shortcuts (R, F, M, S, P) and Escape still acted on the viewer behind it: a key could reset the view or take a screenshot behind the dialog, and Escape closed the tools panel along with the dialog. Keys now belong to the open dialog, wherever the focus is; ? still closes Help.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturerZEJPDWtDI4c/UZUEa/CwOYHXSyql3DtZoZiStkj8GahJ7fwXaLzAMFgvz16luTKgh/rekkOpfdoi+M9NVK8XV696vG6PUNB//y7dPbANbyAntQyDIVGmkTMtCTu4uMZrsCa227zTJecbKdsrHhiVCsFqWs2CcLM4Ih0UfNwzknlgXpQ21AM+L6FaNWFnrhEtURXWcSgQvwz/bcm2b+mb/Qp5khws78GIU3i/Z4a6LpMuhUJxHMQ8PjfNQyzTA1vr4ehd8UmSZJ6P4IcF1/qlXA3PkDaD2fhnELwdfy2s4Y5B74sX76I97AICTOehnoEEOIoMdU7aLeAfezuoZA6BSupy0dwp5JtyTzFdcFf3I7iMWTY/HXtQ90vXIs0uV2pg0oLPSPbsnXMZ6B6nUs3KWzma7aFppkWYTSCB4RXUEXZ5e6WA3WtGYMpos3SXF6TaFS0H0QTH74DXO0p9UXzuT04JP3VaFiK+B2lnHt6mz/YOKhDZ3TmJmmfe8yZZm1ail6F8jI/kVoePQ2X4JeTxkN6cijqp0aLhExXhuX5B1xNeUNuQMhOVTglfQMPSYwVc3OVcn5yO86j0gD72MWDUP6i9+SPM0V2N5av8i/ccnPwXLYBR6ygjsOHKsHoASesGxtmSZ628PrrsR9nRQg/c4mBMUmkAPqdrpWQpKWiB/U=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.1
Release Details
UpdatedOct. 28, 2026, 2:45 a.m.
Changelog

Thumbnails made in the viewer now show the model as its file has it, whatever wireframe, exploded view, transform or animation state the viewer was in, and exports from exploded view have the parts together. The top bar stays usable while a model loads, and every control in the tools panel works from the keyboard and is announced to screen readers. Textures named by a path above the model's folder, such as ..\..\textures\wood.jpg, are found beside the model. Every route that changes data now checks Nextcloud's request token.

Fixed

  • A thumbnail made in the viewer could show how one user was viewing the model, and it is shown to everyone who can see the file. The viewer makes the thumbnail from the model it has open, half a second after it opens, and several tools change that model: with Wireframe on, the thumbnail was a wireframe; in Exploded view, its parts were flung apart and framed small; after the transform gizmo had turned or stretched the model, it was drawn that way; while a clipping plane or box was open, the back of an open surface showed; an animated model was caught at whatever frame its animation had reached; and a G-code file opened with Single colour picked was drawn in that colour, where the same file got the gradient when Files made its thumbnail. Each tool now keeps a record of what it changed, and the thumbnail is drawn from the model as the file has it, as it already was for Clay, Normals and X-ray: no wireframe (a material the file itself makes wireframe stays one), the parts together, the model as loaded, the pose the file opens in, and the G-code gradient. The viewer keeps showing what you chose. Thumbnails already stored are not remade; one goes when its file changes.
  • Exports made in Exploded view wrote the parts where the view had moved them, so an STL, OBJ, PLY, 3MF, GLB or USDZ file, and the file Send to Slicer sends, had the parts flung apart. Exports now have the parts together, in every format; a model turned or moved with the transform gizmo is still exported as you placed it, and an animated model in the pose it is in. Exporting a GLB, USDZ or 3MF no longer drops a paused animation's parts back to where the file puts them.
  • With Wireframe on, the next model you opened was drawn solid while the switch still read on, and Exploded view stayed "Active" with the next model's parts together. Both now carry on to the next model, as the Clay, Normals and X-ray shading does; Exploded view turns off for a model it cannot take apart, and the camera frames the parts as they are shown. The model statistics are those of the model with its parts together, also when it opens exploded. Wireframe also reaches the parts of a model that have more than one material, which it skipped.
  • Switching G-code from Single colour back to Gradient tinted the gradient with the single colour, and a toolpath opened while Single colour was picked turned black on the switch. The gradient now comes back as it was.
  • While a model loaded, the loading screen covered the top bar as well as the 3D view, so Help, the Tools button and the view actions could not be pressed until the model arrived, which for a large file is the whole wait. The loading screen now covers only the 3D view, and the bar stays usable, with Cancel loading still on the loading screen.
  • Models whose textures are named by a path relative to the project rather than to the model, such as ..\..\textures\wood.jpg or ../maps/wood.png, opened untextured even with the texture right beside them, and got no thumbnail in Files. 3ds Max and other exporters write such paths in FBX, 3DS, DAE, X3D, OBJ/MTL and glTF files, and the app refused any path that leads out of the model's folder. Such a texture is now looked for by its file name, as one named by a full path already is since 3.8.0: beside the model, for background thumbnails and on public shares, and in the viewer also in the model's subfolders, such as textures or maps, where the viewer looks for any texture. The folders the path names are never followed, and a texture that is not beside the model still leaves the thumbnail to the viewer. Background thumbnails also read an MTL's maps relative to the MTL, as the viewer on a public share already did, so an OBJ whose MTL sits in a subfolder now gets its textures in its thumbnail.
  • A DAE, X3D or glTF model whose texture path has its slashes percent-encoded, such as ..%2F..%2Fmaps%2Fchecker.png or C:%5Cx%5Cwood.png, had the texture fetched but drew without it, and got no thumbnail in Files: the loaders looked for a file called by the whole path. They now find it by its file name.
  • A folder holding an OBJ or MTL file made largely of blank lines could stall the Files page while background thumbnails read it. The file is now read in a time proportional to its size.
  • The tools panel's Grid, Axes and Wireframe switches and the animation Loop switch could not be reached with the keyboard: Tab skipped them, and a screen reader read out neither that they were switches nor whether they were on. They are now real switches. Tab reaches each one, Space or Enter turns it on or off, a focus ring shows where you are, and a screen reader says "Grid, switch, on". They look as before, High Contrast mode included. The same pass named controls that a screen reader read as "×" or by a symbol (removing a bookmark, resetting a colour, stepping the animation), gave the animation and cross-section sliders names, and made the cross-section plane, box and axis buttons, the transform Move, Rotate and Scale buttons and the lighting presets say which one is chosen; in High Contrast mode the chosen axis and transform button are now marked. The T shortcut that opens and closes the panel no longer fires while a modifier such as Ctrl is held. Choosing a format in the Export section's format list no longer starts the export: moving through the list with the arrow keys used to write a GLB file with the first key, in Chrome and Firefox on Windows and Linux. The row is now a format list that starts on GLB and only chooses, plus an Export button that exports in the chosen format.

Security

  • Four actions could be triggered by another website while you were signed in to Nextcloud, because their routes skipped Nextcloud's CSRF check, the request token that proves a request came from Nextcloud's own pages: sending a model to the slicer (which uploads a temporary copy of it and makes a share link for that copy), deleting that temporary copy, re-indexing your 3D files, and saving the viewer's settings. Your browser's SameSite cookies, and the preflight a browser makes before it sends a PUT or DELETE from another site, stopped most of this, but they do not cover every case, such as a request from another page on the same site, so these routes now require the token like the rest of Nextcloud. The viewer sends it itself, so nothing changes in use; anything outside the viewer that called these routes with a session cookie and no token now gets a 412 and has to send the requesttoken header. Saving the viewer's settings also accepted any setting name and value; it now accepts only the two the viewer stores, the file-browser sort and the selected file, and refuses the whole request if either is unknown or invalid.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureIm8PqU0r/Nr8aRKa1jh6a+lsP8E3b6u1GCxKzIqtb1C4vUZNgKeSdD/it/SABY5O0GBwso8NdFBD3XHV5jgg/yriggt78tRHo7pvVEo/J4CyRhKEWNRChjWfoOMuxRZF5sBJCOacuCQoMyofcHBCSIPjDils6gZ2CW5H3xZ0atGx5hgjcmQKS7TGxaq/3tymVcGmgl17DNbiSULcfFeIL9b0uBQs0HpLzIBGskp0dffLean9atDu5WAzm8CB9ASVCl+m2DyWkE0mcgQpFKDijWCohaNJXKA+P37KSnBOZ1zuQbDV+8B49hoKQr4hxUgfKhM/tVrbZ/F0Fo2FerTrLpi+H1eWYFvPFXicooi51yLVi2vn+OA8cwBcIrvViUCNiYiI0luZOzNiYRGk17WrZWow7suGL810YJk5eGzkEIj5HQ7dsSoU9Alaz6ubEFRc/Fqw4A0wOjXJIcFm3Gw2uFkjRGA7hUIY0Bhbnwsahi1K2DQMv1nr/pUZKwYK1mTsxJjE2+UwPV6ayLxk2+aMSDPxtNYG/fJrEoCeNUaDTljxsT3P8/EXko8/6e6teSUHTKDBFNBM52HSt7Mzl82R/9bBvXHLCmJk8fg1Td/+QebTbtqW4u1RUio946WlErrk75Ku/WmvhDH4RKeyGWdvJz1j2BKYSzxvk2YHAlEMrBA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.0
Release Details
UpdatedOct. 27, 2026, 4:59 p.m.
Changelog

New inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ. Measurements, statistics and exports now use each model's real unit, so a glTF figure measures in metres and an STL part in millimetres. FBX readings change: they follow the file's own unit, centimetres by default, so most FBX models read 10× larger than in 3.7.x, which is their correct size. Textures a model names by a full path from the machine that exported it, such as C:\…\wood.jpg, are found beside the model, and a texture that is not one of the model's files is never requested.

Four inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ.

Added

  • Angle measurement. The measurement panel has a Distance / Angle switch. In Angle mode, three clicks measure the angle at the second one, in degrees, and changing the length unit leaves it alone. A second click on the point just picked, usually a double click, is not taken, since it would leave the angle without a side. Angles are shared like distances: everyone who can open the model sees them, with their author, and anyone who can edit it can delete them. They are stored as a note type of their own, so a viewer older than this one skips them instead of drawing them as a distance. The annotations JSON export includes angles, and import restores them, whichever mode the panel is in.
  • A layer slider for G-code, in the Analyze section of the tools panel, for files with more than one layer. It shows the print up to the chosen layer, with that layer's height, and "Only this layer" shows that layer alone. A Z-hop counts as part of its layer rather than as a layer of its own. Each step changes only the layers it passes, so dragging across a vase-mode print with tens of thousands of layers stays smooth. The slider and the switch work from the keyboard.
  • Clay, Normals and X-ray shading, in the Scene section of the tools panel. Clay shows the shape of the surface in each part's colour, Normals shows which way each face points, which makes dents and flipped faces stand out, and X-ray makes the parts see-through to show the inside of an assembly. The model's own materials are not changed: exports use them, Send to Slicer included, and so do thumbnails, so a model opened while X-ray is on still gets a normal thumbnail. The shading stays on when you open the next model.
  • Export to 3MF, PLY and USDZ. 3MF, which slicers open, is written with Z up, and its parts are closed solids, so a slicer does not have to repair them. PLY is binary and keeps vertex colours. USDZ opens in AR Quick Look on iPhone and iPad. Both 3MF and USDZ come out at the model's real size: glTF, IFC, COLLADA, X3D, VRML and dotbim models are taken to be in metres, Rhino, 3MF and FBX models in the unit their file says, and STL, OBJ, CAD and the other formats in millimetres. A model with no surfaces, such as a G-code toolpath, gets a message instead of an empty file. The exporters themselves load only when an export starts.

Fixed

  • Measurements and model statistics took every model to be in millimetres, so a 1.8 m glTF figure measured "1.800 mm", and its surface area and volume were a million and a billion times too small. They now use the model's own unit, the same one the 3MF and USDZ exports use: a distance, the bounding box and Copy Measurements read in metres for a glTF, IFC or COLLADA model, and areas and volumes follow as square and cubic units. STL, OBJ, STEP and the other millimetre formats read exactly as before, and angles are unchanged. Both unit pickers now start in the model's own unit (metres for a glTF, millimetres for an STL) until you pick one. Your pick is kept as you open other models in the 3D Viewer app, and is not saved: reloading the page starts again from the model's own unit. "Generic Units" shows the numbers in the model's file, unconverted. The comparison table converts each model from its own unit, so a glTF and an STL of the same part compare as the same size. Shared measurements are stored as points on the model, so they need no change and show the right reading.
  • 3MF files written in a unit other than millimetres opened at the wrong size: three.js reads a 3MF file's unit and never applies it, so a file in metres was a thousandth of its size, measured wrong and exported wrong. The unit (micron, millimeter, centimeter, inch, foot or meter) is now read from the file, so measurements are right and a metre 3MF exports and opens again at its real size. The model is shown as before. Only the start of the file's model part is read for it, and in linear time, so a crafted file cannot stall the page.
  • FBX readings now follow the file's own unit, UnitScaleFactor, which is centimetres when a file does not say. In 3.7.x every FBX was read as millimetres, so every FBX model, small ones included, now reads 10× larger than it did (more for a file in metres or inches): that is its correct size. FBX models larger than 50 units also measured and exported at a fraction of their size, because the viewer shrinks such a model to fit its lighting; that is now undone for measurements, statistics and export. FBX models are shown exactly as before.
  • OBJ models larger than 1000 units, a metre for an OBJ in millimetres, measured, reported and exported at 1000 mm whatever their size: the viewer shrinks such a model to fit, and the shrinking was not undone. A 5 m OBJ now reads 5000 mm. Such models read differently from 3.7.x, and correctly; they are shown as before.
  • G-code toolpaths drew the travel moves the parser leaves out. Each layer was drawn as a single connected line through all its moves, so a line joined the end of each move to the start of the next. Layers are now drawn as separate moves.
  • Closing the measurement panel with one point picked left that point's marker in the scene. Markers were matched to measurements by their position in a list of pairs, so every later pair was off by one, and deleting a measurement could remove another one's markers. Each measurement now keeps track of what it drew, and an unused point's marker goes when the panel closes or the mode changes.
  • Send to Slicer's PLY option always sent ASCII PLY: the binary option was passed where the exporter expects its callback. It now sends binary PLY.
  • In Windows High Contrast mode, the tools panel's switches (Grid, Axes, Wireframe and the others) showed no difference between on and off, because their track was filled with a colour that the system palette replaces. They now have an outline, and a switch that is on is filled with the highlight colour.
  • Models whose textures are named by a full path from the machine that exported them, such as C:\Users\anna\textures\wood.jpg, file:///C:/textures/wood.jpg, \\server\share\wood.jpg or /home/anna/wood.jpg, opened untextured even with the texture right beside them, and got no thumbnail in Files. SketchUp, 3ds Max and some Blender exports write such paths in DAE, FBX, 3DS, OBJ/MTL and X3D files, and the app took them for files that are not beside the model. The texture is now looked for by its file name in the model's own folder, in the viewer, for background thumbnails and on public shares. This also covers glTF, an FBX that names its texture only in a video's Filename, and a material map written after options such as -bm 0.3. The folders the path names are never searched, and a texture that is not beside the model still leaves the thumbnail to the viewer.

Security

  • Opening a DAE, 3DS, glTF or VRML model in the viewer requested any texture the model named that was not one of its own files, at the address the model gave. A texture written as https://…, or as a Windows network path such as \\server\share\x.png, which the browser turns into http://server/…, made your browser contact a server the model's author chose, and only Nextcloud's Content Security Policy stood in the way. Such a texture is now never requested: the model is drawn without it. Thumbnails made in the background already refused these.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCmm5ayL+RHBF5+iiN7ZD5hk7WWM1pgz+KPfzKS6H+3ZnGg8ZyORrq7C6H+eaNxd54PoTbGonDOSIomtizZ/I/udluAu1lRW0ZA6ZS4olC6t+iylmvWU2lc5CaCLqBSXHt7+uE5RSSHbXCZl1MdYWlyYQkJo+SVPCbupSBQu/EenOXfNOMsBJhjdtyGmNzTOhqil+IGmPGRf+YiPa9cW9X0ll+sreqKvbICTxtrfw0OhEbA801v1R2ecXns2RL1iqiWxn/gGcAM4ibCEd+RdsrdUgs3QKSBBgtsZEzJNYKDeH15WoPGHnThDY13aVv7Qv446rHYcn8V3ZIk/vR4Znu0EVLPoRHW6ltJppSi0aeNE62WWCd5KWNfY0Q0K5oykwbtl2szFx+AEYgvbRdRFyOylExNZUcoigYFmlkrQLvPTKtvivYVUi9NIQIbCgSHHygjZEdWy1otFuRs9+W7pqtqH++SJ0cXwZp/ruIikfAJ3/JnQdAuNlWelNErjj2DJVa7cScS8zdiYINzXoToPnDYG9vrNTftnYNwTEvLOQBaHFVDBEC/XdwnPk0ZqEXseJMid632xpWxLdQny2nuVnzjXkG3Nw5klYZ1wWbKOzwUuRHKWatOCJxRiQIXlIs07SgNqV2HlcelYUJ9GG5pX8R21zQBbEKqAsRrT/0WsEtBw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.1
Release Details
UpdatedOct. 27, 2026, 1:58 a.m.
Changelog

3MF, AMF, DAE and X3D files now get thumbnails while folders are browsed, like every other model. FreeCAD files show only the part FreeCAD itself shows, instead of every step of its history on top of each other.

Added

  • 3MF, AMF, DAE (COLLADA) and X3D files get a thumbnail while you browse Files, like every other model, instead of only once opened in the viewer. Their loaders read XML with the browser's DOMParser, which the background worker does not have; the worker now loads its own XML parser (@xmldom/xmldom) for these formats only, and the viewer keeps the browser's. The parser fetches nothing a file names, such as an external DTD or entity, and expands no entity a file declares, so a crafted file can neither make a request nor multiply itself. Parsing still takes memory in proportion to the file, about 31 bytes per character for a 3MF mesh, so XML longer than 16 Mi characters (roughly a 250,000-triangle 3MF, measured unzipped) is not parsed in the background: the file is skipped for its version, like one that runs out of time, and gets its thumbnail when opened in the viewer. As with OBJ, glTF and FBX, a DAE or X3D texture the file names is downloaded with it, and a file whose named texture cannot be found is left for the viewer rather than given an untextured thumbnail; a 3MF carries its textures inside it. Only .nc and .g files, which may not be G-code at all, are still left to the viewer.

Fixed

  • FreeCAD files (.fcstd) showed every step of a part's history on top of each other, in the viewer and in their thumbnail: a PartDesign part with a Pad and a Pocket drew the Pad's uncut solid around the finished part, and the tool the Pocket cut away sticking out of it. FreeCAD saves a shape for every feature and shows only some of them; the loader drew them all. It now draws only the objects GuiDocument.xml marks visible, and nothing inside a hidden Body or App::Part, which FreeCAD hides as a whole while their members stay marked visible (the tips of two Bodies combined by a Part Cut, for one). A file saved without it (by FreeCADCmd), or one where nothing visible can be drawn, is drawn whole as before. In FreeCAD's own PartDesign example this is 1 solid instead of 18; in its Part workbench engine block, 10 shapes instead of 35.

Security

  • Finding the texture and material files a COLLADA, X3D, OBJ or MTL upload names took time that grew with the square of the file's size, so a crafted upload of a few megabytes could keep the server busy for minutes. These files are now read in a single pass, on the server and in the browser.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5P5xxH9yvWf8Au302k0c7SEVJMaiS+KTOElYVIGw4KinRBHTSr3Lc/tmNF0aTDJaecbjcKuz5zrzimSYyQhtuWYIjyBqDew8qfJRoek/6+rycT2u7zvYyzyjmuWGHA+Wns13LLoCk6GsRxeya03ZyWvdwfyJ5eCGmJfAFN+ukZl3VTUmeADIqaQbKiYnHzj3lYQljacH3x0ebkFQ8Q1TPbCcRrUTjEVOAheWM3g7k5knbYstBo0EGUCr84TtsNvyenqCnr0uH8g10NPBt+mIaQRIWlK2xua6RoPtCGY33JmoGVzOGppM8rhyaVQ40wCdY2Sp1Mm+itA1ViyqjF/OcumS4LL5bAE+J0Y5J+4zzCELNWPQIOXGaA6X+YwWXEEN8Q+TB5UlqNOqyUliRVa3JsXcnmfUpwBRQPGEYe5UN48bXEUNRzodZ9J97SclDVApc8wayuf7kZNe3q8HOkjj8euEUXw1QZeSSfPv1cFWm0iy72SYqlrBfylMs4+L32u0B3nloFACcrgVgi0+erNSNGyzKJS2AwwFLXo1MEwTT6IGAx2Lmquq5sT427HDavOlwRpsAeGQ5IwZF+2rxJyhAYl+PkLHCeHH7UwyjHteTxbIYmsUSwzSjPd81sVuI/O/UUfFVhAQbH2etMRBSdQj19Ub0+frTScX32NKmmvYjG8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.0
Release Details
UpdatedOct. 26, 2026, 11:33 a.m.
Changelog

Models get thumbnails in Files without anyone opening them: the browser of whoever is viewing a folder renders them in the background, within limits each user sets. A thumbnail is kept once per file version and shown to everyone who can see the file, including share recipients and public links. VRML files, FreeCAD files saved by FreeCAD and FBX files with embedded textures open correctly again. Existing thumbnails turn back into icons after the upgrade and are made again as folders are browsed.

Added

  • Thumbnails are made while you browse Files. Models in the folder you are viewing get a thumbnail without being opened: one file at a time, while the browser is idle and the tab is visible, and not while Save-Data is on or a model is open in the viewer. Folders without models load none of this code. Each model is rendered in its own Web Worker, which is stopped the moment it runs out of time, so a large CAD file never freezes the page. Personal Settings → 3D Viewer → Thumbnails has the new options: "Create thumbnails while browsing Files" (on by default, and separate from making thumbnails in the viewer), "Skip files larger than" (50 MB), "Give up on a file after" (30 seconds) and "Download at most, per folder visit" (500 MB). A file that runs out of time or cannot be read is skipped by this browser until the file changes or you press "Retry skipped files"; network and server errors never skip a file, and a browser that cannot render in a worker downloads nothing at all. Only the companion files a model names (materials, textures, glTF buffers) are downloaded, and only while they fit in the folder budget, so a model whose textures are not named gets an untextured thumbnail. A model is never rendered without a companion it names, since a thumbnail is kept for the file's version: one that does not fit in what is left of the budget waits for your next visit to the folder, and one that cannot be found beside the model (or at the path it names) is left for the viewer, which looks further, to make the first thumbnail. In Favorites, Shares and Tags, thumbnails are made once you open a folder, not on their top-level lists. Only files you can edit get a thumbnail. 3MF, AMF, DAE and X3D files, whose loaders cannot run in a worker, and .nc and .g files, which may not be G-code at all, still get theirs when opened in the viewer.

Changed

  • One thumbnail per file version, shared by everyone who can see the file. Thumbnails were stored per user, keyed by file id alone, so each user made their own and an edited file kept its old picture. They are now stored once per version in the app data (thumbnails-v2/<fileId>/), dropped when the file is edited or deleted, and the old per-user thumbnails are removed on upgrade. Models that showed a thumbnail before the upgrade show their file icon again until a new thumbnail is made, by browsing their folder or opening them.
  • The viewer captures a model's thumbnail with the same fixed look as every other thumbnail (transparent background, default lighting and camera framing) instead of a crop of the live canvas, and only once per file version: it asks the server first and skips files you cannot edit, files that already have a thumbnail for their current version, public shares, and opens that took a material or texture from the browser's dependency cache, which may be older than the file. It now covers every format the viewer opens, not a fixed list of 12.
  • The CAD runtime chunk is 63 KB instead of 776 KB. The OCCT glue's require("crypto"), reached only when it runs under Node, pulled the whole browser polyfill for Node's crypto into the bundle: crypto-browserify, bn.js, elliptic, asn1.js and readable-stream. crypto is no longer polyfilled, which also removes the vm-browserify eval warning from the build.
  • The GLB, STL and OBJ exporters load when an export starts instead of with the viewer, taking 40 KB off the main chunk. The slicer's dynamic imports of the same exporters had no effect while the export composable imported them statically.
  • webdav 5.10.0 → 5.11.0 and fast-xml-parser 5.10.1 → 5.11.2, which drop a duplicate key the build warned about.
  • "Retry skipped files" replaces "Clear thumbnails" in the thumbnail settings. Thumbnails are now shared, so clearing them would have removed other people's too; a thumbnail goes away when its file changes instead.
  • roave/security-advisories is no longer a dev dependency. Its latest release declares a conflict with every PHP below 8.2.35, PHP 8.1 included, so Composer could not resolve the dev tree for the app's minimum PHP and every PHP job in CI failed at install. Advisories are still checked by composer audit in the security scan.

Fixed

  • IFC, FCSTD and G-code files never showed a thumbnail in Files. The preview provider registered for model/* MIME types only, and these three have application/x-step, application/x-fcstd and text/x-gcode. It now registers for the MIME type of every format that gets a thumbnail.
  • Thumbnails never showed to share recipients or on public links. They were looked up for the logged-in user, so a recipient saw their own (usually none) and a public link, with no user, saw none at all. Anyone who can see a file now sees its thumbnail.
  • FreeCAD files (.fcstd) saved by FreeCAD failed to open with "FCSTD archive contains no .brep geometry entries". FreeCAD stores each body as a .brp file inside the document, and only .brep was read; both are now.
  • VRML files (.wrl, .vrml) spun for 30 seconds and then failed to open in the viewer. The loader waited for a callback three.js's VRML loader never calls; it now uses the scene the parser returns, so they open.
  • FBX files with embedded textures opened untextured. The images the file carries were looked up by name among the companion files, matched nothing, and got a blank placeholder; they are now used as they are.

Security

  • A model rendered in the background loads only the files it was given. A texture or buffer it named but that was not among them was requested from the URL the model wrote, so a model in a shared folder could make the browser of anyone browsing that folder request an address of its author's choosing. The worker now refuses every URL except the model's own files and the app's decoders, before any request is made, and skips the model.
  • Storing a thumbnail requires the request token (CSRF protection) and edit rights on the file. The upload route opted out of CSRF checks, and anyone who could read a file could set its thumbnail; read-only share recipients now get 403. The browser also sends the version it rendered, which it learns before it starts downloading the model, and an upload for a version that is no longer current is refused, so a thumbnail can never be filed under a newer version of the file.
  • Dependencies updated within their existing ranges to clear npm audit: follow-redirects 1.16.0 → 1.16.2 (credential headers kept across cross-origin redirects), reached through axios's Node adapter, which the browser bundle does not use, and vite 7.3.6 → 7.3.7 (dev-server advisories). Dev-only fast-uri, brace-expansion, js-yaml, moment, pbkdf2 and qs move to patched releases as well.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureHW+gyQT9pWQ7mEMg5wkO6BOlN33tSE+lpY33kDjIvJ0XIvGh3JY9P7KW64TxfSUb5xG+sArQE8CsPqTDbwGjKPy+sMK+iPQrKdbIMKA/xmrW6ISonhqxt1IN9siV0EiPhGVlLsigL76kuWbzAzaxiHiISofv79xZt2hR0YwD/vHqn3IRNQXREf7gtSnNDGXF5HmQVELaR43ccFlJK4husCsCGSxnQ0Fdbt6I3OZd2+bp1l53cG2EVOTbNFn5Azc4krpiy1jP8oNO6PVKveZPHObTBiXqYMNm6seHkEuATm0vw+aq5F9u/HCVitQyzXo+0oANQFoYW1+FqtWT1ueSgTaz4++lsn5Z5b7aDtubDWISj5AJL/kERXdfPvux6klusP4UPb1YP1yLkO/tWBQWn3is4SsSvC9f3UGBTKch/IEAcHjnL2c5PSqml491KCHF3/4jqQUu8ozKuG6n/09yImtQDmBvuyGlEuBCZT4rANZ8Pr9Q29zbDkFqQCNR/qe+tveEQzKDKO0ECaDp4bjxydNvnff3+m6ceWp5/KpHXLUfs228XoJqBVuyY5jI3+i/ltEnXYp8l3coQFBVKmKiLvuE8aGbP92EmTZXc1sv2Q8RHBSMt3Mxd/78cMIF4pMYjGsKOIHWl4R9hvctcntXkiB4BD2ALVTCZ2ypvBihqjE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.6.0
Release Details
UpdatedOct. 19, 2026, 10:51 a.m.
Changelog

Annotations and measurements become shared notes that everyone who can open a model sees, and the app supports Nextcloud 35. CAD, BIM and 3DM files open again on App Store installs, Rhino 3DM files open at all, and the Favorites view lists starred models.

Added

  • Annotations and measurements are shared with everyone who can open the model. They were saved privately per user, and measurements were not saved at all. Now each model has one set of notes: share recipients and public-link visitors see it, and anyone who can edit the model can add, change or delete notes. Each note saves on its own, so two people editing at once never overwrite each other. Each note shows its author, except on public links. Existing private annotations move into the shared set the first time their author opens the model, if that user can edit it; otherwise they stay visible to that user only, marked "Private, not shared". Notes follow a model through moves and renames within the same storage; moving it to another storage, such as a group folder or external storage, gives it a new file id, and its notes are removed by the daily cleanup.
  • JSON export includes measurements, and import restores them.
  • Nextcloud 35 support. The app declared max-version="34", so the updater listed 3D Viewer under "Apps missing compatible version" and an upgrade to 35 would have disabled it. Checked on 35.0.2 by upgrading a 34 instance in place: the app stays enabled, and models open from the Files app, the standalone viewer, the file picker and public shares, with nothing logged. Nextcloud 35 types the schema and file APIs more strictly, which surfaced three changes: the folder_path migration calls modifyColumn(), the only form 35's ITable declares (changeColumn() was a deprecated alias of it in every supported server); the slicer download endpoint no longer treats a folder ID as a file, which deleted any folder older than a day inside its temp directory; and the temp-file cleanup job skips a non-folder at the temp path instead of failing on it.

Changed

  • three.js r185 → r186. The Babel config moves from package.json to a project-wide babel.config.json: r186's CommonJS entry only re-exports the ES module build, and a package.json config never reaches files inside node_modules, so Jest could no longer load three.
  • Babel 7 → 8 for the Jest transform. @babel/plugin-proposal-object-rest-spread, @babel/plugin-syntax-dynamic-import and @babel/plugin-transform-runtime are dropped: no config referenced them, and the first had no Babel 8 release. Babel 8 needs Node 22.18 or later, so engines.node and .nvmrc move to 22.18.

Removed

  • The /api/annotations/{fileId} endpoints, replaced by /api/notes/{fileId}.

Fixed

  • STEP, IGES, BREP, FCSTD, IFC and 3DM files never opened when the app was installed from the App Store, failing with both async and sync fetching of the wasm failed. The loaders fetched their WebAssembly runtimes from /apps/threedviewer/…, which exists only when the app sits in the shipped apps root; App Store installs land in the writable root, custom_apps/ on the official image. The OCCT, web-ifc, rhino3dm, DRACO and Basis paths now resolve against the app's actual web root, which also covers instances served from a sub-directory (#188).
  • The file browser's Favorites view was always empty. The controller read favorites through OCP\Files\Tags\ISystemTagManager, which does not exist, so Nextcloud injected null; it also looked for system tag 10, while favorites are per-user tags. It now reads the user's starred files from ITagManager, and the browser renders the flat Favorites list, which no view state reached before (#205).
  • Rhino 3DM files never opened, for three reasons. The published app shipped rhino3dm.js but no rhino3dm.wasm: the wasm is git-ignored and its old source in three.js's examples/ no longer exists, and the copy step skipped missing files silently. The runtime now comes from the rhino3dm package (8.32.1, the version three's loader names), and the build fails if either file is missing. Its emscripten glue compiled code with new Function, which Nextcloud's CSP blocks; it is rewritten as it is copied, the same repair #169 made for the CAD runtime. And a worker whose runtime failed to start never answered, so the viewer spun with no error: the decode now has a deadline, worker errors are reported by their message, and each load terminates its worker (#204).

Security

  • Production dependencies updated within their existing ranges to clear npm audit: vue 3.5.41 → 3.5.44 (@vue/server-renderer XSS), axios 1.19.0 → 1.20.0 (prototype-pollution gadgets), brace-expansion 5.0.9 → 5.0.12 (ReDoS), source-map-js 1.2.1 → 1.2.2 (event-loop DoS) and DOMPurify 3.4.15 → 3.4.17 (IN_PLACE sanitizer bypasses).
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewgF9SWWbzLH22o0y9lzTkAUX33vlRdY37uuPVanE2nMuer3V8gTzxkFnfGn2m6iXOWfHvlTAM1xMXbMIiFOCvTTw5rA2GMq+T+QVh8kAyeDAOtQVU7Rr0J0+x2PK0HNkmwlJstOc7xGiYEiVLXEu6mEKbb99Q/o7xlkUsgvmAuK6Xxu1EZvWJi30bx8pJwJC3jbRMaZX5CRWYpoPkeoOCqglgXTm/j8wCqNP8W4ofmWZlOdqM5VQoApIv3Iv9Bvtp7lzau+r+saUNcgShEHuMhiODLeWDdznd5XUrQNLp7yLiYM2tk/brB04F9qIL0nr1BJtA1qhOe2nlIHtmvyp1Yy8bEryIPLKgWHubSyMYV8qUt5pJP3b5ZOmlDfNYBpru1mNJVNFOPlEyWw/74lRAWRadFY3dOASyyUfzcr0rMraMcvKg3Kg881XEAPiizNfTCHi5trQR4+gfbd7e42gT2Z5KGuZLGllAntMzP9B/ZwMl5VuFZLaWJNosjkOdry5TRH8ZOn1hyEj+vP4fdi+a81Wcq2yRjuER+LG6SL3LKkVIjXLCnat1N0nEzxOfPEi3VPNDy6jNJhoX8aaDAQJklXFc/TcD7+6MwLXR/ZXLlxRZ7dvpu/tO3WQF75kxCo94Z6fILOS5EhjDf0JZb6iFscELiwK9R7A8XaEiqHKFY8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.5.1
Release Details
UpdatedSept. 21, 2026, 6:09 a.m.
Changelog

CAD models open again on Nextcloud 34: STEP, IGES, BREP and FCSTD no longer need 'unsafe-eval', which the server stopped letting apps request.

Changed

  • @nextcloud/vue 9.9.0 → 9.13.0 and @nextcloud/dialogs 7.4.1 → 7.5.0.

Fixed

  • STEP, IGES, BREP and FCSTD files failed to open with EvalError: call to Function() blocked by CSP, and no page let the viewer compile WebAssembly at all. The OCCT runtime is rewritten at build time to stop compiling its bindings with new Function, and the app now adds 'wasm-unsafe-eval' — never 'unsafe-eval' — through Nextcloud's additive CSP event, so CAD and IFC models open from the Files app without an administrator editing the policy (#169).
  • The release workflow reported success when nothing was published. The app store step was continue-on-error, so a failed publish went green at the step, the job and the run — v3.5.0 was tagged, built and released with nothing on the store and nothing anywhere to say so. That flag is gone, and a step after it asks the store whether it is serving the release, checking the platform range and download URL as well as the version.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureRJMTtOs8itpPTgMandBOx/fB3Lb4eQhNqh82AL50L6dsquHrSy1TrIOYVtn0rZV50Zwy23N2wGiC8yP450hkEYtMG4Hqmb3RRijNvGdioBpHryA7XyF2II7sx13g9ebXTkk6Aq4qq82sTVe8LtqElZfjD7mshN8eRrrQDMCh7xgly6dBOkyagZqowByvVaiL093OVqO3qUCCGfrWslIB17NtM+8BreXIrsLn/fiQe67uBpumOD6hzEYaWwxqYNrLoejUfZWsShZnvUa5mUG5v1mquxXDUGUswb757OYsDq1Yg3KEU3mv4yYZHmigJfjnHZ+n17j9sWt0g6xXI8YXAY5t2cxx91ll9WQGeVddYZIJjseJodhAV5fCe1V87Ks+pdVYPdXhhdDMQGbFBacnFM+BRRuN7yP3QIpBM1re1qKRcIBDTl3oSUqnCQJ9vLG6im+NxvUYQ5y2VfETyRlQdJPo1j+WToO4dhpI3KHSYpU42HW+T1YvPlmpsosrxanIEnqNzl8uqvjzQibww0A/uIHMbKLsp5nGblQRhlbeLzeZvQs5C28K9Ux9PvNpCEIdXsyYwhB7PHXuu/SYzcsBWR1Zu3i+/cV0SywOER8qSltd+KQuvBXuAveGUqZhRe0zOJStOs90w2rcdHe32BIAJ2GSOdYA+uDnkdY6k26qTAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.5.0
Release Details
UpdatedAug. 11, 2026, 3:17 a.m.
Changelog

The viewer is redrawn on a themeable design system: the Theme control now moves the whole viewer rather than only the scene, every control is a Material Design icon instead of an emoji, and the top bar, tools panel, slicer dialog, performance HUD and the measurement and annotation surfaces follow the design sheet. Also carries three.js r185, the move to ESLint flat config, and the security fixes listed below.

Added

  • Keyboard shortcuts for six tool rows: R reset view, F fit to view, M measurement, S statistics, P screenshot, ? help.
  • The Playwright suite now runs in CI.
  • A browser check that the design system's tokens resolve to real colours.

Changed

  • Three.js r184 → r185.
  • The live loader check covers OBJ, glTF, Draco-compressed glTF, PLY and STL instead of two app-owned parsers.
  • "Open in 3D Viewer" is the mockup's outlined pill, with an open-in-new icon and a tooltip saying it opens a new tab.
  • The tools panel's sections are rows separated by hairlines rather than nested cards.
  • The dark palette comes from the design sheet, with separate tokens for the accent as a fill and as text.
  • The viewer's Theme control themes the whole viewer rather than only the scene background and grid.
  • The slicer dialog follows the design sheet: 560px, one segmented format control, a lighter backdrop.
  • The performance HUD and the model statistics panel follow the sheet, both drawn as canvas chrome.
  • Every control is drawn with a Material Design icon instead of an emoji — 154 across nine components.
  • The tools panel is redesigned as a floating card with collapsible sections.
  • The viewer's top bar is redesigned: a 56px header, the filename centred with its format, an FPS pill, and Tools as the one primary button.
  • ESLint moved to flat config, for @nextcloud/eslint-config v9. New rule families are off and newly-promoted rules are warnings, each carrying its count in the config.

Fixed

  • The antialiasing setting did nothing — the renderer hardcoded it on. It now follows the setting, applied at mount.
  • The help dialog dimmed the viewer and left the rest of the page lit. The four fixed overlays are teleported to the body, past the containing block Nextcloud's backdrop-filter creates.
  • The help panel drew different icons from the controls it describes; eight rows disagreed.
  • The controller rail's full-width controls sat seventeen pixels left of centre.
  • Every NcButton rendered as secondary whatever it declared: @nextcloud/vue 9 moved the appearance from type to variant.
  • Four controls were still drawn with an arrow character instead of an icon.
  • Nextcloud's navigation toggle looked disabled on the viewer's top bar.
  • The browser fixture rendered the whole viewer into a zero-width column, so every geometry it measured was of that.
  • A tools panel section header turned pale blue with grey writing on it under the pointer.
  • The slicer's format selector rendered as three chips floating in a track.
  • The 3D controller was a white ring on a white scene.
  • The Send to Slicer dialog carried a hand-written dark theme that fought the tokens.
  • The per-slicer Open buttons were drawn in each slicer's brand colour, down to 1.78:1.
  • The browser legibility sweep mis-read every color-mix() surface.
  • A theme chosen in the viewer was forgotten on the next load.
  • The viewer's header kept its white text into the light theme.
  • The measurement and annotation panels' close buttons did nothing, and no tool row ever showed it was active.
  • An annotation's note could not be edited.
  • Label text rendered stretched.
  • A measured point landed on the transform gizmo's picker plane rather than on the model.
  • The measurement reading was 1% of the model's height.
  • The markers drawn into the scene were still the debug palette.
  • Every label was a wide black bar with a few pixels of text in it.
  • Taking a measurement filled the viewport with flat green.
  • The measurements and annotations panels had never been redesigned.
  • Switching measurement mode on produced no visible change — the panel opened underneath the tools panel.
  • The measurements panel only appeared once there was something to list.
  • The tools panel never announced the state it restored on load.
  • The export format control and the slicer's format control had labels that labelled nothing.
  • The viewer drew a light card and a dark card on the same rendered model.
  • The performance HUD's mode chip was a pale blue pill with near-black text on a near-black panel.
  • Half the model statistics panel was white text on a white panel.
  • The design system had the status colours backwards: the unsuffixed token is now always the foreground.
  • Every button in the viewer lost its colours the moment it was used.
  • The ground grid was fluorescent green on every theme, and the setting meant to control it did nothing.
  • The viewer's buttons were the wrong shape on every real Nextcloud page.
  • The top bar crashed before a model was chosen.
  • A "Model loaded" toast covered the Tools button on every page load.
  • Ten style declarations had stopped applying, from an rgb() bracket a token sweep left behind.
  • The watertightness badges were pale green and pale amber on white, at 1.9:1.
  • The bundle size check picked which file to measure by the shape of its content hash.
  • obj-mtl.spec.ts had been failing since before 3.4.0 with nobody to see it: its Nextcloud stub was missing OC.filePath.
  • --color-main-text was written with five different fallbacks, --color-border with eight. All 224 references now go through the tokens.
  • Dark mode asked the light theme what colour to be.
  • A help icon's background tint never rendered — it mixed from a variable Nextcloud does not publish.
  • The dark theme never reached the toolbar, the viewer or the toasts: ten rules keyed off a class the app never sets.
  • The slicer dialog followed the theme only when opened from the standalone app.
  • The viewer asked Nextcloud for the wrong primary colour in twenty-five places.
  • --color-primary carried three different fallbacks, so an unthemed instance rendered whichever rule an element landed on.

Security

  • Cleared three advisories from the production dependency graph, lockfile only: dompurify 3.4.13 (GHSA-55q2-fjhq-7xh7), nanoid 3.3.18 (GHSA-2v37-7h3g-55p8) and brace-expansion 5.0.9 under webdav (GHSA-rgw5-rvv9-x895). npm audit --omit=dev goes from one moderate and two high to zero. The residual 3.4.0 recorded is unchanged: webdav ships a prebuilt bundle with brace-expansion inlined, which no lockfile change reaches.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature33Nuq3jk4L8Tk0nUQ/SUKRB9gkf/O+h254EVTLZroD9RvLW9vF6sZqc5zxHEqjR1n15D6JGSbNsdiHTIg3XruaBzMEQ9IplE8gzY6tkclHRN1n1plXbYwrFrZs/nfmUcEJQu2XKVoeCFrN+hPwVIXng/m5p+xGzcHhOnev/o2GNRTFHbSET9Sy7HHIGmieaOf2g6Nrbhto/X7QAnjk2i8Ht/OVLggubgPe85SYsG8qvmi03S0xY8NzFl7XQ6lBlY1DqZSt7Rn/PY8OQsYAMIRA+Ihwu6JU4hWiw1wyV0EFnWayypnS2l4hTk2GNG06Y8bbe1qt05r2LSQSqPI3HOd87nKK/ETmwJdHkpNDJi9NNV0iYuAp1fOsfH7d0VHkbuxWTXZ223YWtlTA+0XKNfZ7Jw5mq0zlyIjpyNDPjScwXv3g+/M835mTUFeGJGkv6HrVSoNtIhkAp0Mh1O5XXw/X0Y/6UlKsky/r0BsWD9a4ddMLocWOm3EvGWi17lsPkX7DJFUH6DKuIiPrKxjGiUM0+Op6sLPSVULqyrAudTe7nHZ2/YM2iDeOrUc6jc3+aNqKLbnv1ApdpmlFoErnZvL/RwCLd9463758stiEOrKksv1K84oCvak7W6FCsEZdEEMK7fzqS+r7mzCqAwH5qewAcIB1l844DIDh7X2i4eQ4o=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.4.0
Release Details
UpdatedJuly 28, 2026, 6:52 p.m.
Changelog

Public share links now render 3D models, and every format that carries external textures resolves them there. Also carries the security fixes listed below.

Added

  • A merge gate that treats an absent check as a failure, as a ruleset on main and as npm run pr:ready. gh pr checks reports only the checks that exist, so a queued run contributes none and a pull request whose matrix has not started looks exactly like one where everything passed — which is how a broken eslint run reached main. The required set is the seven summary jobs: each aggregates its own matrix and reports unconditionally. Because the ruleset also covers pushes, work on main now goes through a pull request.
  • The integration suite now also runs on MySQL and PostgreSQL. The version axis is covered on SQLite; this adds the database axis, which is where the queries differ rather than where the server does — escapeLikeParameter() picks an escape character per platform, and MySQL's default collation is case-insensitive where SQLite's and PostgreSQL's are not, so a folder listing settled on one backend is not settled on the others. Newest supported server only, rather than crossing both axes.
  • A PHP integration suite that runs against a real Nextcloud server, covering what mocks cannot: real storage for the dependency route, and a real connection for the file browser's folder listing. Both bugs that reached production here — the str_replace prefix that reported folders like modelstextures, and escapeLikeParameter() called on the query builder, where the interface never declared it — are now caught, each verified by reintroducing it. The bootstrap exits non-zero when it finds no server rather than skipping, which is how the retired workflow managed to report coverage it never had.
  • FBX and 3DS textures now load on public shares, which completes declaration-based resolution for every format carrying external textures. Both name their textures in binary structures, so they are walked — FBX node records, 3DS chunks — rather than searched for their marker bytes: a filename occurring inside vertex data is not a declaration, and treating it as one would let a crafted model name any file beside it. Walking also steps over mesh data by its declared length instead of reading it. The folder listing is now only a signed-in fallback.
  • COLLADA and X3D textures now load on public shares. Both formats name their textures in the document, so they resolve by declaration through the existing /dep/{name} route, the way OBJ and glTF do — no folder listing, which is what needed a session and left them untextured. Matched with patterns rather than an XML parser: only the paths are wanted, and a parser on an uploaded document brings entity expansion with it. Signed-in sessions keep the folder-listing fallback for documents the patterns miss.
  • 3D models are now viewable through public share links (#115). Share pages previously rendered nothing at all: LoadFilesListener returns early for anonymous visitors, so the bundle never loaded. A new LoadPublicShareListener hooks the public template.
  • Textures load on public shares. OBJ materials, their maps and glTF buffers are served by name through /public/file/{token}/{fileId}/dep/{name}. The model's own declarations are the authorisation — an undeclared name is refused, as is any path escaping the model's folder.
  • Tests for both database migrations, which had none. The load-bearing one pins the sha256 folder-path digest across the three places it is computed — the migration backfill, FileIndexService on write, FileIndexMapper on read. Drift there silently returns empty folder listings rather than an error.
  • Doctrine DBAL stubs in tests/bootstrap.php. IQueryBuilder takes its PARAM_* constants from Doctrine, which nextcloud/ocp does not require, so mocking IDBConnection used to fatal. No database-facing class had ever been unit-tested here.

Changed

  • A successful model load no longer fills the console with warnings. getFileIdByPath logged its normal progress at warn — the folder listing, the name being looked for, the move to subdirectories — so a model with ten textures produced twenty-odd warnings on a load where nothing was wrong, and a real one was indistinguishable from the noise. Routine steps are now debug; warn is left for a listing that failed, a subdirectory that could not be searched, and an unexpected error. A declared file that is genuinely absent is the caller's to report, which it already does once via missingFiles rather than once per lookup.
  • The signed-in texture lookup no longer guesses at a name that does not match. About 150 lines of similarity rules — singular against plural, a leading word dropped, a length-ratio partial match, and a mapping from any colour-ish filename to any body-ish one — came from making one model work and then applied to every model. A rule that matches two different filenames is a guess, and a guess that lands wrong serves the wrong texture with nothing to say so. The case it was most likely to get right, a name differing only in case, is handled before that point and on the server since PathLocator; what remained only fired when a model referenced a file that is genuinely absent, where rendering untextured is the honest answer. getFileIdByPath goes from 366 lines to 155, and the conventional-texture-directory search that was duplicated in its catch block is now one function.
  • Unblocked dependency updates and refreshed the lockfile, superseding ten stalled dependabot PRs (#104–#114) in one pass.
  • stylelint 17 rule fallout, all resolved — color-function-alias-notation rewrote 30 rgba() calls across four files.
  • The 3D navigation controller is now a "Split console": a steering annulus with its buttons on an attached rail, styled from Nextcloud tokens. The eight decorative arrows are gone; arrow keys now steer a focused ring. The 15% dead zone, 110% hit slop and hold-to-repeat behaviour are unchanged.

Fixed

  • A texture in a subdirectory the model does not name was only found if the directory had a conventional name. The search that walks whatever subfolders the listing reports asked for each one without includeDependencies, and in that mode the server filters its answer to 3D models and drops every image — so the one pass written to find textures in subdirectories could never return one. A separate pass over sixteen hardcoded names (textures, maps, images, and case variants) covered the common cases and hid it, leaving a texture in a folder called skin or PBR unreachable with the folder sitting right there in the listing. Signed-in sessions only.
  • "Reset" and "Fit" could not be clicked. The viewer's top bar is position: absolute inside a container that was not position: relative, so it anchored to the wrong ancestor and sat under the app content.
  • The navigation controller was hidden behind the app navigation, which Nextcloud renders at z-index: 1800. It is now anchored to .three-viewer.
  • The 3D file browser invented folders that do not exist. getFolders() stripped the parent prefix with str_replace(), which removes every occurrence: with no parent the prefix was a bare /, so models/textures was reported as modelstextures and matched no files.
  • FileIndexMapper called escapeLikeParameter() on the query builder, where it is not declared — it lives on IDBConnection. It worked only because the server's concrete class happens to carry it, the same bet that fataled the Nextcloud 34 upgrade.
  • A test file was raw binary as far as git was concerned. \0\x01 reached xmlModelDependencies.test.js as literal bytes rather than escapes, and a NUL makes git stop producing diffs for a file — it had dropped out of line-level review while passing CI. A new guard scans source for C0 control bytes.
  • composer test:unit ran every test under tests/, not the suite its config names. It passed a positional tests path, which overrides the configured testsuite — the same defect that made the old integration workflow run the unit suite under another name. Retiring that workflow left this copy of it in place, where it stayed invisible until a directory appeared that the glob should not have swept up.
  • A texture declared in a different case than the file rendered as missing. 3DS keeps map names in DOS 8.3 form, so exporters write WOOD.JPG beside a file saved as wood.jpg; MTL files written on Windows do the same with subdirectories. The declaration matched — that lookup is already case-insensitive — and the fetch then asked storage for the path exactly as written, so the model rendered untextured with its texture sitting next to it. The exact path is still tried first; only a miss walks the model's own folder, stopping at the first segment with no match. Where both cases exist, the exact name wins.
  • The merge gate blocked a ready pull request after a force-push. Every workflow here uses a concurrency group with cancel-in-progress, so superseding a running job leaves a CANCELLED entry in the status rollup beside the SUCCESS of the run that replaced it — both under the same check name. The gate read every entry and reported the superseded one as a failure. It now takes the most recent entry per name. This is the second state-reading defect in that script, so the deciding logic moved to pr-ready-lib.mjs and gained tests; a gate that cries wolf is the one people learn to merge past.
  • The case-insensitive lookup above covered the public route only, and only its last step. An OBJ's textures are named by its material, so the material has to be read before they are known at all — and that read still asked for the exact path, so a mtllib CHAIR.MTL beside chair.mtl left every texture in it undeclared. Signed-in sessions missed for a second reason: /api/files/find and /api/files/list resolve the declared path themselves, and both matched case exactly, so Textures/wood.png beside a folder saved as textures found nothing. All three now share one implementation.
  • A path climbing out of the user's folder returned 500 instead of 404. Folder::get() raises NotPermittedException for a path like ../../etc/passwd, and /api/files/find caught only NotFoundException — so probing the endpoint produced an internal error and a logged stack trace apiece, and told the caller a refused path is not the same as an absent one.
  • Every viewer session logged a PCFSoftShadowMap has been deprecated warning. Three.js r184 reassigns the constant on the first shadow pass, so the app was already rendering PCFShadowMap.

Removed

  • Retired the "Integration Tests" workflow, which reported coverage it did not have. test:integration and test:migration passed a positional path that overrode their configured testsuite, so both ran the unit suite under other names — and the job had no Nextcloud server to integrate against. test-phpunit.yml covers those tests across the same OCP 31–34 matrix.

Security

  • Resolved four npm advisories in production dependencies via npm audit fix, lockfile only: form-data CRLF injection (GHSA-hmw2-7cc7-3qxx), postcss path traversal (GHSA-r28c-9q8g-f849), fast-xml-builder sanitisation bypasses, and nine in dompurify.
  • Cleared the brace-expansion DoS (GHSA-mh99-v99m-4gvg / CVE-2026-14257) from the production graph, taking npm audit --omit=dev from 7 high to 0. Only brace-expansion@5.0.8 is patched, and it renamed its default export, so it cannot be forced globally; pinning overrides.webdav.minimatch to ^10.2.3 reaches it instead. Glob behaviour is unchanged and the advisory's proof of concept no longer exhausts the heap.
  • Capped material libraries per model at ModelDependencyResolver::MAX_MATERIALS. Each mtllib name costs a storage lookup and ~226,000 fit in the scanned header, so one crafted model turned every /dep/{name} request on its public share into hundreds of thousands of lookups. The sharer can be the attacker, and the route has no rate limit.
  • Known residual: the shipped browser chunk still carries the unpatched brace-expansion. Vite resolves webdav to a prebuilt bundle with it inlined, which no override can reach; it clears when webdav republishes. Unreachable in practice, since nothing here passes a glob to getDirectoryContents. The dev tree still reports the advisory through eslint 8 and jest.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureQX95f1tTvJoYA9LqLShsNFPKOwHPgMmw0dE0Rq/KdnyoJLpH+TOYawYFTn6RFykF5RIFDyCrTTEDJo5ehLy3bLnyCRX9pLza7My58nDdCND+fRlHgzvUNwzfULSQC1zPi92sHmy+SPqnUrp6xhevBe57/IWxE2JyUf/T80Gf+EXwWFzc9uoVoE2+mHvzvV0db/ZVO+OX/HHgYzZ65Q8wZTbycb1nZFx8RaxkJogd/5Nh8aEb/NlpyKRlJi9Zx3XtyQL6AL0Dcnv2eOQbOuA5VnwFql1UqCtmR1400ZCd0Cf0sSaoX6S1XW3+oLjGhJSHBEi/A+0MVpDChPPHSjfH+ER5gr628NQFfVSxFBXr0hHbHx3r8wBxIk0kRyqjR5Uj5CNzg8QVQsNhhFvsP7FFl59Il2JmzsJXAe99e/8mf1rMGmEZ5kVXwUImUl4Bqgc3IDF5SKUTfspNPWKWojRzT9sRgLZDI1RFqep2ov1wMNYOQ4Qzbzk9N93iumDQk1FsiYud2VO3TVpjaNCc6vWL/S+jA03TksnkF4sqxexJRAGwBpyEcwOHwUG0R6HVZpchkdhL6MW5UVwSb2i9b1eIqs1+DbQ70HCffX7pPIxFW6zOVDsdNb00Q6vaYeT2q9IIR32DnKj20bnBjLgP3BX61V3Ul7PLoAbbhF8vABR4CEA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.2
Release Details
UpdatedJuly 27, 2026, 11:31 a.m.
Changelog

Security release. Upgrade immediately if you use password-protected link shares containing 3D models.

Security

  • Share passwords were not enforced on the public 3D file endpoint. PublicFileController extended plain Controller with #[PublicPage], and PublicShareMiddleware returns early for anything that is not a PublicShareController — so no share authorisation ran at all. Combined with ShareFileService::loadLinkShare(), which only checked that getShareByToken() matched, anyone holding a share token could read the file regardless of its password. The token is the part of the URL people paste into chats and emails; the password exists precisely for when the token alone should not be enough. PublicFileController now extends PublicShareController and implements isValidToken(), isPasswordProtected() and getPasswordHash(), delegating password and token validation to the framework and picking up brute-force throttling with it. findValidLinkShare() additionally rejects non-link/email share types and expired shares, and treats a ShareNotFound from the share manager as "no share" rather than letting it escape as a 500. Present since the controller was added in 1b0ca86 (2025-09-15), so every release from v1.7.9 through v3.3.1 is affected. Verified on Nextcloud 34.0.2.1: a password-protected share now returns 404 without the password and 200 with it, ordinary public shares are unaffected, and expired shares return 404 instead of 500.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturemn6HBntWm2p/at2aww2v9uJiZOjj7tNXpISa325yfW2SKnZnaJHt7wSDJnb6ghsYuF1u19BVFg+1b+e/hFTFmhD8l1a0BL69FqZej7a4bwwfFbovRiDUkkZjik0nVahdD3k6fg6qabxnJbHEh79Lz2/vgcE/5nzAio06G5xJrZgeR10aoZs5oxMEpnNQlPWCiIZV78oY2PCGWosOEQaz78SzU2sX1Saq4goXMCyG1ElYLLeMbtmTPv3g9Dp08FyUctxtxGX1JbuNFg2F2AuhZSyMmFcnS6jGp9STzWZrD6x9jbd6KP5/mu6dPe88CAouT/RmUSFS14CCYAXOPdeqc0wgsOJncpoRZycZ/FwACJKGOjzR1qACdtG5wKjXnMDMJA5mdgDL+G77s7PzeY+u4jBkS2iF8swLxzZphid2HGNN5Rg4NECyCwsBYM/o+hbPVpro6iLKOg/hpiugpoQpwDw0RmcujPDYWewonRnNKdB+7i/Qk5FXDluhqGr7GoRek+nZaqm/pxSIGEeSnkpqoPAxHY978OncdxhKSIe8ACCxl9g9nokM4bxLA4zXWGHL8Onnb/DdWIGEfuGIhdhV/5QtSSW1xW75HrTcuHPJJFBAF2ovwQYPfexY26ZU0m7pyt2ucN8xxkkpuD6RDMpm2U/v9y+AxbtuNunW2CoKqm0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.1
Release Details
UpdatedJuly 27, 2026, 10:35 a.m.
Changelog

Hotfix for Nextcloud 34. 3.3.0 is unusable on NC 34 — both the viewer page and every model download return HTTP 500, and the app cannot be upgraded or enabled. Nothing else changed.

Added

  • Regression tests for both fatals. tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface with no __call() fallback, so it raises the same Error production did. tests/unit/NoPrivateServerApiTest.php scans lib/ for any use of the private \OC container or the legacy OC_* static classes, stripping comments via token_get_all() first so documentation about a removed API isn't mistaken for a call to it. Both fail on the pre-fix code. Note that composer test:unit is not currently run by any CI workflow — test-integration.yml runs only test:integration and test:migration — so these are verified locally until that gap is closed.

Changed

  • nextcloud/ocp dev dependency moved from dev-stable30 to dev-stable31, to match the min-version="31" declared in appinfo/info.xml; the weekly update-nextcloud-ocp-matrix.yml job, which had target: ['stable30'] hardcoded and kept re-pinning it, now tracks stable31 too. The pin deliberately follows min-version, not max: pinning to the oldest supported server is what stops us using APIs that don't exist there yet, and ocp stable33+ requires php ~8.2, which cannot coexist with the config.platform php 8.1 that NC 31 support demands. Verifying against the newest branch is psalm-matrix.yml's job — it already builds an ocp matrix across the full min..max range from info.xml. Psalm run manually against stable34 for this release confirms no further removed-API calls in lib/ beyond the two fixed below, and flags ten DeprecatedMethod notices worth scheduling: IConfig::getUserValue/setUserValue/deleteUserValue in PageController, SettingsController and ConfigController, plus IContainer::query in Application. None break on 34.

Fixed

  • App upgrade fataled on Nextcloud 34 (#116) with Call to undefined method OC\Server::getDatabaseConnection(). The Version010903Date20251121000000 migration reached into the private \OC server container in its constructor; NC 34 stripped getDatabaseConnection() from that class, which now exposes only getUserFolder(), getWebRoot() and getL10N(). It now resolves the connection through the public OCP\Server::get(IDBConnection::class) locator. Deliberately not constructor injection: MigrationService::createInstance() falls back to new $class() when the container cannot resolve a step, and an injected dependency would turn that fallback into an ArgumentCountError. The stale use OC; import is gone and $connection is now typed. Reported with a patch by @EX0l0N; same fix independently submitted as #117 by @coverslide.
  • Every model download and viewer page render fataled on Nextcloud 34 with Call to undefined method OCP\AppFramework\Http\EmptyContentSecurityPolicy::addAllowedChildSrcDomain(). Nextcloud removed the legacy child-src helper from its public CSP class (deprecated years ago in favour of the split worker-src/frame-src directives), so the call added in commit 4e0a532 ("fix CSP") became a hard error the moment the app ran on 34 — taking down FileController::serveFile and both PageController entry points, not just the worker policy. ResponseBuilder::addCspHeaders now calls addAllowedWorkerSrcDomain('blob:'), which is the correct directive for the blob-URL Web Workers that DRACO, KTX2 and web-ifc spin up, and has existed since NC 17 — safe across the whole declared 31–34 range. Reported in #116. The new tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface exactly, so it reproduces the production Error on the old code regardless of which OCP branch happens to be vendored.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturenWYgrEeOKWzfUMwxb3X0hAzGbWgJ9vd3jRvsIFUKLCvTsARpp09yWa5mnupOhaRtHcffz0eidnz5Blw7uN8sTMvty2tpnzgLad3CTvV401vV+je0pyeWgol+mkhoymUwBNcvlR4OWx4NWHX1azz9NdLAlF/J+A1ImE3D2UMf5mXlGctjznQwBeMLrXkKke1qdy23heuGTi6HCnNicY1PpkhC9RcmY0wlBbZr9/kx/TbCYx136AKjctjt4554x9SXkiAjt5S1SSEFefSaNxURYWlSx6xeCt4enQVAfA2uNfj772yxyWRhyvcQMnfIFz4IGTYMCi3KlKZgQvSa/Zj1KNP6d9NWOubG+RqOjzv84wjznJ6xoSTEoNLuQhuYt1zqW2wDyDFT9GD2jSONDYZjOZjal37C4NEvEv9HIkZYkVBL4kvwlytafsD9u4cmKCmgv0ROvPCM2PWVmnT3kXGOS+ivKLcaC0Zt0VciNlhYIAawDLDL+1j+WKRHIPhNUsLjiNfqbmUWUZlbEHPJvdIxTBfePDbvnnyFGWlUBKO+QoEo0JPHrpfisz31eb88G/33/1iblqUUZ3OwU71GAHjFvD8Vf0ivihZZSUqsTDdiEGU1dsbT7DnkFi2bLNfNQhcG14k03NzUVQW+fODwNoRO9pkWJnadgsZQ1ICJAj3sw9M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.0
Release Details
UpdatedApril 20, 2026, 6:47 a.m.
Changelog

Fixed

  • 3DS files loaded tipped on their side when the exporter used Y-up instead of the 3ds-Max Z-up default. The loader had been hard-coded to rotate every 3DS model -90° around X (commit d8117a0, "fix: add coordinate system rotation corrections for Z-up formats"), which helped CAD-style Z-up files but silently broke Y-up exports — Cottage_FREE.3DS for example rendered with its long axis as the vertical. Dropped the unconditional rotation; 3DS files now pass through at their authored orientation. Most modern 3DS exporters (Blender, recent Maya) are already Y-up, and Z-up outliers can be fixed by orbiting or — if this becomes common — by a future user-facing orientation toggle. Other Z-up-rotating loaders (STL, PLY, 3MF, DAE) are unchanged because those formats have stronger Z-up conventions that are not affected by this fix. Reproduced and verified in the live Nextcloud container by downloading the user's 3DS via WebDAV, inspecting bbox dimensions with a new scripts/inspect-3ds.mjs harness (raw X=1243, Y=675, Z=1465 — Y is the height), and hard-refreshing the viewer page.
  • Standalone viewer: KTX2 loader threw on init with Cannot read properties of undefined (reading 'isWebGPURenderer'), so models with Basis/KTX2-compressed textures silently fell back to placeholder textures. Root cause: the standalone ThreeViewer.vue never forwarded the live WebGLRenderer into the loading context, and Three.js r182's KTX2Loader.detectSupport(renderer) dereferences renderer.isWebGPURenderer before checking anything else. Fix: pass renderer: renderer.value in the context, plus a defensive guard in gltf.js that skips KTX2 entirely (with a warning) when no renderer is available instead of throwing. The modal viewer was unaffected — it already forwarded this.renderer. Live-E2E harness now also watches for KTX2/WebGPU console complaints so the regression can't sneak back in.
  • Standalone viewer: glTF models with embedded data: buffers failed to load under Nextcloud's Content Security Policy. The app CSP allowed data: for img-src but not connect-src, so GLTFLoader's fetch() of embedded base64 buffers (the default export shape of many gltf files) was blocked by the browser. ResponseBuilder::addCspHeaders now also adds data: to connect-src. Caught end-to-end by driving Playwright against the real dev container (scripts/live-e2e-check.mjs).
  • Skip-to-viewer link wasn't in a usable tab position — it was rendered inside <NcContent> → <NcAppContent>, which put it ~9 tabs deep behind Nextcloud's header, nav sidebar, and file list. Wrapped in <Teleport to="body"> so the anchor lives at the body level regardless of where Nextcloud's chrome wraps us. DOM-verified focusable in the live container; clicking it moves focus to #viewer-wrapper. The CHANGELOG entry below that claims "first tab stop" was aspirational — Nextcloud's own Skip to main content / Skip to navigation links always win the first two positions, which is correct behavior we can't (and shouldn't) override.

Added

  • Four more CAD formats via OpenCascade — STEP, IGES, BREP, FCSTD (closes #97): All four formats come from the OCCT family of NURBS/B-Rep CAD files and share one WASM integration. Uses occt-import-js (Viktor Kovacs's purpose-built emscripten port of OpenCascade's import + tessellation) — much smaller than vanilla opencascade.js (~7 MB WASM vs 10+) and with a JSON-based mesh output that's a clean fit for Three.js. Architecture: one shared src/loaders/occt-runtime.js exposes getOcct() (memoized module promise so the 7 MB WASM downloads once across all 4 loaders) and buildGroupFromOccResult() (walks the assembly hierarchy preserving node names, transforms are already baked into tessellated vertices so we don't reapply matrices). Each format gets a thin ~25-line loader: STEP (.step, .stp) → ReadStepFile, IGES (.iges, .igs) → ReadIgesFile, BREP (.brep, .brp) → ReadBrepFile, FCSTD (FreeCAD document) → unzip via fflate, filter for .brep entries, feed each to ReadBrepFile — per-body failures don't take out the whole document. The resulting bundle shape: four 1 KB loader chunks + one 725 KB shared occt-runtime-*.chunk.mjs + 7.3 MB WASM served separately from /apps/threedviewer/occt/. Main/app bundles unchanged (0 B trend). Not unit-tested because the whole pipeline needs live WASM + real CAD fixtures to exercise meaningfully; covered by the existing smoke suite pattern.
  • Fifth new format — IFC (.ifc) (#97): Industry Foundation Classes, the de-facto BIM interchange standard. Loader in src/loaders/types/ifc.js uses ThatOpen's web-ifc WASM (~5 MB, lazy-loaded on first .ifc open) and a hand-written Three.js bridge — web-ifc-three has been deprecated and its modern replacement (@thatopen/components) is a heavy framework we don't need just for file loading. Bridge logic: StreamAllMeshes walks the IFC element graph, each FlatMesh's PlacedGeometry entries yield tessellated vertex data in interleaved [px,py,pz,nx,ny,nz] layout which we deinterleave into separate position/normal attributes, then apply flatTransformation (column-major 4×4) and per-geometry color. Express IDs (expressID, geometryExpressID) are preserved on mesh.userData.ifc for future property-panel tooling. Memory is carefully managed — geometry.delete() in a finally block releases the WASM-side handle per mesh, and CloseModel runs unconditionally so parse errors don't leak the full IFC model. WASM copies ship via scripts/copy-decoders.mjs to /apps/threedviewer/web-ifc/ (both single-threaded web-ifc.wasm and multi-threaded web-ifc-mt.wasm — IfcAPI picks the variant based on runtime feature detection) so air-gapped Nextcloud deploys don't need CDN reachability. JS glue is lazy-chunked into a 3.4 MB ifc-*.chunk.mjs that only loads when a user opens a .ifc file; main/app bundle impact is minimal. Not unit-tested because the whole pipeline requires live WASM + real IFC fixtures; covered by the smoke spec suite instead.
  • Fourth new format — dotbim (.bim) (#97): JSON-based BIM format from dotbim.net — simple schema (meshes + elements with { mesh_id, vector, rotation, guid, type, color, info, face_colors? }). Loader in src/loaders/types/dotbim.js handles mesh instancing (multiple elements sharing a mesh_id each get their own cloned geometry), element transforms (translation vector + unit quaternion rotation), RGBA byte colors, optional per-face colors (v1.1.0 schema — unindexes geometry so each triangle's 3 verts carry the face color), and tolerates legacy exporter quirks (missing/null rotation, mismatched face_colors length). Element metadata (guid, type, info) is stashed on mesh.userData.dotbim for future inspection tooling. 10 Jest specs in tests/unit/loaders/dotbim-parser.test.js cover instancing, transforms, color handling, transparency, legacy-rotation fallback, orphan elements, metadata, and JSON/schema validation error paths. Zero WASM, zero external deps, ~180 lines. Like the other new loaders it's lazy-chunked via the registry's dynamic imports — 0 B impact on the main/app bundles.
  • Three new 3D formats — OFF, AMF, 3DM (closes #97 partially): (1) .off (Geomview Object File Format) — hand-rolled plain-text parser in src/loaders/types/off.js covering OFF, COFF (per-vertex colors, float or 0–255 int), N-gon fan triangulation, inline # comments, and binary-OFF rejection. 9-test Jest suite exercises the parse path end-to-end (tetrahedron, n-gon, comments, COFF float/int colors, degenerate inputs). (2) .amf (Additive Manufacturing Format) — wraps three.js's bundled AMFLoader, handles both XML and ZIP-compressed variants. (3) .3dm (Rhinoceros) — wraps Rhino3dmLoader, loads rhino3dm.js + rhino3dm.wasm (~4 MB) on first use from the app's bundled copy at /apps/threedviewer/rhino3dm/ so air-gapped Nextcloud deployments don't need CDN reachability. scripts/copy-decoders.mjs now copies the rhino3dm assets alongside DRACO/Basis. All three formats are registered across the 5 sync points (lib/Constants/SupportedFormats.php, src/config/viewer-config.js, src/loaders/registry.js, src/main.js::SUPPORTED_MIMES, appinfo/mimetypemapping.json); npm run format:check PASS. Each loader is lazy-chunked via the registry's dynamic imports, so the main/app bundle size is unchanged (only users who open the new format pay the download cost). The 6 remaining formats from #97 (step, iges, brep, fcstd, ifc, bim) all require OpenCascade.js or web-ifc WASM (~5–10 MB each) and are deferred until demand justifies the bundle hit.
  • Forced-colors (Windows High Contrast) CSS hardening: New central override sheet (src/css/forced-colors.css, imported from main.js) scoped entirely to @media (forced-colors: active) — no effect on default rendering. Addresses three patterns the a11y audit found: (1) custom badges (.stats-badge, .fps-badge, .active-badge, .last-used-badge) that rely on background-color + text-color alone collapsed into the surrounding surface once the UA repainted both with system colors; they now get a 1px CanvasText border so they stay visible. (2) .filter-format-chip.active lost its visual distinction from the inactive state because both backgrounds became Canvas; the active variant now uses Highlight / HighlightText with forced-color-adjust: none so selection state survives. (3) Focus states that suppressed outline and relied on box-shadow or colored border-color (.export-select:focus, .annotation-text-input:focus, .skip-to-viewer:focus) had no visible focus indicator; they now re-enable a 2px Highlight outline. Verified by a new Playwright spec (tests/playwright/a11y-forced-colors.spec.ts, 5 tests) that loads the real CSS file into static fixtures mirroring each component shape and asserts border/outline width > 0 under emulateMedia({ forcedColors: 'active' }). Caveat: Playwright's forced-colors emulation uses a neutral palette — this closes the keyboard/CSS correctness gap but an interactive Windows HC walkthrough is still the final source of truth for theme-specific contrast issues.
  • i18n parity tooling (scripts/check-i18n.mjs): Walks src/ (.vue/.js/.ts) and lib/ (.php) and extracts every t('threedviewer', 'string'), this.t(...), and ->t(...) call via regex. Diffs the extracted set against l10n/en.json: ERROR on source strings missing from en.json, WARN on orphan keys no source uses, per-locale coverage summary that reports missing, passthrough (value === key), and orphan counts. --sync-en auto-adds missing keys (value = key); --prune removes orphans. Wired as npm run i18n:check / npm run i18n:sync; i18n:check is now part of the validate chain. First sync closed 329 missing keys and pruned 87 orphans in en.json — ar/de/es coverage now reports accurately at ~27% (previously looked higher only because en.json itself was incomplete).
  • Export pipeline unit tests + MIME consistency fix: Nine Jest specs in tests/unit/composables/useExport.test.js cover getGeometryStats (indexed, non-indexed, multi-mesh groups, non-geometry descendants) and exportAsSTL/exportAsOBJ/exportAsGLB (correct MIME — model/stl, model/obj, model/gltf-binary — correct byte length, null-object guards, and a multi-material OBJ scene that passes through the exporter with its material array and geometry groups intact instead of being flattened). Three.js exporters are mocked so the test runs deterministically in jsdom without pulling in Three's ESM-only subpath imports; the real exporter output remains covered by the Playwright smoke suite. Shipped alongside a MIME consistency fix in SlicerModal.vue — the slicer-export fallback path was still using application/octet-stream for STL and text/plain for OBJ even though the main Export Model path had been updated to model/stl / model/obj; both paths now agree.
  • Loader smoke-test coverage for cancel/retry/network-drop: Two new Playwright specs in tests/smoke/viewer.spec.ts use page.route() to deterministically simulate (a) a user cancelling a hanging load and then retrying — the second fetch is fulfilled with a fixture and the viewer reaches __LOAD_COMPLETE, and (b) a network failure (route.abort('failed')) that must surface via __LOAD_ERROR without being misclassified as a user abort. ThreeViewer grew two tiny test hooks (window.__LOAD_COMPLETE on successful model-loaded, window.__LOAD_ERROR on non-abort errors) to give tests a DOM-independent signal — production code is unchanged in behavior.
  • Accessibility follow-ups: Keyboard users now get a "Skip to 3D viewer" link as the first tab stop (visually hidden until focused, slides into view on focus) that jumps focus past the navigation directly to #viewer-wrapper. Both modals (SlicerModal, HelpPanel) now trap Tab/Shift+Tab inside the dialog while open and restore focus to the element that opened them on close — implemented via a dependency-free useFocusTrap composable. Modals also gained aria-modal="true" and tabindex="-1" on the dialog surface. Covered by a new Jest unit suite (tests/unit/composables/useFocusTrap.test.js) and a Playwright a11y spec (tests/playwright/a11y-skip-link.spec.ts) that verifies skip-link behavior under forced-colors: active (Windows High Contrast).
  • Transform Gizmo: Translate, rotate, and scale models interactively with Three.js TransformControls. Mode selector (Move/Rotate/Scale) and reset button in the Analyze section. Disables orbit controls while dragging.
  • Animation clip selector: Dropdown in the Animation panel to switch between individual animation clips (e.g., Run, Walk, Idle) for models with multiple animations
  • Texture optimization pipeline: Quality presets (Original/High/Medium/Low) with Canvas 2D downscaling, memory tracking, and configurable setting in Personal Settings
  • X3D parser: Full XML-based parser replacing placeholder cube — supports IndexedFaceSet geometry, materials, textures, transforms, and DEF/USE references
  • Volume & surface area measurement: Model statistics panel now shows actual mesh surface area (sum of triangle areas) and mesh volume (signed tetrahedra method, accurate for watertight meshes), in addition to bounding box volume
  • Custom color palette editor: Personal Settings → SlideOutToolPanel now exposes four user-overridable scene/chrome colors on top of the base light/dark theme: background, grid, toolbar bg, and toolbar text. Each has a native <input type="color"> picker plus a per-key "×" clear button; a single "Reset all palette colors" button clears everything at once. Overrides persist to localStorage['threedviewer:customPalette'] and are merged on top of whichever base theme (auto/light/dark) is active, so switching themes leaves your palette alone. The viewer rebuilds its THREE.GridHelper in place when gridColor changes and swaps scene.background when background changes — no reload required.
  • Decoder worker pool tuning (parallel decoding): GLTFLoader's DRACO and KTX2 sub-loaders now call setWorkerLimit(n) with n = min(hardwareConcurrency - 1, 4) (floor 2), so GLB textures and compressed meshes decode in parallel across multiple Web Workers instead of serializing on one. Defensive feature-detection — falls through when the Three.js version doesn't expose setWorkerLimit. Result: GLBs with multiple KTX2 textures load noticeably faster on 4+ core machines without starving the main thread or flooding it with workers on 1-core devices.
  • Memory pressure auto-step-down: The performance composable now samples performance.memory.usedJSHeapSize each frame and, when usage crosses 85 % of the configured maxMemoryUsage cap (default 500 MB), auto-switches to the Low performance mode — reducing pixel ratio, disabling shadows, and applying LOD. A one-time warning toast ("Auto-reduced quality — memory pressure") surfaces what happened; the flag clears once memory drops back below 70 % so the user can re-raise quality manually. 5-second cooldown prevents thrashing, and the check gracefully skips browsers without the non-standard performance.memory API (Firefox, Safari).
  • Background indexing status API + progress bar: New GET /api/files/index-status endpoint backed by ICacheFactory::createDistributed returns { active, processed, total, percent, startedAt, updatedAt } for the authenticated user. FileIndexService.reindexUser now pre-counts supported files (respecting .no3d and hidden-folder skip rules), then bumps the processed counter after each file. The Personal Settings "Re-index now" button starts a 750 ms poll in parallel with the blocking POST request and renders a live NcProgressBar ("142 / 284 (50 %)"), falling back to "Scanning…" until the pre-scan completes. Status TTL is 1 hour.
  • Adaptive texture streaming (visibility-aware queue): useProgressiveTextures now accepts a (camera, scene) streaming context and sorts its pending queue by frustum visibility before each batch — textures on meshes currently in view load first, off-screen ones are deferred. When the camera moves, the very next batch picks up the new viewpoint. queueTexture has a new optional mesh argument for visibility scoring; the signature stays backward-compatible, so existing callers are unaffected. ThreeViewer auto-registers the context on init so future loader pipelines that opt into progressive loading get adaptive scheduling for free.
  • Measurement suite upgrades: The Statistics panel's measurement section is now a full measurement tool:
  • Unit picker: Choose mm / cm / m / in / ft / generic units from a dropdown; bounding box, surface area, and volume values re-render instantly with the right suffix (mm, mm², mm³, etc.). Defaults to the project's configured VIEWER_CONFIG.measurement.defaultUnit so it matches the existing ruler tool.
  • Per-mesh breakdown: When a model has more than one mesh, a scrollable list shows each mesh's surface area and volume in the selected unit. Clicking a row focuses the panel on that mesh — the Width/Height/Depth/Area/Volume rows switch to its numbers and a "Focused: mesh name" header with a Show-all link appears. Helper meshes (transform-gizmo pickers, annotation markers) are excluded.
  • Pick mesh in viewport: A one-shot button arms a raycasting click handler on the canvas; the next click on a mesh focuses it in the stats panel. Works with any mesh in the loaded model, respects visibility, and skips gizmo helpers.
  • Watertightness badge: The section header flags each mesh as Watertight (all edges shared by exactly two triangles — volume is reliable), Not watertight (open or non-manifold edges detected — volume should be treated as approximate), or Unknown (non-indexed geometry, or >500k triangles so we skip the edge-map check for performance). Aggregated badge appears at the top of the section; per-mesh flags appear next to each row in the breakdown.
  • Copy measurements: A button copies a plain-text report of the current values (bounding box, surface area, mesh volume, bbox volume, watertight status, per-mesh table) to the clipboard in the selected unit. Uses navigator.clipboard.writeText on secure contexts with the textarea + execCommand fallback for legacy browsers. Success/error toast via the existing push-toast channel.
  • WebXR / VR mode: Enter immersive VR via the 🥽 button in the top bar (only shown when the browser advertises immersive-vr support). Animation loop swaps to renderer.setAnimationLoop during XR sessions, and FPS throttling is bypassed since the headset enforces its own cadence. Testable without a headset using the Chrome WebXR API Emulator extension.
  • Annotation JSON export/import: Save annotations as a versioned JSON document and re-import them later. The schema includes format, version, exportedAt, modelFilename, and an annotations array of { id, point, text, timestamp }. Import/Export buttons appear in the Annotations overlay when annotation mode is active.
  • Scene comparison diff overlay: When two models are loaded side-by-side via Comparison mode, a "Scene Diff" panel auto-appears showing original vs. comparison stats (vertex count, face count, mesh count, bounding box X/Y/Z, diagonal length) with color-coded deltas (green = increase, red = decrease). Helper meshes (transform gizmo pickers, axes/grid helpers) are excluded from stats so the diff reflects only the loaded geometry. The overlay is dismissable via × and re-openable from a "Diff" button in the comparison controls.
  • Annotations persistence (per-file backend save): Annotations now save automatically to your Nextcloud account, keyed by file ID, so they reappear next time you open the same model. Backed by a new AnnotationsService that stores one JSON document per (user, file) under app data (appdata_*/threedviewer/annotations/{userId}/{fileId}.json), with GET/PUT/DELETE routes at /api/annotations/{fileId} validating that you have access to the underlying file before reading or writing. Saves are debounced 600 ms after each change; clearing all annotations issues a DELETE so the backend doc is removed instead of storing an empty list. A small status pill ("Loading… / Saving… / Saved / Save failed") in the annotation overlay header surfaces sync state. 256 KB cap per document.
  • Shareable view link: New "Copy View Link" button in the View section copies a URL that encodes the current camera viewpoint (position, target, zoom) as a compact cam=px,py,pz,tx,ty,tz,z query parameter. Opening the link reloads the model and restores the exact same angle after auto-fit runs, so you can send collaborators a link and say "check out this corner of the model" without needing a live session. Toast feedback on copy success/failure; button disabled until a model is loaded. Works on any secure context (HTTPS or localhost) via navigator.clipboard, with an offscreen-textarea + execCommand fallback for legacy environments. No backend — pure URL round-trip.
  • Clipping box (6-plane section analysis): The existing Cross-Section tool now has a Plane / Box mode switch. Plane mode is unchanged (single cross-section plane with axis + position slider). Box mode enables six axis-aligned clipping planes, one per face of the model's bounding box, each with an independent 0–1 offset slider that moves that face inward toward the opposite side. Setting xMin=0.35 and zMax=0.35, for example, slices the leftmost 35% off along X and the nearest 35% off along Z, leaving the intersection slab visible with interior surfaces rendered via DoubleSide. A "Reset box" button returns all six offsets to 0. Plane and box state are kept independently so toggling between modes is non-destructive, and box offsets auto-remap when a new model is loaded.
  • File browser search & filters: New filter toolbar in the file browser. At the Folders/Types/Dates overview level the search box runs a global recursive search that walks every folder, type, and date bucket and surfaces matching files as a flat results grid (each card showing its full path) — so searching wolf from the Folders root finds Wolf-Blender-2.82a.glb two folders deep without manual drilling. Inside leaf views (drilled into a folder, type, or month) the search filters both subfolder cards and file cards together, and unlocks two extra controls: multi-select format chips (one per extension actually present in the current view) and a size bucket dropdown (Any / < 1 MB / 1–10 MB / > 10 MB). Filters compose with AND between categories and OR within format chips, and a "Clear filters" button appears as soon as any filter is active. Filters auto-reset when navigating between folders/types/dates so a stale query never makes the next view look empty.

Fixed

  • FBX taillight/lens transparency: FBX post-processing was force-overriding every material to opacity = 1.0; transparent = false, which made authored translucent lens shells (taillights, headlights, glass) opaque and hid the coloured mesh inside — the Mercedes GLS rear lights rendered as a solid grey slab with the red only visible if the camera was pushed inside the body. Now preserves the FBX-authored opacity: materials with 0 < opacity < 1 are left transparent with depthWrite = false so inner meshes show through; only materials with opacity <= 0 (which would render invisible) are still forced opaque.
  • Stats panel — File size on multi-file loads: File Size showed "0.00 MB" for formats that come with sidecar assets (OBJ+MTL, FBX with textures, GLTF+BIN). The stats panel was reading modelLoading.progress.value.total, but multi-file loaders report progress as a percentage (max 100) rather than bytes, so the resulting "100 B → 0.00 MB" rounded to zero. Now reads the actual byte count from modelSourceFiles.value[].size (matching main filename first, else first file), with the progress-total value only used as a fallback when it looks like real byte counts (> 1 KB).
  • Stats panel — Texture memory accounting: Textures section reported "Memory: 0.00 MB" while textures were still loading, because the loaded-image check used a 512×512 fallback that happened to resolve before decode — making tiny placeholders look real. Now distinguishes three states: loaded (dimensions ≥ 4×4 contribute to memory), pending (flagged separately so the UI can show "loading…"), and missing (1×1 placeholder or HTML img with empty src / zero naturalWidth). ThreeViewer polls analyzeModel once a second for up to 8 seconds after load when any texture is pending, so async decodes get picked up without the user needing to reopen the panel.
  • FBX dark rendering without textures: When an FBX referenced textures (e.g. texture.png) that weren't shipped alongside the model, the loader substituted a 1×1 base64 placeholder into mat.map. final = color × placeholder then multiplied every surface down toward near-black, producing a silhouette-dark render where the Mercedes GLS looked like a blob. The loader now detects its own placeholder (1×1 image size) and nulls out mat.map; a new "clay mode" kicks in when zero textures resolved from dependencies, brightening near-greyscale dark material colours to a pleasant 0.75 clay-grey while hue-preserving the brightening on coloured materials (green license plate, red brake lights) so they still read as coloured instead of washing out to clay.
  • ZIP export — main file path: The main model file is now packed under its basename (e.g. eyeball.obj) instead of its full Nextcloud path (e.g. /3D files/Eyeball/eyeball.obj). The leading slash is invalid in ZIP archives and broke extraction on some platforms.
  • ZIP export — preserved subdirectory layout: Textures discovered in subdirectories (e.g. textures/) are now packed under their original relative paths in the ZIP instead of being flattened to the root, so the unzipped folder mirrors the original Nextcloud layout and re-imports cleanly.
  • Comparison mode loading 400 error: loadComparisonModelFromPath was passing the { id, subdir } object returned by getFileIdByPath directly into URL templates, producing [object Object] and a 400 from the file API. Now destructures id before use. (Latent regression from the earlier getFileIdByPath return-shape change.)

Fixed

  • Animation playback: Previously all animation clips played simultaneously, causing blended/static poses. Now only one clip plays at a time
  • Animation timeline seek: Slider scrubber now correctly updates the model pose when dragged (replaced mixer.setTime() with action.time + mixer.update(0))
  • Animation loop toggle: Toggling Loop off and back on no longer leaves the animation stuck; finished LoopOnce actions properly restart
  • FBX rendering: Fixed dark/black eyeball — upgraded Lambert to Phong materials, added SRGBColorSpace, normalized scale, detected transparent shells
  • PLY loader: Preserved original vertex normals from file instead of always recomputing them
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewL1fCXkzaQs2nvi2AkLccAX0u8v5YD4iOypG70neHtpgIb3wIQO+XaIgyQ+8n5k5ZO8pYI33VMdCWsomImFSTZmT7bH7s5DdFotdIektcqSN+kjfyeBeJkdoAcXUU2sJZgSdlFMrIxINc35c1nagz9g+B7qabynqu0yP2FnuWkeN9E37Rb4aXqvPUPPCEFkNjilYHkC1vrW6cUR/Z+L806b1IGJ0eZs6yH/3BB83V6TZ92btN80bbLdK0IbDY/uJ7sggkhuir0WKKoyC31fKsTaqcUi9I6G7Gr2Xzm3NqS6Bk2Y/pp0rLCSQJ+4xO3LA2ktbZIMZtqgA3gEfRYqMzJ10Eyny6TCwfAwOuvozvPoKbfm+5tlhjQyGgE/C7iDOEJ2LFqC9SEH9RrNPlzKxNJ/u1zXHdmEzmxGQpAKS1qoDEAPdKOsVFdkcdKugTNIi6PpP+29HGGl7lE8OuKIiq/JY8z2+9jnfAD087bE8QxnqJBi4eL8401WSzVDRRHlbh6nS/RFb/AqCDkJE+O7G5KskqRvrb3Re7QGNXcogubljsyRMVxQT2azuv/xg6VXJGKGIMv78/OBcQW+fMfr1s6NoDweK0LTQXIVGjQbx0SRJBPsGE5arytY9eIdaoRyHcxXje81vWhp5/Ar5MMLsQf6FjBKZILulUZWPvG2VfmE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.2.0
Release Details
UpdatedApril 5, 2026, 6:44 p.m.
Changelog

Added

  • Cross-Section tool: Interactive clipping plane to slice models along X/Y/Z axes with position slider, flip direction, and DoubleSide rendering for visible interiors
  • Animation Timeline Scrubber: Seek to any point in animated models with a slider, step forward/backward frame-by-frame
  • View Bookmarks: Save and restore camera positions with display toggles (grid, axes, wireframe, background), persisted in localStorage
  • Lighting Presets: Quick-switch between 5 lighting setups (Default, Studio, Outdoor, Dramatic, Flat) affecting ambient, directional, and point lights

  • Exploded View: For multi-mesh models, animate parts outward from centroid with adjustable explosion factor slider

  • Slicer: Export format selector: Choose STL, OBJ, or PLY before sending to slicer (non-passthrough formats)
  • Slicer: Upload progress bar: Real-time progress indicator when uploading to server (XMLHttpRequest with progress events, 2-min timeout)
  • Slicer: Copy share link: Copy the temporary Nextcloud share URL to clipboard for manual use
  • Slicer: Size validation: Warns and blocks uploads exceeding 50MB before attempting server transfer
  • Slicer: Upload size display: Shows file size in MB during upload for files >5MB
  • Modal viewer stats panel: Lightweight model info overlay (meshes, vertices, faces, dimensions) accessible via bottom-right button
  • Modal viewer screenshot: Download a PNG screenshot of the current view from the modal preview
  • Cache settings in Personal Settings: Configurable max cache size, max file size, expiration days, enable/disable toggle, and clear cache button
  • Cache hits/misses in performance overlay: Shows individual hit and miss counts alongside hit rate percentage
  • Cache privacy documentation: Documented local-only storage, per-browser isolation, and user control in TECHNICAL.md
  • Multi-file matching test suite: 48 tests covering texture/MTL name matching strategies (space normalization, prefix removal, plural handling, color/body mapping, partial matching)
  • Edge case fixtures: mixed-case extensions, missing MTL, orphaned textures for multi-file loading tests
  • Export triangle count warnings: Toast notifications for large models (>500K info, >2M warning) before export starts
  • Export MIME type fix: STL exports use model/stl, OBJ exports use model/obj instead of generic types
  • Help panel refresh: Added Slicer & Export section, cross-section, exploded view, lighting presets, bookmarks, dependency cache documentation
  • i18n audit: Wrapped hardcoded export/error toast strings in t(), added 31 new keys to l10n/en.json, documented i18n checklist in TECHNICAL.md
  • Accessibility review: Added role="dialog" + aria-labelledby to SlicerModal, aria-controls on all 6 panel section headers, aria-label on emoji-only buttons, role="alert" on texture warning, role="region" on stats panel
  • Format parity guard: Build-time script (npm run format:check) validates that PHP, JS, loader registry, Viewer MIME list, and mimetypemapping.json stay synchronized
  • X3D/VRML MIME registration: Added model/x3d+xml and model/vrml to Nextcloud Viewer MIME list so these formats open in the viewer
  • Slicer security documentation: Documented full security posture — authentication, path traversal prevention, MIME validation, size limits, share expiry, and file lifecycle in TECHNICAL.md

Fixed

  • Slicer OBJ/PLY upload: Added obj and ply to backend upload allowlist — OBJ/PLY format selector was added to frontend but backend rejected these formats
  • EufyStudio URL parsing: Replaced fragile regex filename extraction with proper URL parsing
  • Slicer upload timeout: Added 2-minute timeout to XMLHttpRequest (previously no timeout — could hang forever)

Changed

  • Tools panel redesign: Reorganized from 4 sections to 6 (View, Scene, Analyze, Animation, Export, Settings) for clearer grouping
  • Toggle switches: Replaced text checkmarks with custom CSS toggle switches for Grid, Axes, Wireframe, and Loop controls
  • Export section: Screenshot, Export Model, and Send to Slicer moved from Settings to dedicated Export section
  • Animation section: Elevated from nested group to its own collapsible section (conditional, only shown for animated models)
  • Model Statistics: Moved from Settings to Analyze section alongside Measurement, Annotation, and Cross-Section

Fixed

  • Theme consistency: Unified --color-primary-element fallback values across all components to #0082c9 (Nextcloud default), replacing inconsistent #4287f5, #1976d2 fallbacks
  • MinimalTopBar hardcoded colors: Replaced 7 instances of hardcoded rgb(0 130 201) with var(--color-primary-element) and related NC variables
  • Dark theme hack removed: Deleted ~130 lines of .dark-theme CSS overrides (46 !important declarations) from SlideOutToolPanel — dark mode now works automatically via Nextcloud CSS variables
  • Cache stat colors: Replaced hardcoded Material Design hex colors with NC semantic variables (--color-success-text, --color-warning-text, --color-error-text)
  • Missing panel props: Added wireframe, background-color, performance-mode, theme-mode, has-animations, is-animation-playing props to SlideOutToolPanel binding in App.vue
  • Clipping plane Z-fighting: Added 5% margin beyond model bounds so the slider at extremes doesn't cause Z-fighting artifacts
  • Bundle budget: Updated app chunk thresholds for new features (+12KB raw)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature2a2j5kFHSYZk5lKX7ByFRqNuCv8hg06X3uc2Mr98biRZSxeIgBug6aCdHONpN8oppv+6SDc5XrviS8HcLVFw1UEZvHjePdJYR8gri52Aa7ao7IfghYUHnoIwinqvWfP0sRod5vn3ZaKuo1pMpAHQlt/fkQCVz9eH6r+6TQTTBzBeAc7yWyEumArposNVcDqhgy0kK8tNWpNGYhpdqcMEvS7zGi0bl5EeK8k3ufaNaqwlzoXNwNMBiJ1aWFV2phxOxekkxlsHfRmGLkkdbPCOHg1Kj2CTLldHOjSQmQcUH9KOL1xkOZzuAC3Mh8SnrG/47h48uKIe62T7WYHgPbeOrDHvP8Z9d8S9V7kRq3puAonSPX/TFZt7j4YZoyar86w1MVakhT1mwiK3hBax+TCzxPRo63941VwID08MonJxW4D9C59MuF5qb/7UcDZ4Fm/TrSa0yvvKi+gy+gRCmBdzCnTg+WsEZW6Lotin80zckyH/+4wkUhUQBcq97ElIQKDwG5g9Bv6SGtQ2sR3zoUi7T/Wle4iqh38p5to6VMOlKfiBDiloGwpzXtHtlXDIZw8QqPL2h6gTQNdPizlovGLaZLup3sLL6MX7uyD5ZhbWz4kg1Qj4CqORSmpAanqJbBdUhLfA8dU2YhX5q7PFm8pMDAzKOq3h45kiSFI9K39f73c=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.0.0
Release Details
UpdatedApril 2, 2026, 7:40 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.6 → ^7.29.0
  • @babel/plugin-transform-runtime: ^7.28.5 → ^7.29.0
  • @babel/preset-env: ^7.28.6 → ^7.29.0
  • @playwright/test: ^1.56.1 → ^1.58.2
  • jest-environment-jsdom: ^29.7.0 → ^30.3.0
  • vue: ^2.7.16 → ^3.5.0
  • @nextcloud/vue: ^8.33.0 → ^9.5.0
  • @nextcloud/vite-config: ^1.7.1 → ^2.5.0
  • Vue 3 migration: Migrated app from Vue 2 to Vue 3
  • main.js: new Vue() + Vue.mixin() → createApp() + globalProperties
  • settings-personal.js: Vue.extend() → createApp()
  • viewer-api.js: new Vue() / $mount() / $destroy() → createApp() / app.mount() / app.unmount()
  • ViewerWrapper.js: New Vue 2 bridge component — Nextcloud Viewer bundles Vue 2 internally, so a plain JS wrapper renders in Vue 2 and creates an isolated Vue 3 createApp() inside for the real ViewerComponent
  • Removed @vue/vue2-jest (Vue 2 specific)
  • Nextcloud 34 compatibility: min-version 31, max-version 34 (@nextcloud/vue v9.x requires NC 31+)
  • Vue component imports: Migrated deep imports (@nextcloud/vue/dist/Components/...) to barrel imports (@nextcloud/vue) for forward compatibility with @nextcloud/vue v9
  • Template modifiers: Removed deprecated .native event modifiers from Vue components (compatible with Vue 2.7+, required for Vue 3)
  • @nextcloud/vue v9 API migration: Updated all form component bindings to Vue 3 API
  • NcCheckboxRadioSwitch: :checked → :model-value, @update:checked → @update:model-value
  • NcTextField: :value → :model-value, @update:value → @update:model-value
  • NcSelect: :value → :model-value, @input → @update:model-value
  • NcSettingsSelectGroup: :value → :model-value, @update:value → @update:model-value
  • Bundle budget: Updated index chunk thresholds in bundle size checker

Fixed

  • npm audit: Resolved dependency vulnerabilities via npm audit fix (#77)
  • npm audit: Applied non-breaking security patches, reducing vulnerabilities from 43 to 25 (42% reduction)
  • Lint: Fixed one-var error in useThumbnailCapture.js
  • Animation loop toggle broken: AnimationMixer.LoopRepeat/LoopOnce are module-level constants, not static properties — setLoop(undefined) made loop toggling non-functional. Imported LoopRepeat/LoopOnce directly from 'three' (useAnimation.js, useComparison.js)
  • Model load errors invisible to user: Variable shadowing in handleLoadError — parameter error shadowed the error ref, so error.value = error was a no-op. Renamed parameter to loadError, fixed logger level from info to error (useModelLoading.js)
  • Lights leak on re-setup: Vue 3 proxy wraps items in ref([]) arrays — scene.remove(proxy) doesn't match raw Three.js objects via indexOf. Added toRaw() for light/helper removal and instanceof checks (useScene.js)
  • Toast auto-dismiss broken: ToastContainer was mutating the toasts prop directly (setting progress/paused on prop objects), which triggers Vue 3 warnings and breaks in strict mode. Moved progress and paused state to local data() (ToastContainer.vue)
  • Mobile touch listener leak: setupPinchZoom() and setupDoubleTapReset() added document event listeners but never stored references for cleanup. Stored refs in eventListeners and added removal in dispose() (useMobile.js)
  • Settings page form controls not responding: @nextcloud/vue 9.x changed all form component props from checked/value to modelValue. Updated all bindings in PersonalSettings.vue
  • CSS nesting bug: .select-group-row rule was nested inside .setting-row braces — silently dropped in browsers without CSS Nesting support. Moved to separate rule block (PersonalSettings.vue)
  • Viewer registration errors silent: Both registerViewerHandler and registerViewerHandlerLegacy had empty catch blocks — any registration failure was invisible. Added logger.error() calls (viewer-api.js)
  • Loader errors invisible: All BaseLoader logging methods (logInfo, logWarning, logError) had empty bodies. Delegated to project logger (BaseLoader.js)

Technical

  • PHP CS Fixer: Blank lines before returns, doc comment whitespace, type-cast spacing, removed unused imports
  • OpenAPI spec: Regenerated with slicer and thumbnail controller tags and updated description
  • Three.js + Vue 3 pattern: shallowRef for single Three.js objects, ref for arrays, toRaw() when passing proxied objects to Three.js APIs
  • Vue 3 Maps pattern: Maps moved to created() hook as non-reactive instance properties (this._timers) to avoid Vue 3 proxy breaking Map.has()/Map.get()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCqMDLnrHd2ejY1wtQ+IUFnXE3ObdTMqRro2/QTWhZPmyh7h5lD2zlldE7hV6K2PLVbSbQA8RyylLA7chfBAfNhqrGVcv9N8oS4N8JZlvp2sx7uCnQJx9hA34xXsRvCNednaE0NuhE4zelLAkmDaJZEcQm0r0mQKkZAMSaIPgRg52ZFiEV+KFa7dyHlyLqc/Cp6doYyEeJxnyDiLOiCCsS0zyrt1qQlRyEgGcOocdeIPJRutmBwX3Sy/018v5midNblK+gleIzcCAFIOM7qDJKuf5ESnvQmqQko/7J+ahhAGzIdpGRHmKCVjAayZ2g3uLkT9dNQrBwKAaYvNWODkSyVEoBBU1wrucKcw237GMSjxHrVf/h5G64+Nb9/krmriBkdcpVupQ94wgyU9H5NO+BsBP/+3EfuuECRwOxXMW15taFrsiflxos1Fuhu5PQtyfOv4UPtUzn+uf4MaSzeLYdBHsOHfO6ZTjTPRbO5NLz5nlMw3F4PEgSKW7bk9brH7dEjzQzVG1RrDYCg75bGW+8l7/vtAmWc87s6otqoCj9rPVHQhrY7njOUcU8gwjdw0WuFTD//i2Vk0+agy7GO9WWfUdrbQ/i8Grq5AwoEgD3b0NLpPB9XxDCOR3yZQhSDbHNvgI8BRDp3Xe1FDv8izK2Dw9HwjykDDU+uMNZNtNpdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 33

3D Viewer 3.9.0
Release Details
UpdatedOct. 28, 2026, 11:08 a.m.
Changelog

Three new tools. Compare a model with an earlier version of the same file, side by side or as a see-through ghost over the current one. Print check tells you whether a model fits your printer, where it overhangs, roughly how much filament it needs and which walls are too thin, or, for G-code, what the slicer worked out. Record turntable makes a short video of the model turning, to download or save next to it.

Added

  • Print check, in the tools panel's Analyze section: see whether a model fits your printer's build volume (pick from common printers or enter your own size), which surfaces overhang past an angle you choose, and roughly how much filament it needs. For G-code files it shows the print time and filament the slicer worked out. A wall thickness check colours walls thinner than a minimum you set.
  • Compare a model with an earlier version of the same file: pick one of the versions Nextcloud kept, and see the two side by side with what changed, or the earlier one as a see-through ghost over the current one. Supporting files such as textures are loaded as they are now.
  • Record a turntable video, from the tools panel's Export section: one full turn of the camera around the model, 4 to 15 seconds at 720p, 1080p or your window's size, as the viewer draws it, with its shading, annotations and measurements (face labels aren't included). Download it as WebM (MP4 in browsers that can't record WebM, such as older Safari), or save it next to the model in your Nextcloud when you can add files to that folder.

Fixed

  • While a dialog such as Help or Send to Slicer was open, the tool shortcuts (R, F, M, S, P) and Escape still acted on the viewer behind it: a key could reset the view or take a screenshot behind the dialog, and Escape closed the tools panel along with the dialog. Keys now belong to the open dialog, wherever the focus is; ? still closes Help.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturerZEJPDWtDI4c/UZUEa/CwOYHXSyql3DtZoZiStkj8GahJ7fwXaLzAMFgvz16luTKgh/rekkOpfdoi+M9NVK8XV696vG6PUNB//y7dPbANbyAntQyDIVGmkTMtCTu4uMZrsCa227zTJecbKdsrHhiVCsFqWs2CcLM4Ih0UfNwzknlgXpQ21AM+L6FaNWFnrhEtURXWcSgQvwz/bcm2b+mb/Qp5khws78GIU3i/Z4a6LpMuhUJxHMQ8PjfNQyzTA1vr4ehd8UmSZJ6P4IcF1/qlXA3PkDaD2fhnELwdfy2s4Y5B74sX76I97AICTOehnoEEOIoMdU7aLeAfezuoZA6BSupy0dwp5JtyTzFdcFf3I7iMWTY/HXtQ90vXIs0uV2pg0oLPSPbsnXMZ6B6nUs3KWzma7aFppkWYTSCB4RXUEXZ5e6WA3WtGYMpos3SXF6TaFS0H0QTH74DXO0p9UXzuT04JP3VaFiK+B2lnHt6mz/YOKhDZ3TmJmmfe8yZZm1ail6F8jI/kVoePQ2X4JeTxkN6cijqp0aLhExXhuX5B1xNeUNuQMhOVTglfQMPSYwVc3OVcn5yO86j0gD72MWDUP6i9+SPM0V2N5av8i/ccnPwXLYBR6ygjsOHKsHoASesGxtmSZ628PrrsR9nRQg/c4mBMUmkAPqdrpWQpKWiB/U=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.1
Release Details
UpdatedOct. 28, 2026, 2:45 a.m.
Changelog

Thumbnails made in the viewer now show the model as its file has it, whatever wireframe, exploded view, transform or animation state the viewer was in, and exports from exploded view have the parts together. The top bar stays usable while a model loads, and every control in the tools panel works from the keyboard and is announced to screen readers. Textures named by a path above the model's folder, such as ..\..\textures\wood.jpg, are found beside the model. Every route that changes data now checks Nextcloud's request token.

Fixed

  • A thumbnail made in the viewer could show how one user was viewing the model, and it is shown to everyone who can see the file. The viewer makes the thumbnail from the model it has open, half a second after it opens, and several tools change that model: with Wireframe on, the thumbnail was a wireframe; in Exploded view, its parts were flung apart and framed small; after the transform gizmo had turned or stretched the model, it was drawn that way; while a clipping plane or box was open, the back of an open surface showed; an animated model was caught at whatever frame its animation had reached; and a G-code file opened with Single colour picked was drawn in that colour, where the same file got the gradient when Files made its thumbnail. Each tool now keeps a record of what it changed, and the thumbnail is drawn from the model as the file has it, as it already was for Clay, Normals and X-ray: no wireframe (a material the file itself makes wireframe stays one), the parts together, the model as loaded, the pose the file opens in, and the G-code gradient. The viewer keeps showing what you chose. Thumbnails already stored are not remade; one goes when its file changes.
  • Exports made in Exploded view wrote the parts where the view had moved them, so an STL, OBJ, PLY, 3MF, GLB or USDZ file, and the file Send to Slicer sends, had the parts flung apart. Exports now have the parts together, in every format; a model turned or moved with the transform gizmo is still exported as you placed it, and an animated model in the pose it is in. Exporting a GLB, USDZ or 3MF no longer drops a paused animation's parts back to where the file puts them.
  • With Wireframe on, the next model you opened was drawn solid while the switch still read on, and Exploded view stayed "Active" with the next model's parts together. Both now carry on to the next model, as the Clay, Normals and X-ray shading does; Exploded view turns off for a model it cannot take apart, and the camera frames the parts as they are shown. The model statistics are those of the model with its parts together, also when it opens exploded. Wireframe also reaches the parts of a model that have more than one material, which it skipped.
  • Switching G-code from Single colour back to Gradient tinted the gradient with the single colour, and a toolpath opened while Single colour was picked turned black on the switch. The gradient now comes back as it was.
  • While a model loaded, the loading screen covered the top bar as well as the 3D view, so Help, the Tools button and the view actions could not be pressed until the model arrived, which for a large file is the whole wait. The loading screen now covers only the 3D view, and the bar stays usable, with Cancel loading still on the loading screen.
  • Models whose textures are named by a path relative to the project rather than to the model, such as ..\..\textures\wood.jpg or ../maps/wood.png, opened untextured even with the texture right beside them, and got no thumbnail in Files. 3ds Max and other exporters write such paths in FBX, 3DS, DAE, X3D, OBJ/MTL and glTF files, and the app refused any path that leads out of the model's folder. Such a texture is now looked for by its file name, as one named by a full path already is since 3.8.0: beside the model, for background thumbnails and on public shares, and in the viewer also in the model's subfolders, such as textures or maps, where the viewer looks for any texture. The folders the path names are never followed, and a texture that is not beside the model still leaves the thumbnail to the viewer. Background thumbnails also read an MTL's maps relative to the MTL, as the viewer on a public share already did, so an OBJ whose MTL sits in a subfolder now gets its textures in its thumbnail.
  • A DAE, X3D or glTF model whose texture path has its slashes percent-encoded, such as ..%2F..%2Fmaps%2Fchecker.png or C:%5Cx%5Cwood.png, had the texture fetched but drew without it, and got no thumbnail in Files: the loaders looked for a file called by the whole path. They now find it by its file name.
  • A folder holding an OBJ or MTL file made largely of blank lines could stall the Files page while background thumbnails read it. The file is now read in a time proportional to its size.
  • The tools panel's Grid, Axes and Wireframe switches and the animation Loop switch could not be reached with the keyboard: Tab skipped them, and a screen reader read out neither that they were switches nor whether they were on. They are now real switches. Tab reaches each one, Space or Enter turns it on or off, a focus ring shows where you are, and a screen reader says "Grid, switch, on". They look as before, High Contrast mode included. The same pass named controls that a screen reader read as "×" or by a symbol (removing a bookmark, resetting a colour, stepping the animation), gave the animation and cross-section sliders names, and made the cross-section plane, box and axis buttons, the transform Move, Rotate and Scale buttons and the lighting presets say which one is chosen; in High Contrast mode the chosen axis and transform button are now marked. The T shortcut that opens and closes the panel no longer fires while a modifier such as Ctrl is held. Choosing a format in the Export section's format list no longer starts the export: moving through the list with the arrow keys used to write a GLB file with the first key, in Chrome and Firefox on Windows and Linux. The row is now a format list that starts on GLB and only chooses, plus an Export button that exports in the chosen format.

Security

  • Four actions could be triggered by another website while you were signed in to Nextcloud, because their routes skipped Nextcloud's CSRF check, the request token that proves a request came from Nextcloud's own pages: sending a model to the slicer (which uploads a temporary copy of it and makes a share link for that copy), deleting that temporary copy, re-indexing your 3D files, and saving the viewer's settings. Your browser's SameSite cookies, and the preflight a browser makes before it sends a PUT or DELETE from another site, stopped most of this, but they do not cover every case, such as a request from another page on the same site, so these routes now require the token like the rest of Nextcloud. The viewer sends it itself, so nothing changes in use; anything outside the viewer that called these routes with a session cookie and no token now gets a 412 and has to send the requesttoken header. Saving the viewer's settings also accepted any setting name and value; it now accepts only the two the viewer stores, the file-browser sort and the selected file, and refuses the whole request if either is unknown or invalid.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureIm8PqU0r/Nr8aRKa1jh6a+lsP8E3b6u1GCxKzIqtb1C4vUZNgKeSdD/it/SABY5O0GBwso8NdFBD3XHV5jgg/yriggt78tRHo7pvVEo/J4CyRhKEWNRChjWfoOMuxRZF5sBJCOacuCQoMyofcHBCSIPjDils6gZ2CW5H3xZ0atGx5hgjcmQKS7TGxaq/3tymVcGmgl17DNbiSULcfFeIL9b0uBQs0HpLzIBGskp0dffLean9atDu5WAzm8CB9ASVCl+m2DyWkE0mcgQpFKDijWCohaNJXKA+P37KSnBOZ1zuQbDV+8B49hoKQr4hxUgfKhM/tVrbZ/F0Fo2FerTrLpi+H1eWYFvPFXicooi51yLVi2vn+OA8cwBcIrvViUCNiYiI0luZOzNiYRGk17WrZWow7suGL810YJk5eGzkEIj5HQ7dsSoU9Alaz6ubEFRc/Fqw4A0wOjXJIcFm3Gw2uFkjRGA7hUIY0Bhbnwsahi1K2DQMv1nr/pUZKwYK1mTsxJjE2+UwPV6ayLxk2+aMSDPxtNYG/fJrEoCeNUaDTljxsT3P8/EXko8/6e6teSUHTKDBFNBM52HSt7Mzl82R/9bBvXHLCmJk8fg1Td/+QebTbtqW4u1RUio946WlErrk75Ku/WmvhDH4RKeyGWdvJz1j2BKYSzxvk2YHAlEMrBA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.0
Release Details
UpdatedOct. 27, 2026, 4:59 p.m.
Changelog

New inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ. Measurements, statistics and exports now use each model's real unit, so a glTF figure measures in metres and an STL part in millimetres. FBX readings change: they follow the file's own unit, centimetres by default, so most FBX models read 10× larger than in 3.7.x, which is their correct size. Textures a model names by a full path from the machine that exported it, such as C:\…\wood.jpg, are found beside the model, and a texture that is not one of the model's files is never requested.

Four inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ.

Added

  • Angle measurement. The measurement panel has a Distance / Angle switch. In Angle mode, three clicks measure the angle at the second one, in degrees, and changing the length unit leaves it alone. A second click on the point just picked, usually a double click, is not taken, since it would leave the angle without a side. Angles are shared like distances: everyone who can open the model sees them, with their author, and anyone who can edit it can delete them. They are stored as a note type of their own, so a viewer older than this one skips them instead of drawing them as a distance. The annotations JSON export includes angles, and import restores them, whichever mode the panel is in.
  • A layer slider for G-code, in the Analyze section of the tools panel, for files with more than one layer. It shows the print up to the chosen layer, with that layer's height, and "Only this layer" shows that layer alone. A Z-hop counts as part of its layer rather than as a layer of its own. Each step changes only the layers it passes, so dragging across a vase-mode print with tens of thousands of layers stays smooth. The slider and the switch work from the keyboard.
  • Clay, Normals and X-ray shading, in the Scene section of the tools panel. Clay shows the shape of the surface in each part's colour, Normals shows which way each face points, which makes dents and flipped faces stand out, and X-ray makes the parts see-through to show the inside of an assembly. The model's own materials are not changed: exports use them, Send to Slicer included, and so do thumbnails, so a model opened while X-ray is on still gets a normal thumbnail. The shading stays on when you open the next model.
  • Export to 3MF, PLY and USDZ. 3MF, which slicers open, is written with Z up, and its parts are closed solids, so a slicer does not have to repair them. PLY is binary and keeps vertex colours. USDZ opens in AR Quick Look on iPhone and iPad. Both 3MF and USDZ come out at the model's real size: glTF, IFC, COLLADA, X3D, VRML and dotbim models are taken to be in metres, Rhino, 3MF and FBX models in the unit their file says, and STL, OBJ, CAD and the other formats in millimetres. A model with no surfaces, such as a G-code toolpath, gets a message instead of an empty file. The exporters themselves load only when an export starts.

Fixed

  • Measurements and model statistics took every model to be in millimetres, so a 1.8 m glTF figure measured "1.800 mm", and its surface area and volume were a million and a billion times too small. They now use the model's own unit, the same one the 3MF and USDZ exports use: a distance, the bounding box and Copy Measurements read in metres for a glTF, IFC or COLLADA model, and areas and volumes follow as square and cubic units. STL, OBJ, STEP and the other millimetre formats read exactly as before, and angles are unchanged. Both unit pickers now start in the model's own unit (metres for a glTF, millimetres for an STL) until you pick one. Your pick is kept as you open other models in the 3D Viewer app, and is not saved: reloading the page starts again from the model's own unit. "Generic Units" shows the numbers in the model's file, unconverted. The comparison table converts each model from its own unit, so a glTF and an STL of the same part compare as the same size. Shared measurements are stored as points on the model, so they need no change and show the right reading.
  • 3MF files written in a unit other than millimetres opened at the wrong size: three.js reads a 3MF file's unit and never applies it, so a file in metres was a thousandth of its size, measured wrong and exported wrong. The unit (micron, millimeter, centimeter, inch, foot or meter) is now read from the file, so measurements are right and a metre 3MF exports and opens again at its real size. The model is shown as before. Only the start of the file's model part is read for it, and in linear time, so a crafted file cannot stall the page.
  • FBX readings now follow the file's own unit, UnitScaleFactor, which is centimetres when a file does not say. In 3.7.x every FBX was read as millimetres, so every FBX model, small ones included, now reads 10× larger than it did (more for a file in metres or inches): that is its correct size. FBX models larger than 50 units also measured and exported at a fraction of their size, because the viewer shrinks such a model to fit its lighting; that is now undone for measurements, statistics and export. FBX models are shown exactly as before.
  • OBJ models larger than 1000 units, a metre for an OBJ in millimetres, measured, reported and exported at 1000 mm whatever their size: the viewer shrinks such a model to fit, and the shrinking was not undone. A 5 m OBJ now reads 5000 mm. Such models read differently from 3.7.x, and correctly; they are shown as before.
  • G-code toolpaths drew the travel moves the parser leaves out. Each layer was drawn as a single connected line through all its moves, so a line joined the end of each move to the start of the next. Layers are now drawn as separate moves.
  • Closing the measurement panel with one point picked left that point's marker in the scene. Markers were matched to measurements by their position in a list of pairs, so every later pair was off by one, and deleting a measurement could remove another one's markers. Each measurement now keeps track of what it drew, and an unused point's marker goes when the panel closes or the mode changes.
  • Send to Slicer's PLY option always sent ASCII PLY: the binary option was passed where the exporter expects its callback. It now sends binary PLY.
  • In Windows High Contrast mode, the tools panel's switches (Grid, Axes, Wireframe and the others) showed no difference between on and off, because their track was filled with a colour that the system palette replaces. They now have an outline, and a switch that is on is filled with the highlight colour.
  • Models whose textures are named by a full path from the machine that exported them, such as C:\Users\anna\textures\wood.jpg, file:///C:/textures/wood.jpg, \\server\share\wood.jpg or /home/anna/wood.jpg, opened untextured even with the texture right beside them, and got no thumbnail in Files. SketchUp, 3ds Max and some Blender exports write such paths in DAE, FBX, 3DS, OBJ/MTL and X3D files, and the app took them for files that are not beside the model. The texture is now looked for by its file name in the model's own folder, in the viewer, for background thumbnails and on public shares. This also covers glTF, an FBX that names its texture only in a video's Filename, and a material map written after options such as -bm 0.3. The folders the path names are never searched, and a texture that is not beside the model still leaves the thumbnail to the viewer.

Security

  • Opening a DAE, 3DS, glTF or VRML model in the viewer requested any texture the model named that was not one of its own files, at the address the model gave. A texture written as https://…, or as a Windows network path such as \\server\share\x.png, which the browser turns into http://server/…, made your browser contact a server the model's author chose, and only Nextcloud's Content Security Policy stood in the way. Such a texture is now never requested: the model is drawn without it. Thumbnails made in the background already refused these.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCmm5ayL+RHBF5+iiN7ZD5hk7WWM1pgz+KPfzKS6H+3ZnGg8ZyORrq7C6H+eaNxd54PoTbGonDOSIomtizZ/I/udluAu1lRW0ZA6ZS4olC6t+iylmvWU2lc5CaCLqBSXHt7+uE5RSSHbXCZl1MdYWlyYQkJo+SVPCbupSBQu/EenOXfNOMsBJhjdtyGmNzTOhqil+IGmPGRf+YiPa9cW9X0ll+sreqKvbICTxtrfw0OhEbA801v1R2ecXns2RL1iqiWxn/gGcAM4ibCEd+RdsrdUgs3QKSBBgtsZEzJNYKDeH15WoPGHnThDY13aVv7Qv446rHYcn8V3ZIk/vR4Znu0EVLPoRHW6ltJppSi0aeNE62WWCd5KWNfY0Q0K5oykwbtl2szFx+AEYgvbRdRFyOylExNZUcoigYFmlkrQLvPTKtvivYVUi9NIQIbCgSHHygjZEdWy1otFuRs9+W7pqtqH++SJ0cXwZp/ruIikfAJ3/JnQdAuNlWelNErjj2DJVa7cScS8zdiYINzXoToPnDYG9vrNTftnYNwTEvLOQBaHFVDBEC/XdwnPk0ZqEXseJMid632xpWxLdQny2nuVnzjXkG3Nw5klYZ1wWbKOzwUuRHKWatOCJxRiQIXlIs07SgNqV2HlcelYUJ9GG5pX8R21zQBbEKqAsRrT/0WsEtBw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.1
Release Details
UpdatedOct. 27, 2026, 1:58 a.m.
Changelog

3MF, AMF, DAE and X3D files now get thumbnails while folders are browsed, like every other model. FreeCAD files show only the part FreeCAD itself shows, instead of every step of its history on top of each other.

Added

  • 3MF, AMF, DAE (COLLADA) and X3D files get a thumbnail while you browse Files, like every other model, instead of only once opened in the viewer. Their loaders read XML with the browser's DOMParser, which the background worker does not have; the worker now loads its own XML parser (@xmldom/xmldom) for these formats only, and the viewer keeps the browser's. The parser fetches nothing a file names, such as an external DTD or entity, and expands no entity a file declares, so a crafted file can neither make a request nor multiply itself. Parsing still takes memory in proportion to the file, about 31 bytes per character for a 3MF mesh, so XML longer than 16 Mi characters (roughly a 250,000-triangle 3MF, measured unzipped) is not parsed in the background: the file is skipped for its version, like one that runs out of time, and gets its thumbnail when opened in the viewer. As with OBJ, glTF and FBX, a DAE or X3D texture the file names is downloaded with it, and a file whose named texture cannot be found is left for the viewer rather than given an untextured thumbnail; a 3MF carries its textures inside it. Only .nc and .g files, which may not be G-code at all, are still left to the viewer.

Fixed

  • FreeCAD files (.fcstd) showed every step of a part's history on top of each other, in the viewer and in their thumbnail: a PartDesign part with a Pad and a Pocket drew the Pad's uncut solid around the finished part, and the tool the Pocket cut away sticking out of it. FreeCAD saves a shape for every feature and shows only some of them; the loader drew them all. It now draws only the objects GuiDocument.xml marks visible, and nothing inside a hidden Body or App::Part, which FreeCAD hides as a whole while their members stay marked visible (the tips of two Bodies combined by a Part Cut, for one). A file saved without it (by FreeCADCmd), or one where nothing visible can be drawn, is drawn whole as before. In FreeCAD's own PartDesign example this is 1 solid instead of 18; in its Part workbench engine block, 10 shapes instead of 35.

Security

  • Finding the texture and material files a COLLADA, X3D, OBJ or MTL upload names took time that grew with the square of the file's size, so a crafted upload of a few megabytes could keep the server busy for minutes. These files are now read in a single pass, on the server and in the browser.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5P5xxH9yvWf8Au302k0c7SEVJMaiS+KTOElYVIGw4KinRBHTSr3Lc/tmNF0aTDJaecbjcKuz5zrzimSYyQhtuWYIjyBqDew8qfJRoek/6+rycT2u7zvYyzyjmuWGHA+Wns13LLoCk6GsRxeya03ZyWvdwfyJ5eCGmJfAFN+ukZl3VTUmeADIqaQbKiYnHzj3lYQljacH3x0ebkFQ8Q1TPbCcRrUTjEVOAheWM3g7k5knbYstBo0EGUCr84TtsNvyenqCnr0uH8g10NPBt+mIaQRIWlK2xua6RoPtCGY33JmoGVzOGppM8rhyaVQ40wCdY2Sp1Mm+itA1ViyqjF/OcumS4LL5bAE+J0Y5J+4zzCELNWPQIOXGaA6X+YwWXEEN8Q+TB5UlqNOqyUliRVa3JsXcnmfUpwBRQPGEYe5UN48bXEUNRzodZ9J97SclDVApc8wayuf7kZNe3q8HOkjj8euEUXw1QZeSSfPv1cFWm0iy72SYqlrBfylMs4+L32u0B3nloFACcrgVgi0+erNSNGyzKJS2AwwFLXo1MEwTT6IGAx2Lmquq5sT427HDavOlwRpsAeGQ5IwZF+2rxJyhAYl+PkLHCeHH7UwyjHteTxbIYmsUSwzSjPd81sVuI/O/UUfFVhAQbH2etMRBSdQj19Ub0+frTScX32NKmmvYjG8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.0
Release Details
UpdatedOct. 26, 2026, 11:33 a.m.
Changelog

Models get thumbnails in Files without anyone opening them: the browser of whoever is viewing a folder renders them in the background, within limits each user sets. A thumbnail is kept once per file version and shown to everyone who can see the file, including share recipients and public links. VRML files, FreeCAD files saved by FreeCAD and FBX files with embedded textures open correctly again. Existing thumbnails turn back into icons after the upgrade and are made again as folders are browsed.

Added

  • Thumbnails are made while you browse Files. Models in the folder you are viewing get a thumbnail without being opened: one file at a time, while the browser is idle and the tab is visible, and not while Save-Data is on or a model is open in the viewer. Folders without models load none of this code. Each model is rendered in its own Web Worker, which is stopped the moment it runs out of time, so a large CAD file never freezes the page. Personal Settings → 3D Viewer → Thumbnails has the new options: "Create thumbnails while browsing Files" (on by default, and separate from making thumbnails in the viewer), "Skip files larger than" (50 MB), "Give up on a file after" (30 seconds) and "Download at most, per folder visit" (500 MB). A file that runs out of time or cannot be read is skipped by this browser until the file changes or you press "Retry skipped files"; network and server errors never skip a file, and a browser that cannot render in a worker downloads nothing at all. Only the companion files a model names (materials, textures, glTF buffers) are downloaded, and only while they fit in the folder budget, so a model whose textures are not named gets an untextured thumbnail. A model is never rendered without a companion it names, since a thumbnail is kept for the file's version: one that does not fit in what is left of the budget waits for your next visit to the folder, and one that cannot be found beside the model (or at the path it names) is left for the viewer, which looks further, to make the first thumbnail. In Favorites, Shares and Tags, thumbnails are made once you open a folder, not on their top-level lists. Only files you can edit get a thumbnail. 3MF, AMF, DAE and X3D files, whose loaders cannot run in a worker, and .nc and .g files, which may not be G-code at all, still get theirs when opened in the viewer.

Changed

  • One thumbnail per file version, shared by everyone who can see the file. Thumbnails were stored per user, keyed by file id alone, so each user made their own and an edited file kept its old picture. They are now stored once per version in the app data (thumbnails-v2/<fileId>/), dropped when the file is edited or deleted, and the old per-user thumbnails are removed on upgrade. Models that showed a thumbnail before the upgrade show their file icon again until a new thumbnail is made, by browsing their folder or opening them.
  • The viewer captures a model's thumbnail with the same fixed look as every other thumbnail (transparent background, default lighting and camera framing) instead of a crop of the live canvas, and only once per file version: it asks the server first and skips files you cannot edit, files that already have a thumbnail for their current version, public shares, and opens that took a material or texture from the browser's dependency cache, which may be older than the file. It now covers every format the viewer opens, not a fixed list of 12.
  • The CAD runtime chunk is 63 KB instead of 776 KB. The OCCT glue's require("crypto"), reached only when it runs under Node, pulled the whole browser polyfill for Node's crypto into the bundle: crypto-browserify, bn.js, elliptic, asn1.js and readable-stream. crypto is no longer polyfilled, which also removes the vm-browserify eval warning from the build.
  • The GLB, STL and OBJ exporters load when an export starts instead of with the viewer, taking 40 KB off the main chunk. The slicer's dynamic imports of the same exporters had no effect while the export composable imported them statically.
  • webdav 5.10.0 → 5.11.0 and fast-xml-parser 5.10.1 → 5.11.2, which drop a duplicate key the build warned about.
  • "Retry skipped files" replaces "Clear thumbnails" in the thumbnail settings. Thumbnails are now shared, so clearing them would have removed other people's too; a thumbnail goes away when its file changes instead.
  • roave/security-advisories is no longer a dev dependency. Its latest release declares a conflict with every PHP below 8.2.35, PHP 8.1 included, so Composer could not resolve the dev tree for the app's minimum PHP and every PHP job in CI failed at install. Advisories are still checked by composer audit in the security scan.

Fixed

  • IFC, FCSTD and G-code files never showed a thumbnail in Files. The preview provider registered for model/* MIME types only, and these three have application/x-step, application/x-fcstd and text/x-gcode. It now registers for the MIME type of every format that gets a thumbnail.
  • Thumbnails never showed to share recipients or on public links. They were looked up for the logged-in user, so a recipient saw their own (usually none) and a public link, with no user, saw none at all. Anyone who can see a file now sees its thumbnail.
  • FreeCAD files (.fcstd) saved by FreeCAD failed to open with "FCSTD archive contains no .brep geometry entries". FreeCAD stores each body as a .brp file inside the document, and only .brep was read; both are now.
  • VRML files (.wrl, .vrml) spun for 30 seconds and then failed to open in the viewer. The loader waited for a callback three.js's VRML loader never calls; it now uses the scene the parser returns, so they open.
  • FBX files with embedded textures opened untextured. The images the file carries were looked up by name among the companion files, matched nothing, and got a blank placeholder; they are now used as they are.

Security

  • A model rendered in the background loads only the files it was given. A texture or buffer it named but that was not among them was requested from the URL the model wrote, so a model in a shared folder could make the browser of anyone browsing that folder request an address of its author's choosing. The worker now refuses every URL except the model's own files and the app's decoders, before any request is made, and skips the model.
  • Storing a thumbnail requires the request token (CSRF protection) and edit rights on the file. The upload route opted out of CSRF checks, and anyone who could read a file could set its thumbnail; read-only share recipients now get 403. The browser also sends the version it rendered, which it learns before it starts downloading the model, and an upload for a version that is no longer current is refused, so a thumbnail can never be filed under a newer version of the file.
  • Dependencies updated within their existing ranges to clear npm audit: follow-redirects 1.16.0 → 1.16.2 (credential headers kept across cross-origin redirects), reached through axios's Node adapter, which the browser bundle does not use, and vite 7.3.6 → 7.3.7 (dev-server advisories). Dev-only fast-uri, brace-expansion, js-yaml, moment, pbkdf2 and qs move to patched releases as well.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureHW+gyQT9pWQ7mEMg5wkO6BOlN33tSE+lpY33kDjIvJ0XIvGh3JY9P7KW64TxfSUb5xG+sArQE8CsPqTDbwGjKPy+sMK+iPQrKdbIMKA/xmrW6ISonhqxt1IN9siV0EiPhGVlLsigL76kuWbzAzaxiHiISofv79xZt2hR0YwD/vHqn3IRNQXREf7gtSnNDGXF5HmQVELaR43ccFlJK4husCsCGSxnQ0Fdbt6I3OZd2+bp1l53cG2EVOTbNFn5Azc4krpiy1jP8oNO6PVKveZPHObTBiXqYMNm6seHkEuATm0vw+aq5F9u/HCVitQyzXo+0oANQFoYW1+FqtWT1ueSgTaz4++lsn5Z5b7aDtubDWISj5AJL/kERXdfPvux6klusP4UPb1YP1yLkO/tWBQWn3is4SsSvC9f3UGBTKch/IEAcHjnL2c5PSqml491KCHF3/4jqQUu8ozKuG6n/09yImtQDmBvuyGlEuBCZT4rANZ8Pr9Q29zbDkFqQCNR/qe+tveEQzKDKO0ECaDp4bjxydNvnff3+m6ceWp5/KpHXLUfs228XoJqBVuyY5jI3+i/ltEnXYp8l3coQFBVKmKiLvuE8aGbP92EmTZXc1sv2Q8RHBSMt3Mxd/78cMIF4pMYjGsKOIHWl4R9hvctcntXkiB4BD2ALVTCZ2ypvBihqjE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.6.0
Release Details
UpdatedOct. 19, 2026, 10:51 a.m.
Changelog

Annotations and measurements become shared notes that everyone who can open a model sees, and the app supports Nextcloud 35. CAD, BIM and 3DM files open again on App Store installs, Rhino 3DM files open at all, and the Favorites view lists starred models.

Added

  • Annotations and measurements are shared with everyone who can open the model. They were saved privately per user, and measurements were not saved at all. Now each model has one set of notes: share recipients and public-link visitors see it, and anyone who can edit the model can add, change or delete notes. Each note saves on its own, so two people editing at once never overwrite each other. Each note shows its author, except on public links. Existing private annotations move into the shared set the first time their author opens the model, if that user can edit it; otherwise they stay visible to that user only, marked "Private, not shared". Notes follow a model through moves and renames within the same storage; moving it to another storage, such as a group folder or external storage, gives it a new file id, and its notes are removed by the daily cleanup.
  • JSON export includes measurements, and import restores them.
  • Nextcloud 35 support. The app declared max-version="34", so the updater listed 3D Viewer under "Apps missing compatible version" and an upgrade to 35 would have disabled it. Checked on 35.0.2 by upgrading a 34 instance in place: the app stays enabled, and models open from the Files app, the standalone viewer, the file picker and public shares, with nothing logged. Nextcloud 35 types the schema and file APIs more strictly, which surfaced three changes: the folder_path migration calls modifyColumn(), the only form 35's ITable declares (changeColumn() was a deprecated alias of it in every supported server); the slicer download endpoint no longer treats a folder ID as a file, which deleted any folder older than a day inside its temp directory; and the temp-file cleanup job skips a non-folder at the temp path instead of failing on it.

Changed

  • three.js r185 → r186. The Babel config moves from package.json to a project-wide babel.config.json: r186's CommonJS entry only re-exports the ES module build, and a package.json config never reaches files inside node_modules, so Jest could no longer load three.
  • Babel 7 → 8 for the Jest transform. @babel/plugin-proposal-object-rest-spread, @babel/plugin-syntax-dynamic-import and @babel/plugin-transform-runtime are dropped: no config referenced them, and the first had no Babel 8 release. Babel 8 needs Node 22.18 or later, so engines.node and .nvmrc move to 22.18.

Removed

  • The /api/annotations/{fileId} endpoints, replaced by /api/notes/{fileId}.

Fixed

  • STEP, IGES, BREP, FCSTD, IFC and 3DM files never opened when the app was installed from the App Store, failing with both async and sync fetching of the wasm failed. The loaders fetched their WebAssembly runtimes from /apps/threedviewer/…, which exists only when the app sits in the shipped apps root; App Store installs land in the writable root, custom_apps/ on the official image. The OCCT, web-ifc, rhino3dm, DRACO and Basis paths now resolve against the app's actual web root, which also covers instances served from a sub-directory (#188).
  • The file browser's Favorites view was always empty. The controller read favorites through OCP\Files\Tags\ISystemTagManager, which does not exist, so Nextcloud injected null; it also looked for system tag 10, while favorites are per-user tags. It now reads the user's starred files from ITagManager, and the browser renders the flat Favorites list, which no view state reached before (#205).
  • Rhino 3DM files never opened, for three reasons. The published app shipped rhino3dm.js but no rhino3dm.wasm: the wasm is git-ignored and its old source in three.js's examples/ no longer exists, and the copy step skipped missing files silently. The runtime now comes from the rhino3dm package (8.32.1, the version three's loader names), and the build fails if either file is missing. Its emscripten glue compiled code with new Function, which Nextcloud's CSP blocks; it is rewritten as it is copied, the same repair #169 made for the CAD runtime. And a worker whose runtime failed to start never answered, so the viewer spun with no error: the decode now has a deadline, worker errors are reported by their message, and each load terminates its worker (#204).

Security

  • Production dependencies updated within their existing ranges to clear npm audit: vue 3.5.41 → 3.5.44 (@vue/server-renderer XSS), axios 1.19.0 → 1.20.0 (prototype-pollution gadgets), brace-expansion 5.0.9 → 5.0.12 (ReDoS), source-map-js 1.2.1 → 1.2.2 (event-loop DoS) and DOMPurify 3.4.15 → 3.4.17 (IN_PLACE sanitizer bypasses).
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewgF9SWWbzLH22o0y9lzTkAUX33vlRdY37uuPVanE2nMuer3V8gTzxkFnfGn2m6iXOWfHvlTAM1xMXbMIiFOCvTTw5rA2GMq+T+QVh8kAyeDAOtQVU7Rr0J0+x2PK0HNkmwlJstOc7xGiYEiVLXEu6mEKbb99Q/o7xlkUsgvmAuK6Xxu1EZvWJi30bx8pJwJC3jbRMaZX5CRWYpoPkeoOCqglgXTm/j8wCqNP8W4ofmWZlOdqM5VQoApIv3Iv9Bvtp7lzau+r+saUNcgShEHuMhiODLeWDdznd5XUrQNLp7yLiYM2tk/brB04F9qIL0nr1BJtA1qhOe2nlIHtmvyp1Yy8bEryIPLKgWHubSyMYV8qUt5pJP3b5ZOmlDfNYBpru1mNJVNFOPlEyWw/74lRAWRadFY3dOASyyUfzcr0rMraMcvKg3Kg881XEAPiizNfTCHi5trQR4+gfbd7e42gT2Z5KGuZLGllAntMzP9B/ZwMl5VuFZLaWJNosjkOdry5TRH8ZOn1hyEj+vP4fdi+a81Wcq2yRjuER+LG6SL3LKkVIjXLCnat1N0nEzxOfPEi3VPNDy6jNJhoX8aaDAQJklXFc/TcD7+6MwLXR/ZXLlxRZ7dvpu/tO3WQF75kxCo94Z6fILOS5EhjDf0JZb6iFscELiwK9R7A8XaEiqHKFY8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.5.1
Release Details
UpdatedSept. 21, 2026, 6:09 a.m.
Changelog

CAD models open again on Nextcloud 34: STEP, IGES, BREP and FCSTD no longer need 'unsafe-eval', which the server stopped letting apps request.

Changed

  • @nextcloud/vue 9.9.0 → 9.13.0 and @nextcloud/dialogs 7.4.1 → 7.5.0.

Fixed

  • STEP, IGES, BREP and FCSTD files failed to open with EvalError: call to Function() blocked by CSP, and no page let the viewer compile WebAssembly at all. The OCCT runtime is rewritten at build time to stop compiling its bindings with new Function, and the app now adds 'wasm-unsafe-eval' — never 'unsafe-eval' — through Nextcloud's additive CSP event, so CAD and IFC models open from the Files app without an administrator editing the policy (#169).
  • The release workflow reported success when nothing was published. The app store step was continue-on-error, so a failed publish went green at the step, the job and the run — v3.5.0 was tagged, built and released with nothing on the store and nothing anywhere to say so. That flag is gone, and a step after it asks the store whether it is serving the release, checking the platform range and download URL as well as the version.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureRJMTtOs8itpPTgMandBOx/fB3Lb4eQhNqh82AL50L6dsquHrSy1TrIOYVtn0rZV50Zwy23N2wGiC8yP450hkEYtMG4Hqmb3RRijNvGdioBpHryA7XyF2II7sx13g9ebXTkk6Aq4qq82sTVe8LtqElZfjD7mshN8eRrrQDMCh7xgly6dBOkyagZqowByvVaiL093OVqO3qUCCGfrWslIB17NtM+8BreXIrsLn/fiQe67uBpumOD6hzEYaWwxqYNrLoejUfZWsShZnvUa5mUG5v1mquxXDUGUswb757OYsDq1Yg3KEU3mv4yYZHmigJfjnHZ+n17j9sWt0g6xXI8YXAY5t2cxx91ll9WQGeVddYZIJjseJodhAV5fCe1V87Ks+pdVYPdXhhdDMQGbFBacnFM+BRRuN7yP3QIpBM1re1qKRcIBDTl3oSUqnCQJ9vLG6im+NxvUYQ5y2VfETyRlQdJPo1j+WToO4dhpI3KHSYpU42HW+T1YvPlmpsosrxanIEnqNzl8uqvjzQibww0A/uIHMbKLsp5nGblQRhlbeLzeZvQs5C28K9Ux9PvNpCEIdXsyYwhB7PHXuu/SYzcsBWR1Zu3i+/cV0SywOER8qSltd+KQuvBXuAveGUqZhRe0zOJStOs90w2rcdHe32BIAJ2GSOdYA+uDnkdY6k26qTAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.5.0
Release Details
UpdatedAug. 11, 2026, 3:17 a.m.
Changelog

The viewer is redrawn on a themeable design system: the Theme control now moves the whole viewer rather than only the scene, every control is a Material Design icon instead of an emoji, and the top bar, tools panel, slicer dialog, performance HUD and the measurement and annotation surfaces follow the design sheet. Also carries three.js r185, the move to ESLint flat config, and the security fixes listed below.

Added

  • Keyboard shortcuts for six tool rows: R reset view, F fit to view, M measurement, S statistics, P screenshot, ? help.
  • The Playwright suite now runs in CI.
  • A browser check that the design system's tokens resolve to real colours.

Changed

  • Three.js r184 → r185.
  • The live loader check covers OBJ, glTF, Draco-compressed glTF, PLY and STL instead of two app-owned parsers.
  • "Open in 3D Viewer" is the mockup's outlined pill, with an open-in-new icon and a tooltip saying it opens a new tab.
  • The tools panel's sections are rows separated by hairlines rather than nested cards.
  • The dark palette comes from the design sheet, with separate tokens for the accent as a fill and as text.
  • The viewer's Theme control themes the whole viewer rather than only the scene background and grid.
  • The slicer dialog follows the design sheet: 560px, one segmented format control, a lighter backdrop.
  • The performance HUD and the model statistics panel follow the sheet, both drawn as canvas chrome.
  • Every control is drawn with a Material Design icon instead of an emoji — 154 across nine components.
  • The tools panel is redesigned as a floating card with collapsible sections.
  • The viewer's top bar is redesigned: a 56px header, the filename centred with its format, an FPS pill, and Tools as the one primary button.
  • ESLint moved to flat config, for @nextcloud/eslint-config v9. New rule families are off and newly-promoted rules are warnings, each carrying its count in the config.

Fixed

  • The antialiasing setting did nothing — the renderer hardcoded it on. It now follows the setting, applied at mount.
  • The help dialog dimmed the viewer and left the rest of the page lit. The four fixed overlays are teleported to the body, past the containing block Nextcloud's backdrop-filter creates.
  • The help panel drew different icons from the controls it describes; eight rows disagreed.
  • The controller rail's full-width controls sat seventeen pixels left of centre.
  • Every NcButton rendered as secondary whatever it declared: @nextcloud/vue 9 moved the appearance from type to variant.
  • Four controls were still drawn with an arrow character instead of an icon.
  • Nextcloud's navigation toggle looked disabled on the viewer's top bar.
  • The browser fixture rendered the whole viewer into a zero-width column, so every geometry it measured was of that.
  • A tools panel section header turned pale blue with grey writing on it under the pointer.
  • The slicer's format selector rendered as three chips floating in a track.
  • The 3D controller was a white ring on a white scene.
  • The Send to Slicer dialog carried a hand-written dark theme that fought the tokens.
  • The per-slicer Open buttons were drawn in each slicer's brand colour, down to 1.78:1.
  • The browser legibility sweep mis-read every color-mix() surface.
  • A theme chosen in the viewer was forgotten on the next load.
  • The viewer's header kept its white text into the light theme.
  • The measurement and annotation panels' close buttons did nothing, and no tool row ever showed it was active.
  • An annotation's note could not be edited.
  • Label text rendered stretched.
  • A measured point landed on the transform gizmo's picker plane rather than on the model.
  • The measurement reading was 1% of the model's height.
  • The markers drawn into the scene were still the debug palette.
  • Every label was a wide black bar with a few pixels of text in it.
  • Taking a measurement filled the viewport with flat green.
  • The measurements and annotations panels had never been redesigned.
  • Switching measurement mode on produced no visible change — the panel opened underneath the tools panel.
  • The measurements panel only appeared once there was something to list.
  • The tools panel never announced the state it restored on load.
  • The export format control and the slicer's format control had labels that labelled nothing.
  • The viewer drew a light card and a dark card on the same rendered model.
  • The performance HUD's mode chip was a pale blue pill with near-black text on a near-black panel.
  • Half the model statistics panel was white text on a white panel.
  • The design system had the status colours backwards: the unsuffixed token is now always the foreground.
  • Every button in the viewer lost its colours the moment it was used.
  • The ground grid was fluorescent green on every theme, and the setting meant to control it did nothing.
  • The viewer's buttons were the wrong shape on every real Nextcloud page.
  • The top bar crashed before a model was chosen.
  • A "Model loaded" toast covered the Tools button on every page load.
  • Ten style declarations had stopped applying, from an rgb() bracket a token sweep left behind.
  • The watertightness badges were pale green and pale amber on white, at 1.9:1.
  • The bundle size check picked which file to measure by the shape of its content hash.
  • obj-mtl.spec.ts had been failing since before 3.4.0 with nobody to see it: its Nextcloud stub was missing OC.filePath.
  • --color-main-text was written with five different fallbacks, --color-border with eight. All 224 references now go through the tokens.
  • Dark mode asked the light theme what colour to be.
  • A help icon's background tint never rendered — it mixed from a variable Nextcloud does not publish.
  • The dark theme never reached the toolbar, the viewer or the toasts: ten rules keyed off a class the app never sets.
  • The slicer dialog followed the theme only when opened from the standalone app.
  • The viewer asked Nextcloud for the wrong primary colour in twenty-five places.
  • --color-primary carried three different fallbacks, so an unthemed instance rendered whichever rule an element landed on.

Security

  • Cleared three advisories from the production dependency graph, lockfile only: dompurify 3.4.13 (GHSA-55q2-fjhq-7xh7), nanoid 3.3.18 (GHSA-2v37-7h3g-55p8) and brace-expansion 5.0.9 under webdav (GHSA-rgw5-rvv9-x895). npm audit --omit=dev goes from one moderate and two high to zero. The residual 3.4.0 recorded is unchanged: webdav ships a prebuilt bundle with brace-expansion inlined, which no lockfile change reaches.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature33Nuq3jk4L8Tk0nUQ/SUKRB9gkf/O+h254EVTLZroD9RvLW9vF6sZqc5zxHEqjR1n15D6JGSbNsdiHTIg3XruaBzMEQ9IplE8gzY6tkclHRN1n1plXbYwrFrZs/nfmUcEJQu2XKVoeCFrN+hPwVIXng/m5p+xGzcHhOnev/o2GNRTFHbSET9Sy7HHIGmieaOf2g6Nrbhto/X7QAnjk2i8Ht/OVLggubgPe85SYsG8qvmi03S0xY8NzFl7XQ6lBlY1DqZSt7Rn/PY8OQsYAMIRA+Ihwu6JU4hWiw1wyV0EFnWayypnS2l4hTk2GNG06Y8bbe1qt05r2LSQSqPI3HOd87nKK/ETmwJdHkpNDJi9NNV0iYuAp1fOsfH7d0VHkbuxWTXZ223YWtlTA+0XKNfZ7Jw5mq0zlyIjpyNDPjScwXv3g+/M835mTUFeGJGkv6HrVSoNtIhkAp0Mh1O5XXw/X0Y/6UlKsky/r0BsWD9a4ddMLocWOm3EvGWi17lsPkX7DJFUH6DKuIiPrKxjGiUM0+Op6sLPSVULqyrAudTe7nHZ2/YM2iDeOrUc6jc3+aNqKLbnv1ApdpmlFoErnZvL/RwCLd9463758stiEOrKksv1K84oCvak7W6FCsEZdEEMK7fzqS+r7mzCqAwH5qewAcIB1l844DIDh7X2i4eQ4o=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.4.0
Release Details
UpdatedJuly 28, 2026, 6:52 p.m.
Changelog

Public share links now render 3D models, and every format that carries external textures resolves them there. Also carries the security fixes listed below.

Added

  • A merge gate that treats an absent check as a failure, as a ruleset on main and as npm run pr:ready. gh pr checks reports only the checks that exist, so a queued run contributes none and a pull request whose matrix has not started looks exactly like one where everything passed — which is how a broken eslint run reached main. The required set is the seven summary jobs: each aggregates its own matrix and reports unconditionally. Because the ruleset also covers pushes, work on main now goes through a pull request.
  • The integration suite now also runs on MySQL and PostgreSQL. The version axis is covered on SQLite; this adds the database axis, which is where the queries differ rather than where the server does — escapeLikeParameter() picks an escape character per platform, and MySQL's default collation is case-insensitive where SQLite's and PostgreSQL's are not, so a folder listing settled on one backend is not settled on the others. Newest supported server only, rather than crossing both axes.
  • A PHP integration suite that runs against a real Nextcloud server, covering what mocks cannot: real storage for the dependency route, and a real connection for the file browser's folder listing. Both bugs that reached production here — the str_replace prefix that reported folders like modelstextures, and escapeLikeParameter() called on the query builder, where the interface never declared it — are now caught, each verified by reintroducing it. The bootstrap exits non-zero when it finds no server rather than skipping, which is how the retired workflow managed to report coverage it never had.
  • FBX and 3DS textures now load on public shares, which completes declaration-based resolution for every format carrying external textures. Both name their textures in binary structures, so they are walked — FBX node records, 3DS chunks — rather than searched for their marker bytes: a filename occurring inside vertex data is not a declaration, and treating it as one would let a crafted model name any file beside it. Walking also steps over mesh data by its declared length instead of reading it. The folder listing is now only a signed-in fallback.
  • COLLADA and X3D textures now load on public shares. Both formats name their textures in the document, so they resolve by declaration through the existing /dep/{name} route, the way OBJ and glTF do — no folder listing, which is what needed a session and left them untextured. Matched with patterns rather than an XML parser: only the paths are wanted, and a parser on an uploaded document brings entity expansion with it. Signed-in sessions keep the folder-listing fallback for documents the patterns miss.
  • 3D models are now viewable through public share links (#115). Share pages previously rendered nothing at all: LoadFilesListener returns early for anonymous visitors, so the bundle never loaded. A new LoadPublicShareListener hooks the public template.
  • Textures load on public shares. OBJ materials, their maps and glTF buffers are served by name through /public/file/{token}/{fileId}/dep/{name}. The model's own declarations are the authorisation — an undeclared name is refused, as is any path escaping the model's folder.
  • Tests for both database migrations, which had none. The load-bearing one pins the sha256 folder-path digest across the three places it is computed — the migration backfill, FileIndexService on write, FileIndexMapper on read. Drift there silently returns empty folder listings rather than an error.
  • Doctrine DBAL stubs in tests/bootstrap.php. IQueryBuilder takes its PARAM_* constants from Doctrine, which nextcloud/ocp does not require, so mocking IDBConnection used to fatal. No database-facing class had ever been unit-tested here.

Changed

  • A successful model load no longer fills the console with warnings. getFileIdByPath logged its normal progress at warn — the folder listing, the name being looked for, the move to subdirectories — so a model with ten textures produced twenty-odd warnings on a load where nothing was wrong, and a real one was indistinguishable from the noise. Routine steps are now debug; warn is left for a listing that failed, a subdirectory that could not be searched, and an unexpected error. A declared file that is genuinely absent is the caller's to report, which it already does once via missingFiles rather than once per lookup.
  • The signed-in texture lookup no longer guesses at a name that does not match. About 150 lines of similarity rules — singular against plural, a leading word dropped, a length-ratio partial match, and a mapping from any colour-ish filename to any body-ish one — came from making one model work and then applied to every model. A rule that matches two different filenames is a guess, and a guess that lands wrong serves the wrong texture with nothing to say so. The case it was most likely to get right, a name differing only in case, is handled before that point and on the server since PathLocator; what remained only fired when a model referenced a file that is genuinely absent, where rendering untextured is the honest answer. getFileIdByPath goes from 366 lines to 155, and the conventional-texture-directory search that was duplicated in its catch block is now one function.
  • Unblocked dependency updates and refreshed the lockfile, superseding ten stalled dependabot PRs (#104–#114) in one pass.
  • stylelint 17 rule fallout, all resolved — color-function-alias-notation rewrote 30 rgba() calls across four files.
  • The 3D navigation controller is now a "Split console": a steering annulus with its buttons on an attached rail, styled from Nextcloud tokens. The eight decorative arrows are gone; arrow keys now steer a focused ring. The 15% dead zone, 110% hit slop and hold-to-repeat behaviour are unchanged.

Fixed

  • A texture in a subdirectory the model does not name was only found if the directory had a conventional name. The search that walks whatever subfolders the listing reports asked for each one without includeDependencies, and in that mode the server filters its answer to 3D models and drops every image — so the one pass written to find textures in subdirectories could never return one. A separate pass over sixteen hardcoded names (textures, maps, images, and case variants) covered the common cases and hid it, leaving a texture in a folder called skin or PBR unreachable with the folder sitting right there in the listing. Signed-in sessions only.
  • "Reset" and "Fit" could not be clicked. The viewer's top bar is position: absolute inside a container that was not position: relative, so it anchored to the wrong ancestor and sat under the app content.
  • The navigation controller was hidden behind the app navigation, which Nextcloud renders at z-index: 1800. It is now anchored to .three-viewer.
  • The 3D file browser invented folders that do not exist. getFolders() stripped the parent prefix with str_replace(), which removes every occurrence: with no parent the prefix was a bare /, so models/textures was reported as modelstextures and matched no files.
  • FileIndexMapper called escapeLikeParameter() on the query builder, where it is not declared — it lives on IDBConnection. It worked only because the server's concrete class happens to carry it, the same bet that fataled the Nextcloud 34 upgrade.
  • A test file was raw binary as far as git was concerned. \0\x01 reached xmlModelDependencies.test.js as literal bytes rather than escapes, and a NUL makes git stop producing diffs for a file — it had dropped out of line-level review while passing CI. A new guard scans source for C0 control bytes.
  • composer test:unit ran every test under tests/, not the suite its config names. It passed a positional tests path, which overrides the configured testsuite — the same defect that made the old integration workflow run the unit suite under another name. Retiring that workflow left this copy of it in place, where it stayed invisible until a directory appeared that the glob should not have swept up.
  • A texture declared in a different case than the file rendered as missing. 3DS keeps map names in DOS 8.3 form, so exporters write WOOD.JPG beside a file saved as wood.jpg; MTL files written on Windows do the same with subdirectories. The declaration matched — that lookup is already case-insensitive — and the fetch then asked storage for the path exactly as written, so the model rendered untextured with its texture sitting next to it. The exact path is still tried first; only a miss walks the model's own folder, stopping at the first segment with no match. Where both cases exist, the exact name wins.
  • The merge gate blocked a ready pull request after a force-push. Every workflow here uses a concurrency group with cancel-in-progress, so superseding a running job leaves a CANCELLED entry in the status rollup beside the SUCCESS of the run that replaced it — both under the same check name. The gate read every entry and reported the superseded one as a failure. It now takes the most recent entry per name. This is the second state-reading defect in that script, so the deciding logic moved to pr-ready-lib.mjs and gained tests; a gate that cries wolf is the one people learn to merge past.
  • The case-insensitive lookup above covered the public route only, and only its last step. An OBJ's textures are named by its material, so the material has to be read before they are known at all — and that read still asked for the exact path, so a mtllib CHAIR.MTL beside chair.mtl left every texture in it undeclared. Signed-in sessions missed for a second reason: /api/files/find and /api/files/list resolve the declared path themselves, and both matched case exactly, so Textures/wood.png beside a folder saved as textures found nothing. All three now share one implementation.
  • A path climbing out of the user's folder returned 500 instead of 404. Folder::get() raises NotPermittedException for a path like ../../etc/passwd, and /api/files/find caught only NotFoundException — so probing the endpoint produced an internal error and a logged stack trace apiece, and told the caller a refused path is not the same as an absent one.
  • Every viewer session logged a PCFSoftShadowMap has been deprecated warning. Three.js r184 reassigns the constant on the first shadow pass, so the app was already rendering PCFShadowMap.

Removed

  • Retired the "Integration Tests" workflow, which reported coverage it did not have. test:integration and test:migration passed a positional path that overrode their configured testsuite, so both ran the unit suite under other names — and the job had no Nextcloud server to integrate against. test-phpunit.yml covers those tests across the same OCP 31–34 matrix.

Security

  • Resolved four npm advisories in production dependencies via npm audit fix, lockfile only: form-data CRLF injection (GHSA-hmw2-7cc7-3qxx), postcss path traversal (GHSA-r28c-9q8g-f849), fast-xml-builder sanitisation bypasses, and nine in dompurify.
  • Cleared the brace-expansion DoS (GHSA-mh99-v99m-4gvg / CVE-2026-14257) from the production graph, taking npm audit --omit=dev from 7 high to 0. Only brace-expansion@5.0.8 is patched, and it renamed its default export, so it cannot be forced globally; pinning overrides.webdav.minimatch to ^10.2.3 reaches it instead. Glob behaviour is unchanged and the advisory's proof of concept no longer exhausts the heap.
  • Capped material libraries per model at ModelDependencyResolver::MAX_MATERIALS. Each mtllib name costs a storage lookup and ~226,000 fit in the scanned header, so one crafted model turned every /dep/{name} request on its public share into hundreds of thousands of lookups. The sharer can be the attacker, and the route has no rate limit.
  • Known residual: the shipped browser chunk still carries the unpatched brace-expansion. Vite resolves webdav to a prebuilt bundle with it inlined, which no override can reach; it clears when webdav republishes. Unreachable in practice, since nothing here passes a glob to getDirectoryContents. The dev tree still reports the advisory through eslint 8 and jest.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureQX95f1tTvJoYA9LqLShsNFPKOwHPgMmw0dE0Rq/KdnyoJLpH+TOYawYFTn6RFykF5RIFDyCrTTEDJo5ehLy3bLnyCRX9pLza7My58nDdCND+fRlHgzvUNwzfULSQC1zPi92sHmy+SPqnUrp6xhevBe57/IWxE2JyUf/T80Gf+EXwWFzc9uoVoE2+mHvzvV0db/ZVO+OX/HHgYzZ65Q8wZTbycb1nZFx8RaxkJogd/5Nh8aEb/NlpyKRlJi9Zx3XtyQL6AL0Dcnv2eOQbOuA5VnwFql1UqCtmR1400ZCd0Cf0sSaoX6S1XW3+oLjGhJSHBEi/A+0MVpDChPPHSjfH+ER5gr628NQFfVSxFBXr0hHbHx3r8wBxIk0kRyqjR5Uj5CNzg8QVQsNhhFvsP7FFl59Il2JmzsJXAe99e/8mf1rMGmEZ5kVXwUImUl4Bqgc3IDF5SKUTfspNPWKWojRzT9sRgLZDI1RFqep2ov1wMNYOQ4Qzbzk9N93iumDQk1FsiYud2VO3TVpjaNCc6vWL/S+jA03TksnkF4sqxexJRAGwBpyEcwOHwUG0R6HVZpchkdhL6MW5UVwSb2i9b1eIqs1+DbQ70HCffX7pPIxFW6zOVDsdNb00Q6vaYeT2q9IIR32DnKj20bnBjLgP3BX61V3Ul7PLoAbbhF8vABR4CEA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.2
Release Details
UpdatedJuly 27, 2026, 11:31 a.m.
Changelog

Security release. Upgrade immediately if you use password-protected link shares containing 3D models.

Security

  • Share passwords were not enforced on the public 3D file endpoint. PublicFileController extended plain Controller with #[PublicPage], and PublicShareMiddleware returns early for anything that is not a PublicShareController — so no share authorisation ran at all. Combined with ShareFileService::loadLinkShare(), which only checked that getShareByToken() matched, anyone holding a share token could read the file regardless of its password. The token is the part of the URL people paste into chats and emails; the password exists precisely for when the token alone should not be enough. PublicFileController now extends PublicShareController and implements isValidToken(), isPasswordProtected() and getPasswordHash(), delegating password and token validation to the framework and picking up brute-force throttling with it. findValidLinkShare() additionally rejects non-link/email share types and expired shares, and treats a ShareNotFound from the share manager as "no share" rather than letting it escape as a 500. Present since the controller was added in 1b0ca86 (2025-09-15), so every release from v1.7.9 through v3.3.1 is affected. Verified on Nextcloud 34.0.2.1: a password-protected share now returns 404 without the password and 200 with it, ordinary public shares are unaffected, and expired shares return 404 instead of 500.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturemn6HBntWm2p/at2aww2v9uJiZOjj7tNXpISa325yfW2SKnZnaJHt7wSDJnb6ghsYuF1u19BVFg+1b+e/hFTFmhD8l1a0BL69FqZej7a4bwwfFbovRiDUkkZjik0nVahdD3k6fg6qabxnJbHEh79Lz2/vgcE/5nzAio06G5xJrZgeR10aoZs5oxMEpnNQlPWCiIZV78oY2PCGWosOEQaz78SzU2sX1Saq4goXMCyG1ElYLLeMbtmTPv3g9Dp08FyUctxtxGX1JbuNFg2F2AuhZSyMmFcnS6jGp9STzWZrD6x9jbd6KP5/mu6dPe88CAouT/RmUSFS14CCYAXOPdeqc0wgsOJncpoRZycZ/FwACJKGOjzR1qACdtG5wKjXnMDMJA5mdgDL+G77s7PzeY+u4jBkS2iF8swLxzZphid2HGNN5Rg4NECyCwsBYM/o+hbPVpro6iLKOg/hpiugpoQpwDw0RmcujPDYWewonRnNKdB+7i/Qk5FXDluhqGr7GoRek+nZaqm/pxSIGEeSnkpqoPAxHY978OncdxhKSIe8ACCxl9g9nokM4bxLA4zXWGHL8Onnb/DdWIGEfuGIhdhV/5QtSSW1xW75HrTcuHPJJFBAF2ovwQYPfexY26ZU0m7pyt2ucN8xxkkpuD6RDMpm2U/v9y+AxbtuNunW2CoKqm0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.1
Release Details
UpdatedJuly 27, 2026, 10:35 a.m.
Changelog

Hotfix for Nextcloud 34. 3.3.0 is unusable on NC 34 — both the viewer page and every model download return HTTP 500, and the app cannot be upgraded or enabled. Nothing else changed.

Added

  • Regression tests for both fatals. tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface with no __call() fallback, so it raises the same Error production did. tests/unit/NoPrivateServerApiTest.php scans lib/ for any use of the private \OC container or the legacy OC_* static classes, stripping comments via token_get_all() first so documentation about a removed API isn't mistaken for a call to it. Both fail on the pre-fix code. Note that composer test:unit is not currently run by any CI workflow — test-integration.yml runs only test:integration and test:migration — so these are verified locally until that gap is closed.

Changed

  • nextcloud/ocp dev dependency moved from dev-stable30 to dev-stable31, to match the min-version="31" declared in appinfo/info.xml; the weekly update-nextcloud-ocp-matrix.yml job, which had target: ['stable30'] hardcoded and kept re-pinning it, now tracks stable31 too. The pin deliberately follows min-version, not max: pinning to the oldest supported server is what stops us using APIs that don't exist there yet, and ocp stable33+ requires php ~8.2, which cannot coexist with the config.platform php 8.1 that NC 31 support demands. Verifying against the newest branch is psalm-matrix.yml's job — it already builds an ocp matrix across the full min..max range from info.xml. Psalm run manually against stable34 for this release confirms no further removed-API calls in lib/ beyond the two fixed below, and flags ten DeprecatedMethod notices worth scheduling: IConfig::getUserValue/setUserValue/deleteUserValue in PageController, SettingsController and ConfigController, plus IContainer::query in Application. None break on 34.

Fixed

  • App upgrade fataled on Nextcloud 34 (#116) with Call to undefined method OC\Server::getDatabaseConnection(). The Version010903Date20251121000000 migration reached into the private \OC server container in its constructor; NC 34 stripped getDatabaseConnection() from that class, which now exposes only getUserFolder(), getWebRoot() and getL10N(). It now resolves the connection through the public OCP\Server::get(IDBConnection::class) locator. Deliberately not constructor injection: MigrationService::createInstance() falls back to new $class() when the container cannot resolve a step, and an injected dependency would turn that fallback into an ArgumentCountError. The stale use OC; import is gone and $connection is now typed. Reported with a patch by @EX0l0N; same fix independently submitted as #117 by @coverslide.
  • Every model download and viewer page render fataled on Nextcloud 34 with Call to undefined method OCP\AppFramework\Http\EmptyContentSecurityPolicy::addAllowedChildSrcDomain(). Nextcloud removed the legacy child-src helper from its public CSP class (deprecated years ago in favour of the split worker-src/frame-src directives), so the call added in commit 4e0a532 ("fix CSP") became a hard error the moment the app ran on 34 — taking down FileController::serveFile and both PageController entry points, not just the worker policy. ResponseBuilder::addCspHeaders now calls addAllowedWorkerSrcDomain('blob:'), which is the correct directive for the blob-URL Web Workers that DRACO, KTX2 and web-ifc spin up, and has existed since NC 17 — safe across the whole declared 31–34 range. Reported in #116. The new tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface exactly, so it reproduces the production Error on the old code regardless of which OCP branch happens to be vendored.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturenWYgrEeOKWzfUMwxb3X0hAzGbWgJ9vd3jRvsIFUKLCvTsARpp09yWa5mnupOhaRtHcffz0eidnz5Blw7uN8sTMvty2tpnzgLad3CTvV401vV+je0pyeWgol+mkhoymUwBNcvlR4OWx4NWHX1azz9NdLAlF/J+A1ImE3D2UMf5mXlGctjznQwBeMLrXkKke1qdy23heuGTi6HCnNicY1PpkhC9RcmY0wlBbZr9/kx/TbCYx136AKjctjt4554x9SXkiAjt5S1SSEFefSaNxURYWlSx6xeCt4enQVAfA2uNfj772yxyWRhyvcQMnfIFz4IGTYMCi3KlKZgQvSa/Zj1KNP6d9NWOubG+RqOjzv84wjznJ6xoSTEoNLuQhuYt1zqW2wDyDFT9GD2jSONDYZjOZjal37C4NEvEv9HIkZYkVBL4kvwlytafsD9u4cmKCmgv0ROvPCM2PWVmnT3kXGOS+ivKLcaC0Zt0VciNlhYIAawDLDL+1j+WKRHIPhNUsLjiNfqbmUWUZlbEHPJvdIxTBfePDbvnnyFGWlUBKO+QoEo0JPHrpfisz31eb88G/33/1iblqUUZ3OwU71GAHjFvD8Vf0ivihZZSUqsTDdiEGU1dsbT7DnkFi2bLNfNQhcG14k03NzUVQW+fODwNoRO9pkWJnadgsZQ1ICJAj3sw9M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.0
Release Details
UpdatedApril 20, 2026, 6:47 a.m.
Changelog

Fixed

  • 3DS files loaded tipped on their side when the exporter used Y-up instead of the 3ds-Max Z-up default. The loader had been hard-coded to rotate every 3DS model -90° around X (commit d8117a0, "fix: add coordinate system rotation corrections for Z-up formats"), which helped CAD-style Z-up files but silently broke Y-up exports — Cottage_FREE.3DS for example rendered with its long axis as the vertical. Dropped the unconditional rotation; 3DS files now pass through at their authored orientation. Most modern 3DS exporters (Blender, recent Maya) are already Y-up, and Z-up outliers can be fixed by orbiting or — if this becomes common — by a future user-facing orientation toggle. Other Z-up-rotating loaders (STL, PLY, 3MF, DAE) are unchanged because those formats have stronger Z-up conventions that are not affected by this fix. Reproduced and verified in the live Nextcloud container by downloading the user's 3DS via WebDAV, inspecting bbox dimensions with a new scripts/inspect-3ds.mjs harness (raw X=1243, Y=675, Z=1465 — Y is the height), and hard-refreshing the viewer page.
  • Standalone viewer: KTX2 loader threw on init with Cannot read properties of undefined (reading 'isWebGPURenderer'), so models with Basis/KTX2-compressed textures silently fell back to placeholder textures. Root cause: the standalone ThreeViewer.vue never forwarded the live WebGLRenderer into the loading context, and Three.js r182's KTX2Loader.detectSupport(renderer) dereferences renderer.isWebGPURenderer before checking anything else. Fix: pass renderer: renderer.value in the context, plus a defensive guard in gltf.js that skips KTX2 entirely (with a warning) when no renderer is available instead of throwing. The modal viewer was unaffected — it already forwarded this.renderer. Live-E2E harness now also watches for KTX2/WebGPU console complaints so the regression can't sneak back in.
  • Standalone viewer: glTF models with embedded data: buffers failed to load under Nextcloud's Content Security Policy. The app CSP allowed data: for img-src but not connect-src, so GLTFLoader's fetch() of embedded base64 buffers (the default export shape of many gltf files) was blocked by the browser. ResponseBuilder::addCspHeaders now also adds data: to connect-src. Caught end-to-end by driving Playwright against the real dev container (scripts/live-e2e-check.mjs).
  • Skip-to-viewer link wasn't in a usable tab position — it was rendered inside <NcContent> → <NcAppContent>, which put it ~9 tabs deep behind Nextcloud's header, nav sidebar, and file list. Wrapped in <Teleport to="body"> so the anchor lives at the body level regardless of where Nextcloud's chrome wraps us. DOM-verified focusable in the live container; clicking it moves focus to #viewer-wrapper. The CHANGELOG entry below that claims "first tab stop" was aspirational — Nextcloud's own Skip to main content / Skip to navigation links always win the first two positions, which is correct behavior we can't (and shouldn't) override.

Added

  • Four more CAD formats via OpenCascade — STEP, IGES, BREP, FCSTD (closes #97): All four formats come from the OCCT family of NURBS/B-Rep CAD files and share one WASM integration. Uses occt-import-js (Viktor Kovacs's purpose-built emscripten port of OpenCascade's import + tessellation) — much smaller than vanilla opencascade.js (~7 MB WASM vs 10+) and with a JSON-based mesh output that's a clean fit for Three.js. Architecture: one shared src/loaders/occt-runtime.js exposes getOcct() (memoized module promise so the 7 MB WASM downloads once across all 4 loaders) and buildGroupFromOccResult() (walks the assembly hierarchy preserving node names, transforms are already baked into tessellated vertices so we don't reapply matrices). Each format gets a thin ~25-line loader: STEP (.step, .stp) → ReadStepFile, IGES (.iges, .igs) → ReadIgesFile, BREP (.brep, .brp) → ReadBrepFile, FCSTD (FreeCAD document) → unzip via fflate, filter for .brep entries, feed each to ReadBrepFile — per-body failures don't take out the whole document. The resulting bundle shape: four 1 KB loader chunks + one 725 KB shared occt-runtime-*.chunk.mjs + 7.3 MB WASM served separately from /apps/threedviewer/occt/. Main/app bundles unchanged (0 B trend). Not unit-tested because the whole pipeline needs live WASM + real CAD fixtures to exercise meaningfully; covered by the existing smoke suite pattern.
  • Fifth new format — IFC (.ifc) (#97): Industry Foundation Classes, the de-facto BIM interchange standard. Loader in src/loaders/types/ifc.js uses ThatOpen's web-ifc WASM (~5 MB, lazy-loaded on first .ifc open) and a hand-written Three.js bridge — web-ifc-three has been deprecated and its modern replacement (@thatopen/components) is a heavy framework we don't need just for file loading. Bridge logic: StreamAllMeshes walks the IFC element graph, each FlatMesh's PlacedGeometry entries yield tessellated vertex data in interleaved [px,py,pz,nx,ny,nz] layout which we deinterleave into separate position/normal attributes, then apply flatTransformation (column-major 4×4) and per-geometry color. Express IDs (expressID, geometryExpressID) are preserved on mesh.userData.ifc for future property-panel tooling. Memory is carefully managed — geometry.delete() in a finally block releases the WASM-side handle per mesh, and CloseModel runs unconditionally so parse errors don't leak the full IFC model. WASM copies ship via scripts/copy-decoders.mjs to /apps/threedviewer/web-ifc/ (both single-threaded web-ifc.wasm and multi-threaded web-ifc-mt.wasm — IfcAPI picks the variant based on runtime feature detection) so air-gapped Nextcloud deploys don't need CDN reachability. JS glue is lazy-chunked into a 3.4 MB ifc-*.chunk.mjs that only loads when a user opens a .ifc file; main/app bundle impact is minimal. Not unit-tested because the whole pipeline requires live WASM + real IFC fixtures; covered by the smoke spec suite instead.
  • Fourth new format — dotbim (.bim) (#97): JSON-based BIM format from dotbim.net — simple schema (meshes + elements with { mesh_id, vector, rotation, guid, type, color, info, face_colors? }). Loader in src/loaders/types/dotbim.js handles mesh instancing (multiple elements sharing a mesh_id each get their own cloned geometry), element transforms (translation vector + unit quaternion rotation), RGBA byte colors, optional per-face colors (v1.1.0 schema — unindexes geometry so each triangle's 3 verts carry the face color), and tolerates legacy exporter quirks (missing/null rotation, mismatched face_colors length). Element metadata (guid, type, info) is stashed on mesh.userData.dotbim for future inspection tooling. 10 Jest specs in tests/unit/loaders/dotbim-parser.test.js cover instancing, transforms, color handling, transparency, legacy-rotation fallback, orphan elements, metadata, and JSON/schema validation error paths. Zero WASM, zero external deps, ~180 lines. Like the other new loaders it's lazy-chunked via the registry's dynamic imports — 0 B impact on the main/app bundles.
  • Three new 3D formats — OFF, AMF, 3DM (closes #97 partially): (1) .off (Geomview Object File Format) — hand-rolled plain-text parser in src/loaders/types/off.js covering OFF, COFF (per-vertex colors, float or 0–255 int), N-gon fan triangulation, inline # comments, and binary-OFF rejection. 9-test Jest suite exercises the parse path end-to-end (tetrahedron, n-gon, comments, COFF float/int colors, degenerate inputs). (2) .amf (Additive Manufacturing Format) — wraps three.js's bundled AMFLoader, handles both XML and ZIP-compressed variants. (3) .3dm (Rhinoceros) — wraps Rhino3dmLoader, loads rhino3dm.js + rhino3dm.wasm (~4 MB) on first use from the app's bundled copy at /apps/threedviewer/rhino3dm/ so air-gapped Nextcloud deployments don't need CDN reachability. scripts/copy-decoders.mjs now copies the rhino3dm assets alongside DRACO/Basis. All three formats are registered across the 5 sync points (lib/Constants/SupportedFormats.php, src/config/viewer-config.js, src/loaders/registry.js, src/main.js::SUPPORTED_MIMES, appinfo/mimetypemapping.json); npm run format:check PASS. Each loader is lazy-chunked via the registry's dynamic imports, so the main/app bundle size is unchanged (only users who open the new format pay the download cost). The 6 remaining formats from #97 (step, iges, brep, fcstd, ifc, bim) all require OpenCascade.js or web-ifc WASM (~5–10 MB each) and are deferred until demand justifies the bundle hit.
  • Forced-colors (Windows High Contrast) CSS hardening: New central override sheet (src/css/forced-colors.css, imported from main.js) scoped entirely to @media (forced-colors: active) — no effect on default rendering. Addresses three patterns the a11y audit found: (1) custom badges (.stats-badge, .fps-badge, .active-badge, .last-used-badge) that rely on background-color + text-color alone collapsed into the surrounding surface once the UA repainted both with system colors; they now get a 1px CanvasText border so they stay visible. (2) .filter-format-chip.active lost its visual distinction from the inactive state because both backgrounds became Canvas; the active variant now uses Highlight / HighlightText with forced-color-adjust: none so selection state survives. (3) Focus states that suppressed outline and relied on box-shadow or colored border-color (.export-select:focus, .annotation-text-input:focus, .skip-to-viewer:focus) had no visible focus indicator; they now re-enable a 2px Highlight outline. Verified by a new Playwright spec (tests/playwright/a11y-forced-colors.spec.ts, 5 tests) that loads the real CSS file into static fixtures mirroring each component shape and asserts border/outline width > 0 under emulateMedia({ forcedColors: 'active' }). Caveat: Playwright's forced-colors emulation uses a neutral palette — this closes the keyboard/CSS correctness gap but an interactive Windows HC walkthrough is still the final source of truth for theme-specific contrast issues.
  • i18n parity tooling (scripts/check-i18n.mjs): Walks src/ (.vue/.js/.ts) and lib/ (.php) and extracts every t('threedviewer', 'string'), this.t(...), and ->t(...) call via regex. Diffs the extracted set against l10n/en.json: ERROR on source strings missing from en.json, WARN on orphan keys no source uses, per-locale coverage summary that reports missing, passthrough (value === key), and orphan counts. --sync-en auto-adds missing keys (value = key); --prune removes orphans. Wired as npm run i18n:check / npm run i18n:sync; i18n:check is now part of the validate chain. First sync closed 329 missing keys and pruned 87 orphans in en.json — ar/de/es coverage now reports accurately at ~27% (previously looked higher only because en.json itself was incomplete).
  • Export pipeline unit tests + MIME consistency fix: Nine Jest specs in tests/unit/composables/useExport.test.js cover getGeometryStats (indexed, non-indexed, multi-mesh groups, non-geometry descendants) and exportAsSTL/exportAsOBJ/exportAsGLB (correct MIME — model/stl, model/obj, model/gltf-binary — correct byte length, null-object guards, and a multi-material OBJ scene that passes through the exporter with its material array and geometry groups intact instead of being flattened). Three.js exporters are mocked so the test runs deterministically in jsdom without pulling in Three's ESM-only subpath imports; the real exporter output remains covered by the Playwright smoke suite. Shipped alongside a MIME consistency fix in SlicerModal.vue — the slicer-export fallback path was still using application/octet-stream for STL and text/plain for OBJ even though the main Export Model path had been updated to model/stl / model/obj; both paths now agree.
  • Loader smoke-test coverage for cancel/retry/network-drop: Two new Playwright specs in tests/smoke/viewer.spec.ts use page.route() to deterministically simulate (a) a user cancelling a hanging load and then retrying — the second fetch is fulfilled with a fixture and the viewer reaches __LOAD_COMPLETE, and (b) a network failure (route.abort('failed')) that must surface via __LOAD_ERROR without being misclassified as a user abort. ThreeViewer grew two tiny test hooks (window.__LOAD_COMPLETE on successful model-loaded, window.__LOAD_ERROR on non-abort errors) to give tests a DOM-independent signal — production code is unchanged in behavior.
  • Accessibility follow-ups: Keyboard users now get a "Skip to 3D viewer" link as the first tab stop (visually hidden until focused, slides into view on focus) that jumps focus past the navigation directly to #viewer-wrapper. Both modals (SlicerModal, HelpPanel) now trap Tab/Shift+Tab inside the dialog while open and restore focus to the element that opened them on close — implemented via a dependency-free useFocusTrap composable. Modals also gained aria-modal="true" and tabindex="-1" on the dialog surface. Covered by a new Jest unit suite (tests/unit/composables/useFocusTrap.test.js) and a Playwright a11y spec (tests/playwright/a11y-skip-link.spec.ts) that verifies skip-link behavior under forced-colors: active (Windows High Contrast).
  • Transform Gizmo: Translate, rotate, and scale models interactively with Three.js TransformControls. Mode selector (Move/Rotate/Scale) and reset button in the Analyze section. Disables orbit controls while dragging.
  • Animation clip selector: Dropdown in the Animation panel to switch between individual animation clips (e.g., Run, Walk, Idle) for models with multiple animations
  • Texture optimization pipeline: Quality presets (Original/High/Medium/Low) with Canvas 2D downscaling, memory tracking, and configurable setting in Personal Settings
  • X3D parser: Full XML-based parser replacing placeholder cube — supports IndexedFaceSet geometry, materials, textures, transforms, and DEF/USE references
  • Volume & surface area measurement: Model statistics panel now shows actual mesh surface area (sum of triangle areas) and mesh volume (signed tetrahedra method, accurate for watertight meshes), in addition to bounding box volume
  • Custom color palette editor: Personal Settings → SlideOutToolPanel now exposes four user-overridable scene/chrome colors on top of the base light/dark theme: background, grid, toolbar bg, and toolbar text. Each has a native <input type="color"> picker plus a per-key "×" clear button; a single "Reset all palette colors" button clears everything at once. Overrides persist to localStorage['threedviewer:customPalette'] and are merged on top of whichever base theme (auto/light/dark) is active, so switching themes leaves your palette alone. The viewer rebuilds its THREE.GridHelper in place when gridColor changes and swaps scene.background when background changes — no reload required.
  • Decoder worker pool tuning (parallel decoding): GLTFLoader's DRACO and KTX2 sub-loaders now call setWorkerLimit(n) with n = min(hardwareConcurrency - 1, 4) (floor 2), so GLB textures and compressed meshes decode in parallel across multiple Web Workers instead of serializing on one. Defensive feature-detection — falls through when the Three.js version doesn't expose setWorkerLimit. Result: GLBs with multiple KTX2 textures load noticeably faster on 4+ core machines without starving the main thread or flooding it with workers on 1-core devices.
  • Memory pressure auto-step-down: The performance composable now samples performance.memory.usedJSHeapSize each frame and, when usage crosses 85 % of the configured maxMemoryUsage cap (default 500 MB), auto-switches to the Low performance mode — reducing pixel ratio, disabling shadows, and applying LOD. A one-time warning toast ("Auto-reduced quality — memory pressure") surfaces what happened; the flag clears once memory drops back below 70 % so the user can re-raise quality manually. 5-second cooldown prevents thrashing, and the check gracefully skips browsers without the non-standard performance.memory API (Firefox, Safari).
  • Background indexing status API + progress bar: New GET /api/files/index-status endpoint backed by ICacheFactory::createDistributed returns { active, processed, total, percent, startedAt, updatedAt } for the authenticated user. FileIndexService.reindexUser now pre-counts supported files (respecting .no3d and hidden-folder skip rules), then bumps the processed counter after each file. The Personal Settings "Re-index now" button starts a 750 ms poll in parallel with the blocking POST request and renders a live NcProgressBar ("142 / 284 (50 %)"), falling back to "Scanning…" until the pre-scan completes. Status TTL is 1 hour.
  • Adaptive texture streaming (visibility-aware queue): useProgressiveTextures now accepts a (camera, scene) streaming context and sorts its pending queue by frustum visibility before each batch — textures on meshes currently in view load first, off-screen ones are deferred. When the camera moves, the very next batch picks up the new viewpoint. queueTexture has a new optional mesh argument for visibility scoring; the signature stays backward-compatible, so existing callers are unaffected. ThreeViewer auto-registers the context on init so future loader pipelines that opt into progressive loading get adaptive scheduling for free.
  • Measurement suite upgrades: The Statistics panel's measurement section is now a full measurement tool:
  • Unit picker: Choose mm / cm / m / in / ft / generic units from a dropdown; bounding box, surface area, and volume values re-render instantly with the right suffix (mm, mm², mm³, etc.). Defaults to the project's configured VIEWER_CONFIG.measurement.defaultUnit so it matches the existing ruler tool.
  • Per-mesh breakdown: When a model has more than one mesh, a scrollable list shows each mesh's surface area and volume in the selected unit. Clicking a row focuses the panel on that mesh — the Width/Height/Depth/Area/Volume rows switch to its numbers and a "Focused: mesh name" header with a Show-all link appears. Helper meshes (transform-gizmo pickers, annotation markers) are excluded.
  • Pick mesh in viewport: A one-shot button arms a raycasting click handler on the canvas; the next click on a mesh focuses it in the stats panel. Works with any mesh in the loaded model, respects visibility, and skips gizmo helpers.
  • Watertightness badge: The section header flags each mesh as Watertight (all edges shared by exactly two triangles — volume is reliable), Not watertight (open or non-manifold edges detected — volume should be treated as approximate), or Unknown (non-indexed geometry, or >500k triangles so we skip the edge-map check for performance). Aggregated badge appears at the top of the section; per-mesh flags appear next to each row in the breakdown.
  • Copy measurements: A button copies a plain-text report of the current values (bounding box, surface area, mesh volume, bbox volume, watertight status, per-mesh table) to the clipboard in the selected unit. Uses navigator.clipboard.writeText on secure contexts with the textarea + execCommand fallback for legacy browsers. Success/error toast via the existing push-toast channel.
  • WebXR / VR mode: Enter immersive VR via the 🥽 button in the top bar (only shown when the browser advertises immersive-vr support). Animation loop swaps to renderer.setAnimationLoop during XR sessions, and FPS throttling is bypassed since the headset enforces its own cadence. Testable without a headset using the Chrome WebXR API Emulator extension.
  • Annotation JSON export/import: Save annotations as a versioned JSON document and re-import them later. The schema includes format, version, exportedAt, modelFilename, and an annotations array of { id, point, text, timestamp }. Import/Export buttons appear in the Annotations overlay when annotation mode is active.
  • Scene comparison diff overlay: When two models are loaded side-by-side via Comparison mode, a "Scene Diff" panel auto-appears showing original vs. comparison stats (vertex count, face count, mesh count, bounding box X/Y/Z, diagonal length) with color-coded deltas (green = increase, red = decrease). Helper meshes (transform gizmo pickers, axes/grid helpers) are excluded from stats so the diff reflects only the loaded geometry. The overlay is dismissable via × and re-openable from a "Diff" button in the comparison controls.
  • Annotations persistence (per-file backend save): Annotations now save automatically to your Nextcloud account, keyed by file ID, so they reappear next time you open the same model. Backed by a new AnnotationsService that stores one JSON document per (user, file) under app data (appdata_*/threedviewer/annotations/{userId}/{fileId}.json), with GET/PUT/DELETE routes at /api/annotations/{fileId} validating that you have access to the underlying file before reading or writing. Saves are debounced 600 ms after each change; clearing all annotations issues a DELETE so the backend doc is removed instead of storing an empty list. A small status pill ("Loading… / Saving… / Saved / Save failed") in the annotation overlay header surfaces sync state. 256 KB cap per document.
  • Shareable view link: New "Copy View Link" button in the View section copies a URL that encodes the current camera viewpoint (position, target, zoom) as a compact cam=px,py,pz,tx,ty,tz,z query parameter. Opening the link reloads the model and restores the exact same angle after auto-fit runs, so you can send collaborators a link and say "check out this corner of the model" without needing a live session. Toast feedback on copy success/failure; button disabled until a model is loaded. Works on any secure context (HTTPS or localhost) via navigator.clipboard, with an offscreen-textarea + execCommand fallback for legacy environments. No backend — pure URL round-trip.
  • Clipping box (6-plane section analysis): The existing Cross-Section tool now has a Plane / Box mode switch. Plane mode is unchanged (single cross-section plane with axis + position slider). Box mode enables six axis-aligned clipping planes, one per face of the model's bounding box, each with an independent 0–1 offset slider that moves that face inward toward the opposite side. Setting xMin=0.35 and zMax=0.35, for example, slices the leftmost 35% off along X and the nearest 35% off along Z, leaving the intersection slab visible with interior surfaces rendered via DoubleSide. A "Reset box" button returns all six offsets to 0. Plane and box state are kept independently so toggling between modes is non-destructive, and box offsets auto-remap when a new model is loaded.
  • File browser search & filters: New filter toolbar in the file browser. At the Folders/Types/Dates overview level the search box runs a global recursive search that walks every folder, type, and date bucket and surfaces matching files as a flat results grid (each card showing its full path) — so searching wolf from the Folders root finds Wolf-Blender-2.82a.glb two folders deep without manual drilling. Inside leaf views (drilled into a folder, type, or month) the search filters both subfolder cards and file cards together, and unlocks two extra controls: multi-select format chips (one per extension actually present in the current view) and a size bucket dropdown (Any / < 1 MB / 1–10 MB / > 10 MB). Filters compose with AND between categories and OR within format chips, and a "Clear filters" button appears as soon as any filter is active. Filters auto-reset when navigating between folders/types/dates so a stale query never makes the next view look empty.

Fixed

  • FBX taillight/lens transparency: FBX post-processing was force-overriding every material to opacity = 1.0; transparent = false, which made authored translucent lens shells (taillights, headlights, glass) opaque and hid the coloured mesh inside — the Mercedes GLS rear lights rendered as a solid grey slab with the red only visible if the camera was pushed inside the body. Now preserves the FBX-authored opacity: materials with 0 < opacity < 1 are left transparent with depthWrite = false so inner meshes show through; only materials with opacity <= 0 (which would render invisible) are still forced opaque.
  • Stats panel — File size on multi-file loads: File Size showed "0.00 MB" for formats that come with sidecar assets (OBJ+MTL, FBX with textures, GLTF+BIN). The stats panel was reading modelLoading.progress.value.total, but multi-file loaders report progress as a percentage (max 100) rather than bytes, so the resulting "100 B → 0.00 MB" rounded to zero. Now reads the actual byte count from modelSourceFiles.value[].size (matching main filename first, else first file), with the progress-total value only used as a fallback when it looks like real byte counts (> 1 KB).
  • Stats panel — Texture memory accounting: Textures section reported "Memory: 0.00 MB" while textures were still loading, because the loaded-image check used a 512×512 fallback that happened to resolve before decode — making tiny placeholders look real. Now distinguishes three states: loaded (dimensions ≥ 4×4 contribute to memory), pending (flagged separately so the UI can show "loading…"), and missing (1×1 placeholder or HTML img with empty src / zero naturalWidth). ThreeViewer polls analyzeModel once a second for up to 8 seconds after load when any texture is pending, so async decodes get picked up without the user needing to reopen the panel.
  • FBX dark rendering without textures: When an FBX referenced textures (e.g. texture.png) that weren't shipped alongside the model, the loader substituted a 1×1 base64 placeholder into mat.map. final = color × placeholder then multiplied every surface down toward near-black, producing a silhouette-dark render where the Mercedes GLS looked like a blob. The loader now detects its own placeholder (1×1 image size) and nulls out mat.map; a new "clay mode" kicks in when zero textures resolved from dependencies, brightening near-greyscale dark material colours to a pleasant 0.75 clay-grey while hue-preserving the brightening on coloured materials (green license plate, red brake lights) so they still read as coloured instead of washing out to clay.
  • ZIP export — main file path: The main model file is now packed under its basename (e.g. eyeball.obj) instead of its full Nextcloud path (e.g. /3D files/Eyeball/eyeball.obj). The leading slash is invalid in ZIP archives and broke extraction on some platforms.
  • ZIP export — preserved subdirectory layout: Textures discovered in subdirectories (e.g. textures/) are now packed under their original relative paths in the ZIP instead of being flattened to the root, so the unzipped folder mirrors the original Nextcloud layout and re-imports cleanly.
  • Comparison mode loading 400 error: loadComparisonModelFromPath was passing the { id, subdir } object returned by getFileIdByPath directly into URL templates, producing [object Object] and a 400 from the file API. Now destructures id before use. (Latent regression from the earlier getFileIdByPath return-shape change.)

Fixed

  • Animation playback: Previously all animation clips played simultaneously, causing blended/static poses. Now only one clip plays at a time
  • Animation timeline seek: Slider scrubber now correctly updates the model pose when dragged (replaced mixer.setTime() with action.time + mixer.update(0))
  • Animation loop toggle: Toggling Loop off and back on no longer leaves the animation stuck; finished LoopOnce actions properly restart
  • FBX rendering: Fixed dark/black eyeball — upgraded Lambert to Phong materials, added SRGBColorSpace, normalized scale, detected transparent shells
  • PLY loader: Preserved original vertex normals from file instead of always recomputing them
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewL1fCXkzaQs2nvi2AkLccAX0u8v5YD4iOypG70neHtpgIb3wIQO+XaIgyQ+8n5k5ZO8pYI33VMdCWsomImFSTZmT7bH7s5DdFotdIektcqSN+kjfyeBeJkdoAcXUU2sJZgSdlFMrIxINc35c1nagz9g+B7qabynqu0yP2FnuWkeN9E37Rb4aXqvPUPPCEFkNjilYHkC1vrW6cUR/Z+L806b1IGJ0eZs6yH/3BB83V6TZ92btN80bbLdK0IbDY/uJ7sggkhuir0WKKoyC31fKsTaqcUi9I6G7Gr2Xzm3NqS6Bk2Y/pp0rLCSQJ+4xO3LA2ktbZIMZtqgA3gEfRYqMzJ10Eyny6TCwfAwOuvozvPoKbfm+5tlhjQyGgE/C7iDOEJ2LFqC9SEH9RrNPlzKxNJ/u1zXHdmEzmxGQpAKS1qoDEAPdKOsVFdkcdKugTNIi6PpP+29HGGl7lE8OuKIiq/JY8z2+9jnfAD087bE8QxnqJBi4eL8401WSzVDRRHlbh6nS/RFb/AqCDkJE+O7G5KskqRvrb3Re7QGNXcogubljsyRMVxQT2azuv/xg6VXJGKGIMv78/OBcQW+fMfr1s6NoDweK0LTQXIVGjQbx0SRJBPsGE5arytY9eIdaoRyHcxXje81vWhp5/Ar5MMLsQf6FjBKZILulUZWPvG2VfmE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.2.0
Release Details
UpdatedApril 5, 2026, 6:44 p.m.
Changelog

Added

  • Cross-Section tool: Interactive clipping plane to slice models along X/Y/Z axes with position slider, flip direction, and DoubleSide rendering for visible interiors
  • Animation Timeline Scrubber: Seek to any point in animated models with a slider, step forward/backward frame-by-frame
  • View Bookmarks: Save and restore camera positions with display toggles (grid, axes, wireframe, background), persisted in localStorage
  • Lighting Presets: Quick-switch between 5 lighting setups (Default, Studio, Outdoor, Dramatic, Flat) affecting ambient, directional, and point lights

  • Exploded View: For multi-mesh models, animate parts outward from centroid with adjustable explosion factor slider

  • Slicer: Export format selector: Choose STL, OBJ, or PLY before sending to slicer (non-passthrough formats)
  • Slicer: Upload progress bar: Real-time progress indicator when uploading to server (XMLHttpRequest with progress events, 2-min timeout)
  • Slicer: Copy share link: Copy the temporary Nextcloud share URL to clipboard for manual use
  • Slicer: Size validation: Warns and blocks uploads exceeding 50MB before attempting server transfer
  • Slicer: Upload size display: Shows file size in MB during upload for files >5MB
  • Modal viewer stats panel: Lightweight model info overlay (meshes, vertices, faces, dimensions) accessible via bottom-right button
  • Modal viewer screenshot: Download a PNG screenshot of the current view from the modal preview
  • Cache settings in Personal Settings: Configurable max cache size, max file size, expiration days, enable/disable toggle, and clear cache button
  • Cache hits/misses in performance overlay: Shows individual hit and miss counts alongside hit rate percentage
  • Cache privacy documentation: Documented local-only storage, per-browser isolation, and user control in TECHNICAL.md
  • Multi-file matching test suite: 48 tests covering texture/MTL name matching strategies (space normalization, prefix removal, plural handling, color/body mapping, partial matching)
  • Edge case fixtures: mixed-case extensions, missing MTL, orphaned textures for multi-file loading tests
  • Export triangle count warnings: Toast notifications for large models (>500K info, >2M warning) before export starts
  • Export MIME type fix: STL exports use model/stl, OBJ exports use model/obj instead of generic types
  • Help panel refresh: Added Slicer & Export section, cross-section, exploded view, lighting presets, bookmarks, dependency cache documentation
  • i18n audit: Wrapped hardcoded export/error toast strings in t(), added 31 new keys to l10n/en.json, documented i18n checklist in TECHNICAL.md
  • Accessibility review: Added role="dialog" + aria-labelledby to SlicerModal, aria-controls on all 6 panel section headers, aria-label on emoji-only buttons, role="alert" on texture warning, role="region" on stats panel
  • Format parity guard: Build-time script (npm run format:check) validates that PHP, JS, loader registry, Viewer MIME list, and mimetypemapping.json stay synchronized
  • X3D/VRML MIME registration: Added model/x3d+xml and model/vrml to Nextcloud Viewer MIME list so these formats open in the viewer
  • Slicer security documentation: Documented full security posture — authentication, path traversal prevention, MIME validation, size limits, share expiry, and file lifecycle in TECHNICAL.md

Fixed

  • Slicer OBJ/PLY upload: Added obj and ply to backend upload allowlist — OBJ/PLY format selector was added to frontend but backend rejected these formats
  • EufyStudio URL parsing: Replaced fragile regex filename extraction with proper URL parsing
  • Slicer upload timeout: Added 2-minute timeout to XMLHttpRequest (previously no timeout — could hang forever)

Changed

  • Tools panel redesign: Reorganized from 4 sections to 6 (View, Scene, Analyze, Animation, Export, Settings) for clearer grouping
  • Toggle switches: Replaced text checkmarks with custom CSS toggle switches for Grid, Axes, Wireframe, and Loop controls
  • Export section: Screenshot, Export Model, and Send to Slicer moved from Settings to dedicated Export section
  • Animation section: Elevated from nested group to its own collapsible section (conditional, only shown for animated models)
  • Model Statistics: Moved from Settings to Analyze section alongside Measurement, Annotation, and Cross-Section

Fixed

  • Theme consistency: Unified --color-primary-element fallback values across all components to #0082c9 (Nextcloud default), replacing inconsistent #4287f5, #1976d2 fallbacks
  • MinimalTopBar hardcoded colors: Replaced 7 instances of hardcoded rgb(0 130 201) with var(--color-primary-element) and related NC variables
  • Dark theme hack removed: Deleted ~130 lines of .dark-theme CSS overrides (46 !important declarations) from SlideOutToolPanel — dark mode now works automatically via Nextcloud CSS variables
  • Cache stat colors: Replaced hardcoded Material Design hex colors with NC semantic variables (--color-success-text, --color-warning-text, --color-error-text)
  • Missing panel props: Added wireframe, background-color, performance-mode, theme-mode, has-animations, is-animation-playing props to SlideOutToolPanel binding in App.vue
  • Clipping plane Z-fighting: Added 5% margin beyond model bounds so the slider at extremes doesn't cause Z-fighting artifacts
  • Bundle budget: Updated app chunk thresholds for new features (+12KB raw)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature2a2j5kFHSYZk5lKX7ByFRqNuCv8hg06X3uc2Mr98biRZSxeIgBug6aCdHONpN8oppv+6SDc5XrviS8HcLVFw1UEZvHjePdJYR8gri52Aa7ao7IfghYUHnoIwinqvWfP0sRod5vn3ZaKuo1pMpAHQlt/fkQCVz9eH6r+6TQTTBzBeAc7yWyEumArposNVcDqhgy0kK8tNWpNGYhpdqcMEvS7zGi0bl5EeK8k3ufaNaqwlzoXNwNMBiJ1aWFV2phxOxekkxlsHfRmGLkkdbPCOHg1Kj2CTLldHOjSQmQcUH9KOL1xkOZzuAC3Mh8SnrG/47h48uKIe62T7WYHgPbeOrDHvP8Z9d8S9V7kRq3puAonSPX/TFZt7j4YZoyar86w1MVakhT1mwiK3hBax+TCzxPRo63941VwID08MonJxW4D9C59MuF5qb/7UcDZ4Fm/TrSa0yvvKi+gy+gRCmBdzCnTg+WsEZW6Lotin80zckyH/+4wkUhUQBcq97ElIQKDwG5g9Bv6SGtQ2sR3zoUi7T/Wle4iqh38p5to6VMOlKfiBDiloGwpzXtHtlXDIZw8QqPL2h6gTQNdPizlovGLaZLup3sLL6MX7uyD5ZhbWz4kg1Qj4CqORSmpAanqJbBdUhLfA8dU2YhX5q7PFm8pMDAzKOq3h45kiSFI9K39f73c=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.0.0
Release Details
UpdatedApril 2, 2026, 7:40 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.6 → ^7.29.0
  • @babel/plugin-transform-runtime: ^7.28.5 → ^7.29.0
  • @babel/preset-env: ^7.28.6 → ^7.29.0
  • @playwright/test: ^1.56.1 → ^1.58.2
  • jest-environment-jsdom: ^29.7.0 → ^30.3.0
  • vue: ^2.7.16 → ^3.5.0
  • @nextcloud/vue: ^8.33.0 → ^9.5.0
  • @nextcloud/vite-config: ^1.7.1 → ^2.5.0
  • Vue 3 migration: Migrated app from Vue 2 to Vue 3
  • main.js: new Vue() + Vue.mixin() → createApp() + globalProperties
  • settings-personal.js: Vue.extend() → createApp()
  • viewer-api.js: new Vue() / $mount() / $destroy() → createApp() / app.mount() / app.unmount()
  • ViewerWrapper.js: New Vue 2 bridge component — Nextcloud Viewer bundles Vue 2 internally, so a plain JS wrapper renders in Vue 2 and creates an isolated Vue 3 createApp() inside for the real ViewerComponent
  • Removed @vue/vue2-jest (Vue 2 specific)
  • Nextcloud 34 compatibility: min-version 31, max-version 34 (@nextcloud/vue v9.x requires NC 31+)
  • Vue component imports: Migrated deep imports (@nextcloud/vue/dist/Components/...) to barrel imports (@nextcloud/vue) for forward compatibility with @nextcloud/vue v9
  • Template modifiers: Removed deprecated .native event modifiers from Vue components (compatible with Vue 2.7+, required for Vue 3)
  • @nextcloud/vue v9 API migration: Updated all form component bindings to Vue 3 API
  • NcCheckboxRadioSwitch: :checked → :model-value, @update:checked → @update:model-value
  • NcTextField: :value → :model-value, @update:value → @update:model-value
  • NcSelect: :value → :model-value, @input → @update:model-value
  • NcSettingsSelectGroup: :value → :model-value, @update:value → @update:model-value
  • Bundle budget: Updated index chunk thresholds in bundle size checker

Fixed

  • npm audit: Resolved dependency vulnerabilities via npm audit fix (#77)
  • npm audit: Applied non-breaking security patches, reducing vulnerabilities from 43 to 25 (42% reduction)
  • Lint: Fixed one-var error in useThumbnailCapture.js
  • Animation loop toggle broken: AnimationMixer.LoopRepeat/LoopOnce are module-level constants, not static properties — setLoop(undefined) made loop toggling non-functional. Imported LoopRepeat/LoopOnce directly from 'three' (useAnimation.js, useComparison.js)
  • Model load errors invisible to user: Variable shadowing in handleLoadError — parameter error shadowed the error ref, so error.value = error was a no-op. Renamed parameter to loadError, fixed logger level from info to error (useModelLoading.js)
  • Lights leak on re-setup: Vue 3 proxy wraps items in ref([]) arrays — scene.remove(proxy) doesn't match raw Three.js objects via indexOf. Added toRaw() for light/helper removal and instanceof checks (useScene.js)
  • Toast auto-dismiss broken: ToastContainer was mutating the toasts prop directly (setting progress/paused on prop objects), which triggers Vue 3 warnings and breaks in strict mode. Moved progress and paused state to local data() (ToastContainer.vue)
  • Mobile touch listener leak: setupPinchZoom() and setupDoubleTapReset() added document event listeners but never stored references for cleanup. Stored refs in eventListeners and added removal in dispose() (useMobile.js)
  • Settings page form controls not responding: @nextcloud/vue 9.x changed all form component props from checked/value to modelValue. Updated all bindings in PersonalSettings.vue
  • CSS nesting bug: .select-group-row rule was nested inside .setting-row braces — silently dropped in browsers without CSS Nesting support. Moved to separate rule block (PersonalSettings.vue)
  • Viewer registration errors silent: Both registerViewerHandler and registerViewerHandlerLegacy had empty catch blocks — any registration failure was invisible. Added logger.error() calls (viewer-api.js)
  • Loader errors invisible: All BaseLoader logging methods (logInfo, logWarning, logError) had empty bodies. Delegated to project logger (BaseLoader.js)

Technical

  • PHP CS Fixer: Blank lines before returns, doc comment whitespace, type-cast spacing, removed unused imports
  • OpenAPI spec: Regenerated with slicer and thumbnail controller tags and updated description
  • Three.js + Vue 3 pattern: shallowRef for single Three.js objects, ref for arrays, toRaw() when passing proxied objects to Three.js APIs
  • Vue 3 Maps pattern: Maps moved to created() hook as non-reactive instance properties (this._timers) to avoid Vue 3 proxy breaking Map.has()/Map.get()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCqMDLnrHd2ejY1wtQ+IUFnXE3ObdTMqRro2/QTWhZPmyh7h5lD2zlldE7hV6K2PLVbSbQA8RyylLA7chfBAfNhqrGVcv9N8oS4N8JZlvp2sx7uCnQJx9hA34xXsRvCNednaE0NuhE4zelLAkmDaJZEcQm0r0mQKkZAMSaIPgRg52ZFiEV+KFa7dyHlyLqc/Cp6doYyEeJxnyDiLOiCCsS0zyrt1qQlRyEgGcOocdeIPJRutmBwX3Sy/018v5midNblK+gleIzcCAFIOM7qDJKuf5ESnvQmqQko/7J+ahhAGzIdpGRHmKCVjAayZ2g3uLkT9dNQrBwKAaYvNWODkSyVEoBBU1wrucKcw237GMSjxHrVf/h5G64+Nb9/krmriBkdcpVupQ94wgyU9H5NO+BsBP/+3EfuuECRwOxXMW15taFrsiflxos1Fuhu5PQtyfOv4UPtUzn+uf4MaSzeLYdBHsOHfO6ZTjTPRbO5NLz5nlMw3F4PEgSKW7bk9brH7dEjzQzVG1RrDYCg75bGW+8l7/vtAmWc87s6otqoCj9rPVHQhrY7njOUcU8gwjdw0WuFTD//i2Vk0+agy7GO9WWfUdrbQ/i8Grq5AwoEgD3b0NLpPB9XxDCOR3yZQhSDbHNvgI8BRDp3Xe1FDv8izK2Dw9HwjykDDU+uMNZNtNpdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32

Nextcloud 32

3D Viewer 3.9.0
Release Details
UpdatedOct. 28, 2026, 11:08 a.m.
Changelog

Three new tools. Compare a model with an earlier version of the same file, side by side or as a see-through ghost over the current one. Print check tells you whether a model fits your printer, where it overhangs, roughly how much filament it needs and which walls are too thin, or, for G-code, what the slicer worked out. Record turntable makes a short video of the model turning, to download or save next to it.

Added

  • Print check, in the tools panel's Analyze section: see whether a model fits your printer's build volume (pick from common printers or enter your own size), which surfaces overhang past an angle you choose, and roughly how much filament it needs. For G-code files it shows the print time and filament the slicer worked out. A wall thickness check colours walls thinner than a minimum you set.
  • Compare a model with an earlier version of the same file: pick one of the versions Nextcloud kept, and see the two side by side with what changed, or the earlier one as a see-through ghost over the current one. Supporting files such as textures are loaded as they are now.
  • Record a turntable video, from the tools panel's Export section: one full turn of the camera around the model, 4 to 15 seconds at 720p, 1080p or your window's size, as the viewer draws it, with its shading, annotations and measurements (face labels aren't included). Download it as WebM (MP4 in browsers that can't record WebM, such as older Safari), or save it next to the model in your Nextcloud when you can add files to that folder.

Fixed

  • While a dialog such as Help or Send to Slicer was open, the tool shortcuts (R, F, M, S, P) and Escape still acted on the viewer behind it: a key could reset the view or take a screenshot behind the dialog, and Escape closed the tools panel along with the dialog. Keys now belong to the open dialog, wherever the focus is; ? still closes Help.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturerZEJPDWtDI4c/UZUEa/CwOYHXSyql3DtZoZiStkj8GahJ7fwXaLzAMFgvz16luTKgh/rekkOpfdoi+M9NVK8XV696vG6PUNB//y7dPbANbyAntQyDIVGmkTMtCTu4uMZrsCa227zTJecbKdsrHhiVCsFqWs2CcLM4Ih0UfNwzknlgXpQ21AM+L6FaNWFnrhEtURXWcSgQvwz/bcm2b+mb/Qp5khws78GIU3i/Z4a6LpMuhUJxHMQ8PjfNQyzTA1vr4ehd8UmSZJ6P4IcF1/qlXA3PkDaD2fhnELwdfy2s4Y5B74sX76I97AICTOehnoEEOIoMdU7aLeAfezuoZA6BSupy0dwp5JtyTzFdcFf3I7iMWTY/HXtQ90vXIs0uV2pg0oLPSPbsnXMZ6B6nUs3KWzma7aFppkWYTSCB4RXUEXZ5e6WA3WtGYMpos3SXF6TaFS0H0QTH74DXO0p9UXzuT04JP3VaFiK+B2lnHt6mz/YOKhDZ3TmJmmfe8yZZm1ail6F8jI/kVoePQ2X4JeTxkN6cijqp0aLhExXhuX5B1xNeUNuQMhOVTglfQMPSYwVc3OVcn5yO86j0gD72MWDUP6i9+SPM0V2N5av8i/ccnPwXLYBR6ygjsOHKsHoASesGxtmSZ628PrrsR9nRQg/c4mBMUmkAPqdrpWQpKWiB/U=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.1
Release Details
UpdatedOct. 28, 2026, 2:45 a.m.
Changelog

Thumbnails made in the viewer now show the model as its file has it, whatever wireframe, exploded view, transform or animation state the viewer was in, and exports from exploded view have the parts together. The top bar stays usable while a model loads, and every control in the tools panel works from the keyboard and is announced to screen readers. Textures named by a path above the model's folder, such as ..\..\textures\wood.jpg, are found beside the model. Every route that changes data now checks Nextcloud's request token.

Fixed

  • A thumbnail made in the viewer could show how one user was viewing the model, and it is shown to everyone who can see the file. The viewer makes the thumbnail from the model it has open, half a second after it opens, and several tools change that model: with Wireframe on, the thumbnail was a wireframe; in Exploded view, its parts were flung apart and framed small; after the transform gizmo had turned or stretched the model, it was drawn that way; while a clipping plane or box was open, the back of an open surface showed; an animated model was caught at whatever frame its animation had reached; and a G-code file opened with Single colour picked was drawn in that colour, where the same file got the gradient when Files made its thumbnail. Each tool now keeps a record of what it changed, and the thumbnail is drawn from the model as the file has it, as it already was for Clay, Normals and X-ray: no wireframe (a material the file itself makes wireframe stays one), the parts together, the model as loaded, the pose the file opens in, and the G-code gradient. The viewer keeps showing what you chose. Thumbnails already stored are not remade; one goes when its file changes.
  • Exports made in Exploded view wrote the parts where the view had moved them, so an STL, OBJ, PLY, 3MF, GLB or USDZ file, and the file Send to Slicer sends, had the parts flung apart. Exports now have the parts together, in every format; a model turned or moved with the transform gizmo is still exported as you placed it, and an animated model in the pose it is in. Exporting a GLB, USDZ or 3MF no longer drops a paused animation's parts back to where the file puts them.
  • With Wireframe on, the next model you opened was drawn solid while the switch still read on, and Exploded view stayed "Active" with the next model's parts together. Both now carry on to the next model, as the Clay, Normals and X-ray shading does; Exploded view turns off for a model it cannot take apart, and the camera frames the parts as they are shown. The model statistics are those of the model with its parts together, also when it opens exploded. Wireframe also reaches the parts of a model that have more than one material, which it skipped.
  • Switching G-code from Single colour back to Gradient tinted the gradient with the single colour, and a toolpath opened while Single colour was picked turned black on the switch. The gradient now comes back as it was.
  • While a model loaded, the loading screen covered the top bar as well as the 3D view, so Help, the Tools button and the view actions could not be pressed until the model arrived, which for a large file is the whole wait. The loading screen now covers only the 3D view, and the bar stays usable, with Cancel loading still on the loading screen.
  • Models whose textures are named by a path relative to the project rather than to the model, such as ..\..\textures\wood.jpg or ../maps/wood.png, opened untextured even with the texture right beside them, and got no thumbnail in Files. 3ds Max and other exporters write such paths in FBX, 3DS, DAE, X3D, OBJ/MTL and glTF files, and the app refused any path that leads out of the model's folder. Such a texture is now looked for by its file name, as one named by a full path already is since 3.8.0: beside the model, for background thumbnails and on public shares, and in the viewer also in the model's subfolders, such as textures or maps, where the viewer looks for any texture. The folders the path names are never followed, and a texture that is not beside the model still leaves the thumbnail to the viewer. Background thumbnails also read an MTL's maps relative to the MTL, as the viewer on a public share already did, so an OBJ whose MTL sits in a subfolder now gets its textures in its thumbnail.
  • A DAE, X3D or glTF model whose texture path has its slashes percent-encoded, such as ..%2F..%2Fmaps%2Fchecker.png or C:%5Cx%5Cwood.png, had the texture fetched but drew without it, and got no thumbnail in Files: the loaders looked for a file called by the whole path. They now find it by its file name.
  • A folder holding an OBJ or MTL file made largely of blank lines could stall the Files page while background thumbnails read it. The file is now read in a time proportional to its size.
  • The tools panel's Grid, Axes and Wireframe switches and the animation Loop switch could not be reached with the keyboard: Tab skipped them, and a screen reader read out neither that they were switches nor whether they were on. They are now real switches. Tab reaches each one, Space or Enter turns it on or off, a focus ring shows where you are, and a screen reader says "Grid, switch, on". They look as before, High Contrast mode included. The same pass named controls that a screen reader read as "×" or by a symbol (removing a bookmark, resetting a colour, stepping the animation), gave the animation and cross-section sliders names, and made the cross-section plane, box and axis buttons, the transform Move, Rotate and Scale buttons and the lighting presets say which one is chosen; in High Contrast mode the chosen axis and transform button are now marked. The T shortcut that opens and closes the panel no longer fires while a modifier such as Ctrl is held. Choosing a format in the Export section's format list no longer starts the export: moving through the list with the arrow keys used to write a GLB file with the first key, in Chrome and Firefox on Windows and Linux. The row is now a format list that starts on GLB and only chooses, plus an Export button that exports in the chosen format.

Security

  • Four actions could be triggered by another website while you were signed in to Nextcloud, because their routes skipped Nextcloud's CSRF check, the request token that proves a request came from Nextcloud's own pages: sending a model to the slicer (which uploads a temporary copy of it and makes a share link for that copy), deleting that temporary copy, re-indexing your 3D files, and saving the viewer's settings. Your browser's SameSite cookies, and the preflight a browser makes before it sends a PUT or DELETE from another site, stopped most of this, but they do not cover every case, such as a request from another page on the same site, so these routes now require the token like the rest of Nextcloud. The viewer sends it itself, so nothing changes in use; anything outside the viewer that called these routes with a session cookie and no token now gets a 412 and has to send the requesttoken header. Saving the viewer's settings also accepted any setting name and value; it now accepts only the two the viewer stores, the file-browser sort and the selected file, and refuses the whole request if either is unknown or invalid.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureIm8PqU0r/Nr8aRKa1jh6a+lsP8E3b6u1GCxKzIqtb1C4vUZNgKeSdD/it/SABY5O0GBwso8NdFBD3XHV5jgg/yriggt78tRHo7pvVEo/J4CyRhKEWNRChjWfoOMuxRZF5sBJCOacuCQoMyofcHBCSIPjDils6gZ2CW5H3xZ0atGx5hgjcmQKS7TGxaq/3tymVcGmgl17DNbiSULcfFeIL9b0uBQs0HpLzIBGskp0dffLean9atDu5WAzm8CB9ASVCl+m2DyWkE0mcgQpFKDijWCohaNJXKA+P37KSnBOZ1zuQbDV+8B49hoKQr4hxUgfKhM/tVrbZ/F0Fo2FerTrLpi+H1eWYFvPFXicooi51yLVi2vn+OA8cwBcIrvViUCNiYiI0luZOzNiYRGk17WrZWow7suGL810YJk5eGzkEIj5HQ7dsSoU9Alaz6ubEFRc/Fqw4A0wOjXJIcFm3Gw2uFkjRGA7hUIY0Bhbnwsahi1K2DQMv1nr/pUZKwYK1mTsxJjE2+UwPV6ayLxk2+aMSDPxtNYG/fJrEoCeNUaDTljxsT3P8/EXko8/6e6teSUHTKDBFNBM52HSt7Mzl82R/9bBvXHLCmJk8fg1Td/+QebTbtqW4u1RUio946WlErrk75Ku/WmvhDH4RKeyGWdvJz1j2BKYSzxvk2YHAlEMrBA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.0
Release Details
UpdatedOct. 27, 2026, 4:59 p.m.
Changelog

New inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ. Measurements, statistics and exports now use each model's real unit, so a glTF figure measures in metres and an STL part in millimetres. FBX readings change: they follow the file's own unit, centimetres by default, so most FBX models read 10× larger than in 3.7.x, which is their correct size. Textures a model names by a full path from the machine that exported it, such as C:\…\wood.jpg, are found beside the model, and a texture that is not one of the model's files is never requested.

Four inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ.

Added

  • Angle measurement. The measurement panel has a Distance / Angle switch. In Angle mode, three clicks measure the angle at the second one, in degrees, and changing the length unit leaves it alone. A second click on the point just picked, usually a double click, is not taken, since it would leave the angle without a side. Angles are shared like distances: everyone who can open the model sees them, with their author, and anyone who can edit it can delete them. They are stored as a note type of their own, so a viewer older than this one skips them instead of drawing them as a distance. The annotations JSON export includes angles, and import restores them, whichever mode the panel is in.
  • A layer slider for G-code, in the Analyze section of the tools panel, for files with more than one layer. It shows the print up to the chosen layer, with that layer's height, and "Only this layer" shows that layer alone. A Z-hop counts as part of its layer rather than as a layer of its own. Each step changes only the layers it passes, so dragging across a vase-mode print with tens of thousands of layers stays smooth. The slider and the switch work from the keyboard.
  • Clay, Normals and X-ray shading, in the Scene section of the tools panel. Clay shows the shape of the surface in each part's colour, Normals shows which way each face points, which makes dents and flipped faces stand out, and X-ray makes the parts see-through to show the inside of an assembly. The model's own materials are not changed: exports use them, Send to Slicer included, and so do thumbnails, so a model opened while X-ray is on still gets a normal thumbnail. The shading stays on when you open the next model.
  • Export to 3MF, PLY and USDZ. 3MF, which slicers open, is written with Z up, and its parts are closed solids, so a slicer does not have to repair them. PLY is binary and keeps vertex colours. USDZ opens in AR Quick Look on iPhone and iPad. Both 3MF and USDZ come out at the model's real size: glTF, IFC, COLLADA, X3D, VRML and dotbim models are taken to be in metres, Rhino, 3MF and FBX models in the unit their file says, and STL, OBJ, CAD and the other formats in millimetres. A model with no surfaces, such as a G-code toolpath, gets a message instead of an empty file. The exporters themselves load only when an export starts.

Fixed

  • Measurements and model statistics took every model to be in millimetres, so a 1.8 m glTF figure measured "1.800 mm", and its surface area and volume were a million and a billion times too small. They now use the model's own unit, the same one the 3MF and USDZ exports use: a distance, the bounding box and Copy Measurements read in metres for a glTF, IFC or COLLADA model, and areas and volumes follow as square and cubic units. STL, OBJ, STEP and the other millimetre formats read exactly as before, and angles are unchanged. Both unit pickers now start in the model's own unit (metres for a glTF, millimetres for an STL) until you pick one. Your pick is kept as you open other models in the 3D Viewer app, and is not saved: reloading the page starts again from the model's own unit. "Generic Units" shows the numbers in the model's file, unconverted. The comparison table converts each model from its own unit, so a glTF and an STL of the same part compare as the same size. Shared measurements are stored as points on the model, so they need no change and show the right reading.
  • 3MF files written in a unit other than millimetres opened at the wrong size: three.js reads a 3MF file's unit and never applies it, so a file in metres was a thousandth of its size, measured wrong and exported wrong. The unit (micron, millimeter, centimeter, inch, foot or meter) is now read from the file, so measurements are right and a metre 3MF exports and opens again at its real size. The model is shown as before. Only the start of the file's model part is read for it, and in linear time, so a crafted file cannot stall the page.
  • FBX readings now follow the file's own unit, UnitScaleFactor, which is centimetres when a file does not say. In 3.7.x every FBX was read as millimetres, so every FBX model, small ones included, now reads 10× larger than it did (more for a file in metres or inches): that is its correct size. FBX models larger than 50 units also measured and exported at a fraction of their size, because the viewer shrinks such a model to fit its lighting; that is now undone for measurements, statistics and export. FBX models are shown exactly as before.
  • OBJ models larger than 1000 units, a metre for an OBJ in millimetres, measured, reported and exported at 1000 mm whatever their size: the viewer shrinks such a model to fit, and the shrinking was not undone. A 5 m OBJ now reads 5000 mm. Such models read differently from 3.7.x, and correctly; they are shown as before.
  • G-code toolpaths drew the travel moves the parser leaves out. Each layer was drawn as a single connected line through all its moves, so a line joined the end of each move to the start of the next. Layers are now drawn as separate moves.
  • Closing the measurement panel with one point picked left that point's marker in the scene. Markers were matched to measurements by their position in a list of pairs, so every later pair was off by one, and deleting a measurement could remove another one's markers. Each measurement now keeps track of what it drew, and an unused point's marker goes when the panel closes or the mode changes.
  • Send to Slicer's PLY option always sent ASCII PLY: the binary option was passed where the exporter expects its callback. It now sends binary PLY.
  • In Windows High Contrast mode, the tools panel's switches (Grid, Axes, Wireframe and the others) showed no difference between on and off, because their track was filled with a colour that the system palette replaces. They now have an outline, and a switch that is on is filled with the highlight colour.
  • Models whose textures are named by a full path from the machine that exported them, such as C:\Users\anna\textures\wood.jpg, file:///C:/textures/wood.jpg, \\server\share\wood.jpg or /home/anna/wood.jpg, opened untextured even with the texture right beside them, and got no thumbnail in Files. SketchUp, 3ds Max and some Blender exports write such paths in DAE, FBX, 3DS, OBJ/MTL and X3D files, and the app took them for files that are not beside the model. The texture is now looked for by its file name in the model's own folder, in the viewer, for background thumbnails and on public shares. This also covers glTF, an FBX that names its texture only in a video's Filename, and a material map written after options such as -bm 0.3. The folders the path names are never searched, and a texture that is not beside the model still leaves the thumbnail to the viewer.

Security

  • Opening a DAE, 3DS, glTF or VRML model in the viewer requested any texture the model named that was not one of its own files, at the address the model gave. A texture written as https://…, or as a Windows network path such as \\server\share\x.png, which the browser turns into http://server/…, made your browser contact a server the model's author chose, and only Nextcloud's Content Security Policy stood in the way. Such a texture is now never requested: the model is drawn without it. Thumbnails made in the background already refused these.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCmm5ayL+RHBF5+iiN7ZD5hk7WWM1pgz+KPfzKS6H+3ZnGg8ZyORrq7C6H+eaNxd54PoTbGonDOSIomtizZ/I/udluAu1lRW0ZA6ZS4olC6t+iylmvWU2lc5CaCLqBSXHt7+uE5RSSHbXCZl1MdYWlyYQkJo+SVPCbupSBQu/EenOXfNOMsBJhjdtyGmNzTOhqil+IGmPGRf+YiPa9cW9X0ll+sreqKvbICTxtrfw0OhEbA801v1R2ecXns2RL1iqiWxn/gGcAM4ibCEd+RdsrdUgs3QKSBBgtsZEzJNYKDeH15WoPGHnThDY13aVv7Qv446rHYcn8V3ZIk/vR4Znu0EVLPoRHW6ltJppSi0aeNE62WWCd5KWNfY0Q0K5oykwbtl2szFx+AEYgvbRdRFyOylExNZUcoigYFmlkrQLvPTKtvivYVUi9NIQIbCgSHHygjZEdWy1otFuRs9+W7pqtqH++SJ0cXwZp/ruIikfAJ3/JnQdAuNlWelNErjj2DJVa7cScS8zdiYINzXoToPnDYG9vrNTftnYNwTEvLOQBaHFVDBEC/XdwnPk0ZqEXseJMid632xpWxLdQny2nuVnzjXkG3Nw5klYZ1wWbKOzwUuRHKWatOCJxRiQIXlIs07SgNqV2HlcelYUJ9GG5pX8R21zQBbEKqAsRrT/0WsEtBw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.1
Release Details
UpdatedOct. 27, 2026, 1:58 a.m.
Changelog

3MF, AMF, DAE and X3D files now get thumbnails while folders are browsed, like every other model. FreeCAD files show only the part FreeCAD itself shows, instead of every step of its history on top of each other.

Added

  • 3MF, AMF, DAE (COLLADA) and X3D files get a thumbnail while you browse Files, like every other model, instead of only once opened in the viewer. Their loaders read XML with the browser's DOMParser, which the background worker does not have; the worker now loads its own XML parser (@xmldom/xmldom) for these formats only, and the viewer keeps the browser's. The parser fetches nothing a file names, such as an external DTD or entity, and expands no entity a file declares, so a crafted file can neither make a request nor multiply itself. Parsing still takes memory in proportion to the file, about 31 bytes per character for a 3MF mesh, so XML longer than 16 Mi characters (roughly a 250,000-triangle 3MF, measured unzipped) is not parsed in the background: the file is skipped for its version, like one that runs out of time, and gets its thumbnail when opened in the viewer. As with OBJ, glTF and FBX, a DAE or X3D texture the file names is downloaded with it, and a file whose named texture cannot be found is left for the viewer rather than given an untextured thumbnail; a 3MF carries its textures inside it. Only .nc and .g files, which may not be G-code at all, are still left to the viewer.

Fixed

  • FreeCAD files (.fcstd) showed every step of a part's history on top of each other, in the viewer and in their thumbnail: a PartDesign part with a Pad and a Pocket drew the Pad's uncut solid around the finished part, and the tool the Pocket cut away sticking out of it. FreeCAD saves a shape for every feature and shows only some of them; the loader drew them all. It now draws only the objects GuiDocument.xml marks visible, and nothing inside a hidden Body or App::Part, which FreeCAD hides as a whole while their members stay marked visible (the tips of two Bodies combined by a Part Cut, for one). A file saved without it (by FreeCADCmd), or one where nothing visible can be drawn, is drawn whole as before. In FreeCAD's own PartDesign example this is 1 solid instead of 18; in its Part workbench engine block, 10 shapes instead of 35.

Security

  • Finding the texture and material files a COLLADA, X3D, OBJ or MTL upload names took time that grew with the square of the file's size, so a crafted upload of a few megabytes could keep the server busy for minutes. These files are now read in a single pass, on the server and in the browser.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5P5xxH9yvWf8Au302k0c7SEVJMaiS+KTOElYVIGw4KinRBHTSr3Lc/tmNF0aTDJaecbjcKuz5zrzimSYyQhtuWYIjyBqDew8qfJRoek/6+rycT2u7zvYyzyjmuWGHA+Wns13LLoCk6GsRxeya03ZyWvdwfyJ5eCGmJfAFN+ukZl3VTUmeADIqaQbKiYnHzj3lYQljacH3x0ebkFQ8Q1TPbCcRrUTjEVOAheWM3g7k5knbYstBo0EGUCr84TtsNvyenqCnr0uH8g10NPBt+mIaQRIWlK2xua6RoPtCGY33JmoGVzOGppM8rhyaVQ40wCdY2Sp1Mm+itA1ViyqjF/OcumS4LL5bAE+J0Y5J+4zzCELNWPQIOXGaA6X+YwWXEEN8Q+TB5UlqNOqyUliRVa3JsXcnmfUpwBRQPGEYe5UN48bXEUNRzodZ9J97SclDVApc8wayuf7kZNe3q8HOkjj8euEUXw1QZeSSfPv1cFWm0iy72SYqlrBfylMs4+L32u0B3nloFACcrgVgi0+erNSNGyzKJS2AwwFLXo1MEwTT6IGAx2Lmquq5sT427HDavOlwRpsAeGQ5IwZF+2rxJyhAYl+PkLHCeHH7UwyjHteTxbIYmsUSwzSjPd81sVuI/O/UUfFVhAQbH2etMRBSdQj19Ub0+frTScX32NKmmvYjG8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.0
Release Details
UpdatedOct. 26, 2026, 11:33 a.m.
Changelog

Models get thumbnails in Files without anyone opening them: the browser of whoever is viewing a folder renders them in the background, within limits each user sets. A thumbnail is kept once per file version and shown to everyone who can see the file, including share recipients and public links. VRML files, FreeCAD files saved by FreeCAD and FBX files with embedded textures open correctly again. Existing thumbnails turn back into icons after the upgrade and are made again as folders are browsed.

Added

  • Thumbnails are made while you browse Files. Models in the folder you are viewing get a thumbnail without being opened: one file at a time, while the browser is idle and the tab is visible, and not while Save-Data is on or a model is open in the viewer. Folders without models load none of this code. Each model is rendered in its own Web Worker, which is stopped the moment it runs out of time, so a large CAD file never freezes the page. Personal Settings → 3D Viewer → Thumbnails has the new options: "Create thumbnails while browsing Files" (on by default, and separate from making thumbnails in the viewer), "Skip files larger than" (50 MB), "Give up on a file after" (30 seconds) and "Download at most, per folder visit" (500 MB). A file that runs out of time or cannot be read is skipped by this browser until the file changes or you press "Retry skipped files"; network and server errors never skip a file, and a browser that cannot render in a worker downloads nothing at all. Only the companion files a model names (materials, textures, glTF buffers) are downloaded, and only while they fit in the folder budget, so a model whose textures are not named gets an untextured thumbnail. A model is never rendered without a companion it names, since a thumbnail is kept for the file's version: one that does not fit in what is left of the budget waits for your next visit to the folder, and one that cannot be found beside the model (or at the path it names) is left for the viewer, which looks further, to make the first thumbnail. In Favorites, Shares and Tags, thumbnails are made once you open a folder, not on their top-level lists. Only files you can edit get a thumbnail. 3MF, AMF, DAE and X3D files, whose loaders cannot run in a worker, and .nc and .g files, which may not be G-code at all, still get theirs when opened in the viewer.

Changed

  • One thumbnail per file version, shared by everyone who can see the file. Thumbnails were stored per user, keyed by file id alone, so each user made their own and an edited file kept its old picture. They are now stored once per version in the app data (thumbnails-v2/<fileId>/), dropped when the file is edited or deleted, and the old per-user thumbnails are removed on upgrade. Models that showed a thumbnail before the upgrade show their file icon again until a new thumbnail is made, by browsing their folder or opening them.
  • The viewer captures a model's thumbnail with the same fixed look as every other thumbnail (transparent background, default lighting and camera framing) instead of a crop of the live canvas, and only once per file version: it asks the server first and skips files you cannot edit, files that already have a thumbnail for their current version, public shares, and opens that took a material or texture from the browser's dependency cache, which may be older than the file. It now covers every format the viewer opens, not a fixed list of 12.
  • The CAD runtime chunk is 63 KB instead of 776 KB. The OCCT glue's require("crypto"), reached only when it runs under Node, pulled the whole browser polyfill for Node's crypto into the bundle: crypto-browserify, bn.js, elliptic, asn1.js and readable-stream. crypto is no longer polyfilled, which also removes the vm-browserify eval warning from the build.
  • The GLB, STL and OBJ exporters load when an export starts instead of with the viewer, taking 40 KB off the main chunk. The slicer's dynamic imports of the same exporters had no effect while the export composable imported them statically.
  • webdav 5.10.0 → 5.11.0 and fast-xml-parser 5.10.1 → 5.11.2, which drop a duplicate key the build warned about.
  • "Retry skipped files" replaces "Clear thumbnails" in the thumbnail settings. Thumbnails are now shared, so clearing them would have removed other people's too; a thumbnail goes away when its file changes instead.
  • roave/security-advisories is no longer a dev dependency. Its latest release declares a conflict with every PHP below 8.2.35, PHP 8.1 included, so Composer could not resolve the dev tree for the app's minimum PHP and every PHP job in CI failed at install. Advisories are still checked by composer audit in the security scan.

Fixed

  • IFC, FCSTD and G-code files never showed a thumbnail in Files. The preview provider registered for model/* MIME types only, and these three have application/x-step, application/x-fcstd and text/x-gcode. It now registers for the MIME type of every format that gets a thumbnail.
  • Thumbnails never showed to share recipients or on public links. They were looked up for the logged-in user, so a recipient saw their own (usually none) and a public link, with no user, saw none at all. Anyone who can see a file now sees its thumbnail.
  • FreeCAD files (.fcstd) saved by FreeCAD failed to open with "FCSTD archive contains no .brep geometry entries". FreeCAD stores each body as a .brp file inside the document, and only .brep was read; both are now.
  • VRML files (.wrl, .vrml) spun for 30 seconds and then failed to open in the viewer. The loader waited for a callback three.js's VRML loader never calls; it now uses the scene the parser returns, so they open.
  • FBX files with embedded textures opened untextured. The images the file carries were looked up by name among the companion files, matched nothing, and got a blank placeholder; they are now used as they are.

Security

  • A model rendered in the background loads only the files it was given. A texture or buffer it named but that was not among them was requested from the URL the model wrote, so a model in a shared folder could make the browser of anyone browsing that folder request an address of its author's choosing. The worker now refuses every URL except the model's own files and the app's decoders, before any request is made, and skips the model.
  • Storing a thumbnail requires the request token (CSRF protection) and edit rights on the file. The upload route opted out of CSRF checks, and anyone who could read a file could set its thumbnail; read-only share recipients now get 403. The browser also sends the version it rendered, which it learns before it starts downloading the model, and an upload for a version that is no longer current is refused, so a thumbnail can never be filed under a newer version of the file.
  • Dependencies updated within their existing ranges to clear npm audit: follow-redirects 1.16.0 → 1.16.2 (credential headers kept across cross-origin redirects), reached through axios's Node adapter, which the browser bundle does not use, and vite 7.3.6 → 7.3.7 (dev-server advisories). Dev-only fast-uri, brace-expansion, js-yaml, moment, pbkdf2 and qs move to patched releases as well.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureHW+gyQT9pWQ7mEMg5wkO6BOlN33tSE+lpY33kDjIvJ0XIvGh3JY9P7KW64TxfSUb5xG+sArQE8CsPqTDbwGjKPy+sMK+iPQrKdbIMKA/xmrW6ISonhqxt1IN9siV0EiPhGVlLsigL76kuWbzAzaxiHiISofv79xZt2hR0YwD/vHqn3IRNQXREf7gtSnNDGXF5HmQVELaR43ccFlJK4husCsCGSxnQ0Fdbt6I3OZd2+bp1l53cG2EVOTbNFn5Azc4krpiy1jP8oNO6PVKveZPHObTBiXqYMNm6seHkEuATm0vw+aq5F9u/HCVitQyzXo+0oANQFoYW1+FqtWT1ueSgTaz4++lsn5Z5b7aDtubDWISj5AJL/kERXdfPvux6klusP4UPb1YP1yLkO/tWBQWn3is4SsSvC9f3UGBTKch/IEAcHjnL2c5PSqml491KCHF3/4jqQUu8ozKuG6n/09yImtQDmBvuyGlEuBCZT4rANZ8Pr9Q29zbDkFqQCNR/qe+tveEQzKDKO0ECaDp4bjxydNvnff3+m6ceWp5/KpHXLUfs228XoJqBVuyY5jI3+i/ltEnXYp8l3coQFBVKmKiLvuE8aGbP92EmTZXc1sv2Q8RHBSMt3Mxd/78cMIF4pMYjGsKOIHWl4R9hvctcntXkiB4BD2ALVTCZ2ypvBihqjE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.6.0
Release Details
UpdatedOct. 19, 2026, 10:51 a.m.
Changelog

Annotations and measurements become shared notes that everyone who can open a model sees, and the app supports Nextcloud 35. CAD, BIM and 3DM files open again on App Store installs, Rhino 3DM files open at all, and the Favorites view lists starred models.

Added

  • Annotations and measurements are shared with everyone who can open the model. They were saved privately per user, and measurements were not saved at all. Now each model has one set of notes: share recipients and public-link visitors see it, and anyone who can edit the model can add, change or delete notes. Each note saves on its own, so two people editing at once never overwrite each other. Each note shows its author, except on public links. Existing private annotations move into the shared set the first time their author opens the model, if that user can edit it; otherwise they stay visible to that user only, marked "Private, not shared". Notes follow a model through moves and renames within the same storage; moving it to another storage, such as a group folder or external storage, gives it a new file id, and its notes are removed by the daily cleanup.
  • JSON export includes measurements, and import restores them.
  • Nextcloud 35 support. The app declared max-version="34", so the updater listed 3D Viewer under "Apps missing compatible version" and an upgrade to 35 would have disabled it. Checked on 35.0.2 by upgrading a 34 instance in place: the app stays enabled, and models open from the Files app, the standalone viewer, the file picker and public shares, with nothing logged. Nextcloud 35 types the schema and file APIs more strictly, which surfaced three changes: the folder_path migration calls modifyColumn(), the only form 35's ITable declares (changeColumn() was a deprecated alias of it in every supported server); the slicer download endpoint no longer treats a folder ID as a file, which deleted any folder older than a day inside its temp directory; and the temp-file cleanup job skips a non-folder at the temp path instead of failing on it.

Changed

  • three.js r185 → r186. The Babel config moves from package.json to a project-wide babel.config.json: r186's CommonJS entry only re-exports the ES module build, and a package.json config never reaches files inside node_modules, so Jest could no longer load three.
  • Babel 7 → 8 for the Jest transform. @babel/plugin-proposal-object-rest-spread, @babel/plugin-syntax-dynamic-import and @babel/plugin-transform-runtime are dropped: no config referenced them, and the first had no Babel 8 release. Babel 8 needs Node 22.18 or later, so engines.node and .nvmrc move to 22.18.

Removed

  • The /api/annotations/{fileId} endpoints, replaced by /api/notes/{fileId}.

Fixed

  • STEP, IGES, BREP, FCSTD, IFC and 3DM files never opened when the app was installed from the App Store, failing with both async and sync fetching of the wasm failed. The loaders fetched their WebAssembly runtimes from /apps/threedviewer/…, which exists only when the app sits in the shipped apps root; App Store installs land in the writable root, custom_apps/ on the official image. The OCCT, web-ifc, rhino3dm, DRACO and Basis paths now resolve against the app's actual web root, which also covers instances served from a sub-directory (#188).
  • The file browser's Favorites view was always empty. The controller read favorites through OCP\Files\Tags\ISystemTagManager, which does not exist, so Nextcloud injected null; it also looked for system tag 10, while favorites are per-user tags. It now reads the user's starred files from ITagManager, and the browser renders the flat Favorites list, which no view state reached before (#205).
  • Rhino 3DM files never opened, for three reasons. The published app shipped rhino3dm.js but no rhino3dm.wasm: the wasm is git-ignored and its old source in three.js's examples/ no longer exists, and the copy step skipped missing files silently. The runtime now comes from the rhino3dm package (8.32.1, the version three's loader names), and the build fails if either file is missing. Its emscripten glue compiled code with new Function, which Nextcloud's CSP blocks; it is rewritten as it is copied, the same repair #169 made for the CAD runtime. And a worker whose runtime failed to start never answered, so the viewer spun with no error: the decode now has a deadline, worker errors are reported by their message, and each load terminates its worker (#204).

Security

  • Production dependencies updated within their existing ranges to clear npm audit: vue 3.5.41 → 3.5.44 (@vue/server-renderer XSS), axios 1.19.0 → 1.20.0 (prototype-pollution gadgets), brace-expansion 5.0.9 → 5.0.12 (ReDoS), source-map-js 1.2.1 → 1.2.2 (event-loop DoS) and DOMPurify 3.4.15 → 3.4.17 (IN_PLACE sanitizer bypasses).
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewgF9SWWbzLH22o0y9lzTkAUX33vlRdY37uuPVanE2nMuer3V8gTzxkFnfGn2m6iXOWfHvlTAM1xMXbMIiFOCvTTw5rA2GMq+T+QVh8kAyeDAOtQVU7Rr0J0+x2PK0HNkmwlJstOc7xGiYEiVLXEu6mEKbb99Q/o7xlkUsgvmAuK6Xxu1EZvWJi30bx8pJwJC3jbRMaZX5CRWYpoPkeoOCqglgXTm/j8wCqNP8W4ofmWZlOdqM5VQoApIv3Iv9Bvtp7lzau+r+saUNcgShEHuMhiODLeWDdznd5XUrQNLp7yLiYM2tk/brB04F9qIL0nr1BJtA1qhOe2nlIHtmvyp1Yy8bEryIPLKgWHubSyMYV8qUt5pJP3b5ZOmlDfNYBpru1mNJVNFOPlEyWw/74lRAWRadFY3dOASyyUfzcr0rMraMcvKg3Kg881XEAPiizNfTCHi5trQR4+gfbd7e42gT2Z5KGuZLGllAntMzP9B/ZwMl5VuFZLaWJNosjkOdry5TRH8ZOn1hyEj+vP4fdi+a81Wcq2yRjuER+LG6SL3LKkVIjXLCnat1N0nEzxOfPEi3VPNDy6jNJhoX8aaDAQJklXFc/TcD7+6MwLXR/ZXLlxRZ7dvpu/tO3WQF75kxCo94Z6fILOS5EhjDf0JZb6iFscELiwK9R7A8XaEiqHKFY8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.5.1
Release Details
UpdatedSept. 21, 2026, 6:09 a.m.
Changelog

CAD models open again on Nextcloud 34: STEP, IGES, BREP and FCSTD no longer need 'unsafe-eval', which the server stopped letting apps request.

Changed

  • @nextcloud/vue 9.9.0 → 9.13.0 and @nextcloud/dialogs 7.4.1 → 7.5.0.

Fixed

  • STEP, IGES, BREP and FCSTD files failed to open with EvalError: call to Function() blocked by CSP, and no page let the viewer compile WebAssembly at all. The OCCT runtime is rewritten at build time to stop compiling its bindings with new Function, and the app now adds 'wasm-unsafe-eval' — never 'unsafe-eval' — through Nextcloud's additive CSP event, so CAD and IFC models open from the Files app without an administrator editing the policy (#169).
  • The release workflow reported success when nothing was published. The app store step was continue-on-error, so a failed publish went green at the step, the job and the run — v3.5.0 was tagged, built and released with nothing on the store and nothing anywhere to say so. That flag is gone, and a step after it asks the store whether it is serving the release, checking the platform range and download URL as well as the version.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureRJMTtOs8itpPTgMandBOx/fB3Lb4eQhNqh82AL50L6dsquHrSy1TrIOYVtn0rZV50Zwy23N2wGiC8yP450hkEYtMG4Hqmb3RRijNvGdioBpHryA7XyF2II7sx13g9ebXTkk6Aq4qq82sTVe8LtqElZfjD7mshN8eRrrQDMCh7xgly6dBOkyagZqowByvVaiL093OVqO3qUCCGfrWslIB17NtM+8BreXIrsLn/fiQe67uBpumOD6hzEYaWwxqYNrLoejUfZWsShZnvUa5mUG5v1mquxXDUGUswb757OYsDq1Yg3KEU3mv4yYZHmigJfjnHZ+n17j9sWt0g6xXI8YXAY5t2cxx91ll9WQGeVddYZIJjseJodhAV5fCe1V87Ks+pdVYPdXhhdDMQGbFBacnFM+BRRuN7yP3QIpBM1re1qKRcIBDTl3oSUqnCQJ9vLG6im+NxvUYQ5y2VfETyRlQdJPo1j+WToO4dhpI3KHSYpU42HW+T1YvPlmpsosrxanIEnqNzl8uqvjzQibww0A/uIHMbKLsp5nGblQRhlbeLzeZvQs5C28K9Ux9PvNpCEIdXsyYwhB7PHXuu/SYzcsBWR1Zu3i+/cV0SywOER8qSltd+KQuvBXuAveGUqZhRe0zOJStOs90w2rcdHe32BIAJ2GSOdYA+uDnkdY6k26qTAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.5.0
Release Details
UpdatedAug. 11, 2026, 3:17 a.m.
Changelog

The viewer is redrawn on a themeable design system: the Theme control now moves the whole viewer rather than only the scene, every control is a Material Design icon instead of an emoji, and the top bar, tools panel, slicer dialog, performance HUD and the measurement and annotation surfaces follow the design sheet. Also carries three.js r185, the move to ESLint flat config, and the security fixes listed below.

Added

  • Keyboard shortcuts for six tool rows: R reset view, F fit to view, M measurement, S statistics, P screenshot, ? help.
  • The Playwright suite now runs in CI.
  • A browser check that the design system's tokens resolve to real colours.

Changed

  • Three.js r184 → r185.
  • The live loader check covers OBJ, glTF, Draco-compressed glTF, PLY and STL instead of two app-owned parsers.
  • "Open in 3D Viewer" is the mockup's outlined pill, with an open-in-new icon and a tooltip saying it opens a new tab.
  • The tools panel's sections are rows separated by hairlines rather than nested cards.
  • The dark palette comes from the design sheet, with separate tokens for the accent as a fill and as text.
  • The viewer's Theme control themes the whole viewer rather than only the scene background and grid.
  • The slicer dialog follows the design sheet: 560px, one segmented format control, a lighter backdrop.
  • The performance HUD and the model statistics panel follow the sheet, both drawn as canvas chrome.
  • Every control is drawn with a Material Design icon instead of an emoji — 154 across nine components.
  • The tools panel is redesigned as a floating card with collapsible sections.
  • The viewer's top bar is redesigned: a 56px header, the filename centred with its format, an FPS pill, and Tools as the one primary button.
  • ESLint moved to flat config, for @nextcloud/eslint-config v9. New rule families are off and newly-promoted rules are warnings, each carrying its count in the config.

Fixed

  • The antialiasing setting did nothing — the renderer hardcoded it on. It now follows the setting, applied at mount.
  • The help dialog dimmed the viewer and left the rest of the page lit. The four fixed overlays are teleported to the body, past the containing block Nextcloud's backdrop-filter creates.
  • The help panel drew different icons from the controls it describes; eight rows disagreed.
  • The controller rail's full-width controls sat seventeen pixels left of centre.
  • Every NcButton rendered as secondary whatever it declared: @nextcloud/vue 9 moved the appearance from type to variant.
  • Four controls were still drawn with an arrow character instead of an icon.
  • Nextcloud's navigation toggle looked disabled on the viewer's top bar.
  • The browser fixture rendered the whole viewer into a zero-width column, so every geometry it measured was of that.
  • A tools panel section header turned pale blue with grey writing on it under the pointer.
  • The slicer's format selector rendered as three chips floating in a track.
  • The 3D controller was a white ring on a white scene.
  • The Send to Slicer dialog carried a hand-written dark theme that fought the tokens.
  • The per-slicer Open buttons were drawn in each slicer's brand colour, down to 1.78:1.
  • The browser legibility sweep mis-read every color-mix() surface.
  • A theme chosen in the viewer was forgotten on the next load.
  • The viewer's header kept its white text into the light theme.
  • The measurement and annotation panels' close buttons did nothing, and no tool row ever showed it was active.
  • An annotation's note could not be edited.
  • Label text rendered stretched.
  • A measured point landed on the transform gizmo's picker plane rather than on the model.
  • The measurement reading was 1% of the model's height.
  • The markers drawn into the scene were still the debug palette.
  • Every label was a wide black bar with a few pixels of text in it.
  • Taking a measurement filled the viewport with flat green.
  • The measurements and annotations panels had never been redesigned.
  • Switching measurement mode on produced no visible change — the panel opened underneath the tools panel.
  • The measurements panel only appeared once there was something to list.
  • The tools panel never announced the state it restored on load.
  • The export format control and the slicer's format control had labels that labelled nothing.
  • The viewer drew a light card and a dark card on the same rendered model.
  • The performance HUD's mode chip was a pale blue pill with near-black text on a near-black panel.
  • Half the model statistics panel was white text on a white panel.
  • The design system had the status colours backwards: the unsuffixed token is now always the foreground.
  • Every button in the viewer lost its colours the moment it was used.
  • The ground grid was fluorescent green on every theme, and the setting meant to control it did nothing.
  • The viewer's buttons were the wrong shape on every real Nextcloud page.
  • The top bar crashed before a model was chosen.
  • A "Model loaded" toast covered the Tools button on every page load.
  • Ten style declarations had stopped applying, from an rgb() bracket a token sweep left behind.
  • The watertightness badges were pale green and pale amber on white, at 1.9:1.
  • The bundle size check picked which file to measure by the shape of its content hash.
  • obj-mtl.spec.ts had been failing since before 3.4.0 with nobody to see it: its Nextcloud stub was missing OC.filePath.
  • --color-main-text was written with five different fallbacks, --color-border with eight. All 224 references now go through the tokens.
  • Dark mode asked the light theme what colour to be.
  • A help icon's background tint never rendered — it mixed from a variable Nextcloud does not publish.
  • The dark theme never reached the toolbar, the viewer or the toasts: ten rules keyed off a class the app never sets.
  • The slicer dialog followed the theme only when opened from the standalone app.
  • The viewer asked Nextcloud for the wrong primary colour in twenty-five places.
  • --color-primary carried three different fallbacks, so an unthemed instance rendered whichever rule an element landed on.

Security

  • Cleared three advisories from the production dependency graph, lockfile only: dompurify 3.4.13 (GHSA-55q2-fjhq-7xh7), nanoid 3.3.18 (GHSA-2v37-7h3g-55p8) and brace-expansion 5.0.9 under webdav (GHSA-rgw5-rvv9-x895). npm audit --omit=dev goes from one moderate and two high to zero. The residual 3.4.0 recorded is unchanged: webdav ships a prebuilt bundle with brace-expansion inlined, which no lockfile change reaches.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature33Nuq3jk4L8Tk0nUQ/SUKRB9gkf/O+h254EVTLZroD9RvLW9vF6sZqc5zxHEqjR1n15D6JGSbNsdiHTIg3XruaBzMEQ9IplE8gzY6tkclHRN1n1plXbYwrFrZs/nfmUcEJQu2XKVoeCFrN+hPwVIXng/m5p+xGzcHhOnev/o2GNRTFHbSET9Sy7HHIGmieaOf2g6Nrbhto/X7QAnjk2i8Ht/OVLggubgPe85SYsG8qvmi03S0xY8NzFl7XQ6lBlY1DqZSt7Rn/PY8OQsYAMIRA+Ihwu6JU4hWiw1wyV0EFnWayypnS2l4hTk2GNG06Y8bbe1qt05r2LSQSqPI3HOd87nKK/ETmwJdHkpNDJi9NNV0iYuAp1fOsfH7d0VHkbuxWTXZ223YWtlTA+0XKNfZ7Jw5mq0zlyIjpyNDPjScwXv3g+/M835mTUFeGJGkv6HrVSoNtIhkAp0Mh1O5XXw/X0Y/6UlKsky/r0BsWD9a4ddMLocWOm3EvGWi17lsPkX7DJFUH6DKuIiPrKxjGiUM0+Op6sLPSVULqyrAudTe7nHZ2/YM2iDeOrUc6jc3+aNqKLbnv1ApdpmlFoErnZvL/RwCLd9463758stiEOrKksv1K84oCvak7W6FCsEZdEEMK7fzqS+r7mzCqAwH5qewAcIB1l844DIDh7X2i4eQ4o=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.4.0
Release Details
UpdatedJuly 28, 2026, 6:52 p.m.
Changelog

Public share links now render 3D models, and every format that carries external textures resolves them there. Also carries the security fixes listed below.

Added

  • A merge gate that treats an absent check as a failure, as a ruleset on main and as npm run pr:ready. gh pr checks reports only the checks that exist, so a queued run contributes none and a pull request whose matrix has not started looks exactly like one where everything passed — which is how a broken eslint run reached main. The required set is the seven summary jobs: each aggregates its own matrix and reports unconditionally. Because the ruleset also covers pushes, work on main now goes through a pull request.
  • The integration suite now also runs on MySQL and PostgreSQL. The version axis is covered on SQLite; this adds the database axis, which is where the queries differ rather than where the server does — escapeLikeParameter() picks an escape character per platform, and MySQL's default collation is case-insensitive where SQLite's and PostgreSQL's are not, so a folder listing settled on one backend is not settled on the others. Newest supported server only, rather than crossing both axes.
  • A PHP integration suite that runs against a real Nextcloud server, covering what mocks cannot: real storage for the dependency route, and a real connection for the file browser's folder listing. Both bugs that reached production here — the str_replace prefix that reported folders like modelstextures, and escapeLikeParameter() called on the query builder, where the interface never declared it — are now caught, each verified by reintroducing it. The bootstrap exits non-zero when it finds no server rather than skipping, which is how the retired workflow managed to report coverage it never had.
  • FBX and 3DS textures now load on public shares, which completes declaration-based resolution for every format carrying external textures. Both name their textures in binary structures, so they are walked — FBX node records, 3DS chunks — rather than searched for their marker bytes: a filename occurring inside vertex data is not a declaration, and treating it as one would let a crafted model name any file beside it. Walking also steps over mesh data by its declared length instead of reading it. The folder listing is now only a signed-in fallback.
  • COLLADA and X3D textures now load on public shares. Both formats name their textures in the document, so they resolve by declaration through the existing /dep/{name} route, the way OBJ and glTF do — no folder listing, which is what needed a session and left them untextured. Matched with patterns rather than an XML parser: only the paths are wanted, and a parser on an uploaded document brings entity expansion with it. Signed-in sessions keep the folder-listing fallback for documents the patterns miss.
  • 3D models are now viewable through public share links (#115). Share pages previously rendered nothing at all: LoadFilesListener returns early for anonymous visitors, so the bundle never loaded. A new LoadPublicShareListener hooks the public template.
  • Textures load on public shares. OBJ materials, their maps and glTF buffers are served by name through /public/file/{token}/{fileId}/dep/{name}. The model's own declarations are the authorisation — an undeclared name is refused, as is any path escaping the model's folder.
  • Tests for both database migrations, which had none. The load-bearing one pins the sha256 folder-path digest across the three places it is computed — the migration backfill, FileIndexService on write, FileIndexMapper on read. Drift there silently returns empty folder listings rather than an error.
  • Doctrine DBAL stubs in tests/bootstrap.php. IQueryBuilder takes its PARAM_* constants from Doctrine, which nextcloud/ocp does not require, so mocking IDBConnection used to fatal. No database-facing class had ever been unit-tested here.

Changed

  • A successful model load no longer fills the console with warnings. getFileIdByPath logged its normal progress at warn — the folder listing, the name being looked for, the move to subdirectories — so a model with ten textures produced twenty-odd warnings on a load where nothing was wrong, and a real one was indistinguishable from the noise. Routine steps are now debug; warn is left for a listing that failed, a subdirectory that could not be searched, and an unexpected error. A declared file that is genuinely absent is the caller's to report, which it already does once via missingFiles rather than once per lookup.
  • The signed-in texture lookup no longer guesses at a name that does not match. About 150 lines of similarity rules — singular against plural, a leading word dropped, a length-ratio partial match, and a mapping from any colour-ish filename to any body-ish one — came from making one model work and then applied to every model. A rule that matches two different filenames is a guess, and a guess that lands wrong serves the wrong texture with nothing to say so. The case it was most likely to get right, a name differing only in case, is handled before that point and on the server since PathLocator; what remained only fired when a model referenced a file that is genuinely absent, where rendering untextured is the honest answer. getFileIdByPath goes from 366 lines to 155, and the conventional-texture-directory search that was duplicated in its catch block is now one function.
  • Unblocked dependency updates and refreshed the lockfile, superseding ten stalled dependabot PRs (#104–#114) in one pass.
  • stylelint 17 rule fallout, all resolved — color-function-alias-notation rewrote 30 rgba() calls across four files.
  • The 3D navigation controller is now a "Split console": a steering annulus with its buttons on an attached rail, styled from Nextcloud tokens. The eight decorative arrows are gone; arrow keys now steer a focused ring. The 15% dead zone, 110% hit slop and hold-to-repeat behaviour are unchanged.

Fixed

  • A texture in a subdirectory the model does not name was only found if the directory had a conventional name. The search that walks whatever subfolders the listing reports asked for each one without includeDependencies, and in that mode the server filters its answer to 3D models and drops every image — so the one pass written to find textures in subdirectories could never return one. A separate pass over sixteen hardcoded names (textures, maps, images, and case variants) covered the common cases and hid it, leaving a texture in a folder called skin or PBR unreachable with the folder sitting right there in the listing. Signed-in sessions only.
  • "Reset" and "Fit" could not be clicked. The viewer's top bar is position: absolute inside a container that was not position: relative, so it anchored to the wrong ancestor and sat under the app content.
  • The navigation controller was hidden behind the app navigation, which Nextcloud renders at z-index: 1800. It is now anchored to .three-viewer.
  • The 3D file browser invented folders that do not exist. getFolders() stripped the parent prefix with str_replace(), which removes every occurrence: with no parent the prefix was a bare /, so models/textures was reported as modelstextures and matched no files.
  • FileIndexMapper called escapeLikeParameter() on the query builder, where it is not declared — it lives on IDBConnection. It worked only because the server's concrete class happens to carry it, the same bet that fataled the Nextcloud 34 upgrade.
  • A test file was raw binary as far as git was concerned. \0\x01 reached xmlModelDependencies.test.js as literal bytes rather than escapes, and a NUL makes git stop producing diffs for a file — it had dropped out of line-level review while passing CI. A new guard scans source for C0 control bytes.
  • composer test:unit ran every test under tests/, not the suite its config names. It passed a positional tests path, which overrides the configured testsuite — the same defect that made the old integration workflow run the unit suite under another name. Retiring that workflow left this copy of it in place, where it stayed invisible until a directory appeared that the glob should not have swept up.
  • A texture declared in a different case than the file rendered as missing. 3DS keeps map names in DOS 8.3 form, so exporters write WOOD.JPG beside a file saved as wood.jpg; MTL files written on Windows do the same with subdirectories. The declaration matched — that lookup is already case-insensitive — and the fetch then asked storage for the path exactly as written, so the model rendered untextured with its texture sitting next to it. The exact path is still tried first; only a miss walks the model's own folder, stopping at the first segment with no match. Where both cases exist, the exact name wins.
  • The merge gate blocked a ready pull request after a force-push. Every workflow here uses a concurrency group with cancel-in-progress, so superseding a running job leaves a CANCELLED entry in the status rollup beside the SUCCESS of the run that replaced it — both under the same check name. The gate read every entry and reported the superseded one as a failure. It now takes the most recent entry per name. This is the second state-reading defect in that script, so the deciding logic moved to pr-ready-lib.mjs and gained tests; a gate that cries wolf is the one people learn to merge past.
  • The case-insensitive lookup above covered the public route only, and only its last step. An OBJ's textures are named by its material, so the material has to be read before they are known at all — and that read still asked for the exact path, so a mtllib CHAIR.MTL beside chair.mtl left every texture in it undeclared. Signed-in sessions missed for a second reason: /api/files/find and /api/files/list resolve the declared path themselves, and both matched case exactly, so Textures/wood.png beside a folder saved as textures found nothing. All three now share one implementation.
  • A path climbing out of the user's folder returned 500 instead of 404. Folder::get() raises NotPermittedException for a path like ../../etc/passwd, and /api/files/find caught only NotFoundException — so probing the endpoint produced an internal error and a logged stack trace apiece, and told the caller a refused path is not the same as an absent one.
  • Every viewer session logged a PCFSoftShadowMap has been deprecated warning. Three.js r184 reassigns the constant on the first shadow pass, so the app was already rendering PCFShadowMap.

Removed

  • Retired the "Integration Tests" workflow, which reported coverage it did not have. test:integration and test:migration passed a positional path that overrode their configured testsuite, so both ran the unit suite under other names — and the job had no Nextcloud server to integrate against. test-phpunit.yml covers those tests across the same OCP 31–34 matrix.

Security

  • Resolved four npm advisories in production dependencies via npm audit fix, lockfile only: form-data CRLF injection (GHSA-hmw2-7cc7-3qxx), postcss path traversal (GHSA-r28c-9q8g-f849), fast-xml-builder sanitisation bypasses, and nine in dompurify.
  • Cleared the brace-expansion DoS (GHSA-mh99-v99m-4gvg / CVE-2026-14257) from the production graph, taking npm audit --omit=dev from 7 high to 0. Only brace-expansion@5.0.8 is patched, and it renamed its default export, so it cannot be forced globally; pinning overrides.webdav.minimatch to ^10.2.3 reaches it instead. Glob behaviour is unchanged and the advisory's proof of concept no longer exhausts the heap.
  • Capped material libraries per model at ModelDependencyResolver::MAX_MATERIALS. Each mtllib name costs a storage lookup and ~226,000 fit in the scanned header, so one crafted model turned every /dep/{name} request on its public share into hundreds of thousands of lookups. The sharer can be the attacker, and the route has no rate limit.
  • Known residual: the shipped browser chunk still carries the unpatched brace-expansion. Vite resolves webdav to a prebuilt bundle with it inlined, which no override can reach; it clears when webdav republishes. Unreachable in practice, since nothing here passes a glob to getDirectoryContents. The dev tree still reports the advisory through eslint 8 and jest.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureQX95f1tTvJoYA9LqLShsNFPKOwHPgMmw0dE0Rq/KdnyoJLpH+TOYawYFTn6RFykF5RIFDyCrTTEDJo5ehLy3bLnyCRX9pLza7My58nDdCND+fRlHgzvUNwzfULSQC1zPi92sHmy+SPqnUrp6xhevBe57/IWxE2JyUf/T80Gf+EXwWFzc9uoVoE2+mHvzvV0db/ZVO+OX/HHgYzZ65Q8wZTbycb1nZFx8RaxkJogd/5Nh8aEb/NlpyKRlJi9Zx3XtyQL6AL0Dcnv2eOQbOuA5VnwFql1UqCtmR1400ZCd0Cf0sSaoX6S1XW3+oLjGhJSHBEi/A+0MVpDChPPHSjfH+ER5gr628NQFfVSxFBXr0hHbHx3r8wBxIk0kRyqjR5Uj5CNzg8QVQsNhhFvsP7FFl59Il2JmzsJXAe99e/8mf1rMGmEZ5kVXwUImUl4Bqgc3IDF5SKUTfspNPWKWojRzT9sRgLZDI1RFqep2ov1wMNYOQ4Qzbzk9N93iumDQk1FsiYud2VO3TVpjaNCc6vWL/S+jA03TksnkF4sqxexJRAGwBpyEcwOHwUG0R6HVZpchkdhL6MW5UVwSb2i9b1eIqs1+DbQ70HCffX7pPIxFW6zOVDsdNb00Q6vaYeT2q9IIR32DnKj20bnBjLgP3BX61V3Ul7PLoAbbhF8vABR4CEA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.2
Release Details
UpdatedJuly 27, 2026, 11:31 a.m.
Changelog

Security release. Upgrade immediately if you use password-protected link shares containing 3D models.

Security

  • Share passwords were not enforced on the public 3D file endpoint. PublicFileController extended plain Controller with #[PublicPage], and PublicShareMiddleware returns early for anything that is not a PublicShareController — so no share authorisation ran at all. Combined with ShareFileService::loadLinkShare(), which only checked that getShareByToken() matched, anyone holding a share token could read the file regardless of its password. The token is the part of the URL people paste into chats and emails; the password exists precisely for when the token alone should not be enough. PublicFileController now extends PublicShareController and implements isValidToken(), isPasswordProtected() and getPasswordHash(), delegating password and token validation to the framework and picking up brute-force throttling with it. findValidLinkShare() additionally rejects non-link/email share types and expired shares, and treats a ShareNotFound from the share manager as "no share" rather than letting it escape as a 500. Present since the controller was added in 1b0ca86 (2025-09-15), so every release from v1.7.9 through v3.3.1 is affected. Verified on Nextcloud 34.0.2.1: a password-protected share now returns 404 without the password and 200 with it, ordinary public shares are unaffected, and expired shares return 404 instead of 500.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturemn6HBntWm2p/at2aww2v9uJiZOjj7tNXpISa325yfW2SKnZnaJHt7wSDJnb6ghsYuF1u19BVFg+1b+e/hFTFmhD8l1a0BL69FqZej7a4bwwfFbovRiDUkkZjik0nVahdD3k6fg6qabxnJbHEh79Lz2/vgcE/5nzAio06G5xJrZgeR10aoZs5oxMEpnNQlPWCiIZV78oY2PCGWosOEQaz78SzU2sX1Saq4goXMCyG1ElYLLeMbtmTPv3g9Dp08FyUctxtxGX1JbuNFg2F2AuhZSyMmFcnS6jGp9STzWZrD6x9jbd6KP5/mu6dPe88CAouT/RmUSFS14CCYAXOPdeqc0wgsOJncpoRZycZ/FwACJKGOjzR1qACdtG5wKjXnMDMJA5mdgDL+G77s7PzeY+u4jBkS2iF8swLxzZphid2HGNN5Rg4NECyCwsBYM/o+hbPVpro6iLKOg/hpiugpoQpwDw0RmcujPDYWewonRnNKdB+7i/Qk5FXDluhqGr7GoRek+nZaqm/pxSIGEeSnkpqoPAxHY978OncdxhKSIe8ACCxl9g9nokM4bxLA4zXWGHL8Onnb/DdWIGEfuGIhdhV/5QtSSW1xW75HrTcuHPJJFBAF2ovwQYPfexY26ZU0m7pyt2ucN8xxkkpuD6RDMpm2U/v9y+AxbtuNunW2CoKqm0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.1
Release Details
UpdatedJuly 27, 2026, 10:35 a.m.
Changelog

Hotfix for Nextcloud 34. 3.3.0 is unusable on NC 34 — both the viewer page and every model download return HTTP 500, and the app cannot be upgraded or enabled. Nothing else changed.

Added

  • Regression tests for both fatals. tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface with no __call() fallback, so it raises the same Error production did. tests/unit/NoPrivateServerApiTest.php scans lib/ for any use of the private \OC container or the legacy OC_* static classes, stripping comments via token_get_all() first so documentation about a removed API isn't mistaken for a call to it. Both fail on the pre-fix code. Note that composer test:unit is not currently run by any CI workflow — test-integration.yml runs only test:integration and test:migration — so these are verified locally until that gap is closed.

Changed

  • nextcloud/ocp dev dependency moved from dev-stable30 to dev-stable31, to match the min-version="31" declared in appinfo/info.xml; the weekly update-nextcloud-ocp-matrix.yml job, which had target: ['stable30'] hardcoded and kept re-pinning it, now tracks stable31 too. The pin deliberately follows min-version, not max: pinning to the oldest supported server is what stops us using APIs that don't exist there yet, and ocp stable33+ requires php ~8.2, which cannot coexist with the config.platform php 8.1 that NC 31 support demands. Verifying against the newest branch is psalm-matrix.yml's job — it already builds an ocp matrix across the full min..max range from info.xml. Psalm run manually against stable34 for this release confirms no further removed-API calls in lib/ beyond the two fixed below, and flags ten DeprecatedMethod notices worth scheduling: IConfig::getUserValue/setUserValue/deleteUserValue in PageController, SettingsController and ConfigController, plus IContainer::query in Application. None break on 34.

Fixed

  • App upgrade fataled on Nextcloud 34 (#116) with Call to undefined method OC\Server::getDatabaseConnection(). The Version010903Date20251121000000 migration reached into the private \OC server container in its constructor; NC 34 stripped getDatabaseConnection() from that class, which now exposes only getUserFolder(), getWebRoot() and getL10N(). It now resolves the connection through the public OCP\Server::get(IDBConnection::class) locator. Deliberately not constructor injection: MigrationService::createInstance() falls back to new $class() when the container cannot resolve a step, and an injected dependency would turn that fallback into an ArgumentCountError. The stale use OC; import is gone and $connection is now typed. Reported with a patch by @EX0l0N; same fix independently submitted as #117 by @coverslide.
  • Every model download and viewer page render fataled on Nextcloud 34 with Call to undefined method OCP\AppFramework\Http\EmptyContentSecurityPolicy::addAllowedChildSrcDomain(). Nextcloud removed the legacy child-src helper from its public CSP class (deprecated years ago in favour of the split worker-src/frame-src directives), so the call added in commit 4e0a532 ("fix CSP") became a hard error the moment the app ran on 34 — taking down FileController::serveFile and both PageController entry points, not just the worker policy. ResponseBuilder::addCspHeaders now calls addAllowedWorkerSrcDomain('blob:'), which is the correct directive for the blob-URL Web Workers that DRACO, KTX2 and web-ifc spin up, and has existed since NC 17 — safe across the whole declared 31–34 range. Reported in #116. The new tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface exactly, so it reproduces the production Error on the old code regardless of which OCP branch happens to be vendored.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturenWYgrEeOKWzfUMwxb3X0hAzGbWgJ9vd3jRvsIFUKLCvTsARpp09yWa5mnupOhaRtHcffz0eidnz5Blw7uN8sTMvty2tpnzgLad3CTvV401vV+je0pyeWgol+mkhoymUwBNcvlR4OWx4NWHX1azz9NdLAlF/J+A1ImE3D2UMf5mXlGctjznQwBeMLrXkKke1qdy23heuGTi6HCnNicY1PpkhC9RcmY0wlBbZr9/kx/TbCYx136AKjctjt4554x9SXkiAjt5S1SSEFefSaNxURYWlSx6xeCt4enQVAfA2uNfj772yxyWRhyvcQMnfIFz4IGTYMCi3KlKZgQvSa/Zj1KNP6d9NWOubG+RqOjzv84wjznJ6xoSTEoNLuQhuYt1zqW2wDyDFT9GD2jSONDYZjOZjal37C4NEvEv9HIkZYkVBL4kvwlytafsD9u4cmKCmgv0ROvPCM2PWVmnT3kXGOS+ivKLcaC0Zt0VciNlhYIAawDLDL+1j+WKRHIPhNUsLjiNfqbmUWUZlbEHPJvdIxTBfePDbvnnyFGWlUBKO+QoEo0JPHrpfisz31eb88G/33/1iblqUUZ3OwU71GAHjFvD8Vf0ivihZZSUqsTDdiEGU1dsbT7DnkFi2bLNfNQhcG14k03NzUVQW+fODwNoRO9pkWJnadgsZQ1ICJAj3sw9M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.0
Release Details
UpdatedApril 20, 2026, 6:47 a.m.
Changelog

Fixed

  • 3DS files loaded tipped on their side when the exporter used Y-up instead of the 3ds-Max Z-up default. The loader had been hard-coded to rotate every 3DS model -90° around X (commit d8117a0, "fix: add coordinate system rotation corrections for Z-up formats"), which helped CAD-style Z-up files but silently broke Y-up exports — Cottage_FREE.3DS for example rendered with its long axis as the vertical. Dropped the unconditional rotation; 3DS files now pass through at their authored orientation. Most modern 3DS exporters (Blender, recent Maya) are already Y-up, and Z-up outliers can be fixed by orbiting or — if this becomes common — by a future user-facing orientation toggle. Other Z-up-rotating loaders (STL, PLY, 3MF, DAE) are unchanged because those formats have stronger Z-up conventions that are not affected by this fix. Reproduced and verified in the live Nextcloud container by downloading the user's 3DS via WebDAV, inspecting bbox dimensions with a new scripts/inspect-3ds.mjs harness (raw X=1243, Y=675, Z=1465 — Y is the height), and hard-refreshing the viewer page.
  • Standalone viewer: KTX2 loader threw on init with Cannot read properties of undefined (reading 'isWebGPURenderer'), so models with Basis/KTX2-compressed textures silently fell back to placeholder textures. Root cause: the standalone ThreeViewer.vue never forwarded the live WebGLRenderer into the loading context, and Three.js r182's KTX2Loader.detectSupport(renderer) dereferences renderer.isWebGPURenderer before checking anything else. Fix: pass renderer: renderer.value in the context, plus a defensive guard in gltf.js that skips KTX2 entirely (with a warning) when no renderer is available instead of throwing. The modal viewer was unaffected — it already forwarded this.renderer. Live-E2E harness now also watches for KTX2/WebGPU console complaints so the regression can't sneak back in.
  • Standalone viewer: glTF models with embedded data: buffers failed to load under Nextcloud's Content Security Policy. The app CSP allowed data: for img-src but not connect-src, so GLTFLoader's fetch() of embedded base64 buffers (the default export shape of many gltf files) was blocked by the browser. ResponseBuilder::addCspHeaders now also adds data: to connect-src. Caught end-to-end by driving Playwright against the real dev container (scripts/live-e2e-check.mjs).
  • Skip-to-viewer link wasn't in a usable tab position — it was rendered inside <NcContent> → <NcAppContent>, which put it ~9 tabs deep behind Nextcloud's header, nav sidebar, and file list. Wrapped in <Teleport to="body"> so the anchor lives at the body level regardless of where Nextcloud's chrome wraps us. DOM-verified focusable in the live container; clicking it moves focus to #viewer-wrapper. The CHANGELOG entry below that claims "first tab stop" was aspirational — Nextcloud's own Skip to main content / Skip to navigation links always win the first two positions, which is correct behavior we can't (and shouldn't) override.

Added

  • Four more CAD formats via OpenCascade — STEP, IGES, BREP, FCSTD (closes #97): All four formats come from the OCCT family of NURBS/B-Rep CAD files and share one WASM integration. Uses occt-import-js (Viktor Kovacs's purpose-built emscripten port of OpenCascade's import + tessellation) — much smaller than vanilla opencascade.js (~7 MB WASM vs 10+) and with a JSON-based mesh output that's a clean fit for Three.js. Architecture: one shared src/loaders/occt-runtime.js exposes getOcct() (memoized module promise so the 7 MB WASM downloads once across all 4 loaders) and buildGroupFromOccResult() (walks the assembly hierarchy preserving node names, transforms are already baked into tessellated vertices so we don't reapply matrices). Each format gets a thin ~25-line loader: STEP (.step, .stp) → ReadStepFile, IGES (.iges, .igs) → ReadIgesFile, BREP (.brep, .brp) → ReadBrepFile, FCSTD (FreeCAD document) → unzip via fflate, filter for .brep entries, feed each to ReadBrepFile — per-body failures don't take out the whole document. The resulting bundle shape: four 1 KB loader chunks + one 725 KB shared occt-runtime-*.chunk.mjs + 7.3 MB WASM served separately from /apps/threedviewer/occt/. Main/app bundles unchanged (0 B trend). Not unit-tested because the whole pipeline needs live WASM + real CAD fixtures to exercise meaningfully; covered by the existing smoke suite pattern.
  • Fifth new format — IFC (.ifc) (#97): Industry Foundation Classes, the de-facto BIM interchange standard. Loader in src/loaders/types/ifc.js uses ThatOpen's web-ifc WASM (~5 MB, lazy-loaded on first .ifc open) and a hand-written Three.js bridge — web-ifc-three has been deprecated and its modern replacement (@thatopen/components) is a heavy framework we don't need just for file loading. Bridge logic: StreamAllMeshes walks the IFC element graph, each FlatMesh's PlacedGeometry entries yield tessellated vertex data in interleaved [px,py,pz,nx,ny,nz] layout which we deinterleave into separate position/normal attributes, then apply flatTransformation (column-major 4×4) and per-geometry color. Express IDs (expressID, geometryExpressID) are preserved on mesh.userData.ifc for future property-panel tooling. Memory is carefully managed — geometry.delete() in a finally block releases the WASM-side handle per mesh, and CloseModel runs unconditionally so parse errors don't leak the full IFC model. WASM copies ship via scripts/copy-decoders.mjs to /apps/threedviewer/web-ifc/ (both single-threaded web-ifc.wasm and multi-threaded web-ifc-mt.wasm — IfcAPI picks the variant based on runtime feature detection) so air-gapped Nextcloud deploys don't need CDN reachability. JS glue is lazy-chunked into a 3.4 MB ifc-*.chunk.mjs that only loads when a user opens a .ifc file; main/app bundle impact is minimal. Not unit-tested because the whole pipeline requires live WASM + real IFC fixtures; covered by the smoke spec suite instead.
  • Fourth new format — dotbim (.bim) (#97): JSON-based BIM format from dotbim.net — simple schema (meshes + elements with { mesh_id, vector, rotation, guid, type, color, info, face_colors? }). Loader in src/loaders/types/dotbim.js handles mesh instancing (multiple elements sharing a mesh_id each get their own cloned geometry), element transforms (translation vector + unit quaternion rotation), RGBA byte colors, optional per-face colors (v1.1.0 schema — unindexes geometry so each triangle's 3 verts carry the face color), and tolerates legacy exporter quirks (missing/null rotation, mismatched face_colors length). Element metadata (guid, type, info) is stashed on mesh.userData.dotbim for future inspection tooling. 10 Jest specs in tests/unit/loaders/dotbim-parser.test.js cover instancing, transforms, color handling, transparency, legacy-rotation fallback, orphan elements, metadata, and JSON/schema validation error paths. Zero WASM, zero external deps, ~180 lines. Like the other new loaders it's lazy-chunked via the registry's dynamic imports — 0 B impact on the main/app bundles.
  • Three new 3D formats — OFF, AMF, 3DM (closes #97 partially): (1) .off (Geomview Object File Format) — hand-rolled plain-text parser in src/loaders/types/off.js covering OFF, COFF (per-vertex colors, float or 0–255 int), N-gon fan triangulation, inline # comments, and binary-OFF rejection. 9-test Jest suite exercises the parse path end-to-end (tetrahedron, n-gon, comments, COFF float/int colors, degenerate inputs). (2) .amf (Additive Manufacturing Format) — wraps three.js's bundled AMFLoader, handles both XML and ZIP-compressed variants. (3) .3dm (Rhinoceros) — wraps Rhino3dmLoader, loads rhino3dm.js + rhino3dm.wasm (~4 MB) on first use from the app's bundled copy at /apps/threedviewer/rhino3dm/ so air-gapped Nextcloud deployments don't need CDN reachability. scripts/copy-decoders.mjs now copies the rhino3dm assets alongside DRACO/Basis. All three formats are registered across the 5 sync points (lib/Constants/SupportedFormats.php, src/config/viewer-config.js, src/loaders/registry.js, src/main.js::SUPPORTED_MIMES, appinfo/mimetypemapping.json); npm run format:check PASS. Each loader is lazy-chunked via the registry's dynamic imports, so the main/app bundle size is unchanged (only users who open the new format pay the download cost). The 6 remaining formats from #97 (step, iges, brep, fcstd, ifc, bim) all require OpenCascade.js or web-ifc WASM (~5–10 MB each) and are deferred until demand justifies the bundle hit.
  • Forced-colors (Windows High Contrast) CSS hardening: New central override sheet (src/css/forced-colors.css, imported from main.js) scoped entirely to @media (forced-colors: active) — no effect on default rendering. Addresses three patterns the a11y audit found: (1) custom badges (.stats-badge, .fps-badge, .active-badge, .last-used-badge) that rely on background-color + text-color alone collapsed into the surrounding surface once the UA repainted both with system colors; they now get a 1px CanvasText border so they stay visible. (2) .filter-format-chip.active lost its visual distinction from the inactive state because both backgrounds became Canvas; the active variant now uses Highlight / HighlightText with forced-color-adjust: none so selection state survives. (3) Focus states that suppressed outline and relied on box-shadow or colored border-color (.export-select:focus, .annotation-text-input:focus, .skip-to-viewer:focus) had no visible focus indicator; they now re-enable a 2px Highlight outline. Verified by a new Playwright spec (tests/playwright/a11y-forced-colors.spec.ts, 5 tests) that loads the real CSS file into static fixtures mirroring each component shape and asserts border/outline width > 0 under emulateMedia({ forcedColors: 'active' }). Caveat: Playwright's forced-colors emulation uses a neutral palette — this closes the keyboard/CSS correctness gap but an interactive Windows HC walkthrough is still the final source of truth for theme-specific contrast issues.
  • i18n parity tooling (scripts/check-i18n.mjs): Walks src/ (.vue/.js/.ts) and lib/ (.php) and extracts every t('threedviewer', 'string'), this.t(...), and ->t(...) call via regex. Diffs the extracted set against l10n/en.json: ERROR on source strings missing from en.json, WARN on orphan keys no source uses, per-locale coverage summary that reports missing, passthrough (value === key), and orphan counts. --sync-en auto-adds missing keys (value = key); --prune removes orphans. Wired as npm run i18n:check / npm run i18n:sync; i18n:check is now part of the validate chain. First sync closed 329 missing keys and pruned 87 orphans in en.json — ar/de/es coverage now reports accurately at ~27% (previously looked higher only because en.json itself was incomplete).
  • Export pipeline unit tests + MIME consistency fix: Nine Jest specs in tests/unit/composables/useExport.test.js cover getGeometryStats (indexed, non-indexed, multi-mesh groups, non-geometry descendants) and exportAsSTL/exportAsOBJ/exportAsGLB (correct MIME — model/stl, model/obj, model/gltf-binary — correct byte length, null-object guards, and a multi-material OBJ scene that passes through the exporter with its material array and geometry groups intact instead of being flattened). Three.js exporters are mocked so the test runs deterministically in jsdom without pulling in Three's ESM-only subpath imports; the real exporter output remains covered by the Playwright smoke suite. Shipped alongside a MIME consistency fix in SlicerModal.vue — the slicer-export fallback path was still using application/octet-stream for STL and text/plain for OBJ even though the main Export Model path had been updated to model/stl / model/obj; both paths now agree.
  • Loader smoke-test coverage for cancel/retry/network-drop: Two new Playwright specs in tests/smoke/viewer.spec.ts use page.route() to deterministically simulate (a) a user cancelling a hanging load and then retrying — the second fetch is fulfilled with a fixture and the viewer reaches __LOAD_COMPLETE, and (b) a network failure (route.abort('failed')) that must surface via __LOAD_ERROR without being misclassified as a user abort. ThreeViewer grew two tiny test hooks (window.__LOAD_COMPLETE on successful model-loaded, window.__LOAD_ERROR on non-abort errors) to give tests a DOM-independent signal — production code is unchanged in behavior.
  • Accessibility follow-ups: Keyboard users now get a "Skip to 3D viewer" link as the first tab stop (visually hidden until focused, slides into view on focus) that jumps focus past the navigation directly to #viewer-wrapper. Both modals (SlicerModal, HelpPanel) now trap Tab/Shift+Tab inside the dialog while open and restore focus to the element that opened them on close — implemented via a dependency-free useFocusTrap composable. Modals also gained aria-modal="true" and tabindex="-1" on the dialog surface. Covered by a new Jest unit suite (tests/unit/composables/useFocusTrap.test.js) and a Playwright a11y spec (tests/playwright/a11y-skip-link.spec.ts) that verifies skip-link behavior under forced-colors: active (Windows High Contrast).
  • Transform Gizmo: Translate, rotate, and scale models interactively with Three.js TransformControls. Mode selector (Move/Rotate/Scale) and reset button in the Analyze section. Disables orbit controls while dragging.
  • Animation clip selector: Dropdown in the Animation panel to switch between individual animation clips (e.g., Run, Walk, Idle) for models with multiple animations
  • Texture optimization pipeline: Quality presets (Original/High/Medium/Low) with Canvas 2D downscaling, memory tracking, and configurable setting in Personal Settings
  • X3D parser: Full XML-based parser replacing placeholder cube — supports IndexedFaceSet geometry, materials, textures, transforms, and DEF/USE references
  • Volume & surface area measurement: Model statistics panel now shows actual mesh surface area (sum of triangle areas) and mesh volume (signed tetrahedra method, accurate for watertight meshes), in addition to bounding box volume
  • Custom color palette editor: Personal Settings → SlideOutToolPanel now exposes four user-overridable scene/chrome colors on top of the base light/dark theme: background, grid, toolbar bg, and toolbar text. Each has a native <input type="color"> picker plus a per-key "×" clear button; a single "Reset all palette colors" button clears everything at once. Overrides persist to localStorage['threedviewer:customPalette'] and are merged on top of whichever base theme (auto/light/dark) is active, so switching themes leaves your palette alone. The viewer rebuilds its THREE.GridHelper in place when gridColor changes and swaps scene.background when background changes — no reload required.
  • Decoder worker pool tuning (parallel decoding): GLTFLoader's DRACO and KTX2 sub-loaders now call setWorkerLimit(n) with n = min(hardwareConcurrency - 1, 4) (floor 2), so GLB textures and compressed meshes decode in parallel across multiple Web Workers instead of serializing on one. Defensive feature-detection — falls through when the Three.js version doesn't expose setWorkerLimit. Result: GLBs with multiple KTX2 textures load noticeably faster on 4+ core machines without starving the main thread or flooding it with workers on 1-core devices.
  • Memory pressure auto-step-down: The performance composable now samples performance.memory.usedJSHeapSize each frame and, when usage crosses 85 % of the configured maxMemoryUsage cap (default 500 MB), auto-switches to the Low performance mode — reducing pixel ratio, disabling shadows, and applying LOD. A one-time warning toast ("Auto-reduced quality — memory pressure") surfaces what happened; the flag clears once memory drops back below 70 % so the user can re-raise quality manually. 5-second cooldown prevents thrashing, and the check gracefully skips browsers without the non-standard performance.memory API (Firefox, Safari).
  • Background indexing status API + progress bar: New GET /api/files/index-status endpoint backed by ICacheFactory::createDistributed returns { active, processed, total, percent, startedAt, updatedAt } for the authenticated user. FileIndexService.reindexUser now pre-counts supported files (respecting .no3d and hidden-folder skip rules), then bumps the processed counter after each file. The Personal Settings "Re-index now" button starts a 750 ms poll in parallel with the blocking POST request and renders a live NcProgressBar ("142 / 284 (50 %)"), falling back to "Scanning…" until the pre-scan completes. Status TTL is 1 hour.
  • Adaptive texture streaming (visibility-aware queue): useProgressiveTextures now accepts a (camera, scene) streaming context and sorts its pending queue by frustum visibility before each batch — textures on meshes currently in view load first, off-screen ones are deferred. When the camera moves, the very next batch picks up the new viewpoint. queueTexture has a new optional mesh argument for visibility scoring; the signature stays backward-compatible, so existing callers are unaffected. ThreeViewer auto-registers the context on init so future loader pipelines that opt into progressive loading get adaptive scheduling for free.
  • Measurement suite upgrades: The Statistics panel's measurement section is now a full measurement tool:
  • Unit picker: Choose mm / cm / m / in / ft / generic units from a dropdown; bounding box, surface area, and volume values re-render instantly with the right suffix (mm, mm², mm³, etc.). Defaults to the project's configured VIEWER_CONFIG.measurement.defaultUnit so it matches the existing ruler tool.
  • Per-mesh breakdown: When a model has more than one mesh, a scrollable list shows each mesh's surface area and volume in the selected unit. Clicking a row focuses the panel on that mesh — the Width/Height/Depth/Area/Volume rows switch to its numbers and a "Focused: mesh name" header with a Show-all link appears. Helper meshes (transform-gizmo pickers, annotation markers) are excluded.
  • Pick mesh in viewport: A one-shot button arms a raycasting click handler on the canvas; the next click on a mesh focuses it in the stats panel. Works with any mesh in the loaded model, respects visibility, and skips gizmo helpers.
  • Watertightness badge: The section header flags each mesh as Watertight (all edges shared by exactly two triangles — volume is reliable), Not watertight (open or non-manifold edges detected — volume should be treated as approximate), or Unknown (non-indexed geometry, or >500k triangles so we skip the edge-map check for performance). Aggregated badge appears at the top of the section; per-mesh flags appear next to each row in the breakdown.
  • Copy measurements: A button copies a plain-text report of the current values (bounding box, surface area, mesh volume, bbox volume, watertight status, per-mesh table) to the clipboard in the selected unit. Uses navigator.clipboard.writeText on secure contexts with the textarea + execCommand fallback for legacy browsers. Success/error toast via the existing push-toast channel.
  • WebXR / VR mode: Enter immersive VR via the 🥽 button in the top bar (only shown when the browser advertises immersive-vr support). Animation loop swaps to renderer.setAnimationLoop during XR sessions, and FPS throttling is bypassed since the headset enforces its own cadence. Testable without a headset using the Chrome WebXR API Emulator extension.
  • Annotation JSON export/import: Save annotations as a versioned JSON document and re-import them later. The schema includes format, version, exportedAt, modelFilename, and an annotations array of { id, point, text, timestamp }. Import/Export buttons appear in the Annotations overlay when annotation mode is active.
  • Scene comparison diff overlay: When two models are loaded side-by-side via Comparison mode, a "Scene Diff" panel auto-appears showing original vs. comparison stats (vertex count, face count, mesh count, bounding box X/Y/Z, diagonal length) with color-coded deltas (green = increase, red = decrease). Helper meshes (transform gizmo pickers, axes/grid helpers) are excluded from stats so the diff reflects only the loaded geometry. The overlay is dismissable via × and re-openable from a "Diff" button in the comparison controls.
  • Annotations persistence (per-file backend save): Annotations now save automatically to your Nextcloud account, keyed by file ID, so they reappear next time you open the same model. Backed by a new AnnotationsService that stores one JSON document per (user, file) under app data (appdata_*/threedviewer/annotations/{userId}/{fileId}.json), with GET/PUT/DELETE routes at /api/annotations/{fileId} validating that you have access to the underlying file before reading or writing. Saves are debounced 600 ms after each change; clearing all annotations issues a DELETE so the backend doc is removed instead of storing an empty list. A small status pill ("Loading… / Saving… / Saved / Save failed") in the annotation overlay header surfaces sync state. 256 KB cap per document.
  • Shareable view link: New "Copy View Link" button in the View section copies a URL that encodes the current camera viewpoint (position, target, zoom) as a compact cam=px,py,pz,tx,ty,tz,z query parameter. Opening the link reloads the model and restores the exact same angle after auto-fit runs, so you can send collaborators a link and say "check out this corner of the model" without needing a live session. Toast feedback on copy success/failure; button disabled until a model is loaded. Works on any secure context (HTTPS or localhost) via navigator.clipboard, with an offscreen-textarea + execCommand fallback for legacy environments. No backend — pure URL round-trip.
  • Clipping box (6-plane section analysis): The existing Cross-Section tool now has a Plane / Box mode switch. Plane mode is unchanged (single cross-section plane with axis + position slider). Box mode enables six axis-aligned clipping planes, one per face of the model's bounding box, each with an independent 0–1 offset slider that moves that face inward toward the opposite side. Setting xMin=0.35 and zMax=0.35, for example, slices the leftmost 35% off along X and the nearest 35% off along Z, leaving the intersection slab visible with interior surfaces rendered via DoubleSide. A "Reset box" button returns all six offsets to 0. Plane and box state are kept independently so toggling between modes is non-destructive, and box offsets auto-remap when a new model is loaded.
  • File browser search & filters: New filter toolbar in the file browser. At the Folders/Types/Dates overview level the search box runs a global recursive search that walks every folder, type, and date bucket and surfaces matching files as a flat results grid (each card showing its full path) — so searching wolf from the Folders root finds Wolf-Blender-2.82a.glb two folders deep without manual drilling. Inside leaf views (drilled into a folder, type, or month) the search filters both subfolder cards and file cards together, and unlocks two extra controls: multi-select format chips (one per extension actually present in the current view) and a size bucket dropdown (Any / < 1 MB / 1–10 MB / > 10 MB). Filters compose with AND between categories and OR within format chips, and a "Clear filters" button appears as soon as any filter is active. Filters auto-reset when navigating between folders/types/dates so a stale query never makes the next view look empty.

Fixed

  • FBX taillight/lens transparency: FBX post-processing was force-overriding every material to opacity = 1.0; transparent = false, which made authored translucent lens shells (taillights, headlights, glass) opaque and hid the coloured mesh inside — the Mercedes GLS rear lights rendered as a solid grey slab with the red only visible if the camera was pushed inside the body. Now preserves the FBX-authored opacity: materials with 0 < opacity < 1 are left transparent with depthWrite = false so inner meshes show through; only materials with opacity <= 0 (which would render invisible) are still forced opaque.
  • Stats panel — File size on multi-file loads: File Size showed "0.00 MB" for formats that come with sidecar assets (OBJ+MTL, FBX with textures, GLTF+BIN). The stats panel was reading modelLoading.progress.value.total, but multi-file loaders report progress as a percentage (max 100) rather than bytes, so the resulting "100 B → 0.00 MB" rounded to zero. Now reads the actual byte count from modelSourceFiles.value[].size (matching main filename first, else first file), with the progress-total value only used as a fallback when it looks like real byte counts (> 1 KB).
  • Stats panel — Texture memory accounting: Textures section reported "Memory: 0.00 MB" while textures were still loading, because the loaded-image check used a 512×512 fallback that happened to resolve before decode — making tiny placeholders look real. Now distinguishes three states: loaded (dimensions ≥ 4×4 contribute to memory), pending (flagged separately so the UI can show "loading…"), and missing (1×1 placeholder or HTML img with empty src / zero naturalWidth). ThreeViewer polls analyzeModel once a second for up to 8 seconds after load when any texture is pending, so async decodes get picked up without the user needing to reopen the panel.
  • FBX dark rendering without textures: When an FBX referenced textures (e.g. texture.png) that weren't shipped alongside the model, the loader substituted a 1×1 base64 placeholder into mat.map. final = color × placeholder then multiplied every surface down toward near-black, producing a silhouette-dark render where the Mercedes GLS looked like a blob. The loader now detects its own placeholder (1×1 image size) and nulls out mat.map; a new "clay mode" kicks in when zero textures resolved from dependencies, brightening near-greyscale dark material colours to a pleasant 0.75 clay-grey while hue-preserving the brightening on coloured materials (green license plate, red brake lights) so they still read as coloured instead of washing out to clay.
  • ZIP export — main file path: The main model file is now packed under its basename (e.g. eyeball.obj) instead of its full Nextcloud path (e.g. /3D files/Eyeball/eyeball.obj). The leading slash is invalid in ZIP archives and broke extraction on some platforms.
  • ZIP export — preserved subdirectory layout: Textures discovered in subdirectories (e.g. textures/) are now packed under their original relative paths in the ZIP instead of being flattened to the root, so the unzipped folder mirrors the original Nextcloud layout and re-imports cleanly.
  • Comparison mode loading 400 error: loadComparisonModelFromPath was passing the { id, subdir } object returned by getFileIdByPath directly into URL templates, producing [object Object] and a 400 from the file API. Now destructures id before use. (Latent regression from the earlier getFileIdByPath return-shape change.)

Fixed

  • Animation playback: Previously all animation clips played simultaneously, causing blended/static poses. Now only one clip plays at a time
  • Animation timeline seek: Slider scrubber now correctly updates the model pose when dragged (replaced mixer.setTime() with action.time + mixer.update(0))
  • Animation loop toggle: Toggling Loop off and back on no longer leaves the animation stuck; finished LoopOnce actions properly restart
  • FBX rendering: Fixed dark/black eyeball — upgraded Lambert to Phong materials, added SRGBColorSpace, normalized scale, detected transparent shells
  • PLY loader: Preserved original vertex normals from file instead of always recomputing them
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewL1fCXkzaQs2nvi2AkLccAX0u8v5YD4iOypG70neHtpgIb3wIQO+XaIgyQ+8n5k5ZO8pYI33VMdCWsomImFSTZmT7bH7s5DdFotdIektcqSN+kjfyeBeJkdoAcXUU2sJZgSdlFMrIxINc35c1nagz9g+B7qabynqu0yP2FnuWkeN9E37Rb4aXqvPUPPCEFkNjilYHkC1vrW6cUR/Z+L806b1IGJ0eZs6yH/3BB83V6TZ92btN80bbLdK0IbDY/uJ7sggkhuir0WKKoyC31fKsTaqcUi9I6G7Gr2Xzm3NqS6Bk2Y/pp0rLCSQJ+4xO3LA2ktbZIMZtqgA3gEfRYqMzJ10Eyny6TCwfAwOuvozvPoKbfm+5tlhjQyGgE/C7iDOEJ2LFqC9SEH9RrNPlzKxNJ/u1zXHdmEzmxGQpAKS1qoDEAPdKOsVFdkcdKugTNIi6PpP+29HGGl7lE8OuKIiq/JY8z2+9jnfAD087bE8QxnqJBi4eL8401WSzVDRRHlbh6nS/RFb/AqCDkJE+O7G5KskqRvrb3Re7QGNXcogubljsyRMVxQT2azuv/xg6VXJGKGIMv78/OBcQW+fMfr1s6NoDweK0LTQXIVGjQbx0SRJBPsGE5arytY9eIdaoRyHcxXje81vWhp5/Ar5MMLsQf6FjBKZILulUZWPvG2VfmE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.2.0
Release Details
UpdatedApril 5, 2026, 6:44 p.m.
Changelog

Added

  • Cross-Section tool: Interactive clipping plane to slice models along X/Y/Z axes with position slider, flip direction, and DoubleSide rendering for visible interiors
  • Animation Timeline Scrubber: Seek to any point in animated models with a slider, step forward/backward frame-by-frame
  • View Bookmarks: Save and restore camera positions with display toggles (grid, axes, wireframe, background), persisted in localStorage
  • Lighting Presets: Quick-switch between 5 lighting setups (Default, Studio, Outdoor, Dramatic, Flat) affecting ambient, directional, and point lights

  • Exploded View: For multi-mesh models, animate parts outward from centroid with adjustable explosion factor slider

  • Slicer: Export format selector: Choose STL, OBJ, or PLY before sending to slicer (non-passthrough formats)
  • Slicer: Upload progress bar: Real-time progress indicator when uploading to server (XMLHttpRequest with progress events, 2-min timeout)
  • Slicer: Copy share link: Copy the temporary Nextcloud share URL to clipboard for manual use
  • Slicer: Size validation: Warns and blocks uploads exceeding 50MB before attempting server transfer
  • Slicer: Upload size display: Shows file size in MB during upload for files >5MB
  • Modal viewer stats panel: Lightweight model info overlay (meshes, vertices, faces, dimensions) accessible via bottom-right button
  • Modal viewer screenshot: Download a PNG screenshot of the current view from the modal preview
  • Cache settings in Personal Settings: Configurable max cache size, max file size, expiration days, enable/disable toggle, and clear cache button
  • Cache hits/misses in performance overlay: Shows individual hit and miss counts alongside hit rate percentage
  • Cache privacy documentation: Documented local-only storage, per-browser isolation, and user control in TECHNICAL.md
  • Multi-file matching test suite: 48 tests covering texture/MTL name matching strategies (space normalization, prefix removal, plural handling, color/body mapping, partial matching)
  • Edge case fixtures: mixed-case extensions, missing MTL, orphaned textures for multi-file loading tests
  • Export triangle count warnings: Toast notifications for large models (>500K info, >2M warning) before export starts
  • Export MIME type fix: STL exports use model/stl, OBJ exports use model/obj instead of generic types
  • Help panel refresh: Added Slicer & Export section, cross-section, exploded view, lighting presets, bookmarks, dependency cache documentation
  • i18n audit: Wrapped hardcoded export/error toast strings in t(), added 31 new keys to l10n/en.json, documented i18n checklist in TECHNICAL.md
  • Accessibility review: Added role="dialog" + aria-labelledby to SlicerModal, aria-controls on all 6 panel section headers, aria-label on emoji-only buttons, role="alert" on texture warning, role="region" on stats panel
  • Format parity guard: Build-time script (npm run format:check) validates that PHP, JS, loader registry, Viewer MIME list, and mimetypemapping.json stay synchronized
  • X3D/VRML MIME registration: Added model/x3d+xml and model/vrml to Nextcloud Viewer MIME list so these formats open in the viewer
  • Slicer security documentation: Documented full security posture — authentication, path traversal prevention, MIME validation, size limits, share expiry, and file lifecycle in TECHNICAL.md

Fixed

  • Slicer OBJ/PLY upload: Added obj and ply to backend upload allowlist — OBJ/PLY format selector was added to frontend but backend rejected these formats
  • EufyStudio URL parsing: Replaced fragile regex filename extraction with proper URL parsing
  • Slicer upload timeout: Added 2-minute timeout to XMLHttpRequest (previously no timeout — could hang forever)

Changed

  • Tools panel redesign: Reorganized from 4 sections to 6 (View, Scene, Analyze, Animation, Export, Settings) for clearer grouping
  • Toggle switches: Replaced text checkmarks with custom CSS toggle switches for Grid, Axes, Wireframe, and Loop controls
  • Export section: Screenshot, Export Model, and Send to Slicer moved from Settings to dedicated Export section
  • Animation section: Elevated from nested group to its own collapsible section (conditional, only shown for animated models)
  • Model Statistics: Moved from Settings to Analyze section alongside Measurement, Annotation, and Cross-Section

Fixed

  • Theme consistency: Unified --color-primary-element fallback values across all components to #0082c9 (Nextcloud default), replacing inconsistent #4287f5, #1976d2 fallbacks
  • MinimalTopBar hardcoded colors: Replaced 7 instances of hardcoded rgb(0 130 201) with var(--color-primary-element) and related NC variables
  • Dark theme hack removed: Deleted ~130 lines of .dark-theme CSS overrides (46 !important declarations) from SlideOutToolPanel — dark mode now works automatically via Nextcloud CSS variables
  • Cache stat colors: Replaced hardcoded Material Design hex colors with NC semantic variables (--color-success-text, --color-warning-text, --color-error-text)
  • Missing panel props: Added wireframe, background-color, performance-mode, theme-mode, has-animations, is-animation-playing props to SlideOutToolPanel binding in App.vue
  • Clipping plane Z-fighting: Added 5% margin beyond model bounds so the slider at extremes doesn't cause Z-fighting artifacts
  • Bundle budget: Updated app chunk thresholds for new features (+12KB raw)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature2a2j5kFHSYZk5lKX7ByFRqNuCv8hg06X3uc2Mr98biRZSxeIgBug6aCdHONpN8oppv+6SDc5XrviS8HcLVFw1UEZvHjePdJYR8gri52Aa7ao7IfghYUHnoIwinqvWfP0sRod5vn3ZaKuo1pMpAHQlt/fkQCVz9eH6r+6TQTTBzBeAc7yWyEumArposNVcDqhgy0kK8tNWpNGYhpdqcMEvS7zGi0bl5EeK8k3ufaNaqwlzoXNwNMBiJ1aWFV2phxOxekkxlsHfRmGLkkdbPCOHg1Kj2CTLldHOjSQmQcUH9KOL1xkOZzuAC3Mh8SnrG/47h48uKIe62T7WYHgPbeOrDHvP8Z9d8S9V7kRq3puAonSPX/TFZt7j4YZoyar86w1MVakhT1mwiK3hBax+TCzxPRo63941VwID08MonJxW4D9C59MuF5qb/7UcDZ4Fm/TrSa0yvvKi+gy+gRCmBdzCnTg+WsEZW6Lotin80zckyH/+4wkUhUQBcq97ElIQKDwG5g9Bv6SGtQ2sR3zoUi7T/Wle4iqh38p5to6VMOlKfiBDiloGwpzXtHtlXDIZw8QqPL2h6gTQNdPizlovGLaZLup3sLL6MX7uyD5ZhbWz4kg1Qj4CqORSmpAanqJbBdUhLfA8dU2YhX5q7PFm8pMDAzKOq3h45kiSFI9K39f73c=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.0.0
Release Details
UpdatedApril 2, 2026, 7:40 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.6 → ^7.29.0
  • @babel/plugin-transform-runtime: ^7.28.5 → ^7.29.0
  • @babel/preset-env: ^7.28.6 → ^7.29.0
  • @playwright/test: ^1.56.1 → ^1.58.2
  • jest-environment-jsdom: ^29.7.0 → ^30.3.0
  • vue: ^2.7.16 → ^3.5.0
  • @nextcloud/vue: ^8.33.0 → ^9.5.0
  • @nextcloud/vite-config: ^1.7.1 → ^2.5.0
  • Vue 3 migration: Migrated app from Vue 2 to Vue 3
  • main.js: new Vue() + Vue.mixin() → createApp() + globalProperties
  • settings-personal.js: Vue.extend() → createApp()
  • viewer-api.js: new Vue() / $mount() / $destroy() → createApp() / app.mount() / app.unmount()
  • ViewerWrapper.js: New Vue 2 bridge component — Nextcloud Viewer bundles Vue 2 internally, so a plain JS wrapper renders in Vue 2 and creates an isolated Vue 3 createApp() inside for the real ViewerComponent
  • Removed @vue/vue2-jest (Vue 2 specific)
  • Nextcloud 34 compatibility: min-version 31, max-version 34 (@nextcloud/vue v9.x requires NC 31+)
  • Vue component imports: Migrated deep imports (@nextcloud/vue/dist/Components/...) to barrel imports (@nextcloud/vue) for forward compatibility with @nextcloud/vue v9
  • Template modifiers: Removed deprecated .native event modifiers from Vue components (compatible with Vue 2.7+, required for Vue 3)
  • @nextcloud/vue v9 API migration: Updated all form component bindings to Vue 3 API
  • NcCheckboxRadioSwitch: :checked → :model-value, @update:checked → @update:model-value
  • NcTextField: :value → :model-value, @update:value → @update:model-value
  • NcSelect: :value → :model-value, @input → @update:model-value
  • NcSettingsSelectGroup: :value → :model-value, @update:value → @update:model-value
  • Bundle budget: Updated index chunk thresholds in bundle size checker

Fixed

  • npm audit: Resolved dependency vulnerabilities via npm audit fix (#77)
  • npm audit: Applied non-breaking security patches, reducing vulnerabilities from 43 to 25 (42% reduction)
  • Lint: Fixed one-var error in useThumbnailCapture.js
  • Animation loop toggle broken: AnimationMixer.LoopRepeat/LoopOnce are module-level constants, not static properties — setLoop(undefined) made loop toggling non-functional. Imported LoopRepeat/LoopOnce directly from 'three' (useAnimation.js, useComparison.js)
  • Model load errors invisible to user: Variable shadowing in handleLoadError — parameter error shadowed the error ref, so error.value = error was a no-op. Renamed parameter to loadError, fixed logger level from info to error (useModelLoading.js)
  • Lights leak on re-setup: Vue 3 proxy wraps items in ref([]) arrays — scene.remove(proxy) doesn't match raw Three.js objects via indexOf. Added toRaw() for light/helper removal and instanceof checks (useScene.js)
  • Toast auto-dismiss broken: ToastContainer was mutating the toasts prop directly (setting progress/paused on prop objects), which triggers Vue 3 warnings and breaks in strict mode. Moved progress and paused state to local data() (ToastContainer.vue)
  • Mobile touch listener leak: setupPinchZoom() and setupDoubleTapReset() added document event listeners but never stored references for cleanup. Stored refs in eventListeners and added removal in dispose() (useMobile.js)
  • Settings page form controls not responding: @nextcloud/vue 9.x changed all form component props from checked/value to modelValue. Updated all bindings in PersonalSettings.vue
  • CSS nesting bug: .select-group-row rule was nested inside .setting-row braces — silently dropped in browsers without CSS Nesting support. Moved to separate rule block (PersonalSettings.vue)
  • Viewer registration errors silent: Both registerViewerHandler and registerViewerHandlerLegacy had empty catch blocks — any registration failure was invisible. Added logger.error() calls (viewer-api.js)
  • Loader errors invisible: All BaseLoader logging methods (logInfo, logWarning, logError) had empty bodies. Delegated to project logger (BaseLoader.js)

Technical

  • PHP CS Fixer: Blank lines before returns, doc comment whitespace, type-cast spacing, removed unused imports
  • OpenAPI spec: Regenerated with slicer and thumbnail controller tags and updated description
  • Three.js + Vue 3 pattern: shallowRef for single Three.js objects, ref for arrays, toRaw() when passing proxied objects to Three.js APIs
  • Vue 3 Maps pattern: Maps moved to created() hook as non-reactive instance properties (this._timers) to avoid Vue 3 proxy breaking Map.has()/Map.get()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCqMDLnrHd2ejY1wtQ+IUFnXE3ObdTMqRro2/QTWhZPmyh7h5lD2zlldE7hV6K2PLVbSbQA8RyylLA7chfBAfNhqrGVcv9N8oS4N8JZlvp2sx7uCnQJx9hA34xXsRvCNednaE0NuhE4zelLAkmDaJZEcQm0r0mQKkZAMSaIPgRg52ZFiEV+KFa7dyHlyLqc/Cp6doYyEeJxnyDiLOiCCsS0zyrt1qQlRyEgGcOocdeIPJRutmBwX3Sy/018v5midNblK+gleIzcCAFIOM7qDJKuf5ESnvQmqQko/7J+ahhAGzIdpGRHmKCVjAayZ2g3uLkT9dNQrBwKAaYvNWODkSyVEoBBU1wrucKcw237GMSjxHrVf/h5G64+Nb9/krmriBkdcpVupQ94wgyU9H5NO+BsBP/+3EfuuECRwOxXMW15taFrsiflxos1Fuhu5PQtyfOv4UPtUzn+uf4MaSzeLYdBHsOHfO6ZTjTPRbO5NLz5nlMw3F4PEgSKW7bk9brH7dEjzQzVG1RrDYCg75bGW+8l7/vtAmWc87s6otqoCj9rPVHQhrY7njOUcU8gwjdw0WuFTD//i2Vk0+agy7GO9WWfUdrbQ/i8Grq5AwoEgD3b0NLpPB9XxDCOR3yZQhSDbHNvgI8BRDp3Xe1FDv8izK2Dw9HwjykDDU+uMNZNtNpdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 2.3.4
Release Details
UpdatedJan. 18, 2026, 6:57 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.5 → ^7.28.6
  • @babel/preset-env: ^7.28.5 → ^7.28.6
  • postcss-html: ^1.7.0 → ^1.8.1
  • vite: ^7.2.7 → ^7.3.1

Fixed

  • Nextcloud Subfolder Compatibility (#74)
  • Fixed app not working when Nextcloud is installed in a subfolder (e.g., https://example.com/nextcloud/)
  • Replaced all hardcoded /apps/threedviewer/... paths with Nextcloud's generateUrl() and imagePath() functions
  • API endpoints, decoder paths, and static assets now correctly respect the configured webroot
  • Affected files: multiFileHelpers.js, useModelLoading.js, useComparison.js, gltf.js, ViewerComponent.vue, SlicerModal.vue, App.vue, viewer-config.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturem8HTfU5qBOyGRmdHu4hylXbuVzwpcofBxDmpgWjNf0qvBkujJC75qp0z1GcJKDRppDXuE5z70L6g5AAUda+v3Z5y9byJqnisxNVRZM0nbXwYgOmqi8MdBnP7bzd5Ms43jDwr8hKt5ffEKR+g6h7Um8q5BDl3Vdk2J8nVuk+uvduDXalukp+USx3ibBHjg3MDSJqdkTKkHPe41SXEpjyor9VMKLer8r7HxHcwlELGseyuFHgL/PPYQOT5GQf6KO7ae0s2lYH6tbBu3ZnP/f0tg4cYZAHAycm4fvAwRj44XMh6a3OFP0FdQOkpSmYa7Tp5IH5rj4zBjrazGCxtUU40q3bBmYRvIKiv/je83LfQA5n8FCgmjREez0JslPFBByM//FtrcFgsm1mrzSgkufLTdvQ8JYb1XbV6xI7jKWSYKdCidwpvHCWAUqvQDWRVL2cZ7tLyGwSVHtLEsftO3Ykhu7WYpyQmNIG2s6zTuBC9x52IL7hxU9+7127R5L7S2AdPxvUB5Xrz5PJbfKal41mSi5GTJ1s1d+ow6lyB/rU1yGMX12Y0gDTZjXLHvCMq6Ixtcy1igH1nd/C8P+j1vDHbDPyZnIKtx9yBTMsejnv9tdsPpE4ya+eM54l1i+gZEdM8NhFxows/bBHJaljU6DBRvtdYBriWsMHsrsDB2wSuTMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.3
Release Details
UpdatedJan. 16, 2026, 2:42 a.m.
Changelog

Added

  • Automatic Thumbnail Generation: 3D model thumbnails are now automatically generated when viewing files
  • Thumbnails are captured with smart cropping to focus on model content
  • Grid and axes are hidden during capture for clean previews
  • Thumbnails stored in Nextcloud app data (not in user files) to avoid cluttering recent files
  • User setting to enable/disable thumbnail generation in Personal Settings → 3D Viewer → Thumbnails
  • "Clear thumbnails" button in settings to delete all stored thumbnails
  • Works in both standalone viewer and Nextcloud Files integration modal
  • Thumbnail Settings: New Thumbnails section in Personal Settings
  • Toggle to enable/disable automatic thumbnail generation
  • Clear thumbnails button with confirmation dialog and count of deleted files

Changed

  • Thumbnails are now stored in Nextcloud's internal app data folder instead of user folder
  • Prevents thumbnails from appearing in "Recommended files" and recent files lists
  • Improved privacy and cleaner user file space

Technical

  • New ThumbnailService using IAppData for app-internal storage
  • New ThumbnailController with endpoints for storing and clearing thumbnails
  • useThumbnailCapture composable with smart content-aware cropping
  • Thumbnail capture integrated into both ThreeViewer.vue and ViewerComponent.vue
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureuFzoE7HPsOrBKlyKjSzOuEeHjB3dAL7I/y0IF+GNXsgneUJobVLzw+kWlOtZyGa4wNcavAH3caVRlqPjMdw/ZlrlgkJN62qghl/yvbskmmSsiC43ntcKMfGd8Gat1wCXPzTGipRc4DqwbpUyN3PIVltZgpSvX+xaTB3+bLwPNIg2vX2GMpalC9vN2eGa60fPm//OXZKujqfRVwGqg36OpfuRKgOr+J2xDyxAsyc6UywFRGHUnpGLiZgp0jXIRshqpM+k3fpbZDB1JrDbYlPaAZwa4Ze//xxv4Xbk4KmmlLY0S6GXChNbX0rAYrwv+PxJxFeLSI5ytw9iZPdDFQFmgm5RJv7XVhmc31ccZyJ8eaTRz2+JfqBRV5csX5Ox9QIVjmkL3WX70eNIcRUBGX4BFOcCqZxnRgg3moqoSKAZFQ3sOMxwStiKlHhrffZvclDLZRL3S76C492BVlF9HFZc5KYnuF17VQPs1OL8xWFcW57HfcjpMs6xydfnMtfTJUTWbS9p6fjaxLjUgl93ZqWTMmc8dpXl7ywiLjVViTNk8PNn3NzHP3H5yszzBxcO6elz7cIQgXRKdVKB8OQoaeMEjxQQfmWAIViZyXoEEhS76bsI1oeuqAiNH/9p+ytUkBeN7FK6yljt/8QauU38PLTih0QE2xCLB6fP1EMAtLAb7PY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.2
Release Details
UpdatedJan. 7, 2026, 4:17 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • stylelint-scss: ^6.13.0 → ^6.14.0 (dev)
    • Enhanced dollar-variable-no-missing-interpolation to flag namespaced variables in custom properties
    • Extended function-disallowed-list to detect disallowed functions within @return expressions
    • Fixed false positives in dollar-variable-no-missing-interpolation when variables already exist inside interpolation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYV9kZk3oeyVbeTn1qLIVLgb5ERRXgsTQamJCdPTLt4vgt7xf3ShcXd6Srry0UKHzEGvXqQjVq3sfxtq5INZNKQugyC67dlmP+WfFbq7nsLoGJp4Cpjza5ud+4ozbxgbVqNFhNoFVz/zYGyc1lAcSUP8zC10x6zfq/0WoRUFgWHOKCuK6ZB4vQd5J7JRX7JKX1sxbl+rGHSqbQeWuyJwlCir+zr5Y+746EJ+2S29f8z3KFxf2PfEZwAmE1Nf05FAVL5RhQhlDjqM7J2NvQEP6Pod8b6Q+RxFKMWkYyTHncCfQTNz+3VYeuJsqfYC6ZVADYOT5duvHbqOIW4sdnrotXWQDq0uxww7qKYISKp4srSKBwiN1k6EIGt2Clr5FOlji80tIlOtzqanLWQlhpV3wuiVSA8TTbfxPVlB4Mn7dGu/LHc5TjfQ5JEct+5nfEwSCdnLbnEhLyiNRFPppmpIjfHhSWhVrHpz/YRdSarEsmNeP31dSoJKbhNtqhi1oIbtyXG5ElYrTFFg+nxz/yc3Ok33KY+D1nJ9gIN+Qdde9E86qg0cXeIgq+CtM0TGomTKPt9BXQfresW9zuzKli1p7xS3d0C21acP8r6humszQcZQNliz936kTkULULFo86VkyYmK79Gmm16tcMDXbXiZcvhwqF/xrXS6QuQb44K9hC2s=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.1
Release Details
UpdatedJan. 7, 2026, 3:03 a.m.
Changelog

Fixed

  • "Send to Slicer" Button Always Disabled
  • Fixed missing modelLoaded prop binding in SlideOutToolPanel component
  • Button now correctly enables when a model is loaded
  • Affects STL and all other 3D model formats

  • CRITICAL: Cron Job Fatal Error (#65)

  • Fixed foreach() argument must be of type array|object, null given error in CleanupTempFiles cron job
  • Removed incorrect foreach wrapper around callForAllUsers() method

  • CRITICAL: Missing Exception Method

  • Added missing getExtension() method to UnsupportedFileTypeException class
  • Fixed fatal error when attempting to access extension information from unsupported file type exceptions
  • Updated all exception throw sites to include extension parameter

  • CRITICAL: Insecure Rate Limiting

  • Replaced insecure session-based rate limiting with distributed cache implementation
  • Uses ICacheFactory for thread-safe, multi-server compatible rate limiting
  • Prevents rate limit bypass by session clearing
  • Improved scalability for large installations

  • HIGH: IP Address Spoofing Vulnerability

  • Replaced custom IP detection with Nextcloud's secure $request->getRemoteAddress() method
  • Prevents IP spoofing via forged X-Forwarded-For headers
  • Properly respects trusted proxy configuration

  • HIGH: Path Traversal Vulnerability

  • Fixed path traversal vulnerability in temp folder verification (SlicerController)
  • Replaced insecure strpos() checks with proper str_starts_with() path validation
  • Prevents unauthorized file access via path traversal attacks

  • MEDIUM: HTTP Header Injection

  • Fixed potential header injection in Content-Disposition header (PublicFileController)
  • Replaced addslashes() with proper RFC 2231 encoding using rawurlencode()
  • Prevents header injection via malicious filenames

Improved

  • Temp File Cleanup Enhancements
  • Added comprehensive logging for NotFoundException cases
  • Implemented cleanup statistics tracking (files deleted, shares deleted, users processed, errors)
  • Improved share deletion error handling with separate method and individual error logging
  • Added file type validation to skip directories during cleanup
  • Added progress logging every 100 users for large instances
  • Added stack traces to error logs for better debugging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMK0+lREma3H+5o5HY/1XiCvaVsxjUPM6tcB151nPTrwJOeBdbFF50jwp8N7+xgyKEF8GZEOYpwqBu2jYjlN8bJBJFDUI4Givl++H6HAT0/yOhq22oItRC9eOdmrdLiduHqSm10UDj37gda6vOjZwGiPKG6OcmqTkAB5z6tCSX9xH4RznZwbWIpCKBKfUDv53iCyBiogCX3Zyz7443IEdkBy51an9N64COoiWJACcw1kse1f2JtS5ptYTXBA1ImMNOVkX+I2n+b4qp9n972UGMXyx8JnhH7nuVp4XH7nY2oargPPk4b+p1Yqq5/bJGsdazm+i8PaYcJ6UHJTySjTOIvh6MuWpA+xuXTWSgM9k1DUTFG2YQ1RS/JJsFQ4Xhm9GffxDOQ8mSMRp3MnEdMd8FgIU+REqQP3MAYvxQ/c11RWoaolM+VbWrKNGJqB2U2pliu4oM7DRlZg4GmgHvYUMUQQYTq+L1lZlvyQp+NweTuvaUcx6cY5z3SdiRb75o3fYhgNGPDg6ZgsV45Oy6srZofHfGiuYy+c/AkL9Nr8c0nBlZWtxV/OVdPsbHHmNhSVAmv2baFug1mGUcGYRApCmWLlpJbskW3ZNeFfsHp/d/st0LPjhK6ItDHe+6khDFAYoqQWFA2z2nmrWZuD/FzB2Ny3TVCZBQkpzDc7hJiBBxGY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.0
Release Details
UpdatedDec. 28, 2025, 1:15 p.m.
Changelog

Added

  • G-code Visualization Enhancements
  • Smooth rainbow spectrum gradient coloring across entire model
  • Intelligent filtering of travel moves, retractions, and parking movements
  • Skip movements exceeding 50mm in XY plane to remove edge artifacts
  • Fixed vertex color handling for proper gradient display when toggling modes

  • Mobile View Optimizations

  • Responsive MinimalTopBar with all icons visible on small screens
  • Help panel displays full-screen with sticky header on mobile
  • Smooth scrolling support for iOS devices
  • Performance stats overlay hidden by default on mobile and screens ≤768px

  • G-code Toolpath Visualization: Added support for G-code files used in 3D printing and CNC machining

  • New G-code parser supporting G0/G1 movement commands
  • Automatic layer detection based on Z-height changes
  • Color-coded visualization with different colors per layer using HSL gradient
  • Support for multiple file extensions: .gcode, .gco, .nc, .acode (AnkerMake)
  • MIME type registration: text/x-gcode
  • Custom file type icon for G-code files
  • Toolpath rendered as 3D line segments with configurable transparency

  • Extended G-code Ecosystem Support

  • Additional extensions recognized and routed to the G-code loader: .g, .gx (FlashForge), .g3drem (Dremel), .makerbot, .thing
  • MIME mappings added for container variants: application/x-gcode for .gx, .g3drem, .makerbot, .thing
  • Viewer “By Type” browser now lists these extensions via SUPPORTED_FORMATS config

Changed

  • Backend Listing Filters: Updated FileController filters to include G‑code-related extensions in Folders, Type, and Date views so these files surface across all navigation modes
  • Applies to folder-scoped listing, type grouping, date grouping, nested folder inclusion checks, and descendant checks
  • Slicer Handoff Options: Simplified send-to-slicer flow by letting users pick passthrough extensions; all other formats now auto-convert to STL by default (export-format selector removed)
  • G-code Visualization Default: Default toolpath color mode is now gradient instead of single-color orange for clearer multi-layer contrast

Dependencies

  • Updated @nextcloud/dialogs from 7.1.0 to 7.2.0
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturePzIAblqli6tJofqSxWWKOwCp0h/EaSHMlT/+yyoqArTsvBAPzn8xLqU4/58/5pja+YAA7WeQ9egTPUicOilrSFBSHQVGa2QSLQVWFu+vTXKGEuu90qNxXnNqDnl66TVKuWR0OiXHeR2nCAdu3jh7310h8afbBLR3eQAXIeuWlLtXnbzVLoaiqy4pK9l2FzN8GpQoU4/7fo7SH1CFE6iR+Yea4Y2P6C1UXGQfG/RnJylCj1UT+2UkkLUAopk9AOxMEgQpwQzUV5kl4FwvND4biwcuXEajDXwVizX9qAEDtOkzILdV46RcWkyHxnDcqIGHuRImgfqWlwwz6fZ14Huf7/5bmvNafPuSPPRJHEWvH66whuXS5sft+Crvd1/AR+sF2bDGtGtoQo2iSRSCmVS8XtsK0+dW+DLGIvd7pQsul/AIWPDYYkPtMCgAXCFLJpFmmOhNNoCOshjRSgJ5LrnfBC3kbF66gBZGhlkBc0n9MDkqlgxDNVNGwYHYG6xllH4u+1xdGIilc7TE2EXSmpD3GEJOCTLiRdMDbL2ZSC04fd9Lf5YbVgKMggroGFNbtl9VszrOD8GtgpyUqdQnZ9D2U9ef8sJqRxzWKNT3Sr3A2PxgKiCf8G0yu0016JlmwHeEJQAWs9u+0ZpnYo51eSGQKnKtonW1avH6I95Mqh6cPbA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.2.0
Release Details
UpdatedDec. 17, 2025, 6:10 p.m.
Changelog

Added

  • Measurement & Annotation Visual Sizing Controls: Added per-user sliders to control measurement and annotation visuals
  • New Personal Settings section for adjusting measurement point size, line thickness, and label width
  • New Personal Settings sliders for annotation point size and label width
  • Settings are stored per user via SettingsController and merged into VIEWER_CONFIG.visualSizing
  • Measurement and annotation composables (useMeasurement.js, useAnnotation.js) now derive sizes from configurable percentages of the model size
  • Animation Controls in UI: Added animation play/pause controls to viewer interface
  • Animation play/pause button in MinimalTopBar component
  • Animation controls section in SlideOutToolPanel with play/pause and loop toggle
  • Animation state props passed through App.vue to child components
  • Integration of useAnimation composable in ThreeViewer component
  • Improved directory path extraction for multi-file model loading
  • Dependency Cache Insights: Added live cache statistics (size, entries, hit rate) to the viewer and slide-out tools
  • Viewer performance panel now shows cache size and hit rate
  • Slide-out tools panel displays cache stats and clear-cache control with status
  • Cache stats refresh automatically during use and after model loads or cache clears
  • Cache hit/miss tracking with reset when clearing the cache
  • Performance Scaling for Large Models: Automatic performance mode suggestions and easy mode switching
  • Configurable triangle count thresholds (warn: 500K, strong: 1M faces) for detecting heavy models
  • Automatic toast notification suggesting performance mode for models exceeding thresholds
  • Clickable performance mode label in stats overlay to cycle through modes (Auto → Low → Balanced → High → Ultra)
  • ViewerToolbar performance button now cycles through modes instead of toggling stats
  • Performance mode changes apply immediately with visual feedback
  • Slicer Temp File Security Hardening: Enhanced security for temporary file uploads
  • File size validation: 50MB per file limit, 200MB rolling folder cap
  • MIME type validation for STL files with header checking
  • Rolling 24-hour expiration enforced on file access
  • Increased cleanup frequency from 24h to 6h for faster expiry enforcement
  • Comprehensive audit logging for creation, access, and deletion events
  • Security posture documented in TECHNICAL.md
  • Automated Bundle Budget Enforcement: Enhanced bundle size checking with historical tracking
  • Comprehensive budget thresholds for all major bundles (main, loaders, app, three-core, index, nc-select)
  • Historical size trend tracking in bundle-sizes.json (last 50 builds)
  • CI workflow uploads bundle size history as artifact (90-day retention)
  • Size trend comparison shows changes vs previous build
  • Improved error reporting with formatted bytes and clear failure messages
  • Fails CI builds when budgets are exceeded (with environment variable overrides)
  • Vue 3 Migration Pre-Work: Eliminated Vue 3 incompatible patterns to prepare for future migration
  • Replaced deprecated lifecycle hooks (beforeDestroy → beforeUnmount in ViewerToolbar, ToastContainer, ViewerComponent, viewer-api.js)
  • Added explicit emits declarations to all components (ViewerToolbar, ToastContainer, ViewerComponent, ViewerModal, FileNavigation, FileBrowser)
  • Verified no implicit $listeners usage (removed in Vue 3)
  • Added comprehensive migration notes to COMPOSABLES_API.md with dependency compatibility matrix
  • Audited dependencies: vue 2.7 → 3.x (API compatible), @nextcloud/vue 8.x → 9.x (requires Nextcloud 30+)

Changed

  • Cache Statistics Updates: Improved cache stats refresh frequency from 5 seconds to 2 seconds for more responsive UI
  • Performance Mode Controls: Enhanced performance mode switching with clickable controls
  • Performance mode label in stats overlay is now clickable to cycle modes
  • ViewerToolbar performance button cycles modes (preserves MinimalTopBar toggle behavior)
  • All mode changes apply immediately with proper event propagation
  • Dependencies: Updated core runtime and build tooling
  • three: ^0.181.2 → ^0.182.0 (patch update)
  • vite: ^7.2.6 → ^7.2.7 (dev, patch update)
  • Security Workflow: Updated GitHub Actions CodeQL workflow to use github/codeql-action v4 in preparation for the v3 deprecation in December 2026

Fixed

  • Toast Event Handling: Fixed missing push-toast event handler in App.vue preventing performance suggestion toasts from displaying
  • ViewerModal Performance Mode: Fixed performance mode cycling in ViewerModal component by adding proper prop passing and event handling
  • Model Comparison Positioning: Fixed comparison model positioning issues where the second model's position was immutable
  • Wrapped comparison model in a Group to neutralize baked position offset from loading
  • Fixed parent-child relationship handling by using toRaw() to get actual Three.js objects instead of Vue proxies
  • Ensured matrixAutoUpdate is enabled for wrapper and all children for proper render loop updates
  • Fixed matrix validation order: validate parent matrices before child objects to prevent "Cannot read properties of undefined" errors
  • Improved scene hierarchy validation to include all objects (grid, axes, lights) not just models
  • Fixed matrix update sequence: call updateMatrix() on all objects before updateMatrixWorld() to ensure proper transformations
  • Comparison models now position correctly side-by-side with proper spacing and alignment
  • CSP Compliance for Texture Loading: Fixed Content Security Policy violations when loading GLB/GLTF models with embedded textures in Nextcloud modal viewer
  • Patched Image.prototype.src setter to automatically convert blob URLs to data URIs for texture loading
  • Patched URL.createObjectURL to track blob-to-URL mappings for later conversion
  • Patched fetch() and XMLHttpRequest to intercept blob URLs and convert them to data URIs
  • Patched THREE.FileLoader to handle blob URL conversion for texture resources
  • Automatic detection of modal viewer context (iframe or Nextcloud viewer) to apply CSP workarounds only when needed
  • All patches are automatically restored after model loading completes
  • CSP Compliance for Buffer Loading: Fixed CSP violations when loading GLTF files with external .bin buffer files
  • Updated setupResourceManager to use data URIs for buffers in modal context (instead of blob URLs)
  • Patched fetch() to intercept data URI requests and decode them manually, bypassing CSP restrictions
  • Supports both base64 and URL-encoded data URIs for maximum compatibility
  • Animation Support for Multi-File GLTF: Fixed missing animation initialization for GLTF files loaded with external dependencies
  • Added animation detection and initialization to loadModelWithFiles function
  • Animations now properly initialize when loading GLTF files with external .bin files
  • Animation controls now appear correctly for animated multi-file GLTF models
  • False Texture Warning: Removed automatic texture warning banner that was incorrectly showing for all GLB/GLTF files
  • Warning now only appears when actual CSP errors or texture loading failures are detected
  • Improved user experience by eliminating false warnings when textures load successfully
  • Slicer Upload Safety: Hardened temporary STL upload handling with size and MIME validation
  • Reject uploads over 50 MB per file and enforce a 200 MB rolling temp folder cap
  • Validate STL MIME/header before accepting; reject invalid content
  • Enforce rolling 24h expiration on access and clean up expired shares/files
  • Use rolling +1 day expiration for generated share links
  • OBJ Texture Loading Robustness: Improved OBJ/MTL parsing and texture handling
  • Preserve texture/MTL paths with spaces and mark materials for update after textures load
  • Use existing blob File objects for texture URLs and tighten loader logging
  • Handle texture load failures gracefully and ensure needsUpdate flags are set
  • Texture Dependency Lookup: Skip direct “find by path” lookups for image textures to avoid unnecessary 404s; rely on directory listings for textures commonly stored in subfolders
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signatureacd0z9v0ifd6EhLDkZPQmIADZp+TPcgSOqKKp6W5KUtq4CA/Xk0lTNssZpirrEaLO4s15qemVCz87Aa6hhxGipOFlBSMFIyc1U0q8OQzHMOa4cYU8w1hvmi7aXeG+IxPWXelCAYaBNYGbleOvhc+/ao1gh1mn+3esakeTLdBi8XihNAKBDzglkOD30oBunXuzXJD1chBFBx7LCMoRhVXHDxShzi39+vbumkbme4IGV9kL323hTSjD1+XrMeiIG2P9tvHDEkGRXEOKohmWQNrsFCamApVqslvRzg6mBFTFv+dtnBUFbbQnC25IvyE+UNY4j2TGzncrW2+jWGddicKYq4SIrI7RzB91gTxv+3qzwCUb8OlyaU38L042l4HJXSFMPh8Mmj2n/0zhWlH4M2h/ZEPjySsaK8lU/x/DaBoyLcPoJcDsuD1haONv6mpRsoz/ohXdV/U9s61rbGOnJkSqMMblFoVXVmkzTKpBatf08e9Uaig21b9q/ekYyhbVFlujmAu9ku9EWk5SddY26zNvSeiGinV1xV3BayMI3rVZYINZkPb72Dv6DOInRWlVOxVqLQBUY98ejlhpls+6l3IdtTDUmrITmGr+Nq3b8T8kgNJpgNQbf3Ij14MgdfNsxd4nxMyX/4hsOSZdU4dAHsTfatSxRgM1q9FpuvZ+OkyWMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.1.0
Release Details
UpdatedDec. 6, 2025, 10:21 a.m.
Changelog

Added

  • File Browser Default View Setting: Added user preference for default file browser view mode (Grid or List)
  • New setting in Personal Settings → File Browser → Default View
  • FileBrowser component now loads and respects the default view from user settings
  • Setting takes precedence over localStorage, ensuring consistent default behavior
  • Manual view changes are still saved to localStorage for session persistence
  • Format Sync Test Suite: Created comprehensive unit tests (tests/unit/Service/FormatSyncTest.php) to ensure format definitions stay synchronized across:
  • Backend PHP constants (lib/Constants/SupportedFormats.php)
  • Frontend configuration (src/config/viewer-config.js)
  • Nextcloud MIME registration (appinfo/mimetypemapping.json)
  • File Browser List View: Added ability to toggle between grid and list views in file browser

Changed

  • Format Definitions Centralized: Consolidated all 3D model format definitions into lib/Constants/SupportedFormats.php as single source of truth
  • EXT_MIME_MAP for extension to MIME type mappings
  • CONTENT_TYPE_MAP for file streaming content types
  • All repair steps and services now reference centralized constants
  • Eliminates format definition divergence between components
  • File Browser Grid Padding: Updated file grid padding to consistent 20px on all sides for better visual spacing

Documentation

  • Corrected repository URLs and upstream fork instructions in CONTRIBUTING.md (replaced placeholders with maz1987in/3Dviewer-Nextcloud).
  • Updated TECHNICAL.md with new controllers (SettingsController, SlicerController), components (PersonalSettings.vue, SlicerModal.vue), and detailed Personal Settings + File Browser implementation sections.
  • Added comprehensive "Adding a New Format" guide in TECHNICAL.md with step-by-step instructions and code examples
  • Expanded IMPLEMENTATION.md: added Slicer Integration & Personal Settings System sections; reorganized and deduplicated legacy "Code Audit and Cleanup" content; refreshed Table of Contents.
  • Updated README.md (docs version) advanced features list to include Slicer Integration and Personal Settings.
  • Added troubleshooting sections for Slicer Integration and Personal Settings in TROUBLESHOOTING.md.
  • Expanded test coverage notes in TESTING.md to include new controllers (Settings/Slicer) and components (PersonalSettings/SlicerModal).
  • Normalized wording and removed outdated dual-mode duplication in implementation documentation.
  • Documented dual-mode viewer architecture in TECHNICAL.md with viewer lifecycle diagram (standalone vs modal modes).

Fixed

  • Settings page image/logo path resolution: replaced hardcoded asset URL with imagePath() helper in PersonalSettings.vue to ensure correct loading under all deployment paths.
  • VRML preprocessing duplication: Removed duplicate preprocessing code in preprocessVrmlText() that was applying BOM removal, line ending normalization, and null byte removal twice, causing inconsistent preprocessing behavior.
  • Flexible texture matching loop control: Fixed nested loop control flow in texture matching logic (multiFileHelpers.js) by adding foundMatch flag to properly exit outer loop when match is found in inner loop, preventing valid texture matches from being skipped.
  • Premature texture issue check: Moved checkForTextureIssues() setTimeout call in ViewerComponent.vue to execute after model successfully loads and is added to scene, ensuring accurate texture loading status assessment.
  • Debug logging cleanup: Removed console.log statements from FileBrowser.vue component (viewMode watcher and setViewMode method) to improve production code quality.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureatLgIGKAv/DO42HgoDbqVaos4kWPFuPmF5l4PHyM5S5ivOlmqnC5xu1+d5uX/h+0rUmL+pG6wKezHo7WT0PVKSBciYZzlb5D+6sJisB5x/jMVqFaQRmnEEPKfoz2DsDucmXtH6Ppv3oXjyKd5wDjyFMwtoG1hU43QBeA4ngYTMTCXqdITK5sT4OM4IwiYhUi7zZRh6/YoRxgojXIqo/RasM3sGIUIrQs075RKqXcdGcGUwJgQPAQnGsMEW9D0LODp6bYurgBZRgnmgBrpTE3D7Ktt1G+ppQTSW4tHpuq3mFJdhFlkbhbWJw9QW28xJmDnqUGfhzXTSMqoJLnbio0n7iT5bhiyxBmhi5patzHifmDwndpRPoZNbw7vxr2zzqCc+1pMt/CoXHn1PTthmYtQMg9tvyjS7jbCdYtDYLtluzITRHP8GzRXIMmBrCRzXLymk0b3IvwyB9G83+QwzHyWSTLsKV314CSasumGhFDSxnz0E2DhVVZV7yaG89Dj9HHwmxkQVeh4iI9DCd5ynu6q/8Ljyp4nJ1p//+3WCo3NksaM0ubqkXiVFnSTnSjsoDw8mbP8TU/iESpYJ11llVA9ftZSGnnczY2zFKKK17C5m/pjE9sskLlo2bfhBzRLvYOV8ejRyaXPK1B6ejCfk2VwWntq7UKFh/+/1R5usmQkRg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.0.0
Release Details
UpdatedDec. 2, 2025, 2:53 a.m.
Changelog

Added

  • Personal Settings: Added personal settings page for user-specific preferences
  • SettingsController and PersonalSettings view for managing user preferences
  • Settings routes and configuration updates
  • Enhanced File Loaders: Significantly improved loader capabilities
  • Enhanced FBX loader with additional features and better support
  • Improved VRML loader with expanded capabilities
  • Updated DAE loader for better compatibility
  • Enhanced multi-file loading helpers for improved dependency resolution

Changed

  • Viewer Enhancements: Enhanced ThreeViewer component with improved controls and features
  • Camera Improvements: Updated camera composable with additional functionality
  • Circular Controller: Enhanced circular controller with better user experience
  • Theme and Performance: Updated theme and performance composables
  • Major Version Bump: Version 2.0.0 introduces significant improvements and new features

Technical

  • Updated GitHub workflows with improved condition syntax
  • Updated Dependabot timezone to Asia/Muscat
  • Added change detection to prevent unnecessary PRs in workflows
  • Updated stylelint to 16.26.1
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureJrtVI0UiQsSqL82PvCmO+FLp06iE6caUY8nD1+dKlVr5YjeTvZjLUtd0sAbmwQzlR2iC4IuRJllv7hr5cWk8oWuJtnLuE4uzcXu3bu8ndzO/Zyf4ph65Fn3GyML8Zdm6TqECBFCzk+2NG0pVxt4TM7qML5p437Q6IrF0+uOh3Vj55CTf160KAjEN7v2Sa8CT4Qf/JPKtnfyAgnKkNRXMQCf7xXe3RqtgtiVPnNq6nKfYzERoihIHhZ4LqLewUfY7jvxKUpAIxF89K+yTk1PmsJgKPBN88FF3ReKwcfJcfIhMXuH7CJEHCg++Hbd0oxzvl3uv0o8k5USdkojH0vG5IQaBF8AAEGY0aKCsScmffSN7WvmpPngsHs56V/olPNPVG1QNepRvSvBljr9D6idShiRlaW7uOBl9SVwLernLBjZ30bfqRtdrm2T7oO6gozY0zRc3jxt+ajAuaPLp8pAKC/Bv8nt3Og7R3r/mpk7qXWEgC/u78v/LqjhtICx1CQDSROq8vuces8k4iEN1kJabrhxLFPDPmZucoXWSzUStP+ISyPksq+pk5KAtnKuVRMTN8rhYkMlyK1790OOF53Q1bvlHnTc3LmzTLi6b/mVPBtz+KgkHHoU8j4TYwSlnbmSx6gDqzrQa8q+cRXlK1NNmF211NX0J8B9nRkng+xHg5ts=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.8
Release Details
UpdatedNov. 28, 2025, 6:24 a.m.
Changelog

Changed

  • Dependencies: Updated development and runtime dependencies
  • three: ^0.181.1 → ^0.181.2 (patch update)
  • stylelint: ^16.25.0 → ^16.26.0 (dev)
  • vite: ^7.2.2 → ^7.2.4 (dev)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMKCHrKGV1G4CZgG/gyhGvg34zK0migxfZ/FS9ZZEylzffGxQxoQbW+wubOR3BAjgWRx5smOBIdKVeLLuPOV00JnPgrII3lRaltLVO72nNrYtVh5DruKC5ZoMM4BuJsdEOFP8euy/GDCmlhQYNTusS/T+meggfylcrAjZAWR+SZZfb4x7QlBJTdmCuWpHNBJO081IpeLfn5HXohFAWm6sHHm6mun4ar6znAlwsZ/pe6Lo8ZjzLBuDN9mLEujrw6AgjTq2DTSUrHrJ9/WQF3zBiYjRLMNurJLnrwCXuUmPkKGWrPM3ybFADqfgb3QmqzPsPiGUL87k/1yVewEqE2olFpVRZ9hxa11ANingG+zMZwY1bCbzIcrRCq4Uoae7zgwZV/CF2wJQk+cRMbQl7Y7AoAy6PPECLMFtU93VgHqcPtFQzAxSPEXRNW+GpkVFHWvBZ7iuL6Ys8gMjl9i9imuNYcIAkuLlKLmu8t5pQpOLKvNpiQkeIf5aWXHm5Fvq7+zmeKIsuMVNvPoqQOSlC1AkwnLi1EAf/2VrqbAmDoC/o3Y6h1mg6vskUNFRPhz2h37TEb65h1bm9kFjNx/6TNNF0xM96GSAt7v+I1naBODhvGqDqyJ653663yuE+CxdKd1kZ4ous3urqXRwAmKHVdOvXYZTb+N/Feu7PHqrdcwlHHQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.7
Release Details
UpdatedNov. 27, 2025, 5:42 a.m.
Changelog

Added

  • AnycubicSlicer Support: Added integration for AnycubicSlicer with custom icon and URL scheme. (#52)
  • Folder Exclusion: Added support for .no3d marker file to exclude specific folders from 3D file scanning.
  • Hidden Folder Exclusion: Automatically exclude hidden folders (starting with .) from the file index.
  • Temp File Cleanup: Implemented background job to automatically clean up .3dviewer_temp files older than 24 hours.

Fixed

  • Layout Issue: Fixed white empty space when hiding the navigation sidebar by ensuring correct flexbox behavior and explicit slot usage.
  • Viewer Resizing: Fixed 3D canvas visual resizing issue by syncing internal resolution with CSS dimensions (width: 100%).
  • Slicer Icons: Fixed missing slicer icons by using the correct imagePath helper for asset URLs.
  • "By Folder" Navigation: improved folder indexing logic to correctly build hierarchy and handle edge cases.
  • Server Error: Resolved persistent preg_match error in PreviewManager by disabling unused app preview provider registration.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureLfmZ7uqqVk6tDgQQuwcGuzQml5OZWmFvpfNYW9Nd+3gVtIRUCBKsgtB8D6CMf1ymq2JxllnMvhATHE3YVZGmCsL5CRrj6al9+zdVnP8An2BUBP/DsqDTTwLM64mwKFIqYYkjtwVfttaa/aPJn2cdH7umzjOEHsYzbRx7eiP1lq2ERy4KYs7aA26hfleg9kKbr1jKTf0/vPCHvmbKg+o3RS+QfP/lRuoPjk1UYog52IAHd9bpAtAgmB2yLSfghjPSvMhZkyWt38CKMiKyO2R+VdB6wc/lWWVWq1JfJDw9oZlhaMpBZhXVDqR7uoMYdtR2pTNhB6d+kaTQtclvLSO5RREUDMwdocOl7iVMwrXCmKmUIyDqMa/VF3FFtK31fKlSYuvIhGDi1He0NNqlv2jiMsb2EPhrYLBHTyrmY0Px3i+ZmbYos1sFXLi7CliafZY/nZwxqPSc8xsZI42MFO9ZWA7yBKYE6UHphNnbxTbx5H33NrwHdRhOh0g+wpLJebbjyi1momlshADpfec+gAImcCnyaXhIX0Vpt73R1DmadH0xbi37iCEg73s9h6cul1RGeRhOzr+c8nhuFOUq4wodj9VLbTwwqm+bjd1jvshyo0++A0Nhjfj79phnnIJl0xsKU9QaN1eFlB6vPQRMyFswy6bWW4IW/yI2KL7693JFhRE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.6
Release Details
UpdatedNov. 20, 2025, 12:48 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
  • Folder Path Length: Removed the 512-character limit by hashing folder paths for indexing
  • Restored folder_path to TEXT and added a folder_path_hash column with a new migration
  • Existing rows are backfilled automatically so deep folder structures continue to work
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature4F3B+MMtZoJiTGIBdBSaJDW9PVnv2o38ob0m+f5cIJmMM6jpgqYETLsQaW1zRCdQ8ozE8v5LfkehmhMlXy8folvlapuRcDGh9N7PPIRzTM/8b9ymOMQ1vkBPERw8k5grkUwFNamMPw+K4rFGHLMflN+vR/S6m0jmAKls/2L4kg2UdhaTdtaw/ozZAM4a09dJ038kyBS6jbh6g/M233Ue/R6aXSCMVgrA5zw38djqE9O8zmUg8xOJlOzdMRCRdhKWZOHoocLV9TBTasdU99YG7o+VIgAXeTY4X0rKxtOdfqdX+c+YNEt4syMmig/1p3e3yO1q0CmQPg6rgxtrdVOW1KK6Bbsvd8DkNwyQ0SgREq8bD+SIlF3QfqTDAFKCDgjTCn9N7HBnZIVa+NtseVrVfVeEdWdVNGMnfu9QhSeMqrE5dJMzKJLiPy4ftrmzM+aff+3aoQU69wWVffIBw1RsC3+352E90jiYw8CnH0ul8I6ffA+NVD4v8+Jo2MruxzwzTsq7/RfQMvIhsCTrhyauECtmwAQPEx9CX4h+7nryjxefgDwPQlFyszt6F53LSTWEeFO49SAlV9dH5P6tsjsMozdUts7HtG+UU+T7gHD65rY/nPL8ICF6C5YHGuuxwDFQGAkKtBNN/9VWBZr8h1X9jq1o57hwIqGBnxNdAs9YqdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.5
Release Details
UpdatedNov. 20, 2025, 10:08 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturefF9sWdsS9s2UfdNuMWzSK76VLnucCn+KchXB9eFwbDsdXACnQYvhUVvS+xoMBYIfaXQEYUjfIl9oc0/sghPaIfdPvHwhCSze3XUPHkeF8yHb7eXQfHPr8N6KQZdmFM50brxxpHt5yxI40xZU+mMyW3eTP982e+aLenLEr2taQYNoaTHf35NbVknGLAE1xautWR1JQzYyVkCcc4T9/hxMp7LaGBrUPwdDALyuAp6WTHtZk+XeOxxxwRiNb7q7SpXWGuwdbX7MDvEzfBUmhw8dbORK3kqxgfXFAe7uGVdW+5sKtkdn+fxuN4gHKmBwW8N4X7YW1XaAL0KkYFoHsrdAcHunsSn/KfuqhTLHRpxOblgFPEcCwwqWcPVdGASFkD96rYylV9yzoJV2zS/pDqMVG38TQjHOs6ukBbJ5KHzpGJpgOZXDzLn24or5wVubjXkCyOsfS0by89VmrrxrERJZPNl6XTgeCZaBTPsypeWAlezfqVEjVVmdkoKuL+I3m/gMURucZV3Do+wHmxoEAT/2bTRVtIZzBsjrwuljZk4cok0oQ8reFBlngACWmXWskJOiEmshDUpnd2IykjhaU6LPEBDprd0fSM3+RE6Sx1+iJuPdXQ+CUATytcbKc8kiEjcPapzQ7xt3uR2fukG3eLrkFKoKHM7jJtWWMcWmN0yppuQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.4
Release Details
UpdatedNov. 20, 2025, 2:09 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYu4MvwQG1iiUU+ZnS3qdmTsCZ0NYAx89VzPzGTNqkdgQ/LolnwTZNDpkHEOoGQOM7Q95wtrJ1iHj2KQNxHsg/nw26/L0nqQtE8+HN92EVkjKhj4Chve7AB+A8dFlj0nXdSpgtH/PbwN+90lQTCGnmd6rRc46+OqEfEoFzjBhqtrpzXuURKsZG4GV644vSvAyzZFBDncgI7tlGhUhCDj/5UShHQy9ClymB6Yb5K7smP0BSDEWYOxsUOj2/cy7hAIlCyofINC8p6UzA6Po6NiyQefX2zds56a8TZhvvuaeiO3setCQzKXdYp3WqlnVVSpRA7UZfGeqdvkDqtQuTIs4Zo9w6GWX9M+N2Z15rQ59t/cNX0OYhCs+Fw2UdJMAPYW+me911YHsaMmANkaw7sUZ0ncqxbc0NZ8NULhJakF4DHizsCSLZbVrOhpfcaDEpsy6q88bg6o4modcj+gTIytUCI/RLjTlKWYAZcRFJfmPngQiuUmBeZntR2y1a0ROFsmcRAaSys5UFHF1KuqaNocqcAo+CbN5vRqMQXnmGSg54kEHvQ6xjDki/rs/0/5sxIX5TrGhGCyubdpN5Avv1y+eZfMdxVrZu0wxOubN3zgF47b/O5wql0Mv2tpFH88gy5XMGRTyaB1fIeFQXV874btSvkBwJPJhKdTZV0pyA9C+wUk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.3
Release Details
UpdatedNov. 19, 2025, 5:36 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYmgaFcAnfaw/gFhMAFJupT22RE/RWydQkyTNQrbQNyHQGq406C58CN9hKEIJ5kaSzibif3goqa31T/si8GlYBnejJmQzYsQpr5Wua0dSrkUtCv0fRFv5GNWJvazyREN7XKWp+Ir/Q51J7trkGs5RCuZRnH1DhHTPyieLyZLTFsSWTSPuDRHEnpqr91B0y+epvQGujYSYG931+5tc6QaX1rfZ9kmT1qNph4SdaLeMa4hbiSFc7fuuSSNqvE8q1QPrBJEz5TMixrr1U8iMA38HBWVsufDfd+lg18bVWg3zQG1yFNZn/RYCuPck6DPiDo2uJ/9AJzOTQpJEFjDfc2zG77oE7EufwDtoaJzbhXhiseW7u0Ty6nVywrYTnEbfuIYhHeo6lbxY8KtoPLZSDSiSbH73oigsGW7o7xA+RDjtVZoWRguPZF9VfUjJ1v5WQpHD0jdZflYJ3byMygWF1z7GaT8wE9gbzwvWUyMGE6XCpzwpNxx3yOlq0mp5aUNFAjJYAO8BSgaFBxSBVnXTv22JEnhaUPAWL3HaMh3EJLVJla/TOG7cd7N9eS6JT3maO8Z7QUu32B/ah48w/uxk9qzmBX+t1onXwUA/6CuY8Gfhcg20AK5JuUiqhIZD1eW7HiC0pNJJqZbzLbXBl049JBFUqZbKLdf+ttISPjCmlRTnxdg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.2
Release Details
UpdatedNov. 19, 2025, 5:32 p.m.
Changelog

Added

  • Database-Backed File Indexing: New tv_file_index database table for fast folder, type, date, and favorites navigation
  • Automatic indexing via filesystem event listeners (NodeCreated, NodeWritten, NodeDeleted)
  • Manual reindexing via php occ threedviewer:index-files [userId] command or /apps/threedviewer/api/files/index endpoint
  • Migration automatically creates the index table on upgrade
  • Smart File Browser: Complete file navigation system with multiple view modes
  • Viewer mode: Opens 3D viewer by default on app load
  • Folders mode: Hierarchical folder navigation with recursive folder structure
  • Type mode: Browse files grouped by extension (GLB, GLTF, OBJ, etc.)
  • Date mode: Browse files organized by year and month
  • Favorites mode: View all favorited 3D files using Nextcloud system tags
  • Breadcrumb navigation for easy navigation back through folder/type/date hierarchies
  • Consistent card-based UI for folders, types, dates, and files
  • Per-User Configuration: Remembers user preferences via ConfigController
  • Saves preferred sort mode (viewer/folders/type/date/favorites)
  • Remembers last opened file ID for session persistence
  • Mobile experience: automatically hides the circular 3D controller when the viewer detects a small/mobile viewport, preventing overlap with the canvas controls.

Changed

  • Viewer opens by default on app load; the file browser now appears only when a user explicitly selects a navigation mode.
  • GET /apps/threedviewer/api/files/list now serves hierarchical payloads from the database index (folders, types, dates, favorites) instead of scanning filesystem
  • Supports includeDependencies=1 parameter to return all files including textures and nested subfolders for multi-file model loading
  • Dramatically reduces filesystem scans and improves performance
  • Navigation data is loaded lazily per sort mode and cached so switching between viewer and browser modes no longer blocks on loading every file upfront.
  • File browser UI refinements:
  • File cards now share the same compact layout as folder cards (consistent padding, thumbnail sizing, fonts, and grid spacing).
  • Type view heading and breadcrumbs no longer show a leading dot (e.g. GLB instead of .GLB).
  • Breadcrumb component now handles clicks directly via NcBreadcrumb to improve reliability.
  • Remembered folder/type state is cleared when returning to the root via breadcrumbs to ensure a fresh reload.

Fixed

  • Newly uploaded, edited, or deleted 3D files (and favorites) appear instantly in every navigation mode because the indexing listener reacts to filesystem events instead of relying on manual rescans.
  • Root breadcrumb ("Home") navigation restores the folder list correctly, even after drilling into nested folders.
  • Multi-file dependency loading:
  • Backend listFiles now supports includeDependencies=1 to return every file (including textures) and nested subfolders.
  • The dependency crawler recursively searches texture subdirectories so 3DS/FBX models with textured assets load successfully.
  • Texture search now uses the updated backend response structure to avoid missing files and 404 fetches.

Technical

  • Created lib/Db/FileIndex.php and lib/Db/FileIndexMapper.php for database operations
  • Created lib/Service/FileIndexService.php for indexing logic
  • Created lib/Listener/FileIndexListener.php for automatic index updates
  • Created lib/Command/IndexFiles.php for manual reindexing command
  • Created lib/Controller/ConfigController.php for user preference storage
  • Created lib/Migration/Version010902Date20251116061241.php for database schema migration
  • Created src/components/FileNavigation.vue and src/components/FileBrowser.vue for new navigation UI
  • Updated lib/Controller/FileController.php with new listFiles() and indexFiles() endpoints
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5KdirqBqYm1NTouEBce9EtmTO6/UJwHsg40DC/oCvhIuYOF8eLVWy5O5Vwx25hFvq7bHrr9SQQ9cdJS1G+fY8aQ7bSeqCR1dAdUlsoBhrYWQnKAEnoG8vIIxfk0gA+HgcRGLJPtph9TYLdeAqdhD7/VVUg8l9ZdIVJHsTQn8JijVkTeMpk5612wUyoMn7HaA8IJJrOnU6dmkfv95FcZbVLzR6QRDc2fqlM3aYbZl9KOVYzEU6e736rsQAOVn0IqrHmQyJNVIdiFnycbtRiJnJgRJj05KblfVIOC0qZwzl7sqIHdAswvYwDmdCoLK/rblTc8R7INoUFpmGFBzPITfVwBNrJh9jsxxuWnl0X/vAAtVACVGHR1utLtd4dMFSjwOVxD3f53eazH9/Hho18BBh3GOLNJEWAGNal3u8Ng+inilqQu4Nmhds9AYUoq6T38oU9NABr7mDce1TsO0AYQqfez3HBodgbbBOoXKSGLMoDCk9T71NR2Oc2XC8Urz08F9Q/0Dcoy06kdvm9neAOH+yHqjcPXfI98JCdqao6Li05VQaLOUxIyYbVpngGu3LWthpaH7rlxhD5DlOwknjimFfGifjZ0/yScHSbfRqtgxy8q8jfHJ71wMKOAzQ6xCgqfuWWROUqWNmo4ypsE23Ioj1n/yEA60bComBBgSdSjCwpA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.1
Release Details
UpdatedNov. 15, 2025, 9:35 a.m.
Changelog

Added

  • Preview Provider Implementation: Implemented Nextcloud IPreviewProvider interface for 3D model previews
  • Admins can enable/disable via enabledPreviewProviders config in config/config.php
  • Integrates with Nextcloud's native preview system
  • When enabled, provider is registered and ready for future preview rendering implementation
  • When disabled, Nextcloud automatically uses custom filetype SVG icons

Changed

  • Updated dependencies:
  • three: ^0.181.0 → ^0.181.1 (patch update)
  • @nextcloud/router: ^3.0.1 → ^3.1.0 (minor update)
  • vite: ^7.1.12 → ^7.2.2 (patch update)
  • @nextcloud/browserslist-config: ^3.1.1 → ^3.1.2 (patch update)
  • Improved duplicate registration prevention:
  • Added guards to prevent duplicate file action registration
  • Added guards to prevent duplicate viewer handler registration
  • Enhanced error handling with try-catch blocks

Removed

  • ThumbnailController: Removed custom thumbnail controller endpoint
  • Replaced by proper Nextcloud IPreviewProvider implementation
  • No longer needed as Nextcloud handles previews natively
  • Thumbnail Placeholder: Removed dependency on thumbnail-placeholder.png
  • Nextcloud automatically uses custom filetype icons when previews are disabled
  • Custom icons already registered via mimetypemapping.json
  • CSS Thumbnail Overrides: Removed CSS rules that forced app.svg background on thumbnails
  • Allows Nextcloud's preview system to work properly
  • Custom filetype icons display correctly when previews are unavailable

Fixed

  • Duplicate Registration Warnings: Fixed console warnings about duplicate settings/registrations
  • Added registration guards using window/globalThis flags
  • Improved handler registration checks
  • Better error handling for duplicate registrations

Technical

  • Created lib/Preview/ModelPreviewProvider.php implementing IPreviewProvider
  • Registered preview provider in Application.php bootstrap
  • Removed THUMBNAIL endpoint from constants and API documentation
  • Updated openapi.json with preview provider documentation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureyK5Dmfw2bZsjqJMtY0eaIkST1jQZ4XKcPtlHp4f7GNHWV1A5PrKjkxKHO32JML49YXEI7D8PouQvnHXDq55pRDm/PBmXV3cB5wvx3ZkMVMGRQBqHNR2iBfSsl6w9I0SaGSgNSt/aa1kmZRCq20GPv8hLMHSJdcR2VpU5mA/BLp6vP51LBp9wDwzCEsU82zX6VVBXkd5YIHY+6HMRsNQNXFfRsWERTq5yjmTYfyK4Zq1rvZShPX3KG8WDWl6g3C1lJhyr/7FWLoM7Lsq6k1pirdAYUtQO7ooB0UDiTpvEgmCj/OLhL/8dvaD29dyzUxnokjj8IlpiDEcPfeLN34nqXHfMbmPZh9+aqBqsZr0GaqSD6dXGoplkBc+F/SHfWdevkZDHk8p/W0flcjIThr+K5/8YhwdDZd3G2JIRmhDdJXFfoTn2f+zYkHTV3enXsRAG95FVU3WE6zZwi8w4MOUiXIpxqb/DAISRSId3k24iPFlbguOQwPvcpMGNIruPRkoE7ihCgiU+dvcQ6ieYtwvtAHgNy+N284BdcsK7/0g5tCkzDad8Fdt4pMImPkO+cWBy3c19Uce5+pBx36Nts6q5so6O96s0Dcnf4NQPsnRFHNKduWxafeoBHCwn2NoH2aIRw0JQcwyCLvL2j8vLcN6PhAQwJ2I6P/4yHX5aEWEwaP8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.0
Release Details
UpdatedNov. 10, 2025, 6:57 a.m.
Changelog

Added

  • 🖨️ Slicer Integration: Send 3D models directly to slicer applications for 3D printing
  • Support for PrusaSlicer, UltiMaker Cura, BambuStudio, OrcaSlicer, Simplify3D, and Eufy Studio
  • One-click export with URL scheme integration
  • Automatic STL conversion and temporary share link creation
  • Professional slicer logos with brand-matched colors
  • Last used slicer appears first for quick access
  • Smart detection of uninstalled slicers with user-friendly error messages
  • Auto-download fallback when slicer app is not registered
  • Temporary file cleanup after 2 minutes
  • Share links expire after 24 hours for security
  • SlicerController API: Backend controller for handling slicer exports
  • POST /api/slicer/temp - Upload STL and create temporary share link
  • GET /api/slicer/temp/{fileId} - Download temporary file
  • DELETE /api/slicer/temp/{fileId} - Delete temporary file and share
  • Automatic cleanup of old temporary files
  • Proper filename sanitization for paths and special characters
  • CORS headers for slicer application compatibility

Changed

  • Updated app version to 1.9.0
  • Enhanced toolbar with "Send to Slicer" button
  • Added slicer integration to slide-out tools panel
  • Improved error handling with toast notifications
  • Updated translations for all supported languages

Technical

  • Created appinfo/routes.php for route registration
  • Added @NoCSRFRequired annotations for API endpoints
  • Implemented Nextcloud native share system for temporary URLs
  • Fixed filename handling for files with paths and special characters
  • Added proper authentication and cleanup mechanisms
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebdu+2mjB9NyEPJbbogKcFBovDi99alBx7ixiNLiVWRmMw63yphLItjrEkmMxHL5KeEmkQj05gqf17mPXlngrWI3pkhn1gLHtbCiPwOqloKdQ+bf0US34nClshbCzP0drRsNWSxhEWbd0a0vc1Zy31vk9I2PVHugbpbo4BCy20Ks6t0aCkv6o+g8x+v+B348txV1p1F5hyM+2BBUqxEB8fNiRJ/p6bd1eMf6lWfeqBrWG6n3zB11BsKKYAa941deyJ8vLlEpI8D7Kyz7TVHuHzx9hlxbZyOCYEnD52EmpYDl/VaKnj+9EzxaFDqkQPOIHPbMtfe9tc0nZOrqEASXrUC/iPMpp1JNAyRa0Egx+DkdRE/p6wFMBxipGqfgb7MJ6gV4I7t7ML1scquVtcc8aZt3oNhdMd6gPZ7/uxOYQ8Bm6YX6fiuwxtqe5yb2uwiw9oKjQhs3miEXEAiaiab3S4HLC6Ke9QYowEzASoAD1tHf+kf4I6+IszL5V6XOVzXRhKU8t/Vdl/JSZh3LEOma2KCPBoxIwove9PpHARcEIz6Tk4eu4UvMdTjhyIBKCvEnq8Bs5fzaq+jqSuE+8h3Y+qP0Q2fSqcNHpLtBEAfrkSu883dCM7dg0miDxSGumfqn+oJ80bVDGm8mQ3WdP7AvJiygdj5i2pFqyeiPE+MGDDUE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.8.0
Release Details
UpdatedNov. 6, 2025, 2:21 a.m.
Changelog

Added

  • Screenshot Feature: Capture high-quality screenshots of 3D models directly from the viewer
  • PNG and JPEG format support with configurable quality
  • Automatic filename generation with timestamp
  • Accessible from toolbar and tools panel
  • Download screenshots directly to local device
  • Fixed WebGL renderer configuration to enable screenshot capture (preserveDrawingBuffer: true)
  • Billboard Text Labels: Annotation and measurement text now always faces the camera
  • Text remains readable from any viewing angle
  • No more reversed/mirrored text when viewing from behind
  • Smooth rotation as camera moves around the model
  • Improved user experience for annotations and measurements

Changed

  • Updated app version to 1.8.0
  • Enhanced info.xml with new feature descriptions
  • Updated English translations for screenshot and billboard features
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebvx0cRDWADLGiuJ0jIg9f+81Add0o7SM3Dkq8foP/AnLfM2fSL1/ds6JsckXbP8xdOMoPyAxnN9XFi6qckR+cTiBSkkbpbQeg32ZDShLwwVRH8dubBAHkIk7qSBVjw4gV2BdKuMjWZZ5cRPlcFNUtdVP776NXH6Vv5FxK4UoudEN3VA8qDZC9+8fC46n7NhnobbviHIzeNqPA0V3Wo50UuuptyDdip0o/bbBiaw1vszWuV4QoHFMHAjFan2v3kKb5ptIHYukVOLMj8rY1dKeTACQYjEwi+YXOV+4eJMiMrHXx06eMYxiOsTdNHsCWTy9PW+4QTHM9dI/GNygSKC/V4Z9lxlPt7jncHOAT5P+tEFrWQYImvBtWsXPXoP6UPAIpP82NPWhp0lBZdjR3Fvx4yi0PM7V4/lRTztUkTrobnlVNpIxSe6SH0jWmVlyAFAW0XSiWrWbM+Q0gNEERVNudpCw7y41xv2Q45ZgPIMhGzjn7CbY8gVacSrXcsnglD8pcFAnI7hh+KfepgSSeJg1ZV7IF5UM9Qr9Cu7VDtq8Lc5bw3tqXNDf4U7T5UgnAjbA/GFXY2fz6yT5TVXWoNxER4d3Bocb73r5wWYlag6z2CKOxTOWenRUnrG0iFPi95MxaykgJMVTAilRLoHOOGZie9DHpOUwcvXWAokeDaXGBSQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.13
Release Details
UpdatedNov. 5, 2025, 4:44 p.m.
Changelog

Fixed

  • CSP Conflicts: Removed global CSP listener that was breaking other Nextcloud apps (Memories, etc.)
  • File Icons: Fixed custom file type icons not displaying by copying them to correct location
  • App Compatibility: CSP modifications now only apply to 3D viewer routes, allowing other apps to function normally

Changed

  • CSP headers now scoped to specific 3D viewer routes instead of globally
  • Added automatic icon copying during build process (scripts/copy-icons.mjs)
  • Removed lib/Listener/CspListener.php (no longer needed)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureqfnNbykFms5wS6aOa/TKBYua37o3OqBNqe9LY0dU86/MrGrqbv+v9inkFm63UQyR19ux9ACVlqG6J5rCDpLLTYAe1IkFGZMKfJzNnna8uiA734PltS8gNSv8ol0zMsRs6tvanPlDxVedCmzp31VsErPKOShjvTRbhf9ZmgJjZccX3UODdfIzZxJLqHd3vBrYqoksFXDZnzUqEkb5OK7RwfxAs71IGoVjXHOf9HuOVuefc1mhElbIygGnPYJ3ewlU72SD6gB9/kYgmZEb9xklvcTz6IjTbfKdZ7fLb640/71wCIa5mYleBkaxsoAzfGo2QjC6GHNkAQX7gAXeZp1GYBixwIF4JvawV2s1pKnIDZE3hkLqhx+e4OOYvawhlVoUdILsXSC2oiG4Q5qF/tSz6fh/2gFY+/2fxJMSwwbPXTkUPA5eXKCTpRIbWdIK61XEMKenSlWFF0giXWcOMkxXH4/T1NFlV7/g57MPogu+DD41Y2LNkdZQVaVYWEyvTw3t0pVvgohGNPRSNKV8qR8yzSM5yVe0Ck6SWOk+74SWwBoItjp66LwNQ60JK2Od2+NOSE3+2eG1SkIKL4+q0Uo4NGQgyxblPWIXJZTl14XcaxyXCJr/K913khhw+b+DumTz6TTGg6jMR8oXRFawccmEHL/DN55Y3HfRAwVj/cNIx7I=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.12
Release Details
UpdatedNov. 4, 2025, 4:14 p.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets
  • Axes Positioning: Axes helper now positioned at bottom center of models, aligned with grid
  • Axes Scaling: Made axes size dynamic (25% of model's largest dimension, minimum 5 units)
  • Logo Loading: Fixed app logo path in demo scene to use generateUrl() for correct URL resolution

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
  • Axes now recreate on model load to ensure proper sizing and positioning
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturetHcAULDv6Z6JlPEmMcFbBFAPFTScfSULZoxHuFuCtCO965w5/TPzwTm9AAbrvWQlTzOza8VK9le5cP96O8XKoLPU/rUTY0YPL+11rga9rU5c9nNcCiRTKwaha9wnos5/fDxZOhefb9aFojmpf7HsrSmHL+JMrLwaVvp846k4ShSPNbV7cVTqsrwz1C4Vy4JRzGMuSfcAMjxLBC02obtPqVYX4T5s7/QCithiTGI1VmmtMxZM0sG3fkIRf9ouPQ44hpHX7beIm4vBGthPwjhlNpR+aXq9rQd4annpZKtZGfGRX/RDXlFXUWBE/07A+/CBzP9DX9pegUvzvrelj67ADuiRDfxsL3vKN1VGU1rqHi1ZrwE24BzfeGnWgUb+wEBOcM8x8BEc0thH2zbfKAwqjY1h/iH8IjoIkbPPm3mKwQ2b5w2v2aaRRr3qbK3cOLhyzWJBpQFZaahomr9x+/hN5aU9fhr9hMjgPboRadI7rO+V6QtQDobhKlz+6Rdb1mo+Bzk9btyCJ7flqqos07yo/O01o4G/0L0hdw20vYh3WlbeGRWC6W/OBzQmRwT4MWFoHV2mQOYO4Nie4SkFCzs2lT3NWW7I/uCrCPMZDJrLhSk1KiKzctZXjTUalB4DuPHSpq99WjCcf2oPLo6rQ7DJd/sIRSh3tSj8WIlxcWCrSWk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.11
Release Details
UpdatedNov. 4, 2025, 10:58 a.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturehK5rTudkrE4neZOI/n/jv+qjY/VfKVkRkouiBK5jBARxwK9e0W8U07ajlvP2+U8q8qwNzjGY77RproUnQIkOlALx3c6V9ufsMaHcpuAR2GAvK84WgqqEM37VSPQrGhLLuZzz4nU5nYmNOsTg+maV0O5XEND29zOdafsJHxpY8mQofXCA0vyLYNLYQtLEAPRBtl3uDQgSSjPGB6kN87VUcqebvVrVim8aa8l7BpDoMiW5j60T7toenQiisu6S4aKMRHCxx7I1cHK/mkGjLzl+/AHpfwWpKCr13StVLzqqlVPc5+SL/yI14XW2JtJARHShE0zBqkARG0yqsFXVTGvLzvAahLuCfIdlied8HFK/NQVmqgLlRAc30xWGq2vcQvpnr9L7GwCkImnM5YeoTqVOJHawhc3wU8FQ56g/imCYgMqwSOegC1BVegdX/XvCMCxhQXEj4OxUxETQ/T1+BWqBBLLbZQQiyyRXw6o/bzpBWJYtDH/XIzFKxJdC7WKfIM6lPmPb7CCc6sJGfEi/Lyv3rTfCjyQj7ioMlB+5sLV5/d+tth6eeY9O6jZsz92/V5byhrtIdYvu4j7IZR2m8+/IpkjmTMnuv18wyU6+unVo8V2hKNLhoWYHJqEQ9Ylx/Rj6tKb4TwsrciMPV+2T38lJK5cS0GZittMNUVAKSUPBSnQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.10
Release Details
UpdatedNov. 4, 2025, 9:48 a.m.
Changelog

Fixed

  • Dark Theme Support: Fixed slide-out toolbar panel not responding to theme changes
  • Theme Switching: Implemented reactive theme binding using Vue computed properties
  • CSS Integration: Converted base styles to use Nextcloud CSS variables for better theme integration
  • UI Consistency: Toolbar panel now properly switches between Light, Dark, and Auto themes

Changed

  • Improved maintainability by using Nextcloud's standard color system throughout the toolbar
  • Enhanced theme responsiveness with component-level class binding
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT38bK6V5aDGCGlNZJEV3QCmwYaO21RK3QtifSTQ3O311NOPA+O7ryIjncKIdr/rdN/9ZjeFLqwcxRk2traF5MkhvjRCr3NT5oV5cuMQgmw2Js7MAOLrSXOUkn6b7LpQKesEmzQPqygl2fuqN5qrxILxeWU5xWq3nyoV+G2TzByeLOT9CX8+EH1rzMnJNb/35aFS0kj/fU4V0FHFM3h8KxNDWYVjlJ/tPtq5IFBvICRm6HLCkQ2fEPPXkxP2Z1lpBam6ZGuSbcmo2lb5yQ7Zq3n6rd7FIzkU6r0/xvYF5gY08bwIdtULEpJOEQwp/n9KBR2itV4twQG2QAZFEIMF+EIg4lRzu0vdX8GVDFbirW8YqCX8PXyd+UAm1FfZ4vf8VHQXF7weZgAnvm3crR01GNStlBHl37Zke3z8CHmiYj3ffi4WMwGBT4Gb8f6fxlCOHH/mOlCyA3rtVlI5cHqJGHbdiwgHYX6e+0p5JRWOlecK/HTcVd0Aa5KX57BTcRQXEhGaQqHIbRhWTWwDMesbAyJMtCT72O2eRUqJCnSI+SRSxl/Hh4lHUEnOPx9D2P/03xDEDpO3xoR7tQGN927un6811AS+v+kABFDJQcayHiFTpR28U+WzF1vqS2fktc90NaWwiJuO1/hnQ/H8J6icAF4oxmvArZI2uO1X3CPZMwN8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.9
Release Details
UpdatedNov. 4, 2025, 8:39 a.m.
Changelog

Added

  • 3D Camera Controller: New circular controller interface for intuitive 3D model navigation
  • Camera Control Methods: Advanced camera manipulation including rotation, zoom, and directional nudging
  • View Snapping: Animated camera transitions to predefined views (Front, Back, Left, Right, Top, Bottom)
  • Controller Persistence: Save and restore controller position and visibility preferences
  • Smooth Animations: Eased camera transitions with customizable duration and easing functions
  • Face Labels: Orientation markers (TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT) on model faces
  • Export Functionality: Export models to GLB, STL, and OBJ formats
  • Camera Projection Toggle: Switch between perspective and orthographic views
  • Progressive Texture Loading: Background texture loading for improved performance
  • Dependency Caching: IndexedDB caching system for faster multi-file model loading
  • Model Statistics Panel: Detailed information about loaded models
  • Help Panel: Comprehensive in-app documentation and controls guide
  • Theme Customization: Enhanced theme switching with RTL support
  • Performance Overlay: Visual performance stats display with real-time monitoring
  • KTX2 Texture Support: GPU texture compression for better performance
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT4jr3f8pu++HPy93L1HdiayTsqKV8PwgIw7jkye4TkqYM7wBuBsK6qv/TXwuZElOm9hzBEVH/MnuFoRl2QXPxurhZLvu918h0rDnTIjfRNMC2gIVFI8JLneLq8ya+m9lKGNLXEodRAhQ3pGn97cuSFzff+iGhQOFkojIvbWM4HbSQFq6pfF3/ZYjUCt5BhRIHdPkk+mzwjpVaniq98gh4XS3AMYqs4p2n7vysSGjAO8hZrDdGUJGxBlz1amgeUpdiGOpTwI4FyT2vbxcmHcMVwZTQyXoYZ/Ux70JZp98nz1H1kKqIoDxzZSRmetrm23Pzn+k440rHQUSjuIa/p6I6w8nGG25M0kjB46lacCtbUlR6Rrrj6SNwFITde5Rr87HP2R/ILqI5MCC73SebBxcdY/IXw7xHjUiBJTlrOmPi1ppktirZnOsmSR2Sa/yE81zrYWJ2HidTbxEGpJDZBIiwOaBCYh6nwQ51PG1z4mxgJE4G+7lMZ/bpu5UaJihys2WcoBhPxrGNeoLoEN+D1JiaOU1C974Wwncz1/BobA08FSx+rzXCnRNSQWbBapz3J6MYT5TwChku40FeqGR2KJm+v2pYgm8K/Eepieubi61tD9St1exw3AjBiJ2/dKOezNb8ZHFcoWn2bxCZc4rzuBUO0tJSrjmbW5dcysusajrvMs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32

Nextcloud 31

3D Viewer 3.9.0
Release Details
UpdatedOct. 28, 2026, 11:08 a.m.
Changelog

Three new tools. Compare a model with an earlier version of the same file, side by side or as a see-through ghost over the current one. Print check tells you whether a model fits your printer, where it overhangs, roughly how much filament it needs and which walls are too thin, or, for G-code, what the slicer worked out. Record turntable makes a short video of the model turning, to download or save next to it.

Added

  • Print check, in the tools panel's Analyze section: see whether a model fits your printer's build volume (pick from common printers or enter your own size), which surfaces overhang past an angle you choose, and roughly how much filament it needs. For G-code files it shows the print time and filament the slicer worked out. A wall thickness check colours walls thinner than a minimum you set.
  • Compare a model with an earlier version of the same file: pick one of the versions Nextcloud kept, and see the two side by side with what changed, or the earlier one as a see-through ghost over the current one. Supporting files such as textures are loaded as they are now.
  • Record a turntable video, from the tools panel's Export section: one full turn of the camera around the model, 4 to 15 seconds at 720p, 1080p or your window's size, as the viewer draws it, with its shading, annotations and measurements (face labels aren't included). Download it as WebM (MP4 in browsers that can't record WebM, such as older Safari), or save it next to the model in your Nextcloud when you can add files to that folder.

Fixed

  • While a dialog such as Help or Send to Slicer was open, the tool shortcuts (R, F, M, S, P) and Escape still acted on the viewer behind it: a key could reset the view or take a screenshot behind the dialog, and Escape closed the tools panel along with the dialog. Keys now belong to the open dialog, wherever the focus is; ? still closes Help.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturerZEJPDWtDI4c/UZUEa/CwOYHXSyql3DtZoZiStkj8GahJ7fwXaLzAMFgvz16luTKgh/rekkOpfdoi+M9NVK8XV696vG6PUNB//y7dPbANbyAntQyDIVGmkTMtCTu4uMZrsCa227zTJecbKdsrHhiVCsFqWs2CcLM4Ih0UfNwzknlgXpQ21AM+L6FaNWFnrhEtURXWcSgQvwz/bcm2b+mb/Qp5khws78GIU3i/Z4a6LpMuhUJxHMQ8PjfNQyzTA1vr4ehd8UmSZJ6P4IcF1/qlXA3PkDaD2fhnELwdfy2s4Y5B74sX76I97AICTOehnoEEOIoMdU7aLeAfezuoZA6BSupy0dwp5JtyTzFdcFf3I7iMWTY/HXtQ90vXIs0uV2pg0oLPSPbsnXMZ6B6nUs3KWzma7aFppkWYTSCB4RXUEXZ5e6WA3WtGYMpos3SXF6TaFS0H0QTH74DXO0p9UXzuT04JP3VaFiK+B2lnHt6mz/YOKhDZ3TmJmmfe8yZZm1ail6F8jI/kVoePQ2X4JeTxkN6cijqp0aLhExXhuX5B1xNeUNuQMhOVTglfQMPSYwVc3OVcn5yO86j0gD72MWDUP6i9+SPM0V2N5av8i/ccnPwXLYBR6ygjsOHKsHoASesGxtmSZ628PrrsR9nRQg/c4mBMUmkAPqdrpWQpKWiB/U=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.1
Release Details
UpdatedOct. 28, 2026, 2:45 a.m.
Changelog

Thumbnails made in the viewer now show the model as its file has it, whatever wireframe, exploded view, transform or animation state the viewer was in, and exports from exploded view have the parts together. The top bar stays usable while a model loads, and every control in the tools panel works from the keyboard and is announced to screen readers. Textures named by a path above the model's folder, such as ..\..\textures\wood.jpg, are found beside the model. Every route that changes data now checks Nextcloud's request token.

Fixed

  • A thumbnail made in the viewer could show how one user was viewing the model, and it is shown to everyone who can see the file. The viewer makes the thumbnail from the model it has open, half a second after it opens, and several tools change that model: with Wireframe on, the thumbnail was a wireframe; in Exploded view, its parts were flung apart and framed small; after the transform gizmo had turned or stretched the model, it was drawn that way; while a clipping plane or box was open, the back of an open surface showed; an animated model was caught at whatever frame its animation had reached; and a G-code file opened with Single colour picked was drawn in that colour, where the same file got the gradient when Files made its thumbnail. Each tool now keeps a record of what it changed, and the thumbnail is drawn from the model as the file has it, as it already was for Clay, Normals and X-ray: no wireframe (a material the file itself makes wireframe stays one), the parts together, the model as loaded, the pose the file opens in, and the G-code gradient. The viewer keeps showing what you chose. Thumbnails already stored are not remade; one goes when its file changes.
  • Exports made in Exploded view wrote the parts where the view had moved them, so an STL, OBJ, PLY, 3MF, GLB or USDZ file, and the file Send to Slicer sends, had the parts flung apart. Exports now have the parts together, in every format; a model turned or moved with the transform gizmo is still exported as you placed it, and an animated model in the pose it is in. Exporting a GLB, USDZ or 3MF no longer drops a paused animation's parts back to where the file puts them.
  • With Wireframe on, the next model you opened was drawn solid while the switch still read on, and Exploded view stayed "Active" with the next model's parts together. Both now carry on to the next model, as the Clay, Normals and X-ray shading does; Exploded view turns off for a model it cannot take apart, and the camera frames the parts as they are shown. The model statistics are those of the model with its parts together, also when it opens exploded. Wireframe also reaches the parts of a model that have more than one material, which it skipped.
  • Switching G-code from Single colour back to Gradient tinted the gradient with the single colour, and a toolpath opened while Single colour was picked turned black on the switch. The gradient now comes back as it was.
  • While a model loaded, the loading screen covered the top bar as well as the 3D view, so Help, the Tools button and the view actions could not be pressed until the model arrived, which for a large file is the whole wait. The loading screen now covers only the 3D view, and the bar stays usable, with Cancel loading still on the loading screen.
  • Models whose textures are named by a path relative to the project rather than to the model, such as ..\..\textures\wood.jpg or ../maps/wood.png, opened untextured even with the texture right beside them, and got no thumbnail in Files. 3ds Max and other exporters write such paths in FBX, 3DS, DAE, X3D, OBJ/MTL and glTF files, and the app refused any path that leads out of the model's folder. Such a texture is now looked for by its file name, as one named by a full path already is since 3.8.0: beside the model, for background thumbnails and on public shares, and in the viewer also in the model's subfolders, such as textures or maps, where the viewer looks for any texture. The folders the path names are never followed, and a texture that is not beside the model still leaves the thumbnail to the viewer. Background thumbnails also read an MTL's maps relative to the MTL, as the viewer on a public share already did, so an OBJ whose MTL sits in a subfolder now gets its textures in its thumbnail.
  • A DAE, X3D or glTF model whose texture path has its slashes percent-encoded, such as ..%2F..%2Fmaps%2Fchecker.png or C:%5Cx%5Cwood.png, had the texture fetched but drew without it, and got no thumbnail in Files: the loaders looked for a file called by the whole path. They now find it by its file name.
  • A folder holding an OBJ or MTL file made largely of blank lines could stall the Files page while background thumbnails read it. The file is now read in a time proportional to its size.
  • The tools panel's Grid, Axes and Wireframe switches and the animation Loop switch could not be reached with the keyboard: Tab skipped them, and a screen reader read out neither that they were switches nor whether they were on. They are now real switches. Tab reaches each one, Space or Enter turns it on or off, a focus ring shows where you are, and a screen reader says "Grid, switch, on". They look as before, High Contrast mode included. The same pass named controls that a screen reader read as "×" or by a symbol (removing a bookmark, resetting a colour, stepping the animation), gave the animation and cross-section sliders names, and made the cross-section plane, box and axis buttons, the transform Move, Rotate and Scale buttons and the lighting presets say which one is chosen; in High Contrast mode the chosen axis and transform button are now marked. The T shortcut that opens and closes the panel no longer fires while a modifier such as Ctrl is held. Choosing a format in the Export section's format list no longer starts the export: moving through the list with the arrow keys used to write a GLB file with the first key, in Chrome and Firefox on Windows and Linux. The row is now a format list that starts on GLB and only chooses, plus an Export button that exports in the chosen format.

Security

  • Four actions could be triggered by another website while you were signed in to Nextcloud, because their routes skipped Nextcloud's CSRF check, the request token that proves a request came from Nextcloud's own pages: sending a model to the slicer (which uploads a temporary copy of it and makes a share link for that copy), deleting that temporary copy, re-indexing your 3D files, and saving the viewer's settings. Your browser's SameSite cookies, and the preflight a browser makes before it sends a PUT or DELETE from another site, stopped most of this, but they do not cover every case, such as a request from another page on the same site, so these routes now require the token like the rest of Nextcloud. The viewer sends it itself, so nothing changes in use; anything outside the viewer that called these routes with a session cookie and no token now gets a 412 and has to send the requesttoken header. Saving the viewer's settings also accepted any setting name and value; it now accepts only the two the viewer stores, the file-browser sort and the selected file, and refuses the whole request if either is unknown or invalid.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureIm8PqU0r/Nr8aRKa1jh6a+lsP8E3b6u1GCxKzIqtb1C4vUZNgKeSdD/it/SABY5O0GBwso8NdFBD3XHV5jgg/yriggt78tRHo7pvVEo/J4CyRhKEWNRChjWfoOMuxRZF5sBJCOacuCQoMyofcHBCSIPjDils6gZ2CW5H3xZ0atGx5hgjcmQKS7TGxaq/3tymVcGmgl17DNbiSULcfFeIL9b0uBQs0HpLzIBGskp0dffLean9atDu5WAzm8CB9ASVCl+m2DyWkE0mcgQpFKDijWCohaNJXKA+P37KSnBOZ1zuQbDV+8B49hoKQr4hxUgfKhM/tVrbZ/F0Fo2FerTrLpi+H1eWYFvPFXicooi51yLVi2vn+OA8cwBcIrvViUCNiYiI0luZOzNiYRGk17WrZWow7suGL810YJk5eGzkEIj5HQ7dsSoU9Alaz6ubEFRc/Fqw4A0wOjXJIcFm3Gw2uFkjRGA7hUIY0Bhbnwsahi1K2DQMv1nr/pUZKwYK1mTsxJjE2+UwPV6ayLxk2+aMSDPxtNYG/fJrEoCeNUaDTljxsT3P8/EXko8/6e6teSUHTKDBFNBM52HSt7Mzl82R/9bBvXHLCmJk8fg1Td/+QebTbtqW4u1RUio946WlErrk75Ku/WmvhDH4RKeyGWdvJz1j2BKYSzxvk2YHAlEMrBA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.8.0
Release Details
UpdatedOct. 27, 2026, 4:59 p.m.
Changelog

New inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ. Measurements, statistics and exports now use each model's real unit, so a glTF figure measures in metres and an STL part in millimetres. FBX readings change: they follow the file's own unit, centimetres by default, so most FBX models read 10× larger than in 3.7.x, which is their correct size. Textures a model names by a full path from the machine that exported it, such as C:\…\wood.jpg, are found beside the model, and a texture that is not one of the model's files is never requested.

Four inspection tools: angle measurement, a layer slider for G-code, Clay, Normals and X-ray shading, and export to 3MF, PLY and USDZ.

Added

  • Angle measurement. The measurement panel has a Distance / Angle switch. In Angle mode, three clicks measure the angle at the second one, in degrees, and changing the length unit leaves it alone. A second click on the point just picked, usually a double click, is not taken, since it would leave the angle without a side. Angles are shared like distances: everyone who can open the model sees them, with their author, and anyone who can edit it can delete them. They are stored as a note type of their own, so a viewer older than this one skips them instead of drawing them as a distance. The annotations JSON export includes angles, and import restores them, whichever mode the panel is in.
  • A layer slider for G-code, in the Analyze section of the tools panel, for files with more than one layer. It shows the print up to the chosen layer, with that layer's height, and "Only this layer" shows that layer alone. A Z-hop counts as part of its layer rather than as a layer of its own. Each step changes only the layers it passes, so dragging across a vase-mode print with tens of thousands of layers stays smooth. The slider and the switch work from the keyboard.
  • Clay, Normals and X-ray shading, in the Scene section of the tools panel. Clay shows the shape of the surface in each part's colour, Normals shows which way each face points, which makes dents and flipped faces stand out, and X-ray makes the parts see-through to show the inside of an assembly. The model's own materials are not changed: exports use them, Send to Slicer included, and so do thumbnails, so a model opened while X-ray is on still gets a normal thumbnail. The shading stays on when you open the next model.
  • Export to 3MF, PLY and USDZ. 3MF, which slicers open, is written with Z up, and its parts are closed solids, so a slicer does not have to repair them. PLY is binary and keeps vertex colours. USDZ opens in AR Quick Look on iPhone and iPad. Both 3MF and USDZ come out at the model's real size: glTF, IFC, COLLADA, X3D, VRML and dotbim models are taken to be in metres, Rhino, 3MF and FBX models in the unit their file says, and STL, OBJ, CAD and the other formats in millimetres. A model with no surfaces, such as a G-code toolpath, gets a message instead of an empty file. The exporters themselves load only when an export starts.

Fixed

  • Measurements and model statistics took every model to be in millimetres, so a 1.8 m glTF figure measured "1.800 mm", and its surface area and volume were a million and a billion times too small. They now use the model's own unit, the same one the 3MF and USDZ exports use: a distance, the bounding box and Copy Measurements read in metres for a glTF, IFC or COLLADA model, and areas and volumes follow as square and cubic units. STL, OBJ, STEP and the other millimetre formats read exactly as before, and angles are unchanged. Both unit pickers now start in the model's own unit (metres for a glTF, millimetres for an STL) until you pick one. Your pick is kept as you open other models in the 3D Viewer app, and is not saved: reloading the page starts again from the model's own unit. "Generic Units" shows the numbers in the model's file, unconverted. The comparison table converts each model from its own unit, so a glTF and an STL of the same part compare as the same size. Shared measurements are stored as points on the model, so they need no change and show the right reading.
  • 3MF files written in a unit other than millimetres opened at the wrong size: three.js reads a 3MF file's unit and never applies it, so a file in metres was a thousandth of its size, measured wrong and exported wrong. The unit (micron, millimeter, centimeter, inch, foot or meter) is now read from the file, so measurements are right and a metre 3MF exports and opens again at its real size. The model is shown as before. Only the start of the file's model part is read for it, and in linear time, so a crafted file cannot stall the page.
  • FBX readings now follow the file's own unit, UnitScaleFactor, which is centimetres when a file does not say. In 3.7.x every FBX was read as millimetres, so every FBX model, small ones included, now reads 10× larger than it did (more for a file in metres or inches): that is its correct size. FBX models larger than 50 units also measured and exported at a fraction of their size, because the viewer shrinks such a model to fit its lighting; that is now undone for measurements, statistics and export. FBX models are shown exactly as before.
  • OBJ models larger than 1000 units, a metre for an OBJ in millimetres, measured, reported and exported at 1000 mm whatever their size: the viewer shrinks such a model to fit, and the shrinking was not undone. A 5 m OBJ now reads 5000 mm. Such models read differently from 3.7.x, and correctly; they are shown as before.
  • G-code toolpaths drew the travel moves the parser leaves out. Each layer was drawn as a single connected line through all its moves, so a line joined the end of each move to the start of the next. Layers are now drawn as separate moves.
  • Closing the measurement panel with one point picked left that point's marker in the scene. Markers were matched to measurements by their position in a list of pairs, so every later pair was off by one, and deleting a measurement could remove another one's markers. Each measurement now keeps track of what it drew, and an unused point's marker goes when the panel closes or the mode changes.
  • Send to Slicer's PLY option always sent ASCII PLY: the binary option was passed where the exporter expects its callback. It now sends binary PLY.
  • In Windows High Contrast mode, the tools panel's switches (Grid, Axes, Wireframe and the others) showed no difference between on and off, because their track was filled with a colour that the system palette replaces. They now have an outline, and a switch that is on is filled with the highlight colour.
  • Models whose textures are named by a full path from the machine that exported them, such as C:\Users\anna\textures\wood.jpg, file:///C:/textures/wood.jpg, \\server\share\wood.jpg or /home/anna/wood.jpg, opened untextured even with the texture right beside them, and got no thumbnail in Files. SketchUp, 3ds Max and some Blender exports write such paths in DAE, FBX, 3DS, OBJ/MTL and X3D files, and the app took them for files that are not beside the model. The texture is now looked for by its file name in the model's own folder, in the viewer, for background thumbnails and on public shares. This also covers glTF, an FBX that names its texture only in a video's Filename, and a material map written after options such as -bm 0.3. The folders the path names are never searched, and a texture that is not beside the model still leaves the thumbnail to the viewer.

Security

  • Opening a DAE, 3DS, glTF or VRML model in the viewer requested any texture the model named that was not one of its own files, at the address the model gave. A texture written as https://…, or as a Windows network path such as \\server\share\x.png, which the browser turns into http://server/…, made your browser contact a server the model's author chose, and only Nextcloud's Content Security Policy stood in the way. Such a texture is now never requested: the model is drawn without it. Thumbnails made in the background already refused these.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCmm5ayL+RHBF5+iiN7ZD5hk7WWM1pgz+KPfzKS6H+3ZnGg8ZyORrq7C6H+eaNxd54PoTbGonDOSIomtizZ/I/udluAu1lRW0ZA6ZS4olC6t+iylmvWU2lc5CaCLqBSXHt7+uE5RSSHbXCZl1MdYWlyYQkJo+SVPCbupSBQu/EenOXfNOMsBJhjdtyGmNzTOhqil+IGmPGRf+YiPa9cW9X0ll+sreqKvbICTxtrfw0OhEbA801v1R2ecXns2RL1iqiWxn/gGcAM4ibCEd+RdsrdUgs3QKSBBgtsZEzJNYKDeH15WoPGHnThDY13aVv7Qv446rHYcn8V3ZIk/vR4Znu0EVLPoRHW6ltJppSi0aeNE62WWCd5KWNfY0Q0K5oykwbtl2szFx+AEYgvbRdRFyOylExNZUcoigYFmlkrQLvPTKtvivYVUi9NIQIbCgSHHygjZEdWy1otFuRs9+W7pqtqH++SJ0cXwZp/ruIikfAJ3/JnQdAuNlWelNErjj2DJVa7cScS8zdiYINzXoToPnDYG9vrNTftnYNwTEvLOQBaHFVDBEC/XdwnPk0ZqEXseJMid632xpWxLdQny2nuVnzjXkG3Nw5klYZ1wWbKOzwUuRHKWatOCJxRiQIXlIs07SgNqV2HlcelYUJ9GG5pX8R21zQBbEKqAsRrT/0WsEtBw=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.1
Release Details
UpdatedOct. 27, 2026, 1:58 a.m.
Changelog

3MF, AMF, DAE and X3D files now get thumbnails while folders are browsed, like every other model. FreeCAD files show only the part FreeCAD itself shows, instead of every step of its history on top of each other.

Added

  • 3MF, AMF, DAE (COLLADA) and X3D files get a thumbnail while you browse Files, like every other model, instead of only once opened in the viewer. Their loaders read XML with the browser's DOMParser, which the background worker does not have; the worker now loads its own XML parser (@xmldom/xmldom) for these formats only, and the viewer keeps the browser's. The parser fetches nothing a file names, such as an external DTD or entity, and expands no entity a file declares, so a crafted file can neither make a request nor multiply itself. Parsing still takes memory in proportion to the file, about 31 bytes per character for a 3MF mesh, so XML longer than 16 Mi characters (roughly a 250,000-triangle 3MF, measured unzipped) is not parsed in the background: the file is skipped for its version, like one that runs out of time, and gets its thumbnail when opened in the viewer. As with OBJ, glTF and FBX, a DAE or X3D texture the file names is downloaded with it, and a file whose named texture cannot be found is left for the viewer rather than given an untextured thumbnail; a 3MF carries its textures inside it. Only .nc and .g files, which may not be G-code at all, are still left to the viewer.

Fixed

  • FreeCAD files (.fcstd) showed every step of a part's history on top of each other, in the viewer and in their thumbnail: a PartDesign part with a Pad and a Pocket drew the Pad's uncut solid around the finished part, and the tool the Pocket cut away sticking out of it. FreeCAD saves a shape for every feature and shows only some of them; the loader drew them all. It now draws only the objects GuiDocument.xml marks visible, and nothing inside a hidden Body or App::Part, which FreeCAD hides as a whole while their members stay marked visible (the tips of two Bodies combined by a Part Cut, for one). A file saved without it (by FreeCADCmd), or one where nothing visible can be drawn, is drawn whole as before. In FreeCAD's own PartDesign example this is 1 solid instead of 18; in its Part workbench engine block, 10 shapes instead of 35.

Security

  • Finding the texture and material files a COLLADA, X3D, OBJ or MTL upload names took time that grew with the square of the file's size, so a crafted upload of a few megabytes could keep the server busy for minutes. These files are now read in a single pass, on the server and in the browser.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5P5xxH9yvWf8Au302k0c7SEVJMaiS+KTOElYVIGw4KinRBHTSr3Lc/tmNF0aTDJaecbjcKuz5zrzimSYyQhtuWYIjyBqDew8qfJRoek/6+rycT2u7zvYyzyjmuWGHA+Wns13LLoCk6GsRxeya03ZyWvdwfyJ5eCGmJfAFN+ukZl3VTUmeADIqaQbKiYnHzj3lYQljacH3x0ebkFQ8Q1TPbCcRrUTjEVOAheWM3g7k5knbYstBo0EGUCr84TtsNvyenqCnr0uH8g10NPBt+mIaQRIWlK2xua6RoPtCGY33JmoGVzOGppM8rhyaVQ40wCdY2Sp1Mm+itA1ViyqjF/OcumS4LL5bAE+J0Y5J+4zzCELNWPQIOXGaA6X+YwWXEEN8Q+TB5UlqNOqyUliRVa3JsXcnmfUpwBRQPGEYe5UN48bXEUNRzodZ9J97SclDVApc8wayuf7kZNe3q8HOkjj8euEUXw1QZeSSfPv1cFWm0iy72SYqlrBfylMs4+L32u0B3nloFACcrgVgi0+erNSNGyzKJS2AwwFLXo1MEwTT6IGAx2Lmquq5sT427HDavOlwRpsAeGQ5IwZF+2rxJyhAYl+PkLHCeHH7UwyjHteTxbIYmsUSwzSjPd81sVuI/O/UUfFVhAQbH2etMRBSdQj19Ub0+frTScX32NKmmvYjG8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.7.0
Release Details
UpdatedOct. 26, 2026, 11:33 a.m.
Changelog

Models get thumbnails in Files without anyone opening them: the browser of whoever is viewing a folder renders them in the background, within limits each user sets. A thumbnail is kept once per file version and shown to everyone who can see the file, including share recipients and public links. VRML files, FreeCAD files saved by FreeCAD and FBX files with embedded textures open correctly again. Existing thumbnails turn back into icons after the upgrade and are made again as folders are browsed.

Added

  • Thumbnails are made while you browse Files. Models in the folder you are viewing get a thumbnail without being opened: one file at a time, while the browser is idle and the tab is visible, and not while Save-Data is on or a model is open in the viewer. Folders without models load none of this code. Each model is rendered in its own Web Worker, which is stopped the moment it runs out of time, so a large CAD file never freezes the page. Personal Settings → 3D Viewer → Thumbnails has the new options: "Create thumbnails while browsing Files" (on by default, and separate from making thumbnails in the viewer), "Skip files larger than" (50 MB), "Give up on a file after" (30 seconds) and "Download at most, per folder visit" (500 MB). A file that runs out of time or cannot be read is skipped by this browser until the file changes or you press "Retry skipped files"; network and server errors never skip a file, and a browser that cannot render in a worker downloads nothing at all. Only the companion files a model names (materials, textures, glTF buffers) are downloaded, and only while they fit in the folder budget, so a model whose textures are not named gets an untextured thumbnail. A model is never rendered without a companion it names, since a thumbnail is kept for the file's version: one that does not fit in what is left of the budget waits for your next visit to the folder, and one that cannot be found beside the model (or at the path it names) is left for the viewer, which looks further, to make the first thumbnail. In Favorites, Shares and Tags, thumbnails are made once you open a folder, not on their top-level lists. Only files you can edit get a thumbnail. 3MF, AMF, DAE and X3D files, whose loaders cannot run in a worker, and .nc and .g files, which may not be G-code at all, still get theirs when opened in the viewer.

Changed

  • One thumbnail per file version, shared by everyone who can see the file. Thumbnails were stored per user, keyed by file id alone, so each user made their own and an edited file kept its old picture. They are now stored once per version in the app data (thumbnails-v2/<fileId>/), dropped when the file is edited or deleted, and the old per-user thumbnails are removed on upgrade. Models that showed a thumbnail before the upgrade show their file icon again until a new thumbnail is made, by browsing their folder or opening them.
  • The viewer captures a model's thumbnail with the same fixed look as every other thumbnail (transparent background, default lighting and camera framing) instead of a crop of the live canvas, and only once per file version: it asks the server first and skips files you cannot edit, files that already have a thumbnail for their current version, public shares, and opens that took a material or texture from the browser's dependency cache, which may be older than the file. It now covers every format the viewer opens, not a fixed list of 12.
  • The CAD runtime chunk is 63 KB instead of 776 KB. The OCCT glue's require("crypto"), reached only when it runs under Node, pulled the whole browser polyfill for Node's crypto into the bundle: crypto-browserify, bn.js, elliptic, asn1.js and readable-stream. crypto is no longer polyfilled, which also removes the vm-browserify eval warning from the build.
  • The GLB, STL and OBJ exporters load when an export starts instead of with the viewer, taking 40 KB off the main chunk. The slicer's dynamic imports of the same exporters had no effect while the export composable imported them statically.
  • webdav 5.10.0 → 5.11.0 and fast-xml-parser 5.10.1 → 5.11.2, which drop a duplicate key the build warned about.
  • "Retry skipped files" replaces "Clear thumbnails" in the thumbnail settings. Thumbnails are now shared, so clearing them would have removed other people's too; a thumbnail goes away when its file changes instead.
  • roave/security-advisories is no longer a dev dependency. Its latest release declares a conflict with every PHP below 8.2.35, PHP 8.1 included, so Composer could not resolve the dev tree for the app's minimum PHP and every PHP job in CI failed at install. Advisories are still checked by composer audit in the security scan.

Fixed

  • IFC, FCSTD and G-code files never showed a thumbnail in Files. The preview provider registered for model/* MIME types only, and these three have application/x-step, application/x-fcstd and text/x-gcode. It now registers for the MIME type of every format that gets a thumbnail.
  • Thumbnails never showed to share recipients or on public links. They were looked up for the logged-in user, so a recipient saw their own (usually none) and a public link, with no user, saw none at all. Anyone who can see a file now sees its thumbnail.
  • FreeCAD files (.fcstd) saved by FreeCAD failed to open with "FCSTD archive contains no .brep geometry entries". FreeCAD stores each body as a .brp file inside the document, and only .brep was read; both are now.
  • VRML files (.wrl, .vrml) spun for 30 seconds and then failed to open in the viewer. The loader waited for a callback three.js's VRML loader never calls; it now uses the scene the parser returns, so they open.
  • FBX files with embedded textures opened untextured. The images the file carries were looked up by name among the companion files, matched nothing, and got a blank placeholder; they are now used as they are.

Security

  • A model rendered in the background loads only the files it was given. A texture or buffer it named but that was not among them was requested from the URL the model wrote, so a model in a shared folder could make the browser of anyone browsing that folder request an address of its author's choosing. The worker now refuses every URL except the model's own files and the app's decoders, before any request is made, and skips the model.
  • Storing a thumbnail requires the request token (CSRF protection) and edit rights on the file. The upload route opted out of CSRF checks, and anyone who could read a file could set its thumbnail; read-only share recipients now get 403. The browser also sends the version it rendered, which it learns before it starts downloading the model, and an upload for a version that is no longer current is refused, so a thumbnail can never be filed under a newer version of the file.
  • Dependencies updated within their existing ranges to clear npm audit: follow-redirects 1.16.0 → 1.16.2 (credential headers kept across cross-origin redirects), reached through axios's Node adapter, which the browser bundle does not use, and vite 7.3.6 → 7.3.7 (dev-server advisories). Dev-only fast-uri, brace-expansion, js-yaml, moment, pbkdf2 and qs move to patched releases as well.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureHW+gyQT9pWQ7mEMg5wkO6BOlN33tSE+lpY33kDjIvJ0XIvGh3JY9P7KW64TxfSUb5xG+sArQE8CsPqTDbwGjKPy+sMK+iPQrKdbIMKA/xmrW6ISonhqxt1IN9siV0EiPhGVlLsigL76kuWbzAzaxiHiISofv79xZt2hR0YwD/vHqn3IRNQXREf7gtSnNDGXF5HmQVELaR43ccFlJK4husCsCGSxnQ0Fdbt6I3OZd2+bp1l53cG2EVOTbNFn5Azc4krpiy1jP8oNO6PVKveZPHObTBiXqYMNm6seHkEuATm0vw+aq5F9u/HCVitQyzXo+0oANQFoYW1+FqtWT1ueSgTaz4++lsn5Z5b7aDtubDWISj5AJL/kERXdfPvux6klusP4UPb1YP1yLkO/tWBQWn3is4SsSvC9f3UGBTKch/IEAcHjnL2c5PSqml491KCHF3/4jqQUu8ozKuG6n/09yImtQDmBvuyGlEuBCZT4rANZ8Pr9Q29zbDkFqQCNR/qe+tveEQzKDKO0ECaDp4bjxydNvnff3+m6ceWp5/KpHXLUfs228XoJqBVuyY5jI3+i/ltEnXYp8l3coQFBVKmKiLvuE8aGbP92EmTZXc1sv2Q8RHBSMt3Mxd/78cMIF4pMYjGsKOIHWl4R9hvctcntXkiB4BD2ALVTCZ2ypvBihqjE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.6.0
Release Details
UpdatedOct. 19, 2026, 10:51 a.m.
Changelog

Annotations and measurements become shared notes that everyone who can open a model sees, and the app supports Nextcloud 35. CAD, BIM and 3DM files open again on App Store installs, Rhino 3DM files open at all, and the Favorites view lists starred models.

Added

  • Annotations and measurements are shared with everyone who can open the model. They were saved privately per user, and measurements were not saved at all. Now each model has one set of notes: share recipients and public-link visitors see it, and anyone who can edit the model can add, change or delete notes. Each note saves on its own, so two people editing at once never overwrite each other. Each note shows its author, except on public links. Existing private annotations move into the shared set the first time their author opens the model, if that user can edit it; otherwise they stay visible to that user only, marked "Private, not shared". Notes follow a model through moves and renames within the same storage; moving it to another storage, such as a group folder or external storage, gives it a new file id, and its notes are removed by the daily cleanup.
  • JSON export includes measurements, and import restores them.
  • Nextcloud 35 support. The app declared max-version="34", so the updater listed 3D Viewer under "Apps missing compatible version" and an upgrade to 35 would have disabled it. Checked on 35.0.2 by upgrading a 34 instance in place: the app stays enabled, and models open from the Files app, the standalone viewer, the file picker and public shares, with nothing logged. Nextcloud 35 types the schema and file APIs more strictly, which surfaced three changes: the folder_path migration calls modifyColumn(), the only form 35's ITable declares (changeColumn() was a deprecated alias of it in every supported server); the slicer download endpoint no longer treats a folder ID as a file, which deleted any folder older than a day inside its temp directory; and the temp-file cleanup job skips a non-folder at the temp path instead of failing on it.

Changed

  • three.js r185 → r186. The Babel config moves from package.json to a project-wide babel.config.json: r186's CommonJS entry only re-exports the ES module build, and a package.json config never reaches files inside node_modules, so Jest could no longer load three.
  • Babel 7 → 8 for the Jest transform. @babel/plugin-proposal-object-rest-spread, @babel/plugin-syntax-dynamic-import and @babel/plugin-transform-runtime are dropped: no config referenced them, and the first had no Babel 8 release. Babel 8 needs Node 22.18 or later, so engines.node and .nvmrc move to 22.18.

Removed

  • The /api/annotations/{fileId} endpoints, replaced by /api/notes/{fileId}.

Fixed

  • STEP, IGES, BREP, FCSTD, IFC and 3DM files never opened when the app was installed from the App Store, failing with both async and sync fetching of the wasm failed. The loaders fetched their WebAssembly runtimes from /apps/threedviewer/…, which exists only when the app sits in the shipped apps root; App Store installs land in the writable root, custom_apps/ on the official image. The OCCT, web-ifc, rhino3dm, DRACO and Basis paths now resolve against the app's actual web root, which also covers instances served from a sub-directory (#188).
  • The file browser's Favorites view was always empty. The controller read favorites through OCP\Files\Tags\ISystemTagManager, which does not exist, so Nextcloud injected null; it also looked for system tag 10, while favorites are per-user tags. It now reads the user's starred files from ITagManager, and the browser renders the flat Favorites list, which no view state reached before (#205).
  • Rhino 3DM files never opened, for three reasons. The published app shipped rhino3dm.js but no rhino3dm.wasm: the wasm is git-ignored and its old source in three.js's examples/ no longer exists, and the copy step skipped missing files silently. The runtime now comes from the rhino3dm package (8.32.1, the version three's loader names), and the build fails if either file is missing. Its emscripten glue compiled code with new Function, which Nextcloud's CSP blocks; it is rewritten as it is copied, the same repair #169 made for the CAD runtime. And a worker whose runtime failed to start never answered, so the viewer spun with no error: the decode now has a deadline, worker errors are reported by their message, and each load terminates its worker (#204).

Security

  • Production dependencies updated within their existing ranges to clear npm audit: vue 3.5.41 → 3.5.44 (@vue/server-renderer XSS), axios 1.19.0 → 1.20.0 (prototype-pollution gadgets), brace-expansion 5.0.9 → 5.0.12 (ReDoS), source-map-js 1.2.1 → 1.2.2 (event-loop DoS) and DOMPurify 3.4.15 → 3.4.17 (IN_PLACE sanitizer bypasses).
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewgF9SWWbzLH22o0y9lzTkAUX33vlRdY37uuPVanE2nMuer3V8gTzxkFnfGn2m6iXOWfHvlTAM1xMXbMIiFOCvTTw5rA2GMq+T+QVh8kAyeDAOtQVU7Rr0J0+x2PK0HNkmwlJstOc7xGiYEiVLXEu6mEKbb99Q/o7xlkUsgvmAuK6Xxu1EZvWJi30bx8pJwJC3jbRMaZX5CRWYpoPkeoOCqglgXTm/j8wCqNP8W4ofmWZlOdqM5VQoApIv3Iv9Bvtp7lzau+r+saUNcgShEHuMhiODLeWDdznd5XUrQNLp7yLiYM2tk/brB04F9qIL0nr1BJtA1qhOe2nlIHtmvyp1Yy8bEryIPLKgWHubSyMYV8qUt5pJP3b5ZOmlDfNYBpru1mNJVNFOPlEyWw/74lRAWRadFY3dOASyyUfzcr0rMraMcvKg3Kg881XEAPiizNfTCHi5trQR4+gfbd7e42gT2Z5KGuZLGllAntMzP9B/ZwMl5VuFZLaWJNosjkOdry5TRH8ZOn1hyEj+vP4fdi+a81Wcq2yRjuER+LG6SL3LKkVIjXLCnat1N0nEzxOfPEi3VPNDy6jNJhoX8aaDAQJklXFc/TcD7+6MwLXR/ZXLlxRZ7dvpu/tO3WQF75kxCo94Z6fILOS5EhjDf0JZb6iFscELiwK9R7A8XaEiqHKFY8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<36.0.0
Minimum Integer bits32
3D Viewer 3.5.1
Release Details
UpdatedSept. 21, 2026, 6:09 a.m.
Changelog

CAD models open again on Nextcloud 34: STEP, IGES, BREP and FCSTD no longer need 'unsafe-eval', which the server stopped letting apps request.

Changed

  • @nextcloud/vue 9.9.0 → 9.13.0 and @nextcloud/dialogs 7.4.1 → 7.5.0.

Fixed

  • STEP, IGES, BREP and FCSTD files failed to open with EvalError: call to Function() blocked by CSP, and no page let the viewer compile WebAssembly at all. The OCCT runtime is rewritten at build time to stop compiling its bindings with new Function, and the app now adds 'wasm-unsafe-eval' — never 'unsafe-eval' — through Nextcloud's additive CSP event, so CAD and IFC models open from the Files app without an administrator editing the policy (#169).
  • The release workflow reported success when nothing was published. The app store step was continue-on-error, so a failed publish went green at the step, the job and the run — v3.5.0 was tagged, built and released with nothing on the store and nothing anywhere to say so. That flag is gone, and a step after it asks the store whether it is serving the release, checking the platform range and download URL as well as the version.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureRJMTtOs8itpPTgMandBOx/fB3Lb4eQhNqh82AL50L6dsquHrSy1TrIOYVtn0rZV50Zwy23N2wGiC8yP450hkEYtMG4Hqmb3RRijNvGdioBpHryA7XyF2II7sx13g9ebXTkk6Aq4qq82sTVe8LtqElZfjD7mshN8eRrrQDMCh7xgly6dBOkyagZqowByvVaiL093OVqO3qUCCGfrWslIB17NtM+8BreXIrsLn/fiQe67uBpumOD6hzEYaWwxqYNrLoejUfZWsShZnvUa5mUG5v1mquxXDUGUswb757OYsDq1Yg3KEU3mv4yYZHmigJfjnHZ+n17j9sWt0g6xXI8YXAY5t2cxx91ll9WQGeVddYZIJjseJodhAV5fCe1V87Ks+pdVYPdXhhdDMQGbFBacnFM+BRRuN7yP3QIpBM1re1qKRcIBDTl3oSUqnCQJ9vLG6im+NxvUYQ5y2VfETyRlQdJPo1j+WToO4dhpI3KHSYpU42HW+T1YvPlmpsosrxanIEnqNzl8uqvjzQibww0A/uIHMbKLsp5nGblQRhlbeLzeZvQs5C28K9Ux9PvNpCEIdXsyYwhB7PHXuu/SYzcsBWR1Zu3i+/cV0SywOER8qSltd+KQuvBXuAveGUqZhRe0zOJStOs90w2rcdHe32BIAJ2GSOdYA+uDnkdY6k26qTAY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.5.0
Release Details
UpdatedAug. 11, 2026, 3:17 a.m.
Changelog

The viewer is redrawn on a themeable design system: the Theme control now moves the whole viewer rather than only the scene, every control is a Material Design icon instead of an emoji, and the top bar, tools panel, slicer dialog, performance HUD and the measurement and annotation surfaces follow the design sheet. Also carries three.js r185, the move to ESLint flat config, and the security fixes listed below.

Added

  • Keyboard shortcuts for six tool rows: R reset view, F fit to view, M measurement, S statistics, P screenshot, ? help.
  • The Playwright suite now runs in CI.
  • A browser check that the design system's tokens resolve to real colours.

Changed

  • Three.js r184 → r185.
  • The live loader check covers OBJ, glTF, Draco-compressed glTF, PLY and STL instead of two app-owned parsers.
  • "Open in 3D Viewer" is the mockup's outlined pill, with an open-in-new icon and a tooltip saying it opens a new tab.
  • The tools panel's sections are rows separated by hairlines rather than nested cards.
  • The dark palette comes from the design sheet, with separate tokens for the accent as a fill and as text.
  • The viewer's Theme control themes the whole viewer rather than only the scene background and grid.
  • The slicer dialog follows the design sheet: 560px, one segmented format control, a lighter backdrop.
  • The performance HUD and the model statistics panel follow the sheet, both drawn as canvas chrome.
  • Every control is drawn with a Material Design icon instead of an emoji — 154 across nine components.
  • The tools panel is redesigned as a floating card with collapsible sections.
  • The viewer's top bar is redesigned: a 56px header, the filename centred with its format, an FPS pill, and Tools as the one primary button.
  • ESLint moved to flat config, for @nextcloud/eslint-config v9. New rule families are off and newly-promoted rules are warnings, each carrying its count in the config.

Fixed

  • The antialiasing setting did nothing — the renderer hardcoded it on. It now follows the setting, applied at mount.
  • The help dialog dimmed the viewer and left the rest of the page lit. The four fixed overlays are teleported to the body, past the containing block Nextcloud's backdrop-filter creates.
  • The help panel drew different icons from the controls it describes; eight rows disagreed.
  • The controller rail's full-width controls sat seventeen pixels left of centre.
  • Every NcButton rendered as secondary whatever it declared: @nextcloud/vue 9 moved the appearance from type to variant.
  • Four controls were still drawn with an arrow character instead of an icon.
  • Nextcloud's navigation toggle looked disabled on the viewer's top bar.
  • The browser fixture rendered the whole viewer into a zero-width column, so every geometry it measured was of that.
  • A tools panel section header turned pale blue with grey writing on it under the pointer.
  • The slicer's format selector rendered as three chips floating in a track.
  • The 3D controller was a white ring on a white scene.
  • The Send to Slicer dialog carried a hand-written dark theme that fought the tokens.
  • The per-slicer Open buttons were drawn in each slicer's brand colour, down to 1.78:1.
  • The browser legibility sweep mis-read every color-mix() surface.
  • A theme chosen in the viewer was forgotten on the next load.
  • The viewer's header kept its white text into the light theme.
  • The measurement and annotation panels' close buttons did nothing, and no tool row ever showed it was active.
  • An annotation's note could not be edited.
  • Label text rendered stretched.
  • A measured point landed on the transform gizmo's picker plane rather than on the model.
  • The measurement reading was 1% of the model's height.
  • The markers drawn into the scene were still the debug palette.
  • Every label was a wide black bar with a few pixels of text in it.
  • Taking a measurement filled the viewport with flat green.
  • The measurements and annotations panels had never been redesigned.
  • Switching measurement mode on produced no visible change — the panel opened underneath the tools panel.
  • The measurements panel only appeared once there was something to list.
  • The tools panel never announced the state it restored on load.
  • The export format control and the slicer's format control had labels that labelled nothing.
  • The viewer drew a light card and a dark card on the same rendered model.
  • The performance HUD's mode chip was a pale blue pill with near-black text on a near-black panel.
  • Half the model statistics panel was white text on a white panel.
  • The design system had the status colours backwards: the unsuffixed token is now always the foreground.
  • Every button in the viewer lost its colours the moment it was used.
  • The ground grid was fluorescent green on every theme, and the setting meant to control it did nothing.
  • The viewer's buttons were the wrong shape on every real Nextcloud page.
  • The top bar crashed before a model was chosen.
  • A "Model loaded" toast covered the Tools button on every page load.
  • Ten style declarations had stopped applying, from an rgb() bracket a token sweep left behind.
  • The watertightness badges were pale green and pale amber on white, at 1.9:1.
  • The bundle size check picked which file to measure by the shape of its content hash.
  • obj-mtl.spec.ts had been failing since before 3.4.0 with nobody to see it: its Nextcloud stub was missing OC.filePath.
  • --color-main-text was written with five different fallbacks, --color-border with eight. All 224 references now go through the tokens.
  • Dark mode asked the light theme what colour to be.
  • A help icon's background tint never rendered — it mixed from a variable Nextcloud does not publish.
  • The dark theme never reached the toolbar, the viewer or the toasts: ten rules keyed off a class the app never sets.
  • The slicer dialog followed the theme only when opened from the standalone app.
  • The viewer asked Nextcloud for the wrong primary colour in twenty-five places.
  • --color-primary carried three different fallbacks, so an unthemed instance rendered whichever rule an element landed on.

Security

  • Cleared three advisories from the production dependency graph, lockfile only: dompurify 3.4.13 (GHSA-55q2-fjhq-7xh7), nanoid 3.3.18 (GHSA-2v37-7h3g-55p8) and brace-expansion 5.0.9 under webdav (GHSA-rgw5-rvv9-x895). npm audit --omit=dev goes from one moderate and two high to zero. The residual 3.4.0 recorded is unchanged: webdav ships a prebuilt bundle with brace-expansion inlined, which no lockfile change reaches.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature33Nuq3jk4L8Tk0nUQ/SUKRB9gkf/O+h254EVTLZroD9RvLW9vF6sZqc5zxHEqjR1n15D6JGSbNsdiHTIg3XruaBzMEQ9IplE8gzY6tkclHRN1n1plXbYwrFrZs/nfmUcEJQu2XKVoeCFrN+hPwVIXng/m5p+xGzcHhOnev/o2GNRTFHbSET9Sy7HHIGmieaOf2g6Nrbhto/X7QAnjk2i8Ht/OVLggubgPe85SYsG8qvmi03S0xY8NzFl7XQ6lBlY1DqZSt7Rn/PY8OQsYAMIRA+Ihwu6JU4hWiw1wyV0EFnWayypnS2l4hTk2GNG06Y8bbe1qt05r2LSQSqPI3HOd87nKK/ETmwJdHkpNDJi9NNV0iYuAp1fOsfH7d0VHkbuxWTXZ223YWtlTA+0XKNfZ7Jw5mq0zlyIjpyNDPjScwXv3g+/M835mTUFeGJGkv6HrVSoNtIhkAp0Mh1O5XXw/X0Y/6UlKsky/r0BsWD9a4ddMLocWOm3EvGWi17lsPkX7DJFUH6DKuIiPrKxjGiUM0+Op6sLPSVULqyrAudTe7nHZ2/YM2iDeOrUc6jc3+aNqKLbnv1ApdpmlFoErnZvL/RwCLd9463758stiEOrKksv1K84oCvak7W6FCsEZdEEMK7fzqS+r7mzCqAwH5qewAcIB1l844DIDh7X2i4eQ4o=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.4.0
Release Details
UpdatedJuly 28, 2026, 6:52 p.m.
Changelog

Public share links now render 3D models, and every format that carries external textures resolves them there. Also carries the security fixes listed below.

Added

  • A merge gate that treats an absent check as a failure, as a ruleset on main and as npm run pr:ready. gh pr checks reports only the checks that exist, so a queued run contributes none and a pull request whose matrix has not started looks exactly like one where everything passed — which is how a broken eslint run reached main. The required set is the seven summary jobs: each aggregates its own matrix and reports unconditionally. Because the ruleset also covers pushes, work on main now goes through a pull request.
  • The integration suite now also runs on MySQL and PostgreSQL. The version axis is covered on SQLite; this adds the database axis, which is where the queries differ rather than where the server does — escapeLikeParameter() picks an escape character per platform, and MySQL's default collation is case-insensitive where SQLite's and PostgreSQL's are not, so a folder listing settled on one backend is not settled on the others. Newest supported server only, rather than crossing both axes.
  • A PHP integration suite that runs against a real Nextcloud server, covering what mocks cannot: real storage for the dependency route, and a real connection for the file browser's folder listing. Both bugs that reached production here — the str_replace prefix that reported folders like modelstextures, and escapeLikeParameter() called on the query builder, where the interface never declared it — are now caught, each verified by reintroducing it. The bootstrap exits non-zero when it finds no server rather than skipping, which is how the retired workflow managed to report coverage it never had.
  • FBX and 3DS textures now load on public shares, which completes declaration-based resolution for every format carrying external textures. Both name their textures in binary structures, so they are walked — FBX node records, 3DS chunks — rather than searched for their marker bytes: a filename occurring inside vertex data is not a declaration, and treating it as one would let a crafted model name any file beside it. Walking also steps over mesh data by its declared length instead of reading it. The folder listing is now only a signed-in fallback.
  • COLLADA and X3D textures now load on public shares. Both formats name their textures in the document, so they resolve by declaration through the existing /dep/{name} route, the way OBJ and glTF do — no folder listing, which is what needed a session and left them untextured. Matched with patterns rather than an XML parser: only the paths are wanted, and a parser on an uploaded document brings entity expansion with it. Signed-in sessions keep the folder-listing fallback for documents the patterns miss.
  • 3D models are now viewable through public share links (#115). Share pages previously rendered nothing at all: LoadFilesListener returns early for anonymous visitors, so the bundle never loaded. A new LoadPublicShareListener hooks the public template.
  • Textures load on public shares. OBJ materials, their maps and glTF buffers are served by name through /public/file/{token}/{fileId}/dep/{name}. The model's own declarations are the authorisation — an undeclared name is refused, as is any path escaping the model's folder.
  • Tests for both database migrations, which had none. The load-bearing one pins the sha256 folder-path digest across the three places it is computed — the migration backfill, FileIndexService on write, FileIndexMapper on read. Drift there silently returns empty folder listings rather than an error.
  • Doctrine DBAL stubs in tests/bootstrap.php. IQueryBuilder takes its PARAM_* constants from Doctrine, which nextcloud/ocp does not require, so mocking IDBConnection used to fatal. No database-facing class had ever been unit-tested here.

Changed

  • A successful model load no longer fills the console with warnings. getFileIdByPath logged its normal progress at warn — the folder listing, the name being looked for, the move to subdirectories — so a model with ten textures produced twenty-odd warnings on a load where nothing was wrong, and a real one was indistinguishable from the noise. Routine steps are now debug; warn is left for a listing that failed, a subdirectory that could not be searched, and an unexpected error. A declared file that is genuinely absent is the caller's to report, which it already does once via missingFiles rather than once per lookup.
  • The signed-in texture lookup no longer guesses at a name that does not match. About 150 lines of similarity rules — singular against plural, a leading word dropped, a length-ratio partial match, and a mapping from any colour-ish filename to any body-ish one — came from making one model work and then applied to every model. A rule that matches two different filenames is a guess, and a guess that lands wrong serves the wrong texture with nothing to say so. The case it was most likely to get right, a name differing only in case, is handled before that point and on the server since PathLocator; what remained only fired when a model referenced a file that is genuinely absent, where rendering untextured is the honest answer. getFileIdByPath goes from 366 lines to 155, and the conventional-texture-directory search that was duplicated in its catch block is now one function.
  • Unblocked dependency updates and refreshed the lockfile, superseding ten stalled dependabot PRs (#104–#114) in one pass.
  • stylelint 17 rule fallout, all resolved — color-function-alias-notation rewrote 30 rgba() calls across four files.
  • The 3D navigation controller is now a "Split console": a steering annulus with its buttons on an attached rail, styled from Nextcloud tokens. The eight decorative arrows are gone; arrow keys now steer a focused ring. The 15% dead zone, 110% hit slop and hold-to-repeat behaviour are unchanged.

Fixed

  • A texture in a subdirectory the model does not name was only found if the directory had a conventional name. The search that walks whatever subfolders the listing reports asked for each one without includeDependencies, and in that mode the server filters its answer to 3D models and drops every image — so the one pass written to find textures in subdirectories could never return one. A separate pass over sixteen hardcoded names (textures, maps, images, and case variants) covered the common cases and hid it, leaving a texture in a folder called skin or PBR unreachable with the folder sitting right there in the listing. Signed-in sessions only.
  • "Reset" and "Fit" could not be clicked. The viewer's top bar is position: absolute inside a container that was not position: relative, so it anchored to the wrong ancestor and sat under the app content.
  • The navigation controller was hidden behind the app navigation, which Nextcloud renders at z-index: 1800. It is now anchored to .three-viewer.
  • The 3D file browser invented folders that do not exist. getFolders() stripped the parent prefix with str_replace(), which removes every occurrence: with no parent the prefix was a bare /, so models/textures was reported as modelstextures and matched no files.
  • FileIndexMapper called escapeLikeParameter() on the query builder, where it is not declared — it lives on IDBConnection. It worked only because the server's concrete class happens to carry it, the same bet that fataled the Nextcloud 34 upgrade.
  • A test file was raw binary as far as git was concerned. \0\x01 reached xmlModelDependencies.test.js as literal bytes rather than escapes, and a NUL makes git stop producing diffs for a file — it had dropped out of line-level review while passing CI. A new guard scans source for C0 control bytes.
  • composer test:unit ran every test under tests/, not the suite its config names. It passed a positional tests path, which overrides the configured testsuite — the same defect that made the old integration workflow run the unit suite under another name. Retiring that workflow left this copy of it in place, where it stayed invisible until a directory appeared that the glob should not have swept up.
  • A texture declared in a different case than the file rendered as missing. 3DS keeps map names in DOS 8.3 form, so exporters write WOOD.JPG beside a file saved as wood.jpg; MTL files written on Windows do the same with subdirectories. The declaration matched — that lookup is already case-insensitive — and the fetch then asked storage for the path exactly as written, so the model rendered untextured with its texture sitting next to it. The exact path is still tried first; only a miss walks the model's own folder, stopping at the first segment with no match. Where both cases exist, the exact name wins.
  • The merge gate blocked a ready pull request after a force-push. Every workflow here uses a concurrency group with cancel-in-progress, so superseding a running job leaves a CANCELLED entry in the status rollup beside the SUCCESS of the run that replaced it — both under the same check name. The gate read every entry and reported the superseded one as a failure. It now takes the most recent entry per name. This is the second state-reading defect in that script, so the deciding logic moved to pr-ready-lib.mjs and gained tests; a gate that cries wolf is the one people learn to merge past.
  • The case-insensitive lookup above covered the public route only, and only its last step. An OBJ's textures are named by its material, so the material has to be read before they are known at all — and that read still asked for the exact path, so a mtllib CHAIR.MTL beside chair.mtl left every texture in it undeclared. Signed-in sessions missed for a second reason: /api/files/find and /api/files/list resolve the declared path themselves, and both matched case exactly, so Textures/wood.png beside a folder saved as textures found nothing. All three now share one implementation.
  • A path climbing out of the user's folder returned 500 instead of 404. Folder::get() raises NotPermittedException for a path like ../../etc/passwd, and /api/files/find caught only NotFoundException — so probing the endpoint produced an internal error and a logged stack trace apiece, and told the caller a refused path is not the same as an absent one.
  • Every viewer session logged a PCFSoftShadowMap has been deprecated warning. Three.js r184 reassigns the constant on the first shadow pass, so the app was already rendering PCFShadowMap.

Removed

  • Retired the "Integration Tests" workflow, which reported coverage it did not have. test:integration and test:migration passed a positional path that overrode their configured testsuite, so both ran the unit suite under other names — and the job had no Nextcloud server to integrate against. test-phpunit.yml covers those tests across the same OCP 31–34 matrix.

Security

  • Resolved four npm advisories in production dependencies via npm audit fix, lockfile only: form-data CRLF injection (GHSA-hmw2-7cc7-3qxx), postcss path traversal (GHSA-r28c-9q8g-f849), fast-xml-builder sanitisation bypasses, and nine in dompurify.
  • Cleared the brace-expansion DoS (GHSA-mh99-v99m-4gvg / CVE-2026-14257) from the production graph, taking npm audit --omit=dev from 7 high to 0. Only brace-expansion@5.0.8 is patched, and it renamed its default export, so it cannot be forced globally; pinning overrides.webdav.minimatch to ^10.2.3 reaches it instead. Glob behaviour is unchanged and the advisory's proof of concept no longer exhausts the heap.
  • Capped material libraries per model at ModelDependencyResolver::MAX_MATERIALS. Each mtllib name costs a storage lookup and ~226,000 fit in the scanned header, so one crafted model turned every /dep/{name} request on its public share into hundreds of thousands of lookups. The sharer can be the attacker, and the route has no rate limit.
  • Known residual: the shipped browser chunk still carries the unpatched brace-expansion. Vite resolves webdav to a prebuilt bundle with it inlined, which no override can reach; it clears when webdav republishes. Unreachable in practice, since nothing here passes a glob to getDirectoryContents. The dev tree still reports the advisory through eslint 8 and jest.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureQX95f1tTvJoYA9LqLShsNFPKOwHPgMmw0dE0Rq/KdnyoJLpH+TOYawYFTn6RFykF5RIFDyCrTTEDJo5ehLy3bLnyCRX9pLza7My58nDdCND+fRlHgzvUNwzfULSQC1zPi92sHmy+SPqnUrp6xhevBe57/IWxE2JyUf/T80Gf+EXwWFzc9uoVoE2+mHvzvV0db/ZVO+OX/HHgYzZ65Q8wZTbycb1nZFx8RaxkJogd/5Nh8aEb/NlpyKRlJi9Zx3XtyQL6AL0Dcnv2eOQbOuA5VnwFql1UqCtmR1400ZCd0Cf0sSaoX6S1XW3+oLjGhJSHBEi/A+0MVpDChPPHSjfH+ER5gr628NQFfVSxFBXr0hHbHx3r8wBxIk0kRyqjR5Uj5CNzg8QVQsNhhFvsP7FFl59Il2JmzsJXAe99e/8mf1rMGmEZ5kVXwUImUl4Bqgc3IDF5SKUTfspNPWKWojRzT9sRgLZDI1RFqep2ov1wMNYOQ4Qzbzk9N93iumDQk1FsiYud2VO3TVpjaNCc6vWL/S+jA03TksnkF4sqxexJRAGwBpyEcwOHwUG0R6HVZpchkdhL6MW5UVwSb2i9b1eIqs1+DbQ70HCffX7pPIxFW6zOVDsdNb00Q6vaYeT2q9IIR32DnKj20bnBjLgP3BX61V3Ul7PLoAbbhF8vABR4CEA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.2
Release Details
UpdatedJuly 27, 2026, 11:31 a.m.
Changelog

Security release. Upgrade immediately if you use password-protected link shares containing 3D models.

Security

  • Share passwords were not enforced on the public 3D file endpoint. PublicFileController extended plain Controller with #[PublicPage], and PublicShareMiddleware returns early for anything that is not a PublicShareController — so no share authorisation ran at all. Combined with ShareFileService::loadLinkShare(), which only checked that getShareByToken() matched, anyone holding a share token could read the file regardless of its password. The token is the part of the URL people paste into chats and emails; the password exists precisely for when the token alone should not be enough. PublicFileController now extends PublicShareController and implements isValidToken(), isPasswordProtected() and getPasswordHash(), delegating password and token validation to the framework and picking up brute-force throttling with it. findValidLinkShare() additionally rejects non-link/email share types and expired shares, and treats a ShareNotFound from the share manager as "no share" rather than letting it escape as a 500. Present since the controller was added in 1b0ca86 (2025-09-15), so every release from v1.7.9 through v3.3.1 is affected. Verified on Nextcloud 34.0.2.1: a password-protected share now returns 404 without the password and 200 with it, ordinary public shares are unaffected, and expired shares return 404 instead of 500.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturemn6HBntWm2p/at2aww2v9uJiZOjj7tNXpISa325yfW2SKnZnaJHt7wSDJnb6ghsYuF1u19BVFg+1b+e/hFTFmhD8l1a0BL69FqZej7a4bwwfFbovRiDUkkZjik0nVahdD3k6fg6qabxnJbHEh79Lz2/vgcE/5nzAio06G5xJrZgeR10aoZs5oxMEpnNQlPWCiIZV78oY2PCGWosOEQaz78SzU2sX1Saq4goXMCyG1ElYLLeMbtmTPv3g9Dp08FyUctxtxGX1JbuNFg2F2AuhZSyMmFcnS6jGp9STzWZrD6x9jbd6KP5/mu6dPe88CAouT/RmUSFS14CCYAXOPdeqc0wgsOJncpoRZycZ/FwACJKGOjzR1qACdtG5wKjXnMDMJA5mdgDL+G77s7PzeY+u4jBkS2iF8swLxzZphid2HGNN5Rg4NECyCwsBYM/o+hbPVpro6iLKOg/hpiugpoQpwDw0RmcujPDYWewonRnNKdB+7i/Qk5FXDluhqGr7GoRek+nZaqm/pxSIGEeSnkpqoPAxHY978OncdxhKSIe8ACCxl9g9nokM4bxLA4zXWGHL8Onnb/DdWIGEfuGIhdhV/5QtSSW1xW75HrTcuHPJJFBAF2ovwQYPfexY26ZU0m7pyt2ucN8xxkkpuD6RDMpm2U/v9y+AxbtuNunW2CoKqm0=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.1
Release Details
UpdatedJuly 27, 2026, 10:35 a.m.
Changelog

Hotfix for Nextcloud 34. 3.3.0 is unusable on NC 34 — both the viewer page and every model download return HTTP 500, and the app cannot be upgraded or enabled. Nothing else changed.

Added

  • Regression tests for both fatals. tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface with no __call() fallback, so it raises the same Error production did. tests/unit/NoPrivateServerApiTest.php scans lib/ for any use of the private \OC container or the legacy OC_* static classes, stripping comments via token_get_all() first so documentation about a removed API isn't mistaken for a call to it. Both fail on the pre-fix code. Note that composer test:unit is not currently run by any CI workflow — test-integration.yml runs only test:integration and test:migration — so these are verified locally until that gap is closed.

Changed

  • nextcloud/ocp dev dependency moved from dev-stable30 to dev-stable31, to match the min-version="31" declared in appinfo/info.xml; the weekly update-nextcloud-ocp-matrix.yml job, which had target: ['stable30'] hardcoded and kept re-pinning it, now tracks stable31 too. The pin deliberately follows min-version, not max: pinning to the oldest supported server is what stops us using APIs that don't exist there yet, and ocp stable33+ requires php ~8.2, which cannot coexist with the config.platform php 8.1 that NC 31 support demands. Verifying against the newest branch is psalm-matrix.yml's job — it already builds an ocp matrix across the full min..max range from info.xml. Psalm run manually against stable34 for this release confirms no further removed-API calls in lib/ beyond the two fixed below, and flags ten DeprecatedMethod notices worth scheduling: IConfig::getUserValue/setUserValue/deleteUserValue in PageController, SettingsController and ConfigController, plus IContainer::query in Application. None break on 34.

Fixed

  • App upgrade fataled on Nextcloud 34 (#116) with Call to undefined method OC\Server::getDatabaseConnection(). The Version010903Date20251121000000 migration reached into the private \OC server container in its constructor; NC 34 stripped getDatabaseConnection() from that class, which now exposes only getUserFolder(), getWebRoot() and getL10N(). It now resolves the connection through the public OCP\Server::get(IDBConnection::class) locator. Deliberately not constructor injection: MigrationService::createInstance() falls back to new $class() when the container cannot resolve a step, and an injected dependency would turn that fallback into an ArgumentCountError. The stale use OC; import is gone and $connection is now typed. Reported with a patch by @EX0l0N; same fix independently submitted as #117 by @coverslide.
  • Every model download and viewer page render fataled on Nextcloud 34 with Call to undefined method OCP\AppFramework\Http\EmptyContentSecurityPolicy::addAllowedChildSrcDomain(). Nextcloud removed the legacy child-src helper from its public CSP class (deprecated years ago in favour of the split worker-src/frame-src directives), so the call added in commit 4e0a532 ("fix CSP") became a hard error the moment the app ran on 34 — taking down FileController::serveFile and both PageController entry points, not just the worker policy. ResponseBuilder::addCspHeaders now calls addAllowedWorkerSrcDomain('blob:'), which is the correct directive for the blob-URL Web Workers that DRACO, KTX2 and web-ifc spin up, and has existed since NC 17 — safe across the whole declared 31–34 range. Reported in #116. The new tests/unit/Service/ResponseBuilderCspTest.php drives addCspHeaders() against a policy double that mirrors the NC 34 API surface exactly, so it reproduces the production Error on the old code regardless of which OCP branch happens to be vendored.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturenWYgrEeOKWzfUMwxb3X0hAzGbWgJ9vd3jRvsIFUKLCvTsARpp09yWa5mnupOhaRtHcffz0eidnz5Blw7uN8sTMvty2tpnzgLad3CTvV401vV+je0pyeWgol+mkhoymUwBNcvlR4OWx4NWHX1azz9NdLAlF/J+A1ImE3D2UMf5mXlGctjznQwBeMLrXkKke1qdy23heuGTi6HCnNicY1PpkhC9RcmY0wlBbZr9/kx/TbCYx136AKjctjt4554x9SXkiAjt5S1SSEFefSaNxURYWlSx6xeCt4enQVAfA2uNfj772yxyWRhyvcQMnfIFz4IGTYMCi3KlKZgQvSa/Zj1KNP6d9NWOubG+RqOjzv84wjznJ6xoSTEoNLuQhuYt1zqW2wDyDFT9GD2jSONDYZjOZjal37C4NEvEv9HIkZYkVBL4kvwlytafsD9u4cmKCmgv0ROvPCM2PWVmnT3kXGOS+ivKLcaC0Zt0VciNlhYIAawDLDL+1j+WKRHIPhNUsLjiNfqbmUWUZlbEHPJvdIxTBfePDbvnnyFGWlUBKO+QoEo0JPHrpfisz31eb88G/33/1iblqUUZ3OwU71GAHjFvD8Vf0ivihZZSUqsTDdiEGU1dsbT7DnkFi2bLNfNQhcG14k03NzUVQW+fODwNoRO9pkWJnadgsZQ1ICJAj3sw9M=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.3.0
Release Details
UpdatedApril 20, 2026, 6:47 a.m.
Changelog

Fixed

  • 3DS files loaded tipped on their side when the exporter used Y-up instead of the 3ds-Max Z-up default. The loader had been hard-coded to rotate every 3DS model -90° around X (commit d8117a0, "fix: add coordinate system rotation corrections for Z-up formats"), which helped CAD-style Z-up files but silently broke Y-up exports — Cottage_FREE.3DS for example rendered with its long axis as the vertical. Dropped the unconditional rotation; 3DS files now pass through at their authored orientation. Most modern 3DS exporters (Blender, recent Maya) are already Y-up, and Z-up outliers can be fixed by orbiting or — if this becomes common — by a future user-facing orientation toggle. Other Z-up-rotating loaders (STL, PLY, 3MF, DAE) are unchanged because those formats have stronger Z-up conventions that are not affected by this fix. Reproduced and verified in the live Nextcloud container by downloading the user's 3DS via WebDAV, inspecting bbox dimensions with a new scripts/inspect-3ds.mjs harness (raw X=1243, Y=675, Z=1465 — Y is the height), and hard-refreshing the viewer page.
  • Standalone viewer: KTX2 loader threw on init with Cannot read properties of undefined (reading 'isWebGPURenderer'), so models with Basis/KTX2-compressed textures silently fell back to placeholder textures. Root cause: the standalone ThreeViewer.vue never forwarded the live WebGLRenderer into the loading context, and Three.js r182's KTX2Loader.detectSupport(renderer) dereferences renderer.isWebGPURenderer before checking anything else. Fix: pass renderer: renderer.value in the context, plus a defensive guard in gltf.js that skips KTX2 entirely (with a warning) when no renderer is available instead of throwing. The modal viewer was unaffected — it already forwarded this.renderer. Live-E2E harness now also watches for KTX2/WebGPU console complaints so the regression can't sneak back in.
  • Standalone viewer: glTF models with embedded data: buffers failed to load under Nextcloud's Content Security Policy. The app CSP allowed data: for img-src but not connect-src, so GLTFLoader's fetch() of embedded base64 buffers (the default export shape of many gltf files) was blocked by the browser. ResponseBuilder::addCspHeaders now also adds data: to connect-src. Caught end-to-end by driving Playwright against the real dev container (scripts/live-e2e-check.mjs).
  • Skip-to-viewer link wasn't in a usable tab position — it was rendered inside <NcContent> → <NcAppContent>, which put it ~9 tabs deep behind Nextcloud's header, nav sidebar, and file list. Wrapped in <Teleport to="body"> so the anchor lives at the body level regardless of where Nextcloud's chrome wraps us. DOM-verified focusable in the live container; clicking it moves focus to #viewer-wrapper. The CHANGELOG entry below that claims "first tab stop" was aspirational — Nextcloud's own Skip to main content / Skip to navigation links always win the first two positions, which is correct behavior we can't (and shouldn't) override.

Added

  • Four more CAD formats via OpenCascade — STEP, IGES, BREP, FCSTD (closes #97): All four formats come from the OCCT family of NURBS/B-Rep CAD files and share one WASM integration. Uses occt-import-js (Viktor Kovacs's purpose-built emscripten port of OpenCascade's import + tessellation) — much smaller than vanilla opencascade.js (~7 MB WASM vs 10+) and with a JSON-based mesh output that's a clean fit for Three.js. Architecture: one shared src/loaders/occt-runtime.js exposes getOcct() (memoized module promise so the 7 MB WASM downloads once across all 4 loaders) and buildGroupFromOccResult() (walks the assembly hierarchy preserving node names, transforms are already baked into tessellated vertices so we don't reapply matrices). Each format gets a thin ~25-line loader: STEP (.step, .stp) → ReadStepFile, IGES (.iges, .igs) → ReadIgesFile, BREP (.brep, .brp) → ReadBrepFile, FCSTD (FreeCAD document) → unzip via fflate, filter for .brep entries, feed each to ReadBrepFile — per-body failures don't take out the whole document. The resulting bundle shape: four 1 KB loader chunks + one 725 KB shared occt-runtime-*.chunk.mjs + 7.3 MB WASM served separately from /apps/threedviewer/occt/. Main/app bundles unchanged (0 B trend). Not unit-tested because the whole pipeline needs live WASM + real CAD fixtures to exercise meaningfully; covered by the existing smoke suite pattern.
  • Fifth new format — IFC (.ifc) (#97): Industry Foundation Classes, the de-facto BIM interchange standard. Loader in src/loaders/types/ifc.js uses ThatOpen's web-ifc WASM (~5 MB, lazy-loaded on first .ifc open) and a hand-written Three.js bridge — web-ifc-three has been deprecated and its modern replacement (@thatopen/components) is a heavy framework we don't need just for file loading. Bridge logic: StreamAllMeshes walks the IFC element graph, each FlatMesh's PlacedGeometry entries yield tessellated vertex data in interleaved [px,py,pz,nx,ny,nz] layout which we deinterleave into separate position/normal attributes, then apply flatTransformation (column-major 4×4) and per-geometry color. Express IDs (expressID, geometryExpressID) are preserved on mesh.userData.ifc for future property-panel tooling. Memory is carefully managed — geometry.delete() in a finally block releases the WASM-side handle per mesh, and CloseModel runs unconditionally so parse errors don't leak the full IFC model. WASM copies ship via scripts/copy-decoders.mjs to /apps/threedviewer/web-ifc/ (both single-threaded web-ifc.wasm and multi-threaded web-ifc-mt.wasm — IfcAPI picks the variant based on runtime feature detection) so air-gapped Nextcloud deploys don't need CDN reachability. JS glue is lazy-chunked into a 3.4 MB ifc-*.chunk.mjs that only loads when a user opens a .ifc file; main/app bundle impact is minimal. Not unit-tested because the whole pipeline requires live WASM + real IFC fixtures; covered by the smoke spec suite instead.
  • Fourth new format — dotbim (.bim) (#97): JSON-based BIM format from dotbim.net — simple schema (meshes + elements with { mesh_id, vector, rotation, guid, type, color, info, face_colors? }). Loader in src/loaders/types/dotbim.js handles mesh instancing (multiple elements sharing a mesh_id each get their own cloned geometry), element transforms (translation vector + unit quaternion rotation), RGBA byte colors, optional per-face colors (v1.1.0 schema — unindexes geometry so each triangle's 3 verts carry the face color), and tolerates legacy exporter quirks (missing/null rotation, mismatched face_colors length). Element metadata (guid, type, info) is stashed on mesh.userData.dotbim for future inspection tooling. 10 Jest specs in tests/unit/loaders/dotbim-parser.test.js cover instancing, transforms, color handling, transparency, legacy-rotation fallback, orphan elements, metadata, and JSON/schema validation error paths. Zero WASM, zero external deps, ~180 lines. Like the other new loaders it's lazy-chunked via the registry's dynamic imports — 0 B impact on the main/app bundles.
  • Three new 3D formats — OFF, AMF, 3DM (closes #97 partially): (1) .off (Geomview Object File Format) — hand-rolled plain-text parser in src/loaders/types/off.js covering OFF, COFF (per-vertex colors, float or 0–255 int), N-gon fan triangulation, inline # comments, and binary-OFF rejection. 9-test Jest suite exercises the parse path end-to-end (tetrahedron, n-gon, comments, COFF float/int colors, degenerate inputs). (2) .amf (Additive Manufacturing Format) — wraps three.js's bundled AMFLoader, handles both XML and ZIP-compressed variants. (3) .3dm (Rhinoceros) — wraps Rhino3dmLoader, loads rhino3dm.js + rhino3dm.wasm (~4 MB) on first use from the app's bundled copy at /apps/threedviewer/rhino3dm/ so air-gapped Nextcloud deployments don't need CDN reachability. scripts/copy-decoders.mjs now copies the rhino3dm assets alongside DRACO/Basis. All three formats are registered across the 5 sync points (lib/Constants/SupportedFormats.php, src/config/viewer-config.js, src/loaders/registry.js, src/main.js::SUPPORTED_MIMES, appinfo/mimetypemapping.json); npm run format:check PASS. Each loader is lazy-chunked via the registry's dynamic imports, so the main/app bundle size is unchanged (only users who open the new format pay the download cost). The 6 remaining formats from #97 (step, iges, brep, fcstd, ifc, bim) all require OpenCascade.js or web-ifc WASM (~5–10 MB each) and are deferred until demand justifies the bundle hit.
  • Forced-colors (Windows High Contrast) CSS hardening: New central override sheet (src/css/forced-colors.css, imported from main.js) scoped entirely to @media (forced-colors: active) — no effect on default rendering. Addresses three patterns the a11y audit found: (1) custom badges (.stats-badge, .fps-badge, .active-badge, .last-used-badge) that rely on background-color + text-color alone collapsed into the surrounding surface once the UA repainted both with system colors; they now get a 1px CanvasText border so they stay visible. (2) .filter-format-chip.active lost its visual distinction from the inactive state because both backgrounds became Canvas; the active variant now uses Highlight / HighlightText with forced-color-adjust: none so selection state survives. (3) Focus states that suppressed outline and relied on box-shadow or colored border-color (.export-select:focus, .annotation-text-input:focus, .skip-to-viewer:focus) had no visible focus indicator; they now re-enable a 2px Highlight outline. Verified by a new Playwright spec (tests/playwright/a11y-forced-colors.spec.ts, 5 tests) that loads the real CSS file into static fixtures mirroring each component shape and asserts border/outline width > 0 under emulateMedia({ forcedColors: 'active' }). Caveat: Playwright's forced-colors emulation uses a neutral palette — this closes the keyboard/CSS correctness gap but an interactive Windows HC walkthrough is still the final source of truth for theme-specific contrast issues.
  • i18n parity tooling (scripts/check-i18n.mjs): Walks src/ (.vue/.js/.ts) and lib/ (.php) and extracts every t('threedviewer', 'string'), this.t(...), and ->t(...) call via regex. Diffs the extracted set against l10n/en.json: ERROR on source strings missing from en.json, WARN on orphan keys no source uses, per-locale coverage summary that reports missing, passthrough (value === key), and orphan counts. --sync-en auto-adds missing keys (value = key); --prune removes orphans. Wired as npm run i18n:check / npm run i18n:sync; i18n:check is now part of the validate chain. First sync closed 329 missing keys and pruned 87 orphans in en.json — ar/de/es coverage now reports accurately at ~27% (previously looked higher only because en.json itself was incomplete).
  • Export pipeline unit tests + MIME consistency fix: Nine Jest specs in tests/unit/composables/useExport.test.js cover getGeometryStats (indexed, non-indexed, multi-mesh groups, non-geometry descendants) and exportAsSTL/exportAsOBJ/exportAsGLB (correct MIME — model/stl, model/obj, model/gltf-binary — correct byte length, null-object guards, and a multi-material OBJ scene that passes through the exporter with its material array and geometry groups intact instead of being flattened). Three.js exporters are mocked so the test runs deterministically in jsdom without pulling in Three's ESM-only subpath imports; the real exporter output remains covered by the Playwright smoke suite. Shipped alongside a MIME consistency fix in SlicerModal.vue — the slicer-export fallback path was still using application/octet-stream for STL and text/plain for OBJ even though the main Export Model path had been updated to model/stl / model/obj; both paths now agree.
  • Loader smoke-test coverage for cancel/retry/network-drop: Two new Playwright specs in tests/smoke/viewer.spec.ts use page.route() to deterministically simulate (a) a user cancelling a hanging load and then retrying — the second fetch is fulfilled with a fixture and the viewer reaches __LOAD_COMPLETE, and (b) a network failure (route.abort('failed')) that must surface via __LOAD_ERROR without being misclassified as a user abort. ThreeViewer grew two tiny test hooks (window.__LOAD_COMPLETE on successful model-loaded, window.__LOAD_ERROR on non-abort errors) to give tests a DOM-independent signal — production code is unchanged in behavior.
  • Accessibility follow-ups: Keyboard users now get a "Skip to 3D viewer" link as the first tab stop (visually hidden until focused, slides into view on focus) that jumps focus past the navigation directly to #viewer-wrapper. Both modals (SlicerModal, HelpPanel) now trap Tab/Shift+Tab inside the dialog while open and restore focus to the element that opened them on close — implemented via a dependency-free useFocusTrap composable. Modals also gained aria-modal="true" and tabindex="-1" on the dialog surface. Covered by a new Jest unit suite (tests/unit/composables/useFocusTrap.test.js) and a Playwright a11y spec (tests/playwright/a11y-skip-link.spec.ts) that verifies skip-link behavior under forced-colors: active (Windows High Contrast).
  • Transform Gizmo: Translate, rotate, and scale models interactively with Three.js TransformControls. Mode selector (Move/Rotate/Scale) and reset button in the Analyze section. Disables orbit controls while dragging.
  • Animation clip selector: Dropdown in the Animation panel to switch between individual animation clips (e.g., Run, Walk, Idle) for models with multiple animations
  • Texture optimization pipeline: Quality presets (Original/High/Medium/Low) with Canvas 2D downscaling, memory tracking, and configurable setting in Personal Settings
  • X3D parser: Full XML-based parser replacing placeholder cube — supports IndexedFaceSet geometry, materials, textures, transforms, and DEF/USE references
  • Volume & surface area measurement: Model statistics panel now shows actual mesh surface area (sum of triangle areas) and mesh volume (signed tetrahedra method, accurate for watertight meshes), in addition to bounding box volume
  • Custom color palette editor: Personal Settings → SlideOutToolPanel now exposes four user-overridable scene/chrome colors on top of the base light/dark theme: background, grid, toolbar bg, and toolbar text. Each has a native <input type="color"> picker plus a per-key "×" clear button; a single "Reset all palette colors" button clears everything at once. Overrides persist to localStorage['threedviewer:customPalette'] and are merged on top of whichever base theme (auto/light/dark) is active, so switching themes leaves your palette alone. The viewer rebuilds its THREE.GridHelper in place when gridColor changes and swaps scene.background when background changes — no reload required.
  • Decoder worker pool tuning (parallel decoding): GLTFLoader's DRACO and KTX2 sub-loaders now call setWorkerLimit(n) with n = min(hardwareConcurrency - 1, 4) (floor 2), so GLB textures and compressed meshes decode in parallel across multiple Web Workers instead of serializing on one. Defensive feature-detection — falls through when the Three.js version doesn't expose setWorkerLimit. Result: GLBs with multiple KTX2 textures load noticeably faster on 4+ core machines without starving the main thread or flooding it with workers on 1-core devices.
  • Memory pressure auto-step-down: The performance composable now samples performance.memory.usedJSHeapSize each frame and, when usage crosses 85 % of the configured maxMemoryUsage cap (default 500 MB), auto-switches to the Low performance mode — reducing pixel ratio, disabling shadows, and applying LOD. A one-time warning toast ("Auto-reduced quality — memory pressure") surfaces what happened; the flag clears once memory drops back below 70 % so the user can re-raise quality manually. 5-second cooldown prevents thrashing, and the check gracefully skips browsers without the non-standard performance.memory API (Firefox, Safari).
  • Background indexing status API + progress bar: New GET /api/files/index-status endpoint backed by ICacheFactory::createDistributed returns { active, processed, total, percent, startedAt, updatedAt } for the authenticated user. FileIndexService.reindexUser now pre-counts supported files (respecting .no3d and hidden-folder skip rules), then bumps the processed counter after each file. The Personal Settings "Re-index now" button starts a 750 ms poll in parallel with the blocking POST request and renders a live NcProgressBar ("142 / 284 (50 %)"), falling back to "Scanning…" until the pre-scan completes. Status TTL is 1 hour.
  • Adaptive texture streaming (visibility-aware queue): useProgressiveTextures now accepts a (camera, scene) streaming context and sorts its pending queue by frustum visibility before each batch — textures on meshes currently in view load first, off-screen ones are deferred. When the camera moves, the very next batch picks up the new viewpoint. queueTexture has a new optional mesh argument for visibility scoring; the signature stays backward-compatible, so existing callers are unaffected. ThreeViewer auto-registers the context on init so future loader pipelines that opt into progressive loading get adaptive scheduling for free.
  • Measurement suite upgrades: The Statistics panel's measurement section is now a full measurement tool:
  • Unit picker: Choose mm / cm / m / in / ft / generic units from a dropdown; bounding box, surface area, and volume values re-render instantly with the right suffix (mm, mm², mm³, etc.). Defaults to the project's configured VIEWER_CONFIG.measurement.defaultUnit so it matches the existing ruler tool.
  • Per-mesh breakdown: When a model has more than one mesh, a scrollable list shows each mesh's surface area and volume in the selected unit. Clicking a row focuses the panel on that mesh — the Width/Height/Depth/Area/Volume rows switch to its numbers and a "Focused: mesh name" header with a Show-all link appears. Helper meshes (transform-gizmo pickers, annotation markers) are excluded.
  • Pick mesh in viewport: A one-shot button arms a raycasting click handler on the canvas; the next click on a mesh focuses it in the stats panel. Works with any mesh in the loaded model, respects visibility, and skips gizmo helpers.
  • Watertightness badge: The section header flags each mesh as Watertight (all edges shared by exactly two triangles — volume is reliable), Not watertight (open or non-manifold edges detected — volume should be treated as approximate), or Unknown (non-indexed geometry, or >500k triangles so we skip the edge-map check for performance). Aggregated badge appears at the top of the section; per-mesh flags appear next to each row in the breakdown.
  • Copy measurements: A button copies a plain-text report of the current values (bounding box, surface area, mesh volume, bbox volume, watertight status, per-mesh table) to the clipboard in the selected unit. Uses navigator.clipboard.writeText on secure contexts with the textarea + execCommand fallback for legacy browsers. Success/error toast via the existing push-toast channel.
  • WebXR / VR mode: Enter immersive VR via the 🥽 button in the top bar (only shown when the browser advertises immersive-vr support). Animation loop swaps to renderer.setAnimationLoop during XR sessions, and FPS throttling is bypassed since the headset enforces its own cadence. Testable without a headset using the Chrome WebXR API Emulator extension.
  • Annotation JSON export/import: Save annotations as a versioned JSON document and re-import them later. The schema includes format, version, exportedAt, modelFilename, and an annotations array of { id, point, text, timestamp }. Import/Export buttons appear in the Annotations overlay when annotation mode is active.
  • Scene comparison diff overlay: When two models are loaded side-by-side via Comparison mode, a "Scene Diff" panel auto-appears showing original vs. comparison stats (vertex count, face count, mesh count, bounding box X/Y/Z, diagonal length) with color-coded deltas (green = increase, red = decrease). Helper meshes (transform gizmo pickers, axes/grid helpers) are excluded from stats so the diff reflects only the loaded geometry. The overlay is dismissable via × and re-openable from a "Diff" button in the comparison controls.
  • Annotations persistence (per-file backend save): Annotations now save automatically to your Nextcloud account, keyed by file ID, so they reappear next time you open the same model. Backed by a new AnnotationsService that stores one JSON document per (user, file) under app data (appdata_*/threedviewer/annotations/{userId}/{fileId}.json), with GET/PUT/DELETE routes at /api/annotations/{fileId} validating that you have access to the underlying file before reading or writing. Saves are debounced 600 ms after each change; clearing all annotations issues a DELETE so the backend doc is removed instead of storing an empty list. A small status pill ("Loading… / Saving… / Saved / Save failed") in the annotation overlay header surfaces sync state. 256 KB cap per document.
  • Shareable view link: New "Copy View Link" button in the View section copies a URL that encodes the current camera viewpoint (position, target, zoom) as a compact cam=px,py,pz,tx,ty,tz,z query parameter. Opening the link reloads the model and restores the exact same angle after auto-fit runs, so you can send collaborators a link and say "check out this corner of the model" without needing a live session. Toast feedback on copy success/failure; button disabled until a model is loaded. Works on any secure context (HTTPS or localhost) via navigator.clipboard, with an offscreen-textarea + execCommand fallback for legacy environments. No backend — pure URL round-trip.
  • Clipping box (6-plane section analysis): The existing Cross-Section tool now has a Plane / Box mode switch. Plane mode is unchanged (single cross-section plane with axis + position slider). Box mode enables six axis-aligned clipping planes, one per face of the model's bounding box, each with an independent 0–1 offset slider that moves that face inward toward the opposite side. Setting xMin=0.35 and zMax=0.35, for example, slices the leftmost 35% off along X and the nearest 35% off along Z, leaving the intersection slab visible with interior surfaces rendered via DoubleSide. A "Reset box" button returns all six offsets to 0. Plane and box state are kept independently so toggling between modes is non-destructive, and box offsets auto-remap when a new model is loaded.
  • File browser search & filters: New filter toolbar in the file browser. At the Folders/Types/Dates overview level the search box runs a global recursive search that walks every folder, type, and date bucket and surfaces matching files as a flat results grid (each card showing its full path) — so searching wolf from the Folders root finds Wolf-Blender-2.82a.glb two folders deep without manual drilling. Inside leaf views (drilled into a folder, type, or month) the search filters both subfolder cards and file cards together, and unlocks two extra controls: multi-select format chips (one per extension actually present in the current view) and a size bucket dropdown (Any / < 1 MB / 1–10 MB / > 10 MB). Filters compose with AND between categories and OR within format chips, and a "Clear filters" button appears as soon as any filter is active. Filters auto-reset when navigating between folders/types/dates so a stale query never makes the next view look empty.

Fixed

  • FBX taillight/lens transparency: FBX post-processing was force-overriding every material to opacity = 1.0; transparent = false, which made authored translucent lens shells (taillights, headlights, glass) opaque and hid the coloured mesh inside — the Mercedes GLS rear lights rendered as a solid grey slab with the red only visible if the camera was pushed inside the body. Now preserves the FBX-authored opacity: materials with 0 < opacity < 1 are left transparent with depthWrite = false so inner meshes show through; only materials with opacity <= 0 (which would render invisible) are still forced opaque.
  • Stats panel — File size on multi-file loads: File Size showed "0.00 MB" for formats that come with sidecar assets (OBJ+MTL, FBX with textures, GLTF+BIN). The stats panel was reading modelLoading.progress.value.total, but multi-file loaders report progress as a percentage (max 100) rather than bytes, so the resulting "100 B → 0.00 MB" rounded to zero. Now reads the actual byte count from modelSourceFiles.value[].size (matching main filename first, else first file), with the progress-total value only used as a fallback when it looks like real byte counts (> 1 KB).
  • Stats panel — Texture memory accounting: Textures section reported "Memory: 0.00 MB" while textures were still loading, because the loaded-image check used a 512×512 fallback that happened to resolve before decode — making tiny placeholders look real. Now distinguishes three states: loaded (dimensions ≥ 4×4 contribute to memory), pending (flagged separately so the UI can show "loading…"), and missing (1×1 placeholder or HTML img with empty src / zero naturalWidth). ThreeViewer polls analyzeModel once a second for up to 8 seconds after load when any texture is pending, so async decodes get picked up without the user needing to reopen the panel.
  • FBX dark rendering without textures: When an FBX referenced textures (e.g. texture.png) that weren't shipped alongside the model, the loader substituted a 1×1 base64 placeholder into mat.map. final = color × placeholder then multiplied every surface down toward near-black, producing a silhouette-dark render where the Mercedes GLS looked like a blob. The loader now detects its own placeholder (1×1 image size) and nulls out mat.map; a new "clay mode" kicks in when zero textures resolved from dependencies, brightening near-greyscale dark material colours to a pleasant 0.75 clay-grey while hue-preserving the brightening on coloured materials (green license plate, red brake lights) so they still read as coloured instead of washing out to clay.
  • ZIP export — main file path: The main model file is now packed under its basename (e.g. eyeball.obj) instead of its full Nextcloud path (e.g. /3D files/Eyeball/eyeball.obj). The leading slash is invalid in ZIP archives and broke extraction on some platforms.
  • ZIP export — preserved subdirectory layout: Textures discovered in subdirectories (e.g. textures/) are now packed under their original relative paths in the ZIP instead of being flattened to the root, so the unzipped folder mirrors the original Nextcloud layout and re-imports cleanly.
  • Comparison mode loading 400 error: loadComparisonModelFromPath was passing the { id, subdir } object returned by getFileIdByPath directly into URL templates, producing [object Object] and a 400 from the file API. Now destructures id before use. (Latent regression from the earlier getFileIdByPath return-shape change.)

Fixed

  • Animation playback: Previously all animation clips played simultaneously, causing blended/static poses. Now only one clip plays at a time
  • Animation timeline seek: Slider scrubber now correctly updates the model pose when dragged (replaced mixer.setTime() with action.time + mixer.update(0))
  • Animation loop toggle: Toggling Loop off and back on no longer leaves the animation stuck; finished LoopOnce actions properly restart
  • FBX rendering: Fixed dark/black eyeball — upgraded Lambert to Phong materials, added SRGBColorSpace, normalized scale, detected transparent shells
  • PLY loader: Preserved original vertex normals from file instead of always recomputing them
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturewL1fCXkzaQs2nvi2AkLccAX0u8v5YD4iOypG70neHtpgIb3wIQO+XaIgyQ+8n5k5ZO8pYI33VMdCWsomImFSTZmT7bH7s5DdFotdIektcqSN+kjfyeBeJkdoAcXUU2sJZgSdlFMrIxINc35c1nagz9g+B7qabynqu0yP2FnuWkeN9E37Rb4aXqvPUPPCEFkNjilYHkC1vrW6cUR/Z+L806b1IGJ0eZs6yH/3BB83V6TZ92btN80bbLdK0IbDY/uJ7sggkhuir0WKKoyC31fKsTaqcUi9I6G7Gr2Xzm3NqS6Bk2Y/pp0rLCSQJ+4xO3LA2ktbZIMZtqgA3gEfRYqMzJ10Eyny6TCwfAwOuvozvPoKbfm+5tlhjQyGgE/C7iDOEJ2LFqC9SEH9RrNPlzKxNJ/u1zXHdmEzmxGQpAKS1qoDEAPdKOsVFdkcdKugTNIi6PpP+29HGGl7lE8OuKIiq/JY8z2+9jnfAD087bE8QxnqJBi4eL8401WSzVDRRHlbh6nS/RFb/AqCDkJE+O7G5KskqRvrb3Re7QGNXcogubljsyRMVxQT2azuv/xg6VXJGKGIMv78/OBcQW+fMfr1s6NoDweK0LTQXIVGjQbx0SRJBPsGE5arytY9eIdaoRyHcxXje81vWhp5/Ar5MMLsQf6FjBKZILulUZWPvG2VfmE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.2.0
Release Details
UpdatedApril 5, 2026, 6:44 p.m.
Changelog

Added

  • Cross-Section tool: Interactive clipping plane to slice models along X/Y/Z axes with position slider, flip direction, and DoubleSide rendering for visible interiors
  • Animation Timeline Scrubber: Seek to any point in animated models with a slider, step forward/backward frame-by-frame
  • View Bookmarks: Save and restore camera positions with display toggles (grid, axes, wireframe, background), persisted in localStorage
  • Lighting Presets: Quick-switch between 5 lighting setups (Default, Studio, Outdoor, Dramatic, Flat) affecting ambient, directional, and point lights

  • Exploded View: For multi-mesh models, animate parts outward from centroid with adjustable explosion factor slider

  • Slicer: Export format selector: Choose STL, OBJ, or PLY before sending to slicer (non-passthrough formats)
  • Slicer: Upload progress bar: Real-time progress indicator when uploading to server (XMLHttpRequest with progress events, 2-min timeout)
  • Slicer: Copy share link: Copy the temporary Nextcloud share URL to clipboard for manual use
  • Slicer: Size validation: Warns and blocks uploads exceeding 50MB before attempting server transfer
  • Slicer: Upload size display: Shows file size in MB during upload for files >5MB
  • Modal viewer stats panel: Lightweight model info overlay (meshes, vertices, faces, dimensions) accessible via bottom-right button
  • Modal viewer screenshot: Download a PNG screenshot of the current view from the modal preview
  • Cache settings in Personal Settings: Configurable max cache size, max file size, expiration days, enable/disable toggle, and clear cache button
  • Cache hits/misses in performance overlay: Shows individual hit and miss counts alongside hit rate percentage
  • Cache privacy documentation: Documented local-only storage, per-browser isolation, and user control in TECHNICAL.md
  • Multi-file matching test suite: 48 tests covering texture/MTL name matching strategies (space normalization, prefix removal, plural handling, color/body mapping, partial matching)
  • Edge case fixtures: mixed-case extensions, missing MTL, orphaned textures for multi-file loading tests
  • Export triangle count warnings: Toast notifications for large models (>500K info, >2M warning) before export starts
  • Export MIME type fix: STL exports use model/stl, OBJ exports use model/obj instead of generic types
  • Help panel refresh: Added Slicer & Export section, cross-section, exploded view, lighting presets, bookmarks, dependency cache documentation
  • i18n audit: Wrapped hardcoded export/error toast strings in t(), added 31 new keys to l10n/en.json, documented i18n checklist in TECHNICAL.md
  • Accessibility review: Added role="dialog" + aria-labelledby to SlicerModal, aria-controls on all 6 panel section headers, aria-label on emoji-only buttons, role="alert" on texture warning, role="region" on stats panel
  • Format parity guard: Build-time script (npm run format:check) validates that PHP, JS, loader registry, Viewer MIME list, and mimetypemapping.json stay synchronized
  • X3D/VRML MIME registration: Added model/x3d+xml and model/vrml to Nextcloud Viewer MIME list so these formats open in the viewer
  • Slicer security documentation: Documented full security posture — authentication, path traversal prevention, MIME validation, size limits, share expiry, and file lifecycle in TECHNICAL.md

Fixed

  • Slicer OBJ/PLY upload: Added obj and ply to backend upload allowlist — OBJ/PLY format selector was added to frontend but backend rejected these formats
  • EufyStudio URL parsing: Replaced fragile regex filename extraction with proper URL parsing
  • Slicer upload timeout: Added 2-minute timeout to XMLHttpRequest (previously no timeout — could hang forever)

Changed

  • Tools panel redesign: Reorganized from 4 sections to 6 (View, Scene, Analyze, Animation, Export, Settings) for clearer grouping
  • Toggle switches: Replaced text checkmarks with custom CSS toggle switches for Grid, Axes, Wireframe, and Loop controls
  • Export section: Screenshot, Export Model, and Send to Slicer moved from Settings to dedicated Export section
  • Animation section: Elevated from nested group to its own collapsible section (conditional, only shown for animated models)
  • Model Statistics: Moved from Settings to Analyze section alongside Measurement, Annotation, and Cross-Section

Fixed

  • Theme consistency: Unified --color-primary-element fallback values across all components to #0082c9 (Nextcloud default), replacing inconsistent #4287f5, #1976d2 fallbacks
  • MinimalTopBar hardcoded colors: Replaced 7 instances of hardcoded rgb(0 130 201) with var(--color-primary-element) and related NC variables
  • Dark theme hack removed: Deleted ~130 lines of .dark-theme CSS overrides (46 !important declarations) from SlideOutToolPanel — dark mode now works automatically via Nextcloud CSS variables
  • Cache stat colors: Replaced hardcoded Material Design hex colors with NC semantic variables (--color-success-text, --color-warning-text, --color-error-text)
  • Missing panel props: Added wireframe, background-color, performance-mode, theme-mode, has-animations, is-animation-playing props to SlideOutToolPanel binding in App.vue
  • Clipping plane Z-fighting: Added 5% margin beyond model bounds so the slider at extremes doesn't cause Z-fighting artifacts
  • Bundle budget: Updated app chunk thresholds for new features (+12KB raw)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature2a2j5kFHSYZk5lKX7ByFRqNuCv8hg06X3uc2Mr98biRZSxeIgBug6aCdHONpN8oppv+6SDc5XrviS8HcLVFw1UEZvHjePdJYR8gri52Aa7ao7IfghYUHnoIwinqvWfP0sRod5vn3ZaKuo1pMpAHQlt/fkQCVz9eH6r+6TQTTBzBeAc7yWyEumArposNVcDqhgy0kK8tNWpNGYhpdqcMEvS7zGi0bl5EeK8k3ufaNaqwlzoXNwNMBiJ1aWFV2phxOxekkxlsHfRmGLkkdbPCOHg1Kj2CTLldHOjSQmQcUH9KOL1xkOZzuAC3Mh8SnrG/47h48uKIe62T7WYHgPbeOrDHvP8Z9d8S9V7kRq3puAonSPX/TFZt7j4YZoyar86w1MVakhT1mwiK3hBax+TCzxPRo63941VwID08MonJxW4D9C59MuF5qb/7UcDZ4Fm/TrSa0yvvKi+gy+gRCmBdzCnTg+WsEZW6Lotin80zckyH/+4wkUhUQBcq97ElIQKDwG5g9Bv6SGtQ2sR3zoUi7T/Wle4iqh38p5to6VMOlKfiBDiloGwpzXtHtlXDIZw8QqPL2h6gTQNdPizlovGLaZLup3sLL6MX7uyD5ZhbWz4kg1Qj4CqORSmpAanqJbBdUhLfA8dU2YhX5q7PFm8pMDAzKOq3h45kiSFI9K39f73c=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 3.0.0
Release Details
UpdatedApril 2, 2026, 7:40 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.6 → ^7.29.0
  • @babel/plugin-transform-runtime: ^7.28.5 → ^7.29.0
  • @babel/preset-env: ^7.28.6 → ^7.29.0
  • @playwright/test: ^1.56.1 → ^1.58.2
  • jest-environment-jsdom: ^29.7.0 → ^30.3.0
  • vue: ^2.7.16 → ^3.5.0
  • @nextcloud/vue: ^8.33.0 → ^9.5.0
  • @nextcloud/vite-config: ^1.7.1 → ^2.5.0
  • Vue 3 migration: Migrated app from Vue 2 to Vue 3
  • main.js: new Vue() + Vue.mixin() → createApp() + globalProperties
  • settings-personal.js: Vue.extend() → createApp()
  • viewer-api.js: new Vue() / $mount() / $destroy() → createApp() / app.mount() / app.unmount()
  • ViewerWrapper.js: New Vue 2 bridge component — Nextcloud Viewer bundles Vue 2 internally, so a plain JS wrapper renders in Vue 2 and creates an isolated Vue 3 createApp() inside for the real ViewerComponent
  • Removed @vue/vue2-jest (Vue 2 specific)
  • Nextcloud 34 compatibility: min-version 31, max-version 34 (@nextcloud/vue v9.x requires NC 31+)
  • Vue component imports: Migrated deep imports (@nextcloud/vue/dist/Components/...) to barrel imports (@nextcloud/vue) for forward compatibility with @nextcloud/vue v9
  • Template modifiers: Removed deprecated .native event modifiers from Vue components (compatible with Vue 2.7+, required for Vue 3)
  • @nextcloud/vue v9 API migration: Updated all form component bindings to Vue 3 API
  • NcCheckboxRadioSwitch: :checked → :model-value, @update:checked → @update:model-value
  • NcTextField: :value → :model-value, @update:value → @update:model-value
  • NcSelect: :value → :model-value, @input → @update:model-value
  • NcSettingsSelectGroup: :value → :model-value, @update:value → @update:model-value
  • Bundle budget: Updated index chunk thresholds in bundle size checker

Fixed

  • npm audit: Resolved dependency vulnerabilities via npm audit fix (#77)
  • npm audit: Applied non-breaking security patches, reducing vulnerabilities from 43 to 25 (42% reduction)
  • Lint: Fixed one-var error in useThumbnailCapture.js
  • Animation loop toggle broken: AnimationMixer.LoopRepeat/LoopOnce are module-level constants, not static properties — setLoop(undefined) made loop toggling non-functional. Imported LoopRepeat/LoopOnce directly from 'three' (useAnimation.js, useComparison.js)
  • Model load errors invisible to user: Variable shadowing in handleLoadError — parameter error shadowed the error ref, so error.value = error was a no-op. Renamed parameter to loadError, fixed logger level from info to error (useModelLoading.js)
  • Lights leak on re-setup: Vue 3 proxy wraps items in ref([]) arrays — scene.remove(proxy) doesn't match raw Three.js objects via indexOf. Added toRaw() for light/helper removal and instanceof checks (useScene.js)
  • Toast auto-dismiss broken: ToastContainer was mutating the toasts prop directly (setting progress/paused on prop objects), which triggers Vue 3 warnings and breaks in strict mode. Moved progress and paused state to local data() (ToastContainer.vue)
  • Mobile touch listener leak: setupPinchZoom() and setupDoubleTapReset() added document event listeners but never stored references for cleanup. Stored refs in eventListeners and added removal in dispose() (useMobile.js)
  • Settings page form controls not responding: @nextcloud/vue 9.x changed all form component props from checked/value to modelValue. Updated all bindings in PersonalSettings.vue
  • CSS nesting bug: .select-group-row rule was nested inside .setting-row braces — silently dropped in browsers without CSS Nesting support. Moved to separate rule block (PersonalSettings.vue)
  • Viewer registration errors silent: Both registerViewerHandler and registerViewerHandlerLegacy had empty catch blocks — any registration failure was invisible. Added logger.error() calls (viewer-api.js)
  • Loader errors invisible: All BaseLoader logging methods (logInfo, logWarning, logError) had empty bodies. Delegated to project logger (BaseLoader.js)

Technical

  • PHP CS Fixer: Blank lines before returns, doc comment whitespace, type-cast spacing, removed unused imports
  • OpenAPI spec: Regenerated with slicer and thumbnail controller tags and updated description
  • Three.js + Vue 3 pattern: shallowRef for single Three.js objects, ref for arrays, toRaw() when passing proxied objects to Three.js APIs
  • Vue 3 Maps pattern: Maps moved to created() hook as non-reactive instance properties (this._timers) to avoid Vue 3 proxy breaking Map.has()/Map.get()
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureCqMDLnrHd2ejY1wtQ+IUFnXE3ObdTMqRro2/QTWhZPmyh7h5lD2zlldE7hV6K2PLVbSbQA8RyylLA7chfBAfNhqrGVcv9N8oS4N8JZlvp2sx7uCnQJx9hA34xXsRvCNednaE0NuhE4zelLAkmDaJZEcQm0r0mQKkZAMSaIPgRg52ZFiEV+KFa7dyHlyLqc/Cp6doYyEeJxnyDiLOiCCsS0zyrt1qQlRyEgGcOocdeIPJRutmBwX3Sy/018v5midNblK+gleIzcCAFIOM7qDJKuf5ESnvQmqQko/7J+ahhAGzIdpGRHmKCVjAayZ2g3uLkT9dNQrBwKAaYvNWODkSyVEoBBU1wrucKcw237GMSjxHrVf/h5G64+Nb9/krmriBkdcpVupQ94wgyU9H5NO+BsBP/+3EfuuECRwOxXMW15taFrsiflxos1Fuhu5PQtyfOv4UPtUzn+uf4MaSzeLYdBHsOHfO6ZTjTPRbO5NLz5nlMw3F4PEgSKW7bk9brH7dEjzQzVG1RrDYCg75bGW+8l7/vtAmWc87s6otqoCj9rPVHQhrY7njOUcU8gwjdw0WuFTD//i2Vk0+agy7GO9WWfUdrbQ/i8Grq5AwoEgD3b0NLpPB9XxDCOR3yZQhSDbHNvgI8BRDp3Xe1FDv8izK2Dw9HwjykDDU+uMNZNtNpdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=31.0.0,<35.0.0
Minimum Integer bits32
3D Viewer 2.3.4
Release Details
UpdatedJan. 18, 2026, 6:57 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.5 → ^7.28.6
  • @babel/preset-env: ^7.28.5 → ^7.28.6
  • postcss-html: ^1.7.0 → ^1.8.1
  • vite: ^7.2.7 → ^7.3.1

Fixed

  • Nextcloud Subfolder Compatibility (#74)
  • Fixed app not working when Nextcloud is installed in a subfolder (e.g., https://example.com/nextcloud/)
  • Replaced all hardcoded /apps/threedviewer/... paths with Nextcloud's generateUrl() and imagePath() functions
  • API endpoints, decoder paths, and static assets now correctly respect the configured webroot
  • Affected files: multiFileHelpers.js, useModelLoading.js, useComparison.js, gltf.js, ViewerComponent.vue, SlicerModal.vue, App.vue, viewer-config.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturem8HTfU5qBOyGRmdHu4hylXbuVzwpcofBxDmpgWjNf0qvBkujJC75qp0z1GcJKDRppDXuE5z70L6g5AAUda+v3Z5y9byJqnisxNVRZM0nbXwYgOmqi8MdBnP7bzd5Ms43jDwr8hKt5ffEKR+g6h7Um8q5BDl3Vdk2J8nVuk+uvduDXalukp+USx3ibBHjg3MDSJqdkTKkHPe41SXEpjyor9VMKLer8r7HxHcwlELGseyuFHgL/PPYQOT5GQf6KO7ae0s2lYH6tbBu3ZnP/f0tg4cYZAHAycm4fvAwRj44XMh6a3OFP0FdQOkpSmYa7Tp5IH5rj4zBjrazGCxtUU40q3bBmYRvIKiv/je83LfQA5n8FCgmjREez0JslPFBByM//FtrcFgsm1mrzSgkufLTdvQ8JYb1XbV6xI7jKWSYKdCidwpvHCWAUqvQDWRVL2cZ7tLyGwSVHtLEsftO3Ykhu7WYpyQmNIG2s6zTuBC9x52IL7hxU9+7127R5L7S2AdPxvUB5Xrz5PJbfKal41mSi5GTJ1s1d+ow6lyB/rU1yGMX12Y0gDTZjXLHvCMq6Ixtcy1igH1nd/C8P+j1vDHbDPyZnIKtx9yBTMsejnv9tdsPpE4ya+eM54l1i+gZEdM8NhFxows/bBHJaljU6DBRvtdYBriWsMHsrsDB2wSuTMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.3
Release Details
UpdatedJan. 16, 2026, 2:42 a.m.
Changelog

Added

  • Automatic Thumbnail Generation: 3D model thumbnails are now automatically generated when viewing files
  • Thumbnails are captured with smart cropping to focus on model content
  • Grid and axes are hidden during capture for clean previews
  • Thumbnails stored in Nextcloud app data (not in user files) to avoid cluttering recent files
  • User setting to enable/disable thumbnail generation in Personal Settings → 3D Viewer → Thumbnails
  • "Clear thumbnails" button in settings to delete all stored thumbnails
  • Works in both standalone viewer and Nextcloud Files integration modal
  • Thumbnail Settings: New Thumbnails section in Personal Settings
  • Toggle to enable/disable automatic thumbnail generation
  • Clear thumbnails button with confirmation dialog and count of deleted files

Changed

  • Thumbnails are now stored in Nextcloud's internal app data folder instead of user folder
  • Prevents thumbnails from appearing in "Recommended files" and recent files lists
  • Improved privacy and cleaner user file space

Technical

  • New ThumbnailService using IAppData for app-internal storage
  • New ThumbnailController with endpoints for storing and clearing thumbnails
  • useThumbnailCapture composable with smart content-aware cropping
  • Thumbnail capture integrated into both ThreeViewer.vue and ViewerComponent.vue
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureuFzoE7HPsOrBKlyKjSzOuEeHjB3dAL7I/y0IF+GNXsgneUJobVLzw+kWlOtZyGa4wNcavAH3caVRlqPjMdw/ZlrlgkJN62qghl/yvbskmmSsiC43ntcKMfGd8Gat1wCXPzTGipRc4DqwbpUyN3PIVltZgpSvX+xaTB3+bLwPNIg2vX2GMpalC9vN2eGa60fPm//OXZKujqfRVwGqg36OpfuRKgOr+J2xDyxAsyc6UywFRGHUnpGLiZgp0jXIRshqpM+k3fpbZDB1JrDbYlPaAZwa4Ze//xxv4Xbk4KmmlLY0S6GXChNbX0rAYrwv+PxJxFeLSI5ytw9iZPdDFQFmgm5RJv7XVhmc31ccZyJ8eaTRz2+JfqBRV5csX5Ox9QIVjmkL3WX70eNIcRUBGX4BFOcCqZxnRgg3moqoSKAZFQ3sOMxwStiKlHhrffZvclDLZRL3S76C492BVlF9HFZc5KYnuF17VQPs1OL8xWFcW57HfcjpMs6xydfnMtfTJUTWbS9p6fjaxLjUgl93ZqWTMmc8dpXl7ywiLjVViTNk8PNn3NzHP3H5yszzBxcO6elz7cIQgXRKdVKB8OQoaeMEjxQQfmWAIViZyXoEEhS76bsI1oeuqAiNH/9p+ytUkBeN7FK6yljt/8QauU38PLTih0QE2xCLB6fP1EMAtLAb7PY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.2
Release Details
UpdatedJan. 7, 2026, 4:17 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • stylelint-scss: ^6.13.0 → ^6.14.0 (dev)
    • Enhanced dollar-variable-no-missing-interpolation to flag namespaced variables in custom properties
    • Extended function-disallowed-list to detect disallowed functions within @return expressions
    • Fixed false positives in dollar-variable-no-missing-interpolation when variables already exist inside interpolation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYV9kZk3oeyVbeTn1qLIVLgb5ERRXgsTQamJCdPTLt4vgt7xf3ShcXd6Srry0UKHzEGvXqQjVq3sfxtq5INZNKQugyC67dlmP+WfFbq7nsLoGJp4Cpjza5ud+4ozbxgbVqNFhNoFVz/zYGyc1lAcSUP8zC10x6zfq/0WoRUFgWHOKCuK6ZB4vQd5J7JRX7JKX1sxbl+rGHSqbQeWuyJwlCir+zr5Y+746EJ+2S29f8z3KFxf2PfEZwAmE1Nf05FAVL5RhQhlDjqM7J2NvQEP6Pod8b6Q+RxFKMWkYyTHncCfQTNz+3VYeuJsqfYC6ZVADYOT5duvHbqOIW4sdnrotXWQDq0uxww7qKYISKp4srSKBwiN1k6EIGt2Clr5FOlji80tIlOtzqanLWQlhpV3wuiVSA8TTbfxPVlB4Mn7dGu/LHc5TjfQ5JEct+5nfEwSCdnLbnEhLyiNRFPppmpIjfHhSWhVrHpz/YRdSarEsmNeP31dSoJKbhNtqhi1oIbtyXG5ElYrTFFg+nxz/yc3Ok33KY+D1nJ9gIN+Qdde9E86qg0cXeIgq+CtM0TGomTKPt9BXQfresW9zuzKli1p7xS3d0C21acP8r6humszQcZQNliz936kTkULULFo86VkyYmK79Gmm16tcMDXbXiZcvhwqF/xrXS6QuQb44K9hC2s=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.1
Release Details
UpdatedJan. 7, 2026, 3:03 a.m.
Changelog

Fixed

  • "Send to Slicer" Button Always Disabled
  • Fixed missing modelLoaded prop binding in SlideOutToolPanel component
  • Button now correctly enables when a model is loaded
  • Affects STL and all other 3D model formats

  • CRITICAL: Cron Job Fatal Error (#65)

  • Fixed foreach() argument must be of type array|object, null given error in CleanupTempFiles cron job
  • Removed incorrect foreach wrapper around callForAllUsers() method

  • CRITICAL: Missing Exception Method

  • Added missing getExtension() method to UnsupportedFileTypeException class
  • Fixed fatal error when attempting to access extension information from unsupported file type exceptions
  • Updated all exception throw sites to include extension parameter

  • CRITICAL: Insecure Rate Limiting

  • Replaced insecure session-based rate limiting with distributed cache implementation
  • Uses ICacheFactory for thread-safe, multi-server compatible rate limiting
  • Prevents rate limit bypass by session clearing
  • Improved scalability for large installations

  • HIGH: IP Address Spoofing Vulnerability

  • Replaced custom IP detection with Nextcloud's secure $request->getRemoteAddress() method
  • Prevents IP spoofing via forged X-Forwarded-For headers
  • Properly respects trusted proxy configuration

  • HIGH: Path Traversal Vulnerability

  • Fixed path traversal vulnerability in temp folder verification (SlicerController)
  • Replaced insecure strpos() checks with proper str_starts_with() path validation
  • Prevents unauthorized file access via path traversal attacks

  • MEDIUM: HTTP Header Injection

  • Fixed potential header injection in Content-Disposition header (PublicFileController)
  • Replaced addslashes() with proper RFC 2231 encoding using rawurlencode()
  • Prevents header injection via malicious filenames

Improved

  • Temp File Cleanup Enhancements
  • Added comprehensive logging for NotFoundException cases
  • Implemented cleanup statistics tracking (files deleted, shares deleted, users processed, errors)
  • Improved share deletion error handling with separate method and individual error logging
  • Added file type validation to skip directories during cleanup
  • Added progress logging every 100 users for large instances
  • Added stack traces to error logs for better debugging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMK0+lREma3H+5o5HY/1XiCvaVsxjUPM6tcB151nPTrwJOeBdbFF50jwp8N7+xgyKEF8GZEOYpwqBu2jYjlN8bJBJFDUI4Givl++H6HAT0/yOhq22oItRC9eOdmrdLiduHqSm10UDj37gda6vOjZwGiPKG6OcmqTkAB5z6tCSX9xH4RznZwbWIpCKBKfUDv53iCyBiogCX3Zyz7443IEdkBy51an9N64COoiWJACcw1kse1f2JtS5ptYTXBA1ImMNOVkX+I2n+b4qp9n972UGMXyx8JnhH7nuVp4XH7nY2oargPPk4b+p1Yqq5/bJGsdazm+i8PaYcJ6UHJTySjTOIvh6MuWpA+xuXTWSgM9k1DUTFG2YQ1RS/JJsFQ4Xhm9GffxDOQ8mSMRp3MnEdMd8FgIU+REqQP3MAYvxQ/c11RWoaolM+VbWrKNGJqB2U2pliu4oM7DRlZg4GmgHvYUMUQQYTq+L1lZlvyQp+NweTuvaUcx6cY5z3SdiRb75o3fYhgNGPDg6ZgsV45Oy6srZofHfGiuYy+c/AkL9Nr8c0nBlZWtxV/OVdPsbHHmNhSVAmv2baFug1mGUcGYRApCmWLlpJbskW3ZNeFfsHp/d/st0LPjhK6ItDHe+6khDFAYoqQWFA2z2nmrWZuD/FzB2Ny3TVCZBQkpzDc7hJiBBxGY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.0
Release Details
UpdatedDec. 28, 2025, 1:15 p.m.
Changelog

Added

  • G-code Visualization Enhancements
  • Smooth rainbow spectrum gradient coloring across entire model
  • Intelligent filtering of travel moves, retractions, and parking movements
  • Skip movements exceeding 50mm in XY plane to remove edge artifacts
  • Fixed vertex color handling for proper gradient display when toggling modes

  • Mobile View Optimizations

  • Responsive MinimalTopBar with all icons visible on small screens
  • Help panel displays full-screen with sticky header on mobile
  • Smooth scrolling support for iOS devices
  • Performance stats overlay hidden by default on mobile and screens ≤768px

  • G-code Toolpath Visualization: Added support for G-code files used in 3D printing and CNC machining

  • New G-code parser supporting G0/G1 movement commands
  • Automatic layer detection based on Z-height changes
  • Color-coded visualization with different colors per layer using HSL gradient
  • Support for multiple file extensions: .gcode, .gco, .nc, .acode (AnkerMake)
  • MIME type registration: text/x-gcode
  • Custom file type icon for G-code files
  • Toolpath rendered as 3D line segments with configurable transparency

  • Extended G-code Ecosystem Support

  • Additional extensions recognized and routed to the G-code loader: .g, .gx (FlashForge), .g3drem (Dremel), .makerbot, .thing
  • MIME mappings added for container variants: application/x-gcode for .gx, .g3drem, .makerbot, .thing
  • Viewer “By Type” browser now lists these extensions via SUPPORTED_FORMATS config

Changed

  • Backend Listing Filters: Updated FileController filters to include G‑code-related extensions in Folders, Type, and Date views so these files surface across all navigation modes
  • Applies to folder-scoped listing, type grouping, date grouping, nested folder inclusion checks, and descendant checks
  • Slicer Handoff Options: Simplified send-to-slicer flow by letting users pick passthrough extensions; all other formats now auto-convert to STL by default (export-format selector removed)
  • G-code Visualization Default: Default toolpath color mode is now gradient instead of single-color orange for clearer multi-layer contrast

Dependencies

  • Updated @nextcloud/dialogs from 7.1.0 to 7.2.0
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturePzIAblqli6tJofqSxWWKOwCp0h/EaSHMlT/+yyoqArTsvBAPzn8xLqU4/58/5pja+YAA7WeQ9egTPUicOilrSFBSHQVGa2QSLQVWFu+vTXKGEuu90qNxXnNqDnl66TVKuWR0OiXHeR2nCAdu3jh7310h8afbBLR3eQAXIeuWlLtXnbzVLoaiqy4pK9l2FzN8GpQoU4/7fo7SH1CFE6iR+Yea4Y2P6C1UXGQfG/RnJylCj1UT+2UkkLUAopk9AOxMEgQpwQzUV5kl4FwvND4biwcuXEajDXwVizX9qAEDtOkzILdV46RcWkyHxnDcqIGHuRImgfqWlwwz6fZ14Huf7/5bmvNafPuSPPRJHEWvH66whuXS5sft+Crvd1/AR+sF2bDGtGtoQo2iSRSCmVS8XtsK0+dW+DLGIvd7pQsul/AIWPDYYkPtMCgAXCFLJpFmmOhNNoCOshjRSgJ5LrnfBC3kbF66gBZGhlkBc0n9MDkqlgxDNVNGwYHYG6xllH4u+1xdGIilc7TE2EXSmpD3GEJOCTLiRdMDbL2ZSC04fd9Lf5YbVgKMggroGFNbtl9VszrOD8GtgpyUqdQnZ9D2U9ef8sJqRxzWKNT3Sr3A2PxgKiCf8G0yu0016JlmwHeEJQAWs9u+0ZpnYo51eSGQKnKtonW1avH6I95Mqh6cPbA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.2.0
Release Details
UpdatedDec. 17, 2025, 6:10 p.m.
Changelog

Added

  • Measurement & Annotation Visual Sizing Controls: Added per-user sliders to control measurement and annotation visuals
  • New Personal Settings section for adjusting measurement point size, line thickness, and label width
  • New Personal Settings sliders for annotation point size and label width
  • Settings are stored per user via SettingsController and merged into VIEWER_CONFIG.visualSizing
  • Measurement and annotation composables (useMeasurement.js, useAnnotation.js) now derive sizes from configurable percentages of the model size
  • Animation Controls in UI: Added animation play/pause controls to viewer interface
  • Animation play/pause button in MinimalTopBar component
  • Animation controls section in SlideOutToolPanel with play/pause and loop toggle
  • Animation state props passed through App.vue to child components
  • Integration of useAnimation composable in ThreeViewer component
  • Improved directory path extraction for multi-file model loading
  • Dependency Cache Insights: Added live cache statistics (size, entries, hit rate) to the viewer and slide-out tools
  • Viewer performance panel now shows cache size and hit rate
  • Slide-out tools panel displays cache stats and clear-cache control with status
  • Cache stats refresh automatically during use and after model loads or cache clears
  • Cache hit/miss tracking with reset when clearing the cache
  • Performance Scaling for Large Models: Automatic performance mode suggestions and easy mode switching
  • Configurable triangle count thresholds (warn: 500K, strong: 1M faces) for detecting heavy models
  • Automatic toast notification suggesting performance mode for models exceeding thresholds
  • Clickable performance mode label in stats overlay to cycle through modes (Auto → Low → Balanced → High → Ultra)
  • ViewerToolbar performance button now cycles through modes instead of toggling stats
  • Performance mode changes apply immediately with visual feedback
  • Slicer Temp File Security Hardening: Enhanced security for temporary file uploads
  • File size validation: 50MB per file limit, 200MB rolling folder cap
  • MIME type validation for STL files with header checking
  • Rolling 24-hour expiration enforced on file access
  • Increased cleanup frequency from 24h to 6h for faster expiry enforcement
  • Comprehensive audit logging for creation, access, and deletion events
  • Security posture documented in TECHNICAL.md
  • Automated Bundle Budget Enforcement: Enhanced bundle size checking with historical tracking
  • Comprehensive budget thresholds for all major bundles (main, loaders, app, three-core, index, nc-select)
  • Historical size trend tracking in bundle-sizes.json (last 50 builds)
  • CI workflow uploads bundle size history as artifact (90-day retention)
  • Size trend comparison shows changes vs previous build
  • Improved error reporting with formatted bytes and clear failure messages
  • Fails CI builds when budgets are exceeded (with environment variable overrides)
  • Vue 3 Migration Pre-Work: Eliminated Vue 3 incompatible patterns to prepare for future migration
  • Replaced deprecated lifecycle hooks (beforeDestroy → beforeUnmount in ViewerToolbar, ToastContainer, ViewerComponent, viewer-api.js)
  • Added explicit emits declarations to all components (ViewerToolbar, ToastContainer, ViewerComponent, ViewerModal, FileNavigation, FileBrowser)
  • Verified no implicit $listeners usage (removed in Vue 3)
  • Added comprehensive migration notes to COMPOSABLES_API.md with dependency compatibility matrix
  • Audited dependencies: vue 2.7 → 3.x (API compatible), @nextcloud/vue 8.x → 9.x (requires Nextcloud 30+)

Changed

  • Cache Statistics Updates: Improved cache stats refresh frequency from 5 seconds to 2 seconds for more responsive UI
  • Performance Mode Controls: Enhanced performance mode switching with clickable controls
  • Performance mode label in stats overlay is now clickable to cycle modes
  • ViewerToolbar performance button cycles modes (preserves MinimalTopBar toggle behavior)
  • All mode changes apply immediately with proper event propagation
  • Dependencies: Updated core runtime and build tooling
  • three: ^0.181.2 → ^0.182.0 (patch update)
  • vite: ^7.2.6 → ^7.2.7 (dev, patch update)
  • Security Workflow: Updated GitHub Actions CodeQL workflow to use github/codeql-action v4 in preparation for the v3 deprecation in December 2026

Fixed

  • Toast Event Handling: Fixed missing push-toast event handler in App.vue preventing performance suggestion toasts from displaying
  • ViewerModal Performance Mode: Fixed performance mode cycling in ViewerModal component by adding proper prop passing and event handling
  • Model Comparison Positioning: Fixed comparison model positioning issues where the second model's position was immutable
  • Wrapped comparison model in a Group to neutralize baked position offset from loading
  • Fixed parent-child relationship handling by using toRaw() to get actual Three.js objects instead of Vue proxies
  • Ensured matrixAutoUpdate is enabled for wrapper and all children for proper render loop updates
  • Fixed matrix validation order: validate parent matrices before child objects to prevent "Cannot read properties of undefined" errors
  • Improved scene hierarchy validation to include all objects (grid, axes, lights) not just models
  • Fixed matrix update sequence: call updateMatrix() on all objects before updateMatrixWorld() to ensure proper transformations
  • Comparison models now position correctly side-by-side with proper spacing and alignment
  • CSP Compliance for Texture Loading: Fixed Content Security Policy violations when loading GLB/GLTF models with embedded textures in Nextcloud modal viewer
  • Patched Image.prototype.src setter to automatically convert blob URLs to data URIs for texture loading
  • Patched URL.createObjectURL to track blob-to-URL mappings for later conversion
  • Patched fetch() and XMLHttpRequest to intercept blob URLs and convert them to data URIs
  • Patched THREE.FileLoader to handle blob URL conversion for texture resources
  • Automatic detection of modal viewer context (iframe or Nextcloud viewer) to apply CSP workarounds only when needed
  • All patches are automatically restored after model loading completes
  • CSP Compliance for Buffer Loading: Fixed CSP violations when loading GLTF files with external .bin buffer files
  • Updated setupResourceManager to use data URIs for buffers in modal context (instead of blob URLs)
  • Patched fetch() to intercept data URI requests and decode them manually, bypassing CSP restrictions
  • Supports both base64 and URL-encoded data URIs for maximum compatibility
  • Animation Support for Multi-File GLTF: Fixed missing animation initialization for GLTF files loaded with external dependencies
  • Added animation detection and initialization to loadModelWithFiles function
  • Animations now properly initialize when loading GLTF files with external .bin files
  • Animation controls now appear correctly for animated multi-file GLTF models
  • False Texture Warning: Removed automatic texture warning banner that was incorrectly showing for all GLB/GLTF files
  • Warning now only appears when actual CSP errors or texture loading failures are detected
  • Improved user experience by eliminating false warnings when textures load successfully
  • Slicer Upload Safety: Hardened temporary STL upload handling with size and MIME validation
  • Reject uploads over 50 MB per file and enforce a 200 MB rolling temp folder cap
  • Validate STL MIME/header before accepting; reject invalid content
  • Enforce rolling 24h expiration on access and clean up expired shares/files
  • Use rolling +1 day expiration for generated share links
  • OBJ Texture Loading Robustness: Improved OBJ/MTL parsing and texture handling
  • Preserve texture/MTL paths with spaces and mark materials for update after textures load
  • Use existing blob File objects for texture URLs and tighten loader logging
  • Handle texture load failures gracefully and ensure needsUpdate flags are set
  • Texture Dependency Lookup: Skip direct “find by path” lookups for image textures to avoid unnecessary 404s; rely on directory listings for textures commonly stored in subfolders
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signatureacd0z9v0ifd6EhLDkZPQmIADZp+TPcgSOqKKp6W5KUtq4CA/Xk0lTNssZpirrEaLO4s15qemVCz87Aa6hhxGipOFlBSMFIyc1U0q8OQzHMOa4cYU8w1hvmi7aXeG+IxPWXelCAYaBNYGbleOvhc+/ao1gh1mn+3esakeTLdBi8XihNAKBDzglkOD30oBunXuzXJD1chBFBx7LCMoRhVXHDxShzi39+vbumkbme4IGV9kL323hTSjD1+XrMeiIG2P9tvHDEkGRXEOKohmWQNrsFCamApVqslvRzg6mBFTFv+dtnBUFbbQnC25IvyE+UNY4j2TGzncrW2+jWGddicKYq4SIrI7RzB91gTxv+3qzwCUb8OlyaU38L042l4HJXSFMPh8Mmj2n/0zhWlH4M2h/ZEPjySsaK8lU/x/DaBoyLcPoJcDsuD1haONv6mpRsoz/ohXdV/U9s61rbGOnJkSqMMblFoVXVmkzTKpBatf08e9Uaig21b9q/ekYyhbVFlujmAu9ku9EWk5SddY26zNvSeiGinV1xV3BayMI3rVZYINZkPb72Dv6DOInRWlVOxVqLQBUY98ejlhpls+6l3IdtTDUmrITmGr+Nq3b8T8kgNJpgNQbf3Ij14MgdfNsxd4nxMyX/4hsOSZdU4dAHsTfatSxRgM1q9FpuvZ+OkyWMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.1.0
Release Details
UpdatedDec. 6, 2025, 10:21 a.m.
Changelog

Added

  • File Browser Default View Setting: Added user preference for default file browser view mode (Grid or List)
  • New setting in Personal Settings → File Browser → Default View
  • FileBrowser component now loads and respects the default view from user settings
  • Setting takes precedence over localStorage, ensuring consistent default behavior
  • Manual view changes are still saved to localStorage for session persistence
  • Format Sync Test Suite: Created comprehensive unit tests (tests/unit/Service/FormatSyncTest.php) to ensure format definitions stay synchronized across:
  • Backend PHP constants (lib/Constants/SupportedFormats.php)
  • Frontend configuration (src/config/viewer-config.js)
  • Nextcloud MIME registration (appinfo/mimetypemapping.json)
  • File Browser List View: Added ability to toggle between grid and list views in file browser

Changed

  • Format Definitions Centralized: Consolidated all 3D model format definitions into lib/Constants/SupportedFormats.php as single source of truth
  • EXT_MIME_MAP for extension to MIME type mappings
  • CONTENT_TYPE_MAP for file streaming content types
  • All repair steps and services now reference centralized constants
  • Eliminates format definition divergence between components
  • File Browser Grid Padding: Updated file grid padding to consistent 20px on all sides for better visual spacing

Documentation

  • Corrected repository URLs and upstream fork instructions in CONTRIBUTING.md (replaced placeholders with maz1987in/3Dviewer-Nextcloud).
  • Updated TECHNICAL.md with new controllers (SettingsController, SlicerController), components (PersonalSettings.vue, SlicerModal.vue), and detailed Personal Settings + File Browser implementation sections.
  • Added comprehensive "Adding a New Format" guide in TECHNICAL.md with step-by-step instructions and code examples
  • Expanded IMPLEMENTATION.md: added Slicer Integration & Personal Settings System sections; reorganized and deduplicated legacy "Code Audit and Cleanup" content; refreshed Table of Contents.
  • Updated README.md (docs version) advanced features list to include Slicer Integration and Personal Settings.
  • Added troubleshooting sections for Slicer Integration and Personal Settings in TROUBLESHOOTING.md.
  • Expanded test coverage notes in TESTING.md to include new controllers (Settings/Slicer) and components (PersonalSettings/SlicerModal).
  • Normalized wording and removed outdated dual-mode duplication in implementation documentation.
  • Documented dual-mode viewer architecture in TECHNICAL.md with viewer lifecycle diagram (standalone vs modal modes).

Fixed

  • Settings page image/logo path resolution: replaced hardcoded asset URL with imagePath() helper in PersonalSettings.vue to ensure correct loading under all deployment paths.
  • VRML preprocessing duplication: Removed duplicate preprocessing code in preprocessVrmlText() that was applying BOM removal, line ending normalization, and null byte removal twice, causing inconsistent preprocessing behavior.
  • Flexible texture matching loop control: Fixed nested loop control flow in texture matching logic (multiFileHelpers.js) by adding foundMatch flag to properly exit outer loop when match is found in inner loop, preventing valid texture matches from being skipped.
  • Premature texture issue check: Moved checkForTextureIssues() setTimeout call in ViewerComponent.vue to execute after model successfully loads and is added to scene, ensuring accurate texture loading status assessment.
  • Debug logging cleanup: Removed console.log statements from FileBrowser.vue component (viewMode watcher and setViewMode method) to improve production code quality.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureatLgIGKAv/DO42HgoDbqVaos4kWPFuPmF5l4PHyM5S5ivOlmqnC5xu1+d5uX/h+0rUmL+pG6wKezHo7WT0PVKSBciYZzlb5D+6sJisB5x/jMVqFaQRmnEEPKfoz2DsDucmXtH6Ppv3oXjyKd5wDjyFMwtoG1hU43QBeA4ngYTMTCXqdITK5sT4OM4IwiYhUi7zZRh6/YoRxgojXIqo/RasM3sGIUIrQs075RKqXcdGcGUwJgQPAQnGsMEW9D0LODp6bYurgBZRgnmgBrpTE3D7Ktt1G+ppQTSW4tHpuq3mFJdhFlkbhbWJw9QW28xJmDnqUGfhzXTSMqoJLnbio0n7iT5bhiyxBmhi5patzHifmDwndpRPoZNbw7vxr2zzqCc+1pMt/CoXHn1PTthmYtQMg9tvyjS7jbCdYtDYLtluzITRHP8GzRXIMmBrCRzXLymk0b3IvwyB9G83+QwzHyWSTLsKV314CSasumGhFDSxnz0E2DhVVZV7yaG89Dj9HHwmxkQVeh4iI9DCd5ynu6q/8Ljyp4nJ1p//+3WCo3NksaM0ubqkXiVFnSTnSjsoDw8mbP8TU/iESpYJ11llVA9ftZSGnnczY2zFKKK17C5m/pjE9sskLlo2bfhBzRLvYOV8ejRyaXPK1B6ejCfk2VwWntq7UKFh/+/1R5usmQkRg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.0.0
Release Details
UpdatedDec. 2, 2025, 2:53 a.m.
Changelog

Added

  • Personal Settings: Added personal settings page for user-specific preferences
  • SettingsController and PersonalSettings view for managing user preferences
  • Settings routes and configuration updates
  • Enhanced File Loaders: Significantly improved loader capabilities
  • Enhanced FBX loader with additional features and better support
  • Improved VRML loader with expanded capabilities
  • Updated DAE loader for better compatibility
  • Enhanced multi-file loading helpers for improved dependency resolution

Changed

  • Viewer Enhancements: Enhanced ThreeViewer component with improved controls and features
  • Camera Improvements: Updated camera composable with additional functionality
  • Circular Controller: Enhanced circular controller with better user experience
  • Theme and Performance: Updated theme and performance composables
  • Major Version Bump: Version 2.0.0 introduces significant improvements and new features

Technical

  • Updated GitHub workflows with improved condition syntax
  • Updated Dependabot timezone to Asia/Muscat
  • Added change detection to prevent unnecessary PRs in workflows
  • Updated stylelint to 16.26.1
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureJrtVI0UiQsSqL82PvCmO+FLp06iE6caUY8nD1+dKlVr5YjeTvZjLUtd0sAbmwQzlR2iC4IuRJllv7hr5cWk8oWuJtnLuE4uzcXu3bu8ndzO/Zyf4ph65Fn3GyML8Zdm6TqECBFCzk+2NG0pVxt4TM7qML5p437Q6IrF0+uOh3Vj55CTf160KAjEN7v2Sa8CT4Qf/JPKtnfyAgnKkNRXMQCf7xXe3RqtgtiVPnNq6nKfYzERoihIHhZ4LqLewUfY7jvxKUpAIxF89K+yTk1PmsJgKPBN88FF3ReKwcfJcfIhMXuH7CJEHCg++Hbd0oxzvl3uv0o8k5USdkojH0vG5IQaBF8AAEGY0aKCsScmffSN7WvmpPngsHs56V/olPNPVG1QNepRvSvBljr9D6idShiRlaW7uOBl9SVwLernLBjZ30bfqRtdrm2T7oO6gozY0zRc3jxt+ajAuaPLp8pAKC/Bv8nt3Og7R3r/mpk7qXWEgC/u78v/LqjhtICx1CQDSROq8vuces8k4iEN1kJabrhxLFPDPmZucoXWSzUStP+ISyPksq+pk5KAtnKuVRMTN8rhYkMlyK1790OOF53Q1bvlHnTc3LmzTLi6b/mVPBtz+KgkHHoU8j4TYwSlnbmSx6gDqzrQa8q+cRXlK1NNmF211NX0J8B9nRkng+xHg5ts=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.8
Release Details
UpdatedNov. 28, 2025, 6:24 a.m.
Changelog

Changed

  • Dependencies: Updated development and runtime dependencies
  • three: ^0.181.1 → ^0.181.2 (patch update)
  • stylelint: ^16.25.0 → ^16.26.0 (dev)
  • vite: ^7.2.2 → ^7.2.4 (dev)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMKCHrKGV1G4CZgG/gyhGvg34zK0migxfZ/FS9ZZEylzffGxQxoQbW+wubOR3BAjgWRx5smOBIdKVeLLuPOV00JnPgrII3lRaltLVO72nNrYtVh5DruKC5ZoMM4BuJsdEOFP8euy/GDCmlhQYNTusS/T+meggfylcrAjZAWR+SZZfb4x7QlBJTdmCuWpHNBJO081IpeLfn5HXohFAWm6sHHm6mun4ar6znAlwsZ/pe6Lo8ZjzLBuDN9mLEujrw6AgjTq2DTSUrHrJ9/WQF3zBiYjRLMNurJLnrwCXuUmPkKGWrPM3ybFADqfgb3QmqzPsPiGUL87k/1yVewEqE2olFpVRZ9hxa11ANingG+zMZwY1bCbzIcrRCq4Uoae7zgwZV/CF2wJQk+cRMbQl7Y7AoAy6PPECLMFtU93VgHqcPtFQzAxSPEXRNW+GpkVFHWvBZ7iuL6Ys8gMjl9i9imuNYcIAkuLlKLmu8t5pQpOLKvNpiQkeIf5aWXHm5Fvq7+zmeKIsuMVNvPoqQOSlC1AkwnLi1EAf/2VrqbAmDoC/o3Y6h1mg6vskUNFRPhz2h37TEb65h1bm9kFjNx/6TNNF0xM96GSAt7v+I1naBODhvGqDqyJ653663yuE+CxdKd1kZ4ous3urqXRwAmKHVdOvXYZTb+N/Feu7PHqrdcwlHHQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.7
Release Details
UpdatedNov. 27, 2025, 5:42 a.m.
Changelog

Added

  • AnycubicSlicer Support: Added integration for AnycubicSlicer with custom icon and URL scheme. (#52)
  • Folder Exclusion: Added support for .no3d marker file to exclude specific folders from 3D file scanning.
  • Hidden Folder Exclusion: Automatically exclude hidden folders (starting with .) from the file index.
  • Temp File Cleanup: Implemented background job to automatically clean up .3dviewer_temp files older than 24 hours.

Fixed

  • Layout Issue: Fixed white empty space when hiding the navigation sidebar by ensuring correct flexbox behavior and explicit slot usage.
  • Viewer Resizing: Fixed 3D canvas visual resizing issue by syncing internal resolution with CSS dimensions (width: 100%).
  • Slicer Icons: Fixed missing slicer icons by using the correct imagePath helper for asset URLs.
  • "By Folder" Navigation: improved folder indexing logic to correctly build hierarchy and handle edge cases.
  • Server Error: Resolved persistent preg_match error in PreviewManager by disabling unused app preview provider registration.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureLfmZ7uqqVk6tDgQQuwcGuzQml5OZWmFvpfNYW9Nd+3gVtIRUCBKsgtB8D6CMf1ymq2JxllnMvhATHE3YVZGmCsL5CRrj6al9+zdVnP8An2BUBP/DsqDTTwLM64mwKFIqYYkjtwVfttaa/aPJn2cdH7umzjOEHsYzbRx7eiP1lq2ERy4KYs7aA26hfleg9kKbr1jKTf0/vPCHvmbKg+o3RS+QfP/lRuoPjk1UYog52IAHd9bpAtAgmB2yLSfghjPSvMhZkyWt38CKMiKyO2R+VdB6wc/lWWVWq1JfJDw9oZlhaMpBZhXVDqR7uoMYdtR2pTNhB6d+kaTQtclvLSO5RREUDMwdocOl7iVMwrXCmKmUIyDqMa/VF3FFtK31fKlSYuvIhGDi1He0NNqlv2jiMsb2EPhrYLBHTyrmY0Px3i+ZmbYos1sFXLi7CliafZY/nZwxqPSc8xsZI42MFO9ZWA7yBKYE6UHphNnbxTbx5H33NrwHdRhOh0g+wpLJebbjyi1momlshADpfec+gAImcCnyaXhIX0Vpt73R1DmadH0xbi37iCEg73s9h6cul1RGeRhOzr+c8nhuFOUq4wodj9VLbTwwqm+bjd1jvshyo0++A0Nhjfj79phnnIJl0xsKU9QaN1eFlB6vPQRMyFswy6bWW4IW/yI2KL7693JFhRE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.6
Release Details
UpdatedNov. 20, 2025, 12:48 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
  • Folder Path Length: Removed the 512-character limit by hashing folder paths for indexing
  • Restored folder_path to TEXT and added a folder_path_hash column with a new migration
  • Existing rows are backfilled automatically so deep folder structures continue to work
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature4F3B+MMtZoJiTGIBdBSaJDW9PVnv2o38ob0m+f5cIJmMM6jpgqYETLsQaW1zRCdQ8ozE8v5LfkehmhMlXy8folvlapuRcDGh9N7PPIRzTM/8b9ymOMQ1vkBPERw8k5grkUwFNamMPw+K4rFGHLMflN+vR/S6m0jmAKls/2L4kg2UdhaTdtaw/ozZAM4a09dJ038kyBS6jbh6g/M233Ue/R6aXSCMVgrA5zw38djqE9O8zmUg8xOJlOzdMRCRdhKWZOHoocLV9TBTasdU99YG7o+VIgAXeTY4X0rKxtOdfqdX+c+YNEt4syMmig/1p3e3yO1q0CmQPg6rgxtrdVOW1KK6Bbsvd8DkNwyQ0SgREq8bD+SIlF3QfqTDAFKCDgjTCn9N7HBnZIVa+NtseVrVfVeEdWdVNGMnfu9QhSeMqrE5dJMzKJLiPy4ftrmzM+aff+3aoQU69wWVffIBw1RsC3+352E90jiYw8CnH0ul8I6ffA+NVD4v8+Jo2MruxzwzTsq7/RfQMvIhsCTrhyauECtmwAQPEx9CX4h+7nryjxefgDwPQlFyszt6F53LSTWEeFO49SAlV9dH5P6tsjsMozdUts7HtG+UU+T7gHD65rY/nPL8ICF6C5YHGuuxwDFQGAkKtBNN/9VWBZr8h1X9jq1o57hwIqGBnxNdAs9YqdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.5
Release Details
UpdatedNov. 20, 2025, 10:08 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturefF9sWdsS9s2UfdNuMWzSK76VLnucCn+KchXB9eFwbDsdXACnQYvhUVvS+xoMBYIfaXQEYUjfIl9oc0/sghPaIfdPvHwhCSze3XUPHkeF8yHb7eXQfHPr8N6KQZdmFM50brxxpHt5yxI40xZU+mMyW3eTP982e+aLenLEr2taQYNoaTHf35NbVknGLAE1xautWR1JQzYyVkCcc4T9/hxMp7LaGBrUPwdDALyuAp6WTHtZk+XeOxxxwRiNb7q7SpXWGuwdbX7MDvEzfBUmhw8dbORK3kqxgfXFAe7uGVdW+5sKtkdn+fxuN4gHKmBwW8N4X7YW1XaAL0KkYFoHsrdAcHunsSn/KfuqhTLHRpxOblgFPEcCwwqWcPVdGASFkD96rYylV9yzoJV2zS/pDqMVG38TQjHOs6ukBbJ5KHzpGJpgOZXDzLn24or5wVubjXkCyOsfS0by89VmrrxrERJZPNl6XTgeCZaBTPsypeWAlezfqVEjVVmdkoKuL+I3m/gMURucZV3Do+wHmxoEAT/2bTRVtIZzBsjrwuljZk4cok0oQ8reFBlngACWmXWskJOiEmshDUpnd2IykjhaU6LPEBDprd0fSM3+RE6Sx1+iJuPdXQ+CUATytcbKc8kiEjcPapzQ7xt3uR2fukG3eLrkFKoKHM7jJtWWMcWmN0yppuQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.4
Release Details
UpdatedNov. 20, 2025, 2:09 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYu4MvwQG1iiUU+ZnS3qdmTsCZ0NYAx89VzPzGTNqkdgQ/LolnwTZNDpkHEOoGQOM7Q95wtrJ1iHj2KQNxHsg/nw26/L0nqQtE8+HN92EVkjKhj4Chve7AB+A8dFlj0nXdSpgtH/PbwN+90lQTCGnmd6rRc46+OqEfEoFzjBhqtrpzXuURKsZG4GV644vSvAyzZFBDncgI7tlGhUhCDj/5UShHQy9ClymB6Yb5K7smP0BSDEWYOxsUOj2/cy7hAIlCyofINC8p6UzA6Po6NiyQefX2zds56a8TZhvvuaeiO3setCQzKXdYp3WqlnVVSpRA7UZfGeqdvkDqtQuTIs4Zo9w6GWX9M+N2Z15rQ59t/cNX0OYhCs+Fw2UdJMAPYW+me911YHsaMmANkaw7sUZ0ncqxbc0NZ8NULhJakF4DHizsCSLZbVrOhpfcaDEpsy6q88bg6o4modcj+gTIytUCI/RLjTlKWYAZcRFJfmPngQiuUmBeZntR2y1a0ROFsmcRAaSys5UFHF1KuqaNocqcAo+CbN5vRqMQXnmGSg54kEHvQ6xjDki/rs/0/5sxIX5TrGhGCyubdpN5Avv1y+eZfMdxVrZu0wxOubN3zgF47b/O5wql0Mv2tpFH88gy5XMGRTyaB1fIeFQXV874btSvkBwJPJhKdTZV0pyA9C+wUk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.3
Release Details
UpdatedNov. 19, 2025, 5:36 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYmgaFcAnfaw/gFhMAFJupT22RE/RWydQkyTNQrbQNyHQGq406C58CN9hKEIJ5kaSzibif3goqa31T/si8GlYBnejJmQzYsQpr5Wua0dSrkUtCv0fRFv5GNWJvazyREN7XKWp+Ir/Q51J7trkGs5RCuZRnH1DhHTPyieLyZLTFsSWTSPuDRHEnpqr91B0y+epvQGujYSYG931+5tc6QaX1rfZ9kmT1qNph4SdaLeMa4hbiSFc7fuuSSNqvE8q1QPrBJEz5TMixrr1U8iMA38HBWVsufDfd+lg18bVWg3zQG1yFNZn/RYCuPck6DPiDo2uJ/9AJzOTQpJEFjDfc2zG77oE7EufwDtoaJzbhXhiseW7u0Ty6nVywrYTnEbfuIYhHeo6lbxY8KtoPLZSDSiSbH73oigsGW7o7xA+RDjtVZoWRguPZF9VfUjJ1v5WQpHD0jdZflYJ3byMygWF1z7GaT8wE9gbzwvWUyMGE6XCpzwpNxx3yOlq0mp5aUNFAjJYAO8BSgaFBxSBVnXTv22JEnhaUPAWL3HaMh3EJLVJla/TOG7cd7N9eS6JT3maO8Z7QUu32B/ah48w/uxk9qzmBX+t1onXwUA/6CuY8Gfhcg20AK5JuUiqhIZD1eW7HiC0pNJJqZbzLbXBl049JBFUqZbKLdf+ttISPjCmlRTnxdg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.2
Release Details
UpdatedNov. 19, 2025, 5:32 p.m.
Changelog

Added

  • Database-Backed File Indexing: New tv_file_index database table for fast folder, type, date, and favorites navigation
  • Automatic indexing via filesystem event listeners (NodeCreated, NodeWritten, NodeDeleted)
  • Manual reindexing via php occ threedviewer:index-files [userId] command or /apps/threedviewer/api/files/index endpoint
  • Migration automatically creates the index table on upgrade
  • Smart File Browser: Complete file navigation system with multiple view modes
  • Viewer mode: Opens 3D viewer by default on app load
  • Folders mode: Hierarchical folder navigation with recursive folder structure
  • Type mode: Browse files grouped by extension (GLB, GLTF, OBJ, etc.)
  • Date mode: Browse files organized by year and month
  • Favorites mode: View all favorited 3D files using Nextcloud system tags
  • Breadcrumb navigation for easy navigation back through folder/type/date hierarchies
  • Consistent card-based UI for folders, types, dates, and files
  • Per-User Configuration: Remembers user preferences via ConfigController
  • Saves preferred sort mode (viewer/folders/type/date/favorites)
  • Remembers last opened file ID for session persistence
  • Mobile experience: automatically hides the circular 3D controller when the viewer detects a small/mobile viewport, preventing overlap with the canvas controls.

Changed

  • Viewer opens by default on app load; the file browser now appears only when a user explicitly selects a navigation mode.
  • GET /apps/threedviewer/api/files/list now serves hierarchical payloads from the database index (folders, types, dates, favorites) instead of scanning filesystem
  • Supports includeDependencies=1 parameter to return all files including textures and nested subfolders for multi-file model loading
  • Dramatically reduces filesystem scans and improves performance
  • Navigation data is loaded lazily per sort mode and cached so switching between viewer and browser modes no longer blocks on loading every file upfront.
  • File browser UI refinements:
  • File cards now share the same compact layout as folder cards (consistent padding, thumbnail sizing, fonts, and grid spacing).
  • Type view heading and breadcrumbs no longer show a leading dot (e.g. GLB instead of .GLB).
  • Breadcrumb component now handles clicks directly via NcBreadcrumb to improve reliability.
  • Remembered folder/type state is cleared when returning to the root via breadcrumbs to ensure a fresh reload.

Fixed

  • Newly uploaded, edited, or deleted 3D files (and favorites) appear instantly in every navigation mode because the indexing listener reacts to filesystem events instead of relying on manual rescans.
  • Root breadcrumb ("Home") navigation restores the folder list correctly, even after drilling into nested folders.
  • Multi-file dependency loading:
  • Backend listFiles now supports includeDependencies=1 to return every file (including textures) and nested subfolders.
  • The dependency crawler recursively searches texture subdirectories so 3DS/FBX models with textured assets load successfully.
  • Texture search now uses the updated backend response structure to avoid missing files and 404 fetches.

Technical

  • Created lib/Db/FileIndex.php and lib/Db/FileIndexMapper.php for database operations
  • Created lib/Service/FileIndexService.php for indexing logic
  • Created lib/Listener/FileIndexListener.php for automatic index updates
  • Created lib/Command/IndexFiles.php for manual reindexing command
  • Created lib/Controller/ConfigController.php for user preference storage
  • Created lib/Migration/Version010902Date20251116061241.php for database schema migration
  • Created src/components/FileNavigation.vue and src/components/FileBrowser.vue for new navigation UI
  • Updated lib/Controller/FileController.php with new listFiles() and indexFiles() endpoints
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5KdirqBqYm1NTouEBce9EtmTO6/UJwHsg40DC/oCvhIuYOF8eLVWy5O5Vwx25hFvq7bHrr9SQQ9cdJS1G+fY8aQ7bSeqCR1dAdUlsoBhrYWQnKAEnoG8vIIxfk0gA+HgcRGLJPtph9TYLdeAqdhD7/VVUg8l9ZdIVJHsTQn8JijVkTeMpk5612wUyoMn7HaA8IJJrOnU6dmkfv95FcZbVLzR6QRDc2fqlM3aYbZl9KOVYzEU6e736rsQAOVn0IqrHmQyJNVIdiFnycbtRiJnJgRJj05KblfVIOC0qZwzl7sqIHdAswvYwDmdCoLK/rblTc8R7INoUFpmGFBzPITfVwBNrJh9jsxxuWnl0X/vAAtVACVGHR1utLtd4dMFSjwOVxD3f53eazH9/Hho18BBh3GOLNJEWAGNal3u8Ng+inilqQu4Nmhds9AYUoq6T38oU9NABr7mDce1TsO0AYQqfez3HBodgbbBOoXKSGLMoDCk9T71NR2Oc2XC8Urz08F9Q/0Dcoy06kdvm9neAOH+yHqjcPXfI98JCdqao6Li05VQaLOUxIyYbVpngGu3LWthpaH7rlxhD5DlOwknjimFfGifjZ0/yScHSbfRqtgxy8q8jfHJ71wMKOAzQ6xCgqfuWWROUqWNmo4ypsE23Ioj1n/yEA60bComBBgSdSjCwpA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.1
Release Details
UpdatedNov. 15, 2025, 9:35 a.m.
Changelog

Added

  • Preview Provider Implementation: Implemented Nextcloud IPreviewProvider interface for 3D model previews
  • Admins can enable/disable via enabledPreviewProviders config in config/config.php
  • Integrates with Nextcloud's native preview system
  • When enabled, provider is registered and ready for future preview rendering implementation
  • When disabled, Nextcloud automatically uses custom filetype SVG icons

Changed

  • Updated dependencies:
  • three: ^0.181.0 → ^0.181.1 (patch update)
  • @nextcloud/router: ^3.0.1 → ^3.1.0 (minor update)
  • vite: ^7.1.12 → ^7.2.2 (patch update)
  • @nextcloud/browserslist-config: ^3.1.1 → ^3.1.2 (patch update)
  • Improved duplicate registration prevention:
  • Added guards to prevent duplicate file action registration
  • Added guards to prevent duplicate viewer handler registration
  • Enhanced error handling with try-catch blocks

Removed

  • ThumbnailController: Removed custom thumbnail controller endpoint
  • Replaced by proper Nextcloud IPreviewProvider implementation
  • No longer needed as Nextcloud handles previews natively
  • Thumbnail Placeholder: Removed dependency on thumbnail-placeholder.png
  • Nextcloud automatically uses custom filetype icons when previews are disabled
  • Custom icons already registered via mimetypemapping.json
  • CSS Thumbnail Overrides: Removed CSS rules that forced app.svg background on thumbnails
  • Allows Nextcloud's preview system to work properly
  • Custom filetype icons display correctly when previews are unavailable

Fixed

  • Duplicate Registration Warnings: Fixed console warnings about duplicate settings/registrations
  • Added registration guards using window/globalThis flags
  • Improved handler registration checks
  • Better error handling for duplicate registrations

Technical

  • Created lib/Preview/ModelPreviewProvider.php implementing IPreviewProvider
  • Registered preview provider in Application.php bootstrap
  • Removed THUMBNAIL endpoint from constants and API documentation
  • Updated openapi.json with preview provider documentation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureyK5Dmfw2bZsjqJMtY0eaIkST1jQZ4XKcPtlHp4f7GNHWV1A5PrKjkxKHO32JML49YXEI7D8PouQvnHXDq55pRDm/PBmXV3cB5wvx3ZkMVMGRQBqHNR2iBfSsl6w9I0SaGSgNSt/aa1kmZRCq20GPv8hLMHSJdcR2VpU5mA/BLp6vP51LBp9wDwzCEsU82zX6VVBXkd5YIHY+6HMRsNQNXFfRsWERTq5yjmTYfyK4Zq1rvZShPX3KG8WDWl6g3C1lJhyr/7FWLoM7Lsq6k1pirdAYUtQO7ooB0UDiTpvEgmCj/OLhL/8dvaD29dyzUxnokjj8IlpiDEcPfeLN34nqXHfMbmPZh9+aqBqsZr0GaqSD6dXGoplkBc+F/SHfWdevkZDHk8p/W0flcjIThr+K5/8YhwdDZd3G2JIRmhDdJXFfoTn2f+zYkHTV3enXsRAG95FVU3WE6zZwi8w4MOUiXIpxqb/DAISRSId3k24iPFlbguOQwPvcpMGNIruPRkoE7ihCgiU+dvcQ6ieYtwvtAHgNy+N284BdcsK7/0g5tCkzDad8Fdt4pMImPkO+cWBy3c19Uce5+pBx36Nts6q5so6O96s0Dcnf4NQPsnRFHNKduWxafeoBHCwn2NoH2aIRw0JQcwyCLvL2j8vLcN6PhAQwJ2I6P/4yHX5aEWEwaP8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.0
Release Details
UpdatedNov. 10, 2025, 6:57 a.m.
Changelog

Added

  • 🖨️ Slicer Integration: Send 3D models directly to slicer applications for 3D printing
  • Support for PrusaSlicer, UltiMaker Cura, BambuStudio, OrcaSlicer, Simplify3D, and Eufy Studio
  • One-click export with URL scheme integration
  • Automatic STL conversion and temporary share link creation
  • Professional slicer logos with brand-matched colors
  • Last used slicer appears first for quick access
  • Smart detection of uninstalled slicers with user-friendly error messages
  • Auto-download fallback when slicer app is not registered
  • Temporary file cleanup after 2 minutes
  • Share links expire after 24 hours for security
  • SlicerController API: Backend controller for handling slicer exports
  • POST /api/slicer/temp - Upload STL and create temporary share link
  • GET /api/slicer/temp/{fileId} - Download temporary file
  • DELETE /api/slicer/temp/{fileId} - Delete temporary file and share
  • Automatic cleanup of old temporary files
  • Proper filename sanitization for paths and special characters
  • CORS headers for slicer application compatibility

Changed

  • Updated app version to 1.9.0
  • Enhanced toolbar with "Send to Slicer" button
  • Added slicer integration to slide-out tools panel
  • Improved error handling with toast notifications
  • Updated translations for all supported languages

Technical

  • Created appinfo/routes.php for route registration
  • Added @NoCSRFRequired annotations for API endpoints
  • Implemented Nextcloud native share system for temporary URLs
  • Fixed filename handling for files with paths and special characters
  • Added proper authentication and cleanup mechanisms
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebdu+2mjB9NyEPJbbogKcFBovDi99alBx7ixiNLiVWRmMw63yphLItjrEkmMxHL5KeEmkQj05gqf17mPXlngrWI3pkhn1gLHtbCiPwOqloKdQ+bf0US34nClshbCzP0drRsNWSxhEWbd0a0vc1Zy31vk9I2PVHugbpbo4BCy20Ks6t0aCkv6o+g8x+v+B348txV1p1F5hyM+2BBUqxEB8fNiRJ/p6bd1eMf6lWfeqBrWG6n3zB11BsKKYAa941deyJ8vLlEpI8D7Kyz7TVHuHzx9hlxbZyOCYEnD52EmpYDl/VaKnj+9EzxaFDqkQPOIHPbMtfe9tc0nZOrqEASXrUC/iPMpp1JNAyRa0Egx+DkdRE/p6wFMBxipGqfgb7MJ6gV4I7t7ML1scquVtcc8aZt3oNhdMd6gPZ7/uxOYQ8Bm6YX6fiuwxtqe5yb2uwiw9oKjQhs3miEXEAiaiab3S4HLC6Ke9QYowEzASoAD1tHf+kf4I6+IszL5V6XOVzXRhKU8t/Vdl/JSZh3LEOma2KCPBoxIwove9PpHARcEIz6Tk4eu4UvMdTjhyIBKCvEnq8Bs5fzaq+jqSuE+8h3Y+qP0Q2fSqcNHpLtBEAfrkSu883dCM7dg0miDxSGumfqn+oJ80bVDGm8mQ3WdP7AvJiygdj5i2pFqyeiPE+MGDDUE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.8.0
Release Details
UpdatedNov. 6, 2025, 2:21 a.m.
Changelog

Added

  • Screenshot Feature: Capture high-quality screenshots of 3D models directly from the viewer
  • PNG and JPEG format support with configurable quality
  • Automatic filename generation with timestamp
  • Accessible from toolbar and tools panel
  • Download screenshots directly to local device
  • Fixed WebGL renderer configuration to enable screenshot capture (preserveDrawingBuffer: true)
  • Billboard Text Labels: Annotation and measurement text now always faces the camera
  • Text remains readable from any viewing angle
  • No more reversed/mirrored text when viewing from behind
  • Smooth rotation as camera moves around the model
  • Improved user experience for annotations and measurements

Changed

  • Updated app version to 1.8.0
  • Enhanced info.xml with new feature descriptions
  • Updated English translations for screenshot and billboard features
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebvx0cRDWADLGiuJ0jIg9f+81Add0o7SM3Dkq8foP/AnLfM2fSL1/ds6JsckXbP8xdOMoPyAxnN9XFi6qckR+cTiBSkkbpbQeg32ZDShLwwVRH8dubBAHkIk7qSBVjw4gV2BdKuMjWZZ5cRPlcFNUtdVP776NXH6Vv5FxK4UoudEN3VA8qDZC9+8fC46n7NhnobbviHIzeNqPA0V3Wo50UuuptyDdip0o/bbBiaw1vszWuV4QoHFMHAjFan2v3kKb5ptIHYukVOLMj8rY1dKeTACQYjEwi+YXOV+4eJMiMrHXx06eMYxiOsTdNHsCWTy9PW+4QTHM9dI/GNygSKC/V4Z9lxlPt7jncHOAT5P+tEFrWQYImvBtWsXPXoP6UPAIpP82NPWhp0lBZdjR3Fvx4yi0PM7V4/lRTztUkTrobnlVNpIxSe6SH0jWmVlyAFAW0XSiWrWbM+Q0gNEERVNudpCw7y41xv2Q45ZgPIMhGzjn7CbY8gVacSrXcsnglD8pcFAnI7hh+KfepgSSeJg1ZV7IF5UM9Qr9Cu7VDtq8Lc5bw3tqXNDf4U7T5UgnAjbA/GFXY2fz6yT5TVXWoNxER4d3Bocb73r5wWYlag6z2CKOxTOWenRUnrG0iFPi95MxaykgJMVTAilRLoHOOGZie9DHpOUwcvXWAokeDaXGBSQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.13
Release Details
UpdatedNov. 5, 2025, 4:44 p.m.
Changelog

Fixed

  • CSP Conflicts: Removed global CSP listener that was breaking other Nextcloud apps (Memories, etc.)
  • File Icons: Fixed custom file type icons not displaying by copying them to correct location
  • App Compatibility: CSP modifications now only apply to 3D viewer routes, allowing other apps to function normally

Changed

  • CSP headers now scoped to specific 3D viewer routes instead of globally
  • Added automatic icon copying during build process (scripts/copy-icons.mjs)
  • Removed lib/Listener/CspListener.php (no longer needed)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureqfnNbykFms5wS6aOa/TKBYua37o3OqBNqe9LY0dU86/MrGrqbv+v9inkFm63UQyR19ux9ACVlqG6J5rCDpLLTYAe1IkFGZMKfJzNnna8uiA734PltS8gNSv8ol0zMsRs6tvanPlDxVedCmzp31VsErPKOShjvTRbhf9ZmgJjZccX3UODdfIzZxJLqHd3vBrYqoksFXDZnzUqEkb5OK7RwfxAs71IGoVjXHOf9HuOVuefc1mhElbIygGnPYJ3ewlU72SD6gB9/kYgmZEb9xklvcTz6IjTbfKdZ7fLb640/71wCIa5mYleBkaxsoAzfGo2QjC6GHNkAQX7gAXeZp1GYBixwIF4JvawV2s1pKnIDZE3hkLqhx+e4OOYvawhlVoUdILsXSC2oiG4Q5qF/tSz6fh/2gFY+/2fxJMSwwbPXTkUPA5eXKCTpRIbWdIK61XEMKenSlWFF0giXWcOMkxXH4/T1NFlV7/g57MPogu+DD41Y2LNkdZQVaVYWEyvTw3t0pVvgohGNPRSNKV8qR8yzSM5yVe0Ck6SWOk+74SWwBoItjp66LwNQ60JK2Od2+NOSE3+2eG1SkIKL4+q0Uo4NGQgyxblPWIXJZTl14XcaxyXCJr/K913khhw+b+DumTz6TTGg6jMR8oXRFawccmEHL/DN55Y3HfRAwVj/cNIx7I=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.12
Release Details
UpdatedNov. 4, 2025, 4:14 p.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets
  • Axes Positioning: Axes helper now positioned at bottom center of models, aligned with grid
  • Axes Scaling: Made axes size dynamic (25% of model's largest dimension, minimum 5 units)
  • Logo Loading: Fixed app logo path in demo scene to use generateUrl() for correct URL resolution

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
  • Axes now recreate on model load to ensure proper sizing and positioning
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturetHcAULDv6Z6JlPEmMcFbBFAPFTScfSULZoxHuFuCtCO965w5/TPzwTm9AAbrvWQlTzOza8VK9le5cP96O8XKoLPU/rUTY0YPL+11rga9rU5c9nNcCiRTKwaha9wnos5/fDxZOhefb9aFojmpf7HsrSmHL+JMrLwaVvp846k4ShSPNbV7cVTqsrwz1C4Vy4JRzGMuSfcAMjxLBC02obtPqVYX4T5s7/QCithiTGI1VmmtMxZM0sG3fkIRf9ouPQ44hpHX7beIm4vBGthPwjhlNpR+aXq9rQd4annpZKtZGfGRX/RDXlFXUWBE/07A+/CBzP9DX9pegUvzvrelj67ADuiRDfxsL3vKN1VGU1rqHi1ZrwE24BzfeGnWgUb+wEBOcM8x8BEc0thH2zbfKAwqjY1h/iH8IjoIkbPPm3mKwQ2b5w2v2aaRRr3qbK3cOLhyzWJBpQFZaahomr9x+/hN5aU9fhr9hMjgPboRadI7rO+V6QtQDobhKlz+6Rdb1mo+Bzk9btyCJ7flqqos07yo/O01o4G/0L0hdw20vYh3WlbeGRWC6W/OBzQmRwT4MWFoHV2mQOYO4Nie4SkFCzs2lT3NWW7I/uCrCPMZDJrLhSk1KiKzctZXjTUalB4DuPHSpq99WjCcf2oPLo6rQ7DJd/sIRSh3tSj8WIlxcWCrSWk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.11
Release Details
UpdatedNov. 4, 2025, 10:58 a.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturehK5rTudkrE4neZOI/n/jv+qjY/VfKVkRkouiBK5jBARxwK9e0W8U07ajlvP2+U8q8qwNzjGY77RproUnQIkOlALx3c6V9ufsMaHcpuAR2GAvK84WgqqEM37VSPQrGhLLuZzz4nU5nYmNOsTg+maV0O5XEND29zOdafsJHxpY8mQofXCA0vyLYNLYQtLEAPRBtl3uDQgSSjPGB6kN87VUcqebvVrVim8aa8l7BpDoMiW5j60T7toenQiisu6S4aKMRHCxx7I1cHK/mkGjLzl+/AHpfwWpKCr13StVLzqqlVPc5+SL/yI14XW2JtJARHShE0zBqkARG0yqsFXVTGvLzvAahLuCfIdlied8HFK/NQVmqgLlRAc30xWGq2vcQvpnr9L7GwCkImnM5YeoTqVOJHawhc3wU8FQ56g/imCYgMqwSOegC1BVegdX/XvCMCxhQXEj4OxUxETQ/T1+BWqBBLLbZQQiyyRXw6o/bzpBWJYtDH/XIzFKxJdC7WKfIM6lPmPb7CCc6sJGfEi/Lyv3rTfCjyQj7ioMlB+5sLV5/d+tth6eeY9O6jZsz92/V5byhrtIdYvu4j7IZR2m8+/IpkjmTMnuv18wyU6+unVo8V2hKNLhoWYHJqEQ9Ylx/Rj6tKb4TwsrciMPV+2T38lJK5cS0GZittMNUVAKSUPBSnQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.10
Release Details
UpdatedNov. 4, 2025, 9:48 a.m.
Changelog

Fixed

  • Dark Theme Support: Fixed slide-out toolbar panel not responding to theme changes
  • Theme Switching: Implemented reactive theme binding using Vue computed properties
  • CSS Integration: Converted base styles to use Nextcloud CSS variables for better theme integration
  • UI Consistency: Toolbar panel now properly switches between Light, Dark, and Auto themes

Changed

  • Improved maintainability by using Nextcloud's standard color system throughout the toolbar
  • Enhanced theme responsiveness with component-level class binding
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT38bK6V5aDGCGlNZJEV3QCmwYaO21RK3QtifSTQ3O311NOPA+O7ryIjncKIdr/rdN/9ZjeFLqwcxRk2traF5MkhvjRCr3NT5oV5cuMQgmw2Js7MAOLrSXOUkn6b7LpQKesEmzQPqygl2fuqN5qrxILxeWU5xWq3nyoV+G2TzByeLOT9CX8+EH1rzMnJNb/35aFS0kj/fU4V0FHFM3h8KxNDWYVjlJ/tPtq5IFBvICRm6HLCkQ2fEPPXkxP2Z1lpBam6ZGuSbcmo2lb5yQ7Zq3n6rd7FIzkU6r0/xvYF5gY08bwIdtULEpJOEQwp/n9KBR2itV4twQG2QAZFEIMF+EIg4lRzu0vdX8GVDFbirW8YqCX8PXyd+UAm1FfZ4vf8VHQXF7weZgAnvm3crR01GNStlBHl37Zke3z8CHmiYj3ffi4WMwGBT4Gb8f6fxlCOHH/mOlCyA3rtVlI5cHqJGHbdiwgHYX6e+0p5JRWOlecK/HTcVd0Aa5KX57BTcRQXEhGaQqHIbRhWTWwDMesbAyJMtCT72O2eRUqJCnSI+SRSxl/Hh4lHUEnOPx9D2P/03xDEDpO3xoR7tQGN927un6811AS+v+kABFDJQcayHiFTpR28U+WzF1vqS2fktc90NaWwiJuO1/hnQ/H8J6icAF4oxmvArZI2uO1X3CPZMwN8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.9
Release Details
UpdatedNov. 4, 2025, 8:39 a.m.
Changelog

Added

  • 3D Camera Controller: New circular controller interface for intuitive 3D model navigation
  • Camera Control Methods: Advanced camera manipulation including rotation, zoom, and directional nudging
  • View Snapping: Animated camera transitions to predefined views (Front, Back, Left, Right, Top, Bottom)
  • Controller Persistence: Save and restore controller position and visibility preferences
  • Smooth Animations: Eased camera transitions with customizable duration and easing functions
  • Face Labels: Orientation markers (TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT) on model faces
  • Export Functionality: Export models to GLB, STL, and OBJ formats
  • Camera Projection Toggle: Switch between perspective and orthographic views
  • Progressive Texture Loading: Background texture loading for improved performance
  • Dependency Caching: IndexedDB caching system for faster multi-file model loading
  • Model Statistics Panel: Detailed information about loaded models
  • Help Panel: Comprehensive in-app documentation and controls guide
  • Theme Customization: Enhanced theme switching with RTL support
  • Performance Overlay: Visual performance stats display with real-time monitoring
  • KTX2 Texture Support: GPU texture compression for better performance
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT4jr3f8pu++HPy93L1HdiayTsqKV8PwgIw7jkye4TkqYM7wBuBsK6qv/TXwuZElOm9hzBEVH/MnuFoRl2QXPxurhZLvu918h0rDnTIjfRNMC2gIVFI8JLneLq8ya+m9lKGNLXEodRAhQ3pGn97cuSFzff+iGhQOFkojIvbWM4HbSQFq6pfF3/ZYjUCt5BhRIHdPkk+mzwjpVaniq98gh4XS3AMYqs4p2n7vysSGjAO8hZrDdGUJGxBlz1amgeUpdiGOpTwI4FyT2vbxcmHcMVwZTQyXoYZ/Ux70JZp98nz1H1kKqIoDxzZSRmetrm23Pzn+k440rHQUSjuIa/p6I6w8nGG25M0kjB46lacCtbUlR6Rrrj6SNwFITde5Rr87HP2R/ILqI5MCC73SebBxcdY/IXw7xHjUiBJTlrOmPi1ppktirZnOsmSR2Sa/yE81zrYWJ2HidTbxEGpJDZBIiwOaBCYh6nwQ51PG1z4mxgJE4G+7lMZ/bpu5UaJihys2WcoBhPxrGNeoLoEN+D1JiaOU1C974Wwncz1/BobA08FSx+rzXCnRNSQWbBapz3J6MYT5TwChku40FeqGR2KJm+v2pYgm8K/Eepieubi61tD9St1exw3AjBiJ2/dKOezNb8ZHFcoWn2bxCZc4rzuBUO0tJSrjmbW5dcysusajrvMs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32

Nextcloud 30

3D Viewer 2.3.4
Release Details
UpdatedJan. 18, 2026, 6:57 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • @babel/core: ^7.28.5 → ^7.28.6
  • @babel/preset-env: ^7.28.5 → ^7.28.6
  • postcss-html: ^1.7.0 → ^1.8.1
  • vite: ^7.2.7 → ^7.3.1

Fixed

  • Nextcloud Subfolder Compatibility (#74)
  • Fixed app not working when Nextcloud is installed in a subfolder (e.g., https://example.com/nextcloud/)
  • Replaced all hardcoded /apps/threedviewer/... paths with Nextcloud's generateUrl() and imagePath() functions
  • API endpoints, decoder paths, and static assets now correctly respect the configured webroot
  • Affected files: multiFileHelpers.js, useModelLoading.js, useComparison.js, gltf.js, ViewerComponent.vue, SlicerModal.vue, App.vue, viewer-config.js
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturem8HTfU5qBOyGRmdHu4hylXbuVzwpcofBxDmpgWjNf0qvBkujJC75qp0z1GcJKDRppDXuE5z70L6g5AAUda+v3Z5y9byJqnisxNVRZM0nbXwYgOmqi8MdBnP7bzd5Ms43jDwr8hKt5ffEKR+g6h7Um8q5BDl3Vdk2J8nVuk+uvduDXalukp+USx3ibBHjg3MDSJqdkTKkHPe41SXEpjyor9VMKLer8r7HxHcwlELGseyuFHgL/PPYQOT5GQf6KO7ae0s2lYH6tbBu3ZnP/f0tg4cYZAHAycm4fvAwRj44XMh6a3OFP0FdQOkpSmYa7Tp5IH5rj4zBjrazGCxtUU40q3bBmYRvIKiv/je83LfQA5n8FCgmjREez0JslPFBByM//FtrcFgsm1mrzSgkufLTdvQ8JYb1XbV6xI7jKWSYKdCidwpvHCWAUqvQDWRVL2cZ7tLyGwSVHtLEsftO3Ykhu7WYpyQmNIG2s6zTuBC9x52IL7hxU9+7127R5L7S2AdPxvUB5Xrz5PJbfKal41mSi5GTJ1s1d+ow6lyB/rU1yGMX12Y0gDTZjXLHvCMq6Ixtcy1igH1nd/C8P+j1vDHbDPyZnIKtx9yBTMsejnv9tdsPpE4ya+eM54l1i+gZEdM8NhFxows/bBHJaljU6DBRvtdYBriWsMHsrsDB2wSuTMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.3
Release Details
UpdatedJan. 16, 2026, 2:42 a.m.
Changelog

Added

  • Automatic Thumbnail Generation: 3D model thumbnails are now automatically generated when viewing files
  • Thumbnails are captured with smart cropping to focus on model content
  • Grid and axes are hidden during capture for clean previews
  • Thumbnails stored in Nextcloud app data (not in user files) to avoid cluttering recent files
  • User setting to enable/disable thumbnail generation in Personal Settings → 3D Viewer → Thumbnails
  • "Clear thumbnails" button in settings to delete all stored thumbnails
  • Works in both standalone viewer and Nextcloud Files integration modal
  • Thumbnail Settings: New Thumbnails section in Personal Settings
  • Toggle to enable/disable automatic thumbnail generation
  • Clear thumbnails button with confirmation dialog and count of deleted files

Changed

  • Thumbnails are now stored in Nextcloud's internal app data folder instead of user folder
  • Prevents thumbnails from appearing in "Recommended files" and recent files lists
  • Improved privacy and cleaner user file space

Technical

  • New ThumbnailService using IAppData for app-internal storage
  • New ThumbnailController with endpoints for storing and clearing thumbnails
  • useThumbnailCapture composable with smart content-aware cropping
  • Thumbnail capture integrated into both ThreeViewer.vue and ViewerComponent.vue
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureuFzoE7HPsOrBKlyKjSzOuEeHjB3dAL7I/y0IF+GNXsgneUJobVLzw+kWlOtZyGa4wNcavAH3caVRlqPjMdw/ZlrlgkJN62qghl/yvbskmmSsiC43ntcKMfGd8Gat1wCXPzTGipRc4DqwbpUyN3PIVltZgpSvX+xaTB3+bLwPNIg2vX2GMpalC9vN2eGa60fPm//OXZKujqfRVwGqg36OpfuRKgOr+J2xDyxAsyc6UywFRGHUnpGLiZgp0jXIRshqpM+k3fpbZDB1JrDbYlPaAZwa4Ze//xxv4Xbk4KmmlLY0S6GXChNbX0rAYrwv+PxJxFeLSI5ytw9iZPdDFQFmgm5RJv7XVhmc31ccZyJ8eaTRz2+JfqBRV5csX5Ox9QIVjmkL3WX70eNIcRUBGX4BFOcCqZxnRgg3moqoSKAZFQ3sOMxwStiKlHhrffZvclDLZRL3S76C492BVlF9HFZc5KYnuF17VQPs1OL8xWFcW57HfcjpMs6xydfnMtfTJUTWbS9p6fjaxLjUgl93ZqWTMmc8dpXl7ywiLjVViTNk8PNn3NzHP3H5yszzBxcO6elz7cIQgXRKdVKB8OQoaeMEjxQQfmWAIViZyXoEEhS76bsI1oeuqAiNH/9p+ytUkBeN7FK6yljt/8QauU38PLTih0QE2xCLB6fP1EMAtLAb7PY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.2
Release Details
UpdatedJan. 7, 2026, 4:17 a.m.
Changelog

Changed

  • Dependencies: Updated development dependencies
  • stylelint-scss: ^6.13.0 → ^6.14.0 (dev)
    • Enhanced dollar-variable-no-missing-interpolation to flag namespaced variables in custom properties
    • Extended function-disallowed-list to detect disallowed functions within @return expressions
    • Fixed false positives in dollar-variable-no-missing-interpolation when variables already exist inside interpolation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYV9kZk3oeyVbeTn1qLIVLgb5ERRXgsTQamJCdPTLt4vgt7xf3ShcXd6Srry0UKHzEGvXqQjVq3sfxtq5INZNKQugyC67dlmP+WfFbq7nsLoGJp4Cpjza5ud+4ozbxgbVqNFhNoFVz/zYGyc1lAcSUP8zC10x6zfq/0WoRUFgWHOKCuK6ZB4vQd5J7JRX7JKX1sxbl+rGHSqbQeWuyJwlCir+zr5Y+746EJ+2S29f8z3KFxf2PfEZwAmE1Nf05FAVL5RhQhlDjqM7J2NvQEP6Pod8b6Q+RxFKMWkYyTHncCfQTNz+3VYeuJsqfYC6ZVADYOT5duvHbqOIW4sdnrotXWQDq0uxww7qKYISKp4srSKBwiN1k6EIGt2Clr5FOlji80tIlOtzqanLWQlhpV3wuiVSA8TTbfxPVlB4Mn7dGu/LHc5TjfQ5JEct+5nfEwSCdnLbnEhLyiNRFPppmpIjfHhSWhVrHpz/YRdSarEsmNeP31dSoJKbhNtqhi1oIbtyXG5ElYrTFFg+nxz/yc3Ok33KY+D1nJ9gIN+Qdde9E86qg0cXeIgq+CtM0TGomTKPt9BXQfresW9zuzKli1p7xS3d0C21acP8r6humszQcZQNliz936kTkULULFo86VkyYmK79Gmm16tcMDXbXiZcvhwqF/xrXS6QuQb44K9hC2s=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.1
Release Details
UpdatedJan. 7, 2026, 3:03 a.m.
Changelog

Fixed

  • "Send to Slicer" Button Always Disabled
  • Fixed missing modelLoaded prop binding in SlideOutToolPanel component
  • Button now correctly enables when a model is loaded
  • Affects STL and all other 3D model formats

  • CRITICAL: Cron Job Fatal Error (#65)

  • Fixed foreach() argument must be of type array|object, null given error in CleanupTempFiles cron job
  • Removed incorrect foreach wrapper around callForAllUsers() method

  • CRITICAL: Missing Exception Method

  • Added missing getExtension() method to UnsupportedFileTypeException class
  • Fixed fatal error when attempting to access extension information from unsupported file type exceptions
  • Updated all exception throw sites to include extension parameter

  • CRITICAL: Insecure Rate Limiting

  • Replaced insecure session-based rate limiting with distributed cache implementation
  • Uses ICacheFactory for thread-safe, multi-server compatible rate limiting
  • Prevents rate limit bypass by session clearing
  • Improved scalability for large installations

  • HIGH: IP Address Spoofing Vulnerability

  • Replaced custom IP detection with Nextcloud's secure $request->getRemoteAddress() method
  • Prevents IP spoofing via forged X-Forwarded-For headers
  • Properly respects trusted proxy configuration

  • HIGH: Path Traversal Vulnerability

  • Fixed path traversal vulnerability in temp folder verification (SlicerController)
  • Replaced insecure strpos() checks with proper str_starts_with() path validation
  • Prevents unauthorized file access via path traversal attacks

  • MEDIUM: HTTP Header Injection

  • Fixed potential header injection in Content-Disposition header (PublicFileController)
  • Replaced addslashes() with proper RFC 2231 encoding using rawurlencode()
  • Prevents header injection via malicious filenames

Improved

  • Temp File Cleanup Enhancements
  • Added comprehensive logging for NotFoundException cases
  • Implemented cleanup statistics tracking (files deleted, shares deleted, users processed, errors)
  • Improved share deletion error handling with separate method and individual error logging
  • Added file type validation to skip directories during cleanup
  • Added progress logging every 100 users for large instances
  • Added stack traces to error logs for better debugging
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMK0+lREma3H+5o5HY/1XiCvaVsxjUPM6tcB151nPTrwJOeBdbFF50jwp8N7+xgyKEF8GZEOYpwqBu2jYjlN8bJBJFDUI4Givl++H6HAT0/yOhq22oItRC9eOdmrdLiduHqSm10UDj37gda6vOjZwGiPKG6OcmqTkAB5z6tCSX9xH4RznZwbWIpCKBKfUDv53iCyBiogCX3Zyz7443IEdkBy51an9N64COoiWJACcw1kse1f2JtS5ptYTXBA1ImMNOVkX+I2n+b4qp9n972UGMXyx8JnhH7nuVp4XH7nY2oargPPk4b+p1Yqq5/bJGsdazm+i8PaYcJ6UHJTySjTOIvh6MuWpA+xuXTWSgM9k1DUTFG2YQ1RS/JJsFQ4Xhm9GffxDOQ8mSMRp3MnEdMd8FgIU+REqQP3MAYvxQ/c11RWoaolM+VbWrKNGJqB2U2pliu4oM7DRlZg4GmgHvYUMUQQYTq+L1lZlvyQp+NweTuvaUcx6cY5z3SdiRb75o3fYhgNGPDg6ZgsV45Oy6srZofHfGiuYy+c/AkL9Nr8c0nBlZWtxV/OVdPsbHHmNhSVAmv2baFug1mGUcGYRApCmWLlpJbskW3ZNeFfsHp/d/st0LPjhK6ItDHe+6khDFAYoqQWFA2z2nmrWZuD/FzB2Ny3TVCZBQkpzDc7hJiBBxGY=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.3.0
Release Details
UpdatedDec. 28, 2025, 1:15 p.m.
Changelog

Added

  • G-code Visualization Enhancements
  • Smooth rainbow spectrum gradient coloring across entire model
  • Intelligent filtering of travel moves, retractions, and parking movements
  • Skip movements exceeding 50mm in XY plane to remove edge artifacts
  • Fixed vertex color handling for proper gradient display when toggling modes

  • Mobile View Optimizations

  • Responsive MinimalTopBar with all icons visible on small screens
  • Help panel displays full-screen with sticky header on mobile
  • Smooth scrolling support for iOS devices
  • Performance stats overlay hidden by default on mobile and screens ≤768px

  • G-code Toolpath Visualization: Added support for G-code files used in 3D printing and CNC machining

  • New G-code parser supporting G0/G1 movement commands
  • Automatic layer detection based on Z-height changes
  • Color-coded visualization with different colors per layer using HSL gradient
  • Support for multiple file extensions: .gcode, .gco, .nc, .acode (AnkerMake)
  • MIME type registration: text/x-gcode
  • Custom file type icon for G-code files
  • Toolpath rendered as 3D line segments with configurable transparency

  • Extended G-code Ecosystem Support

  • Additional extensions recognized and routed to the G-code loader: .g, .gx (FlashForge), .g3drem (Dremel), .makerbot, .thing
  • MIME mappings added for container variants: application/x-gcode for .gx, .g3drem, .makerbot, .thing
  • Viewer “By Type” browser now lists these extensions via SUPPORTED_FORMATS config

Changed

  • Backend Listing Filters: Updated FileController filters to include G‑code-related extensions in Folders, Type, and Date views so these files surface across all navigation modes
  • Applies to folder-scoped listing, type grouping, date grouping, nested folder inclusion checks, and descendant checks
  • Slicer Handoff Options: Simplified send-to-slicer flow by letting users pick passthrough extensions; all other formats now auto-convert to STL by default (export-format selector removed)
  • G-code Visualization Default: Default toolpath color mode is now gradient instead of single-color orange for clearer multi-layer contrast

Dependencies

  • Updated @nextcloud/dialogs from 7.1.0 to 7.2.0
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturePzIAblqli6tJofqSxWWKOwCp0h/EaSHMlT/+yyoqArTsvBAPzn8xLqU4/58/5pja+YAA7WeQ9egTPUicOilrSFBSHQVGa2QSLQVWFu+vTXKGEuu90qNxXnNqDnl66TVKuWR0OiXHeR2nCAdu3jh7310h8afbBLR3eQAXIeuWlLtXnbzVLoaiqy4pK9l2FzN8GpQoU4/7fo7SH1CFE6iR+Yea4Y2P6C1UXGQfG/RnJylCj1UT+2UkkLUAopk9AOxMEgQpwQzUV5kl4FwvND4biwcuXEajDXwVizX9qAEDtOkzILdV46RcWkyHxnDcqIGHuRImgfqWlwwz6fZ14Huf7/5bmvNafPuSPPRJHEWvH66whuXS5sft+Crvd1/AR+sF2bDGtGtoQo2iSRSCmVS8XtsK0+dW+DLGIvd7pQsul/AIWPDYYkPtMCgAXCFLJpFmmOhNNoCOshjRSgJ5LrnfBC3kbF66gBZGhlkBc0n9MDkqlgxDNVNGwYHYG6xllH4u+1xdGIilc7TE2EXSmpD3GEJOCTLiRdMDbL2ZSC04fd9Lf5YbVgKMggroGFNbtl9VszrOD8GtgpyUqdQnZ9D2U9ef8sJqRxzWKNT3Sr3A2PxgKiCf8G0yu0016JlmwHeEJQAWs9u+0ZpnYo51eSGQKnKtonW1avH6I95Mqh6cPbA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.2.0
Release Details
UpdatedDec. 17, 2025, 6:10 p.m.
Changelog

Added

  • Measurement & Annotation Visual Sizing Controls: Added per-user sliders to control measurement and annotation visuals
  • New Personal Settings section for adjusting measurement point size, line thickness, and label width
  • New Personal Settings sliders for annotation point size and label width
  • Settings are stored per user via SettingsController and merged into VIEWER_CONFIG.visualSizing
  • Measurement and annotation composables (useMeasurement.js, useAnnotation.js) now derive sizes from configurable percentages of the model size
  • Animation Controls in UI: Added animation play/pause controls to viewer interface
  • Animation play/pause button in MinimalTopBar component
  • Animation controls section in SlideOutToolPanel with play/pause and loop toggle
  • Animation state props passed through App.vue to child components
  • Integration of useAnimation composable in ThreeViewer component
  • Improved directory path extraction for multi-file model loading
  • Dependency Cache Insights: Added live cache statistics (size, entries, hit rate) to the viewer and slide-out tools
  • Viewer performance panel now shows cache size and hit rate
  • Slide-out tools panel displays cache stats and clear-cache control with status
  • Cache stats refresh automatically during use and after model loads or cache clears
  • Cache hit/miss tracking with reset when clearing the cache
  • Performance Scaling for Large Models: Automatic performance mode suggestions and easy mode switching
  • Configurable triangle count thresholds (warn: 500K, strong: 1M faces) for detecting heavy models
  • Automatic toast notification suggesting performance mode for models exceeding thresholds
  • Clickable performance mode label in stats overlay to cycle through modes (Auto → Low → Balanced → High → Ultra)
  • ViewerToolbar performance button now cycles through modes instead of toggling stats
  • Performance mode changes apply immediately with visual feedback
  • Slicer Temp File Security Hardening: Enhanced security for temporary file uploads
  • File size validation: 50MB per file limit, 200MB rolling folder cap
  • MIME type validation for STL files with header checking
  • Rolling 24-hour expiration enforced on file access
  • Increased cleanup frequency from 24h to 6h for faster expiry enforcement
  • Comprehensive audit logging for creation, access, and deletion events
  • Security posture documented in TECHNICAL.md
  • Automated Bundle Budget Enforcement: Enhanced bundle size checking with historical tracking
  • Comprehensive budget thresholds for all major bundles (main, loaders, app, three-core, index, nc-select)
  • Historical size trend tracking in bundle-sizes.json (last 50 builds)
  • CI workflow uploads bundle size history as artifact (90-day retention)
  • Size trend comparison shows changes vs previous build
  • Improved error reporting with formatted bytes and clear failure messages
  • Fails CI builds when budgets are exceeded (with environment variable overrides)
  • Vue 3 Migration Pre-Work: Eliminated Vue 3 incompatible patterns to prepare for future migration
  • Replaced deprecated lifecycle hooks (beforeDestroy → beforeUnmount in ViewerToolbar, ToastContainer, ViewerComponent, viewer-api.js)
  • Added explicit emits declarations to all components (ViewerToolbar, ToastContainer, ViewerComponent, ViewerModal, FileNavigation, FileBrowser)
  • Verified no implicit $listeners usage (removed in Vue 3)
  • Added comprehensive migration notes to COMPOSABLES_API.md with dependency compatibility matrix
  • Audited dependencies: vue 2.7 → 3.x (API compatible), @nextcloud/vue 8.x → 9.x (requires Nextcloud 30+)

Changed

  • Cache Statistics Updates: Improved cache stats refresh frequency from 5 seconds to 2 seconds for more responsive UI
  • Performance Mode Controls: Enhanced performance mode switching with clickable controls
  • Performance mode label in stats overlay is now clickable to cycle modes
  • ViewerToolbar performance button cycles modes (preserves MinimalTopBar toggle behavior)
  • All mode changes apply immediately with proper event propagation
  • Dependencies: Updated core runtime and build tooling
  • three: ^0.181.2 → ^0.182.0 (patch update)
  • vite: ^7.2.6 → ^7.2.7 (dev, patch update)
  • Security Workflow: Updated GitHub Actions CodeQL workflow to use github/codeql-action v4 in preparation for the v3 deprecation in December 2026

Fixed

  • Toast Event Handling: Fixed missing push-toast event handler in App.vue preventing performance suggestion toasts from displaying
  • ViewerModal Performance Mode: Fixed performance mode cycling in ViewerModal component by adding proper prop passing and event handling
  • Model Comparison Positioning: Fixed comparison model positioning issues where the second model's position was immutable
  • Wrapped comparison model in a Group to neutralize baked position offset from loading
  • Fixed parent-child relationship handling by using toRaw() to get actual Three.js objects instead of Vue proxies
  • Ensured matrixAutoUpdate is enabled for wrapper and all children for proper render loop updates
  • Fixed matrix validation order: validate parent matrices before child objects to prevent "Cannot read properties of undefined" errors
  • Improved scene hierarchy validation to include all objects (grid, axes, lights) not just models
  • Fixed matrix update sequence: call updateMatrix() on all objects before updateMatrixWorld() to ensure proper transformations
  • Comparison models now position correctly side-by-side with proper spacing and alignment
  • CSP Compliance for Texture Loading: Fixed Content Security Policy violations when loading GLB/GLTF models with embedded textures in Nextcloud modal viewer
  • Patched Image.prototype.src setter to automatically convert blob URLs to data URIs for texture loading
  • Patched URL.createObjectURL to track blob-to-URL mappings for later conversion
  • Patched fetch() and XMLHttpRequest to intercept blob URLs and convert them to data URIs
  • Patched THREE.FileLoader to handle blob URL conversion for texture resources
  • Automatic detection of modal viewer context (iframe or Nextcloud viewer) to apply CSP workarounds only when needed
  • All patches are automatically restored after model loading completes
  • CSP Compliance for Buffer Loading: Fixed CSP violations when loading GLTF files with external .bin buffer files
  • Updated setupResourceManager to use data URIs for buffers in modal context (instead of blob URLs)
  • Patched fetch() to intercept data URI requests and decode them manually, bypassing CSP restrictions
  • Supports both base64 and URL-encoded data URIs for maximum compatibility
  • Animation Support for Multi-File GLTF: Fixed missing animation initialization for GLTF files loaded with external dependencies
  • Added animation detection and initialization to loadModelWithFiles function
  • Animations now properly initialize when loading GLTF files with external .bin files
  • Animation controls now appear correctly for animated multi-file GLTF models
  • False Texture Warning: Removed automatic texture warning banner that was incorrectly showing for all GLB/GLTF files
  • Warning now only appears when actual CSP errors or texture loading failures are detected
  • Improved user experience by eliminating false warnings when textures load successfully
  • Slicer Upload Safety: Hardened temporary STL upload handling with size and MIME validation
  • Reject uploads over 50 MB per file and enforce a 200 MB rolling temp folder cap
  • Validate STL MIME/header before accepting; reject invalid content
  • Enforce rolling 24h expiration on access and clean up expired shares/files
  • Use rolling +1 day expiration for generated share links
  • OBJ Texture Loading Robustness: Improved OBJ/MTL parsing and texture handling
  • Preserve texture/MTL paths with spaces and mark materials for update after textures load
  • Use existing blob File objects for texture URLs and tighten loader logging
  • Handle texture load failures gracefully and ensure needsUpdate flags are set
  • Texture Dependency Lookup: Skip direct “find by path” lookups for image textures to avoid unnecessary 404s; rely on directory listings for textures commonly stored in subfolders
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signatureacd0z9v0ifd6EhLDkZPQmIADZp+TPcgSOqKKp6W5KUtq4CA/Xk0lTNssZpirrEaLO4s15qemVCz87Aa6hhxGipOFlBSMFIyc1U0q8OQzHMOa4cYU8w1hvmi7aXeG+IxPWXelCAYaBNYGbleOvhc+/ao1gh1mn+3esakeTLdBi8XihNAKBDzglkOD30oBunXuzXJD1chBFBx7LCMoRhVXHDxShzi39+vbumkbme4IGV9kL323hTSjD1+XrMeiIG2P9tvHDEkGRXEOKohmWQNrsFCamApVqslvRzg6mBFTFv+dtnBUFbbQnC25IvyE+UNY4j2TGzncrW2+jWGddicKYq4SIrI7RzB91gTxv+3qzwCUb8OlyaU38L042l4HJXSFMPh8Mmj2n/0zhWlH4M2h/ZEPjySsaK8lU/x/DaBoyLcPoJcDsuD1haONv6mpRsoz/ohXdV/U9s61rbGOnJkSqMMblFoVXVmkzTKpBatf08e9Uaig21b9q/ekYyhbVFlujmAu9ku9EWk5SddY26zNvSeiGinV1xV3BayMI3rVZYINZkPb72Dv6DOInRWlVOxVqLQBUY98ejlhpls+6l3IdtTDUmrITmGr+Nq3b8T8kgNJpgNQbf3Ij14MgdfNsxd4nxMyX/4hsOSZdU4dAHsTfatSxRgM1q9FpuvZ+OkyWMA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.1.0
Release Details
UpdatedDec. 6, 2025, 10:21 a.m.
Changelog

Added

  • File Browser Default View Setting: Added user preference for default file browser view mode (Grid or List)
  • New setting in Personal Settings → File Browser → Default View
  • FileBrowser component now loads and respects the default view from user settings
  • Setting takes precedence over localStorage, ensuring consistent default behavior
  • Manual view changes are still saved to localStorage for session persistence
  • Format Sync Test Suite: Created comprehensive unit tests (tests/unit/Service/FormatSyncTest.php) to ensure format definitions stay synchronized across:
  • Backend PHP constants (lib/Constants/SupportedFormats.php)
  • Frontend configuration (src/config/viewer-config.js)
  • Nextcloud MIME registration (appinfo/mimetypemapping.json)
  • File Browser List View: Added ability to toggle between grid and list views in file browser

Changed

  • Format Definitions Centralized: Consolidated all 3D model format definitions into lib/Constants/SupportedFormats.php as single source of truth
  • EXT_MIME_MAP for extension to MIME type mappings
  • CONTENT_TYPE_MAP for file streaming content types
  • All repair steps and services now reference centralized constants
  • Eliminates format definition divergence between components
  • File Browser Grid Padding: Updated file grid padding to consistent 20px on all sides for better visual spacing

Documentation

  • Corrected repository URLs and upstream fork instructions in CONTRIBUTING.md (replaced placeholders with maz1987in/3Dviewer-Nextcloud).
  • Updated TECHNICAL.md with new controllers (SettingsController, SlicerController), components (PersonalSettings.vue, SlicerModal.vue), and detailed Personal Settings + File Browser implementation sections.
  • Added comprehensive "Adding a New Format" guide in TECHNICAL.md with step-by-step instructions and code examples
  • Expanded IMPLEMENTATION.md: added Slicer Integration & Personal Settings System sections; reorganized and deduplicated legacy "Code Audit and Cleanup" content; refreshed Table of Contents.
  • Updated README.md (docs version) advanced features list to include Slicer Integration and Personal Settings.
  • Added troubleshooting sections for Slicer Integration and Personal Settings in TROUBLESHOOTING.md.
  • Expanded test coverage notes in TESTING.md to include new controllers (Settings/Slicer) and components (PersonalSettings/SlicerModal).
  • Normalized wording and removed outdated dual-mode duplication in implementation documentation.
  • Documented dual-mode viewer architecture in TECHNICAL.md with viewer lifecycle diagram (standalone vs modal modes).

Fixed

  • Settings page image/logo path resolution: replaced hardcoded asset URL with imagePath() helper in PersonalSettings.vue to ensure correct loading under all deployment paths.
  • VRML preprocessing duplication: Removed duplicate preprocessing code in preprocessVrmlText() that was applying BOM removal, line ending normalization, and null byte removal twice, causing inconsistent preprocessing behavior.
  • Flexible texture matching loop control: Fixed nested loop control flow in texture matching logic (multiFileHelpers.js) by adding foundMatch flag to properly exit outer loop when match is found in inner loop, preventing valid texture matches from being skipped.
  • Premature texture issue check: Moved checkForTextureIssues() setTimeout call in ViewerComponent.vue to execute after model successfully loads and is added to scene, ensuring accurate texture loading status assessment.
  • Debug logging cleanup: Removed console.log statements from FileBrowser.vue component (viewMode watcher and setViewMode method) to improve production code quality.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureatLgIGKAv/DO42HgoDbqVaos4kWPFuPmF5l4PHyM5S5ivOlmqnC5xu1+d5uX/h+0rUmL+pG6wKezHo7WT0PVKSBciYZzlb5D+6sJisB5x/jMVqFaQRmnEEPKfoz2DsDucmXtH6Ppv3oXjyKd5wDjyFMwtoG1hU43QBeA4ngYTMTCXqdITK5sT4OM4IwiYhUi7zZRh6/YoRxgojXIqo/RasM3sGIUIrQs075RKqXcdGcGUwJgQPAQnGsMEW9D0LODp6bYurgBZRgnmgBrpTE3D7Ktt1G+ppQTSW4tHpuq3mFJdhFlkbhbWJw9QW28xJmDnqUGfhzXTSMqoJLnbio0n7iT5bhiyxBmhi5patzHifmDwndpRPoZNbw7vxr2zzqCc+1pMt/CoXHn1PTthmYtQMg9tvyjS7jbCdYtDYLtluzITRHP8GzRXIMmBrCRzXLymk0b3IvwyB9G83+QwzHyWSTLsKV314CSasumGhFDSxnz0E2DhVVZV7yaG89Dj9HHwmxkQVeh4iI9DCd5ynu6q/8Ljyp4nJ1p//+3WCo3NksaM0ubqkXiVFnSTnSjsoDw8mbP8TU/iESpYJ11llVA9ftZSGnnczY2zFKKK17C5m/pjE9sskLlo2bfhBzRLvYOV8ejRyaXPK1B6ejCfk2VwWntq7UKFh/+/1R5usmQkRg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 2.0.0
Release Details
UpdatedDec. 2, 2025, 2:53 a.m.
Changelog

Added

  • Personal Settings: Added personal settings page for user-specific preferences
  • SettingsController and PersonalSettings view for managing user preferences
  • Settings routes and configuration updates
  • Enhanced File Loaders: Significantly improved loader capabilities
  • Enhanced FBX loader with additional features and better support
  • Improved VRML loader with expanded capabilities
  • Updated DAE loader for better compatibility
  • Enhanced multi-file loading helpers for improved dependency resolution

Changed

  • Viewer Enhancements: Enhanced ThreeViewer component with improved controls and features
  • Camera Improvements: Updated camera composable with additional functionality
  • Circular Controller: Enhanced circular controller with better user experience
  • Theme and Performance: Updated theme and performance composables
  • Major Version Bump: Version 2.0.0 introduces significant improvements and new features

Technical

  • Updated GitHub workflows with improved condition syntax
  • Updated Dependabot timezone to Asia/Muscat
  • Added change detection to prevent unnecessary PRs in workflows
  • Updated stylelint to 16.26.1
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureJrtVI0UiQsSqL82PvCmO+FLp06iE6caUY8nD1+dKlVr5YjeTvZjLUtd0sAbmwQzlR2iC4IuRJllv7hr5cWk8oWuJtnLuE4uzcXu3bu8ndzO/Zyf4ph65Fn3GyML8Zdm6TqECBFCzk+2NG0pVxt4TM7qML5p437Q6IrF0+uOh3Vj55CTf160KAjEN7v2Sa8CT4Qf/JPKtnfyAgnKkNRXMQCf7xXe3RqtgtiVPnNq6nKfYzERoihIHhZ4LqLewUfY7jvxKUpAIxF89K+yTk1PmsJgKPBN88FF3ReKwcfJcfIhMXuH7CJEHCg++Hbd0oxzvl3uv0o8k5USdkojH0vG5IQaBF8AAEGY0aKCsScmffSN7WvmpPngsHs56V/olPNPVG1QNepRvSvBljr9D6idShiRlaW7uOBl9SVwLernLBjZ30bfqRtdrm2T7oO6gozY0zRc3jxt+ajAuaPLp8pAKC/Bv8nt3Og7R3r/mpk7qXWEgC/u78v/LqjhtICx1CQDSROq8vuces8k4iEN1kJabrhxLFPDPmZucoXWSzUStP+ISyPksq+pk5KAtnKuVRMTN8rhYkMlyK1790OOF53Q1bvlHnTc3LmzTLi6b/mVPBtz+KgkHHoU8j4TYwSlnbmSx6gDqzrQa8q+cRXlK1NNmF211NX0J8B9nRkng+xHg5ts=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.8
Release Details
UpdatedNov. 28, 2025, 6:24 a.m.
Changelog

Changed

  • Dependencies: Updated development and runtime dependencies
  • three: ^0.181.1 → ^0.181.2 (patch update)
  • stylelint: ^16.25.0 → ^16.26.0 (dev)
  • vite: ^7.2.2 → ^7.2.4 (dev)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureMKCHrKGV1G4CZgG/gyhGvg34zK0migxfZ/FS9ZZEylzffGxQxoQbW+wubOR3BAjgWRx5smOBIdKVeLLuPOV00JnPgrII3lRaltLVO72nNrYtVh5DruKC5ZoMM4BuJsdEOFP8euy/GDCmlhQYNTusS/T+meggfylcrAjZAWR+SZZfb4x7QlBJTdmCuWpHNBJO081IpeLfn5HXohFAWm6sHHm6mun4ar6znAlwsZ/pe6Lo8ZjzLBuDN9mLEujrw6AgjTq2DTSUrHrJ9/WQF3zBiYjRLMNurJLnrwCXuUmPkKGWrPM3ybFADqfgb3QmqzPsPiGUL87k/1yVewEqE2olFpVRZ9hxa11ANingG+zMZwY1bCbzIcrRCq4Uoae7zgwZV/CF2wJQk+cRMbQl7Y7AoAy6PPECLMFtU93VgHqcPtFQzAxSPEXRNW+GpkVFHWvBZ7iuL6Ys8gMjl9i9imuNYcIAkuLlKLmu8t5pQpOLKvNpiQkeIf5aWXHm5Fvq7+zmeKIsuMVNvPoqQOSlC1AkwnLi1EAf/2VrqbAmDoC/o3Y6h1mg6vskUNFRPhz2h37TEb65h1bm9kFjNx/6TNNF0xM96GSAt7v+I1naBODhvGqDqyJ653663yuE+CxdKd1kZ4ous3urqXRwAmKHVdOvXYZTb+N/Feu7PHqrdcwlHHQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.7
Release Details
UpdatedNov. 27, 2025, 5:42 a.m.
Changelog

Added

  • AnycubicSlicer Support: Added integration for AnycubicSlicer with custom icon and URL scheme. (#52)
  • Folder Exclusion: Added support for .no3d marker file to exclude specific folders from 3D file scanning.
  • Hidden Folder Exclusion: Automatically exclude hidden folders (starting with .) from the file index.
  • Temp File Cleanup: Implemented background job to automatically clean up .3dviewer_temp files older than 24 hours.

Fixed

  • Layout Issue: Fixed white empty space when hiding the navigation sidebar by ensuring correct flexbox behavior and explicit slot usage.
  • Viewer Resizing: Fixed 3D canvas visual resizing issue by syncing internal resolution with CSS dimensions (width: 100%).
  • Slicer Icons: Fixed missing slicer icons by using the correct imagePath helper for asset URLs.
  • "By Folder" Navigation: improved folder indexing logic to correctly build hierarchy and handle edge cases.
  • Server Error: Resolved persistent preg_match error in PreviewManager by disabling unused app preview provider registration.
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureLfmZ7uqqVk6tDgQQuwcGuzQml5OZWmFvpfNYW9Nd+3gVtIRUCBKsgtB8D6CMf1ymq2JxllnMvhATHE3YVZGmCsL5CRrj6al9+zdVnP8An2BUBP/DsqDTTwLM64mwKFIqYYkjtwVfttaa/aPJn2cdH7umzjOEHsYzbRx7eiP1lq2ERy4KYs7aA26hfleg9kKbr1jKTf0/vPCHvmbKg+o3RS+QfP/lRuoPjk1UYog52IAHd9bpAtAgmB2yLSfghjPSvMhZkyWt38CKMiKyO2R+VdB6wc/lWWVWq1JfJDw9oZlhaMpBZhXVDqR7uoMYdtR2pTNhB6d+kaTQtclvLSO5RREUDMwdocOl7iVMwrXCmKmUIyDqMa/VF3FFtK31fKlSYuvIhGDi1He0NNqlv2jiMsb2EPhrYLBHTyrmY0Px3i+ZmbYos1sFXLi7CliafZY/nZwxqPSc8xsZI42MFO9ZWA7yBKYE6UHphNnbxTbx5H33NrwHdRhOh0g+wpLJebbjyi1momlshADpfec+gAImcCnyaXhIX0Vpt73R1DmadH0xbi37iCEg73s9h6cul1RGeRhOzr+c8nhuFOUq4wodj9VLbTwwqm+bjd1jvshyo0++A0Nhjfj79phnnIJl0xsKU9QaN1eFlB6vPQRMyFswy6bWW4IW/yI2KL7693JFhRE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.6
Release Details
UpdatedNov. 20, 2025, 12:48 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
  • Folder Path Length: Removed the 512-character limit by hashing folder paths for indexing
  • Restored folder_path to TEXT and added a folder_path_hash column with a new migration
  • Existing rows are backfilled automatically so deep folder structures continue to work
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature4F3B+MMtZoJiTGIBdBSaJDW9PVnv2o38ob0m+f5cIJmMM6jpgqYETLsQaW1zRCdQ8ozE8v5LfkehmhMlXy8folvlapuRcDGh9N7PPIRzTM/8b9ymOMQ1vkBPERw8k5grkUwFNamMPw+K4rFGHLMflN+vR/S6m0jmAKls/2L4kg2UdhaTdtaw/ozZAM4a09dJ038kyBS6jbh6g/M233Ue/R6aXSCMVgrA5zw38djqE9O8zmUg8xOJlOzdMRCRdhKWZOHoocLV9TBTasdU99YG7o+VIgAXeTY4X0rKxtOdfqdX+c+YNEt4syMmig/1p3e3yO1q0CmQPg6rgxtrdVOW1KK6Bbsvd8DkNwyQ0SgREq8bD+SIlF3QfqTDAFKCDgjTCn9N7HBnZIVa+NtseVrVfVeEdWdVNGMnfu9QhSeMqrE5dJMzKJLiPy4ftrmzM+aff+3aoQU69wWVffIBw1RsC3+352E90jiYw8CnH0ul8I6ffA+NVD4v8+Jo2MruxzwzTsq7/RfQMvIhsCTrhyauECtmwAQPEx9CX4h+7nryjxefgDwPQlFyszt6F53LSTWEeFO49SAlV9dH5P6tsjsMozdUts7HtG+UU+T7gHD65rY/nPL8ICF6C5YHGuuxwDFQGAkKtBNN/9VWBZr8h1X9jq1o57hwIqGBnxNdAs9YqdA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.5
Release Details
UpdatedNov. 20, 2025, 10:08 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturefF9sWdsS9s2UfdNuMWzSK76VLnucCn+KchXB9eFwbDsdXACnQYvhUVvS+xoMBYIfaXQEYUjfIl9oc0/sghPaIfdPvHwhCSze3XUPHkeF8yHb7eXQfHPr8N6KQZdmFM50brxxpHt5yxI40xZU+mMyW3eTP982e+aLenLEr2taQYNoaTHf35NbVknGLAE1xautWR1JQzYyVkCcc4T9/hxMp7LaGBrUPwdDALyuAp6WTHtZk+XeOxxxwRiNb7q7SpXWGuwdbX7MDvEzfBUmhw8dbORK3kqxgfXFAe7uGVdW+5sKtkdn+fxuN4gHKmBwW8N4X7YW1XaAL0KkYFoHsrdAcHunsSn/KfuqhTLHRpxOblgFPEcCwwqWcPVdGASFkD96rYylV9yzoJV2zS/pDqMVG38TQjHOs6ukBbJ5KHzpGJpgOZXDzLn24or5wVubjXkCyOsfS0by89VmrrxrERJZPNl6XTgeCZaBTPsypeWAlezfqVEjVVmdkoKuL+I3m/gMURucZV3Do+wHmxoEAT/2bTRVtIZzBsjrwuljZk4cok0oQ8reFBlngACWmXWskJOiEmshDUpnd2IykjhaU6LPEBDprd0fSM3+RE6Sx1+iJuPdXQ+CUATytcbKc8kiEjcPapzQ7xt3uR2fukG3eLrkFKoKHM7jJtWWMcWmN0yppuQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.4
Release Details
UpdatedNov. 20, 2025, 2:09 a.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
  • Migration Class Declaration: Fixed missing class declaration in migration file
  • Added class Version010902Date20251116061241 extends SimpleMigrationStep declaration
  • Resolves syntax error: "unexpected token \"public\", expecting end of file"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYu4MvwQG1iiUU+ZnS3qdmTsCZ0NYAx89VzPzGTNqkdgQ/LolnwTZNDpkHEOoGQOM7Q95wtrJ1iHj2KQNxHsg/nw26/L0nqQtE8+HN92EVkjKhj4Chve7AB+A8dFlj0nXdSpgtH/PbwN+90lQTCGnmd6rRc46+OqEfEoFzjBhqtrpzXuURKsZG4GV644vSvAyzZFBDncgI7tlGhUhCDj/5UShHQy9ClymB6Yb5K7smP0BSDEWYOxsUOj2/cy7hAIlCyofINC8p6UzA6Po6NiyQefX2zds56a8TZhvvuaeiO3setCQzKXdYp3WqlnVVSpRA7UZfGeqdvkDqtQuTIs4Zo9w6GWX9M+N2Z15rQ59t/cNX0OYhCs+Fw2UdJMAPYW+me911YHsaMmANkaw7sUZ0ncqxbc0NZ8NULhJakF4DHizsCSLZbVrOhpfcaDEpsy6q88bg6o4modcj+gTIytUCI/RLjTlKWYAZcRFJfmPngQiuUmBeZntR2y1a0ROFsmcRAaSys5UFHF1KuqaNocqcAo+CbN5vRqMQXnmGSg54kEHvQ6xjDki/rs/0/5sxIX5TrGhGCyubdpN5Avv1y+eZfMdxVrZu0wxOubN3zgF47b/O5wql0Mv2tpFH88gy5XMGRTyaB1fIeFQXV874btSvkBwJPJhKdTZV0pyA9C+wUk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.3
Release Details
UpdatedNov. 19, 2025, 5:36 p.m.
Changelog

Fixed

  • Preview Provider Registration: Fixed ArgumentCountError during app service registration
  • registerPreviewProvider() requires 2 arguments (MIME type and provider class)
  • Now registers ModelPreviewProvider for each supported MIME type individually
  • Resolves error: "Too few arguments to function registerPreviewProvider(), 1 passed and exactly 2 expected"
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureYmgaFcAnfaw/gFhMAFJupT22RE/RWydQkyTNQrbQNyHQGq406C58CN9hKEIJ5kaSzibif3goqa31T/si8GlYBnejJmQzYsQpr5Wua0dSrkUtCv0fRFv5GNWJvazyREN7XKWp+Ir/Q51J7trkGs5RCuZRnH1DhHTPyieLyZLTFsSWTSPuDRHEnpqr91B0y+epvQGujYSYG931+5tc6QaX1rfZ9kmT1qNph4SdaLeMa4hbiSFc7fuuSSNqvE8q1QPrBJEz5TMixrr1U8iMA38HBWVsufDfd+lg18bVWg3zQG1yFNZn/RYCuPck6DPiDo2uJ/9AJzOTQpJEFjDfc2zG77oE7EufwDtoaJzbhXhiseW7u0Ty6nVywrYTnEbfuIYhHeo6lbxY8KtoPLZSDSiSbH73oigsGW7o7xA+RDjtVZoWRguPZF9VfUjJ1v5WQpHD0jdZflYJ3byMygWF1z7GaT8wE9gbzwvWUyMGE6XCpzwpNxx3yOlq0mp5aUNFAjJYAO8BSgaFBxSBVnXTv22JEnhaUPAWL3HaMh3EJLVJla/TOG7cd7N9eS6JT3maO8Z7QUu32B/ah48w/uxk9qzmBX+t1onXwUA/6CuY8Gfhcg20AK5JuUiqhIZD1eW7HiC0pNJJqZbzLbXBl049JBFUqZbKLdf+ttISPjCmlRTnxdg=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.2
Release Details
UpdatedNov. 19, 2025, 5:32 p.m.
Changelog

Added

  • Database-Backed File Indexing: New tv_file_index database table for fast folder, type, date, and favorites navigation
  • Automatic indexing via filesystem event listeners (NodeCreated, NodeWritten, NodeDeleted)
  • Manual reindexing via php occ threedviewer:index-files [userId] command or /apps/threedviewer/api/files/index endpoint
  • Migration automatically creates the index table on upgrade
  • Smart File Browser: Complete file navigation system with multiple view modes
  • Viewer mode: Opens 3D viewer by default on app load
  • Folders mode: Hierarchical folder navigation with recursive folder structure
  • Type mode: Browse files grouped by extension (GLB, GLTF, OBJ, etc.)
  • Date mode: Browse files organized by year and month
  • Favorites mode: View all favorited 3D files using Nextcloud system tags
  • Breadcrumb navigation for easy navigation back through folder/type/date hierarchies
  • Consistent card-based UI for folders, types, dates, and files
  • Per-User Configuration: Remembers user preferences via ConfigController
  • Saves preferred sort mode (viewer/folders/type/date/favorites)
  • Remembers last opened file ID for session persistence
  • Mobile experience: automatically hides the circular 3D controller when the viewer detects a small/mobile viewport, preventing overlap with the canvas controls.

Changed

  • Viewer opens by default on app load; the file browser now appears only when a user explicitly selects a navigation mode.
  • GET /apps/threedviewer/api/files/list now serves hierarchical payloads from the database index (folders, types, dates, favorites) instead of scanning filesystem
  • Supports includeDependencies=1 parameter to return all files including textures and nested subfolders for multi-file model loading
  • Dramatically reduces filesystem scans and improves performance
  • Navigation data is loaded lazily per sort mode and cached so switching between viewer and browser modes no longer blocks on loading every file upfront.
  • File browser UI refinements:
  • File cards now share the same compact layout as folder cards (consistent padding, thumbnail sizing, fonts, and grid spacing).
  • Type view heading and breadcrumbs no longer show a leading dot (e.g. GLB instead of .GLB).
  • Breadcrumb component now handles clicks directly via NcBreadcrumb to improve reliability.
  • Remembered folder/type state is cleared when returning to the root via breadcrumbs to ensure a fresh reload.

Fixed

  • Newly uploaded, edited, or deleted 3D files (and favorites) appear instantly in every navigation mode because the indexing listener reacts to filesystem events instead of relying on manual rescans.
  • Root breadcrumb ("Home") navigation restores the folder list correctly, even after drilling into nested folders.
  • Multi-file dependency loading:
  • Backend listFiles now supports includeDependencies=1 to return every file (including textures) and nested subfolders.
  • The dependency crawler recursively searches texture subdirectories so 3DS/FBX models with textured assets load successfully.
  • Texture search now uses the updated backend response structure to avoid missing files and 404 fetches.

Technical

  • Created lib/Db/FileIndex.php and lib/Db/FileIndexMapper.php for database operations
  • Created lib/Service/FileIndexService.php for indexing logic
  • Created lib/Listener/FileIndexListener.php for automatic index updates
  • Created lib/Command/IndexFiles.php for manual reindexing command
  • Created lib/Controller/ConfigController.php for user preference storage
  • Created lib/Migration/Version010902Date20251116061241.php for database schema migration
  • Created src/components/FileNavigation.vue and src/components/FileBrowser.vue for new navigation UI
  • Updated lib/Controller/FileController.php with new listFiles() and indexFiles() endpoints
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signature5KdirqBqYm1NTouEBce9EtmTO6/UJwHsg40DC/oCvhIuYOF8eLVWy5O5Vwx25hFvq7bHrr9SQQ9cdJS1G+fY8aQ7bSeqCR1dAdUlsoBhrYWQnKAEnoG8vIIxfk0gA+HgcRGLJPtph9TYLdeAqdhD7/VVUg8l9ZdIVJHsTQn8JijVkTeMpk5612wUyoMn7HaA8IJJrOnU6dmkfv95FcZbVLzR6QRDc2fqlM3aYbZl9KOVYzEU6e736rsQAOVn0IqrHmQyJNVIdiFnycbtRiJnJgRJj05KblfVIOC0qZwzl7sqIHdAswvYwDmdCoLK/rblTc8R7INoUFpmGFBzPITfVwBNrJh9jsxxuWnl0X/vAAtVACVGHR1utLtd4dMFSjwOVxD3f53eazH9/Hho18BBh3GOLNJEWAGNal3u8Ng+inilqQu4Nmhds9AYUoq6T38oU9NABr7mDce1TsO0AYQqfez3HBodgbbBOoXKSGLMoDCk9T71NR2Oc2XC8Urz08F9Q/0Dcoy06kdvm9neAOH+yHqjcPXfI98JCdqao6Li05VQaLOUxIyYbVpngGu3LWthpaH7rlxhD5DlOwknjimFfGifjZ0/yScHSbfRqtgxy8q8jfHJ71wMKOAzQ6xCgqfuWWROUqWNmo4ypsE23Ioj1n/yEA60bComBBgSdSjCwpA=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.1
Release Details
UpdatedNov. 15, 2025, 9:35 a.m.
Changelog

Added

  • Preview Provider Implementation: Implemented Nextcloud IPreviewProvider interface for 3D model previews
  • Admins can enable/disable via enabledPreviewProviders config in config/config.php
  • Integrates with Nextcloud's native preview system
  • When enabled, provider is registered and ready for future preview rendering implementation
  • When disabled, Nextcloud automatically uses custom filetype SVG icons

Changed

  • Updated dependencies:
  • three: ^0.181.0 → ^0.181.1 (patch update)
  • @nextcloud/router: ^3.0.1 → ^3.1.0 (minor update)
  • vite: ^7.1.12 → ^7.2.2 (patch update)
  • @nextcloud/browserslist-config: ^3.1.1 → ^3.1.2 (patch update)
  • Improved duplicate registration prevention:
  • Added guards to prevent duplicate file action registration
  • Added guards to prevent duplicate viewer handler registration
  • Enhanced error handling with try-catch blocks

Removed

  • ThumbnailController: Removed custom thumbnail controller endpoint
  • Replaced by proper Nextcloud IPreviewProvider implementation
  • No longer needed as Nextcloud handles previews natively
  • Thumbnail Placeholder: Removed dependency on thumbnail-placeholder.png
  • Nextcloud automatically uses custom filetype icons when previews are disabled
  • Custom icons already registered via mimetypemapping.json
  • CSS Thumbnail Overrides: Removed CSS rules that forced app.svg background on thumbnails
  • Allows Nextcloud's preview system to work properly
  • Custom filetype icons display correctly when previews are unavailable

Fixed

  • Duplicate Registration Warnings: Fixed console warnings about duplicate settings/registrations
  • Added registration guards using window/globalThis flags
  • Improved handler registration checks
  • Better error handling for duplicate registrations

Technical

  • Created lib/Preview/ModelPreviewProvider.php implementing IPreviewProvider
  • Registered preview provider in Application.php bootstrap
  • Removed THUMBNAIL endpoint from constants and API documentation
  • Updated openapi.json with preview provider documentation
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureyK5Dmfw2bZsjqJMtY0eaIkST1jQZ4XKcPtlHp4f7GNHWV1A5PrKjkxKHO32JML49YXEI7D8PouQvnHXDq55pRDm/PBmXV3cB5wvx3ZkMVMGRQBqHNR2iBfSsl6w9I0SaGSgNSt/aa1kmZRCq20GPv8hLMHSJdcR2VpU5mA/BLp6vP51LBp9wDwzCEsU82zX6VVBXkd5YIHY+6HMRsNQNXFfRsWERTq5yjmTYfyK4Zq1rvZShPX3KG8WDWl6g3C1lJhyr/7FWLoM7Lsq6k1pirdAYUtQO7ooB0UDiTpvEgmCj/OLhL/8dvaD29dyzUxnokjj8IlpiDEcPfeLN34nqXHfMbmPZh9+aqBqsZr0GaqSD6dXGoplkBc+F/SHfWdevkZDHk8p/W0flcjIThr+K5/8YhwdDZd3G2JIRmhDdJXFfoTn2f+zYkHTV3enXsRAG95FVU3WE6zZwi8w4MOUiXIpxqb/DAISRSId3k24iPFlbguOQwPvcpMGNIruPRkoE7ihCgiU+dvcQ6ieYtwvtAHgNy+N284BdcsK7/0g5tCkzDad8Fdt4pMImPkO+cWBy3c19Uce5+pBx36Nts6q5so6O96s0Dcnf4NQPsnRFHNKduWxafeoBHCwn2NoH2aIRw0JQcwyCLvL2j8vLcN6PhAQwJ2I6P/4yHX5aEWEwaP8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.9.0
Release Details
UpdatedNov. 10, 2025, 6:57 a.m.
Changelog

Added

  • 🖨️ Slicer Integration: Send 3D models directly to slicer applications for 3D printing
  • Support for PrusaSlicer, UltiMaker Cura, BambuStudio, OrcaSlicer, Simplify3D, and Eufy Studio
  • One-click export with URL scheme integration
  • Automatic STL conversion and temporary share link creation
  • Professional slicer logos with brand-matched colors
  • Last used slicer appears first for quick access
  • Smart detection of uninstalled slicers with user-friendly error messages
  • Auto-download fallback when slicer app is not registered
  • Temporary file cleanup after 2 minutes
  • Share links expire after 24 hours for security
  • SlicerController API: Backend controller for handling slicer exports
  • POST /api/slicer/temp - Upload STL and create temporary share link
  • GET /api/slicer/temp/{fileId} - Download temporary file
  • DELETE /api/slicer/temp/{fileId} - Delete temporary file and share
  • Automatic cleanup of old temporary files
  • Proper filename sanitization for paths and special characters
  • CORS headers for slicer application compatibility

Changed

  • Updated app version to 1.9.0
  • Enhanced toolbar with "Send to Slicer" button
  • Added slicer integration to slide-out tools panel
  • Improved error handling with toast notifications
  • Updated translations for all supported languages

Technical

  • Created appinfo/routes.php for route registration
  • Added @NoCSRFRequired annotations for API endpoints
  • Implemented Nextcloud native share system for temporary URLs
  • Fixed filename handling for files with paths and special characters
  • Added proper authentication and cleanup mechanisms
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebdu+2mjB9NyEPJbbogKcFBovDi99alBx7ixiNLiVWRmMw63yphLItjrEkmMxHL5KeEmkQj05gqf17mPXlngrWI3pkhn1gLHtbCiPwOqloKdQ+bf0US34nClshbCzP0drRsNWSxhEWbd0a0vc1Zy31vk9I2PVHugbpbo4BCy20Ks6t0aCkv6o+g8x+v+B348txV1p1F5hyM+2BBUqxEB8fNiRJ/p6bd1eMf6lWfeqBrWG6n3zB11BsKKYAa941deyJ8vLlEpI8D7Kyz7TVHuHzx9hlxbZyOCYEnD52EmpYDl/VaKnj+9EzxaFDqkQPOIHPbMtfe9tc0nZOrqEASXrUC/iPMpp1JNAyRa0Egx+DkdRE/p6wFMBxipGqfgb7MJ6gV4I7t7ML1scquVtcc8aZt3oNhdMd6gPZ7/uxOYQ8Bm6YX6fiuwxtqe5yb2uwiw9oKjQhs3miEXEAiaiab3S4HLC6Ke9QYowEzASoAD1tHf+kf4I6+IszL5V6XOVzXRhKU8t/Vdl/JSZh3LEOma2KCPBoxIwove9PpHARcEIz6Tk4eu4UvMdTjhyIBKCvEnq8Bs5fzaq+jqSuE+8h3Y+qP0Q2fSqcNHpLtBEAfrkSu883dCM7dg0miDxSGumfqn+oJ80bVDGm8mQ3WdP7AvJiygdj5i2pFqyeiPE+MGDDUE=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.8.0
Release Details
UpdatedNov. 6, 2025, 2:21 a.m.
Changelog

Added

  • Screenshot Feature: Capture high-quality screenshots of 3D models directly from the viewer
  • PNG and JPEG format support with configurable quality
  • Automatic filename generation with timestamp
  • Accessible from toolbar and tools panel
  • Download screenshots directly to local device
  • Fixed WebGL renderer configuration to enable screenshot capture (preserveDrawingBuffer: true)
  • Billboard Text Labels: Annotation and measurement text now always faces the camera
  • Text remains readable from any viewing angle
  • No more reversed/mirrored text when viewing from behind
  • Smooth rotation as camera moves around the model
  • Improved user experience for annotations and measurements

Changed

  • Updated app version to 1.8.0
  • Enhanced info.xml with new feature descriptions
  • Updated English translations for screenshot and billboard features
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
Signaturebvx0cRDWADLGiuJ0jIg9f+81Add0o7SM3Dkq8foP/AnLfM2fSL1/ds6JsckXbP8xdOMoPyAxnN9XFi6qckR+cTiBSkkbpbQeg32ZDShLwwVRH8dubBAHkIk7qSBVjw4gV2BdKuMjWZZ5cRPlcFNUtdVP776NXH6Vv5FxK4UoudEN3VA8qDZC9+8fC46n7NhnobbviHIzeNqPA0V3Wo50UuuptyDdip0o/bbBiaw1vszWuV4QoHFMHAjFan2v3kKb5ptIHYukVOLMj8rY1dKeTACQYjEwi+YXOV+4eJMiMrHXx06eMYxiOsTdNHsCWTy9PW+4QTHM9dI/GNygSKC/V4Z9lxlPt7jncHOAT5P+tEFrWQYImvBtWsXPXoP6UPAIpP82NPWhp0lBZdjR3Fvx4yi0PM7V4/lRTztUkTrobnlVNpIxSe6SH0jWmVlyAFAW0XSiWrWbM+Q0gNEERVNudpCw7y41xv2Q45ZgPIMhGzjn7CbY8gVacSrXcsnglD8pcFAnI7hh+KfepgSSeJg1ZV7IF5UM9Qr9Cu7VDtq8Lc5bw3tqXNDf4U7T5UgnAjbA/GFXY2fz6yT5TVXWoNxER4d3Bocb73r5wWYlag6z2CKOxTOWenRUnrG0iFPi95MxaykgJMVTAilRLoHOOGZie9DHpOUwcvXWAokeDaXGBSQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.13
Release Details
UpdatedNov. 5, 2025, 4:44 p.m.
Changelog

Fixed

  • CSP Conflicts: Removed global CSP listener that was breaking other Nextcloud apps (Memories, etc.)
  • File Icons: Fixed custom file type icons not displaying by copying them to correct location
  • App Compatibility: CSP modifications now only apply to 3D viewer routes, allowing other apps to function normally

Changed

  • CSP headers now scoped to specific 3D viewer routes instead of globally
  • Added automatic icon copying during build process (scripts/copy-icons.mjs)
  • Removed lib/Listener/CspListener.php (no longer needed)
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureqfnNbykFms5wS6aOa/TKBYua37o3OqBNqe9LY0dU86/MrGrqbv+v9inkFm63UQyR19ux9ACVlqG6J5rCDpLLTYAe1IkFGZMKfJzNnna8uiA734PltS8gNSv8ol0zMsRs6tvanPlDxVedCmzp31VsErPKOShjvTRbhf9ZmgJjZccX3UODdfIzZxJLqHd3vBrYqoksFXDZnzUqEkb5OK7RwfxAs71IGoVjXHOf9HuOVuefc1mhElbIygGnPYJ3ewlU72SD6gB9/kYgmZEb9xklvcTz6IjTbfKdZ7fLb640/71wCIa5mYleBkaxsoAzfGo2QjC6GHNkAQX7gAXeZp1GYBixwIF4JvawV2s1pKnIDZE3hkLqhx+e4OOYvawhlVoUdILsXSC2oiG4Q5qF/tSz6fh/2gFY+/2fxJMSwwbPXTkUPA5eXKCTpRIbWdIK61XEMKenSlWFF0giXWcOMkxXH4/T1NFlV7/g57MPogu+DD41Y2LNkdZQVaVYWEyvTw3t0pVvgohGNPRSNKV8qR8yzSM5yVe0Ck6SWOk+74SWwBoItjp66LwNQ60JK2Od2+NOSE3+2eG1SkIKL4+q0Uo4NGQgyxblPWIXJZTl14XcaxyXCJr/K913khhw+b+DumTz6TTGg6jMR8oXRFawccmEHL/DN55Y3HfRAwVj/cNIx7I=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.12
Release Details
UpdatedNov. 4, 2025, 4:14 p.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets
  • Axes Positioning: Axes helper now positioned at bottom center of models, aligned with grid
  • Axes Scaling: Made axes size dynamic (25% of model's largest dimension, minimum 5 units)
  • Logo Loading: Fixed app logo path in demo scene to use generateUrl() for correct URL resolution

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
  • Axes now recreate on model load to ensure proper sizing and positioning
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturetHcAULDv6Z6JlPEmMcFbBFAPFTScfSULZoxHuFuCtCO965w5/TPzwTm9AAbrvWQlTzOza8VK9le5cP96O8XKoLPU/rUTY0YPL+11rga9rU5c9nNcCiRTKwaha9wnos5/fDxZOhefb9aFojmpf7HsrSmHL+JMrLwaVvp846k4ShSPNbV7cVTqsrwz1C4Vy4JRzGMuSfcAMjxLBC02obtPqVYX4T5s7/QCithiTGI1VmmtMxZM0sG3fkIRf9ouPQ44hpHX7beIm4vBGthPwjhlNpR+aXq9rQd4annpZKtZGfGRX/RDXlFXUWBE/07A+/CBzP9DX9pegUvzvrelj67ADuiRDfxsL3vKN1VGU1rqHi1ZrwE24BzfeGnWgUb+wEBOcM8x8BEc0thH2zbfKAwqjY1h/iH8IjoIkbPPm3mKwQ2b5w2v2aaRRr3qbK3cOLhyzWJBpQFZaahomr9x+/hN5aU9fhr9hMjgPboRadI7rO+V6QtQDobhKlz+6Rdb1mo+Bzk9btyCJ7flqqos07yo/O01o4G/0L0hdw20vYh3WlbeGRWC6W/OBzQmRwT4MWFoHV2mQOYO4Nie4SkFCzs2lT3NWW7I/uCrCPMZDJrLhSk1KiKzctZXjTUalB4DuPHSpq99WjCcf2oPLo6rQ7DJd/sIRSh3tSj8WIlxcWCrSWk=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.11
Release Details
UpdatedNov. 4, 2025, 10:58 a.m.
Changelog

Fixed

  • Viewer Integration: Fixed files() method not storing files list, causing "No files provided, skipping update" error
  • File Loading: Fixed TypeError: Cannot read properties of undefined (reading 'filename') when opening 3D files
  • Static Assets: Fixed 500 error when loading app-color.svg due to route conflicts
  • Route Structure: Changed viewer route from /{fileId} to /f/{fileId} to prevent conflicts with static assets

Changed

  • Enhanced files() method in ViewerComponent with fallback logic to create synthetic file from props
  • Simplified PageController by removing unnecessary is_numeric() checks
  • Updated URL structure for better RESTful design: /apps/threedviewer/f/{fileId}
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignaturehK5rTudkrE4neZOI/n/jv+qjY/VfKVkRkouiBK5jBARxwK9e0W8U07ajlvP2+U8q8qwNzjGY77RproUnQIkOlALx3c6V9ufsMaHcpuAR2GAvK84WgqqEM37VSPQrGhLLuZzz4nU5nYmNOsTg+maV0O5XEND29zOdafsJHxpY8mQofXCA0vyLYNLYQtLEAPRBtl3uDQgSSjPGB6kN87VUcqebvVrVim8aa8l7BpDoMiW5j60T7toenQiisu6S4aKMRHCxx7I1cHK/mkGjLzl+/AHpfwWpKCr13StVLzqqlVPc5+SL/yI14XW2JtJARHShE0zBqkARG0yqsFXVTGvLzvAahLuCfIdlied8HFK/NQVmqgLlRAc30xWGq2vcQvpnr9L7GwCkImnM5YeoTqVOJHawhc3wU8FQ56g/imCYgMqwSOegC1BVegdX/XvCMCxhQXEj4OxUxETQ/T1+BWqBBLLbZQQiyyRXw6o/bzpBWJYtDH/XIzFKxJdC7WKfIM6lPmPb7CCc6sJGfEi/Lyv3rTfCjyQj7ioMlB+5sLV5/d+tth6eeY9O6jZsz92/V5byhrtIdYvu4j7IZR2m8+/IpkjmTMnuv18wyU6+unVo8V2hKNLhoWYHJqEQ9Ylx/Rj6tKb4TwsrciMPV+2T38lJK5cS0GZittMNUVAKSUPBSnQ=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.10
Release Details
UpdatedNov. 4, 2025, 9:48 a.m.
Changelog

Fixed

  • Dark Theme Support: Fixed slide-out toolbar panel not responding to theme changes
  • Theme Switching: Implemented reactive theme binding using Vue computed properties
  • CSS Integration: Converted base styles to use Nextcloud CSS variables for better theme integration
  • UI Consistency: Toolbar panel now properly switches between Light, Dark, and Auto themes

Changed

  • Improved maintainability by using Nextcloud's standard color system throughout the toolbar
  • Enhanced theme responsiveness with component-level class binding
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT38bK6V5aDGCGlNZJEV3QCmwYaO21RK3QtifSTQ3O311NOPA+O7ryIjncKIdr/rdN/9ZjeFLqwcxRk2traF5MkhvjRCr3NT5oV5cuMQgmw2Js7MAOLrSXOUkn6b7LpQKesEmzQPqygl2fuqN5qrxILxeWU5xWq3nyoV+G2TzByeLOT9CX8+EH1rzMnJNb/35aFS0kj/fU4V0FHFM3h8KxNDWYVjlJ/tPtq5IFBvICRm6HLCkQ2fEPPXkxP2Z1lpBam6ZGuSbcmo2lb5yQ7Zq3n6rd7FIzkU6r0/xvYF5gY08bwIdtULEpJOEQwp/n9KBR2itV4twQG2QAZFEIMF+EIg4lRzu0vdX8GVDFbirW8YqCX8PXyd+UAm1FfZ4vf8VHQXF7weZgAnvm3crR01GNStlBHl37Zke3z8CHmiYj3ffi4WMwGBT4Gb8f6fxlCOHH/mOlCyA3rtVlI5cHqJGHbdiwgHYX6e+0p5JRWOlecK/HTcVd0Aa5KX57BTcRQXEhGaQqHIbRhWTWwDMesbAyJMtCT72O2eRUqJCnSI+SRSxl/Hh4lHUEnOPx9D2P/03xDEDpO3xoR7tQGN927un6811AS+v+kABFDJQcayHiFTpR28U+WzF1vqS2fktc90NaWwiJuO1/hnQ/H8J6icAF4oxmvArZI2uO1X3CPZMwN8=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32
3D Viewer 1.7.9
Release Details
UpdatedNov. 4, 2025, 8:39 a.m.
Changelog

Added

  • 3D Camera Controller: New circular controller interface for intuitive 3D model navigation
  • Camera Control Methods: Advanced camera manipulation including rotation, zoom, and directional nudging
  • View Snapping: Animated camera transitions to predefined views (Front, Back, Left, Right, Top, Bottom)
  • Controller Persistence: Save and restore controller position and visibility preferences
  • Smooth Animations: Eased camera transitions with customizable duration and easing functions
  • Face Labels: Orientation markers (TOP, BOTTOM, FRONT, BACK, LEFT, RIGHT) on model faces
  • Export Functionality: Export models to GLB, STL, and OBJ formats
  • Camera Projection Toggle: Switch between perspective and orthographic views
  • Progressive Texture Loading: Background texture loading for improved performance
  • Dependency Caching: IndexedDB caching system for faster multi-file model loading
  • Model Statistics Panel: Detailed information about loaded models
  • Help Panel: Comprehensive in-app documentation and controls guide
  • Theme Customization: Enhanced theme switching with RTL support
  • Performance Overlay: Visual performance stats display with real-time monitoring
  • KTX2 Texture Support: GPU texture compression for better performance
Licenses AGPLv3+
Certificate-----BEGIN CERTIFICATE-----
MIIEBzCCAu8CAhLKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
dXRob3JpdHkwHhcNMjUxMTA0MDgxMDEzWhcNMzYwMjEwMDgxMDEzWjAXMRUwEwYD
VQQDDAx0aHJlZWR2aWV3ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDnE42wyWoV2dJM9P5a0wX1fka0RV64J/zwc6phoNTGOt6l2KulOVkwjhiGstJN
ZDtJ56Zc3WUYReEZUi7QLkJjb7t1Tyorz24gFDytMxPAzOQ0WICSY3OBEJzPxXzB
lq2XM+EXuyS9en2VRbjtA6BuYDs0leF8Ucm5zVAHNAHPrlWCHeiHHm6qwtYPlRy7
6GLmdgLA9/lOK1cL6leMPZRv5ths4tsda/nRfnXJp8i1n29+PrqSx1U1V26z1Oyg
4W+kYmw/CVQPw3F0dYsmdWG5Esp9WKP4LkhRuIvqbt5AWB9Ymb/zNXQgMcZBo46e
Lf9jMYH5WWnXsuJV6kypn4pkyhjyy8dGsVGPR/O/YafVe5xqTYUKVxyKR5v8pxoB
NqPTKKCe1dyhPrb5aUibc4MIRVq7YNKL7cHqtt0Lgii0AtDoXlFxo+o4iNhl85id
z8aBcDcrNpAlWHvEqqMeuzrdm5wbWdFS9PLzjsjTH2HVLJwWzNY50JIJfgECBrRm
pgx2xcE8artReZUyvn4N7OhQcIMlbiJJD3GAqo+royLWk+saObAMewc8tL9WCDDT
zVliibBIC4cS8TNxeWJBCDE31VRcchUpnOIAl6Ks4BqAjIZWOHywJ1LDZMjYByBK
0kJ3YbbcJ8G4wvhllp+FDeK+72Yaf2W/vXeAxe5RT14XAQIDAQABMA0GCSqGSIb3
DQEBCwUAA4IBAQBr2FhVbBHoSFfBGsLfBQEwVfjBGUb4gnzMmqHHMcFwrQiWNejZ
oK7KqUu28cuU6BjJ5/K2AZBW3/eU5zcPlv36G+VGYfVSsmT50G38fXyP4rCxICdL
0hZnKSJU80YLCPy2uSPJ6os3gcIp7OEkRLukU/Be8GEVZAlX5dVds3ve7GFA1K/z
FI9AMqwGnueXbncuTDo7QGd268qYNNWh/jYloQ7sdW1NCwKGvpPXjB80rw5briXk
Zzf7xuMLAobzRxY23NlBamP7wdv355TWXlrFBkaKdF4+aSzELf9LpXzDsQ6SaDI4
Mol4XeYOOXOL40adta2oG9/2k3GKNRfSh5Qe
-----END CERTIFICATE-----
SignatureT4jr3f8pu++HPy93L1HdiayTsqKV8PwgIw7jkye4TkqYM7wBuBsK6qv/TXwuZElOm9hzBEVH/MnuFoRl2QXPxurhZLvu918h0rDnTIjfRNMC2gIVFI8JLneLq8ya+m9lKGNLXEodRAhQ3pGn97cuSFzff+iGhQOFkojIvbWM4HbSQFq6pfF3/ZYjUCt5BhRIHdPkk+mzwjpVaniq98gh4XS3AMYqs4p2n7vysSGjAO8hZrDdGUJGxBlz1amgeUpdiGOpTwI4FyT2vbxcmHcMVwZTQyXoYZ/Ux70JZp98nz1H1kKqIoDxzZSRmetrm23Pzn+k440rHQUSjuIa/p6I6w8nGG25M0kjB46lacCtbUlR6Rrrj6SNwFITde5Rr87HP2R/ILqI5MCC73SebBxcdY/IXw7xHjUiBJTlrOmPi1ppktirZnOsmSR2Sa/yE81zrYWJ2HidTbxEGpJDZBIiwOaBCYh6nwQ51PG1z4mxgJE4G+7lMZ/bpu5UaJihys2WcoBhPxrGNeoLoEN+D1JiaOU1C974Wwncz1/BobA08FSx+rzXCnRNSQWbBapz3J6MYT5TwChku40FeqGR2KJm+v2pYgm8K/Eepieubi61tD9St1exw3AjBiJ2/dKOezNb8ZHFcoWn2bxCZc4rzuBUO0tJSrjmbW5dcysusajrvMs=
Signature digestsha512
Dependencies
Required Nextcloud versions >=30.0.0,<33.0.0
Minimum Integer bits32