+
{/* 自由输入区域 */}
@@ -154,60 +165,19 @@ export default function CharacterEditor({
))}
- {/* 自定义标签区域 */}
-
-
- {customTags.map((tag) => (
-
- {tag}
-
-
- ))}
-
-
-
setNewTag(e.target.value)}
- onKeyPress={(e) => {
- if (e.key === 'Enter' && newTag.trim()) {
- setCustomTags(tags => [...tags, newTag.trim()]);
- setInputText((text: string) => text + (text.endsWith("。") ? "" : ",") + newTag.trim());
- setNewTag("");
- }
- }}
- placeholder="添加自定义标签..."
- className="flex-1 px-3 py-2 bg-white/5 border-none rounded-lg text-sm
- focus:outline-none focus:ring-2 focus:ring-blue-500 placeholder:text-white/30"
- />
-
-
-
+ {/* 重新生成按钮 */}
+
+
+ {isRegenerating ? "生成中..." : "重新生成"}
+
);
}
diff --git a/components/ui/character-tab-content.tsx b/components/ui/character-tab-content.tsx
index a9aac41..5c14a54 100644
--- a/components/ui/character-tab-content.tsx
+++ b/components/ui/character-tab-content.tsx
@@ -1,11 +1,15 @@
import React, { useState, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
-import { Upload, Library, Play, Pause, RefreshCw, Wand2, Users, Check, Sparkles, Plus, X } from 'lucide-react';
+import { Upload, Library, Play, Pause, RefreshCw, Wand2, Users, Check, ReplaceAll, X } from 'lucide-react';
import { cn } from '@/public/lib/utils';
import { GlassIconButton } from './glass-icon-button';
import { ReplaceCharacterModal } from './replace-character-modal';
import { Slider } from './slider';
import CharacterEditor from './character-editor';
+import ImageBlurTransition from './ImageBlurTransition';
+import FloatingGlassPanel from './FloatingGlassPanel';
+import { ReplaceCharacterPanel, mockShots, mockCharacter } from './replace-character-panel';
+import { ImageWave } from '@/components/ui/ImageWave';
interface Appearance {
hairStyle: string;
@@ -54,6 +58,27 @@ interface CharacterTabContentProps {
roles: Role[];
}
+const imageUrls = [
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
+ 'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
+];
+
export function CharacterTabContent({
taskSketch,
currentRoleIndex,
@@ -64,67 +89,48 @@ export function CharacterTabContent({
const [activeReplaceMethod, setActiveReplaceMethod] = useState('upload');
const [newTag, setNewTag] = useState('');
const [localRole, setLocalRole] = useState(mockRole);
+ const [currentRole, setCurrentRole] = useState(roles[currentRoleIndex]);
+ const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false);
+ const [replacePanelKey, setReplacePanelKey] = useState(0);
+ const [ignoreReplace, setIgnoreReplace] = useState(false);
+ const [isReplaceLibraryOpen, setIsReplaceLibraryOpen] = useState(false);
+ const [replaceLibraryKey, setReplaceLibraryKey] = useState(0);
const textareaRef = useRef
(null);
- // 处理标签添加
- const handleAddTag = () => {
- if (newTag.trim() && !localRole.tags.includes(newTag.trim())) {
- const newTagText = newTag.trim();
- // 更新标签数组
- const updatedTags = [...localRole.tags, newTagText];
- // 更新角色描述文本
- const updatedDescription = localRole.roleDescription + (localRole.roleDescription ? ',' : '') + newTagText;
-
- setLocalRole({
- ...localRole,
- tags: updatedTags,
- roleDescription: updatedDescription
- });
- setNewTag('');
+ const handleReplaceCharacter = (url: string) => {
+ setCurrentRole({
+ ...currentRole,
+ url: url
+ });
- // 自动调整文本框高度
- if (textareaRef.current) {
- textareaRef.current.style.height = 'auto';
- textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px';
+ setIsReplacePanelOpen(true);
+ };
+
+ const handleConfirmReplace = (selectedShots: string[], addToLibrary: boolean) => {
+ // 处理替换确认逻辑
+ console.log('Selected shots:', selectedShots);
+ console.log('Add to library:', addToLibrary);
+ setIsReplacePanelOpen(false);
+ };
+
+ const handleCloseReplacePanel = () => {
+ setIsReplacePanelOpen(false);
+ setIgnoreReplace(true);
+ };
+
+ const handleChangeRole = (index: number) => {
+ if (currentRole.url !== roles[currentRoleIndex].url && !ignoreReplace) {
+ // 提示 角色已修改,弹出替换角色面板
+ if (isReplacePanelOpen) {
+ setReplacePanelKey(replacePanelKey + 1);
+ } else {
+ setIsReplacePanelOpen(true);
}
+ return;
}
- };
-
- // 处理标签删除
- const handleRemoveTag = (tagToRemove: string) => {
- setLocalRole({
- ...localRole,
- tags: localRole.tags.filter(tag => tag !== tagToRemove)
- });
- };
-
- // 处理年龄滑块变化
- const handleAgeChange = (value: number[]) => {
- setLocalRole({
- ...localRole,
- age: value[0]
- });
- };
-
- // 处理描述更新
- const handleDescriptionChange = (e: React.ChangeEvent) => {
- setLocalRole({
- ...localRole,
- roleDescription: e.target.value
- });
-
- // 自动调整文本框高度
- if (textareaRef.current) {
- textareaRef.current.style.height = 'auto';
- textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px';
- }
- };
-
- // 新增智能优化处理函数
- const handleSmartOptimize = () => {
- console.log('Optimizing character description...');
- // TODO: 调用 AI 优化接口
+ onSketchSelect(index);
+ setCurrentRole(roles[index]);
};
// 如果没有角色数据,显示占位内容
@@ -137,9 +143,6 @@ export function CharacterTabContent({
);
}
- // 获取当前选中的角色
- const currentRole = roles[currentRoleIndex];
-
return (
{/* 上部分:角色缩略图 */}
@@ -158,7 +161,7 @@ export function CharacterTabContent({
'aspect-[9/16]',
currentRoleIndex === index ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50'
)}
- onClick={() => onSketchSelect(index)}
+ onClick={() => handleChangeRole(index)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
@@ -183,48 +186,7 @@ export function CharacterTabContent({
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
>
- {/* 左列:角色预览 */}
-
- {/* 角色预览图 */}
-
-

-
-
- console.log('regenerate character')}
- />
-
-
-
-
- {/* 操作按钮 */}
-
-
-
-
-
-
- {/* 右列:角色信息 */}
+ {/* 左列:角色信息 */}
{
+ handleReplaceCharacter(url);
+ }}
/>
+
+ {/* 右列:角色预览 */}
+
+ {/* 角色预览图 */}
+
+
+ {/* 应用角色按钮 */}
+
+ setIsReplaceLibraryOpen(true)}
+ >
+
+
+
+
+
+
+
+
+
+
+ handleCloseReplacePanel()}
+ onConfirm={handleConfirmReplace}
+ />
+
+
+ {/* 从角色库中选择角色 */}
+
+ {/* 标题 从角色库中选择角色 */}
+ Role Library
+ {/* 内容 */}
+ {
+ console.log('index', index);
+ }}
+ />
+ {/* 操作按钮 */}
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/components/ui/replace-character-panel.tsx b/components/ui/replace-character-panel.tsx
new file mode 100644
index 0000000..498a417
--- /dev/null
+++ b/components/ui/replace-character-panel.tsx
@@ -0,0 +1,64 @@
+import { ReplacePanel } from './replace-panel';
+import { Shot, Character } from '@/app/model/types';
+
+interface ReplaceCharacterPanelProps {
+ shots: Shot[];
+ character: Character;
+ onClose: () => void;
+ onConfirm: (selectedShots: string[], addToLibrary: boolean) => void;
+}
+
+// Mock数据
+export const mockShots: Shot[] = [
+ {
+ id: '1',
+ thumbnailUrl: '/assets/3dr_chihiro.png',
+ videoUrl: 'https://video-base-imf.oss-ap-southeast-7.aliyuncs.com/uploads/FJ1-0-20250725023719.mp4',
+ isGenerating: false,
+ isSelected: true,
+ },
+ {
+ id: '2',
+ thumbnailUrl: '/assets/3dr_mono.png',
+ isGenerating: true,
+ isSelected: true,
+ },
+ {
+ id: '3',
+ thumbnailUrl: '/assets/3dr_howlcastle.png',
+ videoUrl: 'https://video-base-imf.oss-ap-southeast-7.aliyuncs.com/uploads/FJ3-0-20250725023725.mp4',
+ isGenerating: false,
+ isSelected: true,
+ },
+ {
+ id: '4',
+ thumbnailUrl: '/assets/3dr_spirited.jpg',
+ isGenerating: true,
+ isSelected: true,
+ },
+];
+
+export const mockCharacter: Character = {
+ id: '1',
+ name: '千寻',
+ avatarUrl: '/assets/3dr_chihiro.png',
+};
+
+export function ReplaceCharacterPanel({
+ shots = mockShots,
+ character = mockCharacter,
+ onClose,
+ onConfirm,
+}: ReplaceCharacterPanelProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/components/ui/replace-panel.tsx b/components/ui/replace-panel.tsx
new file mode 100644
index 0000000..420ae48
--- /dev/null
+++ b/components/ui/replace-panel.tsx
@@ -0,0 +1,195 @@
+import React, { useState, useRef } from 'react';
+import { motion } from 'framer-motion';
+import { Check, X, CircleAlert } from 'lucide-react';
+import { cn } from '@/public/lib/utils';
+
+// 定义类型
+interface Shot {
+ id: string;
+ videoUrl?: string;
+ thumbnailUrl: string;
+ isGenerating: boolean;
+ isSelected: boolean;
+}
+
+interface Item {
+ id: string;
+ name: string;
+ avatarUrl: string;
+}
+
+interface ReplacePanelProps {
+ title: string;
+ shots: Shot[];
+ item: Item;
+ showAddToLibrary?: boolean;
+ addToLibraryText?: string;
+ onClose: () => void;
+ onConfirm: (selectedShots: string[], addToLibrary: boolean) => void;
+}
+
+export function ReplacePanel({
+ title,
+ shots,
+ item,
+ showAddToLibrary = false,
+ addToLibraryText = "同步添加至库",
+ onClose,
+ onConfirm,
+}: ReplacePanelProps) {
+ const [selectedShots, setSelectedShots] = useState(
+ shots.filter(shot => shot.isSelected).map(shot => shot.id)
+ );
+ const [addToLibrary, setAddToLibrary] = useState(true);
+ const [hoveredVideoId, setHoveredVideoId] = useState(null);
+ const videoRefs = useRef<{ [key: string]: HTMLVideoElement }>({});
+
+ const handleShotToggle = (shotId: string) => {
+ setSelectedShots(prev =>
+ prev.includes(shotId)
+ ? prev.filter(id => id !== shotId)
+ : [...prev, shotId]
+ );
+ };
+
+ const handleSelectAllShots = (checked: boolean) => {
+ setSelectedShots(checked ? shots.map(shot => shot.id) : []);
+ };
+
+ const handleMouseEnter = (shotId: string) => {
+ setHoveredVideoId(shotId);
+ if (videoRefs.current[shotId]) {
+ videoRefs.current[shotId].play();
+ }
+ };
+
+ const handleMouseLeave = (shotId: string) => {
+ setHoveredVideoId(null);
+ if (videoRefs.current[shotId]) {
+ videoRefs.current[shotId].pause();
+ videoRefs.current[shotId].currentTime = 0;
+ }
+ };
+
+ const handleConfirm = () => {
+ onConfirm(selectedShots, addToLibrary);
+ };
+
+ return (
+
+ {/* 标题 */}
+
{title}
+
+ {/* 提示信息 */}
+
+
+
+ 该内容出现在 {shots.length} 个分镜中,替换后将影响如下分镜
+
+
+ handleSelectAllShots(e.target.checked)}
+ className="w-4 h-4 rounded border-white/20"
+ />
+
+
+
+
+ {/* 分镜展示区 */}
+
+
选择需要替换的分镜:
+
+ {shots.map((shot) => (
+
handleShotToggle(shot.id)}
+ onMouseEnter={() => handleMouseEnter(shot.id)}
+ onMouseLeave={() => handleMouseLeave(shot.id)}
+ whileHover={{ scale: 1.02 }}
+ whileTap={{ scale: 0.98 }}
+ >
+ {shot.videoUrl && (
+
+ ))}
+
+
+
+ {/* 预览信息 */}
+
+

+
{item.name}
+
+
+ {/* 同步到库选项 */}
+ {showAddToLibrary && (
+
+ setAddToLibrary(e.target.checked)}
+ className="w-4 h-4 rounded border-white/20"
+ />
+
+
+ )}
+
+ {/* 操作按钮 */}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/ui/replace-scene-panel.tsx b/components/ui/replace-scene-panel.tsx
new file mode 100644
index 0000000..266ca36
--- /dev/null
+++ b/components/ui/replace-scene-panel.tsx
@@ -0,0 +1,76 @@
+import { ReplacePanel } from './replace-panel';
+
+interface Shot {
+ id: string;
+ videoUrl?: string;
+ thumbnailUrl: string;
+ isGenerating: boolean;
+ isSelected: boolean;
+}
+
+interface Scene {
+ id: string;
+ name: string;
+ avatarUrl: string;
+}
+
+interface ReplaceScenePanelProps {
+ shots: Shot[];
+ scene: Scene;
+ onClose: () => void;
+ onConfirm: (selectedShots: string[], addToLibrary: boolean) => void;
+}
+
+// Mock数据
+export const mockShots: Shot[] = [
+ {
+ id: '1',
+ thumbnailUrl: '/assets/3dr_chihiro.png',
+ videoUrl: 'https://video-base-imf.oss-ap-southeast-7.aliyuncs.com/uploads/FJ1-0-20250725023719.mp4',
+ isGenerating: false,
+ isSelected: true,
+ },
+ {
+ id: '2',
+ thumbnailUrl: '/assets/3dr_mono.png',
+ isGenerating: true,
+ isSelected: true,
+ },
+ {
+ id: '3',
+ thumbnailUrl: '/assets/3dr_howlcastle.png',
+ videoUrl: 'https://video-base-imf.oss-ap-southeast-7.aliyuncs.com/uploads/FJ3-0-20250725023725.mp4',
+ isGenerating: false,
+ isSelected: true,
+ },
+ {
+ id: '4',
+ thumbnailUrl: '/assets/3dr_spirited.jpg',
+ isGenerating: true,
+ isSelected: true,
+ },
+];
+
+export const mockScene: Scene = {
+ id: '1',
+ name: '场景 1',
+ avatarUrl: '/assets/3dr_howlbg.jpg',
+};
+
+export function ReplaceScenePanel({
+ shots = mockShots,
+ scene = mockScene,
+ onClose,
+ onConfirm,
+}: ReplaceScenePanelProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/components/ui/scene-editor.tsx b/components/ui/scene-editor.tsx
index d849fe0..d168890 100644
--- a/components/ui/scene-editor.tsx
+++ b/components/ui/scene-editor.tsx
@@ -1,7 +1,7 @@
import { useState, useRef } from "react";
import { motion } from "framer-motion";
import { Button } from "@/components/ui/button";
-import { Sparkles, X, Plus, Clock, MapPin, Sun, Moon, Cloud, CloudRain, CloudSnow, CloudLightning, Palette } from 'lucide-react';
+import { Sparkles, X, Plus, Clock, MapPin, Sun, Moon, Cloud, CloudRain, CloudSnow, CloudLightning, Palette, RefreshCw } from 'lucide-react';
import { cn } from "@/public/lib/utils";
import ContentEditable from 'react-contenteditable';
@@ -38,6 +38,7 @@ interface SceneEditorProps {
onDescriptionChange?: (description: string) => void;
onAttributesChange?: (attributes: SceneAttribute[]) => void;
onEnvironmentChange?: (environment: SceneEnvironment) => void;
+ onReplaceScene?: (url: string) => void;
className?: string;
}
@@ -117,10 +118,12 @@ export default function SceneEditor({
onDescriptionChange,
onAttributesChange,
onEnvironmentChange,
+ onReplaceScene,
className
}: SceneEditorProps) {
const [inputText, setInputText] = useState(initialDescription);
const [isOptimizing, setIsOptimizing] = useState(false);
+ const [isRegenerating, setIsRegenerating] = useState(false);
const [customTags, setCustomTags] = useState([]);
const [newTag, setNewTag] = useState("");
const parseResult = useRef(mockParse(initialDescription));
@@ -190,8 +193,16 @@ export default function SceneEditor({
onEnvironmentChange?.(newParseResult.environment);
};
+ const handleRegenerate = () => {
+ setIsRegenerating(true);
+ setTimeout(() => {
+ onReplaceScene?.("https://c.huiying.video/images/0411ac7b-ab7e-4a17-ab4f-6880a28f8915.jpg");
+ setIsRegenerating(false);
+ }, 3000);
+ };
+
return (
-
+
{/* 自由输入区域 */}
@@ -249,60 +260,19 @@ export default function SceneEditor({
))}
- {/* 自定义标签区域 */}
-
-
- {customTags.map((tag) => (
-
- {tag}
-
-
- ))}
-
-
-
setNewTag(e.target.value)}
- onKeyPress={(e) => {
- if (e.key === 'Enter' && newTag.trim()) {
- setCustomTags(tags => [...tags, newTag.trim()]);
- setInputText((text: string) => text + (text.endsWith("。") ? "" : ",") + newTag.trim());
- setNewTag("");
- }
- }}
- placeholder="添加自定义标签..."
- className="flex-1 px-3 py-2 bg-white/5 border-none rounded-lg text-sm
- focus:outline-none focus:ring-2 focus:ring-blue-500 placeholder:text-white/30"
- />
-
-
-
+ {/* 重新生成按钮 */}
+
+
+ {isRegenerating ? "生成中..." : "重新生成"}
+
);
}
\ No newline at end of file
diff --git a/components/ui/scene-tab-content.tsx b/components/ui/scene-tab-content.tsx
index 110c6ff..1e443b5 100644
--- a/components/ui/scene-tab-content.tsx
+++ b/components/ui/scene-tab-content.tsx
@@ -2,9 +2,11 @@
import React, { useRef, useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
-import { Trash2, RefreshCw, Sun, Moon, Cloud, CloudRain, CloudSnow, CloudLightning, Sparkles, Clock, MapPin, Palette, Check, Plus } from 'lucide-react';
+import { Trash2, RefreshCw, Sun, Moon, Cloud, CloudRain, CloudSnow, CloudLightning, Sparkles, Clock, MapPin, Palette, Check, Plus, ReplaceAll } from 'lucide-react';
import { cn } from '@/public/lib/utils';
import SceneEditor from './scene-editor';
+import FloatingGlassPanel from './FloatingGlassPanel';
+import { ReplaceScenePanel, mockShots } from './replace-scene-panel';
interface SceneEnvironment {
time: {
@@ -74,6 +76,10 @@ export function SceneTabContent({
const [localSketch, setLocalSketch] = useState(mockSketch);
const textareaRef = useRef
(null);
+ const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false);
+ const [replacePanelKey, setReplacePanelKey] = useState(0);
+ const [ignoreReplace, setIgnoreReplace] = useState(false);
+ const [currentScene, setCurrentScene] = useState(taskSketch[currentSketchIndex]);
// 天气图标映射
const weatherIcons = {
@@ -136,6 +142,41 @@ export function SceneTabContent({
}
}, [currentSketchIndex]);
+ const handleReplaceScene = (url: string) => {
+ setCurrentScene({
+ ...currentScene,
+ url: url
+ });
+
+ setIsReplacePanelOpen(true);
+ };
+
+ const handleConfirmReplace = (selectedShots: string[], addToLibrary: boolean) => {
+ // 处理替换确认逻辑
+ console.log('Selected shots:', selectedShots);
+ console.log('Add to library:', addToLibrary);
+ setIsReplacePanelOpen(false);
+ };
+
+ const handleCloseReplacePanel = () => {
+ setIsReplacePanelOpen(false);
+ setIgnoreReplace(true);
+ };
+
+ const handleChangeScene = (index: number) => {
+ if (currentScene?.url !== taskSketch[currentSketchIndex]?.url && !ignoreReplace) {
+ // 提示 场景已修改,弹出替换场景面板
+ if (isReplacePanelOpen) {
+ setReplacePanelKey(replacePanelKey + 1);
+ } else {
+ setIsReplacePanelOpen(true);
+ }
+ return;
+ }
+ onSketchSelect(index);
+ setCurrentScene(taskSketch[index]);
+ };
+
// 如果没有数据,显示空状态
if (sketches.length === 0) {
return (
@@ -166,7 +207,7 @@ export function SceneTabContent({
'relative flex-shrink-0 w-32 aspect-video rounded-lg overflow-hidden cursor-pointer group',
currentSketchIndex === index ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50'
)}
- onClick={() => onSketchSelect(index)}
+ onClick={() => handleChangeScene(index)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
@@ -194,7 +235,7 @@ export function SceneTabContent({
))}
{/* 新增占位符 */}
-
添加场景
-
+ */}
@@ -228,7 +269,7 @@ export function SceneTabContent({
'flex-shrink-0 cursor-pointer transition-all duration-300',
isActive ? 'text-white' : 'text-white/50 hover:text-white/80'
)}
- onClick={() => onSketchSelect(index)}
+ onClick={() => handleChangeScene(index)}
initial={false}
animate={{
scale: isActive ? 1.02 : 1,
@@ -262,41 +303,6 @@ export function SceneTabContent({
>
{/* 左列:脚本编辑器 */}