diff --git a/tools/antigravity/.gitignore b/tools/antigravity/.gitignore index f390d12..8b68b61 100644 --- a/tools/antigravity/.gitignore +++ b/tools/antigravity/.gitignore @@ -41,3 +41,7 @@ yarn-error.log* next-env.d.ts /src/generated/prisma + +# sqlite +*.db +*.db-journal diff --git a/tools/antigravity/.previous_gitignore_is_safe_to_append b/tools/antigravity/.previous_gitignore_is_safe_to_append new file mode 100644 index 0000000..92eb3a9 Binary files /dev/null and b/tools/antigravity/.previous_gitignore_is_safe_to_append differ diff --git a/tools/antigravity/check-db.mjs b/tools/antigravity/check-db.mjs index 717ca13..c0b6e01 100644 --- a/tools/antigravity/check-db.mjs +++ b/tools/antigravity/check-db.mjs @@ -1,4 +1,5 @@ // check-db.mjs +import 'dotenv/config'; import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); diff --git a/tools/antigravity/package-lock.json b/tools/antigravity/package-lock.json index 58cfa26..b7a8f2a 100644 --- a/tools/antigravity/package-lock.json +++ b/tools/antigravity/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "^5.22.0", + "dotenv": "^17.2.3", "framer-motion": "^12.23.26", "lucide-react": "^0.562.0", "next": "16.1.0", @@ -2889,6 +2890,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/tools/antigravity/package.json b/tools/antigravity/package.json index 5c42f1d..1b56ea6 100644 --- a/tools/antigravity/package.json +++ b/tools/antigravity/package.json @@ -7,10 +7,12 @@ "build": "next build", "start": "next start", "lint": "next lint", - "db:check": "node check-db.mjs" + "db:check": "node check-db.mjs", + "deploy": "prisma migrate deploy" }, "dependencies": { "@prisma/client": "^5.22.0", + "dotenv": "^17.2.3", "framer-motion": "^12.23.26", "lucide-react": "^0.562.0", "next": "16.1.0", diff --git a/tools/antigravity/prisma/dev.db b/tools/antigravity/prisma/dev.db index 76a5384..d66700e 100644 Binary files a/tools/antigravity/prisma/dev.db and b/tools/antigravity/prisma/dev.db differ diff --git a/tools/antigravity/prisma/migrations/20251223024658_init/migration.sql b/tools/antigravity/prisma/migrations/20251223024658_init/migration.sql new file mode 100644 index 0000000..6e3cc75 --- /dev/null +++ b/tools/antigravity/prisma/migrations/20251223024658_init/migration.sql @@ -0,0 +1,65 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL PRIMARY KEY, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "name" TEXT NOT NULL, + "onboardingCompleted" BOOLEAN NOT NULL DEFAULT false, + "preferences" TEXT, + "aiConfig" TEXT +); + +-- CreateTable +CREATE TABLE "Quote" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "content" TEXT NOT NULL, + "author" TEXT, + "explanation" TEXT, + "category" TEXT, + "tags" TEXT, + "concepts" TEXT, + "generatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "sourceModel" TEXT +); + +-- CreateTable +CREATE TABLE "DailyView" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "quoteId" INTEGER NOT NULL, + "viewedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "date" TEXT NOT NULL, + CONSTRAINT "DailyView_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "DailyView_quoteId_fkey" FOREIGN KEY ("quoteId") REFERENCES "Quote" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Rating" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "quoteId" INTEGER NOT NULL, + "score" INTEGER NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "Rating_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Rating_quoteId_fkey" FOREIGN KEY ("quoteId") REFERENCES "Quote" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Share" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "quoteId" INTEGER NOT NULL, + "platform" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "Share_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Share_quoteId_fkey" FOREIGN KEY ("quoteId") REFERENCES "Quote" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_name_key" ON "User"("name"); + +-- CreateIndex +CREATE INDEX "Quote_category_idx" ON "Quote"("category"); + +-- CreateIndex +CREATE UNIQUE INDEX "DailyView_userId_date_key" ON "DailyView"("userId", "date"); diff --git a/tools/antigravity/prisma/migrations/migration_lock.toml b/tools/antigravity/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..e5e5c47 --- /dev/null +++ b/tools/antigravity/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/tools/antigravity/server.js b/tools/antigravity/server.js new file mode 100644 index 0000000..ffe3e9e --- /dev/null +++ b/tools/antigravity/server.js @@ -0,0 +1,40 @@ +const { createServer } = require('http'); +const { parse } = require('url'); +const next = require('next'); + +const dev = process.env.NODE_ENV !== 'production'; +const hostname = 'localhost'; +const port = process.env.PORT || 3000; +// when using middleware `hostname` and `port` must be provided below +const app = next({ dev, hostname, port }); +const handle = app.getRequestHandler(); + +app.prepare().then(() => { + createServer(async (req, res) => { + try { + // Be sure to pass `true` as the second argument to `url.parse`. + // This tells it to parse the query portion of the URL. + const parsedUrl = parse(req.url, true); + const { pathname, query } = parsedUrl; + + if (pathname === '/a') { + await app.render(req, res, '/a', query); + } else if (pathname === '/b') { + await app.render(req, res, '/b', query); + } else { + await handle(req, res, parsedUrl); + } + } catch (err) { + console.error('Error occurred handling', req.url, err); + res.statusCode = 500; + res.end('internal server error'); + } + }) + .once('error', (err) => { + console.error(err); + process.exit(1); + }) + .listen(port, () => { + console.log(`> Ready on http://${hostname}:${port}`); + }); +}); diff --git a/tools/antigravity/src/app/page.tsx b/tools/antigravity/src/app/page.tsx index 85c5c8a..c98d2a2 100644 --- a/tools/antigravity/src/app/page.tsx +++ b/tools/antigravity/src/app/page.tsx @@ -25,8 +25,8 @@ export default function Home() { // Timeline aggressively tightened for continuous flow timers.push(setTimeout(() => setAnimStage(1), 1200)); // Start expanding (duration ~1s -> finishes at 2200) timers.push(setTimeout(() => setAnimStage(2), 2200)); // Move to top immediately (duration 1.2s -> finishes at 3400) - timers.push(setTimeout(() => setAnimStage(3), 3400)); // Show Text immediately (duration ~1s -> finishes at 4400) - timers.push(setTimeout(() => setAnimStage(4), 4400)); // Show Input immediately + timers.push(setTimeout(() => setAnimStage(3), 3800)); // Show Text delayed (let YinYang settle) + timers.push(setTimeout(() => setAnimStage(4), 5800)); // Show Input delayed // Stage 5 is skipped/merged into 4 for simplicity in this new flow return () => timers.forEach(clearTimeout); @@ -36,7 +36,7 @@ export default function Home() { const subheaderText = "digitaler Abreißkalender"; return ( -
+
{/* Intro Overlay: White Background to Black Dot */} @@ -89,17 +89,15 @@ export default function Home() { - {/* Placeholder for Logo Area */} -
- - {/* STAGE 2: Small Header YinYang */} + {/* Placeholder for Logo Area / YinYang Stage 2 */} +
{animStage >= 2 && ( -
+
- {/* Branding Section */} -
+ {/* Branding Section Group */} +
+ {/* Title */} -
+
= 3 - ? "linear-gradient(to top, #fbbf24 0%, #ffffff 60%)" - : "linear-gradient(to top, #ffffff 100%, #ffffff 100%)", + backgroundImage: "linear-gradient(to top, #fbbf24 0%, #ffffff 60%)", }} > {lettersLabel.map((letter, i) => ( = 3 ? { opacity: 1, y: 0 } : {}} + initial={{ opacity: 0 }} + animate={animStage >= 3 ? { opacity: 1 } : {}} transition={{ - duration: 1, - delay: i * 0.15, - ease: [0.22, 1, 0.36, 1], + duration: 1.5, + delay: i * 0.1, + ease: "easeOut", }} - className="inline-block transform-gpu" - style={{ backfaceVisibility: "hidden" }} + className="inline-block" > {letter} @@ -150,14 +145,14 @@ export default function Home() {
{/* Subheader */} -
-
+
+
{subheaderText.split("").map((char, i) => ( = 3 ? { opacity: 1, filter: "blur(0px)" } : {}} - transition={{ duration: 0.5, delay: i * 0.03 }} + transition={{ duration: 0.8, delay: 1.5 + i * 0.03 }} className={`text-sm md:text-lg font-extralight tracking-tight ${[0, 10, 12, 16].includes(i) ? "text-amber-400 font-bold" : "text-gray-400" @@ -171,51 +166,53 @@ export default function Home() { )}
- {/* Input Section - Now nested to match branding width - w-0 min-w-full prevents parent growth */} -
- - {animStage >= 4 && ( - -
-
- - setName(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && go()} - placeholder="Dein Name..." - className="w-full bg-transparent text-xl placeholder-gray-600 focus:outline-none text-white font-light tracking-wide" - autoFocus - /> -
- - +
+ + {/* Input Section */} +
+ + {animStage >= 4 && ( + +
+
+ + setName(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && go()} + placeholder="Dein Name..." + className="w-full bg-transparent text-xl placeholder-gray-600 focus:outline-none text-white font-light tracking-wide" + // autoFocus removed + />
- - )} - -
+ + +
+
+ )} +
+ {/* Bottom Reflection */}