From 7d4e166678c35d48b22b8d1d05639f981a956a8b Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 13 Apr 2026 04:41:33 +0200 Subject: [PATCH] fix: text measurement, rotation handle, video cache races, playback sync Made-with: Cursor --- apps/web/package.json | 4 +- apps/web/src/app/editor/[project_id]/page.tsx | 255 ++++++------ apps/web/src/app/layout.tsx | 7 - .../preview/preview-interaction-overlay.tsx | 220 +++++------ .../panels/preview/text-edit-overlay.tsx | 363 ++++++++---------- .../panels/preview/transform-handles.tsx | 285 +++++++------- .../components/providers/editor-provider.tsx | 3 +- .../web/src/core/managers/playback-manager.ts | 124 ++++-- .../web/src/core/managers/renderer-manager.ts | 11 + .../web/src/core/managers/timeline-manager.ts | 60 ++- apps/web/src/hooks/use-preview-interaction.ts | 18 - apps/web/src/lib/background/constants.ts | 6 +- apps/web/src/lib/text/layout.ts | 51 ++- apps/web/src/lib/text/measure-element.ts | 289 +++++++------- apps/web/src/services/video-cache/service.ts | 16 + bun.lock | 10 +- 16 files changed, 919 insertions(+), 803 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 7ee5fe1a..06d9babd 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@hello-pangea/dnd": "^18.0.1", - "@hugeicons/core-free-icons": "^3.1.1", + "@hugeicons/core-free-icons": "^3.3.0", "@hugeicons/react": "^1.1.6", "@huggingface/transformers": "^3.8.1", "@opennextjs/cloudflare": "^1.18.0", @@ -52,7 +52,7 @@ "nanoid": "^5.1.5", "next": "16.1.3", "next-themes": "^0.4.4", - "opencut-wasm": "^0.2.3", + "opencut-wasm": "file:../../rust/wasm/pkg", "pg": "^8.16.2", "postgres": "^3.4.5", "radix-ui": "^1.4.3", diff --git a/apps/web/src/app/editor/[project_id]/page.tsx b/apps/web/src/app/editor/[project_id]/page.tsx index 38a25d4c..8456d624 100644 --- a/apps/web/src/app/editor/[project_id]/page.tsx +++ b/apps/web/src/app/editor/[project_id]/page.tsx @@ -1,113 +1,142 @@ -"use client"; - -import { useParams } from "next/navigation"; -import { - ResizablePanelGroup, - ResizablePanel, - ResizableHandle, -} from "@/components/ui/resizable"; -import { AssetsPanel } from "@/components/editor/panels/assets"; -import { PropertiesPanel } from "@/components/editor/panels/properties"; -import { Timeline } from "@/components/editor/panels/timeline"; -import { PreviewPanel } from "@/components/editor/panels/preview"; -import { EditorHeader } from "@/components/editor/editor-header"; -import { EditorProvider } from "@/components/providers/editor-provider"; -import { Onboarding } from "@/components/editor/onboarding"; -import { MigrationDialog } from "@/components/editor/dialogs/migration-dialog"; -import { usePanelStore } from "@/stores/panel-store"; -import { usePasteMedia } from "@/hooks/use-paste-media"; -import { MobileGate } from "@/components/editor/mobile-gate"; - -export default function Editor() { - const params = useParams(); - const projectId = params.project_id as string; - - return ( - - -
- -
- -
- - -
-
-
- ); -} - -function EditorLayout() { - usePasteMedia(); - const { panels, setPanel } = usePanelStore(); - - return ( - { - setPanel("mainContent", sizes[0] ?? panels.mainContent); - setPanel("timeline", sizes[1] ?? panels.timeline); - }} - > - - { - setPanel("tools", sizes[0] ?? panels.tools); - setPanel("preview", sizes[1] ?? panels.preview); - setPanel("properties", sizes[2] ?? panels.properties); - }} - > - - - - - - - - - - - - - - - - - - - - - - - - - ); -} +"use client"; + +import { useParams } from "next/navigation"; +import { + ResizablePanelGroup, + ResizablePanel, + ResizableHandle, +} from "@/components/ui/resizable"; +import { AssetsPanel } from "@/components/editor/panels/assets"; +import { PropertiesPanel } from "@/components/editor/panels/properties"; +import { Timeline } from "@/components/editor/panels/timeline"; +import { PreviewPanel } from "@/components/editor/panels/preview"; +import { EditorHeader } from "@/components/editor/editor-header"; +import { EditorProvider } from "@/components/providers/editor-provider"; +import { Onboarding } from "@/components/editor/onboarding"; +import { MigrationDialog } from "@/components/editor/dialogs/migration-dialog"; +import { usePanelStore } from "@/stores/panel-store"; +import { usePasteMedia } from "@/hooks/use-paste-media"; +import { MobileGate } from "@/components/editor/mobile-gate"; +import { useState } from "react"; +import { useEditor } from "@/hooks/use-editor"; +import { Cancel01Icon } from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Button } from "@/components/ui/button"; + +export default function Editor() { + const params = useParams(); + const projectId = params.project_id as string; + + return ( + + +
+ + +
+ +
+ + +
+
+
+ ); +} + +function DegradedRendererBanner() { + const isDegraded = useEditor((e) => e.renderer.isDegraded); + const [dismissed, setDismissed] = useState(false); + if (!isDegraded || dismissed) return null; + + return ( +
+ + For the best experience, open OpenCut in Chrome. + + +
+ ); +} + +function EditorLayout() { + usePasteMedia(); + const { panels, setPanel } = usePanelStore(); + + return ( + { + setPanel("mainContent", sizes[0] ?? panels.mainContent); + setPanel("timeline", sizes[1] ?? panels.timeline); + }} + > + + { + setPanel("tools", sizes[0] ?? panels.tools); + setPanel("preview", sizes[1] ?? panels.preview); + setPanel("properties", sizes[2] ?? panels.properties); + }} + > + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index b65e028d..d10dc9d3 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -35,13 +35,6 @@ export default function RootLayout({ crossOrigin="anonymous" strategy="beforeInteractive" /> - - {/* code to figma */} - {/*