From 8161de00e684db1f78fa56f88ba3612b54af9b68 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 15 Apr 2026 06:26:55 +0200 Subject: [PATCH] chore: formatting --- .../editor/panels/assets/views/captions.tsx | 4 +- .../assets/views/settings/background.tsx | 11 +- .../panels/properties/tabs/effects-tab.tsx | 618 +++++++++--------- apps/web/src/lib/blog/query.ts | 7 +- .../feedback/components/feedback-popover.tsx | 47 +- .../migrations/__tests__/v1-to-v2.test.ts | 8 +- .../migrations/transformers/v1-to-v2.ts | 4 +- 7 files changed, 357 insertions(+), 342 deletions(-) diff --git a/apps/web/src/components/editor/panels/assets/views/captions.tsx b/apps/web/src/components/editor/panels/assets/views/captions.tsx index 9e394cfc..040df749 100644 --- a/apps/web/src/components/editor/panels/assets/views/captions.tsx +++ b/apps/web/src/components/editor/panels/assets/views/captions.tsx @@ -254,9 +254,7 @@ export function Captions() { - - {diagnostic.message} - + {diagnostic.message} ))} - - ); -} - -function EffectSection({ - effect, - renderParams, - previewParam, - onCommit, - onToggle, - onRemove, -}: { - effect: Effect; - renderParams: ParamValues; - previewParam: (key: string) => (value: number | string | boolean) => void; - onCommit: () => void; - onToggle?: () => void; - onRemove?: () => void; -}) { - const definition = effectsRegistry.get(effect.type); - - return ( -
- - - - - ) - } - > - - {definition.name} - - - - - {definition.params.map((param) => ( -
-
- -
- -
- ))} -
-
-
- ); -} +"use client"; + +import { useState } from "react"; +import type { ParamValues } from "@/lib/params"; +import type { Effect } from "@/lib/effects/types"; +import type { EffectElement, VisualElement } from "@/lib/timeline"; +import { effectsRegistry } from "@/lib/effects"; +import { useEditor } from "@/hooks/use-editor"; +import { useElementPreview } from "@/hooks/use-element-preview"; +import { + Section, + SectionContent, + SectionHeader, + SectionTitle, + SectionFields, +} from "@/components/section"; +import { PropertyParamField } from "../components/property-param-field"; +import { Button } from "@/components/ui/button"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { + Delete02Icon, + ViewIcon, + ViewOffSlashIcon, + MagicWand05Icon, +} from "@hugeicons/core-free-icons"; +import { cn } from "@/utils/ui"; +import { Separator } from "@/components/ui/separator"; +import { useAssetsPanelStore } from "@/stores/assets-panel-store"; + +export function StandaloneEffectTab({ + element, + trackId, +}: { + element: EffectElement; + trackId: string; +}) { + const { renderElement, previewUpdates, commit } = useElementPreview({ + trackId, + elementId: element.id, + fallback: element, + }); + + const effect: Effect = { + id: element.id, + type: element.effectType, + params: element.params, + enabled: true, + }; + + const previewParam = (key: string) => (value: number | string | boolean) => { + previewUpdates({ + params: { ...(renderElement as EffectElement).params, [key]: value }, + }); + }; + + return ( +
+
+ Effect +
+ +
+ ); +} + +export function ClipEffectsTab({ + element, + trackId, +}: { + element: VisualElement; + trackId: string; +}) { + const [dragIndex, setDragIndex] = useState(null); + const [dropIndex, setDropIndex] = useState(null); + const editor = useEditor(); + const { renderElement, previewUpdates, commit } = useElementPreview({ + trackId, + elementId: element.id, + fallback: element, + }); + + const effects: Effect[] = element.effects ?? []; + + const getRenderParams = ({ effectId }: { effectId: string }): ParamValues => { + return ( + (renderElement as VisualElement).effects?.find((ef) => ef.id === effectId) + ?.params ?? + effects.find((ef) => ef.id === effectId)?.params ?? + {} + ); + }; + + const buildPreviewParam = + (effectId: string) => + (key: string) => + (value: number | string | boolean) => { + const updatedEffects = ( + (renderElement as VisualElement).effects ?? [] + ).map((existing) => + existing.id !== effectId + ? existing + : { ...existing, params: { ...existing.params, [key]: value } }, + ); + previewUpdates({ effects: updatedEffects }); + }; + + const handleDragStart = ({ index }: { index: number }) => setDragIndex(index); + + const handleDragOver = ({ + event, + index, + }: { + event: React.DragEvent; + index: number; + }) => { + event.preventDefault(); + if (index !== dropIndex) setDropIndex(index); + }; + + const handleDrop = ({ toIndex }: { toIndex: number }) => { + if (dragIndex !== null && dragIndex !== toIndex) { + editor.timeline.reorderClipEffects({ + trackId, + elementId: element.id, + fromIndex: dragIndex, + toIndex, + }); + } + setDragIndex(null); + setDropIndex(null); + }; + + const handleDragEnd = () => { + setDragIndex(null); + setDropIndex(null); + }; + + return ( +
+
+ Effects +
+ {effects.length === 0 ? ( + + ) : ( +
    + {effects.map((effect, index) => { + const resolvedDragIndex = dragIndex ?? -1; + const isDragging = dragIndex === index; + const isDropTarget = + dropIndex === index && dragIndex !== null && dragIndex !== index; + const showTopDropIndicator = + isDropTarget && index < resolvedDragIndex; + const showBottomDropIndicator = + isDropTarget && index > resolvedDragIndex; + + return ( +
  • handleDragStart({ index })} + onDragOver={(event) => handleDragOver({ event, index })} + onDrop={() => handleDrop({ toIndex: index })} + onDragEnd={handleDragEnd} + className={cn( + "group list-none", + isDragging && "opacity-40", + showTopDropIndicator && "border-t-2 border-primary", + showBottomDropIndicator && "border-b-2 border-primary", + )} + > + + editor.timeline.toggleClipEffect({ + trackId, + elementId: element.id, + effectId: effect.id, + }) + } + onRemove={() => + editor.timeline.removeClipEffect({ + trackId, + elementId: element.id, + effectId: effect.id, + }) + } + /> +
  • + ); + })} +
+ )} +
+ ); +} + +function EmptyView() { + const setActiveTab = useAssetsPanelStore((s) => s.setActiveTab); + + return ( +
+ +
+

No effects

+

+ Add effects to this layer from the Assets panel. +

+
+ +
+ ); +} + +function EffectSection({ + effect, + renderParams, + previewParam, + onCommit, + onToggle, + onRemove, +}: { + effect: Effect; + renderParams: ParamValues; + previewParam: (key: string) => (value: number | string | boolean) => void; + onCommit: () => void; + onToggle?: () => void; + onRemove?: () => void; +}) { + const definition = effectsRegistry.get(effect.type); + + return ( +
+ + + + + ) + } + > + + {definition.name} + + + + + {definition.params.map((param) => ( +
+
+ +
+ +
+ ))} +
+
+
+ ); +} diff --git a/apps/web/src/lib/blog/query.ts b/apps/web/src/lib/blog/query.ts index 9e04a5bb..91c2a514 100644 --- a/apps/web/src/lib/blog/query.ts +++ b/apps/web/src/lib/blog/query.ts @@ -47,7 +47,12 @@ const EMPTY_AUTHORS: MarbleAuthorList = { }; function isMarbleConfigured() { - return !["", "placeholder", "build-placeholder", "your_workspace_key_here"].includes(key); + return ![ + "", + "placeholder", + "build-placeholder", + "your_workspace_key_here", + ].includes(key); } async function fetchFromMarble({ diff --git a/apps/web/src/lib/feedback/components/feedback-popover.tsx b/apps/web/src/lib/feedback/components/feedback-popover.tsx index 9bdc48f4..1b04bad6 100644 --- a/apps/web/src/lib/feedback/components/feedback-popover.tsx +++ b/apps/web/src/lib/feedback/components/feedback-popover.tsx @@ -166,18 +166,17 @@ function FeedbackPopoverContent({ onClose }: { onClose: () => void }) { {entries.length > 0 && ( -
-
- - Previous feedback - -
-
-
- {entries.map((entry) => ( - - ))} -
+
+
+ {entries.map((entry) => ( + + ))}
)} @@ -185,17 +184,29 @@ function FeedbackPopoverContent({ onClose }: { onClose: () => void }) { ); } -function FeedbackEntryItem({ entry }: { entry: FeedbackEntry }) { - const formatted = new Date(entry.createdAt).toLocaleDateString(undefined, { +function relativeDate(iso: string): string { + const diff = Date.now() - new Date(iso).getTime(); + const mins = Math.floor(diff / 60_000); + if (mins < 1) return "just now"; + if (mins < 60) return `${mins}m ago`; + const hrs = Math.floor(mins / 60); + if (hrs < 24) return `${hrs}h ago`; + const days = Math.floor(hrs / 24); + if (days < 7) return `${days}d ago`; + return new Date(iso).toLocaleDateString(undefined, { month: "short", day: "numeric", }); +} +function FeedbackEntryItem({ entry }: { entry: FeedbackEntry }) { return ( -
-

{entry.message}

- - {formatted} +
+

+ {entry.message} +

+ + {relativeDate(entry.createdAt)}
); diff --git a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts index 56c83403..9cea9a2e 100644 --- a/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts +++ b/apps/web/src/services/storage/migrations/__tests__/v1-to-v2.test.ts @@ -1,7 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { - DEFAULT_BACKGROUND_BLUR_INTENSITY, -} from "@/lib/background/blur"; +import { DEFAULT_BACKGROUND_BLUR_INTENSITY } from "@/lib/background/blur"; import { DEFAULT_BACKGROUND_COLOR } from "@/lib/background/color"; import { DEFAULT_CANVAS_SIZE } from "@/lib/canvas/sizes"; const DEFAULT_FPS = 30; @@ -135,9 +133,7 @@ describe("V1 to V2 Migration", () => { const settings = result.project.settings as Record; const background = settings.background as Record; - expect(background.blurIntensity).toBe( - DEFAULT_BACKGROUND_BLUR_INTENSITY, - ); + expect(background.blurIntensity).toBe(DEFAULT_BACKGROUND_BLUR_INTENSITY); }); test("preserves currentSceneId", async () => { diff --git a/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts b/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts index b6b38f3c..a19269cd 100644 --- a/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts +++ b/apps/web/src/services/storage/migrations/transformers/v1-to-v2.ts @@ -1,6 +1,4 @@ -import { - DEFAULT_BACKGROUND_BLUR_INTENSITY, -} from "@/lib/background/blur"; +import { DEFAULT_BACKGROUND_BLUR_INTENSITY } from "@/lib/background/blur"; import { DEFAULT_BACKGROUND_COLOR } from "@/lib/background/color"; import { DEFAULT_CANVAS_SIZE } from "@/lib/canvas/sizes"; const DEFAULT_FPS = 30;