Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5b3b808
tool(antigravity): implement complete ARK application with Next.js
Dec 20, 2025
cb2f764
tool(antigravity): implement complete ARK app with PWA, AI, and Premi…
Dec 20, 2025
1957935
tool(antigravity): localize application to German
Dec 20, 2025
75d9ec8
fix(frontend): implement real CSS modules and fix next config
Dec 20, 2025
c3c5d6f
fix: middleware to proxy migration and rating api
Dec 20, 2025
2ae6ae5
feat: implement url-based identity and complete tracking
Dec 20, 2025
615ac29
fix: auth proxy matcher and onboarding styles
Dec 20, 2025
f8daa17
feat: implement user settings overlay and ui polish
Dec 20, 2025
0f08cc2
feat: responsive layout 100dvh vmin
Dec 20, 2025
0a8a9b4
feat: content enrichment concepts and author logic
Dec 20, 2025
67c045a
feat: interactive concepts and overlay
Dec 20, 2025
5f77521
fix(ai): tune temp and prompt for subtlety; fix(ui): repair highlight…
Dec 20, 2025
29f97ca
fix(ui): explicit input contrast; fix(backend): add detailed ai logs
Dec 20, 2025
5f540fd
fix(debug): add verbose logging and force dynamic rendering
Dec 20, 2025
6b7ae6a
fix(logic): invalidate daily view on profile update to ensure freshness
Dec 20, 2025
ea36b1c
fix(ai): remove biased examples from prompt to increase variety
Dec 20, 2025
d8de11a
feat(ai): add Veit Lindau style modes (Questions/Impulses)
Dec 20, 2025
dec6aa8
feat(history): add archive page with favorites filter; fix(ui): landi…
Dec 20, 2025
4f73e60
fix(auth): switch user session cookie on name change for correct data…
Dec 21, 2025
6b076ec
feat(admin): implement user-specific AI configuration backend
Dec 21, 2025
a38f02e
feat(admin): enhance admin ui, prompt preview, history inspector, and…
Dec 22, 2025
8acef7b
fix(admin): install tailwind for admin ui and fix user inspector feat…
Dec 22, 2025
77ec568
fix(interaction): prevent duplicate favorites and visualize liked state
Dec 22, 2025
f7251cc
gitignore update by CP
Dec 22, 2025
c6396c3
fix(interaction): robust user id retrieval in rate api
Dec 22, 2025
badae92
fix(core): ensure middleware is correctly named for next.js
Dec 22, 2025
bf146d3
fix(core): correctly export middleware function
Dec 22, 2025
fba243e
fix(build): resolve tailwind v4 postcss error and revert middleware n…
Dec 22, 2025
6f76609
fix(ui): import admin styles in layout to enable tailwind
Dec 22, 2025
f9f5d8f
fix(ui): add manual admin css reset to resolve preflight issues
Dec 22, 2025
a4116d1
fix(ui): implement robust css reset for admin forms and polishing
Dec 22, 2025
23e5118
style(admin): implement premium dark theme for admin forms
Dec 22, 2025
9dfb267
refactor(ui): upgrade tailwind css import syntax to v4 alpha compliance
Dec 22, 2025
a9736b9
style(admin): refine spacing, tabs and buttons for premium usage
Dec 22, 2025
5bdf9e2
feat(ai): refactor to master prompt architecture with variable substi…
Dec 22, 2025
5961f3a
refactor(admin): reorder ui components and fix prompt variable substi…
Dec 22, 2025
e2ac1a7
refactor(admin): remove preview, enforce limits, prefill template
Dec 22, 2025
9ab7a7f
feat: polish intro animation, user menu transition, and archive navig…
Dec 23, 2025
78c94d8
DeploymentPrep
Dec 23, 2025
7bc7af4
feat: initialize database schema with User, Quote, DailyView, Rating,…
Dec 23, 2025
0c5f884
feat: Optimize mobile animations, stabilize layout, fix Plesk deployment
Dec 23, 2025
8fcf182
Merge main into tool/antigravity and resolve conflicts (keep ours)
Dec 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tools/antigravity/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ yarn-error.log*
next-env.d.ts

/src/generated/prisma

