mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
feat: masks, properties refactor, shaders, storage migrations, and more
This commit is contained in:
@@ -5,7 +5,7 @@ import { BasePage } from "@/app/base-page";
|
||||
import Prose from "@/components/ui/prose";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { getPosts, getSinglePost, processHtmlContent } from "@/lib/blog/query";
|
||||
import type { Author, Post } from "@/types/blog";
|
||||
import type { Author, Post } from "@/lib/blog/types";
|
||||
|
||||
type PageProps = {
|
||||
params: Promise<{ slug: string }>;
|
||||
|
||||
@@ -3,7 +3,7 @@ import Link from "next/link";
|
||||
import { BasePage } from "@/app/base-page";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { getPosts } from "@/lib/blog/query";
|
||||
import type { Post } from "@/types/blog";
|
||||
import type { Post } from "@/lib/blog/types";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog - OpenCut",
|
||||
|
||||
+219
-219
@@ -8,111 +8,111 @@
|
||||
@plugin "tailwindcss-animate";
|
||||
|
||||
:root {
|
||||
--background: hsl(0, 0%, 100%);
|
||||
--foreground: hsl(0 0% 11%);
|
||||
--card: hsl(0, 0%, 100%);
|
||||
--card-foreground: hsl(0 0% 11%);
|
||||
--popover: hsl(0, 0%, 100%);
|
||||
--popover-hover: hsl(0, 0%, 96%);
|
||||
--popover-foreground: hsl(0 0% 2%);
|
||||
--primary: #009dff;
|
||||
--primary-foreground: hsl(0, 0%, 100%);
|
||||
--secondary: hsl(204, 100%, 97%);
|
||||
--secondary-border: hsl(204, 100%, 94%);
|
||||
--secondary-foreground: hsl(200, 98%, 39%);
|
||||
--muted: hsl(0 0% 85.1%);
|
||||
--muted-foreground: hsl(0 0% 50%);
|
||||
--accent: hsl(0, 0%, 96%);
|
||||
--accent-foreground: hsl(0 0% 2%);
|
||||
--destructive: hsl(0, 83%, 50%);
|
||||
--destructive-foreground: hsl(0, 0%, 100%);
|
||||
--constructive: hsl(141, 71%, 48%);
|
||||
--constructive-foreground: hsl(0, 0%, 100%);
|
||||
--border: hsl(0 0% 91%);
|
||||
--input: hsl(0, 0%, 100%);
|
||||
--ring: hsl(0, 0%, 55%);
|
||||
--chart-1: hsl(220 70% 50%);
|
||||
--chart-2: hsl(160 60% 45%);
|
||||
--chart-3: hsl(30 80% 55%);
|
||||
--chart-4: hsl(280 65% 60%);
|
||||
--chart-5: hsl(340 75% 55%);
|
||||
--sidebar-background: hsl(0 0% 96.1%);
|
||||
--sidebar-foreground: hsl(0 0% 2%);
|
||||
--sidebar-primary: hsl(0 0% 2%);
|
||||
--sidebar-primary-foreground: hsl(0 0% 91%);
|
||||
--sidebar-accent: hsl(0 0% 85.1%);
|
||||
--sidebar-accent-foreground: hsl(0 0% 2%);
|
||||
--sidebar-border: hsl(0 0% 85.1%);
|
||||
--sidebar-ring: hsl(0 0% 16.9%);
|
||||
--sidebar: hsl(0 0% 98%);
|
||||
--background: hsl(0, 0%, 100%);
|
||||
--foreground: hsl(0 0% 11%);
|
||||
--card: hsl(0, 0%, 100%);
|
||||
--card-foreground: hsl(0 0% 11%);
|
||||
--popover: hsl(0, 0%, 100%);
|
||||
--popover-hover: hsl(0, 0%, 96%);
|
||||
--popover-foreground: hsl(0 0% 2%);
|
||||
--primary: hsl(200, 90%, 52%);
|
||||
--primary-foreground: hsl(0, 0%, 100%);
|
||||
--secondary: hsl(204, 100%, 97%);
|
||||
--secondary-border: hsl(204, 100%, 94%);
|
||||
--secondary-foreground: hsl(200, 98%, 39%);
|
||||
--muted: hsl(0 0% 85.1%);
|
||||
--muted-foreground: hsl(0 0% 50%);
|
||||
--accent: hsl(0, 0%, 96%);
|
||||
--accent-foreground: hsl(0 0% 2%);
|
||||
--destructive: hsl(0, 83%, 50%);
|
||||
--destructive-foreground: hsl(0, 0%, 100%);
|
||||
--constructive: hsl(141, 71%, 48%);
|
||||
--constructive-foreground: hsl(0, 0%, 100%);
|
||||
--border: hsl(0 0% 91%);
|
||||
--input: hsl(0, 0%, 100%);
|
||||
--ring: hsl(0, 0%, 55%);
|
||||
--chart-1: hsl(220 70% 50%);
|
||||
--chart-2: hsl(160 60% 45%);
|
||||
--chart-3: hsl(30 80% 55%);
|
||||
--chart-4: hsl(280 65% 60%);
|
||||
--chart-5: hsl(340 75% 55%);
|
||||
--sidebar-background: hsl(0 0% 96.1%);
|
||||
--sidebar-foreground: hsl(0 0% 2%);
|
||||
--sidebar-primary: hsl(0 0% 2%);
|
||||
--sidebar-primary-foreground: hsl(0 0% 91%);
|
||||
--sidebar-accent: hsl(0 0% 85.1%);
|
||||
--sidebar-accent-foreground: hsl(0 0% 2%);
|
||||
--sidebar-border: hsl(0 0% 85.1%);
|
||||
--sidebar-ring: hsl(0 0% 16.9%);
|
||||
--sidebar: hsl(0 0% 98%);
|
||||
}
|
||||
|
||||
.panel {
|
||||
--background: hsl(216 13% 98%);
|
||||
--foreground: hsl(0 0% 13%);
|
||||
--card: hsl(0, 0%, 98%);
|
||||
--card-foreground: hsl(0 0% 13%);
|
||||
--primary-foreground: hsl(0, 0%, 98%);
|
||||
--secondary: hsl(204, 100%, 95%);
|
||||
--secondary-border: hsl(204, 100%, 92%);
|
||||
--secondary-foreground: hsl(200, 98%, 37%);
|
||||
--muted: hsl(0 0% 83.1%);
|
||||
--muted-foreground: hsl(0 0% 48%);
|
||||
--accent: hsl(0, 0%, 93%);
|
||||
--accent-foreground: hsl(0 0% 5%);
|
||||
--destructive-foreground: hsl(0, 0%, 98%);
|
||||
--constructive-foreground: hsl(0, 0%, 98%);
|
||||
--border: hsl(0 0% 89%);
|
||||
--input: hsl(0 0% 93%);
|
||||
--ring: hsl(0, 0%, 53%);
|
||||
--background: hsl(210, 20%, 98%);
|
||||
--foreground: hsl(0 0% 13%);
|
||||
--card: hsl(0, 0%, 98%);
|
||||
--card-foreground: hsl(0 0% 13%);
|
||||
--primary-foreground: hsl(0, 0%, 98%);
|
||||
--secondary: hsl(204, 100%, 95%);
|
||||
--secondary-border: hsl(204, 100%, 92%);
|
||||
--secondary-foreground: hsl(200, 98%, 37%);
|
||||
--muted: hsl(0 0% 83.1%);
|
||||
--muted-foreground: hsl(0 0% 48%);
|
||||
--accent: hsl(0, 0%, 93%);
|
||||
--accent-foreground: hsl(0 0% 5%);
|
||||
--destructive-foreground: hsl(0, 0%, 98%);
|
||||
--constructive-foreground: hsl(0, 0%, 98%);
|
||||
--border: hsl(0 0% 87%);
|
||||
--input: hsl(0 0% 93%);
|
||||
--ring: hsl(0, 0%, 53%);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: hsl(0, 0%, 5%);
|
||||
--foreground: hsl(0 0% 87%);
|
||||
--card: hsl(0, 0%, 5%);
|
||||
--card-foreground: hsl(0 0% 87%);
|
||||
--popover: hsl(0, 0%, 5%);
|
||||
--popover-hover: hsl(0, 0%, 22%);
|
||||
--popover-foreground: hsl(0 0% 95%);
|
||||
--secondary: hsl(204, 100%, 12%);
|
||||
--secondary-border: hsl(204, 100%, 15%);
|
||||
--secondary-foreground: hsl(200, 98%, 61%);
|
||||
--muted: hsl(0 0% 20%);
|
||||
--accent: hsl(0, 0%, 14%);
|
||||
--accent-foreground: hsl(0 0% 95%);
|
||||
--border: hsl(0 0% 16%);
|
||||
--input: hsl(0 0% 5%);
|
||||
--ring: hsl(0, 0%, 50%);
|
||||
--sidebar-background: hsl(0 0% 8%);
|
||||
--sidebar-foreground: hsl(0 0% 95%);
|
||||
--sidebar-primary: hsl(0 0% 95%);
|
||||
--sidebar-primary-foreground: hsl(0 0% 15%);
|
||||
--sidebar-accent: hsl(0 0% 20%);
|
||||
--sidebar-accent-foreground: hsl(0 0% 95%);
|
||||
--sidebar-border: hsl(0 0% 20%);
|
||||
--sidebar-ring: hsl(0 0% 83.1%);
|
||||
--sidebar: hsl(0 0% 6%);
|
||||
--background: hsl(0, 0%, 5%);
|
||||
--foreground: hsl(0 0% 87%);
|
||||
--card: hsl(0, 0%, 5%);
|
||||
--card-foreground: hsl(0 0% 87%);
|
||||
--popover: hsl(0, 0%, 5%);
|
||||
--popover-hover: hsl(0, 0%, 13%);
|
||||
--popover-foreground: hsl(0 0% 95%);
|
||||
--secondary: hsl(204, 100%, 12%);
|
||||
--secondary-border: hsl(204, 100%, 15%);
|
||||
--secondary-foreground: hsl(200, 98%, 61%);
|
||||
--muted: hsl(0 0% 20%);
|
||||
--accent: hsl(0, 0%, 14%);
|
||||
--accent-foreground: hsl(0 0% 95%);
|
||||
--border: hsl(0 0% 16%);
|
||||
--input: hsl(0 0% 5%);
|
||||
--ring: hsl(0, 0%, 50%);
|
||||
--sidebar-background: hsl(0 0% 8%);
|
||||
--sidebar-foreground: hsl(0 0% 95%);
|
||||
--sidebar-primary: hsl(0 0% 95%);
|
||||
--sidebar-primary-foreground: hsl(0 0% 15%);
|
||||
--sidebar-accent: hsl(0 0% 20%);
|
||||
--sidebar-accent-foreground: hsl(0 0% 95%);
|
||||
--sidebar-border: hsl(0 0% 20%);
|
||||
--sidebar-ring: hsl(0 0% 83.1%);
|
||||
--sidebar: hsl(0 0% 6%);
|
||||
}
|
||||
|
||||
.dark .panel {
|
||||
--background: hsl(0 0% 10%);
|
||||
--foreground: hsl(0 0% 85%);
|
||||
--card: hsl(0, 0%, 10%);
|
||||
--card-foreground: hsl(0 0% 85%);
|
||||
--secondary: hsl(204, 67%, 9%);
|
||||
--secondary-border: hsl(204, 100%, 14%);
|
||||
--secondary-foreground: hsl(200, 98%, 63%);
|
||||
--muted: hsl(0 0% 22%);
|
||||
--accent: hsl(0, 0%, 15%);
|
||||
--accent-foreground: hsl(0 0% 93%);
|
||||
--border: hsl(0 0% 18%);
|
||||
--input: hsl(0 0% 22%);
|
||||
--ring: hsl(0, 0%, 52%);
|
||||
--background: hsl(0 0% 10%);
|
||||
--foreground: hsl(0 0% 85%);
|
||||
--card: hsl(0, 0%, 10%);
|
||||
--card-foreground: hsl(0 0% 85%);
|
||||
--secondary: hsl(204, 67%, 9%);
|
||||
--secondary-border: hsl(204, 100%, 14%);
|
||||
--secondary-foreground: hsl(200, 98%, 63%);
|
||||
--muted: hsl(0 0% 22%);
|
||||
--accent: hsl(0, 0%, 15%);
|
||||
--accent-foreground: hsl(0 0% 93%);
|
||||
--border: hsl(0 0% 18%);
|
||||
--input: hsl(0 0% 22%);
|
||||
--ring: hsl(0, 0%, 52%);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
/*
|
||||
/*
|
||||
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
@@ -120,165 +120,165 @@
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentcolor);
|
||||
}
|
||||
/* Other default base styles */
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
/* Prevent back/forward swipe */
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
::selection {
|
||||
@apply bg-primary/35 selection:text-primary-foreground;
|
||||
}
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentcolor);
|
||||
}
|
||||
/* Other default base styles */
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
/* Prevent back/forward swipe */
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
::selection {
|
||||
@apply bg-primary/35 selection:text-primary-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
/* Responsive breakpoints */
|
||||
--breakpoint-xs: 30rem;
|
||||
/* Responsive breakpoints */
|
||||
--breakpoint-xs: 30rem;
|
||||
|
||||
/* Typography */
|
||||
--font-sans: var(--font-inter), sans-serif;
|
||||
/* Typography */
|
||||
--font-sans: var(--font-inter), sans-serif;
|
||||
|
||||
/* Font sizes */
|
||||
--text-xs: 0.72rem;
|
||||
--text-sm: 0.79rem;
|
||||
--text-base: 0.92rem;
|
||||
--text-base--line-height: calc(1.5 / 0.95);
|
||||
--text-xs--line-height: calc(1 / 0.8);
|
||||
/* Font sizes */
|
||||
--text-xs: 0.72rem;
|
||||
--text-sm: 0.79rem;
|
||||
--text-base: 0.92rem;
|
||||
--text-base--line-height: calc(1.5 / 0.95);
|
||||
--text-xs--line-height: calc(1 / 0.8);
|
||||
|
||||
/* Border radius */
|
||||
--radius-lg: 0.82rem;
|
||||
--radius-md: 0.65rem;
|
||||
--radius-sm: 0.35rem;
|
||||
/* Border radius */
|
||||
--radius-lg: 0.82rem;
|
||||
--radius-md: 0.65rem;
|
||||
--radius-sm: 0.35rem;
|
||||
|
||||
/* Palette mapped to root design tokens */
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
/* Palette mapped to root design tokens */
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-hover: var(--popover-hover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-hover: var(--popover-hover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-border: var(--secondary-border);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-border: var(--secondary-border);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
|
||||
--color-constructive: var(--constructive);
|
||||
--color-constructive-foreground: var(--constructive-foreground);
|
||||
--color-constructive: var(--constructive);
|
||||
--color-constructive-foreground: var(--constructive-foreground);
|
||||
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
|
||||
/* Chart colors */
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
/* Chart colors */
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
|
||||
/* Sidebar */
|
||||
--color-sidebar: var(--sidebar-background);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
/* Sidebar */
|
||||
--color-sidebar: var(--sidebar-background);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
|
||||
/* Animations */
|
||||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||
/* Animations */
|
||||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||
|
||||
@keyframes accordion-down {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
}
|
||||
@keyframes accordion-down {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes accordion-up {
|
||||
from {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
@keyframes accordion-up {
|
||||
from {
|
||||
height: var(--radix-accordion-content-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@utility scrollbar-hidden {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@utility scrollbar-x-hidden {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
display: none;
|
||||
}
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@utility scrollbar-y-hidden {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar:vertical {
|
||||
display: none;
|
||||
}
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar:vertical {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@utility scrollbar-thin {
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 8px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--muted-foreground);
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--muted-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,20 @@ export default function RootLayout({
|
||||
<head>
|
||||
<BotIdClient protect={protectedRoutes} />
|
||||
{process.env.NODE_ENV === "development" && (
|
||||
<Script
|
||||
src="//unpkg.com/react-scan/dist/auto.global.js"
|
||||
crossOrigin="anonymous"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
<>
|
||||
<Script
|
||||
src="//unpkg.com/react-scan/dist/auto.global.js"
|
||||
crossOrigin="anonymous"
|
||||
strategy="beforeInteractive"
|
||||
/>
|
||||
|
||||
{/* code to figma */}
|
||||
{/* <script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `(function(){var s=document.createElement('script');s.src='https://mcp.figma.com/mcp/html-to-design/capture.js';document.head.appendChild(s);})();`,
|
||||
}}
|
||||
/> */}
|
||||
</>
|
||||
)}
|
||||
</head>
|
||||
<body className={`${siteFont.className} font-sans antialiased`}>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function PrivacyPage() {
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<h3 className="mb-3 text-lg font-medium">
|
||||
Your content stays private and encrypted.
|
||||
Your content never leaves your device.
|
||||
</h3>
|
||||
<ol className="list-decimal space-y-2 pl-6">
|
||||
<li>
|
||||
@@ -45,22 +45,19 @@ export default function PrivacyPage() {
|
||||
your files
|
||||
</li>
|
||||
<li>
|
||||
AI features require encrypted uploads - your content is
|
||||
encrypted before leaving your device
|
||||
AI features like auto captions run locally in your browser
|
||||
too - nothing is uploaded
|
||||
</li>
|
||||
<li>
|
||||
We only collect your email and basic profile info for your
|
||||
account
|
||||
OpenCut does not currently require an account or login
|
||||
</li>
|
||||
<li>Project data stays on your device, not our servers</li>
|
||||
<li>
|
||||
We use analytics to improve the app, but no personal video
|
||||
We use anonymized analytics to improve the app, but no personal video
|
||||
content is tracked
|
||||
</li>
|
||||
<li>
|
||||
You can delete your account anytime and all data gets removed
|
||||
</li>
|
||||
<li>We don't sell your data or share it with advertisers</li>
|
||||
<li>You can clear local data from your browser at any time</li>
|
||||
<li>We don't sell or share your data with anyone (we don't even have it)</li>
|
||||
</ol>
|
||||
<p className="mt-4">
|
||||
Questions? Email us at{" "}
|
||||
@@ -78,84 +75,30 @@ export default function PrivacyPage() {
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">How We Handle Your Content</h2>
|
||||
<p>
|
||||
<strong>Basic video editing happens locally on your device.</strong>{" "}
|
||||
For standard editing features, we never upload, store, or have access
|
||||
to your video files. Your content remains completely private and under
|
||||
your control.
|
||||
</p>
|
||||
<p>
|
||||
<strong>AI features require secure processing:</strong> When you
|
||||
choose to use AI features like auto captions, your audio/video content
|
||||
is encrypted on your device before being uploaded to our servers for
|
||||
processing. We use zero-knowledge encryption, meaning we cannot
|
||||
decrypt or view your content.
|
||||
</p>
|
||||
<p>
|
||||
After AI processing is complete, the encrypted content is immediately
|
||||
deleted from our servers. Only the results (like generated captions)
|
||||
are returned to your device.
|
||||
<strong>
|
||||
All editing and processing happens locally on your device.
|
||||
</strong>{" "}
|
||||
We never upload, store, or have access to your video or audio files.
|
||||
Your content remains completely private and under your control.
|
||||
AI-powered features like auto captions also run in your browser using
|
||||
on-device models - no content ever leaves your device.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Account Information</h2>
|
||||
<p>When you create an account, we only collect:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Email address (for account access)</li>
|
||||
<li>
|
||||
Profile information from Google OAuth (if you choose to sign in with
|
||||
Google)
|
||||
</li>
|
||||
</ul>
|
||||
<h2 className="text-2xl font-semibold">Accounts & Authentication</h2>
|
||||
<p>
|
||||
<strong>We do NOT store your projects on our servers.</strong> All
|
||||
project data, including names, thumbnails, and creation dates, is
|
||||
stored locally in your browser using IndexedDB.
|
||||
OpenCut does not currently offer user accounts, login, or Google
|
||||
sign-in.
|
||||
</p>
|
||||
<p>
|
||||
We use{" "}
|
||||
<a
|
||||
href="https://www.better-auth.com"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
className="text-primary hover:underline"
|
||||
>
|
||||
Better Auth
|
||||
</a>{" "}
|
||||
for secure authentication and follow industry-standard security
|
||||
practices.
|
||||
Because there is no account system today, we do not collect account
|
||||
emails, profile information, or OAuth identity data.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">AI Features & Encryption</h2>
|
||||
<p>
|
||||
When you use AI-powered features (like auto captions, content
|
||||
analysis, or enhancement tools), your content needs to be processed on
|
||||
our servers. Here's how we protect your privacy:
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>Client-side encryption:</strong> Your content is encrypted
|
||||
on your device before upload
|
||||
</li>
|
||||
<li>
|
||||
<strong>Zero-knowledge processing:</strong> We cannot decrypt or
|
||||
view your original content
|
||||
</li>
|
||||
<li>
|
||||
<strong>Temporary processing:</strong> Encrypted content is deleted
|
||||
immediately after processing
|
||||
</li>
|
||||
<li>
|
||||
<strong>Opt-in only:</strong> AI features are optional - basic
|
||||
editing remains fully local
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Different AI features may process different types of content (audio
|
||||
for captions, video for analysis, etc.), but all follow the same
|
||||
zero-knowledge encryption approach.
|
||||
Your projects are never stored on our servers. All project data,
|
||||
including names, thumbnails, and creation dates, is stored locally
|
||||
in your browser using IndexedDB.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -171,13 +114,12 @@ export default function PrivacyPage() {
|
||||
>
|
||||
Databuddy
|
||||
</a>{" "}
|
||||
for completely anonymized and non-invasive analytics to understand how
|
||||
people use OpenCut.
|
||||
for basic, anonymized visitor counts. We do not track clicks,
|
||||
interactions, or how you use the editor.
|
||||
</p>
|
||||
<p>
|
||||
This helps us improve the editor, but we never collect personal
|
||||
information, track individual users, or store any data that could
|
||||
identify you.
|
||||
No personal information is collected, no individual users are tracked,
|
||||
and no data that could identify you is stored.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -187,7 +129,7 @@ export default function PrivacyPage() {
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Save your projects locally on your device</li>
|
||||
<li>Remember your editor preferences and settings</li>
|
||||
<li>Keep you logged in across browser sessions</li>
|
||||
<li>Store app state needed for the editor to work between sessions</li>
|
||||
</ul>
|
||||
<p>
|
||||
All data stays on your device and can be cleared at any time through
|
||||
@@ -199,10 +141,6 @@ export default function PrivacyPage() {
|
||||
<h2 className="text-2xl font-semibold">Third-Party Services</h2>
|
||||
<p>OpenCut integrates with these services:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong>Google OAuth:</strong> For optional Google sign-in (governed
|
||||
by Google's privacy policy)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Vercel:</strong> For hosting and content delivery
|
||||
</li>
|
||||
@@ -216,8 +154,7 @@ export default function PrivacyPage() {
|
||||
<h2 className="text-2xl font-semibold">Your Rights</h2>
|
||||
<p>You have complete control over your data:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Delete your account and all associated data at any time</li>
|
||||
<li>Export your project data</li>
|
||||
<li>No account is required to use OpenCut today</li>
|
||||
<li>Clear local storage to remove all saved projects</li>
|
||||
<li>Contact us with any privacy concerns</li>
|
||||
</ul>
|
||||
@@ -280,7 +217,7 @@ export default function PrivacyPage() {
|
||||
<Separator />
|
||||
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Last updated: July 14, 2025
|
||||
Last updated: March 15, 2026
|
||||
</p>
|
||||
</BasePage>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { KeyboardEvent, MouseEvent } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { MigrationDialog } from "@/components/editor/dialogs/migration-dialog";
|
||||
import { StoragePersistenceDialog } from "@/components/editor/dialogs/storage-persistence-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
@@ -18,7 +19,7 @@ import type {
|
||||
TProjectMetadata,
|
||||
TProjectSortKey,
|
||||
TProjectSortOption,
|
||||
} from "@/types/project";
|
||||
} from "@/lib/project/types";
|
||||
import { formatTimeCode } from "@/lib/time";
|
||||
import { formatDate } from "@/utils/date";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
@@ -86,6 +87,13 @@ const VIEW_MODE_OPTIONS = [
|
||||
export default function ProjectsPage() {
|
||||
const { searchQuery, sortKey, sortOrder, viewMode } = useProjectsStore();
|
||||
const editor = useEditor();
|
||||
const sortOption: TProjectSortOption = `${sortKey}-${sortOrder}`;
|
||||
|
||||
const isLoading = useEditor((e) => e.project.getIsLoading());
|
||||
const isInitialized = useEditor((e) => e.project.getIsInitialized());
|
||||
const projectsToDisplay = useEditor((e) =>
|
||||
e.project.getFilteredAndSortedProjects({ searchQuery, sortOption }),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.project.getIsInitialized()) {
|
||||
@@ -93,18 +101,10 @@ export default function ProjectsPage() {
|
||||
}
|
||||
}, [editor.project]);
|
||||
|
||||
const sortOption: TProjectSortOption = `${sortKey}-${sortOrder}`;
|
||||
const projectsToDisplay = editor.project.getFilteredAndSortedProjects({
|
||||
searchQuery,
|
||||
sortOption,
|
||||
});
|
||||
|
||||
const isLoading = editor.project.getIsLoading();
|
||||
const isInitialized = editor.project.getIsInitialized();
|
||||
|
||||
return (
|
||||
<div className="bg-background min-h-screen">
|
||||
<MigrationDialog />
|
||||
<StoragePersistenceDialog />
|
||||
<ProjectsHeader />
|
||||
<ProjectsToolbar projectIds={projectsToDisplay.map((p) => p.id)} />
|
||||
<main className="mx-auto px-4 pt-2 pb-6 flex flex-col gap-4">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import type { TProjectSortKey } from "@/types/project";
|
||||
import type { TProjectSortKey } from "@/lib/project/types";
|
||||
|
||||
export type ProjectsViewMode = "grid" | "list";
|
||||
|
||||
|
||||
@@ -41,18 +41,17 @@ export default function TermsPage() {
|
||||
</h3>
|
||||
<ol className="list-decimal space-y-2 pl-6">
|
||||
<li>
|
||||
Your content stays private - basic editing is local, AI features
|
||||
use encrypted uploads
|
||||
Everything runs locally in your browser - nothing is ever
|
||||
uploaded to our servers
|
||||
</li>
|
||||
<li>
|
||||
We never claim ownership of your content, even when processing
|
||||
AI features
|
||||
We never claim ownership of your content
|
||||
</li>
|
||||
<li>
|
||||
Free for personal and commercial use with no watermarks or
|
||||
restrictions
|
||||
</li>
|
||||
<li>Don't use OpenCut for illegal activities or harassment</li>
|
||||
<li>You're responsible for how you use it - don't break the law</li>
|
||||
<li>
|
||||
Service provided "as is" - we can't guarantee perfect uptime
|
||||
</li>
|
||||
@@ -61,8 +60,7 @@ export default function TermsPage() {
|
||||
needed
|
||||
</li>
|
||||
<li>
|
||||
You can delete your account anytime and keep using your exported
|
||||
videos
|
||||
No account required - your exported videos are always yours
|
||||
</li>
|
||||
</ol>
|
||||
<p className="mt-4">
|
||||
@@ -81,21 +79,17 @@ export default function TermsPage() {
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Your Content, Your Rights</h2>
|
||||
<p>
|
||||
<strong>You own everything you create.</strong> OpenCut processes
|
||||
basic editing locally on your device. For AI features, content is
|
||||
encrypted before upload and we cannot access your original files. We
|
||||
make no claims to ownership, licensing, or rights over your videos,
|
||||
projects, or any content you create using OpenCut.
|
||||
<strong>You own everything you create.</strong> All editing and
|
||||
processing happens locally on your device. We never see, store, or
|
||||
have access to your files. We make no claims to ownership, licensing,
|
||||
or rights over your videos, projects, or any content you create using
|
||||
OpenCut.
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
Your content remains private and under your control at all times
|
||||
Your content never leaves your device
|
||||
</li>
|
||||
<li>You retain all intellectual property rights to your content</li>
|
||||
<li>
|
||||
Even when using AI features, we cannot access your unencrypted
|
||||
content
|
||||
</li>
|
||||
<li>You can export and use your content however you choose</li>
|
||||
<li>No watermarks, no licensing restrictions from OpenCut</li>
|
||||
</ul>
|
||||
@@ -115,53 +109,26 @@ export default function TermsPage() {
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>What we ask:</strong> Don't use OpenCut for illegal
|
||||
activities, harassment, or creating harmful content. Be respectful of
|
||||
others and follow applicable laws.
|
||||
You're responsible for how you use OpenCut and the content you create.
|
||||
Don't use it for anything illegal in your jurisdiction.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">
|
||||
AI Features and Data Processing
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold">AI Features</h2>
|
||||
<p>
|
||||
OpenCut offers optional AI-powered features that require server
|
||||
processing:
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
AI features (auto captions, content analysis, etc.) are completely
|
||||
optional
|
||||
</li>
|
||||
<li>Your content is encrypted on your device before any upload</li>
|
||||
<li>
|
||||
We use zero-knowledge encryption - we cannot decrypt your content
|
||||
</li>
|
||||
<li>Encrypted content is deleted immediately after processing</li>
|
||||
<li>
|
||||
You maintain full ownership and control of your content throughout
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
By using AI features, you consent to the temporary, encrypted
|
||||
processing of your content as described in our Privacy Policy. You can
|
||||
always choose to use only local editing features.
|
||||
AI features like auto captions run entirely in your browser using
|
||||
on-device models. No content is uploaded to any server. These features
|
||||
are optional - you can use OpenCut without them.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Account and Service</h2>
|
||||
<p>To use certain features, you may create an account:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Provide accurate information when signing up</li>
|
||||
<li>Keep your account secure and don't share credentials</li>
|
||||
<li>You're responsible for activity under your account</li>
|
||||
<li>You can delete your account at any time</li>
|
||||
</ul>
|
||||
<h2 className="text-2xl font-semibold">Service</h2>
|
||||
<p>
|
||||
OpenCut is provided "as is" without warranties. While we strive for
|
||||
reliability, we can't guarantee uninterrupted service.
|
||||
OpenCut does not currently require an account. The service is provided
|
||||
"as is" without warranties. While we strive for reliability, we can't
|
||||
guarantee uninterrupted service.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -188,24 +155,6 @@ export default function TermsPage() {
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Third-Party Content</h2>
|
||||
<p>
|
||||
When using OpenCut, make sure you have the right to use any content
|
||||
you import:
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Only upload content you own or have permission to use</li>
|
||||
<li>
|
||||
Respect copyright, trademarks, and other intellectual property
|
||||
</li>
|
||||
<li>
|
||||
Don't use copyrighted music, images, or videos without permission
|
||||
</li>
|
||||
<li>You're responsible for any claims related to your content</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Limitations and Liability</h2>
|
||||
<p>
|
||||
@@ -239,13 +188,10 @@ export default function TermsPage() {
|
||||
</section>
|
||||
|
||||
<section className="flex flex-col gap-3">
|
||||
<h2 className="text-2xl font-semibold">Termination</h2>
|
||||
<h2 className="text-2xl font-semibold">Stopping Use</h2>
|
||||
<p>You can stop using OpenCut at any time:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Delete your account through your profile settings</li>
|
||||
<li>Clear your browser data to remove local projects</li>
|
||||
<li>Your content remains yours even if you stop using OpenCut</li>
|
||||
<li>We may suspend accounts for violations of these terms</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@@ -288,7 +234,7 @@ export default function TermsPage() {
|
||||
</section>
|
||||
<Separator />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Last updated: July 14, 2025
|
||||
Last updated: March 15, 2026
|
||||
</p>
|
||||
</BasePage>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user