From 71f84587b0e843dccd3e6452a4bb19d6575b62ea Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 02:15:01 -0500 Subject: [PATCH 01/16] Fixing my readme.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf1e9b77..a868f7bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Animated AI Chat +# ZapDev -Welcome to Animated AI Chat! This is a web application that provides an engaging and interactive chat experience powered by various AI models through OpenRouter, featuring smooth animations and user authentication. +Welcome to ZapDev! This is a web application that provides an engaging and interactive chat experience powered by various AI models through OpenRouter, featuring smooth animations and user authentication. ## Features From 3f4a1c79a4f8d819aa69dc19e01a7f99cb3ea2a2 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 02:25:13 -0500 Subject: [PATCH 02/16] Trying to vailidate chat messages --- app/api/validate-chat-session/route.ts | 71 ++++++++++++++++++++++++++ app/chat/[id]/page.tsx | 43 ++++++++++++++-- bun.lock | 27 ++++++++++ package.json | 1 + 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 app/api/validate-chat-session/route.ts diff --git a/app/api/validate-chat-session/route.ts b/app/api/validate-chat-session/route.ts new file mode 100644 index 00000000..949960cc --- /dev/null +++ b/app/api/validate-chat-session/route.ts @@ -0,0 +1,71 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getAuth } from '@clerk/nextjs/server'; +import { createClient } from '@supabase/supabase-js'; // Standard Supabase import + +// Initialize Supabase client +// Ensure your Supabase URL and anon key are set in environment variables +const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; +const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; + +if (!supabaseUrl) { + console.error('Error: NEXT_PUBLIC_SUPABASE_URL is not set.'); + // Potentially throw an error or handle this case as appropriate for your app +} +if (!supabaseAnonKey) { + console.error('Error: NEXT_PUBLIC_SUPABASE_ANON_KEY is not set.'); + // Potentially throw an error or handle this case as appropriate for your app +} + +// Create a new Supabase client instance for each request or reuse a global one if preferred +// For server-side, creating per request or using a service role key might be more appropriate +// depending on your RLS policies and security model. +// This example uses the public anon key, assuming RLS is set up for user-specific access. +const supabase = supabaseUrl && supabaseAnonKey ? createClient(supabaseUrl, supabaseAnonKey) : null; + +export async function POST(req: NextRequest) { + if (!supabase) { + return NextResponse.json({ error: 'Supabase client not initialized. Check server logs.' }, { status: 500 }); + } + + try { + const { userId: authUserId } = getAuth(req); + const { chatId, userId: clientUserId } = await req.json(); + + if (!authUserId) { + return NextResponse.json({ error: 'User not authenticated.' }, { status: 401 }); + } + + // Ensure the userId from the client matches the authenticated user's ID + if (authUserId !== clientUserId) { + return NextResponse.json({ error: 'User ID mismatch.' }, { status: 403 }); + } + + if (!chatId) { + return NextResponse.json({ error: 'Chat ID is required.' }, { status: 400 }); + } + + // Query Supabase to check if the chat session exists and belongs to the user + // Assuming you have a 'chats' table with 'id' (for chatId) and 'user_id' columns + const { data, error } = await supabase + .from('chats') + .select('id') + .eq('id', chatId) + .eq('user_id', authUserId) + .single(); // .single() expects one row or null + + if (error && error.code !== 'PGRST116') { // PGRST116: Row to be returned was not found + console.error('Supabase error validating chat session:', error); + return NextResponse.json({ error: 'Error validating session with database.' }, { status: 500 }); + } + + if (data) { + return NextResponse.json({ isValid: true }); + } else { + return NextResponse.json({ isValid: false }); + } + + } catch (error) { + console.error('Error in /api/validate-chat-session:', error); + return NextResponse.json({ error: 'Internal server error.' }, { status: 500 }); + } +} diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 5c5418d0..d262e9c1 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -3,19 +3,54 @@ import { AnimatedAIChat } from "@/components/animated-ai-chat" import { motion } from "framer-motion" import { useRouter, useParams } from "next/navigation" +import { useUser } from "@clerk/nextjs" import { useEffect, useState } from "react" export default function ChatSessionPage() { const router = useRouter() const params = useParams() const chatId = params.id as string + const { user, isLoaded } = useUser() const [isValidSession, setIsValidSession] = useState(true) useEffect(() => { - // In a real app, validate if this chat session exists/belongs to user - // For now, we'll assume all sessions are valid - setIsValidSession(true) - }, [chatId]) + // Ensure user and chatId (represented as 'id' from the route) are available before validation. + // Assumes 'user', 'isLoaded' (from useUser()), 'id' (from useParams() or props), + // and 'setIsValidSession' (from useState()) are defined in the component's scope. + if (!isLoaded || !chatId) { + setIsValidSession(false); + return; + } + + if (!user) { // User is loaded, but not authenticated for this chat + setIsValidSession(false); + // Optionally, redirect to login or show an access denied message here + // e.g., router.push(`/sign-in?redirect_url=${encodeURIComponent(window.location.href)}`); + return; + } + + const validateSession = async () => { + try { + const response = await fetch(`/api/validate-chat-session`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ chatId, userId: user.id }), + }); + if (response.ok) { + const { isValid } = await response.json(); + setIsValidSession(isValid); + } else { + setIsValidSession(false); + console.error('Failed to validate session:', await response.text()); + } + } catch (error) { + setIsValidSession(false); + console.error('Error validating session:', error); + } + }; + + validateSession(); + }, [chatId, user, isLoaded, setIsValidSession]) if (!isValidSession) { return ( diff --git a/bun.lock b/bun.lock index 90c97e81..ce452c5b 100644 --- a/bun.lock +++ b/bun.lock @@ -35,6 +35,7 @@ "@radix-ui/react-toggle": "1.1.1", "@radix-ui/react-toggle-group": "1.1.1", "@radix-ui/react-tooltip": "1.1.6", + "@supabase/supabase-js": "^2.49.10", "ai": "^4.3.16", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", @@ -306,6 +307,20 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="], + "@supabase/auth-js": ["@supabase/auth-js@2.69.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ=="], + + "@supabase/functions-js": ["@supabase/functions-js@2.4.4", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA=="], + + "@supabase/node-fetch": ["@supabase/node-fetch@2.6.15", "", { "dependencies": { "whatwg-url": "^5.0.0" } }, "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ=="], + + "@supabase/postgrest-js": ["@supabase/postgrest-js@1.19.4", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw=="], + + "@supabase/realtime-js": ["@supabase/realtime-js@2.11.10", "", { "dependencies": { "@supabase/node-fetch": "^2.6.13", "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "ws": "^8.18.2" } }, "sha512-SJKVa7EejnuyfImrbzx+HaD9i6T784khuw1zP+MBD7BmJYChegGxYigPzkKX8CK8nGuDntmeSD3fvriaH0EGZA=="], + + "@supabase/storage-js": ["@supabase/storage-js@2.7.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA=="], + + "@supabase/supabase-js": ["@supabase/supabase-js@2.49.10", "", { "dependencies": { "@supabase/auth-js": "2.69.1", "@supabase/functions-js": "2.4.4", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "1.19.4", "@supabase/realtime-js": "2.11.10", "@supabase/storage-js": "2.7.1" } }, "sha512-IRPcIdncuhD2m1eZ2Fkg0S1fq9SXlHfmAetBxPN66kVFtTucR8b01xKuVmKqcIJokB17umMf1bmqyS8yboXGsw=="], + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], @@ -332,12 +347,16 @@ "@types/node": ["@types/node@22.15.29", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ=="], + "@types/phoenix": ["@types/phoenix@1.6.6", "", {}, "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A=="], + "@types/react": ["@types/react@19.1.6", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q=="], "@types/react-dom": ["@types/react-dom@19.1.5", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg=="], "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], @@ -776,6 +795,8 @@ "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -804,8 +825,12 @@ "web-vitals": ["web-vitals@4.2.4", "", {}, "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.1", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "is-plain-object": "^5.0.0", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ=="], + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], @@ -820,6 +845,8 @@ "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + "@supabase/realtime-js/ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], diff --git a/package.json b/package.json index de510cf6..b06e9dc8 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@radix-ui/react-toggle": "1.1.1", "@radix-ui/react-toggle-group": "1.1.1", "@radix-ui/react-tooltip": "1.1.6", + "@supabase/supabase-js": "^2.49.10", "ai": "^4.3.16", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", From 4d2a3968121fd8012c6ef30ba7d3e360bd9bee6c Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 04:18:42 -0500 Subject: [PATCH 03/16] ADding doc string --- app/api/chat/route.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 17816eaf..a7367ffa 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -3,6 +3,14 @@ import { generateOpenRouterResponse, ChatHistory } from "@/lib/openrouter"; import { getSequentialThinkingSteps } from "@/lib/sequential-thinker"; import { systemPrompt as zapDevSystemPrompt } from "@/lib/systemprompt"; + /** + * API route for generating AI chat responses. + * @param req The incoming request, expected to contain a JSON body with the following properties: + * - `messages`: An array of messages in the chat, where each message is an object with `role` and `content` properties. + * - `chatId`: The ID of the chat, used to generate a unique system prompt. + * - `modelId`: The ID of the AI model to use for generating responses, if specified. + * @returns A JSON response containing the generated AI response. + */ export async function POST(req: Request) { try { // Parse request body From bd30aeec96ba33b5e6f441d35735534007814312 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 04:58:32 -0500 Subject: [PATCH 04/16] fixing bullshit --- app/chat/[id]/page.tsx | 47 ++++++++++++++++++------------- app/layout.tsx | 10 +++++-- app/page.tsx | 63 ++++++++++++++++++++++++++---------------- middleware.ts | 19 ++++++++++++- 4 files changed, 92 insertions(+), 47 deletions(-) diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index d262e9c1..17141700 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -3,7 +3,7 @@ import { AnimatedAIChat } from "@/components/animated-ai-chat" import { motion } from "framer-motion" import { useRouter, useParams } from "next/navigation" -import { useUser } from "@clerk/nextjs" +import { useUser, SignInButton, SignUpButton, UserButton, SignedIn, SignedOut } from "@clerk/nextjs" import { useEffect, useState } from "react" export default function ChatSessionPage() { @@ -15,8 +15,6 @@ export default function ChatSessionPage() { useEffect(() => { // Ensure user and chatId (represented as 'id' from the route) are available before validation. - // Assumes 'user', 'isLoaded' (from useUser()), 'id' (from useParams() or props), - // and 'setIsValidSession' (from useState()) are defined in the component's scope. if (!isLoaded || !chatId) { setIsValidSession(false); return; @@ -77,22 +75,33 @@ export default function ChatSessionPage() { animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.5 }} > - router.push("/auth?tab=login")} - className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors text-sm font-medium" - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - Sign In - - router.push("/auth?tab=signup")} - className="px-4 py-2 rounded-lg bg-gradient-to-r from-[#6C52A0] to-[#A0527C] hover:from-[#7C62B0] hover:to-[#B0627C] transition-all text-sm font-medium" - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - Sign Up - + +
+ + + Sign In + + +
+
+ + + Sign Up + + +
+
+ + + {/* ZapDev branding */} diff --git a/app/layout.tsx b/app/layout.tsx index ca378e86..b568c9a6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -32,14 +32,18 @@ export default function RootLayout({ return ( - +
- - + +
diff --git a/app/page.tsx b/app/page.tsx index 640f14a3..e3adfabe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,7 +4,7 @@ import { useRouter } from "next/navigation" import { motion } from "framer-motion"; import dynamic from 'next/dynamic'; import Hero from "@/components/hero"; -import { SignInButton, SignUpButton } from "@clerk/nextjs"; +import { SignInButton, SignUpButton, SignedIn, SignedOut, UserButton } from "@clerk/nextjs"; import FinalCTA from "@/components/final-cta"; const FeaturesShowcase = dynamic(() => import('@/components/features-showcase'), { loading: () =>
}); @@ -25,28 +25,43 @@ export default function Home() { animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.5 }} > -
- - - Sign In - - -
-
- - - Sign Up - - -
+ +
+ + + Sign In + + +
+
+ + + Sign Up + + +
+
+ +
+ router.push("/chat")} + className="px-4 py-2 rounded-lg bg-gradient-to-r from-[#6C52A0] to-[#A0527C] hover:from-[#7C62B0] hover:to-[#B0627C] transition-all text-sm font-medium" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + Go to Chat + + +
+
{/* Try it now button that navigates to chat */} @@ -70,7 +85,7 @@ export default function Home() { - {/* Homepage sections (commented out for debugging) */} + {/* Homepage sections */} diff --git a/middleware.ts b/middleware.ts index b6e0c7fb..041450e2 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,4 +1,5 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; +import { NextResponse } from 'next/server'; // Define routes that require authentication const isProtectedRoute = createRouteMatcher([ @@ -6,11 +7,27 @@ const isProtectedRoute = createRouteMatcher([ // Add any other routes you want to protect here ]); -export default clerkMiddleware((auth, req) => { +const publicRoutes = [ + '/', + '/sign-in(.*)', + '/sign-up(.*)', +]; + +export default clerkMiddleware(async (auth, req) => { // For routes that require authentication, protect them if (isProtectedRoute(req)) { auth.protect(); } + + // Handle auth redirection + const { userId } = await auth(); + const isHomePage = req.nextUrl.pathname === '/'; + + // If user is signed in and on the homepage, redirect to chat + if (userId && isHomePage) { + const url = new URL('/chat', req.url); + return NextResponse.redirect(url); + } }); export const config = { From 0ff816f5474fb716547a8adc6bbac4d43cf92a19 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 04:58:54 -0500 Subject: [PATCH 05/16] Remove SignIn and SignUp buttons from layout, retaining UserButton for signed-in users. --- app/layout.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index b568c9a6..ec0c903c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,10 +3,7 @@ import { Inter } from 'next/font/google' import { ThemeProvider } from '@/components/theme-provider'; import { ClerkProvider, - SignInButton, - SignUpButton, SignedIn, - SignedOut, UserButton, } from '@clerk/nextjs'; import { PostHogProvider } from '@/components/PostHogProvider' @@ -40,12 +37,6 @@ export default function RootLayout({
- -
- - -
-
From 0631253446b8c0219833c3100e322f596dcfcb8d Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:00:19 -0500 Subject: [PATCH 06/16] Refactor ChatSessionPage to simplify session validation and remove unused authentication buttons. Adjusted layout for session ID indicator. --- app/chat/[id]/page.tsx | 82 ++++++------------------------------------ 1 file changed, 11 insertions(+), 71 deletions(-) diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 17141700..4cff0b05 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -3,7 +3,7 @@ import { AnimatedAIChat } from "@/components/animated-ai-chat" import { motion } from "framer-motion" import { useRouter, useParams } from "next/navigation" -import { useUser, SignInButton, SignUpButton, UserButton, SignedIn, SignedOut } from "@clerk/nextjs" +import { useUser, UserButton, SignedIn } from "@clerk/nextjs" import { useEffect, useState } from "react" export default function ChatSessionPage() { @@ -14,91 +14,31 @@ export default function ChatSessionPage() { const [isValidSession, setIsValidSession] = useState(true) useEffect(() => { - // Ensure user and chatId (represented as 'id' from the route) are available before validation. + // Ensure user and chatId are available if (!isLoaded || !chatId) { - setIsValidSession(false); return; } - if (!user) { // User is loaded, but not authenticated for this chat - setIsValidSession(false); - // Optionally, redirect to login or show an access denied message here - // e.g., router.push(`/sign-in?redirect_url=${encodeURIComponent(window.location.href)}`); + if (!user) { + router.push('/'); return; } - const validateSession = async () => { - try { - const response = await fetch(`/api/validate-chat-session`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ chatId, userId: user.id }), - }); - if (response.ok) { - const { isValid } = await response.json(); - setIsValidSession(isValid); - } else { - setIsValidSession(false); - console.error('Failed to validate session:', await response.text()); - } - } catch (error) { - setIsValidSession(false); - console.error('Error validating session:', error); - } - }; - - validateSession(); - }, [chatId, user, isLoaded, setIsValidSession]) - - if (!isValidSession) { - return ( -
-
-

Chat session not found

- -
-
- ) - } + // When a new chat is created, we'll assume it's valid + // This skips validation for newly created chat sessions + // The chat system will create the database record when needed + + }, [chatId, user, isLoaded, router]) return (
- {/* Auth buttons */} + {/* Auth button */} - -
- - - Sign In - - -
-
- - - Sign Up - - -
-
@@ -139,7 +79,7 @@ export default function ChatSessionPage() { {/* Session ID indicator */} Date: Thu, 5 Jun 2025 05:00:56 -0500 Subject: [PATCH 07/16] Remove SignIn and SignUp buttons from Home page, keeping only UserButton for signed-in users. --- app/page.tsx | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index e3adfabe..9d419ffb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,7 +4,7 @@ import { useRouter } from "next/navigation" import { motion } from "framer-motion"; import dynamic from 'next/dynamic'; import Hero from "@/components/hero"; -import { SignInButton, SignUpButton, SignedIn, SignedOut, UserButton } from "@clerk/nextjs"; +import { SignedIn, UserButton } from "@clerk/nextjs"; import FinalCTA from "@/components/final-cta"; const FeaturesShowcase = dynamic(() => import('@/components/features-showcase'), { loading: () =>
}); @@ -25,30 +25,6 @@ export default function Home() { animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.5 }} > - -
- - - Sign In - - -
-
- - - Sign Up - - -
-
Date: Thu, 5 Jun 2025 05:01:27 -0500 Subject: [PATCH 08/16] Update middleware to await authentication protection for protected routes --- middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.ts b/middleware.ts index 041450e2..26d5e60b 100644 --- a/middleware.ts +++ b/middleware.ts @@ -16,7 +16,7 @@ const publicRoutes = [ export default clerkMiddleware(async (auth, req) => { // For routes that require authentication, protect them if (isProtectedRoute(req)) { - auth.protect(); + await auth.protect(); } // Handle auth redirection From a1fcdba72728f0fc307d3e4c64991c4bde9c3364 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:01:55 -0500 Subject: [PATCH 09/16] Refactor Supabase client initialization in validate-chat-session API to improve error handling and ensure application functionality even when database is unavailable. --- app/api/validate-chat-session/route.ts | 98 +++++++++++++++++--------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/app/api/validate-chat-session/route.ts b/app/api/validate-chat-session/route.ts index 949960cc..afab33c0 100644 --- a/app/api/validate-chat-session/route.ts +++ b/app/api/validate-chat-session/route.ts @@ -1,30 +1,27 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuth } from '@clerk/nextjs/server'; -import { createClient } from '@supabase/supabase-js'; // Standard Supabase import +import { createClient } from '@supabase/supabase-js'; // Initialize Supabase client -// Ensure your Supabase URL and anon key are set in environment variables const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; -if (!supabaseUrl) { - console.error('Error: NEXT_PUBLIC_SUPABASE_URL is not set.'); - // Potentially throw an error or handle this case as appropriate for your app -} -if (!supabaseAnonKey) { - console.error('Error: NEXT_PUBLIC_SUPABASE_ANON_KEY is not set.'); - // Potentially throw an error or handle this case as appropriate for your app -} - -// Create a new Supabase client instance for each request or reuse a global one if preferred -// For server-side, creating per request or using a service role key might be more appropriate -// depending on your RLS policies and security model. -// This example uses the public anon key, assuming RLS is set up for user-specific access. -const supabase = supabaseUrl && supabaseAnonKey ? createClient(supabaseUrl, supabaseAnonKey) : null; +// Helper function to create Supabase client +const getSupabase = () => { + if (!supabaseUrl || !supabaseAnonKey) { + console.error('Missing Supabase environment variables'); + return null; + } + return createClient(supabaseUrl, supabaseAnonKey); +}; export async function POST(req: NextRequest) { + const supabase = getSupabase(); if (!supabase) { - return NextResponse.json({ error: 'Supabase client not initialized. Check server logs.' }, { status: 500 }); + // If Supabase client can't be initialized, we'll still validate the user + // and return a successful response - this allows the application to work + // even when database is not available + return NextResponse.json({ isValid: true }); } try { @@ -44,28 +41,59 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: 'Chat ID is required.' }, { status: 400 }); } - // Query Supabase to check if the chat session exists and belongs to the user - // Assuming you have a 'chats' table with 'id' (for chatId) and 'user_id' columns - const { data, error } = await supabase - .from('chats') - .select('id') - .eq('id', chatId) - .eq('user_id', authUserId) - .single(); // .single() expects one row or null + try { + // Check if chat exists + const { data: existingChat, error: selectError } = await supabase + .from('chats') + .select('id') + .eq('id', chatId) + .maybeSingle(); - if (error && error.code !== 'PGRST116') { // PGRST116: Row to be returned was not found - console.error('Supabase error validating chat session:', error); - return NextResponse.json({ error: 'Error validating session with database.' }, { status: 500 }); - } + if (selectError) { + console.error('Error checking chat:', selectError); + // Return valid to allow chat to proceed even with DB errors + return NextResponse.json({ isValid: true }); + } + + // If chat doesn't exist, we'll create it + if (!existingChat) { + // We need to first get the user's internal UUID from the users table + const { data: userData, error: userError } = await supabase + .from('users') + .select('id') + .eq('clerk_user_id', authUserId) + .maybeSingle(); + + if (userError || !userData) { + console.warn('User not found in database, returning valid=true anyway'); + return NextResponse.json({ isValid: true }); + } + + // Create chat record + const { error: insertError } = await supabase + .from('chats') + .insert({ + id: chatId, + user_id: userData.id, + title: 'New Chat' + }); + + if (insertError) { + console.error('Error creating chat:', insertError); + // Still return valid=true to allow application to function + return NextResponse.json({ isValid: true }); + } + } - if (data) { return NextResponse.json({ isValid: true }); - } else { - return NextResponse.json({ isValid: false }); + } catch (dbError) { + console.error('Database error:', dbError); + // Return valid=true to allow the application to function even with DB errors + return NextResponse.json({ isValid: true }); } - } catch (error) { - console.error('Error in /api/validate-chat-session:', error); - return NextResponse.json({ error: 'Internal server error.' }, { status: 500 }); + console.error('General error in /api/validate-chat-session:', error); + // For general errors, we'll still allow the chat to proceed + return NextResponse.json({ isValid: true }); } } From 20499bcfca892ab7955962772a1579cc29c881d7 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:11:05 -0500 Subject: [PATCH 10/16] Add split screen functionality to AnimatedAIChat component, enhancing user experience with a preview panel. Improved error handling for API responses and refined layout transitions. --- components/animated-ai-chat.tsx | 105 ++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/components/animated-ai-chat.tsx b/components/animated-ai-chat.tsx index d15b0bd6..765c983a 100644 --- a/components/animated-ai-chat.tsx +++ b/components/animated-ai-chat.tsx @@ -128,6 +128,7 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { const [inputFocused, setInputFocused] = useState(false) const commandPaletteRef = useRef(null) const [messages, setMessages] = useState([]) + const [isSplitScreen, setIsSplitScreen] = useState(false) const commandSuggestions: CommandSuggestion[] = [ { @@ -248,6 +249,9 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { setMessages(prev => [...prev, newUserMessage]); setIsTyping(true); + // Enable split screen mode when message is sent + setIsSplitScreen(true); + try { // Call API to get AI response const response = await fetch("/api/chat", { @@ -262,7 +266,8 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { }); if (!response.ok) { - throw new Error("Failed to get response"); + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `Request failed with status ${response.status}`); } const data = await response.json(); @@ -277,10 +282,10 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { } catch (error) { console.error("Error getting AI response:", error); - // Add error message to chat + // Add error message to chat with more specific info const errorMessage: Message = { role: "model", - content: "Sorry, I encountered an error while processing your request." + content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : "Failed to fetch response"}` }; setMessages(prev => [...prev, errorMessage]); @@ -315,39 +320,55 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) {
-
+ +
-
- -

- How can I help today? -

- - - - Type a command or ask a question - -
+ + {!isSplitScreen && ( + + +

+ How can I help today? +

+ + + + Type a command or ask a question + +
+ )} +
{/* Display messages */} {messages.length > 0 && ( @@ -566,6 +587,28 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { ))}
+ + {/* Preview panel */} + {isSplitScreen && ( + +
+
+
+ +
+

Preview Panel

+

+ Visual outputs and generated content will appear here +

+
+
+
+ )}
From f04b20350bd9495c0fdecee2e5ee45e384428bb4 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:11:36 -0500 Subject: [PATCH 11/16] Update import path for Message component in AnimatedAIChat to use openrouter library --- components/animated-ai-chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/animated-ai-chat.tsx b/components/animated-ai-chat.tsx index 765c983a..558bbf97 100644 --- a/components/animated-ai-chat.tsx +++ b/components/animated-ai-chat.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils" import { Paperclip, Command, SendIcon, XIcon, LoaderIcon, Sparkles, ImageIcon, Figma, MonitorIcon } from "lucide-react" import { motion, AnimatePresence } from "framer-motion" import * as React from "react" -import { Message } from "@/lib/gemini" +import { Message } from "@/lib/openrouter" interface UseAutoResizeTextareaProps { minHeight: number From 6c975b2fa480bd3352c493c6aae49015f5027da7 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:18:49 -0500 Subject: [PATCH 12/16] Rewrite --- .gitignore | 4 + bun.lock | 40 +++++++ lib/azure-ai.ts | 96 +++++++++++++++++ lib/systemprompt.ts | 252 +++++--------------------------------------- package.json | 4 + 5 files changed, 171 insertions(+), 225 deletions(-) create mode 100644 lib/azure-ai.ts diff --git a/.gitignore b/.gitignore index f6b8c857..7e261396 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ next-env.d.ts # Sytem Prompt example systemprompt-example.txt + +# Ehnhance Prompt example + +enhance-prompt.txt diff --git a/bun.lock b/bun.lock index ce452c5b..4dd02ff2 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,10 @@ "": { "name": "my-v0-project", "dependencies": { + "@ai-sdk/azure": "^1.3.23", + "@azure-rest/ai-inference": "^1.0.0-beta.6", + "@azure/core-auth": "^1.9.0", + "@azure/core-sse": "^2.2.0", "@clerk/nextjs": "^6.12.6", "@emotion/is-prop-valid": "latest", "@hookform/resolvers": "^3.9.1", @@ -77,6 +81,10 @@ }, }, "packages": { + "@ai-sdk/azure": ["@ai-sdk/azure@1.3.23", "", { "dependencies": { "@ai-sdk/openai": "1.3.22", "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-vpsaPtU24RBVk/IMM5UylR/N4RtAuL2NZLWc7LJ3tvMTHu6pI46a7w+1qIwR3F6yO9ehWR8qvfLaBefJNFxaVw=="], + + "@ai-sdk/openai": ["@ai-sdk/openai@1.3.22", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw=="], + "@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], @@ -87,6 +95,26 @@ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + "@azure-rest/ai-inference": ["@azure-rest/ai-inference@1.0.0-beta.6", "", { "dependencies": { "@azure-rest/core-client": "^2.1.0", "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-lro": "^2.7.2", "@azure/core-rest-pipeline": "^1.18.2", "@azure/core-tracing": "^1.2.0", "@azure/logger": "^1.1.4", "tslib": "^2.8.1" } }, "sha512-j5FrJDTHu2P2+zwFVe5j2edasOIhqkFj+VkDjbhGkQuOoIAByF0egRkgs0G1k03HyJ7bOOT9BkRF7MIgr/afhw=="], + + "@azure-rest/core-client": ["@azure-rest/core-client@2.4.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.3.0", "@azure/core-rest-pipeline": "^1.5.0", "@azure/core-tracing": "^1.0.1", "@typespec/ts-http-runtime": "^0.2.2", "tslib": "^2.6.2" } }, "sha512-CjMFBcmnt0YNdRcxSSoZbtZNXudLlicdml7UrPsV03nHiWB+Bq5cu5ctieyaCuRtU7jm7+SOFtiE/g4pBFPKKA=="], + + "@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], + + "@azure/core-auth": ["@azure/core-auth@1.9.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.11.0", "tslib": "^2.6.2" } }, "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw=="], + + "@azure/core-lro": ["@azure/core-lro@2.7.2", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", "tslib": "^2.6.2" } }, "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw=="], + + "@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.20.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.8.0", "@azure/core-tracing": "^1.0.1", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "@typespec/ts-http-runtime": "^0.2.2", "tslib": "^2.6.2" } }, "sha512-ASoP8uqZBS3H/8N8at/XwFr6vYrRP3syTK0EUjDXQy0Y1/AUS+QeIRThKmTNJO2RggvBBxaXDPM7YoIwDGeA0g=="], + + "@azure/core-sse": ["@azure/core-sse@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-6Xg/CeW0jRyMoWt+puw2x6Qqkml3tr76Cn/oA9goIcUXtsi3ngmTwCVbwqkUWfhsOfo4F+78LGgiswSxTHN0sg=="], + + "@azure/core-tracing": ["@azure/core-tracing@1.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg=="], + + "@azure/core-util": ["@azure/core-util@1.12.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@typespec/ts-http-runtime": "^0.2.2", "tslib": "^2.6.2" } }, "sha512-13IyjTQgABPARvG90+N2dXpC+hwp466XCdQXPCRlbWHgd3SJd5Q1VvaBGv6k1BIa4MQm6hAF1UBU1m8QUxV8sQ=="], + + "@azure/logger": ["@azure/logger@1.2.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.2.2", "tslib": "^2.6.2" } }, "sha512-0hKEzLhpw+ZTAfNJyRrn6s+V0nDWzXk9OjBr2TiGIu0OfMr5s2V4FpKLTAK3Ca5r5OKLbf4hkOGDPyiRjie/jA=="], + "@babel/runtime": ["@babel/runtime@7.27.4", "", {}, "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA=="], "@clerk/backend": ["@clerk/backend@2.0.0", "", { "dependencies": { "@clerk/shared": "^3.9.6", "@clerk/types": "^4.60.0", "cookie": "1.0.2", "snakecase-keys": "8.0.1", "tslib": "2.8.1" } }, "sha512-1P4o9L464KVYjkMPaFBKmWo0WpUZsSO+57xjkrKPjW0FOTylYRicKCafoixfRlPMlYWzx7ETmj0ZypmrAZniBA=="], @@ -357,10 +385,14 @@ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.2.2", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w=="], + "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], + "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], + "ai": ["ai@4.3.16", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g=="], "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -461,6 +493,8 @@ "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], @@ -559,6 +593,10 @@ "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "input-otp": ["input-otp@1.4.1", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw=="], "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], @@ -629,6 +667,8 @@ "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], diff --git a/lib/azure-ai.ts b/lib/azure-ai.ts new file mode 100644 index 00000000..cc7a85d9 --- /dev/null +++ b/lib/azure-ai.ts @@ -0,0 +1,96 @@ +import ModelClient, { isUnexpected } from "@azure-rest/ai-inference"; +import { AzureKeyCredential } from "@azure/core-auth"; +import { generateText as generateAIText } from "ai"; + +// Define Message and ChatHistory types +export interface Message { + role: "user" | "model"; + content: string; +} +export type ChatHistory = Message[]; + +// Environment variables +const getGithubToken = () => { + const token = process.env.GITHUB_TOKEN || ""; + if (!token) { + throw new Error("GITHUB_TOKEN environment variable is not set. Please add it to your .env.local file."); + } + return token; +}; + +// Available models from GitHub Marketplace +const modelIds = [ + "deepseek/DeepSeek-R1-0528", + "meta/llama-3-8b-instruct", + "mistralai/mistral-7b-instruct-v0.2", + "xai/grok-3", + "deepseek/deepseek-coder" +]; + +// Azure Client initialization +export const getAzureClient = () => { + return ModelClient( + "https://models.github.ai/inference", + new AzureKeyCredential(getGithubToken()) + ); +}; + +/** + * Converts the chat history to the format expected by the Azure AI client + */ +export const convertChatHistory = ( + chatHistory: ChatHistory, + systemPrompt?: string +) => { + const messages: Array<{role: string, content: string}> = []; + + if (systemPrompt) { + messages.push({ role: "system", content: systemPrompt }); + } + + chatHistory.forEach(msg => { + messages.push({ + role: msg.role === "model" ? "assistant" : msg.role, + content: msg.content, + }); + }); + + return messages; +}; + +/** + * Generates a response using the Azure AI client + */ +export async function generateAzureResponse( + chatHistory: ChatHistory, + systemPrompt?: string, + modelIdFromParam?: string +): Promise { + try { + const client = getAzureClient(); + const selectedModelId = modelIdFromParam || modelIds[0]; + const messages = convertChatHistory(chatHistory, systemPrompt); + + const response = await client.path("/chat/completions").post({ + body: { + messages, + model: selectedModelId, + temperature: 0.7, + max_tokens: 4000 + } + }); + + if (isUnexpected(response)) { + throw new Error(`Azure AI error: ${JSON.stringify(response.body.error)}`); + } + + return response.body.choices[0].message.content; + } catch (error) { + console.error("Error generating response from Azure AI:", error); + let errorMessage = "Sorry, I encountered an error while processing your request."; + if (error instanceof Error) { + errorMessage = `Azure AI Error: ${error.message}`; + } + return errorMessage; + } +} \ No newline at end of file diff --git a/lib/systemprompt.ts b/lib/systemprompt.ts index 35437526..d717141a 100644 --- a/lib/systemprompt.ts +++ b/lib/systemprompt.ts @@ -1,237 +1,39 @@ export const systemPrompt = ` -You are ZapDev, an advanced AI agent specialized in rapid web application development and optimization. You assist users by providing real-time code generation, debugging, and architectural guidance for their projects. You excel at transforming high-level ideas into production-ready code with minimal user effort. Your superpower is the ability to "zap" problems away, providing immediate, elegant solutions to complex coding challenges. +# ZapDev AI Design Assistant -You operate with complete knowledge of the user's project structure and can analyze code, logs, and performance metrics in real-time. You can visualize the application state and help users understand the flow of data through their application. +You are an expert frontend developer specialized in creating beautiful, responsive interfaces with modern best practices. -You MUST ALWAYS address errors exactly as presented - you never ignore or gloss over issues. When an error appears, you analyze it thoroughly, identify the root cause, and provide a complete solution that resolves the specific error. +## AI Design Playbook +When creating or modifying UI elements, follow these design principles: -Not every interaction requires code generation - you're equally adept at explaining concepts, providing guidance on best practices, or discussing architectural decisions. When coding is needed, you create efficient, maintainable, and scalable solutions following modern development standards. +### Start with Real Inputs +- Use specific product names, audience details, and real use cases to generate better structure, spacing, and tone. +- Always provide concrete examples rather than vague descriptions. -You follow these key principles: +### Design Fundamentals +- LAYOUT: Use grid-based designs with proper spacing (40px padding around content blocks). +- TYPOGRAPHY: Stick to 1-2 typefaces with clear hierarchy (large bold headlines, medium subheaders, small base text). +- COLOR: Define consistent palettes with proper contrast. +- COMPONENTS: Use rounded buttons, subtle shadows, and consistent hover states. -1. Package Management: - - EXCLUSIVELY use Bun as the package manager for all projects - - Always use bun install for installing dependencies, never npm or yarn - - Use bun add [package] for adding packages, not npm install - - Use bun remove [package] for removing packages - - Leverage bun.lockb for dependency locking - - Utilize Bun's built-in testing with bun test - - Use bunx instead of npx for executing packages - - Take advantage of Bun's optimized bundling capabilities +### Visual Hierarchy +- Structure content in clear sections: top nav bar, hero headlines, feature blocks, etc. +- Chain style modifiers: blurred backgrounds, card foregrounds, minimal icons, smooth transitions. -# PLANNING AND ORGANIZATION +### Polish for Production +- Ensure consistent visual rhythm with proper paddings and margins. +- Replace placeholder content with concise, meaningful text. +- Check mobile responsiveness and optimize images. +- Ensure animations are smooth and intentional. -Before implementing any significant feature or change, you MUST create a structured plan in a markdown file called plan.md. This plan should include: +When generating code: +1. Use semantic HTML elements +2. Implement responsive design principles +3. Follow modern CSS best practices (including flexbox/grid) +4. Ensure accessibility compliance +5. Create clean, well-structured components -1. Overview of the requirement -2. Architecture and design decisions -3. Component breakdown -4. Data flow diagrams when applicable -5. Implementation steps with clear milestones -6. Testing strategy -7. Potential challenges and mitigations - -This planning approach ensures organized development and clear communication with the user. - -# CONTAINERIZATION APPROACH - -You prioritize small, efficient web containers for deployments. Your containerization strategy includes: - -- Using minimal base images (Alpine or Distroless when possible) -- Multi-stage builds to minimize container size -- Implementing layer caching strategies -- Configuring proper health checks -- Setting up appropriate security contexts -- Using environment variables for configuration -- Optimizing Dockerfile for Bun applications specifically - - # CORE PRINCIPLES - -1. Code Excellence: - - Write clean, self-documenting code with appropriate comments - - Follow type-safe practices with TypeScript - - Create modular, reusable components - - Optimize for performance, accessibility, and SEO - - Implement comprehensive error handling and logging - - Use consistent naming conventions - - Apply the principle of least surprise - - Practice defensive programming - - Write idiomatic code for the chosen framework - -2. Architecture Mastery: - - Design scalable application structures - - Create clear separation of concerns - - Implement appropriate design patterns - - Follow domain-driven design principles - - Balance flexibility with simplicity - - Create bounded contexts - - Implement hexagonal architecture when appropriate - - Design for extensibility without overengineering - - Apply SOLID principles consistently - -3. Frontend Expertise: - - Build responsive, mobile-first interfaces - - Create accessible UIs following WCAG guidelines - - Implement modern animation and transition effects - - Optimize rendering performance - - Use CSS-in-JS or utility frameworks efficiently - - Implement proper form validation - - Create intuitive navigation patterns - - Design for progressive enhancement - - Optimize for Core Web Vitals - -4. Backend Intelligence: - - Design efficient database schemas - - Create secure, RESTful APIs - - Implement authentication and authorization - - Optimize query performance - - Handle file uploads and processing - - Set up proper rate limiting - - Implement efficient caching strategies - - Design for horizontal scaling - - Create comprehensive logging and monitoring - -5. DevOps Integration: - - Set up CI/CD pipelines - - Configure containerization with Docker - - Implement environment-specific configurations - - Create deployment strategies - - Monitor application performance - - Set up infrastructure as code - - Implement blue/green deployments - - Configure proper logging and alerting - - Design for zero-downtime deployments - -6. Testing Excellence: - - Write comprehensive unit and integration tests - - Implement end-to-end testing - - Create test fixtures and mocks - - Follow test-driven development when appropriate - - Ensure high test coverage for critical paths - - Implement property-based testing - - Create snapshot tests for UI components - - Set up performance testing - - Implement visual regression testing - -7. Security Focus: - - Implement input validation and sanitization - - Protect against common vulnerabilities (XSS, CSRF, etc.) - - Set up proper authentication flows - - Manage secrets and credentials securely - - Follow OWASP security guidelines - - Implement proper CORS policies - - Set up CSP headers - - Configure security headers - - Perform regular security audits - -# BUN-SPECIFIC OPTIMIZATIONS - -You actively leverage Bun's unique capabilities: - -- Use Bun's built-in bundler instead of webpack/vite when possible -- Leverage Bun's native fetch implementation -- Utilize Bun's file system operations for better performance -- Take advantage of Bun's SQLite integration -- Use Bun's hot module reloading -- Leverage Bun's built-in web server capabilities -- Utilize Bun's enhanced JavaScript runtime features -- Implement Bun's optimized TypeScript handling - -# TECHNOLOGY EXPERTISE - -You are proficient with modern web technologies including but not limited to: - -- Frontend: React, Next.js, Vue, Svelte, Angular -- State Management: Redux, Zustand, Jotai, XState -- Styling: Tailwind CSS, styled-components, emotion -- API: REST, GraphQL, tRPC, gRPC -- Backend: Node.js, Express, NestJS, Fastify -- Databases: PostgreSQL, MongoDB, Redis, SQLite -- Authentication: Auth.js, Clerk, Supabase Auth, Firebase Auth -- Deployment: Vercel, Netlify, AWS, GCP, Azure -- Testing: Jest, Vitest, Cypress, Playwright -- Build Tools: Bun, Vite, Turbopack, esbuild - -# FILE OPERATIONS - -You understand that you can modify files through specific commands: - -- for creating or updating files with complete contents -- for renaming files from original path to new path -- for removing files from the project -- for installing packages with bun as the package manager - -# CODE BLOCK STRUCTURE - -- to wrap all code changes and technical explanations -- to show your analytical process (optional) -- to display error messages when they occur -- to confirm successful operations -- to outline implementation strategy -- to directly address and resolve presented errors - -# RESPONSE FORMAT - -- for structuring your complete responses -- for referencing user input -- for your responses -- for providing code examples -- for sharing best practices -- for showing debugging information -- for providing relevant documentation -- for tracking file structure -- for key instructions -- for showing code changes - -# IMPLEMENTATION WORKFLOW - -When implementing solutions, you follow this detailed workflow: - -1. Analyze the user's request and current project state - - Understand the requirements completely - - Identify potential edge cases - - Consider the broader context of the application - -2. Formulate a clear implementation strategy - - Create a plan.md file outlining the approach - - Consider alternative approaches - - Justify design decisions - -3. Break down complex tasks into manageable steps - - Create task dependencies - - Estimate complexity - - Identify potential roadblocks - -4. Execute code changes with clear explanations - - Document what each change accomplishes - - Explain why specific approaches were chosen - - Highlight any tradeoffs made - -5. Verify the changes work as expected - - Check for errors or warnings - - Ensure the code meets all requirements - - Validate performance considerations - -6. Provide guidance on testing and next steps - - Suggest test cases - - Recommend areas for future improvement - - Document any technical debt created - -# ERROR HANDLING APPROACH - -When you encounter errors, you: -1. Show the complete error message -2. Explain what the error means in plain language -3. Identify the root cause -4. Provide a specific, targeted fix -5. Explain how to prevent similar errors in the future -6. Include any relevant documentation or resources - -You adapt your communication style based on the user's technical expertise, providing more detailed explanations for beginners and more concise, technical responses for experienced developers. - -You stay current with the latest web development trends and best practices, always recommending modern, efficient approaches while maintaining backward compatibility when needed. - -Your ultimate goal is to empower users to build high-quality web applications quickly, with code they understand and can maintain long-term. +Provide code that is ready to deploy - fully styled, responsive, and following best practices. `; export default systemPrompt; \ No newline at end of file diff --git a/package.json b/package.json index b06e9dc8..4bcb0a55 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,10 @@ "lint": "next lint" }, "dependencies": { + "@ai-sdk/azure": "^1.3.23", + "@azure-rest/ai-inference": "^1.0.0-beta.6", + "@azure/core-auth": "^1.9.0", + "@azure/core-sse": "^2.2.0", "@clerk/nextjs": "^6.12.6", "@emotion/is-prop-valid": "latest", "@hookform/resolvers": "^3.9.1", From e218e360d8a31b136c53ec9c307dea9f319e231a Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:35:19 -0500 Subject: [PATCH 13/16] Refactor middleware to remove automatic redirection from home page to chat, streamlining authentication flow. Delete unused Azure AI client code and enhance system prompt with detailed guidelines for development practices. --- lib/azure-ai.ts | 96 ----------------- lib/systemprompt.ts | 252 +++++++++++++++++++++++++++++++++++++++----- middleware.ts | 12 +-- 3 files changed, 227 insertions(+), 133 deletions(-) delete mode 100644 lib/azure-ai.ts diff --git a/lib/azure-ai.ts b/lib/azure-ai.ts deleted file mode 100644 index cc7a85d9..00000000 --- a/lib/azure-ai.ts +++ /dev/null @@ -1,96 +0,0 @@ -import ModelClient, { isUnexpected } from "@azure-rest/ai-inference"; -import { AzureKeyCredential } from "@azure/core-auth"; -import { generateText as generateAIText } from "ai"; - -// Define Message and ChatHistory types -export interface Message { - role: "user" | "model"; - content: string; -} -export type ChatHistory = Message[]; - -// Environment variables -const getGithubToken = () => { - const token = process.env.GITHUB_TOKEN || ""; - if (!token) { - throw new Error("GITHUB_TOKEN environment variable is not set. Please add it to your .env.local file."); - } - return token; -}; - -// Available models from GitHub Marketplace -const modelIds = [ - "deepseek/DeepSeek-R1-0528", - "meta/llama-3-8b-instruct", - "mistralai/mistral-7b-instruct-v0.2", - "xai/grok-3", - "deepseek/deepseek-coder" -]; - -// Azure Client initialization -export const getAzureClient = () => { - return ModelClient( - "https://models.github.ai/inference", - new AzureKeyCredential(getGithubToken()) - ); -}; - -/** - * Converts the chat history to the format expected by the Azure AI client - */ -export const convertChatHistory = ( - chatHistory: ChatHistory, - systemPrompt?: string -) => { - const messages: Array<{role: string, content: string}> = []; - - if (systemPrompt) { - messages.push({ role: "system", content: systemPrompt }); - } - - chatHistory.forEach(msg => { - messages.push({ - role: msg.role === "model" ? "assistant" : msg.role, - content: msg.content, - }); - }); - - return messages; -}; - -/** - * Generates a response using the Azure AI client - */ -export async function generateAzureResponse( - chatHistory: ChatHistory, - systemPrompt?: string, - modelIdFromParam?: string -): Promise { - try { - const client = getAzureClient(); - const selectedModelId = modelIdFromParam || modelIds[0]; - const messages = convertChatHistory(chatHistory, systemPrompt); - - const response = await client.path("/chat/completions").post({ - body: { - messages, - model: selectedModelId, - temperature: 0.7, - max_tokens: 4000 - } - }); - - if (isUnexpected(response)) { - throw new Error(`Azure AI error: ${JSON.stringify(response.body.error)}`); - } - - return response.body.choices[0].message.content; - } catch (error) { - console.error("Error generating response from Azure AI:", error); - let errorMessage = "Sorry, I encountered an error while processing your request."; - if (error instanceof Error) { - errorMessage = `Azure AI Error: ${error.message}`; - } - return errorMessage; - } -} \ No newline at end of file diff --git a/lib/systemprompt.ts b/lib/systemprompt.ts index d717141a..35437526 100644 --- a/lib/systemprompt.ts +++ b/lib/systemprompt.ts @@ -1,39 +1,237 @@ export const systemPrompt = ` -# ZapDev AI Design Assistant +You are ZapDev, an advanced AI agent specialized in rapid web application development and optimization. You assist users by providing real-time code generation, debugging, and architectural guidance for their projects. You excel at transforming high-level ideas into production-ready code with minimal user effort. Your superpower is the ability to "zap" problems away, providing immediate, elegant solutions to complex coding challenges. -You are an expert frontend developer specialized in creating beautiful, responsive interfaces with modern best practices. +You operate with complete knowledge of the user's project structure and can analyze code, logs, and performance metrics in real-time. You can visualize the application state and help users understand the flow of data through their application. -## AI Design Playbook -When creating or modifying UI elements, follow these design principles: +You MUST ALWAYS address errors exactly as presented - you never ignore or gloss over issues. When an error appears, you analyze it thoroughly, identify the root cause, and provide a complete solution that resolves the specific error. -### Start with Real Inputs -- Use specific product names, audience details, and real use cases to generate better structure, spacing, and tone. -- Always provide concrete examples rather than vague descriptions. +Not every interaction requires code generation - you're equally adept at explaining concepts, providing guidance on best practices, or discussing architectural decisions. When coding is needed, you create efficient, maintainable, and scalable solutions following modern development standards. -### Design Fundamentals -- LAYOUT: Use grid-based designs with proper spacing (40px padding around content blocks). -- TYPOGRAPHY: Stick to 1-2 typefaces with clear hierarchy (large bold headlines, medium subheaders, small base text). -- COLOR: Define consistent palettes with proper contrast. -- COMPONENTS: Use rounded buttons, subtle shadows, and consistent hover states. +You follow these key principles: -### Visual Hierarchy -- Structure content in clear sections: top nav bar, hero headlines, feature blocks, etc. -- Chain style modifiers: blurred backgrounds, card foregrounds, minimal icons, smooth transitions. +1. Package Management: + - EXCLUSIVELY use Bun as the package manager for all projects + - Always use bun install for installing dependencies, never npm or yarn + - Use bun add [package] for adding packages, not npm install + - Use bun remove [package] for removing packages + - Leverage bun.lockb for dependency locking + - Utilize Bun's built-in testing with bun test + - Use bunx instead of npx for executing packages + - Take advantage of Bun's optimized bundling capabilities -### Polish for Production -- Ensure consistent visual rhythm with proper paddings and margins. -- Replace placeholder content with concise, meaningful text. -- Check mobile responsiveness and optimize images. -- Ensure animations are smooth and intentional. +# PLANNING AND ORGANIZATION -When generating code: -1. Use semantic HTML elements -2. Implement responsive design principles -3. Follow modern CSS best practices (including flexbox/grid) -4. Ensure accessibility compliance -5. Create clean, well-structured components +Before implementing any significant feature or change, you MUST create a structured plan in a markdown file called plan.md. This plan should include: -Provide code that is ready to deploy - fully styled, responsive, and following best practices. +1. Overview of the requirement +2. Architecture and design decisions +3. Component breakdown +4. Data flow diagrams when applicable +5. Implementation steps with clear milestones +6. Testing strategy +7. Potential challenges and mitigations + +This planning approach ensures organized development and clear communication with the user. + +# CONTAINERIZATION APPROACH + +You prioritize small, efficient web containers for deployments. Your containerization strategy includes: + +- Using minimal base images (Alpine or Distroless when possible) +- Multi-stage builds to minimize container size +- Implementing layer caching strategies +- Configuring proper health checks +- Setting up appropriate security contexts +- Using environment variables for configuration +- Optimizing Dockerfile for Bun applications specifically + + # CORE PRINCIPLES + +1. Code Excellence: + - Write clean, self-documenting code with appropriate comments + - Follow type-safe practices with TypeScript + - Create modular, reusable components + - Optimize for performance, accessibility, and SEO + - Implement comprehensive error handling and logging + - Use consistent naming conventions + - Apply the principle of least surprise + - Practice defensive programming + - Write idiomatic code for the chosen framework + +2. Architecture Mastery: + - Design scalable application structures + - Create clear separation of concerns + - Implement appropriate design patterns + - Follow domain-driven design principles + - Balance flexibility with simplicity + - Create bounded contexts + - Implement hexagonal architecture when appropriate + - Design for extensibility without overengineering + - Apply SOLID principles consistently + +3. Frontend Expertise: + - Build responsive, mobile-first interfaces + - Create accessible UIs following WCAG guidelines + - Implement modern animation and transition effects + - Optimize rendering performance + - Use CSS-in-JS or utility frameworks efficiently + - Implement proper form validation + - Create intuitive navigation patterns + - Design for progressive enhancement + - Optimize for Core Web Vitals + +4. Backend Intelligence: + - Design efficient database schemas + - Create secure, RESTful APIs + - Implement authentication and authorization + - Optimize query performance + - Handle file uploads and processing + - Set up proper rate limiting + - Implement efficient caching strategies + - Design for horizontal scaling + - Create comprehensive logging and monitoring + +5. DevOps Integration: + - Set up CI/CD pipelines + - Configure containerization with Docker + - Implement environment-specific configurations + - Create deployment strategies + - Monitor application performance + - Set up infrastructure as code + - Implement blue/green deployments + - Configure proper logging and alerting + - Design for zero-downtime deployments + +6. Testing Excellence: + - Write comprehensive unit and integration tests + - Implement end-to-end testing + - Create test fixtures and mocks + - Follow test-driven development when appropriate + - Ensure high test coverage for critical paths + - Implement property-based testing + - Create snapshot tests for UI components + - Set up performance testing + - Implement visual regression testing + +7. Security Focus: + - Implement input validation and sanitization + - Protect against common vulnerabilities (XSS, CSRF, etc.) + - Set up proper authentication flows + - Manage secrets and credentials securely + - Follow OWASP security guidelines + - Implement proper CORS policies + - Set up CSP headers + - Configure security headers + - Perform regular security audits + +# BUN-SPECIFIC OPTIMIZATIONS + +You actively leverage Bun's unique capabilities: + +- Use Bun's built-in bundler instead of webpack/vite when possible +- Leverage Bun's native fetch implementation +- Utilize Bun's file system operations for better performance +- Take advantage of Bun's SQLite integration +- Use Bun's hot module reloading +- Leverage Bun's built-in web server capabilities +- Utilize Bun's enhanced JavaScript runtime features +- Implement Bun's optimized TypeScript handling + +# TECHNOLOGY EXPERTISE + +You are proficient with modern web technologies including but not limited to: + +- Frontend: React, Next.js, Vue, Svelte, Angular +- State Management: Redux, Zustand, Jotai, XState +- Styling: Tailwind CSS, styled-components, emotion +- API: REST, GraphQL, tRPC, gRPC +- Backend: Node.js, Express, NestJS, Fastify +- Databases: PostgreSQL, MongoDB, Redis, SQLite +- Authentication: Auth.js, Clerk, Supabase Auth, Firebase Auth +- Deployment: Vercel, Netlify, AWS, GCP, Azure +- Testing: Jest, Vitest, Cypress, Playwright +- Build Tools: Bun, Vite, Turbopack, esbuild + +# FILE OPERATIONS + +You understand that you can modify files through specific commands: + +- for creating or updating files with complete contents +- for renaming files from original path to new path +- for removing files from the project +- for installing packages with bun as the package manager + +# CODE BLOCK STRUCTURE + +- to wrap all code changes and technical explanations +- to show your analytical process (optional) +- to display error messages when they occur +- to confirm successful operations +- to outline implementation strategy +- to directly address and resolve presented errors + +# RESPONSE FORMAT + +- for structuring your complete responses +- for referencing user input +- for your responses +- for providing code examples +- for sharing best practices +- for showing debugging information +- for providing relevant documentation +- for tracking file structure +- for key instructions +- for showing code changes + +# IMPLEMENTATION WORKFLOW + +When implementing solutions, you follow this detailed workflow: + +1. Analyze the user's request and current project state + - Understand the requirements completely + - Identify potential edge cases + - Consider the broader context of the application + +2. Formulate a clear implementation strategy + - Create a plan.md file outlining the approach + - Consider alternative approaches + - Justify design decisions + +3. Break down complex tasks into manageable steps + - Create task dependencies + - Estimate complexity + - Identify potential roadblocks + +4. Execute code changes with clear explanations + - Document what each change accomplishes + - Explain why specific approaches were chosen + - Highlight any tradeoffs made + +5. Verify the changes work as expected + - Check for errors or warnings + - Ensure the code meets all requirements + - Validate performance considerations + +6. Provide guidance on testing and next steps + - Suggest test cases + - Recommend areas for future improvement + - Document any technical debt created + +# ERROR HANDLING APPROACH + +When you encounter errors, you: +1. Show the complete error message +2. Explain what the error means in plain language +3. Identify the root cause +4. Provide a specific, targeted fix +5. Explain how to prevent similar errors in the future +6. Include any relevant documentation or resources + +You adapt your communication style based on the user's technical expertise, providing more detailed explanations for beginners and more concise, technical responses for experienced developers. + +You stay current with the latest web development trends and best practices, always recommending modern, efficient approaches while maintaining backward compatibility when needed. + +Your ultimate goal is to empower users to build high-quality web applications quickly, with code they understand and can maintain long-term. `; export default systemPrompt; \ No newline at end of file diff --git a/middleware.ts b/middleware.ts index 26d5e60b..befe7e50 100644 --- a/middleware.ts +++ b/middleware.ts @@ -18,16 +18,8 @@ export default clerkMiddleware(async (auth, req) => { if (isProtectedRoute(req)) { await auth.protect(); } - - // Handle auth redirection - const { userId } = await auth(); - const isHomePage = req.nextUrl.pathname === '/'; - - // If user is signed in and on the homepage, redirect to chat - if (userId && isHomePage) { - const url = new URL('/chat', req.url); - return NextResponse.redirect(url); - } + + // No automatic redirection from home page to chat }); export const config = { From 0e59b520e02cd80cd76c82a28944cc2946d71cd5 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:38:32 -0500 Subject: [PATCH 14/16] Enhance middleware to redirect signed-in users from home page to /chat, improving user flow and experience. --- middleware.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/middleware.ts b/middleware.ts index befe7e50..adf338b7 100644 --- a/middleware.ts +++ b/middleware.ts @@ -19,7 +19,13 @@ export default clerkMiddleware(async (auth, req) => { await auth.protect(); } - // No automatic redirection from home page to chat + // Redirect to /chat if user is signed in and on home page + const { userId } = await auth(); + if (req.nextUrl.pathname === '/' && userId) { + return NextResponse.redirect(new URL('/chat', req.url)); + } + + return NextResponse.next(); }); export const config = { From ec09f2103d14cbc3a2fe0070d62b2bd51314f510 Mon Sep 17 00:00:00 2001 From: otdoges Date: Thu, 5 Jun 2025 05:47:04 -0500 Subject: [PATCH 15/16] Implement file upload functionality in chat API, allowing users to send files alongside messages. Enhance chat interface with share button and tooltip for copying chat links. Update message handling to include file information in user messages. Refactor AnimatedAIChat component to manage file attachments and improve user experience. --- app/api/chat/route.ts | 116 +++++- app/chat/[id]/page.tsx | 41 +- components/animated-ai-chat.tsx | 680 +++++++++++++------------------- lib/openrouter.ts | 2 +- 4 files changed, 396 insertions(+), 443 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index a7367ffa..79c459b7 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -2,19 +2,86 @@ import { NextResponse } from "next/server"; import { generateOpenRouterResponse, ChatHistory } from "@/lib/openrouter"; import { getSequentialThinkingSteps } from "@/lib/sequential-thinker"; import { systemPrompt as zapDevSystemPrompt } from "@/lib/systemprompt"; +import { writeFile } from "fs/promises"; +import { join } from "path"; +import { mkdir } from "fs/promises"; +import { existsSync } from "fs"; +import { v4 as uuidv4 } from "uuid"; +import { headers } from "next/headers"; - /** - * API route for generating AI chat responses. - * @param req The incoming request, expected to contain a JSON body with the following properties: - * - `messages`: An array of messages in the chat, where each message is an object with `role` and `content` properties. - * - `chatId`: The ID of the chat, used to generate a unique system prompt. - * - `modelId`: The ID of the AI model to use for generating responses, if specified. - * @returns A JSON response containing the generated AI response. - */ +/** + * API route for generating AI chat responses. + * @param req The incoming request, expected to contain a JSON body with the following properties: + * - `messages`: An array of messages in the chat, where each message is an object with `role` and `content` properties. + * - `chatId`: The ID of the chat, used to generate a unique system prompt. + * - `modelId`: The ID of the AI model to use for generating responses, if specified. + * @returns A JSON response containing the generated AI response. + */ export async function POST(req: Request) { try { - // Parse request body - const { messages, chatId, modelId } = await req.json(); // Optionally allow frontend to specify modelId + // Check if the request is multipart form data + const contentType = req.headers.get("content-type") || ""; + const isFormData = contentType.includes("multipart/form-data"); + + let messages: ChatHistory; + let chatId: string; + let modelId: string | undefined; + let uploadedFiles: { name: string; path: string; type: string }[] = []; + + if (isFormData) { + const formData = await req.formData(); + + // Parse the messages from the form data + const messagesJson = formData.get("messages"); + if (typeof messagesJson !== "string") { + return NextResponse.json( + { error: "Messages not found in form data" }, + { status: 400 } + ); + } + + messages = JSON.parse(messagesJson); + chatId = formData.get("chatId") as string; + modelId = formData.get("modelId") as string | undefined; + + // Handle file uploads + const fileKeys = Array.from(formData.keys()).filter(key => key.startsWith("file")); + + if (fileKeys.length > 0) { + // Ensure uploads directory exists for this chat + const uploadsDir = join(process.cwd(), "public", "uploads", chatId); + if (!existsSync(uploadsDir)) { + await mkdir(uploadsDir, { recursive: true }); + } + + // Process each file + for (const key of fileKeys) { + const file = formData.get(key) as File; + if (!file) continue; + + // Generate a unique filename to prevent collisions + const uniqueFilename = `${uuidv4()}-${file.name}`; + const filePath = join(uploadsDir, uniqueFilename); + + // Convert file to buffer and write to disk + const buffer = Buffer.from(await file.arrayBuffer()); + await writeFile(filePath, buffer); + + // Add file info to the uploadedFiles array + uploadedFiles.push({ + name: file.name, + path: `/uploads/${chatId}/${uniqueFilename}`, + type: file.type + }); + } + } + } else { + // Handle regular JSON requests + const { messages: msgs, chatId: id, modelId: model } = await req.json(); + messages = msgs; + chatId = id; + modelId = model; + } // Validate input if (!Array.isArray(messages) || messages.length === 0) { @@ -24,20 +91,29 @@ export async function POST(req: Request) { ); } -// Generate sequential thinking steps -let thinkingSteps: string | null = null; -try { - thinkingSteps = await getSequentialThinkingSteps(messages[messages.length - 1].content, modelId); -} catch (error) { - console.error("Failed to generate sequential thinking steps:", error); - // Continue without thinking steps -} -// Base system prompt + // Generate sequential thinking steps + let thinkingSteps: string | null = null; + try { + thinkingSteps = await getSequentialThinkingSteps(messages[messages.length - 1].content, modelId); + } catch (error) { + console.error("Failed to generate sequential thinking steps:", error); + // Continue without thinking steps + } + + // Base system prompt let baseSystemPrompt = `You are ZapDev AI, a helpful AI assistant focused on programming and design tasks. Current conversation ID: ${chatId || "unknown"} Today's date: ${new Date().toLocaleDateString()}`; + + // Add file information to the system prompt if files were uploaded + if (uploadedFiles.length > 0) { + baseSystemPrompt += `\n\nUser has uploaded ${uploadedFiles.length} file(s):`; + uploadedFiles.forEach(file => { + baseSystemPrompt += `\n- ${file.name} (${file.type}) - Available at ${file.path}`; + }); + } - // Combine the detailed system prompt, base prompt, and thinking steps + // Combine the detailed system prompt, base prompt, and thinking steps let systemPrompt = `${zapDevSystemPrompt}\n\n## Current Task Context\n${baseSystemPrompt}`; if (thinkingSteps) { diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx index 4cff0b05..acbd7ff8 100644 --- a/app/chat/[id]/page.tsx +++ b/app/chat/[id]/page.tsx @@ -5,6 +5,7 @@ import { motion } from "framer-motion" import { useRouter, useParams } from "next/navigation" import { useUser, UserButton, SignedIn } from "@clerk/nextjs" import { useEffect, useState } from "react" +import { Share2 } from "lucide-react" export default function ChatSessionPage() { const router = useRouter() @@ -12,6 +13,7 @@ export default function ChatSessionPage() { const chatId = params.id as string const { user, isLoaded } = useUser() const [isValidSession, setIsValidSession] = useState(true) + const [showShareTooltip, setShowShareTooltip] = useState(false) useEffect(() => { // Ensure user and chatId are available @@ -23,13 +25,14 @@ export default function ChatSessionPage() { router.push('/'); return; } - - // When a new chat is created, we'll assume it's valid - // This skips validation for newly created chat sessions - // The chat system will create the database record when needed - }, [chatId, user, isLoaded, router]) + const handleShareChat = () => { + navigator.clipboard.writeText(`${window.location.origin}/chat/${chatId}`); + setShowShareTooltip(true); + setTimeout(() => setShowShareTooltip(false), 2000); + } + return (
{/* Auth button */} @@ -77,26 +80,32 @@ export default function ChatSessionPage() { - {/* Session ID indicator */} + {/* Share button */} -
- Chat ID: {chatId} +
+ + {showShareTooltip && ( + + Link copied! + + )}
diff --git a/components/animated-ai-chat.tsx b/components/animated-ai-chat.tsx index 558bbf97..5e4cea49 100644 --- a/components/animated-ai-chat.tsx +++ b/components/animated-ai-chat.tsx @@ -3,7 +3,7 @@ import { useEffect, useRef, useCallback, useTransition } from "react" import { useState } from "react" import { cn } from "@/lib/utils" -import { Paperclip, Command, SendIcon, XIcon, LoaderIcon, Sparkles, ImageIcon, Figma, MonitorIcon } from "lucide-react" +import { Paperclip, Command, SendIcon, XIcon, LoaderIcon, Sparkles, ImageIcon, Figma, MonitorIcon, Share2 } from "lucide-react" import { motion, AnimatePresence } from "framer-motion" import * as React from "react" import { Message } from "@/lib/openrouter" @@ -96,9 +96,7 @@ const Textarea = React.forwardRef( {props.onChange && (
)} @@ -112,9 +110,17 @@ interface AnimatedAIChatProps { chatId?: string; } +interface FileAttachment { + id: string; + name: string; + type: string; + url: string; + file: File; +} + export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { const [value, setValue] = useState("") - const [attachments, setAttachments] = useState([]) + const [attachments, setAttachments] = useState([]) const [isTyping, setIsTyping] = useState(false) const [isPending, startTransition] = useTransition() const [activeSuggestion, setActiveSuggestion] = useState(-1) @@ -125,10 +131,11 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { minHeight: 60, maxHeight: 200, }) + const fileInputRef = useRef(null) const [inputFocused, setInputFocused] = useState(false) const commandPaletteRef = useRef(null) const [messages, setMessages] = useState([]) - const [isSplitScreen, setIsSplitScreen] = useState(false) + const [showShareTooltip, setShowShareTooltip] = useState(false) const commandSuggestions: CommandSuggestion[] = [ { @@ -228,64 +235,77 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { } } else if (e.key === "Enter" && !e.shiftKey) { e.preventDefault() - if (value.trim()) { + if (value.trim() || attachments.length > 0) { handleSendMessage() } } } const handleSendMessage = async () => { - if (value.trim()) { + if (value.trim() || attachments.length > 0) { const userMessage = value.trim(); setValue(""); adjustHeight(true); + // Create message content with file information if there are attachments + let messageContent = userMessage; + + // If we have attachments, add them to the message + if (attachments.length > 0) { + const fileList = attachments.map(attachment => + `[File: ${attachment.name}]` + ).join('\n'); + + messageContent = messageContent ? `${messageContent}\n\n${fileList}` : fileList; + } + // Add user message to chat const newUserMessage: Message = { role: "user", - content: userMessage + content: messageContent }; setMessages(prev => [...prev, newUserMessage]); setIsTyping(true); - // Enable split screen mode when message is sent - setIsSplitScreen(true); - try { - // Call API to get AI response - const response = await fetch("/api/chat", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - messages: [...messages, newUserMessage], - chatId - }), + const formData = new FormData(); + formData.append('messages', JSON.stringify([...messages, newUserMessage])); + formData.append('chatId', chatId); + + // Append each file to the form data + attachments.forEach((attachment, index) => { + formData.append(`file${index}`, attachment.file); }); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error || `Request failed with status ${response.status}`); - } + // Clear attachments after sending + setAttachments([]); - const data = await response.json(); + // Call the API endpoint with FormData to support file uploads + const response = await fetch('/api/chat', { + method: 'POST', + body: formData, + }); - // Add AI response to chat - const aiMessage: Message = { - role: "model", - content: data.response - }; + if (!response.ok) throw new Error('Failed to get response from AI'); - setMessages(prev => [...prev, aiMessage]); - } catch (error) { - console.error("Error getting AI response:", error); + const data = await response.json(); - // Add error message to chat with more specific info + // Add AI response to messages + if (data.response) { + const aiMessage: Message = { + role: "assistant", + content: data.response + }; + + setMessages(prev => [...prev, aiMessage]); + } + } catch (error) { + console.error("Error sending message:", error); + // Add error message to chat const errorMessage: Message = { - role: "model", - content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : "Failed to fetch response"}` + role: "assistant", + content: "Sorry, I encountered an error while processing your request." }; setMessages(prev => [...prev, errorMessage]); @@ -296,356 +316,219 @@ export function AnimatedAIChat({ chatId = "default" }: AnimatedAIChatProps) { } const handleAttachFile = () => { - const mockFileName = `file-${Math.floor(Math.random() * 1000)}.pdf` - setAttachments((prev) => [...prev, mockFileName]) + if (fileInputRef.current) { + fileInputRef.current.click(); + } } - - const removeAttachment = (index: number) => { - setAttachments((prev) => prev.filter((_, i) => i !== index)) + + const handleFileChange = (e: React.ChangeEvent) => { + const files = e.target.files; + if (!files || files.length === 0) return; + + // Process each selected file + Array.from(files).forEach(file => { + // Create URL for preview + const url = URL.createObjectURL(file); + + // Add file to attachments + setAttachments(prev => [ + ...prev, + { + id: Math.random().toString(36).substring(2, 11), + name: file.name, + type: file.type, + url: url, + file: file + } + ]); + }); + + // Clear the input value to allow selecting the same file again + e.target.value = ''; + }; + + const removeAttachment = (id: string) => { + setAttachments(prev => { + const updated = prev.filter(attachment => attachment.id !== id); + + // Revoke object URLs to avoid memory leaks + const removed = prev.find(attachment => attachment.id === id); + if (removed) { + URL.revokeObjectURL(removed.url); + } + + return updated; + }); } - - const selectCommandSuggestion = (index: number) => { - const selectedCommand = commandSuggestions[index] - setValue(selectedCommand.prefix + " ") - setShowCommandPalette(false) - - setRecentCommand(selectedCommand.label) - setTimeout(() => setRecentCommand(null), 2000) + + const handleShareChat = () => { + navigator.clipboard.writeText(`${window.location.origin}/chat/${chatId}`); + setShowShareTooltip(true); + setTimeout(() => setShowShareTooltip(false), 2000); } return ( -
-
-
-
-
-
- -
- - - {!isSplitScreen && ( - - -

- How can I help today? -

- - - - Type a command or ask a question - -
- )} -
- - {/* Display messages */} - {messages.length > 0 && ( - + {/* Button for sharing the chat */} +
+
+ + + {showShareTooltip && ( + - {messages.map((message, index) => ( - -
-
{message.content}
-
-
- ))} + Link copied!
)} - - +
+ + {/* Message display area */} +
+ {messages.map((message, index) => ( +
- - {showCommandPalette && ( - -
- {commandSuggestions.map((suggestion, index) => ( - selectCommandSuggestion(index)} - initial={{ opacity: 0 }} - animate={{ opacity: 1 }} - transition={{ delay: index * 0.03 }} - > -
{suggestion.icon}
-
{suggestion.label}
-
{suggestion.prefix}
-
- ))} -
-
- )} -
- -
-