summaryrefslogtreecommitdiff
path: root/apps/web/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src')
-rw-r--r--apps/web/src/app/api/feedback/route.ts31
-rw-r--r--apps/web/src/app/page.tsx26
-rw-r--r--apps/web/src/components/editor/onboarding.tsx5
-rw-r--r--apps/web/src/components/footer.tsx53
-rw-r--r--apps/web/src/components/header.tsx6
-rw-r--r--apps/web/src/components/landing/handlebars.tsx203
-rw-r--r--apps/web/src/components/landing/hero.tsx41
-rw-r--r--apps/web/src/feedback/components/feedback-popover.tsx242
-rw-r--r--apps/web/src/feedback/index.ts3
-rw-r--r--apps/web/src/feedback/queries.ts14
-rw-r--r--apps/web/src/feedback/types.ts11
-rw-r--r--apps/web/src/site/social.ts1
12 files changed, 33 insertions, 603 deletions
diff --git a/apps/web/src/app/api/feedback/route.ts b/apps/web/src/app/api/feedback/route.ts
deleted file mode 100644
index cd853eea..00000000
--- a/apps/web/src/app/api/feedback/route.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { type NextRequest, NextResponse } from "next/server";
-import { z } from "zod";
-import { checkRateLimit } from "@/auth/rate-limit";
-import { submitFeedback, MAX_MESSAGE_LENGTH } from "@/feedback";
-
-const submitSchema = z.object({
- message: z
- .string()
- .min(1, "Message is required")
- .max(MAX_MESSAGE_LENGTH, "Message too long"),
-});
-
-export async function POST(request: NextRequest) {
- const { limited } = await checkRateLimit({ request });
- if (limited) {
- return NextResponse.json({ error: "Too many requests" }, { status: 429 });
- }
-
- const body = await request.json();
- const result = submitSchema.safeParse(body);
-
- if (!result.success) {
- return NextResponse.json(
- { error: "Invalid input", details: result.error.flatten().fieldErrors },
- { status: 400 },
- );
- }
-
- const entry = await submitFeedback(result.data);
- return NextResponse.json({ entry }, { status: 201 });
-}
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 6084a912..287775c5 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -1,21 +1,5 @@
-import { Hero } from "@/components/landing/hero";
-import { Header } from "@/components/header";
-import { Footer } from "@/components/footer";
-import type { Metadata } from "next";
-import { SITE_URL } from "@/site/brand";
-
-export const metadata: Metadata = {
- alternates: {
- canonical: SITE_URL,
- },
-};
-
-export default async function Home() {
- return (
- <div>
- <Header />
- <Hero />
- <Footer />
- </div>
- );
-}
+import { redirect } from "next/navigation";
+
+export default function Home() {
+ redirect("/projects");
+}
diff --git a/apps/web/src/components/editor/onboarding.tsx b/apps/web/src/components/editor/onboarding.tsx
index bc5533ce..08ef3318 100644
--- a/apps/web/src/components/editor/onboarding.tsx
+++ b/apps/web/src/components/editor/onboarding.tsx
@@ -3,7 +3,6 @@
import { ArrowRightIcon } from "lucide-react";
import { useState } from "react";
import ReactMarkdown from "react-markdown";
-import { SOCIAL_LINKS } from "@/site/social";
import { useLocalStorage } from "@/services/storage/use-local-storage";
import { Button } from "../ui/button";
import { Dialog, DialogBody, DialogContent, DialogTitle } from "../ui/dialog";
@@ -67,9 +66,7 @@ export function Onboarding() {
<div className="space-y-5">
<div className="space-y-3">
<Title title={getStepTitle()} />
- <Description
- description={`Join our [Discord](${SOCIAL_LINKS.discord}), chat with cool people and share feedback to help make OpenCut the best editor ever.`}
- />
+ <Description description="Have fun testing! Your projects and media stay local in your browser." />
</div>
<NextButton onClick={handleClose}>Finish</NextButton>
</div>
diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx
index b56f8c6c..e7a8fc39 100644
--- a/apps/web/src/components/footer.tsx
+++ b/apps/web/src/components/footer.tsx
@@ -1,5 +1,5 @@
import Link from "next/link";
-import { RiDiscordFill, RiTwitterXLine } from "react-icons/ri";
+import { RiTwitterXLine } from "react-icons/ri";
import { FaGithub } from "react-icons/fa6";
import Image from "next/image";
import { DEFAULT_LOGO_URL } from "@/site/brand";
@@ -13,23 +13,30 @@ interface FooterLink {
href: string;
}
-type CategoryLinks = Record<Category, FooterLink[]>;
+interface LinkCategory {
+ category: Category;
+ links: FooterLink[];
+}
-const links: CategoryLinks = {
- resources: [
- { label: "Roadmap", href: "/roadmap" },
- { label: "Changelog", href: "/changelog" },
- { label: "Blog", href: "/blog" },
- { label: "Privacy", href: "/privacy" },
- { label: "Terms of use", href: "/terms" },
- ],
- company: [
- { label: "Contributors", href: "/contributors" },
- { label: "Sponsors", href: "/sponsors" },
- { label: "Brand", href: "/brand" },
- { label: "About", href: `${SOCIAL_LINKS.github}/blob/main/README.md` },
- ],
-};
+const linkCategories: LinkCategory[] = [
+ {
+ category: "resources",
+ links: [
+ { label: "Roadmap", href: "/roadmap" },
+ { label: "Privacy", href: "/privacy" },
+ { label: "Terms of use", href: "/terms" },
+ ],
+ },
+ {
+ category: "company",
+ links: [
+ { label: "Contributors", href: "/contributors" },
+ { label: "Sponsors", href: "/sponsors" },
+ { label: "Brand", href: "/brand" },
+ { label: "About", href: `${SOCIAL_LINKS.github}/blob/main/README.md` },
+ ],
+ },
+];
export function Footer() {
return (
@@ -68,25 +75,17 @@ export function Footer() {
>
<RiTwitterXLine className="size-5" />
</Link>
- <Link
- href={SOCIAL_LINKS.discord}
- className="text-muted-foreground hover:text-foreground transition-colors"
- target="_blank"
- rel="noopener noreferrer"
- >
- <RiDiscordFill className="size-5" />
- </Link>
</div>
</div>
<div className="flex items-start justify-start gap-12 py-2">
- {(Object.keys(links) as Category[]).map((category) => (
+ {linkCategories.map(({ category, links: categoryLinks }) => (
<div key={category} className="flex flex-col gap-2">
<h3 className="text-foreground font-semibold">
{capitalizeFirstLetter({ string: category })}
</h3>
<ul className="space-y-2 text-sm">
- {links[category].map((link) => (
+ {categoryLinks.map((link) => (
<li key={link.href}>
<Link
href={link.href}
diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx
index 99ba1730..44a2864f 100644
--- a/apps/web/src/components/header.tsx
+++ b/apps/web/src/components/header.tsx
@@ -16,7 +16,7 @@ import {
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
import { cn } from "@/utils/ui";
-import { DEFAULT_LOGO_URL, SITE_URL } from "@/site/brand";
+import { DEFAULT_LOGO_URL } from "@/site/brand";
import { SOCIAL_LINKS } from "@/site/social";
import {
ContextMenu,
@@ -42,10 +42,6 @@ export function Header() {
label: "Sponsors",
href: "/sponsors",
},
- {
- label: "Blog",
- href: "/blog",
- },
];
return (
diff --git a/apps/web/src/components/landing/handlebars.tsx b/apps/web/src/components/landing/handlebars.tsx
deleted file mode 100644
index 4a81cdba..00000000
--- a/apps/web/src/components/landing/handlebars.tsx
+++ /dev/null
@@ -1,203 +0,0 @@
-"use client";
-
-import {
- type PropsWithChildren,
- useEffect,
- useLayoutEffect,
- useRef,
- useState,
-} from "react";
-
-type HandlebarsProps = PropsWithChildren;
-
-const MIN_HANDLE_SEPARATION_PX = 60;
-const MASK_GRADIENT_EDGE_PADDING_PX = 10;
-const HANDLEBARS_ROTATE_DEG = 2.76;
-const RIGHT_HANDLE_LEFT_OFFSET_PX = -30;
-
-export function Handlebars({ children }: HandlebarsProps) {
- const containerRef = useRef<HTMLDivElement>(null);
- const leftHandleRef = useRef<HTMLDivElement>(null);
- const rightHandleRef = useRef<HTMLDivElement>(null);
-
- const [width, setWidth] = useState(0);
- const [leftHandle, setLeftHandle] = useState(0);
- const [rightHandle, setRightHandle] = useState(0);
-
- const widthRef = useRef(0);
- const leftHandlePositionRef = useRef(0);
- const rightHandlePositionRef = useRef(0);
-
- const dragRef = useRef<{
- isDragging: boolean;
- side: "left" | "right" | null;
- pointerId: number | null;
- startX: number;
- initialPosition: number;
- }>({
- isDragging: false,
- side: null,
- pointerId: null,
- startX: 0,
- initialPosition: 0,
- });
-
- useLayoutEffect(() => {
- const container = containerRef.current;
- if (!container) return;
-
- const updateWidth = () => {
- const newWidth = container.offsetWidth;
- setWidth(newWidth);
- setRightHandle(newWidth);
- };
-
- const observer = new ResizeObserver(updateWidth);
- observer.observe(container);
- updateWidth();
-
- return () => observer.disconnect();
- }, []);
-
- useEffect(() => {
- widthRef.current = width;
- leftHandlePositionRef.current = leftHandle;
- rightHandlePositionRef.current = rightHandle;
- }, [leftHandle, rightHandle, width]);
-
- useEffect(() => {
- const handlePointerMove = (event: PointerEvent) => {
- const { isDragging, side, pointerId, startX, initialPosition } =
- dragRef.current;
-
- if (!isDragging) return;
- if (pointerId !== null && event.pointerId !== pointerId) return;
- if (!side) return;
-
- const deltaX = event.clientX - startX;
-
- if (side === "left") {
- const maxLeft = Math.max(
- 0,
- rightHandlePositionRef.current - MIN_HANDLE_SEPARATION_PX,
- );
- const nextLeftHandle = Math.max(
- 0,
- Math.min(maxLeft, initialPosition + deltaX),
- );
- setLeftHandle(nextLeftHandle);
- return;
- }
-
- const minRight = Math.min(
- widthRef.current,
- leftHandlePositionRef.current + MIN_HANDLE_SEPARATION_PX,
- );
- const nextRightHandle = Math.max(
- minRight,
- Math.min(widthRef.current, initialPosition + deltaX),
- );
- setRightHandle(nextRightHandle);
- };
-
- const handlePointerEnd = (event: PointerEvent) => {
- const { pointerId } = dragRef.current;
- if (pointerId !== null && event.pointerId !== pointerId) return;
-
- dragRef.current.isDragging = false;
- dragRef.current.side = null;
- dragRef.current.pointerId = null;
- };
-
- window.addEventListener("pointermove", handlePointerMove);
- window.addEventListener("pointerup", handlePointerEnd);
- window.addEventListener("pointercancel", handlePointerEnd);
-
- return () => {
- window.removeEventListener("pointermove", handlePointerMove);
- window.removeEventListener("pointerup", handlePointerEnd);
- window.removeEventListener("pointercancel", handlePointerEnd);
- };
- }, []);
-
- const hasMeasuredWidth = width > 0;
- const leftGradientPercent = hasMeasuredWidth
- ? (leftHandle / (width - MASK_GRADIENT_EDGE_PADDING_PX)) * 100
- : 0;
- const rightGradientPercent = hasMeasuredWidth
- ? (rightHandle / (width + MASK_GRADIENT_EDGE_PADDING_PX)) * 100
- : 100;
- const textMask = hasMeasuredWidth
- ? `linear-gradient(90deg,
- rgba(255, 255, 255, 0) 0%,
- rgba(255, 255, 255, 0) ${leftGradientPercent}%,
- rgba(0, 0, 0) ${leftGradientPercent}%,
- rgba(0, 0, 0) ${rightGradientPercent}%,
- rgba(255, 255, 255, 0) ${rightGradientPercent}%,
- rgba(255, 255, 255, 0) 100%)`
- : undefined;
-
- return (
- <div className="flex justify-center gap-4 leading-16">
- <div
- ref={containerRef}
- className="relative mt-0.5"
- style={{ transform: `rotate(-${HANDLEBARS_ROTATE_DEG}deg)` }}
- >
- <div className="absolute inset-0 z-10 flex size-full justify-between rounded-2xl border border-yellow-500">
- <div
- ref={leftHandleRef}
- className="bg-background absolute left-0 z-20 flex h-full w-7 cursor-ew-resize touch-none items-center justify-center rounded-full border border-yellow-500 select-none"
- style={{
- translate: `${leftHandle}px 0`,
- }}
- onPointerDown={(event) => {
- event.preventDefault();
- leftHandleRef.current?.setPointerCapture(event.pointerId);
- dragRef.current.isDragging = true;
- dragRef.current.side = "left";
- dragRef.current.pointerId = event.pointerId;
- dragRef.current.startX = event.clientX;
- dragRef.current.initialPosition = leftHandlePositionRef.current;
- }}
- >
- <div className="h-8 w-2 rounded-full bg-yellow-500" />
- </div>
-
- <div
- ref={rightHandleRef}
- className="bg-background absolute z-20 flex h-full w-7 cursor-ew-resize touch-none items-center justify-center rounded-full border border-yellow-500 select-none"
- style={{
- left: hasMeasuredWidth
- ? `${RIGHT_HANDLE_LEFT_OFFSET_PX}px`
- : undefined,
- right: hasMeasuredWidth ? undefined : "0px",
- translate: hasMeasuredWidth ? `${rightHandle}px 0` : undefined,
- }}
- onPointerDown={(event) => {
- event.preventDefault();
- rightHandleRef.current?.setPointerCapture(event.pointerId);
- dragRef.current.isDragging = true;
- dragRef.current.side = "right";
- dragRef.current.pointerId = event.pointerId;
- dragRef.current.startX = event.clientX;
- dragRef.current.initialPosition = rightHandlePositionRef.current;
- }}
- >
- <div className="h-8 w-2 rounded-full bg-yellow-500" />
- </div>
- </div>
-
- <span
- className="relative z-0 inline-flex size-full items-center justify-center rounded-2xl px-9 will-change-auto"
- style={{
- mask: textMask,
- WebkitMask: textMask,
- }}
- >
- {children}
- </span>
- </div>
- </div>
- );
-}
diff --git a/apps/web/src/components/landing/hero.tsx b/apps/web/src/components/landing/hero.tsx
deleted file mode 100644
index ac49e4be..00000000
--- a/apps/web/src/components/landing/hero.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-"use client";
-
-import { Button } from "../ui/button";
-import { ArrowRight } from "lucide-react";
-import Image from "next/image";
-import { Handlebars } from "./handlebars";
-import Link from "next/link";
-
-export function Hero() {
- return (
- <div className="flex min-h-[calc(100svh-4.5rem)] flex-col items-center justify-between px-4 text-center">
- <Image
- className="absolute top-0 left-0 -z-50 size-full object-cover opacity-85 invert dark:invert-0"
- src="/landing-page-dark.png"
- height={1903.5}
- width={1269}
- alt="OpenCut video editor landing page background"
- />
- <div className="mx-auto flex w-full max-w-3xl flex-1 flex-col justify-center">
- <div className="inline-block text-4xl font-bold tracking-tighter md:text-[4rem]">
- <h1>The open source</h1>
- <Handlebars>Video editor</Handlebars>
- </div>
-
- <p className="text-muted-foreground mx-auto mt-10 max-w-xl text-base font-light tracking-wide sm:text-xl">
- A simple but powerful video editor that gets the job done. Works on
- any platform.
- </p>
-
- <div className="mt-8 flex justify-center gap-8">
- <Link href="/projects">
- <Button type="submit" size="lg" className="h-11 text-base">
- Try early beta
- <ArrowRight className="ml-0.5" />
- </Button>
- </Link>
- </div>
- </div>
- </div>
- );
-}
diff --git a/apps/web/src/feedback/components/feedback-popover.tsx b/apps/web/src/feedback/components/feedback-popover.tsx
deleted file mode 100644
index f94b7253..00000000
--- a/apps/web/src/feedback/components/feedback-popover.tsx
+++ /dev/null
@@ -1,242 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { useForm } from "react-hook-form";
-import { toast } from "sonner";
-import { ClockIcon } from "lucide-react";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Button } from "@/components/ui/button";
-import { Textarea } from "@/components/ui/textarea";
-import { Spinner } from "@/components/ui/spinner";
-import {
- Form,
- FormField,
- FormItem,
- FormControl,
- clearFormDraft,
-} from "@/components/ui/form";
-import type { FeedbackEntry } from "../types";
-
-const PERSIST_KEY = "feedback-draft";
-const HISTORY_KEY = "feedback-history";
-const MAX_HISTORY = 20;
-
-interface FeedbackFormValues {
- message: string;
-}
-
-function readHistory(): FeedbackEntry[] {
- try {
- const stored = localStorage.getItem(HISTORY_KEY);
- return stored ? (JSON.parse(stored) as FeedbackEntry[]) : [];
- } catch {
- return [];
- }
-}
-
-function writeHistory({ entries }: { entries: FeedbackEntry[] }): void {
- try {
- localStorage.setItem(HISTORY_KEY, JSON.stringify(entries));
- } catch {
- // localStorage may be full or unavailable
- }
-}
-
-function useFeedback() {
- const [entries, setEntries] = useState<FeedbackEntry[]>(readHistory);
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- async function submit({
- values,
- onSuccess,
- }: {
- values: FeedbackFormValues;
- onSuccess: () => void;
- }) {
- if (isSubmitting) return;
- setIsSubmitting(true);
-
- try {
- const res = await fetch("/api/feedback", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(values),
- });
-
- if (!res.ok) {
- const data = await res.json().catch(() => null);
- throw new Error(data?.error ?? "Failed to submit");
- }
-
- const { entry } = await res.json();
- const next = [entry, ...entries].slice(0, MAX_HISTORY);
- setEntries(next);
- writeHistory({ entries: next });
- onSuccess();
- toast.success("Feedback sent");
- } catch (error) {
- toast.error(
- error instanceof Error ? error.message : "Failed to send feedback",
- );
- } finally {
- setIsSubmitting(false);
- }
- }
-
- return { entries, isSubmitting, submit };
-}
-
-export function FeedbackPopover() {
- const [open, setOpen] = useState(false);
-
- return (
- <Popover open={open} onOpenChange={setOpen}>
- <PopoverTrigger asChild>
- <Button variant="outline" className="h-8">
- Send feedback
- </Button>
- </PopoverTrigger>
- <PopoverContent align="end" className="w-80 p-0">
- <FeedbackPopoverContent onClose={() => setOpen(false)} />
- </PopoverContent>
- </Popover>
- );
-}
-
-type View = "compose" | "history";
-
-function FeedbackPopoverContent({ onClose }: { onClose: () => void }) {
- const { entries, isSubmitting, submit } = useFeedback();
- const [view, setView] = useState<View>("compose");
-
- const form = useForm<FeedbackFormValues>({
- defaultValues: { message: "" },
- });
-
- async function handleSubmit(values: FeedbackFormValues) {
- await submit({
- values,
- onSuccess: () => {
- form.reset({ message: "" });
- clearFormDraft({ key: PERSIST_KEY });
- onClose();
- },
- });
- }
-
- if (view === "history") {
- return (
- <div className="flex flex-col">
- <div
- className="max-h-72 overflow-y-auto divide-y"
- style={{
- maskImage:
- "linear-gradient(to bottom, black 80%, transparent 100%)",
- }}
- >
- {entries.map((entry) => (
- <FeedbackEntryItem key={entry.id} entry={entry} />
- ))}
- </div>
- <div className="border-t px-3 py-2">
- <button
- type="button"
- onClick={() => setView("compose")}
- className="text-xs text-muted-foreground/60 hover:text-muted-foreground transition-colors"
- >
- ← Back
- </button>
- </div>
- </div>
- );
- }
-
- return (
- <div className="flex flex-col">
- <Form persistKey={PERSIST_KEY} {...form}>
- <form onSubmit={form.handleSubmit(handleSubmit)} className="flex flex-col">
- <FormField
- control={form.control}
- name="message"
- render={({ field }) => (
- <FormItem>
- <FormControl>
- <Textarea
- placeholder="Thoughts, bugs, ideas..."
- className="min-h-[7rem] text-sm p-3 bg-background shadow-none border-none! resize-none"
- {...field}
- />
- </FormControl>
- </FormItem>
- )}
- />
- <div className="flex items-center justify-between border-t px-3 py-2">
- {entries.length > 0 ? (
- <button
- type="button"
- onClick={() => setView("history")}
- className="flex items-center gap-1.5 text-xs text-muted-foreground/60 hover:text-muted-foreground transition-colors"
- >
- <ClockIcon className="size-3" />
- {entries.length}
- </button>
- ) : (
- <span />
- )}
- <div className="flex gap-2">
- {!form.watch("message").trim() && (
- <Button
- type="button"
- variant="outline"
- size="sm"
- onClick={onClose}
- >
- Cancel
- </Button>
- )}
- <Button
- type="submit"
- size="sm"
- disabled={isSubmitting || !form.watch("message").trim()}
- >
- {isSubmitting ? <Spinner /> : "Send"}
- </Button>
- </div>
- </div>
- </form>
- </Form>
- </div>
- );
-}
-
-function relativeDate(iso: string): string {
- const diff = Date.now() - new Date(iso).getTime();
- const mins = Math.floor(diff / 60_000);
- if (mins < 1) return "just now";
- if (mins < 60) return `${mins}m ago`;
- const hrs = Math.floor(mins / 60);
- if (hrs < 24) return `${hrs}h ago`;
- const days = Math.floor(hrs / 24);
- if (days < 7) return `${days}d ago`;
- return new Date(iso).toLocaleDateString(undefined, {
- month: "short",
- day: "numeric",
- });
-}
-
-function FeedbackEntryItem({ entry }: { entry: FeedbackEntry }) {
- return (
- <div className="px-3 py-2.5">
- <p className="text-sm text-muted-foreground leading-snug whitespace-pre-wrap break-words">
- {entry.message}
- </p>
- <span className="mt-1 block text-[11px] text-muted-foreground/50">
- {relativeDate(entry.createdAt)}
- </span>
- </div>
- );
-}
diff --git a/apps/web/src/feedback/index.ts b/apps/web/src/feedback/index.ts
deleted file mode 100644
index dfd0743c..00000000
--- a/apps/web/src/feedback/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { submitFeedback } from "./queries";
-export { MAX_MESSAGE_LENGTH } from "./types";
-export type { FeedbackEntry, SubmitFeedbackInput } from "./types";
diff --git a/apps/web/src/feedback/queries.ts b/apps/web/src/feedback/queries.ts
deleted file mode 100644
index 121c6fe9..00000000
--- a/apps/web/src/feedback/queries.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { db, feedback } from "@/db";
-import { generateUUID } from "@/utils/id";
-import type { FeedbackEntry, SubmitFeedbackInput } from "./types";
-
-export async function submitFeedback({
- message,
-}: SubmitFeedbackInput): Promise<FeedbackEntry> {
- const id = generateUUID();
- const now = new Date();
-
- await db.insert(feedback).values({ id, message, createdAt: now });
-
- return { id, message, createdAt: now.toISOString() };
-}
diff --git a/apps/web/src/feedback/types.ts b/apps/web/src/feedback/types.ts
deleted file mode 100644
index 358b1354..00000000
--- a/apps/web/src/feedback/types.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const MAX_MESSAGE_LENGTH = 5000;
-
-export interface FeedbackEntry {
- id: string;
- message: string;
- createdAt: string;
-}
-
-export interface SubmitFeedbackInput {
- message: string;
-}
diff --git a/apps/web/src/site/social.ts b/apps/web/src/site/social.ts
index 571e45d5..365bfabe 100644
--- a/apps/web/src/site/social.ts
+++ b/apps/web/src/site/social.ts
@@ -1,5 +1,4 @@
export const SOCIAL_LINKS = {
x: "https://x.com/opencutapp",
github: "https://github.com/OpenCut-app/OpenCut",
- discord: "https://discord.com/invite/Mu3acKZvCp",
};