Importing animated pet packages in Tauri: the JavaScript-to-Rust path
By the PetPal Studio team.
A desktop pet importer has two jobs: read an animation package from disk and give the renderer assets it can display. In our PetPal Desktop client, JavaScript handles the interface and animation, while Tauri commands let Rust load local files.
Here is the import path in the public source, including where to look when a package fails to load.
1. Treat the manifest as the package contract
The file picker accepts .petpet, .petpack, and .zip archives. The loader expects a recognized manifest and its referenced animation assets. The supported layouts are:
petpet.jsonwith a sprite sheet.petwebp.jsonwith WebP action files.- Legacy
manifest.jsonwith sprite strips.
The format guide provides examples. Renaming a photo changes its filename, but does not supply this structure. Available actions depend on the imported package.
2. Pass a path across the Tauri boundary
In src/main.js, importPetpackFile() opens the native dialog. This is the relevant part, with UI-state and error handling omitted:
const selected = await open({
directory: false,
multiple: false,
filters: [{ name: "PetPet", extensions: ["petpet", "petpack", "zip"] }],
});
if (selected) await importPetpackPath(selected);
importPetpackPath() calls Rust through invoke. After a successful response, it records the installed file and applies the returned assets:
const installed = await invoke("install_petpack_file", { path });
await saveRecentPetpack("file", installed.path, installed.loaded.manifest.name);
applyPetpack(installed.loaded.manifest, installed.loaded.assets, "installed .petpack");
The saved path points to the application's installed copy. Import requires the native Tauri runtime; a browser-only Vite preview lacks this file-import bridge.
3. Follow the archive through Rust
In src-tauri/src/lib.rs, install_petpack_file() checks the source file and extension, creates an installed-petpacks directory under app data, and copies the package there. It then calls load_petpack_file() on that copy.
The loader opens a ZipArchive, reads the manifest, and asks collect_asset_paths() which assets to load. Each asset is read by its archive path and encoded as a MIME-labelled Base64 data URL. The returned LoadedPetpack contains a manifest and an asset map for the frontend.
These stages give useful debugging boundaries. A corrupt archive produces failed to read petpack zip. A missing referenced file produces failed to find asset. Start by checking the container, then the manifest's paths, before investigating the animation renderer. These messages describe failures handled in the source, not test results from this article.
4. Inspect the flow with public samples
Install Node.js 20 or later, Rust stable, and your operating system's Tauri 2 prerequisites, then run:
git clone https://github.com/ethmoon/petpal-desktop.git
cd petpal-desktop
npm install
npm run tauri:dev
Open the system-tray menu, or menu-bar icon on macOS, choose Import .petpet, and select public/standard-mochi.petpet from your checkout. The tray also provides visibility, click-through, and quit controls. Use public samples when reporting problems, alongside your OS, client version, and reproduction steps.
The source includes Mac and Windows build targets, but a target is not a compatibility test. Check installer requirements; the default bundle configuration has no Linux installer.
5. Keep the hosted service boundary clear
The MIT-licensed client and public demos can be explored without a custom order. PetPal Studio separately generates pets from uploaded photos. A custom pack currently costs US$10 once; payment unlocks the generated preview, and the private package becomes available when ready. The paid backend and customer files are not in the repository. The customer walkthrough covers that workflow.
