Parent epic: #664
Depends on: Slices A–D (#665, #666, #667, #668).
Goal
Close out the epic with the verification, install-time UX, and discoverability work that turns "the app can open files" into "the app gets used to open files".
Scope
Verification matrix in CI
A new GitHub Actions job per OS that:
- macOS: builds the bundle, drops it into
/Applications, runs lsregister -f, then runs macos/verify-file-association.sh (from Slice B) against a fixture file. Fail the build if registration is missing.
- Windows: installs the Inno Setup installer (silent mode), runs
scripts/verify-windows-association.ps1 (from Slice C) against fixture files. Uninstalls and verifies clean uninstall.
- Linux: installs the .deb on a clean Ubuntu container, runs
scripts/verify-linux-association.sh (from Slice D), then uninstalls and verifies clean removal.
These run on every release build (not every PR — too slow / VM-heavy). The existing release pipeline at .github/workflows/deploy.yml is the right place.
Install-time UX
- macOS: After the user drops QtMeshEditor.app into Applications and runs it the first time, show a one-time "Make QtMeshEditor the default app for 3D model files?" dialog. If the user clicks "Yes", call the macOS Launch Services API (
LSSetDefaultRoleHandlerForContentType) to set QtMeshEditor as default for the QtMeshEditor-native UTIs (.mesh, .rsd, .tmd, .material). For non-native formats (.fbx, etc.) explain that the user controls those via Finder's Get Info → Open With → Change All UI.
- Windows: Slice C already plans the first-launch prompt that deep-links to
ms-settings:defaultapps. Slice E ensures it's actually implemented.
- Linux: After install, the existing
mainwindow.cpp-side "first launch" path checks xdg-mime query default model/gltf-binary and, if it's not QtMeshEditor, shows a banner with a "Set as default" button that calls xdg-mime default qtmesheditor.desktop model/gltf-binary (one call per supported type).
- All three prompts have a "Don't ask again" checkbox stored in
QSettings.
Recently-opened files
- A "Recent Files" menu entry under File that tracks the last 10 files opened (regardless of source — argv, FileOpenEvent, drag-and-drop from desktop, or menu pick).
- Persisted in
QSettings. Implemented in MainWindow.
- Surfaces in the macOS Dock's right-click → Recent Files automatically (macOS reads recent docs from a per-app NSDocumentController; we plumb that with
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:]).
- Windows: Jump List integration (
SetCurrentProcessExplicitAppUserModelID + ICustomDestinationList). Use the Qt Windows extras if available; otherwise WinAPI direct.
Drag-and-drop into the running app
MainWindow accepts QDragEnterEvent / QDropEvent on the viewport. Dropping a file from Finder/Explorer/Nautilus loads it (or asks user "Open as new entity?" vs "Merge into current scene?" for non-first drops).
- Already partial in the existing code? Verify and complete.
Marketing / discoverability
Not code, but ships with this epic:
- README hero update: lead with "Free 3D model viewer — opens FBX, glTF, OBJ, DAE, STL and 5+ more formats with one click. macOS, Windows, Linux." Move the technical detail (Ogre, Qt, CLI) below the fold.
- Website (
website/) mirror update.
- Changelog entry: "QtMeshEditor is now your default 3D model viewer. Install it once, then double-click any .fbx, .glb, .obj, or other supported model from your file manager — it just opens."
- Reddit / forum posts: write a one-page draft pitching QtMeshEditor as "the missing Preview.app for 3D models" — to be posted by the maintainer to r/gamedev, r/blender, r/IndieDev around the time the release ships. (Out of scope for the code PR; included in the issue checklist as a release-day task.)
- Sketchfab / Poly Haven / Mixamo SEO: a short blog post titled "How to open .fbx / .glb files without installing Blender" that points to QtMeshEditor. SEO landing page lives on the website. Drives the long-tail search traffic.
- Submit to:
- AlternativeTo.com — list QtMeshEditor as an alternative to "Microsoft 3D Viewer" (discontinued), "Apple Preview" (limited), Blender (heavy), various paid viewers.
- Slant.co — submit under "Best free 3D model viewer".
- GitHub topic tags: add
3d-model-viewer, fbx-viewer, gltf-viewer, obj-viewer, default-app to the repo topics.
Documentation
- New "File Associations" section in
CLAUDE.md documenting:
- Which formats are registered on which OS.
- How to verify registration.
- How to unregister / change defaults.
- The CLI fallback (
qtmesh ... always wins).
Acceptance Criteria
Effort
~6 days for the code (verification + UX + recents); marketing is separate cycle time.
Parent epic: #664
Depends on: Slices A–D (#665, #666, #667, #668).
Goal
Close out the epic with the verification, install-time UX, and discoverability work that turns "the app can open files" into "the app gets used to open files".
Scope
Verification matrix in CI
A new GitHub Actions job per OS that:
/Applications, runslsregister -f, then runsmacos/verify-file-association.sh(from Slice B) against a fixture file. Fail the build if registration is missing.scripts/verify-windows-association.ps1(from Slice C) against fixture files. Uninstalls and verifies clean uninstall.scripts/verify-linux-association.sh(from Slice D), then uninstalls and verifies clean removal.These run on every release build (not every PR — too slow / VM-heavy). The existing release pipeline at
.github/workflows/deploy.ymlis the right place.Install-time UX
LSSetDefaultRoleHandlerForContentType) to set QtMeshEditor as default for the QtMeshEditor-native UTIs (.mesh,.rsd,.tmd,.material). For non-native formats (.fbx, etc.) explain that the user controls those via Finder's Get Info → Open With → Change All UI.ms-settings:defaultapps. Slice E ensures it's actually implemented.mainwindow.cpp-side "first launch" path checksxdg-mime query default model/gltf-binaryand, if it's not QtMeshEditor, shows a banner with a "Set as default" button that callsxdg-mime default qtmesheditor.desktop model/gltf-binary(one call per supported type).QSettings.Recently-opened files
QSettings. Implemented inMainWindow.[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:]).SetCurrentProcessExplicitAppUserModelID+ICustomDestinationList). Use the Qt Windows extras if available; otherwise WinAPI direct.Drag-and-drop into the running app
MainWindowacceptsQDragEnterEvent/QDropEventon the viewport. Dropping a file from Finder/Explorer/Nautilus loads it (or asks user "Open as new entity?" vs "Merge into current scene?" for non-first drops).Marketing / discoverability
Not code, but ships with this epic:
website/) mirror update.3d-model-viewer,fbx-viewer,gltf-viewer,obj-viewer,default-appto the repo topics.Documentation
CLAUDE.mddocumenting:qtmesh ...always wins).Acceptance Criteria
CLAUDE.mdFile Associations section landed.Effort
~6 days for the code (verification + UX + recents); marketing is separate cycle time.