feat(K): Blender Asset Library + production exports (GLB + .blend)
- feat(migration): 045_asset_libraries — new asset_libraries table (blend_file_path, catalog JSONB) - feat(model): AssetLibrary SQLAlchemy model in domains/materials/models.py - feat(api): POST/GET/PATCH/DELETE /api/asset-libraries + /upload-blend + /refresh-catalog endpoints - feat(celery): refresh_asset_library_catalog task on thumbnail_rendering queue — runs Blender headless - feat(blender): catalog_assets.py — extracts asset-marked materials + node_groups from .blend - feat(blender): asset_library.py — apply_asset_library_materials + apply_asset_library_node_groups helpers - feat(blender): export_gltf.py — STEP→STL→GLB production export with optional asset library - feat(blender): export_blend.py — STEP→STL→.blend production export with pack_all() - feat(frontend): api/assetLibraries.ts — full CRUD API client - feat(frontend): AssetLibraryPanel in Admin.tsx — upload, refresh, expand catalog view - docs: Blender asset_data marking requirement learning in LEARNINGS.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import api from './client'
|
||||
|
||||
export interface AssetLibraryCatalog {
|
||||
materials: string[]
|
||||
node_groups: string[]
|
||||
}
|
||||
|
||||
export interface AssetLibrary {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
original_filename: string | null
|
||||
catalog: AssetLibraryCatalog
|
||||
is_active: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export async function listAssetLibraries(): Promise<AssetLibrary[]> {
|
||||
const { data } = await api.get<AssetLibrary[]>('/asset-libraries')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function getAssetLibrary(id: string): Promise<AssetLibrary> {
|
||||
const { data } = await api.get<AssetLibrary>(`/asset-libraries/${id}`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function createAssetLibrary(params: {
|
||||
name: string
|
||||
description?: string
|
||||
blend_file: File
|
||||
}): Promise<AssetLibrary> {
|
||||
const form = new FormData()
|
||||
form.append('name', params.name)
|
||||
if (params.description) form.append('description', params.description)
|
||||
form.append('blend_file', params.blend_file)
|
||||
const { data } = await api.post<AssetLibrary>('/asset-libraries', form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function uploadAssetLibraryBlend(id: string, file: File): Promise<AssetLibrary> {
|
||||
const form = new FormData()
|
||||
form.append('blend_file', file)
|
||||
const { data } = await api.post<AssetLibrary>(`/asset-libraries/${id}/upload-blend`, form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function refreshAssetLibraryCatalog(id: string): Promise<AssetLibrary> {
|
||||
const { data } = await api.post<AssetLibrary>(`/asset-libraries/${id}/refresh-catalog`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function deleteAssetLibrary(id: string): Promise<void> {
|
||||
await api.delete(`/asset-libraries/${id}`)
|
||||
}
|
||||
Reference in New Issue
Block a user