# sqlite
*.db
*.db-journal
Binary file not shown.
5 changes: 3 additions & 2 deletions tools/antigravity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"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",
Expand All @@ -31,4 +32,4 @@
"tailwindcss": "^4.1.18",
"typescript": "^5"
}
}
}
Binary file modified tools/antigravity/prisma/dev.db
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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");
3 changes: 3 additions & 0 deletions tools/antigravity/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -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"
40 changes: 40 additions & 0 deletions tools/antigravity/server.js
Original file line number Diff line number Diff line change
@@ -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}`);
});
});
133 changes: 65 additions & 68 deletions tools/antigravity/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -36,7 +36,7 @@ export default function Home() {
const subheaderText = "digitaler Abreißkalender";

return (
<main className="min-h-screen bg-[#050505] text-white font-sans selection:bg-amber-500/30 overflow-hidden relative flex flex-col items-center justify-center p-6">
<main className="h-[100dvh] bg-[#050505] text-white font-sans selection:bg-amber-500/30 overflow-hidden relative flex flex-col items-center justify-start md:justify-center pt-[20vh] md:pt-0 md:pb-[15vh] p-6">

{/* Intro Overlay: White Background to Black Dot */}
<AnimatePresence mode="wait">
Expand Down Expand Up @@ -89,17 +89,15 @@ export default function Home() {
<motion.div
animate={animStage === 6 ? { opacity: 0, y: -20 } : { opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="relative z-10 text-center max-w-4xl w-full flex flex-col items-center"
className="relative z-10 text-center max-w-4xl w-full flex flex-col items-center gap-8"
>
{/* Placeholder for Logo Area */}
<div className="h-28 mb-4 border-none flex flex-col items-center justify-end">

{/* STAGE 2: Small Header YinYang */}
{/* Placeholder for Logo Area / YinYang Stage 2 */}
<div className="h-12 border-none flex flex-col items-center justify-end shrink-0">
{animStage >= 2 && (
<div className="group cursor-pointer">
<div className="group cursor-pointer relative z-[100]">
<motion.div
layoutId="yinyang-shared"
className="w-10 h-10 flex items-center justify-center rounded-full shadow-[0_0_15px_rgba(255,255,255,0.2)] bg-black/10 backdrop-blur-sm"
className="w-10 h-10 flex items-center justify-center rounded-full shadow-[0_0_15px_rgba(255,255,255,0.2)] bg-black/10 backdrop-blur-sm relative z-[100]"
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
>
<motion.div
Expand All @@ -115,33 +113,30 @@ export default function Home() {
)}
</div>

{/* Branding Section */}
<div className="inline-flex flex-col items-stretch mb-0 w-max max-w-full">
{/* Branding Section Group */}
<div className="flex flex-col items-center w-full gap-2">

{/* Title */}
<div className="group h-[8.5rem] md:h-[14rem] flex items-center justify-center relative z-20">
<div className="group h-auto flex items-center justify-center relative z-20 w-full">
<motion.h1
className="text-9xl md:text-[14rem] font-serif font-medium tracking-[-0.04em] leading-none drop-shadow-[0_0_40px_rgba(255,255,255,0.1)] relative transform-gpu pb-4"
className="text-[20vw] md:text-[14rem] font-serif font-medium tracking-[-0.04em] leading-none drop-shadow-[0_0_40px_rgba(255,255,255,0.1)] relative py-2 px-4 md:px-8"
style={{
WebkitTextFillColor: "transparent",
WebkitBackgroundClip: "text",
backgroundImage:
animStage >= 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) => (
<motion.span
key={i}
initial={{ opacity: 0, y: 40 }}
animate={animStage >= 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}
</motion.span>
Expand All @@ -150,14 +145,14 @@ export default function Home() {
</div>

{/* Subheader */}
<div className="flex flex-col items-center gap-4 h-20">
<div className="w-full flex justify-between overflow-hidden px-1">
<div className="flex flex-col items-center justify-center h-auto">
<div className="w-full flex justify-center gap-[1px] md:gap-[3px] overflow-hidden px-1">
{subheaderText.split("").map((char, i) => (
<motion.span
key={i}
initial={{ opacity: 0, filter: "blur(10px)" }}
animate={animStage >= 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"
Expand All @@ -171,51 +166,53 @@ export default function Home() {
<motion.div
initial={{ width: 0, opacity: 0 }}
animate={{ width: "100%", opacity: 1 }}
transition={{ duration: 1.2, ease: "easeOut" }}
className="h-px bg-gradient-to-r from-transparent via-amber-500/50 to-transparent max-w-[200px]"
transition={{ duration: 1.2, delay: 0.2, ease: "easeOut" }}
className="h-px bg-gradient-to-r from-transparent via-amber-500/50 to-transparent max-w-[200px] mt-4"
/>
)}
</div>
{/* Input Section - Now nested to match branding width - w-0 min-w-full prevents parent growth */}
<div className="mt-2 min-h-[80px] flex items-center justify-center w-0 min-w-full">
<AnimatePresence>
{animStage >= 4 && (
<motion.div
initial={{ opacity: 0, y: 30, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
className="w-full bg-white/[0.03] backdrop-blur-2xl border border-white/10 p-1 rounded-2xl shadow-2xl transition-colors duration-300 hover:bg-white/[0.05] hover:border-white/20 group/input"
>
<div className="flex flex-col md:flex-row items-center gap-1">
<div className="flex-1 w-full px-4 py-3 flex items-center gap-3">
<Sparkles
size={18}
className="text-amber-500 transition-transform group-hover/input:rotate-12"
/>
<input
value={name}
onChange={(e) => 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
/>
</div>

<button
onClick={go}
disabled={!name.trim()}
className="w-full md:w-auto px-6 py-3 rounded-xl bg-white text-black font-bold flex items-center justify-center gap-2 hover:bg-amber-400 transition-all hover:scale-[1.02] active:scale-95 disabled:opacity-30 disabled:grayscale disabled:hover:scale-100 text-sm md:text-base"
>
Eintreten <ArrowRight size={18} />
</button>
</div>

{/* Input Section */}
<div className="w-full max-w-md min-h-[80px] flex items-center justify-center mt-4">
<AnimatePresence>
{animStage >= 4 && (
<motion.div
initial={{ opacity: 0, y: 30, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
className="w-full bg-white/[0.03] backdrop-blur-2xl border border-white/10 p-1 rounded-2xl shadow-2xl transition-colors duration-300 hover:bg-white/[0.05] hover:border-white/20 group/input"
>
<div className="flex flex-col md:flex-row items-center gap-1">
<div className="flex-1 w-full px-4 py-3 flex items-center gap-3">
<Sparkles
size={18}
className="text-amber-500 transition-transform group-hover/input:rotate-12"
/>
<input
value={name}
onChange={(e) => 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
/>
</div>
</motion.div>
)}
</AnimatePresence>
</div>

<button
onClick={go}
disabled={!name.trim()}
className="w-full md:w-auto px-6 py-3 rounded-xl bg-white text-black font-bold flex items-center justify-center gap-2 hover:bg-amber-400 transition-all hover:scale-[1.02] active:scale-95 disabled:opacity-30 disabled:grayscale disabled:hover:scale-100 text-sm md:text-base"
>
Eintreten <ArrowRight size={18} />
</button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>

</motion.div>

{/* Bottom Reflection */}
Expand Down