-
+
AI Presentation
Choose a design, set preferences, and generate polished slides.
diff --git a/servers/nextjs/app/globals.css b/servers/nextjs/app/globals.css
index 5b890cba..5c267055 100644
--- a/servers/nextjs/app/globals.css
+++ b/servers/nextjs/app/globals.css
@@ -4,7 +4,7 @@
body {
- font-family: var(--font-inter), var(--font-roboto), sans-serif;
+ font-family: var(--font-inter), var(--font-unbounded), var(--font-syne), sans-serif;
}
@layer utilities {
@@ -449,7 +449,7 @@ thead {
}
.container__editor {
-
+
font-variant-ligatures: common-ligatures;
background-color: #fafafa;
border-radius: 3px;
@@ -458,6 +458,7 @@ thead {
.container__editor textarea {
outline: 0;
}
+
/* Syntax highlighting */
.token.comment,
.token.prolog,
@@ -465,12 +466,15 @@ thead {
.token.cdata {
color: #90a4ae;
}
+
.token.punctuation {
color: #9e9e9e;
}
+
.namespace {
opacity: 0.7;
}
+
.token.property,
.token.tag,
.token.boolean,
@@ -480,6 +484,7 @@ thead {
.token.deleted {
color: #e91e63;
}
+
.token.selector,
.token.attr-name,
.token.string,
@@ -488,6 +493,7 @@ thead {
.token.inserted {
color: #4caf50;
}
+
.token.operator,
.token.entity,
.token.url,
@@ -495,26 +501,32 @@ thead {
.style .token.string {
color: #795548;
}
+
.token.atrule,
.token.attr-value,
.token.keyword {
color: #3f51b5;
}
+
.token.function {
color: #f44336;
}
+
.token.regex,
.token.important,
.token.variable {
color: #ff9800;
}
+
.token.important,
.token.bold {
font-weight: bold;
}
+
.token.italic {
font-style: italic;
}
+
.token.entity {
cursor: help;
}
\ No newline at end of file
diff --git a/servers/nextjs/app/layout.tsx b/servers/nextjs/app/layout.tsx
index 3c4dcb2c..7a70334b 100644
--- a/servers/nextjs/app/layout.tsx
+++ b/servers/nextjs/app/layout.tsx
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
-import { Roboto, Instrument_Sans } from "next/font/google";
+import { Syne, Unbounded } from "next/font/google";
import "./globals.css";
import { Providers } from "./providers";
import MixpanelInitializer from "./MixpanelInitializer";
@@ -16,16 +16,16 @@ const inter = localFont({
variable: "--font-inter",
});
-const instrument_sans = Instrument_Sans({
+const syne = Syne({
subsets: ["latin"],
- weight: ["400"],
- variable: "--font-instrument-sans",
+ weight: ["400", "500", "600", "700", "800"],
+ variable: "--font-syne",
});
-const roboto = Roboto({
+const unbounded = Unbounded({
subsets: ["latin"],
- weight: ["400"],
- variable: "--font-roboto",
+ weight: ["400", "500", "600", "700", "800"],
+ variable: "--font-unbounded",
});
@@ -82,7 +82,7 @@ export default function RootLayout({
return (
diff --git a/servers/nextjs/components/Home.tsx b/servers/nextjs/components/Home.tsx
index 7dff432c..bb5319c7 100644
--- a/servers/nextjs/components/Home.tsx
+++ b/servers/nextjs/components/Home.tsx
@@ -19,6 +19,7 @@ import OnBoardingHeader from "./OnBoarding/OnBoardingHeader";
import ModeSelectStep from "./OnBoarding/ModeSelectStep";
import PresentonMode from "./OnBoarding/PresentonMode";
import GenerationWithImage from "./OnBoarding/GenerationWithImage";
+import FinalStep from "./OnBoarding/FinalStep";
// Button state interface
interface ButtonState {
@@ -30,6 +31,54 @@ interface ButtonState {
status?: string;
}
+const FINAL_STEP_CONFETTI_PIECES = [
+ // left: denser at top
+ { side: "left", offset: 1, top: 3, width: 28, height: 10, color: "#F59E0B", rotate: 12 },
+ { side: "left", offset: 7, top: 5, width: 18, height: 7, color: "#7C3AED", rotate: -10 },
+ { side: "left", offset: 12, top: 7, width: 20, height: 7, color: "#14B8A6", rotate: 22 },
+ { side: "left", offset: 3, top: 10, width: 22, height: 8, color: "#22C55E", rotate: -18 },
+ { side: "left", offset: 9, top: 12, width: 24, height: 8, color: "#E11D48", rotate: 18 },
+ { side: "left", offset: 14, top: 15, width: 18, height: 7, color: "#F43F5E", rotate: 23 },
+ { side: "left", offset: 5, top: 18, width: 20, height: 7, color: "#0EA5E9", rotate: -12 },
+ { side: "left", offset: 11, top: 21, width: 26, height: 9, color: "#2563EB", rotate: 20 },
+ { side: "left", offset: 2, top: 24, width: 19, height: 7, color: "#14B8A6", rotate: -16 },
+ { side: "left", offset: 8, top: 28, width: 21, height: 8, color: "#FB7185", rotate: 27 },
+ { side: "left", offset: 13, top: 32, width: 20, height: 7, color: "#06B6D4", rotate: 16 },
+ { side: "left", offset: 3, top: 36, width: 24, height: 9, color: "#EAB308", rotate: -22 },
+ { side: "left", offset: 10, top: 41, width: 18, height: 7, color: "#A855F7", rotate: -14 },
+ { side: "left", offset: 2, top: 50, width: 30, height: 10, color: "#EC4899", rotate: -28 },
+ { side: "left", offset: 13, top: 58, width: 19, height: 7, color: "#22C55E", rotate: 17 },
+ { side: "left", offset: 5, top: 66, width: 24, height: 8, color: "#8B5CF6", rotate: 14 },
+ { side: "left", offset: 11, top: 74, width: 18, height: 7, color: "#3B82F6", rotate: 12 },
+ { side: "left", offset: 4, top: 82, width: 20, height: 7, color: "#14B8A6", rotate: 21 },
+ { side: "left", offset: 7, top: 90, width: 24, height: 8, color: "#D946EF", rotate: -26 },
+
+ // right: denser at top
+ { side: "right", offset: 1, top: 4, width: 30, height: 10, color: "#F97316", rotate: -14 },
+ { side: "right", offset: 8, top: 6, width: 19, height: 7, color: "#0EA5E9", rotate: 12 },
+ { side: "right", offset: 13, top: 9, width: 20, height: 7, color: "#22C55E", rotate: -20 },
+ { side: "right", offset: 4, top: 12, width: 24, height: 8, color: "#EC4899", rotate: 20 },
+ { side: "right", offset: 10, top: 15, width: 22, height: 8, color: "#06B6D4", rotate: -18 },
+ { side: "right", offset: 15, top: 18, width: 20, height: 7, color: "#22C55E", rotate: -25 },
+ { side: "right", offset: 5, top: 21, width: 18, height: 7, color: "#8B5CF6", rotate: 19 },
+ { side: "right", offset: 12, top: 24, width: 21, height: 8, color: "#F43F5E", rotate: 14 },
+ { side: "right", offset: 2, top: 28, width: 26, height: 9, color: "#84CC16", rotate: 15 },
+ { side: "right", offset: 9, top: 33, width: 21, height: 8, color: "#F97316", rotate: -11 },
+ { side: "right", offset: 14, top: 38, width: 20, height: 7, color: "#A855F7", rotate: -19 },
+ { side: "right", offset: 4, top: 44, width: 19, height: 7, color: "#F43F5E", rotate: 20 },
+ { side: "right", offset: 2, top: 52, width: 28, height: 10, color: "#FACC15", rotate: 25 },
+ { side: "right", offset: 12, top: 60, width: 18, height: 7, color: "#14B8A6", rotate: -15 },
+ { side: "right", offset: 6, top: 68, width: 24, height: 8, color: "#22C55E", rotate: -17 },
+ { side: "right", offset: 1, top: 76, width: 20, height: 7, color: "#A855F7", rotate: 14 },
+ { side: "right", offset: 13, top: 84, width: 20, height: 7, color: "#3B82F6", rotate: -24 },
+ { side: "right", offset: 5, top: 92, width: 26, height: 9, color: "#EAB308", rotate: 18 },
+] as const;
+
+const getTaperedSideOffset = (offset: number, top: number) => {
+ const taperMultiplier = Math.max(0.72, 1.85 - top * 0.012);
+ return Math.min(29, Number((offset * taperMultiplier).toFixed(2)));
+};
+
export default function Home() {
const router = useRouter();
const pathname = usePathname();
@@ -37,7 +86,7 @@ export default function Home() {
const [selectedMode, setSelectedMode] = useState("presenton")
const config = useSelector((state: RootState) => state.userConfig);
const [llmConfig, setLlmConfig] = useState(config.llm_config);
- console.log('config', config);
+
const [downloadingModel, setDownloadingModel] = useState<{
name: string;
size: number | null;
@@ -270,13 +319,34 @@ export default function Home() {
//