feat: initial fonts support
This commit is contained in:
@ -1,4 +1,3 @@
|
|||||||
import { Inter } from "next/font/google";
|
|
||||||
import { ThemeProvider } from "next-themes";
|
import { ThemeProvider } from "next-themes";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
@ -8,11 +7,7 @@ import { TooltipProvider } from "../components/ui/tooltip";
|
|||||||
import { DevelopmentDebug } from "../components/development-debug";
|
import { DevelopmentDebug } from "../components/development-debug";
|
||||||
import { StorageProvider } from "../components/storage-provider";
|
import { StorageProvider } from "../components/storage-provider";
|
||||||
import { baseMetaData } from "./metadata";
|
import { baseMetaData } from "./metadata";
|
||||||
|
import { defaultFont } from "../lib/font-config";
|
||||||
const inter = Inter({
|
|
||||||
subsets: ["latin"],
|
|
||||||
variable: "--font-inter",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata = baseMetaData;
|
export const metadata = baseMetaData;
|
||||||
|
|
||||||
@ -23,7 +18,7 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${inter.variable} font-sans antialiased`}>
|
<body className={`${defaultFont.className} font-sans antialiased`}>
|
||||||
<ThemeProvider attribute="class" forcedTheme="dark" enableSystem>
|
<ThemeProvider attribute="class" forcedTheme="dark" enableSystem>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<StorageProvider>{children}</StorageProvider>
|
<StorageProvider>{children}</StorageProvider>
|
||||||
|
@ -20,6 +20,7 @@ import { Play, Pause } from "lucide-react";
|
|||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { formatTimeCode } from "@/lib/time";
|
import { formatTimeCode } from "@/lib/time";
|
||||||
|
import { FONT_CLASS_MAP } from "@/lib/font-config";
|
||||||
|
|
||||||
interface ActiveElement {
|
interface ActiveElement {
|
||||||
element: TimelineElement;
|
element: TimelineElement;
|
||||||
@ -141,6 +142,9 @@ export function PreviewPanel() {
|
|||||||
|
|
||||||
// Text elements
|
// Text elements
|
||||||
if (element.type === "text") {
|
if (element.type === "text") {
|
||||||
|
const fontClassName =
|
||||||
|
FONT_CLASS_MAP[element.fontFamily as keyof typeof FONT_CLASS_MAP] || "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={element.id}
|
key={element.id}
|
||||||
@ -154,9 +158,9 @@ export function PreviewPanel() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
className={fontClassName}
|
||||||
style={{
|
style={{
|
||||||
fontSize: `${element.fontSize}px`,
|
fontSize: `${element.fontSize}px`,
|
||||||
fontFamily: element.fontFamily,
|
|
||||||
color: element.color,
|
color: element.color,
|
||||||
backgroundColor: element.backgroundColor,
|
backgroundColor: element.backgroundColor,
|
||||||
textAlign: element.textAlign,
|
textAlign: element.textAlign,
|
||||||
@ -166,6 +170,8 @@ export function PreviewPanel() {
|
|||||||
padding: "4px 8px",
|
padding: "4px 8px",
|
||||||
borderRadius: "2px",
|
borderRadius: "2px",
|
||||||
whiteSpace: "pre-wrap",
|
whiteSpace: "pre-wrap",
|
||||||
|
// Fallback for system fonts that don't have classes
|
||||||
|
...(fontClassName === "" && { fontFamily: element.fontFamily }),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{element.content}
|
{element.content}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "../ui/select";
|
} from "../ui/select";
|
||||||
|
import { FONT_OPTIONS, type FontFamily } from "@/constants/font-constants";
|
||||||
|
|
||||||
export function PropertiesPanel() {
|
export function PropertiesPanel() {
|
||||||
const { activeProject } = useProjectStore();
|
const { activeProject } = useProjectStore();
|
||||||
@ -49,14 +50,21 @@ export function PropertiesPanel() {
|
|||||||
/>
|
/>
|
||||||
<div className="flex items-center justify-between gap-6">
|
<div className="flex items-center justify-between gap-6">
|
||||||
<Label className="text-xs">Font</Label>
|
<Label className="text-xs">Font</Label>
|
||||||
<Select>
|
<Select
|
||||||
|
defaultValue={element.fontFamily}
|
||||||
|
onValueChange={(value: FontFamily) =>
|
||||||
|
updateTextElement(trackId, element.id, { fontFamily: value })
|
||||||
|
}
|
||||||
|
>
|
||||||
<SelectTrigger className="w-full text-xs">
|
<SelectTrigger className="w-full text-xs">
|
||||||
<SelectValue placeholder="Select a font" />
|
<SelectValue placeholder="Select a font" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="Arial">Arial</SelectItem>
|
{FONT_OPTIONS.map((font) => (
|
||||||
<SelectItem value="Helvetica">Helvetica</SelectItem>
|
<SelectItem key={font.value} value={font.value}>
|
||||||
<SelectItem value="Times New Roman">Times New Roman</SelectItem>
|
{font.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@ import { useTimelineElementResize } from "@/hooks/use-timeline-element-resize";
|
|||||||
import {
|
import {
|
||||||
getTrackElementClasses,
|
getTrackElementClasses,
|
||||||
TIMELINE_CONSTANTS,
|
TIMELINE_CONSTANTS,
|
||||||
} from "@/lib/timeline-constants";
|
} from "@/constants/timeline-constants";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
|
@ -17,7 +17,7 @@ import type {
|
|||||||
TimelineElement as TimelineElementType,
|
TimelineElement as TimelineElementType,
|
||||||
DragData,
|
DragData,
|
||||||
} from "@/types/timeline";
|
} from "@/types/timeline";
|
||||||
import { TIMELINE_CONSTANTS } from "@/lib/timeline-constants";
|
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||||
|
|
||||||
export function TimelineTrackContent({
|
export function TimelineTrackContent({
|
||||||
track,
|
track,
|
||||||
|
@ -50,7 +50,7 @@ import {
|
|||||||
getCumulativeHeightBefore,
|
getCumulativeHeightBefore,
|
||||||
getTotalTracksHeight,
|
getTotalTracksHeight,
|
||||||
TIMELINE_CONSTANTS,
|
TIMELINE_CONSTANTS,
|
||||||
} from "@/lib/timeline-constants";
|
} from "@/constants/timeline-constants";
|
||||||
|
|
||||||
export function Timeline() {
|
export function Timeline() {
|
||||||
// Timeline shows all tracks (video, audio, effects) and their elements.
|
// Timeline shows all tracks (video, audio, effects) and their elements.
|
||||||
|
79
apps/web/src/constants/font-constants.ts
Normal file
79
apps/web/src/constants/font-constants.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
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");
|
39
apps/web/src/lib/font-config.ts
Normal file
39
apps/web/src/lib/font-config.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
Inter,
|
||||||
|
Roboto,
|
||||||
|
Open_Sans,
|
||||||
|
Playfair_Display,
|
||||||
|
Comic_Neue,
|
||||||
|
} from "next/font/google";
|
||||||
|
|
||||||
|
// Configure all fonts
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
const roboto = Roboto({ subsets: ["latin"], weight: ["400", "700"] });
|
||||||
|
const openSans = Open_Sans({ subsets: ["latin"] });
|
||||||
|
const playfairDisplay = Playfair_Display({ subsets: ["latin"] });
|
||||||
|
const comicNeue = Comic_Neue({ subsets: ["latin"], weight: ["400", "700"] });
|
||||||
|
|
||||||
|
// Export font class mapping for use in components
|
||||||
|
export const FONT_CLASS_MAP = {
|
||||||
|
Inter: inter.className,
|
||||||
|
Roboto: roboto.className,
|
||||||
|
"Open Sans": openSans.className,
|
||||||
|
"Playfair Display": playfairDisplay.className,
|
||||||
|
"Comic Neue": comicNeue.className,
|
||||||
|
Arial: "",
|
||||||
|
Helvetica: "",
|
||||||
|
"Times New Roman": "",
|
||||||
|
Georgia: "",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Export individual fonts for use in layout
|
||||||
|
export const fonts = {
|
||||||
|
inter,
|
||||||
|
roboto,
|
||||||
|
openSans,
|
||||||
|
playfairDisplay,
|
||||||
|
comicNeue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default font for the body
|
||||||
|
export const defaultFont = inter;
|
Reference in New Issue
Block a user