mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
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
This commit is contained in:
@@ -1,9 +1,3 @@
|
||||
import type { TPlatformLayout } from "@/types/editor";
|
||||
|
||||
export const PLATFORM_LAYOUTS: Record<TPlatformLayout, string> = {
|
||||
tiktok: "TikTok",
|
||||
};
|
||||
|
||||
export const PANEL_CONFIG = {
|
||||
panels: {
|
||||
tools: 25,
|
||||
|
||||
@@ -1,79 +1,13 @@
|
||||
export interface FontOption {
|
||||
value: string;
|
||||
label: string;
|
||||
category: "system" | "google" | "custom";
|
||||
weights?: number[];
|
||||
hasClassName?: boolean;
|
||||
}
|
||||
|
||||
export const FONT_OPTIONS: FontOption[] = [
|
||||
// System fonts (always available)
|
||||
{ value: "Arial", label: "Arial", category: "system", hasClassName: false },
|
||||
{
|
||||
value: "Helvetica",
|
||||
label: "Helvetica",
|
||||
category: "system",
|
||||
hasClassName: false,
|
||||
},
|
||||
{
|
||||
value: "Times New Roman",
|
||||
label: "Times New Roman",
|
||||
category: "system",
|
||||
hasClassName: false,
|
||||
},
|
||||
{
|
||||
value: "Georgia",
|
||||
label: "Georgia",
|
||||
category: "system",
|
||||
hasClassName: false,
|
||||
},
|
||||
|
||||
// Google Fonts (loaded in layout.tsx)
|
||||
{
|
||||
value: "Inter",
|
||||
label: "Inter",
|
||||
category: "google",
|
||||
weights: [400, 700],
|
||||
hasClassName: true,
|
||||
},
|
||||
{
|
||||
value: "Roboto",
|
||||
label: "Roboto",
|
||||
category: "google",
|
||||
weights: [400, 700],
|
||||
hasClassName: true,
|
||||
},
|
||||
{
|
||||
value: "Open Sans",
|
||||
label: "Open Sans",
|
||||
category: "google",
|
||||
hasClassName: true,
|
||||
},
|
||||
{
|
||||
value: "Playfair Display",
|
||||
label: "Playfair Display",
|
||||
category: "google",
|
||||
hasClassName: true,
|
||||
},
|
||||
{
|
||||
value: "Comic Neue",
|
||||
label: "Comic Neue",
|
||||
category: "google",
|
||||
hasClassName: false,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_FONT = "Arial";
|
||||
|
||||
// Type-safe font family union
|
||||
export type FontFamily = (typeof FONT_OPTIONS)[number]["value"];
|
||||
|
||||
// Helper functions
|
||||
export const getFontByValue = (value: string): FontOption | undefined =>
|
||||
FONT_OPTIONS.find((font) => font.value === value);
|
||||
|
||||
export const getGoogleFonts = (): FontOption[] =>
|
||||
FONT_OPTIONS.filter((font) => font.category === "google");
|
||||
|
||||
export const getSystemFonts = (): FontOption[] =>
|
||||
FONT_OPTIONS.filter((font) => font.category === "system");
|
||||
export const SYSTEM_FONTS = new Set([
|
||||
"Arial",
|
||||
"Helvetica",
|
||||
"Times New Roman",
|
||||
"Courier New",
|
||||
"Verdana",
|
||||
"Georgia",
|
||||
"monospace",
|
||||
"sans-serif",
|
||||
"serif",
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OcDataBuddyIcon, OcMarbleIcon, } from "@opencut/ui/icons";
|
||||
import { OcDataBuddyIcon, OcMarbleIcon } from "@opencut/ui/icons";
|
||||
|
||||
export const SITE_URL = "https://opencut.app";
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export const STICKER_CATEGORIES = {
|
||||
all: "All",
|
||||
logos: "Logos",
|
||||
icons: "Icons",
|
||||
emoji: "Emoji",
|
||||
flags: "Flags",
|
||||
shapes: "Shapes",
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
export const STICKER_CATEGORIES = [
|
||||
"all",
|
||||
"general",
|
||||
"brands",
|
||||
"emoji",
|
||||
] as const;
|
||||
|
||||
export const STICKER_CATEGORY_CONFIG: Record<
|
||||
(typeof STICKER_CATEGORIES)[number],
|
||||
string | undefined
|
||||
> = {
|
||||
all: undefined,
|
||||
general: "General",
|
||||
brands: "Brands / Social",
|
||||
emoji: "Emoji",
|
||||
};
|
||||
@@ -1,38 +1,40 @@
|
||||
import type { TextElement } from "@/types/timeline";
|
||||
import { TIMELINE_CONSTANTS } from "./timeline-constants";
|
||||
|
||||
export const MIN_FONT_SIZE = 5;
|
||||
export const MAX_FONT_SIZE = 300;
|
||||
|
||||
/**
|
||||
* higher value: smaller font size
|
||||
* lower value: larger font size
|
||||
*/
|
||||
export const FONT_SIZE_SCALE_REFERENCE = 90;
|
||||
|
||||
export const DEFAULT_TEXT_ELEMENT: Omit<TextElement, "id"> = {
|
||||
type: "text",
|
||||
name: "Text",
|
||||
content: "Default text",
|
||||
fontSize: 15,
|
||||
fontFamily: "Arial",
|
||||
color: "#ffffff",
|
||||
backgroundColor: "transparent",
|
||||
textAlign: "center",
|
||||
fontWeight: "normal",
|
||||
fontStyle: "normal",
|
||||
textDecoration: "none",
|
||||
duration: TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime: 0,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
transform: {
|
||||
scale: 1,
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
rotate: 0,
|
||||
},
|
||||
opacity: 1,
|
||||
};
|
||||
import type { TextElement } from "@/types/timeline";
|
||||
import {
|
||||
DEFAULT_OPACITY,
|
||||
DEFAULT_TRANSFORM,
|
||||
TIMELINE_CONSTANTS,
|
||||
} from "./timeline-constants";
|
||||
|
||||
export const MIN_FONT_SIZE = 5;
|
||||
export const MAX_FONT_SIZE = 300;
|
||||
|
||||
/**
|
||||
* higher value: smaller font size
|
||||
* lower value: larger font size
|
||||
*/
|
||||
export const FONT_SIZE_SCALE_REFERENCE = 90;
|
||||
|
||||
export const DEFAULT_LETTER_SPACING = 0;
|
||||
export const DEFAULT_LINE_HEIGHT = 1.2;
|
||||
|
||||
export const DEFAULT_TEXT_ELEMENT: Omit<TextElement, "id"> = {
|
||||
type: "text",
|
||||
name: "Text",
|
||||
content: "Default text",
|
||||
fontSize: 15,
|
||||
fontFamily: "Arial",
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#000000",
|
||||
textAlign: "center",
|
||||
fontWeight: "normal",
|
||||
fontStyle: "normal",
|
||||
textDecoration: "none",
|
||||
letterSpacing: DEFAULT_LETTER_SPACING,
|
||||
lineHeight: DEFAULT_LINE_HEIGHT,
|
||||
duration: TIMELINE_CONSTANTS.DEFAULT_ELEMENT_DURATION,
|
||||
startTime: 0,
|
||||
trimStart: 0,
|
||||
trimEnd: 0,
|
||||
transform: DEFAULT_TRANSFORM,
|
||||
opacity: DEFAULT_OPACITY,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { TTimelineViewState } from "@/types/project";
|
||||
import type { TrackType } from "@/types/timeline";
|
||||
import type { BlendMode } from "@/types/rendering";
|
||||
import type { TrackType, Transform } from "@/types/timeline";
|
||||
import {
|
||||
Happy01Icon,
|
||||
MusicNote03Icon,
|
||||
@@ -8,6 +9,16 @@ import {
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { OcVideoIcon } from "@opencut/ui/icons";
|
||||
|
||||
export const DEFAULT_TRANSFORM: Transform = {
|
||||
scale: 1,
|
||||
position: { x: 0, y: 0 },
|
||||
rotate: 0,
|
||||
};
|
||||
|
||||
export const DEFAULT_OPACITY = 1;
|
||||
export const DEFAULT_BLEND_MODE: BlendMode = "normal";
|
||||
export const DEFAULT_BOOKMARK_COLOR = "#009dff";
|
||||
|
||||
export const TRACK_COLORS: Record<TrackType, { background: string }> = {
|
||||
video: {
|
||||
background: "transparent",
|
||||
@@ -32,6 +43,8 @@ export const TRACK_HEIGHTS: Record<TrackType, number> = {
|
||||
|
||||
export const TRACK_GAP = 4;
|
||||
|
||||
export const DRAG_THRESHOLD_PX = 5;
|
||||
|
||||
export const TIMELINE_CONSTANTS = {
|
||||
PIXELS_PER_SECOND: 50,
|
||||
DEFAULT_ELEMENT_DURATION: 5,
|
||||
|
||||
Reference in New Issue
Block a user