From 93d1e3383ce47e45157d70f9ba6251239dae6bc2 Mon Sep 17 00:00:00 2001 From: Maze <167211895+mazeincoding@users.noreply.github.com> Date: Mon, 23 Feb 2026 03:24:02 +0100 Subject: feat: major editor overhaul (assets, properties, timeline, fonts) (#709) * feat: major editor overhaul (assets, properties, timeline, fonts) Refactor editor core systems to standardize UI architecture and improve performance. Assets & Properties: - Replace monolithic property items with composable `Section` architecture. - Add specialized sections for Transform, Blending, and Text. - Implement `NumberField` with scrubbing and math evaluation. - Add new ColorPicker with EyeDropper and multiple format support. - Standardize asset panels using new `PanelView` layout. Fonts & Stickers: - Implement custom font atlas/sprite system for high-performance previews. - Add virtualized FontPicker with search and favorites. - Refactor stickers to use a provider-based architecture (icons, emoji, flags, shapes). - Standardize sticker IDs to `provider:value` format. Timeline & Interaction: - Convert bookmarks to rich objects with notes, colors, and duration. - Refactor drag-and-drop to use Command pattern (enabling proper undo/redo). - Add Shift modifier to disable snapping during moves/resizes. - Add new overlays for layout guides and text editing. Renderer: - Add support for multi-line text, custom line-height, and letter-spacing. - Implement global composite operation (blend modes). - Update sticker node to resolve dynamic provider IDs. Infrastructure: - Add storage migrations (v3->v6) for text weights, sticker IDs, and bookmarks. - Update global styles and core UI components (Button, Input, Popover). * add ts-nocheck directive to settings-legacy.tsx to suppress TypeScript errors * fix: correct global composite operation assignment in TextNode to ensure proper blend mode handling * deleted shadcn components with errors * formatting * fix linter issues * migrate from next middleware to proxy * add missing component back * add breadcrumb back * chore: add @radix-ui/react-primitive deps * chore: more deps * chore: add missing env vars to bun-ci * next env --- .../src/components/editor/panels/preview/index.tsx | 181 ++++++++------------- 1 file changed, 72 insertions(+), 109 deletions(-) (limited to 'apps/web/src/components/editor/panels/preview/index.tsx') diff --git a/apps/web/src/components/editor/panels/preview/index.tsx b/apps/web/src/components/editor/panels/preview/index.tsx index 902547bd..90cb11a3 100644 --- a/apps/web/src/components/editor/panels/preview/index.tsx +++ b/apps/web/src/components/editor/panels/preview/index.tsx @@ -9,18 +9,13 @@ import { useFullscreen } from "@/hooks/use-fullscreen"; import { CanvasRenderer } from "@/services/renderer/canvas-renderer"; import type { RootNode } from "@/services/renderer/nodes/root-node"; import { buildScene } from "@/services/renderer/scene-builder"; -import { formatTimeCode, getLastFrameTime } from "@/lib/time"; +import { getLastFrameTime } from "@/lib/time"; import { PreviewInteractionOverlay } from "./preview-interaction-overlay"; -import { EditableTimecode } from "@/components/editable-timecode"; -import { invokeAction } from "@/lib/actions"; -import { Button } from "@/components/ui/button"; -import { - FullScreenIcon, - PauseIcon, - PlayIcon, -} from "@hugeicons/core-free-icons"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { cn } from "@/utils/ui"; +import { BookmarkNoteOverlay } from "./bookmark-note-overlay"; +import { ContextMenu, ContextMenuTrigger } from "@/components/ui/context-menu"; +import { usePreviewStore } from "@/stores/preview-store"; +import { PreviewContextMenu } from "./context-menu"; +import { PreviewToolbar } from "./toolbar"; function usePreviewSize() { const editor = useEditor(); @@ -32,6 +27,30 @@ function usePreviewSize() { }; } +export function PreviewPanel() { + const containerRef = useRef(null); + const { isFullscreen, toggleFullscreen } = useFullscreen({ containerRef }); + + return ( +
+
+ + +
+ +
+ ); +} + function RenderTreeController() { const editor = useEditor(); const tracks = editor.timeline.getTracks(); @@ -58,98 +77,24 @@ function RenderTreeController() { return null; } -export function PreviewPanel() { - const containerRef = useRef(null); - const { isFullscreen, toggleFullscreen } = useFullscreen({ containerRef }); - - return ( -
-
- - -
- -
- ); -} - -function PreviewToolbar({ - isFullscreen, +function PreviewCanvas({ onToggleFullscreen, + containerRef, }: { - isFullscreen: boolean; onToggleFullscreen: () => void; + containerRef: React.RefObject; }) { - const editor = useEditor(); - const isPlaying = editor.playback.getIsPlaying(); - const currentTime = editor.playback.getCurrentTime(); - const totalDuration = editor.timeline.getTotalDuration(); - const fps = editor.project.getActive().settings.fps; - - return ( -
-
- editor.playback.seek({ time })} - className="text-center" - /> - / - - {formatTimeCode({ - timeInSeconds: totalDuration, - format: "HH:MM:SS:FF", - fps, - })} - -
- - - -
- -
-
- ); -} - -function PreviewCanvas() { const canvasRef = useRef(null); - const containerRef = useRef(null); + const outerContainerRef = useRef(null); + const canvasBoundsRef = useRef(null); const lastFrameRef = useRef(-1); const lastSceneRef = useRef(null); const renderingRef = useRef(false); const { width: nativeWidth, height: nativeHeight } = usePreviewSize(); - const containerSize = useContainerSize({ containerRef }); + const containerSize = useContainerSize({ containerRef: outerContainerRef }); const editor = useEditor(); const activeProject = editor.project.getActive(); + const { overlays } = usePreviewStore(); const renderer = useMemo(() => { return new CanvasRenderer({ @@ -224,24 +169,42 @@ function PreviewCanvas() { return (
- - + + +
+ + + {overlays.bookmarks && } +
+
+ +
); } -- cgit v1.3.1