Export
Register the export lifecycle hook, and the platform will invoke your generator to retrieve the resulting file whenever the user clicks "Export" or "Open in Studio." The return value is always { filename, blob }, and any file format is supported (images, SVG, 3D models, PDF, etc.).
Placing the Export Button
You place the export button yourself, inside your app—add an element with the data-atomm-export-button attribute anywhere on the page, and the SDK will render it in place as a platform-controlled Export dropdown (Download / Open in Studio + credit indicator). Clicks are always routed through the platform:
<div data-atomm-export-button></div>
- Position and outer layout are entirely up to you (e.g.,
position: fixedto pin it to a corner); you can place multiple instances on one page, each hydrating independently. - The button renders inside a Shadow DOM for style isolation; theming is done exclusively through the documented
--atomm-export-*CSS variables (set them on the element itself or any ancestor—they're automatically inherited by the button):
[data-atomm-export-button] {
--atomm-export-bg: #d32f2f; /* Button background color */
--atomm-export-radius: 0; /* Border radius */
--atomm-export-width: 200px; /* Width: px, or 100% to fill the container */
--atomm-export-height: 40px; /* Height */
}
| Variable | Default | Controls |
|---|---|---|
--atomm-export-bg / -bg-hover / -bg-active | #070b10 / #252c36 / #1c2129 | Button background for the default / hover / active states |
--atomm-export-color | #fff | Button text color |
--atomm-export-width | auto (fits content) | Button width (px, or 100% to fill the container; min 24px) |
--atomm-export-height | 32px | Button height (px or 100%; min 24px) |
--atomm-export-radius | 8px | Border radius |
--atomm-export-font / -font-size | Inter, system-ui, sans-serif / 14px | Font family / font size |
--atomm-export-menu-bg / -menu-color / -menu-hover-bg | #fff / #111 / #f4f4f5 | Dropdown menu colors |
--atomm-export-menu-radius / -menu-shadow | 8px / 0 4px 16px rgba(16,24,40,.12) | Menu border radius / shadow |
--atomm-export-z | 1000 | Dropdown overlay z-index |
Only the
--atomm-export-*variables listed above are supported customization points; internal button class names may change at any time, so don't rely on them.
The export button—and its Download / Open in Studio / credit indicator—is driven by the atomm platform, and only works when your generator runs inside the atomm environment: the live platform, or the dev tool's local preview (?local=).
If you open your generator standalone, outside atomm (e.g. opening the HTML directly), the button may still render, but clicking it won't produce a file and no billing status will show—this is by design, not a bug. Always verify export via local preview or the live platform.
Free-Use Count / Credit Indicator on the Button
The button automatically displays the current billing status (pushed by the platform—no action needed on your part):
| State | Display | Meaning |
|---|---|---|
| Free window | 30s countdown | A short grace period granted after a successful export; exports within this window are free |
| Free count | Free 3/3 | Remaining / total free exports |
| Credit cost | Credit icon + number | Credits consumed per export once free uses are exhausted |
In local debugging (
?local=), clicking export never actually deducts credits; refreshing the page resets it.
Registering the export Hook
atomm.lifecycle.on('export', async () => {
// No arguments are passed when triggered; the handler reads the current generation result itself and produces a Blob
const blob = await exportCurrentResultAsBlob()
if (!blob) {
throw new Error('请先生成作品后再下载')
}
return {
filename: 'my-generator.glb', // Must include an extension; this determines the downloaded filename and the asset type used by Open in Studio
blob, // A Blob of any format; MIME type comes from blob.type
}
})
Return Value Fields
| Field | Type | Description |
|---|---|---|
| filename | string | The downloaded filename; must include an extension (e.g., design.glb) and must not contain path separators / \ |
| blob | Blob | File content of any format; each file must be ≤ 100MB; MIME type comes from blob.type, falling back to the filename extension when empty |
Multi-File Export (Optional)
The handler can also return an array of files, and the platform will bundle them into a single zip download:
atomm.lifecycle.on('export', async () => [
{ filename: 'model.glb', blob: glbBlob },
{ filename: 'preview.png', blob: pngBlob },
])
| Rule | Description |
|---|---|
| zip filename | Derived from the base name of the first file in the array (model.glb → model.zip) |
| Duplicate names | Automatically deduplicated to name (1).ext |
| Size limit | The combined size of all files must be ≤ 100MB; exceeding this fails the entire export |
| Array of length 1 | Downloads the file directly, without wrapping it in a zip |
| Open in Studio | Supports multiple files—all returned files are handed to Studio to open, with no format restriction; formats Studio doesn't support are flagged by Studio itself. Multi-file import requires xTool Studio 1.8+; single-file export remains compatible with older versions |
Single files still use
{ filename, blob }; use an array for multiple files. If any item in the returned array is invalid (not a Blob, filename missing an extension, etc.), the entire result is considered invalid.
Getting a Blob
Almost any export source can be converted to a Blob in a single line:
| What you have | Convert to Blob |
|---|---|
| Canvas (2D drawing) | canvas.toBlob(cb, 'image/png') |
| SVG string / text / JSON | new Blob([str], { type: 'image/svg+xml' }) |
| ArrayBuffer / 3D mesh (glb/stl, etc.) | new Blob([buffer], { type: 'model/gltf-binary' }) |
| Existing data URL / remote URL | await (await fetch(x)).blob() |
File from <input type=file> | Use it directly (a File is already a Blob) |
Export data is always carried as a Blob, so any format is supported — a Blob preserves the binary content of any file type.
filename must include an extension and must not contain path separators; if a file exceeds 100MB or the return value isn't a valid { filename, blob }, the platform treats it as invalid, the export fails, and the user is notified.
Downloads support saving any format to disk. "Open in Studio" hands the file off to xTool Studio to open—the platform makes no assumptions about format, and Studio itself will flag any format it doesn't support.
The export button is available to every generator (it appears as soon as you add data-atomm-export-button to your app). If a user clicks export and you haven't registered the export hook, the platform receives "the app provides no download method"—and your submission will be rejected in review as a result. Any generator that supports export must register this hook; you can test it locally by clicking "Export" in the preview to confirm a file is produced correctly.