diff --git a/.gitignore b/.gitignore index b3707d084..36e9689b0 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,12 @@ checkpoints/ # 第三方参考源码(不提交到git) reference-sources/ +# Development-only upload fallback. +public/uploads/local/ + +# Local isolated database and video-canvas test fixtures. +.local/ + # Local self-hosted Pascal Editor checkout. .pascal-editor/ diff --git a/app/(admin)/settings/video-canvas/video-canvas-settings-client.tsx b/app/(admin)/settings/video-canvas/video-canvas-settings-client.tsx index c79fba2c7..98ddda963 100644 --- a/app/(admin)/settings/video-canvas/video-canvas-settings-client.tsx +++ b/app/(admin)/settings/video-canvas/video-canvas-settings-client.tsx @@ -35,11 +35,30 @@ import { type VideoCanvasFunctionDefinition, type VideoCanvasEffectPreset, type VideoCanvasMotionPreset, + type VideoCanvasReferenceBehavior, + type VideoCanvasReferenceModelRule, type VideoCanvasRolePreset, + type VideoCanvasSubjectVoicePreset, VIDEO_CANVAS_FUNCTION_CONFIG_KEY, defaultVideoCanvasFunctionConfig, normalizeVideoCanvasFunctionConfig, } from "@/lib/video-canvas/config"; +import { + VIDEO_CANVAS_HISTORY_PICKER_MEDIA_LABELS, + VIDEO_CANVAS_HISTORY_PICKER_MEDIA_MODES, + VIDEO_CANVAS_HISTORY_ZOOM_LEVELS, + type VideoCanvasHistoryPickerMediaMode, + type VideoCanvasHistoryZoomPercent, +} from "@/lib/video-canvas/history-assets"; +import type { VideoHdResolution } from "@/lib/video-canvas/video-hd"; +import { + VIDEO_CANVAS_MODEL_ICON_KEYS, + type VideoCanvasModelCatalogEntry, +} from "@/lib/video-canvas/video-model-catalog"; +import type { + VideoCanvasToolboxConfig, + VideoCanvasToolboxTemplate, +} from "@/lib/video-canvas/toolbox-workflows"; type SettingsResponse = { setting?: { @@ -184,6 +203,735 @@ function ModelSelect({ ); } +function DirectorCameraPresetsEditor({ + onChange, + value, +}: { + onChange: (value: VideoCanvasFunctionConfig["directorDesk"]["cameraPresets"]) => void; + value: VideoCanvasFunctionConfig["directorDesk"]["cameraPresets"]; +}) { + const [draft, setDraft] = useState(() => JSON.stringify(value, null, 2)); + + useEffect(() => { + setDraft(JSON.stringify(value, null, 2)); + }, [value]); + + const applyDraft = () => { + try { + const parsed = JSON.parse(draft) as unknown; + if (!Array.isArray(parsed)) throw new Error("机位配置必须是数组"); + + const normalized = normalizeVideoCanvasFunctionConfig({ + ...defaultVideoCanvasFunctionConfig, + directorDesk: { + ...defaultVideoCanvasFunctionConfig.directorDesk, + cameraPresets: parsed, + }, + }).directorDesk.cameraPresets; + onChange(normalized); + setDraft(JSON.stringify(normalized, null, 2)); + toast.success(`已应用 ${normalized.length} 套导演台机位`); + } catch (error) { + toast.error(error instanceof Error ? error.message : "机位 JSON 无效"); + } + }; + + return ( +
+
+
+ +

+ source=current 使用当前视角;fixed 使用 position、target 和 fov 创建固定机位。 +

+
+ +
+