From 4d127a038bf768eded68aae3cfa5c1b12241bdac Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Fri, 27 Mar 2026 00:55:15 +0100 Subject: chore: normalize line endings --- .../panels/assets/views/settings/background.tsx | 454 ++++++------- .../editor/panels/assets/views/settings/index.tsx | 738 ++++++++++----------- 2 files changed, 596 insertions(+), 596 deletions(-) (limited to 'apps/web/src/components/editor/panels/assets/views/settings') diff --git a/apps/web/src/components/editor/panels/assets/views/settings/background.tsx b/apps/web/src/components/editor/panels/assets/views/settings/background.tsx index b3ef1eb1..7a81188f 100644 --- a/apps/web/src/components/editor/panels/assets/views/settings/background.tsx +++ b/apps/web/src/components/editor/panels/assets/views/settings/background.tsx @@ -1,227 +1,227 @@ -"use client"; - -import { memo, useCallback, useEffect, useMemo, useRef } from "react"; -import { - Section, - SectionContent, - SectionHeader, - SectionTitle, -} from "@/components/section"; -import { - BLUR_INTENSITY_PRESETS, - DEFAULT_BLUR_INTENSITY, - DEFAULT_COLOR, -} from "@/constants/project-constants"; -import { patternCraftGradients } from "@/data/colors/pattern-craft"; -import { colors } from "@/data/colors/solid"; -import { syntaxUIGradients } from "@/data/colors/syntax-ui"; -import { useEditor } from "@/hooks/use-editor"; -import { effectPreviewService } from "@/services/renderer/effect-preview"; -import type { TCanvasSize } from "@/lib/project/types"; -import { cn } from "@/utils/ui"; - -const BlurPreview = memo( - ({ - blur, - canvasSize, - isSelected, - onSelect, - }: { - blur: { label: string; value: number }; - canvasSize: TCanvasSize; - isSelected: boolean; - onSelect: () => void; - }) => { - const canvasRef = useRef(null); - const { width, height } = canvasSize; - - useEffect(() => { - const renderPreview = () => { - if (!canvasRef.current) return; - - effectPreviewService.renderPreview({ - effectType: "blur", - params: { intensity: blur.value }, - targetCanvas: canvasRef.current, - uniformDimensions: { width, height }, - }); - }; - - renderPreview(); - return effectPreviewService.onPreviewImageReady({ callback: renderPreview }); - }, [blur.value, width, height]); - - return ( - - ); - }, -); - -BlurPreview.displayName = "BlurPreview"; - -const BackgroundPreviews = memo( - ({ - backgrounds, - currentBackgroundColor, - isColorBackground, - onSelect, - useBackgroundColor = false, - }: { - backgrounds: string[]; - currentBackgroundColor: string; - isColorBackground: boolean; - onSelect: (bg: string) => void; - useBackgroundColor?: boolean; - }) => { - return useMemo( - () => - backgrounds.map((bg) => ( - + ); + }, +); + +BlurPreview.displayName = "BlurPreview"; + +const BackgroundPreviews = memo( + ({ + backgrounds, + currentBackgroundColor, + isColorBackground, + onSelect, + useBackgroundColor = false, + }: { + backgrounds: string[]; + currentBackgroundColor: string; + isColorBackground: boolean; + onSelect: (bg: string) => void; + useBackgroundColor?: boolean; + }) => { + return useMemo( + () => + backgrounds.map((bg) => ( + - ); -} - -function AspectRatioPreview({ ratio }: { ratio?: string }) { - if (!ratio) return null; - - const [w, h] = ratio.split(":").map(Number); - const maxSize = 16; - const width = w >= h ? maxSize : (w / h) * maxSize; - const height = h >= w ? maxSize : (h / w) * maxSize; - - return ( -
- ); -} +"use client"; + +import { useRef, useState } from "react"; +import { PanelView } from "@/components/editor/panels/assets/views/base-panel"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { FPS_PRESETS } from "@/constants/project-constants"; +import { useEditor } from "@/hooks/use-editor"; +import { + Section, + SectionContent, + SectionHeader, + SectionTitle, +} from "@/components/section"; +import { BackgroundContent } from "./background"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { NumberField } from "@/components/ui/number-field"; +import { useEditorStore } from "@/stores/editor-store"; +import { usePropertyDraft } from "@/components/editor/panels/properties/hooks/use-property-draft"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Tick02Icon } from "@hugeicons/core-free-icons"; +import { cn } from "@/utils/ui"; +import { dimensionToAspectRatio } from "@/utils/geometry"; +import { formatNumberForDisplay } from "@/utils/math"; +import { OcSquarePlusIcon } from "@opencut/ui/icons"; +import type { TCanvasSize } from "@/lib/project/types"; + +type SettingsView = "project-info" | "background"; + +const PRESET_LABELS: Record = { + "1:1": "1:1", + "16:9": "16:9", + "9:16": "9:16", + "4:3": "4:3", +}; + +function areCanvasSizesEqual({ + left, + right, +}: { + left: TCanvasSize; + right: TCanvasSize; +}) { + return left.width === right.width && left.height === right.height; +} + +function formatCanvasDimension({ value }: { value: number }) { + return formatNumberForDisplay({ value, maxFractionDigits: 0 }); +} + +function parseCanvasDimension({ input }: { input: string }): number | null { + const trimmed = input.trim(); + if (!trimmed) return null; + + const parsed = Number(trimmed); + if (!Number.isFinite(parsed)) return null; + + const rounded = Math.round(parsed); + return rounded > 0 ? rounded : null; +} + +function useCanvasDimensionDraft({ + value, + onCommit, +}: { + value: number; + onCommit: (value: number) => void; +}) { + const pendingValueRef = useRef(value); + const syncedValueRef = useRef(value); + + if (syncedValueRef.current !== value) { + syncedValueRef.current = value; + pendingValueRef.current = value; + } + + return usePropertyDraft({ + displayValue: formatCanvasDimension({ value }), + parse: (input) => parseCanvasDimension({ input }), + onPreview: (nextValue) => { + pendingValueRef.current = nextValue; + }, + onCommit: () => { + if (pendingValueRef.current !== value) { + onCommit(pendingValueRef.current); + } + }, + }); +} + +export function SettingsView() { + const [view, setView] = useState("project-info"); + const editor = useEditor(); + const activeProject = useEditor((e) => e.project.getActive()); + const { canvasPresets } = useEditorStore(); + const currentCanvasSize = activeProject.settings.canvasSize; + const canvasSizeMode = activeProject.settings.canvasSizeMode ?? "preset"; + const lastCustomCanvasSize = + activeProject.settings.lastCustomCanvasSize ?? null; + + const presetItems = canvasPresets.map((preset, index) => { + const ratio = dimensionToAspectRatio(preset); + return { + id: index.toString(), + label: PRESET_LABELS[ratio] ?? ratio, + ratio, + canvasSize: preset, + }; + }); + + const selectedPresetId = canvasSizeMode === "preset" + ? (presetItems.find((preset) => + areCanvasSizesEqual({ + left: preset.canvasSize, + right: currentCanvasSize, + }), + )?.id ?? null) + : null; + + const updateCustomCanvasSize = ({ + canvasSize, + }: { + canvasSize: TCanvasSize; + }) => { + const shouldUpdateCanvasSize = !areCanvasSizesEqual({ + left: canvasSize, + right: currentCanvasSize, + }); + const shouldUpdateLastCustomCanvasSize = + lastCustomCanvasSize === null || + !areCanvasSizesEqual({ + left: canvasSize, + right: lastCustomCanvasSize, + }); + const shouldUpdateCanvasSizeMode = canvasSizeMode !== "custom"; + + if ( + !shouldUpdateCanvasSize && + !shouldUpdateLastCustomCanvasSize && + !shouldUpdateCanvasSizeMode + ) { + return; + } + + editor.project.updateSettings({ + settings: { + ...(shouldUpdateCanvasSize ? { canvasSize } : {}), + ...(shouldUpdateCanvasSizeMode + ? { canvasSizeMode: "custom" as const } + : {}), + lastCustomCanvasSize: canvasSize, + }, + }); + }; + + const selectPresetCanvasSize = ({ + canvasSize, + }: { + canvasSize: TCanvasSize; + }) => { + const shouldUpdateCanvasSize = !areCanvasSizesEqual({ + left: canvasSize, + right: currentCanvasSize, + }); + const shouldUpdateCanvasSizeMode = canvasSizeMode !== "preset"; + + if (!shouldUpdateCanvasSize && !shouldUpdateCanvasSizeMode) return; + + editor.project.updateSettings({ + settings: { + ...(shouldUpdateCanvasSize ? { canvasSize } : {}), + ...(shouldUpdateCanvasSizeMode + ? { canvasSizeMode: "preset" as const } + : {}), + }, + }); + }; + + const selectCustomCanvasSize = () => { + updateCustomCanvasSize({ + canvasSize: lastCustomCanvasSize ?? currentCanvasSize, + }); + }; + + const widthDraft = useCanvasDimensionDraft({ + value: currentCanvasSize.width, + onCommit: (width) => + updateCustomCanvasSize({ + canvasSize: { width, height: currentCanvasSize.height }, + }), + }); + + const heightDraft = useCanvasDimensionDraft({ + value: currentCanvasSize.height, + onCommit: (height) => + updateCustomCanvasSize({ + canvasSize: { width: currentCanvasSize.width, height }, + }), + }); + + const isCustomSelected = canvasSizeMode === "custom"; + + return ( + setView(v as SettingsView)}> + + Project info + Background + + + } + > + {view === "project-info" && ( +
+
+ + Name + + {activeProject.metadata.name} + + +
+
+ + Frame rate + + +
+
+ + Aspect ratio + + + {presetItems.map((preset) => ( + } + isSelected={selectedPresetId === preset.id} + onClick={() => { + selectPresetCanvasSize({ + canvasSize: preset.canvasSize, + }); + }} + /> + ))} +
+ } + isSelected={isCustomSelected} + onClick={selectCustomCanvasSize} + uiOptions={ +
+ + +
+ } + /> +
+
+
+
+ )} + {view === "background" && } +
+ ); +} + +function AspectRatioItem({ + label, + previewIcon, + isSelected, + onClick, + uiOptions, +}: { + label: string; + previewIcon: React.ReactNode; + isSelected: boolean; + onClick: () => void; + uiOptions?: React.ReactNode; +}) { + return ( + + ); +} + +function AspectRatioPreview({ ratio }: { ratio?: string }) { + if (!ratio) return null; + + const [w, h] = ratio.split(":").map(Number); + const maxSize = 16; + const width = w >= h ? maxSize : (w / h) * maxSize; + const height = h >= w ? maxSize : (h / w) * maxSize; + + return ( +
+ ); +} -- cgit v1.3.1