Skip to content
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
},
"editor.tabSize": 2
}
2 changes: 1 addition & 1 deletion apps/web/src/app/editor/[project_id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default function Editor() {

return (
<EditorProvider>
<div className="h-screen w-screen flex flex-col bg-background overflow-hidden">
<div className="h-screen w-screen flex flex-col overflow-hidden">
<EditorHeader />
<div className="flex-1 min-h-0 min-w-0">
<ResizablePanelGroup
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function RootLayout({
<head>
<BotIdClient protect={protectedRoutes} />
</head>
<body className={`${defaultFont.className} font-sans antialiased`}>
<body className={`${defaultFont.className} font-sans antialiased transition-colors`}>
<ThemeProvider attribute="class" defaultTheme="dark">
<TooltipProvider>
<StorageProvider>{children}</StorageProvider>
Expand Down
15 changes: 8 additions & 7 deletions apps/web/src/app/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { useProjectStore } from "@/stores/project-store";
import { useTimelineStore } from "@/stores/timeline-store";
import type { TProject } from "@/types/project";
import { cn } from "@/lib/utils";

export default function ProjectsPage() {
const {
Expand Down Expand Up @@ -388,9 +389,9 @@ function ProjectCard({

const cardContent = (
<Card
className={`overflow-hidden bg-background border-none p-0 transition-all ${
isSelectionMode && isSelected ? "ring-2 ring-primary" : ""
}`}
className={cn("overflow-hidden bg-background p-0 transition-all", {
"ring-2 ring-primary": isSelectionMode && isSelected,
})}
>
<div
className={`relative aspect-square bg-muted transition-opacity ${
Expand All @@ -399,14 +400,14 @@ function ProjectCard({
>
{isSelectionMode && (
<div className="absolute top-3 left-3 z-10">
<div className="w-5 h-5 rounded bg-background/80 backdrop-blur-xs border flex items-center justify-center">
<div className="w-5 h-5 rounded bg-transparent border flex items-center justify-center">
<Checkbox
checked={isSelected}
onCheckedChange={(checked) =>
onSelect?.(project.id, checked as boolean)
}
onClick={(e) => e.stopPropagation()}
className="w-4 h-4"
className="w-4 h-4 bg-white"
/>
</div>
</div>
Expand All @@ -432,9 +433,9 @@ function ProjectCard({
</div>
</div>

<CardContent className="px-0 pt-5 flex flex-col gap-1">
<CardContent className="px-3 pt-3 flex flex-col gap-1">
<div className="flex items-start justify-between">
<h3 className="font-medium text-sm leading-snug group-hover:text-foreground/90 transition-colors line-clamp-2">
<h3 className="font-medium text-sm leading-snug group-hover:text-foreground/90 transition-colors truncate">
{project.name}
</h3>
{!isSelectionMode && (
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/editor-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export function EditorHeader() {
leftContent={leftContent}
centerContent={centerContent}
rightContent={rightContent}
className="bg-background h-[3.2rem] px-3 items-center mt-0.5"
className="h-[3.2rem] px-3 items-center"
/>
);
}
3 changes: 1 addition & 2 deletions apps/web/src/components/editor/media-panel/drag-overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Upload, Plus, Image } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Upload } from "lucide-react";

interface MediaDragOverlayProps {
isVisible: boolean;
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/editor/media-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ export function MediaPanel() {
};

return (
<div className="h-full flex bg-panel">
<div className="h-full flex flex-col-reverse md:flex-row bg-panel">
<TabBar />
<Separator orientation="vertical" />
<div className="flex-1 overflow-hidden">{viewMap[activeTab]}</div>
<div className="flex-1">{viewMap[activeTab]}</div>
</div>
);
}
130 changes: 26 additions & 104 deletions apps/web/src/components/editor/media-panel/tabbar.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,45 @@
"use client";

import { cn } from "@/lib/utils";
import { Tab, tabs, useMediaPanelStore } from "./store";
import { Button } from "@/components/ui/button";
import { ChevronRight, ChevronLeft } from "lucide-react";
import { useRef, useState, useEffect } from "react";

import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
export function TabBar() {
const { activeTab, setActiveTab } = useMediaPanelStore();
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [isAtEnd, setIsAtEnd] = useState(false);
const [isAtStart, setIsAtStart] = useState(true);

const scrollToEnd = () => {
if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTo({
left: scrollContainerRef.current.scrollWidth,
});
setIsAtEnd(true);
setIsAtStart(false);
}
};

const scrollToStart = () => {
if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTo({
left: 0,
});
setIsAtStart(true);
setIsAtEnd(false);
}
};

const checkScrollPosition = () => {
if (scrollContainerRef.current) {
const { scrollLeft, scrollWidth, clientWidth } =
scrollContainerRef.current;
const isAtEndNow = scrollLeft + clientWidth >= scrollWidth - 1;
const isAtStartNow = scrollLeft <= 1;
setIsAtEnd(isAtEndNow);
setIsAtStart(isAtStartNow);
}
};

// We're using useEffect because we need to sync with external DOM scroll events
useEffect(() => {
const container = scrollContainerRef.current;
if (!container) return;

checkScrollPosition();
container.addEventListener("scroll", checkScrollPosition);

const resizeObserver = new ResizeObserver(checkScrollPosition);
resizeObserver.observe(container);

return () => {
container.removeEventListener("scroll", checkScrollPosition);
resizeObserver.disconnect();
};
}, []);

return (
<div className="flex">
<ScrollButton
direction="left"
onClick={scrollToStart}
isVisible={!isAtStart}
/>
<div
ref={scrollContainerRef}
className="h-full px-4 flex flex-col justify-start items-center gap-5 overflow-x-auto scrollbar-x-hidden relative w-full py-4"
>
<div className="h-full flex md:flex-col justify-start items-center gap-1 p-1 overflow-x-scroll scrollbar-x-hidden relative w-full ">
{(Object.keys(tabs) as Tab[]).map((tabKey) => {
const tab = tabs[tabKey];
return (
<div
className={cn(
"flex flex-col gap-0.5 items-center cursor-pointer opacity-100 hover:opacity-75",
activeTab === tabKey ? "text-primary !opacity-100" : "text-muted-foreground"
)}
onClick={() => setActiveTab(tabKey)}
key={tabKey}
>
<tab.icon className="size-[1.1rem]!" />
</div>
<Tooltip key={tabKey}>
<TooltipTrigger asChild>
<Button
variant="outline"
size="icon"
className={cn(
"min-w-9 min-h-9 flex items-center justify-center shadow-none border-none",
"bg-inherit text-gray-500",
activeTab === tabKey ? "hover:text-! text-primary " : ""
)}
onClick={() => setActiveTab(tabKey)}
>
<tab.icon className="size-6!" />
</Button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={12}>
{tab.label}
</TooltipContent>
</Tooltip>
);
})}
</div>
<ScrollButton
direction="right"
onClick={scrollToEnd}
isVisible={!isAtEnd}
/>
</div>
);
}

function ScrollButton({
direction,
onClick,
isVisible,
}: {
direction: "left" | "right";
onClick: () => void;
isVisible: boolean;
}) {
if (!isVisible) return null;

const Icon = direction === "left" ? ChevronLeft : ChevronRight;

return (
<div className="bg-panel-accent w-12 h-full flex items-center justify-center">
<Button
size="icon"
className="rounded-[0.4rem] w-4 h-7 bg-foreground/10!"
onClick={onClick}
>
<Icon className="size-4! text-foreground" />
</Button>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function TimelineElement({
}`}
>
<div
className={`absolute top-[0.15rem] bottom-[0.15rem] left-0 right-0`}
className="absolute top-[0.15rem] bottom-[0.15rem] left-0 right-0"
style={{
backgroundImage: imageUrl ? `url(${imageUrl})` : "none",
backgroundRepeat: "repeat-x",
Expand Down