lots more icons to do but
This commit is contained in:
parent
33f453efc7
commit
954160d03b
|
|
@ -1179,7 +1179,7 @@ worker.ts
|
|||
|
||||
## apps/web/src/stores
|
||||
|
||||
assets-panel-store.ts
|
||||
assets-panel-store.tsx
|
||||
export const TAB_KEYS
|
||||
export type Tab = (typeof TAB_KEYS)[number]
|
||||
export const tabs
|
||||
|
|
@ -1678,8 +1678,6 @@ ui.ts
|
|||
## packages/ui/src/icons
|
||||
|
||||
brand.tsx
|
||||
export function OcGoogleIcon({ className }: { className?: string })
|
||||
export function OcGithubIcon({ className }: { className?: string })
|
||||
export function OcVercelIcon({ className }: { className?: string })
|
||||
export function OcMarbleIcon({
|
||||
className = "",
|
||||
|
|
@ -1696,48 +1694,17 @@ brand.tsx
|
|||
size?: number;
|
||||
})
|
||||
|
||||
editor.tsx
|
||||
export function OcBackgroundIcon({ className }: { className?: string })
|
||||
export function OcSocialsIcon({
|
||||
ui.tsx
|
||||
export function OcSplitKeepLeftIcon({
|
||||
className = "",
|
||||
size = 32,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
})
|
||||
export function OcTransitionUpIcon({
|
||||
export function OcSplitKeepRightIcon({
|
||||
className = "",
|
||||
size = 16,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
})
|
||||
|
||||
ui.tsx
|
||||
export function OcMenuIcon({
|
||||
className = "",
|
||||
size = 16,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
})
|
||||
export function OcPencilIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
})
|
||||
export function OcLeftArrowIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
})
|
||||
export function OcTrashIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
size = 32,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
"@ffmpeg/util": "^0.12.2",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@hugeicons/core-free-icons": "^3.1.1",
|
||||
"@hugeicons/react": "^1.1.4",
|
||||
"@huggingface/transformers": "^3.8.1",
|
||||
"@opencut/env": "workspace:*",
|
||||
"@opencut/ui": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { OcGithubIcon } from "@opencut/ui/icons";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { GitHubContributeSection } from "@/components/gitHub-contribute-section";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { EXTERNAL_TOOLS, SOCIAL_LINKS } from "@/constants/site-constants";
|
||||
import { EXTERNAL_TOOLS } from "@/constants/site-constants";
|
||||
import { BasePage } from "../base-page";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
|
|
@ -87,7 +84,6 @@ export default async function ContributorsPage() {
|
|||
{otherContributors.length > 0 && (
|
||||
<AllContributorsSection contributors={otherContributors} />
|
||||
)}
|
||||
{contributors.length === 0 && <EmptyState />}
|
||||
<ExternalToolsSection />
|
||||
<GitHubContributeSection
|
||||
title="Join the community"
|
||||
|
|
@ -210,36 +206,6 @@ function AllContributorsSection({
|
|||
);
|
||||
}
|
||||
|
||||
function EmptyState() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 py-20 text-center">
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="bg-muted/50 mx-auto flex size-20 items-center justify-center rounded-full">
|
||||
<OcGithubIcon className="text-muted-foreground size-8" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<h3 className="text-xl font-medium">No contributors found</h3>
|
||||
<p className="text-muted-foreground mx-auto max-w-md">
|
||||
Unable to load contributors at the moment. Check back later or view
|
||||
on GitHub.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href={`${SOCIAL_LINKS.github}/graphs/contributors`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button variant="outline" className="gap-2">
|
||||
<OcGithubIcon />
|
||||
View on GitHub
|
||||
<ExternalLink />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ExternalToolsSection() {
|
||||
return (
|
||||
<div className="flex flex-col gap-10">
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
ArrowDown01,
|
||||
Calendar,
|
||||
ChevronLeft,
|
||||
MoreHorizontal,
|
||||
Plus,
|
||||
Search,
|
||||
Trash2,
|
||||
Video,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
|
@ -41,6 +30,18 @@ import {
|
|||
import { useEditor } from "@/hooks/use-editor";
|
||||
import type { TProjectMetadata } from "@/types/project";
|
||||
import { formatDate } from "@/utils/date";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import {
|
||||
ArrowHorizontalIcon,
|
||||
ArrowLeft01Icon,
|
||||
Calendar04Icon,
|
||||
Delete02Icon,
|
||||
MultiplicationSignIcon,
|
||||
PlusSignIcon,
|
||||
Search01Icon,
|
||||
SortingNineOneIcon,
|
||||
Video01Icon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
|
||||
export default function ProjectsPage() {
|
||||
const [isSelectionMode, setIsSelectionMode] = useState(false);
|
||||
|
|
@ -160,7 +161,7 @@ export default function ProjectsPage() {
|
|||
href="/"
|
||||
className="hover:text-muted-foreground flex items-center gap-1 transition-colors"
|
||||
>
|
||||
<ChevronLeft className="size-5! shrink-0" />
|
||||
<HugeiconsIcon icon={ArrowLeft01Icon} className="size-5! shrink-0" />
|
||||
<span className="text-sm font-medium">Back</span>
|
||||
</Link>
|
||||
<div className="block md:hidden">
|
||||
|
|
@ -171,7 +172,7 @@ export default function ProjectsPage() {
|
|||
size="sm"
|
||||
onClick={handleCancelSelection}
|
||||
>
|
||||
<X />
|
||||
<HugeiconsIcon icon={MultiplicationSignIcon} />
|
||||
Cancel
|
||||
</Button>
|
||||
{selectedProjects.size > 0 && (
|
||||
|
|
@ -180,7 +181,7 @@ export default function ProjectsPage() {
|
|||
size="sm"
|
||||
onClick={() => setIsBulkDeleteDialogOpen(true)}
|
||||
>
|
||||
<Trash2 />
|
||||
<HugeiconsIcon icon={Delete02Icon} />
|
||||
Delete ({selectedProjects.size})
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -210,7 +211,7 @@ export default function ProjectsPage() {
|
|||
{isSelectionMode ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" onClick={handleCancelSelection}>
|
||||
<X />
|
||||
<HugeiconsIcon icon={MultiplicationSignIcon} />
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
|
|
@ -218,7 +219,7 @@ export default function ProjectsPage() {
|
|||
disabled={selectedProjects.size === 0}
|
||||
onClick={() => setIsBulkDeleteDialogOpen(true)}
|
||||
>
|
||||
<Trash2 />
|
||||
<HugeiconsIcon icon={Delete02Icon} />
|
||||
Delete {selectedProjects.size} projects
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -257,9 +258,8 @@ export default function ProjectsPage() {
|
|||
variant="outline"
|
||||
className="size-9 items-center justify-center"
|
||||
>
|
||||
<ArrowDown01
|
||||
strokeWidth={1.5}
|
||||
className="!size-[1.05rem]"
|
||||
<HugeiconsIcon
|
||||
icon={SortingNineOneIcon}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Button>
|
||||
|
|
@ -430,7 +430,10 @@ function ProjectCard({
|
|||
/>
|
||||
) : (
|
||||
<div className="bg-muted/50 flex size-full items-center justify-center">
|
||||
<Video className="text-muted-foreground size-12 shrink-0" />
|
||||
<HugeiconsIcon
|
||||
icon={Video01Icon}
|
||||
className="text-muted-foreground size-12 shrink-0"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -458,7 +461,10 @@ function ProjectCard({
|
|||
}`}
|
||||
onClick={(event) => event.preventDefault()}
|
||||
>
|
||||
<MoreHorizontal aria-hidden="true" />
|
||||
<HugeiconsIcon
|
||||
icon={ArrowHorizontalIcon}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
|
|
@ -506,7 +512,7 @@ function ProjectCard({
|
|||
|
||||
<div className="space-y-1">
|
||||
<div className="text-muted-foreground flex items-center gap-1.5 text-sm">
|
||||
<Calendar className="size-4!" />
|
||||
<HugeiconsIcon icon={Calendar04Icon} className="size-4" />
|
||||
<span>Created {formatDate({ date: project.createdAt })}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -575,7 +581,7 @@ function ProjectsLoader() {
|
|||
function CreateButton({ onClick }: { onClick?: () => void }) {
|
||||
return (
|
||||
<Button className="flex" onClick={onClick}>
|
||||
<Plus />
|
||||
<HugeiconsIcon icon={PlusSignIcon} />
|
||||
<span className="text-sm font-medium">New project</span>
|
||||
</Button>
|
||||
);
|
||||
|
|
@ -596,7 +602,10 @@ function EmptyState({
|
|||
<div className="flex flex-col items-center justify-center gap-6 py-16 text-center">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="bg-muted/30 flex size-16 items-center justify-center rounded-full">
|
||||
<Search className="text-muted-foreground size-8" />
|
||||
<HugeiconsIcon
|
||||
icon={Search01Icon}
|
||||
className="text-muted-foreground size-8"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-lg font-medium">No results found</h3>
|
||||
<p className="text-muted-foreground max-w-md">
|
||||
|
|
@ -614,7 +623,10 @@ function EmptyState({
|
|||
<div className="flex flex-col items-center justify-center gap-6 py-16 text-center">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="bg-muted/30 flex size-16 items-center justify-center rounded-full">
|
||||
<Video className="text-muted-foreground size-8" />
|
||||
<HugeiconsIcon
|
||||
icon={Video01Icon}
|
||||
className="text-muted-foreground size-8"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-lg font-medium">No projects yet</h3>
|
||||
<p className="text-muted-foreground max-w-md">
|
||||
|
|
@ -623,7 +635,7 @@ function EmptyState({
|
|||
</p>
|
||||
</div>
|
||||
<Button size="lg" className="gap-2" onClick={onCreateProject}>
|
||||
<Plus />
|
||||
<HugeiconsIcon icon={PlusSignIcon} />
|
||||
Create your first project
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
ArrowDown,
|
||||
Clock3,
|
||||
Folder,
|
||||
LayoutGrid,
|
||||
List,
|
||||
MoreHorizontal,
|
||||
Plus,
|
||||
Search,
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import {
|
||||
ArrowHorizontalIcon,
|
||||
Clock03Icon,
|
||||
Folder03Icon,
|
||||
GridViewIcon,
|
||||
LeftToRightListDashIcon,
|
||||
PlusSignIcon,
|
||||
SearchIcon,
|
||||
SortingNineOneIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
|
||||
const projects = [
|
||||
{
|
||||
|
|
@ -110,7 +111,7 @@ export default function ProjectsPage() {
|
|||
href="/projects"
|
||||
className="flex items-center gap-3 rounded-2xl bg-blue-50/50 px-4 py-3 text-sm font-medium text-[#00A3FF]"
|
||||
>
|
||||
<LayoutGrid className="size-5" />
|
||||
<HugeiconsIcon icon={GridViewIcon} className="size-5" />
|
||||
Projects
|
||||
</a>
|
||||
<div className="px-4 pt-6 pb-2">
|
||||
|
|
@ -122,14 +123,20 @@ export default function ProjectsPage() {
|
|||
href="/projects"
|
||||
className="flex items-center gap-3 rounded-2xl px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900"
|
||||
>
|
||||
<Folder className="size-5 text-slate-400" />
|
||||
<HugeiconsIcon
|
||||
icon={Folder03Icon}
|
||||
className="size-5 text-slate-400"
|
||||
/>
|
||||
Marketing
|
||||
</a>
|
||||
<a
|
||||
href="/projects"
|
||||
className="flex items-center gap-3 rounded-2xl px-4 py-3 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900"
|
||||
>
|
||||
<Folder className="size-5 text-slate-400" />
|
||||
<HugeiconsIcon
|
||||
icon={Folder03Icon}
|
||||
className="size-5 text-slate-400"
|
||||
/>
|
||||
Social media
|
||||
</a>
|
||||
</nav>
|
||||
|
|
@ -143,7 +150,10 @@ export default function ProjectsPage() {
|
|||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="group relative">
|
||||
<Search className="absolute top-1/2 left-3.5 size-4 -translate-y-1/2 text-slate-400" />
|
||||
<HugeiconsIcon
|
||||
icon={SearchIcon}
|
||||
className="absolute top-1/2 left-3.5 size-4 -translate-y-1/2 text-slate-400"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
|
|
@ -157,18 +167,22 @@ export default function ProjectsPage() {
|
|||
className="rounded-full bg-slate-100 p-2 text-slate-900"
|
||||
aria-label="Grid view"
|
||||
>
|
||||
<LayoutGrid className="size-4" aria-hidden="true">
|
||||
<title>Grid view</title>
|
||||
</LayoutGrid>
|
||||
<HugeiconsIcon
|
||||
icon={GridViewIcon}
|
||||
className="size-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-full p-2 text-slate-400 hover:text-slate-600"
|
||||
aria-label="List view"
|
||||
>
|
||||
<List className="size-4" aria-hidden="true">
|
||||
<title>List view</title>
|
||||
</List>
|
||||
<HugeiconsIcon
|
||||
icon={LeftToRightListDashIcon}
|
||||
className="size-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -176,7 +190,7 @@ export default function ProjectsPage() {
|
|||
type="button"
|
||||
className="flex items-center gap-2 rounded-full bg-[#00A3FF] px-5 py-2.5 text-sm font-semibold text-white hover:bg-[#008BE0] active:scale-95"
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
<HugeiconsIcon icon={PlusSignIcon} className="size-4" />
|
||||
New project
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -210,7 +224,7 @@ export default function ProjectsPage() {
|
|||
className="inline-flex items-center gap-1.5 rounded-lg p-1 text-sm font-medium text-slate-500 hover:text-slate-900"
|
||||
>
|
||||
Name
|
||||
<ArrowDown className="size-3.5" />
|
||||
<HugeiconsIcon icon={SortingNineOneIcon} className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -243,9 +257,11 @@ export default function ProjectsPage() {
|
|||
className="rounded-full bg-white/90 p-2 text-slate-600 backdrop-blur-sm hover:bg-white hover:text-[#00A3FF]"
|
||||
aria-label="Project menu"
|
||||
>
|
||||
<MoreHorizontal className="size-4" aria-hidden="true">
|
||||
<title>Project menu</title>
|
||||
</MoreHorizontal>
|
||||
<HugeiconsIcon
|
||||
icon={ArrowHorizontalIcon}
|
||||
className="size-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -268,7 +284,11 @@ export default function ProjectsPage() {
|
|||
{name}
|
||||
</h3>
|
||||
<div className="mt-2 flex items-center gap-2 text-slate-400">
|
||||
<Clock3 className="size-4" aria-hidden="true" />
|
||||
<HugeiconsIcon
|
||||
icon={Clock03Icon}
|
||||
className="size-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="text-xs font-medium">{createdAtLabel}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -281,42 +301,33 @@ export default function ProjectsPage() {
|
|||
</main>
|
||||
|
||||
<style jsx global>{`
|
||||
.project-card:hover .card-actions {
|
||||
.project-card:hover .card-actions
|
||||
opacity: 1;
|
||||
}
|
||||
.checkbox-wrapper:checked + div {
|
||||
.checkbox-wrapper:checked + div
|
||||
border-color: var(--brand-blue);
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
.controls-row.has-selection .controls-select {
|
||||
.controls-row.has-selection .controls-select
|
||||
opacity: 1;
|
||||
}
|
||||
.project-select-wrapper {
|
||||
.project-select-wrapper
|
||||
opacity: 0;
|
||||
}
|
||||
.project-card:hover .project-select-wrapper {
|
||||
.project-card:hover .project-select-wrapper
|
||||
opacity: 1;
|
||||
}
|
||||
.project-card:has(.project-select:checked) .project-select-wrapper {
|
||||
.project-card:has(.project-select:checked) .project-select-wrapper
|
||||
opacity: 1;
|
||||
}
|
||||
.multi-select .project-menu {
|
||||
.multi-select .project-menu
|
||||
opacity: 0 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
.custom-checkbox {
|
||||
.custom-checkbox
|
||||
appearance: none;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
display: inline-grid;
|
||||
place-content: center;
|
||||
}
|
||||
.custom-checkbox:checked {
|
||||
.custom-checkbox:checked
|
||||
background-color: var(--brand-blue);
|
||||
border-color: var(--brand-blue);
|
||||
}
|
||||
.custom-checkbox:checked::after {
|
||||
.custom-checkbox:checked::after
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
|
@ -329,7 +340,6 @@ export default function ProjectsPage() {
|
|||
86% 10%,
|
||||
40% 70%
|
||||
);
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { ExternalLink } from "lucide-react";
|
||||
import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { BasePage } from "@/app/base-page";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { SPONSORS, type Sponsor } from "@/constants/site-constants";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { LinkSquare02Icon } from "@hugeicons/core-free-icons";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Sponsors - OpenCut",
|
||||
|
|
@ -25,9 +26,9 @@ export default function SponsorsPage() {
|
|||
<h1 className="text-5xl font-bold tracking-tight md:text-6xl">
|
||||
Sponsors
|
||||
</h1>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-xl leading-relaxed">
|
||||
Support OpenCut and help us build the future of free and open-source
|
||||
video editing.
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-xl leading-relaxed text-pretty">
|
||||
Support OpenCut and help us build the future of privacy-first video
|
||||
editing.
|
||||
</p>
|
||||
</div>
|
||||
<SponsorsGrid />
|
||||
|
|
@ -51,7 +52,7 @@ function SponsorCard({ sponsor }: { sponsor: Sponsor }) {
|
|||
href={sponsor.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="size-full"
|
||||
className="size-full"
|
||||
>
|
||||
<Card className="h-full">
|
||||
<CardContent className="flex h-full flex-col justify-center gap-8 p-8">
|
||||
|
|
@ -67,7 +68,10 @@ function SponsorCard({ sponsor }: { sponsor: Sponsor }) {
|
|||
<h3 className="text-xl font-semibold group-hover:underline">
|
||||
{sponsor.name}
|
||||
</h3>
|
||||
<ExternalLink className="text-muted-foreground size-4" />
|
||||
<HugeiconsIcon
|
||||
icon={LinkSquare02Icon}
|
||||
className="text-muted-foreground size-4"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-muted-foreground">{sponsor.description}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Upload } from "lucide-react";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { UploadIcon } from "@hugeicons/core-free-icons";
|
||||
|
||||
interface MediaDragOverlayProps {
|
||||
isVisible: boolean;
|
||||
|
|
@ -34,7 +35,7 @@ export function MediaDragOverlay({
|
|||
onClick={(event) => handleClick({ event })}
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
<Upload className="text-foreground size-10" />
|
||||
<HugeiconsIcon icon={UploadIcon} className="text-foreground size-10" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export function TabBar() {
|
|||
type="button"
|
||||
aria-label={tab.label}
|
||||
className={cn(
|
||||
"flex cursor-pointer flex-col items-center gap-0.5",
|
||||
"flex cursor-pointer flex-col items-center gap-0.5 [&>svg]:size-4.5! opacity-100 hover:opacity-75",
|
||||
activeTab === tabKey
|
||||
? "text-primary !opacity-100"
|
||||
: "text-muted-foreground",
|
||||
|
|
@ -72,7 +72,7 @@ export function TabBar() {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<tab.icon className="size-[1.1rem]! opacity-100 hover:opacity-75" />
|
||||
<tab.icon className=" " />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import { extractTimelineAudio } from "@/lib/media/mediabunny";
|
|||
import { useEditor } from "@/hooks/use-editor";
|
||||
import { DEFAULT_TEXT_ELEMENT } from "@/constants/text-constants";
|
||||
import { TRANSCRIPTION_LANGUAGES } from "@/constants/transcription-constants";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { Loading03Icon } from "@hugeicons/core-free-icons";
|
||||
import type {
|
||||
TranscriptionLanguage,
|
||||
TranscriptionProgress,
|
||||
|
|
@ -21,6 +22,7 @@ import type {
|
|||
import { transcriptionService } from "@/services/transcription";
|
||||
import { decodeAudioToFloat32 } from "@/lib/media/audio";
|
||||
import { buildCaptionChunks } from "@/lib/transcription/caption";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
|
||||
export function Captions() {
|
||||
const [selectedLanguage, setSelectedLanguage] =
|
||||
|
|
@ -143,7 +145,7 @@ export function Captions() {
|
|||
onClick={handleGenerateTranscript}
|
||||
disabled={isProcessing}
|
||||
>
|
||||
{isProcessing && <Loader2 className="mr-1 animate-spin" />}
|
||||
{isProcessing && <Spinner className="mr-1" />}
|
||||
{isProcessing ? processingStep : "Generate transcript"}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import {
|
||||
ArrowDown01,
|
||||
CloudUpload,
|
||||
Grid2X2,
|
||||
ImageIcon,
|
||||
List,
|
||||
Loader2,
|
||||
Music,
|
||||
Video,
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
|
@ -44,6 +33,17 @@ import { useAssetsPanelStore } from "@/stores/assets-panel-store";
|
|||
import type { MediaAsset } from "@/types/assets";
|
||||
import type { CreateTimelineElement } from "@/types/timeline";
|
||||
import { cn } from "@/utils/ui";
|
||||
import {
|
||||
CloudUploadIcon,
|
||||
GridViewIcon,
|
||||
LeftToRightListDashIcon,
|
||||
SortingOneNineIcon,
|
||||
Image02Icon,
|
||||
Loading03Icon,
|
||||
MusicNote03Icon,
|
||||
Video01Icon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/react";
|
||||
|
||||
export function MediaView() {
|
||||
const editor = useEditor();
|
||||
|
|
@ -207,9 +207,9 @@ export function MediaView() {
|
|||
className="w-full"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 className="animate-spin" />
|
||||
<HugeiconsIcon icon={Loading03Icon} className="animate-spin" />
|
||||
) : (
|
||||
<CloudUpload />
|
||||
<HugeiconsIcon icon={CloudUploadIcon} />
|
||||
)}
|
||||
<span>Upload</span>
|
||||
</Button>
|
||||
|
|
@ -229,12 +229,9 @@ export function MediaView() {
|
|||
className="items-center justify-center"
|
||||
>
|
||||
{mediaViewMode === "grid" ? (
|
||||
<List strokeWidth={1.5} className="!size-[1.05rem]" />
|
||||
<HugeiconsIcon icon={LeftToRightListDashIcon} />
|
||||
) : (
|
||||
<Grid2X2
|
||||
strokeWidth={1.5}
|
||||
className="!size-[1.05rem]"
|
||||
/>
|
||||
<HugeiconsIcon icon={GridViewIcon} />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
|
|
@ -255,10 +252,7 @@ export function MediaView() {
|
|||
disabled={isProcessing}
|
||||
className="items-center justify-center"
|
||||
>
|
||||
<ArrowDown01
|
||||
strokeWidth={1.5}
|
||||
className="!size-[1.05rem]"
|
||||
/>
|
||||
<HugeiconsIcon icon={SortingOneNineIcon} />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
</TooltipTrigger>
|
||||
|
|
@ -531,12 +525,12 @@ function MediaDurationLabel({ duration }: { duration?: number }) {
|
|||
}
|
||||
|
||||
function MediaTypePlaceholder({
|
||||
icon: Icon,
|
||||
icon,
|
||||
label,
|
||||
duration,
|
||||
variant,
|
||||
}: {
|
||||
icon: LucideIcon;
|
||||
icon: IconSvgElement;
|
||||
label: string;
|
||||
duration?: number;
|
||||
variant: "muted" | "bordered";
|
||||
|
|
@ -550,7 +544,7 @@ function MediaTypePlaceholder({
|
|||
variant === "muted" ? "bg-muted/30" : "border",
|
||||
)}
|
||||
>
|
||||
<Icon className={iconClassName} aria-hidden="true" />
|
||||
<HugeiconsIcon icon={icon} className={iconClassName} />
|
||||
<span className="text-xs">{label}</span>
|
||||
<MediaDurationLabel duration={duration} />
|
||||
</div>
|
||||
|
|
@ -596,7 +590,10 @@ function MediaPreview({
|
|||
/>
|
||||
{shouldShowVideoOverlay ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center rounded bg-black/20">
|
||||
<Video className="size-6 text-white drop-shadow-md" />
|
||||
<HugeiconsIcon
|
||||
icon={Video01Icon}
|
||||
className="size-6 text-white drop-shadow-md"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{shouldShowDurationBadge ? (
|
||||
|
|
@ -608,7 +605,7 @@ function MediaPreview({
|
|||
|
||||
return (
|
||||
<MediaTypePlaceholder
|
||||
icon={Video}
|
||||
icon={Video01Icon}
|
||||
label="Video"
|
||||
duration={item.duration}
|
||||
variant="muted"
|
||||
|
|
@ -619,7 +616,7 @@ function MediaPreview({
|
|||
if (item.type === "audio") {
|
||||
return (
|
||||
<MediaTypePlaceholder
|
||||
icon={Music}
|
||||
icon={MusicNote03Icon}
|
||||
label="Audio"
|
||||
duration={item.duration}
|
||||
variant="bordered"
|
||||
|
|
@ -628,7 +625,7 @@ function MediaPreview({
|
|||
}
|
||||
|
||||
return (
|
||||
<MediaTypePlaceholder icon={ImageIcon} label="Unknown" variant="muted" />
|
||||
<MediaTypePlaceholder icon={Image02Icon} label="Unknown" variant="muted" />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { PipetteIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { memo, useCallback, useMemo } from "react";
|
||||
import { PanelBaseView as BaseView } from "@/components/editor/panel-base-view";
|
||||
|
|
@ -31,6 +30,8 @@ import {
|
|||
PropertyItemLabel,
|
||||
PropertyItemValue,
|
||||
} from "../../properties-panel/property-item";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { DropperIcon } from "@hugeicons/core-free-icons";
|
||||
|
||||
export function SettingsView() {
|
||||
return <ProjectSettingsTabs />;
|
||||
|
|
@ -355,7 +356,7 @@ function BackgroundView() {
|
|||
<PropertyGroup title="Colors" defaultExpanded={false}>
|
||||
<div className="grid w-full grid-cols-4 gap-2">
|
||||
<div className="border-foreground/15 hover:border-primary flex aspect-square w-full cursor-pointer items-center justify-center rounded-sm border">
|
||||
<PipetteIcon className="size-4" />
|
||||
<HugeiconsIcon icon={DropperIcon} className="size-4" />
|
||||
</div>
|
||||
<BackgroundPreviews
|
||||
backgrounds={colors}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
HeartIcon,
|
||||
ListFilter,
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
|
|
@ -33,6 +26,14 @@ import { useSoundSearch } from "@/hooks/use-sound-search";
|
|||
import { useSoundsStore } from "@/stores/sounds-store";
|
||||
import type { SavedSound, SoundEffect } from "@/types/sounds";
|
||||
import { cn } from "@/utils/ui";
|
||||
import {
|
||||
FavouriteIcon,
|
||||
FilterMailIcon,
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
PlusSignIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
||||
export function SoundsView() {
|
||||
return (
|
||||
|
|
@ -250,7 +251,7 @@ function SoundEffectsView() {
|
|||
size="icon"
|
||||
className={cn(showCommercialOnly && "text-primary")}
|
||||
>
|
||||
<ListFilter />
|
||||
<HugeiconsIcon icon={FilterMailIcon} />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
|
|
@ -406,9 +407,9 @@ function SavedSoundsView() {
|
|||
if (savedSounds.length === 0) {
|
||||
return (
|
||||
<div className="bg-panel flex h-full flex-col items-center justify-center gap-3 p-4">
|
||||
<HeartIcon
|
||||
<HugeiconsIcon
|
||||
icon={FavouriteIcon}
|
||||
className="text-muted-foreground size-10"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<p className="text-lg font-medium">No saved sounds</p>
|
||||
|
|
@ -527,9 +528,9 @@ function AudioItem({ sound, isPlaying, onPlay }: AudioItemProps) {
|
|||
<div className="bg-accent relative flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-md">
|
||||
<div className="from-primary/20 absolute inset-0 bg-gradient-to-br to-transparent" />
|
||||
{isPlaying ? (
|
||||
<PauseIcon className="size-5" />
|
||||
<HugeiconsIcon icon={PauseIcon} className="size-5" />
|
||||
) : (
|
||||
<PlayIcon className="size-5" />
|
||||
<HugeiconsIcon icon={PlayIcon} className="size-5" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
@ -549,7 +550,7 @@ function AudioItem({ sound, isPlaying, onPlay }: AudioItemProps) {
|
|||
onClick={handleAddToTimeline}
|
||||
title="Add to timeline"
|
||||
>
|
||||
<PlusIcon />
|
||||
<HugeiconsIcon icon={PlusSignIcon} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="text"
|
||||
|
|
@ -562,7 +563,10 @@ function AudioItem({ sound, isPlaying, onPlay }: AudioItemProps) {
|
|||
onClick={handleSaveClick}
|
||||
title={isSaved ? "Remove from saved" : "Save sound"}
|
||||
>
|
||||
<HeartIcon className={`${isSaved ? "fill-current" : ""}`} />
|
||||
<HugeiconsIcon
|
||||
icon={FavouriteIcon}
|
||||
className={`${isSaved ? "fill-current" : ""}`}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
ArrowRight,
|
||||
Clock,
|
||||
Grid3X3,
|
||||
Hash,
|
||||
Loader2,
|
||||
Smile,
|
||||
Sparkles,
|
||||
StickerIcon,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
|
@ -37,6 +26,17 @@ import {
|
|||
import { useStickersStore } from "@/stores/stickers-store";
|
||||
import type { StickerCategory } from "@/types/stickers";
|
||||
import { cn } from "@/utils/ui";
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
HappyIcon,
|
||||
ClockIcon,
|
||||
LayoutGridIcon,
|
||||
MultiplicationSignIcon,
|
||||
SparklesIcon,
|
||||
HashtagIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
|
||||
function isStickerCategory(value: string): value is StickerCategory {
|
||||
return STICKER_CATEGORIES.includes(value as StickerCategory);
|
||||
|
|
@ -57,25 +57,25 @@ export function StickersView() {
|
|||
{
|
||||
value: "all",
|
||||
label: "All",
|
||||
icon: <Grid3X3 className="size-3" />,
|
||||
icon: <HugeiconsIcon icon={LayoutGridIcon} className="size-3" />,
|
||||
content: <StickersContentView category="all" />,
|
||||
},
|
||||
{
|
||||
value: "general",
|
||||
label: "Icons",
|
||||
icon: <Sparkles className="size-3" />,
|
||||
icon: <HugeiconsIcon icon={SparklesIcon} className="size-3" />,
|
||||
content: <StickersContentView category="general" />,
|
||||
},
|
||||
{
|
||||
value: "brands",
|
||||
label: "Brands",
|
||||
icon: <Hash className="size-3" />,
|
||||
icon: <HugeiconsIcon icon={HashtagIcon} className="size-3" />,
|
||||
content: <StickersContentView category="brands" />,
|
||||
},
|
||||
{
|
||||
value: "emoji",
|
||||
label: "Emoji",
|
||||
icon: <Smile className="size-3" />,
|
||||
icon: <HugeiconsIcon icon={HappyIcon} className="size-3" />,
|
||||
content: <StickersContentView category="emoji" />,
|
||||
},
|
||||
]}
|
||||
|
|
@ -150,9 +150,9 @@ function CollectionGrid({
|
|||
function EmptyView({ message }: { message: string }) {
|
||||
return (
|
||||
<div className="bg-panel flex h-full flex-col items-center justify-center gap-3 p-4">
|
||||
<StickerIcon
|
||||
<HugeiconsIcon
|
||||
icon={HappyIcon}
|
||||
className="text-muted-foreground size-10"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<p className="text-lg font-medium">No stickers found</p>
|
||||
|
|
@ -334,7 +334,10 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
{recentStickers.length > 0 && viewMode === "browse" && (
|
||||
<div className="h-full">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<Clock className="text-muted-foreground size-4" />
|
||||
<HugeiconsIcon
|
||||
icon={ClockIcon}
|
||||
className="text-muted-foreground size-4"
|
||||
/>
|
||||
<span className="text-sm font-medium">Recent</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
|
|
@ -344,7 +347,10 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
onClick={clearRecentStickers}
|
||||
className="hover:bg-accent ml-auto flex size-5 items-center justify-center rounded p-0"
|
||||
>
|
||||
<X className="text-muted-foreground size-3" />
|
||||
<HugeiconsIcon
|
||||
icon={MultiplicationSignIcon}
|
||||
className="text-muted-foreground size-3"
|
||||
/>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
|
|
@ -366,7 +372,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
<div className="h-full">
|
||||
{isLoadingCollection ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="text-muted-foreground size-6 animate-spin" />
|
||||
<Spinner className="text-muted-foreground size-6" />
|
||||
</div>
|
||||
) : showCollectionItems ? (
|
||||
<StickerGrid
|
||||
|
|
@ -376,7 +382,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
/>
|
||||
) : (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="text-muted-foreground size-6 animate-spin" />
|
||||
<Spinner className="text-muted-foreground size-6" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -386,7 +392,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
<div className="h-full">
|
||||
{isSearching ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="text-muted-foreground size-6 animate-spin" />
|
||||
<Spinner className="text-muted-foreground size-6" />
|
||||
</div>
|
||||
) : searchResults?.icons.length ? (
|
||||
<>
|
||||
|
|
@ -433,7 +439,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
|
|||
<div className="h-full space-y-4">
|
||||
{isLoadingCollections ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="text-muted-foreground size-6 animate-spin" />
|
||||
<Spinner className="text-muted-foreground size-6" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -489,7 +495,7 @@ function CollectionItem({ title, subtitle, onClick }: CollectionItemProps) {
|
|||
<p className="font-medium">{title}</p>
|
||||
<p className="text-muted-foreground text-xs">{subtitle}</p>
|
||||
</div>
|
||||
<ArrowRight />
|
||||
<HugeiconsIcon icon={ArrowRightIcon} className="size-4" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
@ -593,7 +599,7 @@ function StickerItem({
|
|||
/>
|
||||
{isAdding && (
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-md bg-black/60">
|
||||
<Loader2 className="size-6 animate-spin text-white" />
|
||||
<Spinner className="size-6 text-white" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export function DraggableItem({
|
|||
const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 });
|
||||
const dragRef = useRef<HTMLDivElement>(null);
|
||||
const editor = useEditor();
|
||||
const highlightClassName = "ring-2 ring-primary rounded-sm bg-primary/10";
|
||||
const highlightClassName = `ring-2 ring-primary bg-primary/10 ${isRounded ? "rounded-sm" : ""}`;
|
||||
|
||||
const handleAddToTimeline = () => {
|
||||
onAddToTimeline?.({ currentTime: editor.playback.getCurrentTime() });
|
||||
|
|
@ -111,7 +111,7 @@ export function DraggableItem({
|
|||
ratio={aspectRatio}
|
||||
className={cn(
|
||||
"bg-panel-accent relative overflow-hidden",
|
||||
isRounded && "rounded-md",
|
||||
isRounded && "rounded-sm",
|
||||
isDraggable && "[&::-webkit-drag-ghost]:opacity-0",
|
||||
)}
|
||||
draggable={isDraggable}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import { Button } from "../ui/button";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { KeyboardShortcutsHelp } from "../keyboard-shortcuts-help";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -21,7 +20,14 @@ import { ThemeToggle } from "../theme-toggle";
|
|||
import { SOCIAL_LINKS } from "@/constants/site-constants";
|
||||
import { toast } from "sonner";
|
||||
import { useEditor } from "@/hooks/use-editor";
|
||||
import { OcLeftArrowIcon, OcPencilIcon, OcTrashIcon } from "@opencut/ui/icons";
|
||||
import {
|
||||
ArrowLeft02Icon,
|
||||
Edit03Icon,
|
||||
Delete02Icon,
|
||||
Keyboard,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { ShortcutsDialog } from "./shortcuts-dialog";
|
||||
|
||||
export function EditorHeader() {
|
||||
return (
|
||||
|
|
@ -30,7 +36,6 @@ export function EditorHeader() {
|
|||
<ProjectDropdown />
|
||||
</div>
|
||||
<nav className="flex items-center gap-2">
|
||||
<KeyboardShortcutsHelp />
|
||||
<ExportButton />
|
||||
<ThemeToggle />
|
||||
</nav>
|
||||
|
|
@ -39,8 +44,9 @@ export function EditorHeader() {
|
|||
}
|
||||
|
||||
function ProjectDropdown() {
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
const [isRenameDialogOpen, setIsRenameDialogOpen] = useState(false);
|
||||
const [openDialog, setOpenDialog] = useState<
|
||||
"delete" | "rename" | "shortcuts" | null
|
||||
>(null);
|
||||
const [isExiting, setIsExiting] = useState(false);
|
||||
const router = useRouter();
|
||||
const editor = useEditor();
|
||||
|
|
@ -78,7 +84,7 @@ function ProjectDropdown() {
|
|||
error instanceof Error ? error.message : "Please try again",
|
||||
});
|
||||
} finally {
|
||||
setIsRenameDialogOpen(false);
|
||||
setOpenDialog(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -94,7 +100,7 @@ function ProjectDropdown() {
|
|||
error instanceof Error ? error.message : "Please try again",
|
||||
});
|
||||
} finally {
|
||||
setIsDeleteDialogOpen(false);
|
||||
setOpenDialog(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -119,25 +125,32 @@ function ProjectDropdown() {
|
|||
onClick={handleExit}
|
||||
disabled={isExiting}
|
||||
>
|
||||
<OcLeftArrowIcon className="size-4" />
|
||||
Projects
|
||||
<HugeiconsIcon icon={ArrowLeft02Icon} className="size-4" />
|
||||
Exit project
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={() => setIsRenameDialogOpen(true)}
|
||||
onClick={() => setOpenDialog("rename")}
|
||||
>
|
||||
<OcPencilIcon className="size-4" />
|
||||
<HugeiconsIcon icon={Edit03Icon} className="size-4" />
|
||||
Rename project
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={() => setIsDeleteDialogOpen(true)}
|
||||
onClick={() => setOpenDialog("delete")}
|
||||
>
|
||||
<OcTrashIcon className="size-4" />
|
||||
Delete Project
|
||||
<HugeiconsIcon icon={Delete02Icon} className="size-4" />
|
||||
Delete project
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex items-center gap-1.5"
|
||||
onClick={() => setOpenDialog("shortcuts")}
|
||||
>
|
||||
<HugeiconsIcon icon={Keyboard} className="size-4" />
|
||||
Keyboard shortcuts
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={SOCIAL_LINKS.discord}
|
||||
|
|
@ -152,17 +165,21 @@ function ProjectDropdown() {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<RenameProjectDialog
|
||||
isOpen={isRenameDialogOpen}
|
||||
onOpenChange={setIsRenameDialogOpen}
|
||||
isOpen={openDialog === "rename"}
|
||||
onOpenChange={(isOpen) => setOpenDialog(isOpen ? "rename" : null)}
|
||||
onConfirm={(newName) => handleSaveProjectName(newName)}
|
||||
projectName={activeProject?.metadata.name || ""}
|
||||
/>
|
||||
<DeleteProjectDialog
|
||||
isOpen={isDeleteDialogOpen}
|
||||
onOpenChange={setIsDeleteDialogOpen}
|
||||
isOpen={openDialog === "delete"}
|
||||
onOpenChange={(isOpen) => setOpenDialog(isOpen ? "delete" : null)}
|
||||
onConfirm={handleDeleteProject}
|
||||
projectName={activeProject?.metadata.name || ""}
|
||||
/>
|
||||
<ShortcutsDialog
|
||||
isOpen={openDialog === "shortcuts"}
|
||||
onOpenChange={(isOpen) => setOpenDialog(isOpen ? "shortcuts" : null)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { OcTransitionUpIcon } from "@opencut/ui/icons";
|
||||
import { TransitionTopIcon } from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
|
||||
import { Button } from "../ui/button";
|
||||
import { Label } from "../ui/label";
|
||||
|
|
@ -53,7 +54,7 @@ export function ExportButton() {
|
|||
}}
|
||||
>
|
||||
<div className="relative flex items-center gap-1.5 rounded-[0.8rem] bg-linear-270 from-[#2567EC] to-[#37B6F7] px-4 py-1 shadow-[0_1px_3px_0px_rgba(0,0,0,0.65)]">
|
||||
<OcTransitionUpIcon className="z-50" />
|
||||
<HugeiconsIcon icon={TransitionTopIcon} className="z-50 size-4" />
|
||||
<span className="z-50 text-[0.875rem]">Export</span>
|
||||
<div className="absolute top-0 left-0 z-10 flex size-full items-center justify-center rounded-[0.8rem] bg-linear-to-t from-white/0 to-white/50">
|
||||
<div className="absolute top-[0.08rem] z-50 h-[calc(100%-2px)] w-[calc(100%-2px)] rounded-[0.8rem] bg-linear-270 from-[#2567EC] to-[#37B6F7]"></div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { ScrollArea } from "../../ui/scroll-area";
|
|||
import { AudioProperties } from "./audio-properties";
|
||||
import { VideoProperties } from "./video-properties";
|
||||
import { TextProperties } from "./text-properties";
|
||||
import { SquareSlashIcon } from "lucide-react";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { Settings05Icon } from "@hugeicons/core-free-icons";
|
||||
import { useEditor } from "@/hooks/use-editor";
|
||||
import { useElementSelection } from "@/hooks/timeline/element/use-element-selection";
|
||||
|
||||
|
|
@ -29,12 +30,12 @@ export function PropertiesPanel() {
|
|||
);
|
||||
}
|
||||
if (element.type === "audio") {
|
||||
return <AudioProperties key={element.id} element={element} />;
|
||||
return <AudioProperties key={element.id} _element={element} />;
|
||||
}
|
||||
if (element.type === "video" || element.type === "image") {
|
||||
return (
|
||||
<div key={element.id}>
|
||||
<VideoProperties element={element} />
|
||||
<VideoProperties _element={element} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -51,12 +52,13 @@ export function PropertiesPanel() {
|
|||
function EmptyView() {
|
||||
return (
|
||||
<div className="bg-panel flex h-full flex-col items-center justify-center gap-3 p-4">
|
||||
<SquareSlashIcon
|
||||
className="text-muted-foreground size-10"
|
||||
strokeWidth={1.5}
|
||||
<HugeiconsIcon
|
||||
icon={Settings05Icon}
|
||||
className="text-muted-foreground/75 size-10"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<p className="text-lg font-medium">It’s empty here</p>
|
||||
<p className="text-lg font-medium">It's empty here</p>
|
||||
<p className="text-muted-foreground text-sm text-balance">
|
||||
Click an element on the timeline to edit its properties
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@/utils/ui";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { ArrowDownIcon } from "@hugeicons/core-free-icons";
|
||||
|
||||
interface PropertyItemProps {
|
||||
direction?: "row" | "column";
|
||||
|
|
@ -79,7 +80,10 @@ export function PropertyGroup({
|
|||
<PropertyItemLabel className={cn("cursor-pointer", titleClassName)}>
|
||||
{title}
|
||||
</PropertyItemLabel>
|
||||
<ChevronDown className={cn("size-3", !isExpanded && "-rotate-90")} />
|
||||
<HugeiconsIcon
|
||||
icon={ArrowDownIcon}
|
||||
className={cn("size-3", !isExpanded && "-rotate-90")}
|
||||
/>
|
||||
</button>
|
||||
{isExpanded && <PropertyItemValue>{children}</PropertyItemValue>}
|
||||
</PropertyItem>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import { ColorPicker } from "@/components/ui/color-picker";
|
|||
import { cn } from "@/utils/ui";
|
||||
import { capitalizeFirstLetter, uppercase } from "@/utils/strings";
|
||||
import { clamp } from "@/utils/math";
|
||||
import { Grid2x2 } from "lucide-react";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { LayoutGridIcon } from "@hugeicons/core-free-icons";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
|
|
@ -386,7 +387,7 @@ export function TextProperties({
|
|||
}
|
||||
className="bg-panel-accent size-9 overflow-hidden rounded-full p-0"
|
||||
>
|
||||
<Grid2x2
|
||||
<HugeiconsIcon icon={LayoutGridIcon}
|
||||
className={cn(
|
||||
"text-foreground",
|
||||
element.backgroundColor === "transparent" &&
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
useKeyboardShortcutsHelp,
|
||||
} from "@/hooks/use-keyboard-shortcuts-help";
|
||||
import { useKeybindingsStore } from "@/stores/keybindings-store";
|
||||
import { Button } from "./ui/button";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -16,11 +16,15 @@ import {
|
|||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "./ui/dialog";
|
||||
} from "../ui/dialog";
|
||||
|
||||
export function KeyboardShortcutsHelp() {
|
||||
const [open, setOpen] = useState(false);
|
||||
export function ShortcutsDialog({
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}) {
|
||||
const [recordingShortcut, setRecordingShortcut] =
|
||||
useState<KeyboardShortcut | null>(null);
|
||||
|
||||
|
|
@ -35,7 +39,6 @@ export function KeyboardShortcutsHelp() {
|
|||
isRecording,
|
||||
} = useKeybindingsStore();
|
||||
|
||||
// Get shortcuts from centralized hook
|
||||
const { shortcuts } = useKeyboardShortcutsHelp();
|
||||
|
||||
const categories = Array.from(new Set(shortcuts.map((s) => s.category)));
|
||||
|
|
@ -49,7 +52,6 @@ export function KeyboardShortcutsHelp() {
|
|||
|
||||
const keyString = getKeybindingString(e);
|
||||
if (keyString) {
|
||||
// Auto-save the new keybinding
|
||||
const conflict = validateKeybinding(
|
||||
keyString,
|
||||
recordingShortcut.action,
|
||||
|
|
@ -62,11 +64,11 @@ export function KeyboardShortcutsHelp() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Remove old keybindings for this action
|
||||
const oldKeys = getKeybindingsForAction(recordingShortcut.action);
|
||||
oldKeys.forEach((key) => removeKeybinding(key));
|
||||
for (const key of oldKeys) {
|
||||
removeKeybinding(key);
|
||||
}
|
||||
|
||||
// Add new keybinding
|
||||
updateKeybinding(keyString, recordingShortcut.action);
|
||||
|
||||
setIsRecording(false);
|
||||
|
|
@ -103,18 +105,12 @@ export function KeyboardShortcutsHelp() {
|
|||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="text" size="sm" className="gap-2">
|
||||
<Keyboard />
|
||||
Shortcuts
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col p-0">
|
||||
<DialogHeader className="flex-shrink-0 p-6 pb-0">
|
||||
<DialogHeader className="flex-shrink-0 gap-2 p-6 pb-0">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Keyboard className="size-5" />
|
||||
Keyboard Shortcuts
|
||||
Keyboard shortcuts
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Speed up your video editing workflow with these keyboard shortcuts.
|
||||
|
|
@ -123,13 +119,13 @@ export function KeyboardShortcutsHelp() {
|
|||
</DialogHeader>
|
||||
|
||||
<div className="scrollbar-thin flex-grow overflow-y-auto">
|
||||
<div className="space-y-6 p-6 pt-2">
|
||||
<div className="flex flex-col gap-6 p-6 pt-2">
|
||||
{categories.map((category) => (
|
||||
<div key={category} className="flex flex-col gap-1">
|
||||
<h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
|
||||
{category}
|
||||
</h3>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
{shortcuts
|
||||
.filter((shortcut) => shortcut.category === category)
|
||||
.map((shortcut) => (
|
||||
|
|
@ -139,7 +135,7 @@ export function KeyboardShortcutsHelp() {
|
|||
isRecording={
|
||||
shortcut.action === recordingShortcut?.action
|
||||
}
|
||||
onStartRecording={handleStartRecording}
|
||||
onStartRecording={() => handleStartRecording(shortcut)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -164,9 +160,8 @@ function ShortcutItem({
|
|||
}: {
|
||||
shortcut: KeyboardShortcut;
|
||||
isRecording: boolean;
|
||||
onStartRecording: (shortcut: KeyboardShortcut) => void;
|
||||
onStartRecording: (params: { shortcut: KeyboardShortcut }) => void;
|
||||
}) {
|
||||
// Filter out lowercase duplicates for display - if both "j" and "J" exist, only show "J"
|
||||
const displayKeys = shortcut.keys.filter((key: string) => {
|
||||
if (
|
||||
key.includes("Cmd") &&
|
||||
|
|
@ -195,7 +190,7 @@ function ShortcutItem({
|
|||
<EditableShortcutKey
|
||||
key={keyId}
|
||||
isRecording={isRecording}
|
||||
onStartRecording={() => onStartRecording(shortcut)}
|
||||
onStartRecording={() => onStartRecording({ shortcut })}
|
||||
>
|
||||
{keyPart}
|
||||
</EditableShortcutKey>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { useEditor } from "@/hooks/use-editor";
|
||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||
import { Bookmark } from "lucide-react";
|
||||
import { Bookmark02Icon } from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
||||
interface TimelineBookmarksRowProps {
|
||||
zoomLevel: number;
|
||||
|
|
@ -84,10 +85,10 @@ export function TimelineBookmark({
|
|||
onClick={(event) => handleBookmarkClick({ event })}
|
||||
>
|
||||
<div className="text-primary absolute top-[-1px] left-[-5px]">
|
||||
<Bookmark
|
||||
<HugeiconsIcon
|
||||
icon={Bookmark02Icon}
|
||||
aria-hidden="true"
|
||||
className="fill-primary size-3"
|
||||
focusable="false"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
Eye,
|
||||
EyeOff,
|
||||
VolumeOff,
|
||||
Volume2,
|
||||
type LucideIcon,
|
||||
EyeIcon,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
Delete02Icon,
|
||||
ViewIcon,
|
||||
ViewOffSlashIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeOffIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/react";
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
|
|
@ -133,6 +132,7 @@ export function Timeline() {
|
|||
selectionBox,
|
||||
handleMouseDown: handleSelectionMouseDown,
|
||||
isSelecting,
|
||||
shouldIgnoreClick,
|
||||
} = useSelectionBox({
|
||||
containerRef: tracksContainerRef,
|
||||
onSelectionComplete: (elements) => {
|
||||
|
|
@ -267,17 +267,12 @@ export function Timeline() {
|
|||
}}
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center justify-end gap-2">
|
||||
{/* Debug main track */}
|
||||
{/* {isMainTrack(track) && (
|
||||
<div className="size-2 rounded-full bg-red-500" />
|
||||
)} */}
|
||||
|
||||
{canTracktHaveAudio(track) && (
|
||||
<TrackToggleIcon
|
||||
isOff={track.muted}
|
||||
icons={{
|
||||
on: Volume2,
|
||||
off: VolumeOff,
|
||||
on: VolumeHighIcon,
|
||||
off: VolumeOffIcon,
|
||||
}}
|
||||
onClick={() =>
|
||||
editor.timeline.toggleTrackMute({
|
||||
|
|
@ -291,8 +286,8 @@ export function Timeline() {
|
|||
<TrackToggleIcon
|
||||
isOff={track.hidden}
|
||||
icons={{
|
||||
on: Eye,
|
||||
off: EyeOff,
|
||||
on: ViewIcon,
|
||||
off: ViewOffSlashIcon,
|
||||
}}
|
||||
onClick={() =>
|
||||
editor.timeline.toggleTrackVisibility({
|
||||
|
|
@ -397,6 +392,7 @@ export function Timeline() {
|
|||
onElementMouseDown={handleElementMouseDown}
|
||||
onElementClick={handleElementClick}
|
||||
onTrackMouseDown={handleSelectionMouseDown}
|
||||
shouldIgnoreClick={shouldIgnoreClick}
|
||||
/>
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
|
|
@ -409,7 +405,7 @@ export function Timeline() {
|
|||
});
|
||||
}}
|
||||
>
|
||||
<Volume2 />
|
||||
<HugeiconsIcon icon={VolumeHighIcon} />
|
||||
<span>
|
||||
{canTracktHaveAudio(track) && track.muted
|
||||
? "Unmute track"
|
||||
|
|
@ -424,7 +420,7 @@ export function Timeline() {
|
|||
});
|
||||
}}
|
||||
>
|
||||
<EyeIcon />
|
||||
<HugeiconsIcon icon={ViewIcon} />
|
||||
<span>
|
||||
{canTrackBeHidden(track) && track.hidden
|
||||
? "Show track"
|
||||
|
|
@ -440,7 +436,7 @@ export function Timeline() {
|
|||
}}
|
||||
variant="destructive"
|
||||
>
|
||||
<Trash2 />
|
||||
<HugeiconsIcon icon={Delete02Icon} />
|
||||
Delete track
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
|
|
@ -467,20 +463,22 @@ function TrackToggleIcon({
|
|||
}: {
|
||||
isOff: boolean;
|
||||
icons: {
|
||||
on: LucideIcon;
|
||||
off: LucideIcon;
|
||||
on: IconSvgElement;
|
||||
off: IconSvgElement;
|
||||
};
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{isOff ? (
|
||||
<icons.off
|
||||
<HugeiconsIcon
|
||||
icon={icons.off}
|
||||
className="text-destructive size-4 cursor-pointer"
|
||||
onClick={onClick}
|
||||
/>
|
||||
) : (
|
||||
<icons.on
|
||||
<HugeiconsIcon
|
||||
icon={icons.on}
|
||||
className="text-muted-foreground size-4 cursor-pointer"
|
||||
onClick={onClick}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -213,10 +213,6 @@ export function TimelineElement({
|
|||
Duplicate {element.type === "text" ? "text" : "clip"}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
<ContextMenuItem disabled>
|
||||
<ArrowUpDown className="mr-2 size-4" />
|
||||
Move to track (Coming soon)
|
||||
</ContextMenuItem>
|
||||
{selectedElements.length === 1 && hasMediaId(element) && (
|
||||
<>
|
||||
<ContextMenuItem
|
||||
|
|
@ -289,7 +285,6 @@ function ElementInner({
|
|||
className="absolute inset-0 size-full cursor-pointer"
|
||||
onClick={(e) => onElementClick(e, element)}
|
||||
onMouseDown={(e) => onElementMouseDown(e, element)}
|
||||
onContextMenu={(e) => onElementMouseDown(e, element)}
|
||||
>
|
||||
<div className="absolute inset-0 flex h-full items-center">
|
||||
<ElementContent
|
||||
|
|
@ -300,7 +295,9 @@ function ElementInner({
|
|||
/>
|
||||
</div>
|
||||
|
||||
{(hasAudio ? isMuted : canElementBeHidden(element) && element.hidden) && (
|
||||
{(hasAudio
|
||||
? isMuted
|
||||
: canElementBeHidden(element) && element.hidden) && (
|
||||
<div className="bg-opacity-50 pointer-events-none absolute inset-0 flex items-center justify-center bg-black">
|
||||
{hasAudio ? (
|
||||
<VolumeX className="size-6 text-white" />
|
||||
|
|
@ -381,8 +378,8 @@ function ElementContent({
|
|||
src={`https://api.iconify.design/${element.iconName}.svg?width=20&height=20`}
|
||||
alt={element.name}
|
||||
className="size-5 shrink-0"
|
||||
width={20}
|
||||
height={20}
|
||||
width={20}
|
||||
height={20}
|
||||
unoptimized
|
||||
/>
|
||||
<span className="truncate text-xs text-white">{element.name}</span>
|
||||
|
|
|
|||
|
|
@ -7,21 +7,8 @@ import {
|
|||
} from "@/components/ui/tooltip";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Pause,
|
||||
Play,
|
||||
SkipBack,
|
||||
Bookmark,
|
||||
Magnet,
|
||||
Link,
|
||||
ZoomOut,
|
||||
ZoomIn,
|
||||
Copy,
|
||||
Trash2,
|
||||
Snowflake,
|
||||
ArrowLeftToLine,
|
||||
ArrowRightToLine,
|
||||
SplitSquareHorizontal,
|
||||
Scissors,
|
||||
LayersIcon,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
|
|
@ -38,6 +25,28 @@ import { ScenesView } from "../scenes-view";
|
|||
import { type TAction, invokeAction } from "@/lib/actions";
|
||||
import { cn } from "@/utils/ui";
|
||||
import { useTimelineStore } from "@/stores/timeline-store";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
Bookmark02Icon,
|
||||
Delete02Icon,
|
||||
SnowIcon,
|
||||
ScissorIcon,
|
||||
MagnetIcon,
|
||||
Link04Icon,
|
||||
SearchAddIcon,
|
||||
SearchMinusIcon,
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
Copy01Icon,
|
||||
ArrowLeft03Icon,
|
||||
ArrowRight03Icon,
|
||||
LayoutAlignLeftIcon,
|
||||
LayoutAlignRightIcon,
|
||||
AlignLeftIcon,
|
||||
AlignRightIcon,
|
||||
Layers01Icon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
||||
export function TimelineToolbar({
|
||||
zoomLevel,
|
||||
|
|
@ -60,18 +69,20 @@ export function TimelineToolbar({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-10 items-center justify-between border-b px-2 py-1">
|
||||
<ToolbarLeftSection />
|
||||
<ScrollArea className="scrollbar-hidden">
|
||||
<div className="flex h-10 items-center justify-between border-b px-2 py-1">
|
||||
<ToolbarLeftSection />
|
||||
|
||||
<SceneSelector />
|
||||
<SceneSelector />
|
||||
|
||||
<ToolbarRightSection
|
||||
zoomLevel={zoomLevel}
|
||||
minZoom={minZoom}
|
||||
onZoomChange={(zoom) => setZoomLevel({ zoom })}
|
||||
onZoom={handleZoom}
|
||||
/>
|
||||
</div>
|
||||
<ToolbarRightSection
|
||||
zoomLevel={zoomLevel}
|
||||
minZoom={minZoom}
|
||||
onZoomChange={(zoom) => setZoomLevel({ zoom })}
|
||||
onZoom={handleZoom}
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +107,13 @@ function ToolbarLeftSection() {
|
|||
<div className="flex items-center gap-1">
|
||||
<TooltipProvider delayDuration={500}>
|
||||
<ToolbarButton
|
||||
icon={isPlaying ? <Pause /> : <Play />}
|
||||
icon={
|
||||
isPlaying ? (
|
||||
<HugeiconsIcon icon={PauseIcon} />
|
||||
) : (
|
||||
<HugeiconsIcon icon={PlayIcon} />
|
||||
)
|
||||
}
|
||||
tooltip={isPlaying ? "Pause" : "Play"}
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "toggle-play", event })
|
||||
|
|
@ -109,14 +126,14 @@ function ToolbarLeftSection() {
|
|||
onClick={({ event }) => handleAction({ action: "goto-start", event })}
|
||||
/>
|
||||
|
||||
<div className="bg-border mx-2 h-10 w-px" />
|
||||
<div className="bg-border mx-1 h-6 w-px" />
|
||||
|
||||
<TimeDisplay />
|
||||
|
||||
<div className="bg-border mx-1 h-10 w-px" />
|
||||
<div className="bg-border mx-1 h-6 w-px" />
|
||||
|
||||
<ToolbarButton
|
||||
icon={<Scissors />}
|
||||
icon={<HugeiconsIcon icon={ScissorIcon} />}
|
||||
tooltip="Split element"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "split-selected", event })
|
||||
|
|
@ -124,18 +141,18 @@ function ToolbarLeftSection() {
|
|||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<ArrowLeftToLine />}
|
||||
tooltip="Split and keep left"
|
||||
icon={<HugeiconsIcon icon={AlignLeftIcon} />}
|
||||
tooltip="Split left"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "split-selected-left", event })
|
||||
handleAction({ action: "split-selected-right", event })
|
||||
}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<ArrowRightToLine />}
|
||||
tooltip="Split and keep right"
|
||||
icon={<HugeiconsIcon icon={AlignRightIcon} />}
|
||||
tooltip="Split right"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "split-selected-right", event })
|
||||
handleAction({ action: "split-selected-left", event })
|
||||
}
|
||||
/>
|
||||
|
||||
|
|
@ -147,7 +164,7 @@ function ToolbarLeftSection() {
|
|||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<Copy />}
|
||||
icon={<HugeiconsIcon icon={Copy01Icon} />}
|
||||
tooltip="Duplicate element"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "duplicate-selected", event })
|
||||
|
|
@ -155,14 +172,14 @@ function ToolbarLeftSection() {
|
|||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<Snowflake />}
|
||||
icon={<HugeiconsIcon icon={SnowIcon} />}
|
||||
tooltip="Coming soon" /* freeze frame */
|
||||
disabled={true}
|
||||
onClick={({ event: _event }) => {}}
|
||||
/>
|
||||
|
||||
<ToolbarButton
|
||||
icon={<Trash2 />}
|
||||
icon={<HugeiconsIcon icon={Delete02Icon} />}
|
||||
tooltip="Delete element"
|
||||
onClick={({ event }) =>
|
||||
handleAction({ action: "delete-selected", event })
|
||||
|
|
@ -174,7 +191,8 @@ function ToolbarLeftSection() {
|
|||
<Tooltip>
|
||||
<ToolbarButton
|
||||
icon={
|
||||
<Bookmark
|
||||
<HugeiconsIcon
|
||||
icon={Bookmark02Icon}
|
||||
className={currentBookmarked ? "fill-primary text-primary" : ""}
|
||||
/>
|
||||
}
|
||||
|
|
@ -228,7 +246,7 @@ function SceneSelector() {
|
|||
<SplitButtonSeparator />
|
||||
<ScenesView>
|
||||
<SplitButtonRight onClick={() => {}} type="button">
|
||||
<LayersIcon className="size-4" />
|
||||
<HugeiconsIcon icon={Layers01Icon} className="size-4" />
|
||||
</SplitButtonRight>
|
||||
</ScenesView>
|
||||
</SplitButton>
|
||||
|
|
@ -259,7 +277,10 @@ function ToolbarRightSection({
|
|||
<TooltipProvider delayDuration={500}>
|
||||
<ToolbarButton
|
||||
icon={
|
||||
<Magnet className={cn(snappingEnabled ? "text-primary" : "")} />
|
||||
<HugeiconsIcon
|
||||
icon={MagnetIcon}
|
||||
className={cn(snappingEnabled ? "text-primary" : "")}
|
||||
/>
|
||||
}
|
||||
tooltip="Auto snapping"
|
||||
onClick={() => toggleSnapping()}
|
||||
|
|
@ -267,7 +288,13 @@ function ToolbarRightSection({
|
|||
|
||||
<ToolbarButton
|
||||
icon={
|
||||
<Link className={cn(rippleEditingEnabled ? "text-primary" : "")} />
|
||||
<HugeiconsIcon
|
||||
icon={Link04Icon}
|
||||
className={cn(
|
||||
rippleEditingEnabled ? "text-primary" : "",
|
||||
"scale-110",
|
||||
)}
|
||||
/>
|
||||
}
|
||||
tooltip="Ripple editing"
|
||||
onClick={() => toggleRippleEditing()}
|
||||
|
|
@ -283,7 +310,7 @@ function ToolbarRightSection({
|
|||
type="button"
|
||||
onClick={() => onZoom({ direction: "out" })}
|
||||
>
|
||||
<ZoomOut />
|
||||
<HugeiconsIcon icon={SearchAddIcon} />
|
||||
</Button>
|
||||
<Slider
|
||||
className="w-24"
|
||||
|
|
@ -299,7 +326,7 @@ function ToolbarRightSection({
|
|||
type="button"
|
||||
onClick={() => onZoom({ direction: "in" })}
|
||||
>
|
||||
<ZoomIn />
|
||||
<HugeiconsIcon icon={SearchMinusIcon} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ interface TimelineTrackContentProps {
|
|||
track: TimelineTrack;
|
||||
}) => void;
|
||||
onTrackMouseDown?: (event: React.MouseEvent) => void;
|
||||
shouldIgnoreClick?: () => boolean;
|
||||
}
|
||||
|
||||
export function TimelineTrackContent({
|
||||
|
|
@ -45,6 +46,7 @@ export function TimelineTrackContent({
|
|||
onElementMouseDown,
|
||||
onElementClick,
|
||||
onTrackMouseDown,
|
||||
shouldIgnoreClick,
|
||||
}: TimelineTrackContentProps) {
|
||||
const editor = useEditor();
|
||||
const { isElementSelected, clearElementSelection } = useElementSelection();
|
||||
|
|
@ -66,7 +68,10 @@ export function TimelineTrackContent({
|
|||
return (
|
||||
<button
|
||||
className={cn("size-full", hasSelectedElements && "bg-panel-accent/35")}
|
||||
onClick={clearElementSelection}
|
||||
onClick={() => {
|
||||
if (shouldIgnoreClick?.()) return;
|
||||
clearElementSelection();
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
onTrackMouseDown?.(event);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { Button } from "./ui/button";
|
||||
import { OcGithubIcon } from "@opencut/ui/icons";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { SOCIAL_LINKS } from "@/constants/site-constants";
|
||||
import {
|
||||
GithubIcon,
|
||||
Link04Icon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
||||
export function GitHubContributeSection({
|
||||
title,
|
||||
|
|
@ -24,7 +27,7 @@ export function GitHubContributeSection({
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button className="w-full" size="lg">
|
||||
<OcGithubIcon className="size-4" />
|
||||
<HugeiconsIcon icon={GithubIcon} />
|
||||
Start contributing
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
@ -34,7 +37,7 @@ export function GitHubContributeSection({
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button variant="outline" className="w-full" size="lg">
|
||||
<ExternalLink className="size-4" />
|
||||
<HugeiconsIcon icon={Link04Icon} />
|
||||
Report issues
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import { Button } from "./ui/button";
|
|||
import { ArrowRight } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { ThemeToggle } from "./theme-toggle";
|
||||
import { OcGithubIcon, OcMenuIcon } from "@opencut/ui/icons";
|
||||
import { GithubIcon, Menu02Icon } from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { cn } from "@/utils/ui";
|
||||
import { DEFAULT_LOGO_URL, SOCIAL_LINKS } from "@/constants/site-constants";
|
||||
|
||||
|
|
@ -62,13 +63,13 @@ export function Header() {
|
|||
className="flex items-center justify-center p-0"
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
>
|
||||
<OcMenuIcon size={30} />
|
||||
<HugeiconsIcon icon={Menu02Icon} size={30} />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="hidden items-center gap-3 md:flex">
|
||||
<Link href={SOCIAL_LINKS.github}>
|
||||
<Button className="bg-background text-sm" variant="outline">
|
||||
<OcGithubIcon className="size-4" />
|
||||
<HugeiconsIcon icon={GithubIcon} className="size-4" />
|
||||
31k+
|
||||
</Button>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "./ui/button";
|
||||
import { Sun } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { cn } from "@/utils/ui";
|
||||
import { Sun03Icon } from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
||||
interface ThemeToggleProps {
|
||||
className?: string;
|
||||
|
|
@ -28,7 +29,7 @@ export function ThemeToggle({
|
|||
onToggle?.(e);
|
||||
}}
|
||||
>
|
||||
<Sun className={cn("!size-[1.1rem]", iconClassName)} />
|
||||
<HugeiconsIcon icon={Sun03Icon} className={cn("!size-[1.1rem]", iconClassName)} />
|
||||
<span className="sr-only">{theme === "dark" ? "Light" : "Dark"}</span>
|
||||
</Button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ const ColorPicker = forwardRef<HTMLDivElement, ColorPickerProps>(
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="relative flex-1">
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { HugeiconsIcon, type HugeiconsIconProps } from "@hugeicons/react";
|
||||
import { Loading03Icon } from "@hugeicons/core-free-icons";
|
||||
import { cn } from "@/utils/ui";
|
||||
|
||||
function Spinner({ className, ...props }: Omit<HugeiconsIconProps, "icon">) {
|
||||
return (
|
||||
<HugeiconsIcon
|
||||
icon={Loading03Icon}
|
||||
role="status"
|
||||
aria-label="Loading"
|
||||
className={cn("size-4 animate-spin", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Spinner }
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
import type { TrackType } from "@/types/timeline";
|
||||
import { Video, TypeIcon, Music, Sticker } from "lucide-react";
|
||||
import {
|
||||
Happy01Icon,
|
||||
MusicNote03Icon,
|
||||
TextIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { OcVideoIcon } from "@opencut/ui/icons";
|
||||
|
||||
export const TRACK_COLORS: Record<TrackType, { background: string }> = {
|
||||
video: {
|
||||
|
|
@ -37,8 +43,23 @@ export const TIMELINE_CONSTANTS = {
|
|||
} as const;
|
||||
|
||||
export const TRACK_ICONS: Record<TrackType, React.ReactNode> = {
|
||||
video: <Video className="text-muted-foreground size-4 shrink-0" />,
|
||||
text: <TypeIcon className="text-muted-foreground size-4 shrink-0" />,
|
||||
audio: <Music className="text-muted-foreground size-4 shrink-0" />,
|
||||
sticker: <Sticker className="text-muted-foreground size-4 shrink-0" />,
|
||||
video: <OcVideoIcon className="text-muted-foreground size-4 shrink-0" />,
|
||||
text: (
|
||||
<HugeiconsIcon
|
||||
icon={TextIcon}
|
||||
className="text-muted-foreground size-4 shrink-0"
|
||||
/>
|
||||
),
|
||||
audio: (
|
||||
<HugeiconsIcon
|
||||
icon={MusicNote03Icon}
|
||||
className="text-muted-foreground size-4 shrink-0"
|
||||
/>
|
||||
),
|
||||
sticker: (
|
||||
<HugeiconsIcon
|
||||
icon={Happy01Icon}
|
||||
className="text-muted-foreground size-4 shrink-0"
|
||||
/>
|
||||
),
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -510,13 +510,9 @@ export function useElementInteraction({
|
|||
element: TimelineElement;
|
||||
track: TimelineTrack;
|
||||
}) => {
|
||||
event.stopPropagation();
|
||||
mouseDownLocationRef.current = { x: event.clientX, y: event.clientY };
|
||||
|
||||
const isRightClick = event.button === 2;
|
||||
const isMultiSelect = event.metaKey || event.ctrlKey || event.shiftKey;
|
||||
|
||||
// right-click
|
||||
// right-click: don't stop propagation so ContextMenu can open
|
||||
if (isRightClick) {
|
||||
const alreadySelected = isElementSelected({
|
||||
trackId: track.id,
|
||||
|
|
@ -532,6 +528,12 @@ export function useElementInteraction({
|
|||
return;
|
||||
}
|
||||
|
||||
// left-click: stop propagation for drag operations
|
||||
event.stopPropagation();
|
||||
mouseDownLocationRef.current = { x: event.clientX, y: event.clientY };
|
||||
|
||||
const isMultiSelect = event.metaKey || event.ctrlKey || event.shiftKey;
|
||||
|
||||
// multi-select: toggle selection
|
||||
if (isMultiSelect) {
|
||||
handleSelectionClick({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||
import { getCumulativeHeightBefore, getTrackHeight } from "@/lib/timeline";
|
||||
import { useEditor } from "../use-editor";
|
||||
|
|
@ -98,6 +98,7 @@ export function useSelectionBox({
|
|||
const [selectionBox, setSelectionBox] = useState<SelectionBoxState | null>(
|
||||
null,
|
||||
);
|
||||
const justFinishedSelectingRef = useRef(false);
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
({ clientX, clientY }: React.MouseEvent) => {
|
||||
|
|
@ -195,6 +196,12 @@ export function useSelectionBox({
|
|||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (selectionBox?.isActive) {
|
||||
justFinishedSelectingRef.current = true;
|
||||
requestAnimationFrame(() => {
|
||||
justFinishedSelectingRef.current = false;
|
||||
});
|
||||
}
|
||||
setSelectionBox(null);
|
||||
};
|
||||
|
||||
|
|
@ -223,9 +230,14 @@ export function useSelectionBox({
|
|||
};
|
||||
}, [selectionBox, containerRef]);
|
||||
|
||||
const shouldIgnoreClick = useCallback(() => {
|
||||
return justFinishedSelectingRef.current;
|
||||
}, []);
|
||||
|
||||
return {
|
||||
selectionBox,
|
||||
handleMouseDown,
|
||||
isSelecting: selectionBox?.isActive || false,
|
||||
shouldIgnoreClick,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import {
|
||||
CaptionsIcon,
|
||||
ArrowLeftRightIcon,
|
||||
SparklesIcon,
|
||||
StickerIcon,
|
||||
MusicIcon,
|
||||
VideoIcon,
|
||||
BlendIcon,
|
||||
SlidersHorizontalIcon,
|
||||
type LucideIcon,
|
||||
TypeIcon,
|
||||
SettingsIcon,
|
||||
} from "lucide-react";
|
||||
import type { ElementType } from "react";
|
||||
import { create } from "zustand";
|
||||
import {
|
||||
ArrowRightDoubleIcon,
|
||||
ClosedCaptionIcon,
|
||||
Folder03Icon,
|
||||
Happy01Icon,
|
||||
HeadphonesIcon,
|
||||
MagicWand05Icon,
|
||||
TextIcon,
|
||||
Settings01Icon,
|
||||
SlidersHorizontalIcon,
|
||||
ColorsIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/react";
|
||||
|
||||
export const TAB_KEYS = [
|
||||
"media",
|
||||
|
|
@ -28,48 +29,57 @@ export const TAB_KEYS = [
|
|||
|
||||
export type Tab = (typeof TAB_KEYS)[number];
|
||||
|
||||
const createHugeiconsIcon =
|
||||
({ icon }: { icon: IconSvgElement }) =>
|
||||
({ className }: { className?: string }) => (
|
||||
<HugeiconsIcon icon={icon} className={className} />
|
||||
);
|
||||
|
||||
export const tabs = {
|
||||
media: {
|
||||
icon: VideoIcon,
|
||||
icon: createHugeiconsIcon({ icon: Folder03Icon }),
|
||||
label: "Media",
|
||||
},
|
||||
sounds: {
|
||||
icon: MusicIcon,
|
||||
icon: createHugeiconsIcon({ icon: HeadphonesIcon }),
|
||||
label: "Sounds",
|
||||
},
|
||||
text: {
|
||||
icon: TypeIcon,
|
||||
icon: createHugeiconsIcon({ icon: TextIcon }),
|
||||
label: "Text",
|
||||
},
|
||||
stickers: {
|
||||
icon: StickerIcon,
|
||||
icon: createHugeiconsIcon({ icon: Happy01Icon }),
|
||||
label: "Stickers",
|
||||
},
|
||||
effects: {
|
||||
icon: SparklesIcon,
|
||||
icon: createHugeiconsIcon({ icon: MagicWand05Icon }),
|
||||
label: "Effects",
|
||||
},
|
||||
transitions: {
|
||||
icon: ArrowLeftRightIcon,
|
||||
icon: createHugeiconsIcon({ icon: ArrowRightDoubleIcon }),
|
||||
label: "Transitions",
|
||||
},
|
||||
captions: {
|
||||
icon: CaptionsIcon,
|
||||
icon: createHugeiconsIcon({ icon: ClosedCaptionIcon }),
|
||||
label: "Captions",
|
||||
},
|
||||
filters: {
|
||||
icon: BlendIcon,
|
||||
icon: createHugeiconsIcon({ icon: ColorsIcon }),
|
||||
label: "Filters",
|
||||
},
|
||||
adjustment: {
|
||||
icon: SlidersHorizontalIcon,
|
||||
icon: createHugeiconsIcon({ icon: SlidersHorizontalIcon }),
|
||||
label: "Adjustment",
|
||||
},
|
||||
settings: {
|
||||
icon: SettingsIcon,
|
||||
icon: createHugeiconsIcon({ icon: Settings01Icon }),
|
||||
label: "Settings",
|
||||
},
|
||||
} satisfies Record<Tab, { icon: LucideIcon; label: string }>;
|
||||
} satisfies Record<
|
||||
Tab,
|
||||
{ icon: ElementType<{ className?: string }>; label: string }
|
||||
>;
|
||||
|
||||
type MediaViewMode = "grid" | "list";
|
||||
|
||||
6
bun.lock
6
bun.lock
|
|
@ -21,6 +21,8 @@
|
|||
"@ffmpeg/util": "^0.12.2",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@hugeicons/core-free-icons": "^3.1.1",
|
||||
"@hugeicons/react": "^1.1.4",
|
||||
"@huggingface/transformers": "^3.8.1",
|
||||
"@opencut/env": "workspace:*",
|
||||
"@opencut/ui": "workspace:*",
|
||||
|
|
@ -218,6 +220,10 @@
|
|||
|
||||
"@hookform/resolvers": ["@hookform/resolvers@3.10.0", "", { "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag=="],
|
||||
|
||||
"@hugeicons/core-free-icons": ["@hugeicons/core-free-icons@3.1.1", "", {}, "sha512-UpS2lUQFi5sKyJSWwM6rO+BnPLvVz1gsyCpPHeZyVuZqi89YH8ksliza4cwaODqKOZyeXmG8juo1ty4QtQofkg=="],
|
||||
|
||||
"@hugeicons/react": ["@hugeicons/react@1.1.4", "", { "peerDependencies": { "react": ">=16.0.0" } }, "sha512-gsc3eZyd2fGqRUThW9+lfjxxsOkz6KNVmRXRgJjP32GL0OnnLJnl3hytKt47CBbiQj2xE2kCw+rnP3UQCThcKw=="],
|
||||
|
||||
"@huggingface/jinja": ["@huggingface/jinja@0.5.3", "", {}, "sha512-asqfZ4GQS0hD876Uw4qiUb7Tr/V5Q+JZuo2L+BtdrD4U40QU58nIRq3ZSgAzJgT874VLjhGVacaYfrdpXtEvtA=="],
|
||||
|
||||
"@huggingface/transformers": ["@huggingface/transformers@3.8.1", "", { "dependencies": { "@huggingface/jinja": "^0.5.3", "onnxruntime-node": "1.21.0", "onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4", "sharp": "^0.34.1" } }, "sha512-tsTk4zVjImqdqjS8/AOZg2yNLd1z9S5v+7oUPpXaasDRwEDhB+xnglK1k5cad26lL5/ZIaeREgWWy0bs9y9pPA=="],
|
||||
|
|
|
|||
|
|
@ -1,45 +1,3 @@
|
|||
export function OcGoogleIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 24 24">
|
||||
<title>Google</title>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
/>
|
||||
<path
|
||||
fill="#EA4335"
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcGithubIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
viewBox="0 -3.5 256 256"
|
||||
preserveAspectRatio="xMinYMin meet"
|
||||
>
|
||||
<title>GitHub</title>
|
||||
<g fill="currentColor">
|
||||
<path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0" />
|
||||
|
||||
<path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcVercelIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -1,165 +0,0 @@
|
|||
export function OcBackgroundIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="353"
|
||||
height="353"
|
||||
viewBox="0 0 353 353"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<title>Background</title>
|
||||
<g clipPath="url(#clip0_1_3)">
|
||||
<rect
|
||||
x="-241.816"
|
||||
y="233.387"
|
||||
width="592.187"
|
||||
height="17.765"
|
||||
transform="rotate(-37 -241.816 233.387)"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="-189.907"
|
||||
y="306.804"
|
||||
width="592.187"
|
||||
height="17.765"
|
||||
transform="rotate(-37 -189.907 306.804)"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="-146.928"
|
||||
y="389.501"
|
||||
width="592.187"
|
||||
height="17.765"
|
||||
transform="rotate(-37 -146.928 389.501)"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="-103.144"
|
||||
y="477.904"
|
||||
width="592.187"
|
||||
height="17.765"
|
||||
transform="rotate(-37 -103.144 477.904)"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="-57.169"
|
||||
y="570.714"
|
||||
width="592.187"
|
||||
height="17.765"
|
||||
transform="rotate(-37 -57.169 570.714)"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_3">
|
||||
<rect width="353" height="353" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcSocialsIcon({
|
||||
className = "",
|
||||
size = 32,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 345 243"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<title>Socials</title>
|
||||
<g opacity="0.5">
|
||||
<path
|
||||
d="M203.75 4H39.25C19.782 4 4 19.782 4 39.25V203.75C4 223.218 19.782 239 39.25 239H203.75C223.218 239 239 223.218 239 203.75V39.25C239 19.782 223.218 4 203.75 4Z"
|
||||
fill="#FFFC00"
|
||||
/>
|
||||
<path
|
||||
d="M97.1738 194.02C88.9121 188.053 82.4863 184.84 66.8809 187.594C64.5859 188.053 60.4551 189.43 59.9961 185.299C59.0781 182.086 59.0781 177.037 56.7832 176.578C42.5547 174.742 37.5059 171.988 35.2109 169.234C34.293 168.316 33.834 166.021 35.6699 165.562C59.9961 160.973 71.4707 137.564 73.7656 132.975C76.5195 126.09 71.9297 121.959 63.209 119.205C59.0781 117.828 52.1934 115.992 52.1934 111.402C52.1934 109.107 54.4883 107.73 56.7832 106.812C58.6191 106.354 60.4551 105.895 62.291 106.812C67.7988 109.107 72.8477 110.025 75.6016 107.73C75.6016 95.3379 72.3887 79.7324 77.4375 66.8809C83.4043 52.6523 98.0918 39.8008 121.5 39.8008C144.908 39.8008 159.596 52.6523 165.562 66.8809C170.611 79.7324 167.398 95.3379 167.398 107.73C170.152 110.025 175.201 109.107 180.709 106.812C182.545 105.895 184.381 106.354 186.217 106.812C188.512 107.73 190.807 109.107 190.807 111.402C190.807 115.992 183.922 117.828 179.791 119.205C171.07 121.959 166.48 126.09 169.234 132.975C171.529 137.564 183.004 160.973 207.33 165.562C209.166 166.021 208.707 168.316 207.789 169.234C205.494 171.988 200.445 174.742 186.217 176.578C183.922 177.037 183.922 182.086 183.004 185.299C182.545 189.43 178.414 188.053 176.119 187.594C160.514 184.84 154.088 188.053 145.826 194.02C139.065 199.872 130.442 203.126 121.5 203.199C111.861 203.658 104.059 199.527 97.1738 194.02Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M203.75 4H39.25C19.782 4 4 19.782 4 39.25V203.75C4 223.218 19.782 239 39.25 239H203.75C223.218 239 239 223.218 239 203.75V39.25C239 19.782 223.218 4 203.75 4Z"
|
||||
stroke="black"
|
||||
strokeWidth="7"
|
||||
/>
|
||||
<path
|
||||
d="M97.1738 194.02C88.9121 188.053 82.4863 184.84 66.8809 187.594C64.5859 188.053 60.4551 189.43 59.9961 185.299C59.0781 182.086 59.0781 177.037 56.7832 176.578C42.5547 174.742 37.5059 171.988 35.2109 169.234C34.293 168.316 33.834 166.021 35.6699 165.562C59.9961 160.973 71.4707 137.564 73.7656 132.975C76.5195 126.09 71.9297 121.959 63.209 119.205C59.0781 117.828 52.1934 115.992 52.1934 111.402C52.1934 109.107 54.4883 107.73 56.7832 106.812C58.6191 106.354 60.4551 105.895 62.291 106.812C67.7988 109.107 72.8477 110.025 75.6016 107.73C75.6016 95.3379 72.3887 79.7324 77.4375 66.8809C83.4043 52.6523 98.0918 39.8008 121.5 39.8008C144.908 39.8008 159.596 52.6523 165.562 66.8809C170.611 79.7324 167.398 95.3379 167.398 107.73C170.152 110.025 175.201 109.107 180.709 106.812C182.545 105.895 184.381 106.354 186.217 106.812C188.512 107.73 190.807 109.107 190.807 111.402C190.807 115.992 183.922 117.828 179.791 119.205C171.07 121.959 166.48 126.09 169.234 132.975C171.529 137.564 183.004 160.973 207.33 165.562C209.166 166.021 208.707 168.316 207.789 169.234C205.494 171.988 200.445 174.742 186.217 176.578C183.922 177.037 183.922 182.086 183.004 185.299C182.545 189.43 178.414 188.053 176.119 187.594C160.514 184.84 154.088 188.053 145.826 194.02C139.065 199.872 130.442 203.126 121.5 203.199C111.861 203.658 104.059 199.527 97.1738 194.02Z"
|
||||
stroke="black"
|
||||
strokeWidth="7"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M133.5 4H321.5C334.48 4 345 14.5205 345 27.5V215.5C345 228.48 334.48 239 321.5 239H133.5C120.52 239 110 228.48 110 215.5V27.5C110 14.5205 120.52 4 133.5 4Z"
|
||||
fill="#010101"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M261.497 99.4086C271.784 106.758 284.388 111.083 297.999 111.083V84.9035C295.423 84.9045 292.854 84.6357 290.333 84.1016V104.709C276.723 104.709 264.121 100.384 253.831 93.0345V146.459C253.831 173.185 232.155 194.85 205.417 194.85C195.44 194.85 186.167 191.835 178.464 186.665C187.256 195.65 199.516 201.224 213.08 201.224C239.821 201.224 261.498 179.56 261.498 152.833L261.497 99.4086ZM270.953 72.9965C265.696 67.2559 262.244 59.8365 261.497 51.634V48.267H254.233C256.061 58.6916 262.298 67.5981 270.953 72.9965ZM195.376 166.157C192.438 162.308 190.851 157.598 190.858 152.756C190.858 140.533 200.773 130.622 213.005 130.622C215.285 130.621 217.551 130.97 219.723 131.659V104.894C217.185 104.547 214.622 104.399 212.061 104.454V125.286C209.887 124.597 207.62 124.247 205.34 124.249C193.107 124.249 183.193 134.159 183.193 146.384C183.193 155.027 188.149 162.512 195.376 166.157Z"
|
||||
fill="#EE1D52"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M253.831 93.0345C264.121 100.384 276.723 104.709 290.333 104.709V84.1016C282.736 82.4848 276.011 78.5162 270.953 72.9965C262.298 67.5981 256.061 58.6916 254.233 48.267H235.152V152.832C235.108 165.022 225.21 174.892 213.004 174.892C205.811 174.892 199.422 171.465 195.376 166.157C188.149 162.512 183.193 155.027 183.193 146.384C183.193 134.159 193.107 124.249 205.34 124.249C207.683 124.249 209.942 124.614 212.061 125.286V104.454C185.792 104.996 164.665 126.449 164.665 152.833C164.665 166.003 169.926 177.942 178.464 186.665C186.167 191.835 195.44 194.85 205.417 194.85C232.155 194.85 253.831 173.185 253.831 146.459V93.0345Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M290.333 84.1016V78.5293C283.482 78.5396 276.766 76.6229 270.953 72.9965C276.099 78.6269 282.874 82.5087 290.333 84.1016ZM254.233 48.267C254.058 47.2708 253.924 46.2679 253.831 45.2608V41.8938H227.485V146.459C227.443 158.648 217.545 168.518 205.339 168.518C201.754 168.518 198.372 167.669 195.376 166.157C199.422 171.465 205.811 174.892 213.004 174.892C225.21 174.892 235.108 165.022 235.152 152.832V48.267H254.233ZM212.061 104.454L212.061 98.5212C209.859 98.2202 207.64 98.0698 205.418 98.071C178.676 98.071 157 119.736 157 146.459C157 163.214 165.518 177.979 178.464 186.665C169.926 177.942 164.666 166.002 164.666 152.832C164.666 126.449 185.791 104.996 212.061 104.454Z"
|
||||
fill="#69C9D0"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcTransitionUpIcon({
|
||||
className = "",
|
||||
size = 16,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
className={className}
|
||||
>
|
||||
<title>Transition Up</title>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M2.5 12.6667C2.5 13.4951 3.17157 14.1667 4 14.1667H12C12.8284 14.1667 13.5 13.4951 13.5 12.6667V11.3333C13.5 10.5049 12.8284 9.83333 12 9.83333H4C3.17157 9.83333 2.5 10.5049 2.5 11.3333V12.6667ZM4 15.1667C2.61929 15.1667 1.5 14.0474 1.5 12.6667V11.3333C1.5 9.95262 2.61929 8.83333 4 8.83333H12C13.3807 8.83333 14.5 9.95262 14.5 11.3333V12.6667C14.5 14.0474 13.3807 15.1667 12 15.1667H4Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M2 5.83333C1.72386 5.83333 1.5 5.60947 1.5 5.33333L1.5 4C1.5 2.2511 2.91777 0.833332 4.66667 0.833332L11.3333 0.833332C13.0822 0.833332 14.5 2.2511 14.5 4V5.33333C14.5 5.60947 14.2761 5.83333 14 5.83333C13.7239 5.83333 13.5 5.60947 13.5 5.33333V4C13.5 2.80338 12.53 1.83333 11.3333 1.83333L4.66667 1.83333C3.47005 1.83333 2.5 2.80338 2.5 4V5.33333C2.5 5.60947 2.27614 5.83333 2 5.83333Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M8.35355 3.64645C8.15829 3.45118 7.84171 3.45118 7.64645 3.64645L5.64645 5.64645C5.45118 5.84171 5.45118 6.15829 5.64645 6.35355C5.84171 6.54882 6.15829 6.54882 6.35355 6.35355L7.5 5.20711L7.5 9.33333C7.5 9.60948 7.72386 9.83333 8 9.83333C8.27614 9.83333 8.5 9.60948 8.5 9.33333V5.20711L9.64645 6.35355C9.84171 6.54882 10.1583 6.54882 10.3536 6.35355C10.5488 6.15829 10.5488 5.84171 10.3536 5.64645L8.35355 3.64645Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
export * from "./brand";
|
||||
export * from "./editor";
|
||||
export * from "./ui";
|
||||
|
|
|
|||
|
|
@ -1,108 +1,32 @@
|
|||
export function OcMenuIcon({
|
||||
export function OcVideoIcon({
|
||||
className = "",
|
||||
size = 16,
|
||||
size = 32,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 141 111"
|
||||
fill="none"
|
||||
className={className}
|
||||
viewBox="0 0 29 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Menu</title>
|
||||
<rect y="19.5" width="141" height="12" rx="7.5" fill="currentColor" />
|
||||
<rect y="67.5" width="141" height="12" rx="7.5" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcPencilIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={className}
|
||||
>
|
||||
<title>Edit</title>
|
||||
<path d="M14.0737 3.88545C14.8189 3.07808 15.1915 2.6744 15.5874 2.43893C16.5427 1.87076 17.7191 1.85309 18.6904 2.39232C19.0929 2.6158 19.4769 3.00812 20.245 3.79276C21.0131 4.5774 21.3972 4.96972 21.6159 5.38093C22.1438 6.37312 22.1265 7.57479 21.5703 8.5507C21.3398 8.95516 20.9446 9.33578 20.1543 10.097L10.7506 19.1543C9.25288 20.5969 8.504 21.3182 7.56806 21.6837C6.63212 22.0493 5.6032 22.0224 3.54536 21.9686L3.26538 21.9613C2.63891 21.9449 2.32567 21.9367 2.14359 21.73C1.9615 21.5234 1.98636 21.2043 2.03608 20.5662L2.06308 20.2197C2.20301 18.4235 2.27297 17.5255 2.62371 16.7182C2.97444 15.9109 3.57944 15.2555 4.78943 13.9445L14.0737 3.88545Z" />
|
||||
<path d="M13 4L20 11" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcLeftArrowIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={className}
|
||||
>
|
||||
<title>Left Arrow</title>
|
||||
<path d="M5.5 12.002H19" />
|
||||
<path d="M10.9999 18.002C10.9999 18.002 4.99998 13.583 4.99997 12.0019C4.99996 10.4208 11 6.00195 11 6.00195" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function OcTrashIcon({
|
||||
className = "",
|
||||
size = 24,
|
||||
}: {
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
color="currentColor"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={className}
|
||||
>
|
||||
<title>Trash</title>
|
||||
<path d="M19.5 5.5L18.8803 15.5251C18.7219 18.0864 18.6428 19.3671 18.0008 20.2879C17.6833 20.7431 17.2747 21.1273 16.8007 21.416C15.8421 22 14.559 22 11.9927 22C9.42312 22 8.1383 22 7.17905 21.4149C6.7048 21.1257 6.296 20.7408 5.97868 20.2848C5.33688 19.3626 5.25945 18.0801 5.10461 15.5152L4.5 5.5" />
|
||||
<path d="M3 5.5H21M16.0557 5.5L15.3731 4.09173C14.9196 3.15626 14.6928 2.68852 14.3017 2.39681C14.215 2.3321 14.1231 2.27454 14.027 2.2247C13.5939 2 13.0741 2 12.0345 2C10.9688 2 10.436 2 9.99568 2.23412C9.8981 2.28601 9.80498 2.3459 9.71729 2.41317C9.32164 2.7167 9.10063 3.20155 8.65861 4.17126L8.05292 5.5" />
|
||||
<path d="M9.5 16.5L9.5 10.5" />
|
||||
<path d="M14.5 16.5L14.5 10.5" />
|
||||
<title>Video</title>
|
||||
<path
|
||||
d="M2.41667 11C2.41667 7.70017 2.41667 6.05025 3.65537 5.02513C4.89405 4 6.88771 4 10.875 4H12.0833C16.0706 4 18.0642 4 19.303 5.02513C20.5417 6.05025 20.5417 7.70017 20.5417 11V13C20.5417 16.2998 20.5417 17.9497 19.303 18.9749C18.0642 20 16.0706 20 12.0833 20H10.875C6.88771 20 4.89405 20 3.65537 18.9749C2.41667 17.9497 2.41667 16.2998 2.41667 13V11Z"
|
||||
stroke="#141B34"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M20.5417 8.90585L20.6938 8.80196C23.2504 7.05623 24.5287 6.18336 25.556 6.60482C26.5833 7.02628 26.5833 8.42355 26.5833 11.2181V12.7819C26.5833 15.5765 26.5833 16.9737 25.556 17.3952C24.5287 17.8166 23.2504 16.9438 20.6938 15.198L20.5417 15.0941"
|
||||
stroke="#141B34"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue