feat(project): optimize image imports and converter
- Optimize imported scene preview images (smart WebP/JPEG/PNG, preserve alpha, keep pixel size) - Update converter to re-encode existing image assets with same algorithm - Improve import/export progress overlay and reduce presentation slide stutter Made-with: Cursor
This commit is contained in:
@@ -35,6 +35,7 @@ export type SceneGraphSceneAudioSummary = {
|
||||
export type SceneGraphSceneCard = {
|
||||
title: string;
|
||||
previewAssetId: AssetId | null;
|
||||
previewThumbAssetId: AssetId | null;
|
||||
previewAssetType: 'image' | 'video' | null;
|
||||
previewVideoAutostart: boolean;
|
||||
previewRotationDeg: 0 | 90 | 180 | 270;
|
||||
@@ -69,6 +70,7 @@ type SceneCardData = {
|
||||
title: string;
|
||||
active: boolean;
|
||||
previewAssetId: AssetId | null;
|
||||
previewThumbAssetId: AssetId | null;
|
||||
previewAssetType: 'image' | 'video' | null;
|
||||
previewVideoAutostart: boolean;
|
||||
previewRotationDeg: 0 | 90 | 180 | 270;
|
||||
@@ -129,7 +131,8 @@ function IconVideoPreviewAutostart() {
|
||||
}
|
||||
|
||||
function SceneCardNode({ data }: NodeProps<SceneCardData>) {
|
||||
const url = useAssetUrl(data.previewAssetId);
|
||||
const thumbUrl = useAssetUrl(data.previewThumbAssetId);
|
||||
const previewUrl = useAssetUrl(data.previewAssetId);
|
||||
const cardClass = [styles.card, data.active ? styles.cardActive : ''].filter(Boolean).join(' ');
|
||||
const showCornerVideo = data.previewIsVideo;
|
||||
const showCornerAudio = data.hasSceneAudio;
|
||||
@@ -139,11 +142,11 @@ function SceneCardNode({ data }: NodeProps<SceneCardData>) {
|
||||
<div className={cardClass}>
|
||||
<div className={styles.previewShell}>
|
||||
{data.isStartScene ? <div className={styles.badgeStart}>НАЧАЛО</div> : null}
|
||||
{url && data.previewAssetType === 'image' ? (
|
||||
{thumbUrl ? (
|
||||
<div className={styles.previewFill}>
|
||||
{data.previewRotationDeg === 0 ? (
|
||||
<img
|
||||
src={url}
|
||||
src={thumbUrl}
|
||||
alt=""
|
||||
className={styles.imageCover}
|
||||
draggable={false}
|
||||
@@ -152,7 +155,7 @@ function SceneCardNode({ data }: NodeProps<SceneCardData>) {
|
||||
/>
|
||||
) : (
|
||||
<RotatedImage
|
||||
url={url}
|
||||
url={thumbUrl}
|
||||
rotationDeg={data.previewRotationDeg}
|
||||
mode="cover"
|
||||
loading="lazy"
|
||||
@@ -161,9 +164,31 @@ function SceneCardNode({ data }: NodeProps<SceneCardData>) {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : url && data.previewAssetType === 'video' ? (
|
||||
) : previewUrl && data.previewAssetType === 'image' ? (
|
||||
<div className={styles.previewFill}>
|
||||
{data.previewRotationDeg === 0 ? (
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt=""
|
||||
className={styles.imageCover}
|
||||
draggable={false}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
) : (
|
||||
<RotatedImage
|
||||
url={previewUrl}
|
||||
rotationDeg={data.previewRotationDeg}
|
||||
mode="cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : previewUrl && data.previewAssetType === 'video' ? (
|
||||
<video
|
||||
src={url}
|
||||
src={previewUrl}
|
||||
muted
|
||||
playsInline
|
||||
preload="metadata"
|
||||
@@ -322,6 +347,7 @@ function SceneGraphCanvas({
|
||||
title: c?.title ?? '',
|
||||
active,
|
||||
previewAssetId: c?.previewAssetId ?? null,
|
||||
previewThumbAssetId: c?.previewThumbAssetId ?? null,
|
||||
previewAssetType: c?.previewAssetType ?? null,
|
||||
previewVideoAutostart: c?.previewVideoAutostart ?? false,
|
||||
previewRotationDeg: c?.previewRotationDeg ?? 0,
|
||||
|
||||
Reference in New Issue
Block a user