refactor: clean upload pipeline and restore draco delivery

This commit is contained in:
Tom Boullay
2026-04-29 16:29:32 +02:00
parent 097b8f6486
commit 498765db61
32 changed files with 769 additions and 215 deletions
+10 -3
View File
@@ -29,6 +29,8 @@ interface AlphaMapMaterial extends Material {
alphaTest: number
}
type AlphaImageSource = HTMLImageElement | HTMLCanvasElement | ImageBitmap
const alphaMapTextureCache = new WeakMap<Texture, Texture>()
function getRequestedFilename(requestedUrl: string) {
@@ -91,14 +93,19 @@ function supportsAlphaMap(material: Material): material is AlphaMapMaterial {
return 'alphaMap' in material
}
function isAlphaImageSource(image: object | null | undefined): image is AlphaImageSource {
return image instanceof HTMLImageElement
|| image instanceof HTMLCanvasElement
|| (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap)
}
function createAlphaMapTexture(texture: Texture) {
const cachedTexture = alphaMapTextureCache.get(texture)
if (cachedTexture) return cachedTexture
const image = texture.image as unknown
const isImageBitmap = typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap
const image = texture.image as object | null | undefined
if (!(image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || isImageBitmap)) {
if (!isAlphaImageSource(image)) {
texture.flipY = false
alphaMapTextureCache.set(texture, texture)
return texture
-9
View File
@@ -1,7 +1,3 @@
// ---------------------------------------------------------------------------
// Shared modal wrapper — handles overlay, centering, dialog role, aria
// ---------------------------------------------------------------------------
import type { ReactNode } from 'react'
interface ModalProps {
@@ -24,16 +20,11 @@ export default function Modal({ ariaLabelledBy, children }: ModalProps) {
)
}
// ---------------------------------------------------------------------------
// Shared modal footer with two buttons
// ---------------------------------------------------------------------------
interface ModalActionsProps {
cancelLabel: string
confirmLabel: string
onCancel: () => void
onConfirm: () => void
/** Tailwind classes for the confirm button (default: white bg) */
confirmClassName?: string
disabled?: boolean
}
-4
View File
@@ -1,7 +1,3 @@
// ---------------------------------------------------------------------------
// Shared SVG icon components
// ---------------------------------------------------------------------------
interface IconProps {
className?: string
}
+34 -15
View File
@@ -1,4 +1,5 @@
import { SpinnerIcon } from '@/components/ui/icons'
import { SpinnerIcon, WarningIcon } from '@/components/ui/icons'
import type { GitModelMode } from '@/lib/types'
interface ActionButtonsProps {
isUploading: boolean
@@ -7,7 +8,7 @@ interface ActionButtonsProps {
hasPendingOrErrors: boolean
allDone: boolean
hasErrors: boolean
onUpload: () => void
onUpload: (gitModelMode: GitModelMode) => void
onCancel: () => void
onReset: () => void
}
@@ -27,20 +28,38 @@ export default function ActionButtons({
const isBusy = isUploading || isChecking
return (
<div className="flex gap-3">
<div className="flex flex-col gap-3 sm:flex-row">
{!isBusy && hasPendingOrErrors && (
<button
onClick={onUpload}
disabled={cantUpload}
className={`flex-1 font-medium text-sm py-2.5 px-6 rounded-xl transition-all duration-150
focus:outline-none focus:ring-2 focus:ring-white/50 border border-white/20
${cantUpload
? 'bg-white/30 text-gray-500 cursor-not-allowed'
: 'bg-white text-[#000000] hover:bg-gray-200'
}`}
>
Envoyer
</button>
<>
<button
onClick={() => onUpload('draco-glb')}
disabled={cantUpload}
className={`flex-1 font-medium text-sm py-2.5 px-6 rounded-xl transition-all duration-150
focus:outline-none focus:ring-2 focus:ring-white/50 border border-white/20
${cantUpload
? 'bg-white/30 text-gray-500 cursor-not-allowed'
: 'bg-white text-[#000000] hover:bg-gray-200'
}`}
>
Envoyer
</button>
<button
onClick={() => onUpload('keep-gltf')}
disabled={cantUpload}
className={`flex-1 font-medium text-sm py-2.5 px-6 rounded-xl border transition-all duration-150
focus:outline-none focus:ring-2 focus:ring-white/30
${cantUpload
? 'bg-black-800 text-gray-600 border-white/10 cursor-not-allowed'
: 'bg-black-700 text-gray-300 border-black-600 hover:bg-black-600'
}`}
>
<span className="flex items-center justify-center gap-2">
<WarningIcon className="w-4 h-4 text-yellow-400" />
<span>Envoyer en GLTF</span>
</span>
</button>
</>
)}
{isBusy && (
-4
View File
@@ -1,7 +1,3 @@
// ---------------------------------------------------------------------------
// Drive/Git status sub-line for FolderCard
// ---------------------------------------------------------------------------
import { SpinnerIcon, XIcon, WarningIcon } from '@/components/ui/icons'
import type { FolderEntry } from '@/lib/client-types'
+1 -2
View File
@@ -59,7 +59,6 @@ export default function FolderCard({ entry, index, onToggleViewer, onRemove }: F
)}
</div>
{/* Drive status sub-line (only during upload, not after success) */}
{entry.status !== 'success' && entry.driveStatus && entry.driveStatus !== 'pending' && (
<DriveStatusLine driveStatus={entry.driveStatus} driveError={entry.driveError} />
)}
@@ -97,7 +96,7 @@ export default function FolderCard({ entry, index, onToggleViewer, onRemove }: F
>
<ModelViewer
url={entry.modelUrl}
assetUrls={entry.assetUrls || {}}
assetUrls={entry.assetUrls}
filename={entry.modelFile.name}
size={formatBytes(entry.modelFile.size)}
/>