refactor: clean upload pipeline and restore draco delivery
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared SVG icon components
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface IconProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user