diff --git a/src/app/api/__tests__/popiserverModels.test.ts b/src/app/api/__tests__/popiserverModels.test.ts index abba04d8..2d03247f 100644 --- a/src/app/api/__tests__/popiserverModels.test.ts +++ b/src/app/api/__tests__/popiserverModels.test.ts @@ -166,4 +166,29 @@ describe("popiserver model helpers", () => { }), ]); }); + + it("uses displayDimensions name as the request parameter name", () => { + const parameters = buildPopiModelParameters({ + id: 38, + code: "model-with-aspect-ratio-key", + displayDimensions: JSON.stringify({ + dimensions: { + aspectRatio: { + name: "ratio", + options: [ + { value: "16:9", label: "16:9" }, + ], + }, + }, + }), + }); + + expect(parameters).toEqual([ + expect.objectContaining({ + name: "ratio", + options: [{ value: "16:9", label: "16:9" }], + default: "16:9", + }), + ]); + }); }); diff --git a/src/app/api/_popiserverModels.ts b/src/app/api/_popiserverModels.ts index 4acf6ea0..325c78ae 100644 --- a/src/app/api/_popiserverModels.ts +++ b/src/app/api/_popiserverModels.ts @@ -282,6 +282,15 @@ const POPI_DIMENSION_PARAMETER_TYPES: Record = { videoRatio: "integer", }; +const POPI_DISPLAY_DIMENSION_PARAMETER_NAMES = new Set([ + "ratio", + "aspectRatio", + "aspect_ratio", + "resolution", + "duration", + "videoRatio", +]); + function parsePopiDisplayDimensions(displayDimensions: string | undefined): Record | null { if (!displayDimensions) return null; try { @@ -300,6 +309,10 @@ function buildPopiDisplayDimensionParameters(model: PopiAIModel): ModelParameter const parameters: ModelParameter[] = []; for (const [dimensionKey, dimension] of Object.entries(dimensions)) { + const dimensionName = typeof dimension.name === "string" ? dimension.name.trim() : ""; + const parameterName = POPI_DISPLAY_DIMENSION_PARAMETER_NAMES.has(dimensionName) + ? dimensionName + : dimensionKey; const options = Array.isArray(dimension.options) ? dimension.options .filter((option) => option.value !== undefined) @@ -314,9 +327,9 @@ function buildPopiDisplayDimensionParameters(model: PopiAIModel): ModelParameter ?? (typeof options[0]?.value === "number" ? "number" : "string"); parameters.push({ - name: dimensionKey, + name: parameterName, type, - label: typeof dimension.name === "string" ? dimension.name : undefined, + label: dimensionName || undefined, options, default: options[0]?.value, required: false, diff --git a/src/app/api/generate/providers/__tests__/popiserver.test.ts b/src/app/api/generate/providers/__tests__/popiserver.test.ts index a8063931..fa682629 100644 --- a/src/app/api/generate/providers/__tests__/popiserver.test.ts +++ b/src/app/api/generate/providers/__tests__/popiserver.test.ts @@ -83,7 +83,8 @@ describe("popiserver generation provider", () => { expect(result.taskId).toBe("1189"); const [, init] = vi.mocked(global.fetch).mock.calls[0]; expect(init?.method).toBe("POST"); - expect(JSON.parse(String(init?.body))).toMatchObject({ + const body = JSON.parse(String(init?.body)); + expect(body).toMatchObject({ type: 2, subType: 203, aiPlatform: "GATEWAY", @@ -94,10 +95,11 @@ describe("popiserver generation provider", () => { aiModelCodeAlias: "viduq2-pro", batchSize: 1, ratio: "16:9", - aspectRatio: "16:9", resolution: "720", videoRatio: 10, }); + expect(body.aspectRatio).toBeUndefined(); + expect(body.aspect_ratio).toBeUndefined(); }); it("embeds ratio and resolution in chatPrompt for gpt-image-2-vip", async () => { @@ -133,7 +135,8 @@ describe("popiserver generation provider", () => { await submitPopiTask(makeRequest(), "login-token", input); const [, init] = vi.mocked(global.fetch).mock.calls[0]; - expect(JSON.parse(String(init?.body))).toMatchObject({ + const body = JSON.parse(String(init?.body)); + expect(body).toMatchObject({ chatPrompt: "比例:16:9\n清晰度:1K\n提示词:create a cinematic cat", model: "", ratio: "16:9", @@ -167,19 +170,22 @@ describe("popiserver generation provider", () => { }; input.prompt = "create a clean product render"; input.parameters = { - aspectRatio: "9:16", + ratio: "9:16", resolution: "2K", }; await submitPopiTask(makeRequest(), "login-token", input); const [, init] = vi.mocked(global.fetch).mock.calls[0]; - expect(JSON.parse(String(init?.body))).toMatchObject({ + const body = JSON.parse(String(init?.body)); + expect(body).toMatchObject({ chatPrompt: "比例:9:16\n清晰度:2K\n提示词:create a clean product render", model: "", - aspectRatio: "9:16", + ratio: "9:16", resolution: "2K", }); + expect(body.aspectRatio).toBeUndefined(); + expect(body.aspect_ratio).toBeUndefined(); }); it("builds referenceSubjectList for image tasks without @ in names", async () => { diff --git a/src/app/api/generate/providers/popiserver.ts b/src/app/api/generate/providers/popiserver.ts index e9619ac7..ef402354 100644 --- a/src/app/api/generate/providers/popiserver.ts +++ b/src/app/api/generate/providers/popiserver.ts @@ -335,6 +335,14 @@ function resolveAspectRatio(parameters: Record | undefined): st return asString(parameters?.ratio) ?? asString(parameters?.aspectRatio) ?? "16:9"; } +function removePopiRatioAliases(parameters: Record): Record { + const normalized = { ...parameters }; + delete normalized.ratio; + delete normalized.aspectRatio; + delete normalized.aspect_ratio; + return normalized; +} + function dimensionsForAspectRatio(aspectRatio: string): { width: number; height: number } { if (aspectRatio === "9:16") return { width: 720, height: 1280 }; if (aspectRatio === "1:1") return { width: 1024, height: 1024 }; @@ -462,7 +470,8 @@ function buildReferenceSubjectList( function buildPopiTaskBody(input: GenerationInput, modelDetail: PopiAIModel | null): Record { const rawParameters = removeLegacyAudioParameters(input.parameters || {}); - const parameters = { ...rawParameters }; + const aspectRatio = resolveAspectRatio(rawParameters); + const parameters = removePopiRatioAliases(rawParameters); delete parameters.referenceSubjectList; const selectedModel = { provider: "popiserver" as const, @@ -473,7 +482,6 @@ function buildPopiTaskBody(input: GenerationInput, modelDetail: PopiAIModel | nu }; const type = asNumber(parameters.type) ?? inferTaskType(input); const subType = asNumber(parameters.subType) ?? inferTaskSubType(input, type); - const aspectRatio = resolveAspectRatio(parameters); const dimensions = dimensionsForAspectRatio(aspectRatio); const modelCode = asString(input.model.metadata?.aiModelCode) ?? @@ -535,7 +543,7 @@ function buildPopiTaskBody(input: GenerationInput, modelDetail: PopiAIModel | nu styleId: asNumber(parameters.styleId) ?? 0, width: asNumber(parameters.width) ?? dimensions.width, height: asNumber(parameters.height) ?? dimensions.height, - aspectRatio, + ratio: aspectRatio, duration: asNumber(parameters.duration) ?? undefined, resolution: asString(parameters.resolution) ?? "720p", }; diff --git a/src/app/canvas/page.tsx b/src/app/canvas/page.tsx index 5760c0fd..d70181c5 100644 --- a/src/app/canvas/page.tsx +++ b/src/app/canvas/page.tsx @@ -24,7 +24,7 @@ function parseCanvasTheme(value: string | null): CanvasTheme | null { } function getAppliedCanvasTheme(): CanvasTheme { - return document.documentElement.dataset.canvasTheme === "light" ? "light" : "dark"; + return document.documentElement.dataset.canvasTheme === "dark" ? "dark" : "light"; } function applyCanvasTheme(theme: CanvasTheme) { @@ -76,7 +76,7 @@ function CanvasPageContent() { useEffect(() => { return () => { - delete document.documentElement.dataset.canvasTheme; + document.documentElement.dataset.canvasTheme = "light"; }; }, []); diff --git a/src/app/globals.css b/src/app/globals.css index fffe8cbe..bb36dbbd 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -8,7 +8,7 @@ color-scheme: dark; --canvas-bg: #101112; --canvas-background: var(--canvas-bg); - --canvas-grid: rgba(255, 255, 255, 0.035); + --canvas-grid: #ffffff; --surface-1: #171819; --surface-2: #202122; --surface-3: #2a2b2d; @@ -61,8 +61,8 @@ :root[data-canvas-theme="light"] { color-scheme: light; --canvas-bg: #f0f0f0; - --canvas-background: linear-gradient(132deg, #E9DAEE 3.04%, #EAEBEB 24.64%, #F0F0F0 50%, #DAD6E5 96.96%); - --canvas-grid: rgba(15, 23, 42, 0.025); + --canvas-background: linear-gradient(132deg, #E9DAEE 3.04%, #E9E9E9 50%, #DAD6E5 96.96%); + --canvas-grid: #EBEBEB; --surface-1: #ffffff; --surface-2: #ffffff; --surface-3: #f3f3f2; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0ab0ea70..2f5d85d5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -21,12 +21,15 @@ const canvasThemeScript = ` return; } const stored = window.localStorage.getItem("node-banana-canvas-navigation"); - if (!stored) return; - const settings = JSON.parse(stored); - if (settings && settings.theme === "light") { + if (!stored) { document.documentElement.dataset.canvasTheme = "light"; + return; } - } catch {} + const settings = JSON.parse(stored); + document.documentElement.dataset.canvasTheme = settings && settings.theme === "dark" ? "dark" : "light"; + } catch { + document.documentElement.dataset.canvasTheme = "light"; + } })(); `; diff --git a/src/components/AntdThemeProvider.tsx b/src/components/AntdThemeProvider.tsx index d5a49f3b..b454af88 100644 --- a/src/components/AntdThemeProvider.tsx +++ b/src/components/AntdThemeProvider.tsx @@ -34,32 +34,32 @@ interface CanvasThemeTokens { } const FALLBACK_TOKENS: CanvasThemeTokens = { - mode: "dark", - background: "#171717", - panel: "#262626", - panelElevated: "#1f1f1f", - panelMuted: "#333333", - panelHover: "#404040", - panelActive: "#525252", - overlay: "rgba(0, 0, 0, 0.6)", - border: "#404040", - borderStrong: "#525252", - text: "#fafafa", - textSecondary: "#d4d4d4", - textMuted: "#a3a3a3", - textDisabled: "#737373", + mode: "light", + background: "#f0f0f0", + panel: "#ffffff", + panelElevated: "#ffffff", + panelMuted: "#f3f3f2", + panelHover: "#f5f5f4", + panelActive: "#ececea", + overlay: "rgba(15, 23, 42, 0.38)", + border: "rgba(234, 235, 236, 0.24)", + borderStrong: "rgba(15, 23, 42, 0.12)", + text: "#1f2328", + textSecondary: "#4b5563", + textMuted: "#737780", + textDisabled: "#a1a4aa", textOnOverlay: "#ffffff", - accent: "#3b82f6", - accentHover: "#60a5fa", - accentSoft: "rgba(59, 130, 246, 0.16)", - accentRing: "rgba(59, 130, 246, 0.4)", - danger: "#ef4444", - dangerSoft: "rgba(239, 68, 68, 0.16)", - success: "#10b981", - successSoft: "rgba(16, 185, 129, 0.16)", - warning: "#f59e0b", - warningSoft: "rgba(245, 158, 11, 0.16)", - shadowPanel: "0 12px 30px rgba(0, 0, 0, 0.35)", + accent: "#2563eb", + accentHover: "#1d4ed8", + accentSoft: "rgba(37, 99, 235, 0.12)", + accentRing: "rgba(37, 99, 235, 0.28)", + danger: "#dc2626", + dangerSoft: "rgba(220, 38, 38, 0.12)", + success: "#059669", + successSoft: "rgba(5, 150, 105, 0.12)", + warning: "#d97706", + warningSoft: "rgba(217, 119, 6, 0.14)", + shadowPanel: "0 8px 22px rgba(15, 23, 42, 0.08)", }; function readCssToken(styles: CSSStyleDeclaration, name: string, fallback: string): string { @@ -72,7 +72,7 @@ function readCanvasThemeTokens(): CanvasThemeTokens { } const styles = window.getComputedStyle(document.documentElement); - const mode = document.documentElement.dataset.canvasTheme === "light" ? "light" : "dark"; + const mode = document.documentElement.dataset.canvasTheme === "dark" ? "dark" : "light"; return { mode, diff --git a/src/components/AssetLibraryDrawer.tsx b/src/components/AssetLibraryDrawer.tsx index 63a6ad5d..ad8b3976 100644 --- a/src/components/AssetLibraryDrawer.tsx +++ b/src/components/AssetLibraryDrawer.tsx @@ -402,16 +402,6 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) { }, [childrenByFolderId, creatingFolderParentId, loadedFolderIds, loadingFolderIds]); const treeData = useMemo(() => buildTreeNodes(folders, 0), [buildTreeNodes, folders]); - const movePickerFolders = useMemo(() => [{ - id: ROOT_PARENT_ID, - parentId: ROOT_PARENT_ID, - nodeType: "folder", - name: t("assetLibrary.rootFolder"), - }], [t]); - const movePickerChildrenByFolderId = useMemo(() => ({ - ...childrenByFolderId, - [ROOT_PARENT_ID]: folders, - }), [childrenByFolderId, folders]); const disabledMoveFolderIds = useMemo(() => { if (!movingItem || movingItem.nodeType !== "folder") return undefined; @@ -1201,11 +1191,11 @@ export function AssetLibraryDrawer({ open, onClose }: AssetLibraryDrawerProps) { open={movingItem !== null} title={t("assetLibrary.moveTitle")} confirmText={t("assetLibrary.move")} - folders={movePickerFolders} - childrenByFolderId={movePickerChildrenByFolderId} + folders={folders} + childrenByFolderId={childrenByFolderId} loadingFolderIds={loadingFolderIds} loadedFolderIds={loadedFolderIds} - initialFolderId={movingItem?.parentId ?? ROOT_PARENT_ID} + initialFolderId={movingItem?.parentId === ROOT_PARENT_ID ? null : movingItem?.parentId ?? null} disabledFolderIds={disabledMoveFolderIds} confirmLoading={submittingMove} onLoadFolderChildren={loadFolderChildren} diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 71b2d198..0016cf0f 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -139,6 +139,9 @@ function isLocalKeyboardTarget(target: EventTarget | null): boolean { // Let fitView zoom out for large workflows, but never zoom in a single new node. const FIT_VIEW_OPTIONS = { maxZoom: 1 }; +const CANVAS_MIN_ZOOM = 0.1; +const CANVAS_MAX_ZOOM = 4; +const WHEEL_ZOOM_FACTOR = 1.15; // Connection validation rules // - Image handles can only connect to image handles @@ -439,7 +442,7 @@ export function WorkflowCanvas() { const canUndo = useWorkflowStore((state) => state.canUndo); const canRedo = useWorkflowStore((state) => state.canRedo); const openAnnotationModal = useAnnotationStore((state) => state.openModal); - const { screenToFlowPosition, getViewport, zoomIn, zoomOut, setViewport, setCenter, fitView } = useReactFlow(); + const { screenToFlowPosition, getViewport, setViewport, setCenter, fitView } = useReactFlow(); const { show: showToast } = useToast(); const [isDragOver, setIsDragOver] = useState(false); const [dropType, setDropType] = useState<"image" | "video" | "audio" | "workflow" | "node" | "text" | null>(null); @@ -455,27 +458,59 @@ export function WorkflowCanvas() { const miniMapNodeColor = "var(--text-disabled)"; const boxSelectionActiveRef = useRef(false); const boxSelectionEndTimerRef = useRef | null>(null); + const reactFlowWrapper = useRef(null); + const tutorialViewportSet = useRef(false); const syncZoomPercent = useCallback(() => { setZoomPercent(Math.round(getViewport().zoom * 100)); }, [getViewport]); - const handleZoomPercentChange = useCallback((value: number) => { + const getCanvasScreenCenter = useCallback(() => { + const bounds = reactFlowWrapper.current?.getBoundingClientRect(); + if (!bounds) { + return { x: window.innerWidth / 2, y: window.innerHeight / 2 }; + } + + return { + x: bounds.left + bounds.width / 2, + y: bounds.top + bounds.height / 2, + }; + }, []); + + const zoomAtScreenPoint = useCallback(( + point: { x: number; y: number }, + zoom: number, + options?: { duration?: number } + ) => { const viewport = getViewport(); - const nextZoom = value / 100; - const centerX = window.innerWidth / 2; - const centerY = window.innerHeight / 2; - const flowCenterX = (centerX - viewport.x) / viewport.zoom; - const flowCenterY = (centerY - viewport.y) / viewport.zoom; + const currentZoom = Number.isFinite(viewport.zoom) && viewport.zoom > 0 ? viewport.zoom : 1; + const nextZoom = Math.min(CANVAS_MAX_ZOOM, Math.max(CANVAS_MIN_ZOOM, zoom)); + const flowX = (point.x - viewport.x) / currentZoom; + const flowY = (point.y - viewport.y) / currentZoom; - setZoomPercent(value); + setZoomPercent(Math.round(nextZoom * 100)); setViewport({ - x: centerX - flowCenterX * nextZoom, - y: centerY - flowCenterY * nextZoom, + x: point.x - flowX * nextZoom, + y: point.y - flowY * nextZoom, zoom: nextZoom, - }); + }, options); }, [getViewport, setViewport]); + const getCanvasFlowCenter = useCallback(() => { + const viewport = getViewport(); + const center = getCanvasScreenCenter(); + const zoom = Number.isFinite(viewport.zoom) && viewport.zoom > 0 ? viewport.zoom : 1; + + return { + centerX: (center.x - viewport.x) / zoom, + centerY: (center.y - viewport.y) / zoom, + }; + }, [getCanvasScreenCenter, getViewport]); + + const handleZoomPercentChange = useCallback((value: number) => { + zoomAtScreenPoint(getCanvasScreenCenter(), value / 100); + }, [getCanvasScreenCenter, zoomAtScreenPoint]); + const handleFitViewFromToolbar = useCallback(() => { void fitView({ ...FIT_VIEW_OPTIONS, padding: 0.2, duration: 300 }); }, [fitView]); @@ -519,9 +554,6 @@ export function WorkflowCanvas() { }); }, [canvasNavigationSettings, updateCanvasNavigationSettings]); - const reactFlowWrapper = useRef(null); - const tutorialViewportSet = useRef(false); - // FTUX tutorial state (client-side only to avoid SSR hydration issues) const [tutorialActive, setTutorialActive] = useState(false); const [lockedFeatures, setLockedFeatures] = useState(false); @@ -1868,15 +1900,16 @@ export function WorkflowCanvas() { if (scrollableElement) return; event.preventDefault(); - if (event.deltaY < 0) zoomIn(); - else zoomOut(); + const viewport = getViewport(); + const factor = event.deltaY < 0 ? WHEEL_ZOOM_FACTOR : 1 / WHEEL_ZOOM_FACTOR; + zoomAtScreenPoint({ x: event.clientX, y: event.clientY }, viewport.zoom * factor); }; wrapper.addEventListener('wheel', handleWheelNonPassive, { passive: false }); return () => { wrapper.removeEventListener('wheel', handleWheelNonPassive); }; - }, [isModalOpen, zoomIn, zoomOut]); + }, [getViewport, isModalOpen, zoomAtScreenPoint]); // Keyboard shortcuts for copy/paste and stacking selected nodes const handleKeyDown = useCallback((event: KeyboardEvent) => { @@ -1890,15 +1923,15 @@ export function WorkflowCanvas() { if (isZoomInKey || isZoomOutKey || isZoomResetKey) { event.preventDefault(); event.stopPropagation(); + const center = getCanvasScreenCenter(); + const viewport = getViewport(); if (isZoomInKey) { - zoomIn(); + zoomAtScreenPoint(center, viewport.zoom * WHEEL_ZOOM_FACTOR, { duration: 160 }); } else if (isZoomOutKey) { - zoomOut(); + zoomAtScreenPoint(center, viewport.zoom / WHEEL_ZOOM_FACTOR, { duration: 160 }); } else { - const viewport = getViewport(); - setViewport({ ...viewport, zoom: 1 }, { duration: 160 }); + zoomAtScreenPoint(center, 1, { duration: 160 }); } - syncZoomPercent(); return; } } @@ -1943,10 +1976,7 @@ export function WorkflowCanvas() { // Helper to get viewport center position in flow coordinates const getViewportCenter = () => { - const viewport = getViewport(); - const centerX = (-viewport.x + window.innerWidth / 2) / viewport.zoom; - const centerY = (-viewport.y + window.innerHeight / 2) / viewport.zoom; - return { centerX, centerY }; + return getCanvasFlowCenter(); }; // Handle node creation hotkeys (Shift + key) @@ -2056,9 +2086,7 @@ export function WorkflowCanvas() { }); } else { // No imageInput node selected - create a new one at viewport center - const viewport = getViewport(); - const centerX = (-viewport.x + window.innerWidth / 2) / viewport.zoom; - const centerY = (-viewport.y + window.innerHeight / 2) / viewport.zoom; + const { centerX, centerY } = getCanvasFlowCenter(); // ImageInput node default dimensions: 300x280 const nodeId = addNode("imageInput", { x: centerX - 150, y: centerY - 140 }); @@ -2076,9 +2104,7 @@ export function WorkflowCanvas() { const blob = await item.getType('text/plain'); const text = await blob.text(); if (text.trim()) { - const viewport = getViewport(); - const centerX = (-viewport.x + window.innerWidth / 2) / viewport.zoom; - const centerY = (-viewport.y + window.innerHeight / 2) / viewport.zoom; + const { centerX, centerY } = getCanvasFlowCenter(); const nodeId = addNode("smartText", { x: centerX - 160, y: centerY - 110 }); updateNodeData(nodeId, { manualLocked: true, manualText: text }); return; // Exit after handling text @@ -2182,7 +2208,7 @@ export function WorkflowCanvas() { ]); }); } - }, [nodes, onNodesChange, copySelectedNodes, pasteNodes, clearClipboard, clipboard, getViewport, addNode, updateNodeData, executeWorkflow, setShortcutsDialogOpen, undo, redo, isModalOpen, zoomIn, zoomOut, setViewport, syncZoomPercent]); + }, [nodes, onNodesChange, copySelectedNodes, pasteNodes, clearClipboard, clipboard, getViewport, getCanvasScreenCenter, getCanvasFlowCenter, addNode, updateNodeData, executeWorkflow, setShortcutsDialogOpen, undo, redo, isModalOpen, zoomAtScreenPoint]); useEffect(() => { window.addEventListener("keydown", handleKeyDown, { capture: true }); @@ -2722,8 +2748,8 @@ export function WorkflowCanvas() { zoomOnScroll={tutorialActive ? false : false} zoomOnPinch={tutorialActive ? false : !isModalOpen} zoomOnDoubleClick={false} - minZoom={0.1} - maxZoom={4} + minZoom={CANVAS_MIN_ZOOM} + maxZoom={CANVAS_MAX_ZOOM} defaultViewport={{ x: 0, y: 0, zoom: 1 }} panActivationKeyCode={null} nodesDraggable={!isModalOpen} diff --git a/src/store/utils/__tests__/localStorage.test.ts b/src/store/utils/__tests__/localStorage.test.ts index c2d22a06..7c47e78a 100644 --- a/src/store/utils/__tests__/localStorage.test.ts +++ b/src/store/utils/__tests__/localStorage.test.ts @@ -366,7 +366,7 @@ describe("localStorage utilities", () => { const result = getCanvasNavigationSettings(); expect(result).toEqual({ ...legacySettings, - theme: "dark", + theme: "light", assistantEnabled: false, }); }); diff --git a/src/types/canvas.ts b/src/types/canvas.ts index 56ce389e..2e1983e6 100644 --- a/src/types/canvas.ts +++ b/src/types/canvas.ts @@ -18,7 +18,7 @@ export interface CanvasNavigationSettings { } export const defaultCanvasNavigationSettings: CanvasNavigationSettings = { - theme: "dark", + theme: "light", panMode: "always", zoomMode: "scroll", selectionMode: "rightSpace", diff --git a/src/utils/__tests__/generationConfig.test.ts b/src/utils/__tests__/generationConfig.test.ts index c0e87767..339d5db3 100644 --- a/src/utils/__tests__/generationConfig.test.ts +++ b/src/utils/__tests__/generationConfig.test.ts @@ -128,7 +128,6 @@ describe("generationConfig", () => { subType: 203, aiModelId: 37, aiModelCode: "seedance", - aspectRatio: "9:16", ratio: "9:16", resolution: "1080p", duration: 8, diff --git a/src/utils/generationConfig.ts b/src/utils/generationConfig.ts index 77bff1ca..a0fa5ce0 100644 --- a/src/utils/generationConfig.ts +++ b/src/utils/generationConfig.ts @@ -131,7 +131,7 @@ export type PopiserverTaskPricePayload = { aiModelCode?: string; aiModelCodeAlias?: string; model: string; - aspectRatio: string; + ratio: string; resolution: string; duration?: number; width: number; @@ -561,7 +561,7 @@ export function buildPopiserverTaskPricePayload( aiModelCode: stringFromUnknown(model.metadata?.aiModelCode) || undefined, aiModelCodeAlias: stringFromUnknown(model.metadata?.aiModelCodeAlias) || undefined, model: modelName, - aspectRatio, + ratio: aspectRatio, resolution: config.resolution || (type === 2 ? DEFAULT_VIDEO_RESOLUTION : "2K"), duration: duration ?? undefined, width: dimensions.width,