- 全局样式对标Air Spark设计系统(背景、glass card、配色、圆角) - 视频详情弹窗(VideoDetailModal)全屏预览+信息面板 - GenerationCard重构:fixed定位tooltip、9:16视频适配、blob下载 - 个人中心:总额度/今日/本月三卡片布局 - Dashboard图表配色统一为#6c63ff主色调 - Sidebar、InputBar、Toolbar等组件样式优化 - 新增AmbientBackground、AssetsPage组件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
2.9 KiB
TypeScript
100 lines
2.9 KiB
TypeScript
import { useRef, useCallback, type DragEvent } from 'react';
|
|
import { useInputBarStore } from '../store/inputBar';
|
|
import { UniversalUpload } from './UniversalUpload';
|
|
import { KeyframeUpload } from './KeyframeUpload';
|
|
import { PromptInput } from './PromptInput';
|
|
import { Toolbar } from './Toolbar';
|
|
import { showToast } from './Toast';
|
|
import styles from './InputBar.module.css';
|
|
|
|
export function InputBar() {
|
|
const mode = useInputBarStore((s) => s.mode);
|
|
const addReferences = useInputBarStore((s) => s.addReferences);
|
|
const setFirstFrame = useInputBarStore((s) => s.setFirstFrame);
|
|
const barRef = useRef<HTMLDivElement>(null);
|
|
|
|
const handleDragOver = useCallback((e: DragEvent) => {
|
|
e.preventDefault();
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#00b8e6';
|
|
}
|
|
}, []);
|
|
|
|
const handleDragLeave = useCallback(() => {
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#2a2a38';
|
|
}
|
|
}, []);
|
|
|
|
const handleDrop = useCallback((e: DragEvent) => {
|
|
e.preventDefault();
|
|
if (barRef.current) {
|
|
barRef.current.style.borderColor = '#2a2a38';
|
|
}
|
|
const IMAGE_MAX = 30 * 1024 * 1024;
|
|
const VIDEO_MAX = 50 * 1024 * 1024;
|
|
const AUDIO_MAX = 15 * 1024 * 1024;
|
|
const files = Array.from(e.dataTransfer.files).filter(
|
|
(f) => f.type.startsWith('image/') || f.type.startsWith('video/') || f.type.startsWith('audio/')
|
|
);
|
|
if (!files.length) return;
|
|
|
|
const valid: File[] = [];
|
|
for (const f of files) {
|
|
let limit: number;
|
|
let limitLabel: string;
|
|
if (f.type.startsWith('video/')) {
|
|
limit = VIDEO_MAX;
|
|
limitLabel = '视频文件不能超过50MB';
|
|
} else if (f.type.startsWith('audio/')) {
|
|
limit = AUDIO_MAX;
|
|
limitLabel = '音频文件不能超过15MB';
|
|
} else {
|
|
limit = IMAGE_MAX;
|
|
limitLabel = '图片文件不能超过30MB';
|
|
}
|
|
if (f.size > limit) {
|
|
showToast(limitLabel);
|
|
} else {
|
|
valid.push(f);
|
|
}
|
|
}
|
|
if (!valid.length) return;
|
|
|
|
if (mode === 'universal') {
|
|
addReferences(valid);
|
|
} else {
|
|
const imageFiles = valid.filter((f) => f.type.startsWith('image/'));
|
|
if (imageFiles.length > 0) {
|
|
setFirstFrame(imageFiles[0]);
|
|
}
|
|
}
|
|
}, [mode, addReferences, setFirstFrame]);
|
|
|
|
return (
|
|
<div className={styles.wrapper}>
|
|
<div className={styles.container}>
|
|
<div
|
|
ref={barRef}
|
|
className={styles.bar}
|
|
onDragOver={handleDragOver}
|
|
onDragLeave={handleDragLeave}
|
|
onDrop={handleDrop}
|
|
>
|
|
{/* Upper area: Upload + Prompt */}
|
|
<div className={styles.inputArea}>
|
|
{mode === 'universal' ? <UniversalUpload /> : <KeyframeUpload />}
|
|
<PromptInput />
|
|
</div>
|
|
|
|
{/* Divider */}
|
|
<div className={styles.divider} />
|
|
|
|
{/* Toolbar */}
|
|
<Toolbar />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|