diff --git a/.env.example b/.env.example index 5b3f8e2..b1df96d 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,9 @@ UPSTASH_VECTOR_REST_URL="XXXXX" UPSTASH_VECTOR_REST_TOKEN="XXXXX" - # if you use OpenAI compatible models OPENAI_API_KEY="XXXXX" -# or if you use Upstash hosted models -QSTASH_TOKEN="XXXXX" # Optional: For Redis-based chat history (default is in-memory) UPSTASH_REDIS_REST_URL="XXXXX" diff --git a/app/(routes)/chat/[slug]/page.tsx b/app/(routes)/chat/[slug]/page.tsx new file mode 100644 index 0000000..23369a1 --- /dev/null +++ b/app/(routes)/chat/[slug]/page.tsx @@ -0,0 +1,113 @@ +"use client"; + +import { useState } from 'react'; +import { ChevronLeft, ChevronRight, PaperclipIcon, ArrowUpIcon } from 'lucide-react'; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Button } from "@/components/ui/button"; +import { CustomTextArea } from "@/components/custom/text.area"; +import { SideBar } from "@/components/custom/sidebar/sidebar"; +import useLocalStorage from "@/lib/use.local"; +import { LibSelector } from '@/components/custom/lib.selector'; + +export default function Component() { + const [isCodeCollapsed, setIsCodeCollapsed] = useState(false); + const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage("acter-isCollapsed", false); + + const toggleCodePanel = () => { + setIsCodeCollapsed(!isCodeCollapsed); + }; + + const messages = [ + { id: 1, text: "Hello! How can I help you today?" }, + { id: 2, text: "Can you explain how to use React hooks?" }, + { id: 3, text: "React hooks are functions that let you use state and other React features in functional components..." }, + ]; + + const codeExample = ` +import React, { useState } from 'react'; + +function Counter() { + const [count, setCount] = useState(0); + + return ( +
+

You clicked {count} times

+ +
+ ); +} + `.trim(); + + return ( +
+ {/* Sidebar */} + + + {/* Left Panel */} +
+ + + {messages.map((message) => ( +
+ {message.text} +
+ ))} +
+ + {/* Input Area */} +
+ +
+
+ + +
+ +
+ +
+ + {/* Right Panel - Code Viewer */} +
+ {!isCodeCollapsed && ( +
+

Code

+ +
+                {codeExample}
+              
+
+
+ )} +
+
+ ); +} diff --git a/app/(routes)/chat/page.tsx b/app/(routes)/chat/page.tsx index eb588a7..66f20cd 100644 --- a/app/(routes)/chat/page.tsx +++ b/app/(routes)/chat/page.tsx @@ -1,9 +1,114 @@ -import React from 'react' +"use client"; + +import { Badge } from "@/components/ui/badge"; +import { MoveUpRight } from "lucide-react"; +import useLocalStorage from "@/lib/use.local"; +import { useUser } from "@/context/user.context"; +import { OfcLinks } from "@/db/defaults"; +import { SideBar } from "@/components/custom/sidebar/sidebar"; +import { useEffect, useState } from "react"; +import { ChatComp } from "@/components/custom/chat/comp"; export default function ChatPage() { + const { user } = useUser(); + const [chatID, setChatID] = useState("null"); + const [isMsg, setMsg] = useState(false); + + useEffect(() => { + const fetchChatID = async () => { + const id = Math.floor(Math.random() * 900000) + 100000; + setChatID(id.toString()); + }; + + if (user) { + fetchChatID(); + } + }, [user, chatID]); + + useEffect(() => { + if (chatID && isMsg) { + window.location.href = `/chat/${chatID}`; + } + }, [chatID, isMsg]); + + const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage( + "acter-isCollapsed", + false + ); + return ( -
- Damn! bro can't you even wait for my wings? +
+ {/* Background */} +
+ + {/* Main Layout */} +
+ {/* Sidebar */} + setIsSidebarExpanded(expanded)} + /> + + {/* Main Content */} +
+ {/* Header at the top */} +
+ Public Beta +
+ +
+

+ Need awesome components to ship? +

+

+ Am the one who supports Acternity UI, Magic UI and more libraries. Ask questions, modify component. +

+ + {/* Input area */} +
+ +
+ +
+ + Generate a SaaS pricing calculator + + + + How can I structure LLM output? + + + + Write code to implement a min heap + + +
+
+ + {/* Footer at the bottom */} + +
+
- ) + ); } diff --git a/app/(routes)/page.tsx b/app/(routes)/page.tsx index 28bb5b0..66c5f08 100644 --- a/app/(routes)/page.tsx +++ b/app/(routes)/page.tsx @@ -2,13 +2,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; -import { cn } from "@/lib/utils"; import { - MenuIcon, - PlusIcon, - BookOpenIcon, - LayoutDashboardIcon, - ChevronLeftIcon, PaperclipIcon, ArrowUpIcon, MoveUpRight, @@ -17,13 +11,11 @@ import { CustomTextArea } from "@/components/custom/text.area"; import { Badge } from "@/components/ui/badge"; import { OsBtn } from "@/components/custom/os.button"; import { OfcLinks } from "@/db/defaults"; -import { useUser } from "@/context/user.context"; import DragableCards from "@/components/custom/hero/dragable.cards"; import AuthDialog from "@/components/custom/auth/auth.dialog"; +import { LibSelector } from "@/components/custom/lib.selector"; -export default function Component() { - const { user } = useUser(); - const [isSidebarExpanded, setIsSidebarExpanded] = useState(false); +export default function HomePage() { const [isOpen, setIsOpen] = useState(false); const handleFormSubmit = async(event: React.FormEvent) => { @@ -39,48 +31,6 @@ export default function Component() { {/* Main Layout */}
- {/* Sidebar */} - {user && ( -
-
- - Acter - - -
- -
- )} - {/* Main Content */}
{/* Header at the top */} @@ -94,23 +44,26 @@ export default function Component() {

- What can I help you ship? + Need awesome components to ship?

- Am the one who supports Acternity UI, ask questions, debug code. + Am the one who supports Acternity UI, Magic UI and more libraries. Ask questions, modify component.

{/* Input area */}
-
+
- +
+ + +
diff --git a/app/api/ai/stream/route.ts b/app/api/ai/stream/route.ts new file mode 100644 index 0000000..577e608 --- /dev/null +++ b/app/api/ai/stream/route.ts @@ -0,0 +1,18 @@ +import { NextRequest } from "next/server"; +import { aiUseChatAdapter } from "@upstash/rag-chat/nextjs"; +import { ragChat } from "@/lib/rag"; + +export const POST = async (req: NextRequest) => { + const { messages } = await req.json(); + + const lastMsg = messages[messages.length - 1].content; + const res = await ragChat.chat(lastMsg, { + namespace: "acter-db", + streaming: true, + historyLength: 100, + historyTTL: 604_800, + similarityThreshold: 0.7, + }); + + return aiUseChatAdapter(res); +}; \ No newline at end of file diff --git a/app/api/vectors/route.ts b/app/api/vectors/route.ts new file mode 100644 index 0000000..9768394 --- /dev/null +++ b/app/api/vectors/route.ts @@ -0,0 +1,20 @@ +import { ragChat } from '@/lib/rag'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function POST(req: NextRequest) { + try { + const { source, namespace, metadata } = await req.json(); + + await ragChat.context.add({ + type: "html", + source: source, + config: { chunkOverlap: 50, chunkSize: 200 }, + options: { namespace: namespace, metadata: { title: metadata.title, description: metadata.description } }, + }); + + return NextResponse.json({ message: "Content added successfully to ragChat context!" }); + } catch (error) { + console.error("Error adding content to ragChat context:", error); + return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); + } +} diff --git a/bun.lockb b/bun.lockb index fc58ebf..7b3beba 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/custom/chat/comp.tsx b/components/custom/chat/comp.tsx new file mode 100644 index 0000000..eb59a0f --- /dev/null +++ b/components/custom/chat/comp.tsx @@ -0,0 +1,83 @@ +"use client"; +import { Message, useChat } from 'ai/react'; +import { useEffect, useState } from 'react' +import { CustomTextArea } from "@/components/custom/text.area"; +import { PaperclipIcon, ArrowUpIcon } from "lucide-react"; +import { Button } from '@/components/ui/button'; +import { LibSelector } from '../lib.selector'; + +export const ChatComp = ({ + chatId, + history, + onHasMessagesChange, + msgs, + }: { + chatId: number | string; + history?: Message[]; + onHasMessagesChange?: (hasMessages: boolean) => void; + msgs?: (Messages: Message[]) => void; + }) => { + const [space, setSpace] = useState(null); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + const form = document.querySelector("form"); + if (form) { + form.requestSubmit(); + } + } + }; + + document.addEventListener("keydown", handleKeyDown); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, []); + + const { messages, handleInputChange, handleSubmit, input, isLoading } = + useChat({ + api: "/api/ai/stream", + body: { chatId, namespace: space }, + initialMessages: history, + }); + + useEffect(() => { + if (messages.length > 0) { + if (onHasMessagesChange) { + onHasMessagesChange(true); + } + if (msgs) { + msgs(messages); + } + } + }, [ chatId, onHasMessagesChange, messages, msgs]); + + return ( + + +
+
+ + +
+ +
+ + ) + } diff --git a/components/custom/lib.selector.tsx b/components/custom/lib.selector.tsx new file mode 100644 index 0000000..90569f5 --- /dev/null +++ b/components/custom/lib.selector.tsx @@ -0,0 +1,96 @@ +"use client" + +import * as React from "react" +import { Check, ChevronsUpDown } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" + +const frameworks = [ + { + value: "acterniy-ui", + label: "Acternity UI", + }, + { + value: "magic-ui", + label: "Magic UI", + }, + { + value: "default-ui", + label: "Default UI", + } +] + +interface SpaceCompProps { + onSpaceSelect?: (selectedSpaceId: string | null) => void; + } + +export function LibSelector({ onSpaceSelect }: SpaceCompProps) { + const [open, setOpen] = React.useState(false) + const [value, setValue] = React.useState("default-ui") + + React.useEffect(() => { + if (onSpaceSelect) { + onSpaceSelect(value); + } + }, [value, onSpaceSelect]); + + return ( + + + + + + + + + No library found. + + {frameworks.map((framework) => ( + { + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + }} + > + + {framework.label} + + ))} + + + + + + ) +} diff --git a/components/custom/msgs/msgs.tsx b/components/custom/msgs/msgs.tsx new file mode 100644 index 0000000..156e65b --- /dev/null +++ b/components/custom/msgs/msgs.tsx @@ -0,0 +1,67 @@ +import { cn } from "@/lib/utils"; +import { Bot, CheckCheck } from "lucide-react"; +import React from "react"; +import { Badge } from "@/components/ui/badge"; + +interface MsgProps { + content: string; + isUser: boolean; +} + +export default function Message({ content, isUser }: MsgProps) { + return ( +
+
+
+ {!isUser && ( +
+ +
+ )} + +
+
+ + {!isUser && "x0-GPT"} + + {!isUser && ( + + + Bot + + )} +
+ +

+ {content} +

+
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/custom/msgs/msgs.wrapper.tsx b/components/custom/msgs/msgs.wrapper.tsx new file mode 100644 index 0000000..4c0b4fa --- /dev/null +++ b/components/custom/msgs/msgs.wrapper.tsx @@ -0,0 +1,34 @@ +"use client"; + +import React, { useEffect, useRef } from "react"; +import { type Message as TMsg } from "ai/react"; +import Message from "./msgs"; + +interface MsgsProps { + msgs: TMsg[]; +} + +export default function MsgsWrapper({ msgs }: MsgsProps) { + const containerRef = useRef(null); + + useEffect(() => { + if (containerRef.current) { + containerRef.current.scrollTop = containerRef.current.scrollHeight; + } + }, [msgs]); + + return ( +
+ {msgs.map((message, i) => ( + + ))} +
+ ); +} \ No newline at end of file diff --git a/components/custom/sidebar/sidebar.tsx b/components/custom/sidebar/sidebar.tsx new file mode 100644 index 0000000..e2bc2ac --- /dev/null +++ b/components/custom/sidebar/sidebar.tsx @@ -0,0 +1,90 @@ +import { Button } from "@/components/ui/button"; +import { menuItems } from "@/db/defaults"; +import { cn } from "@/lib/utils"; +import { + PanelLeftClose, + PanelLeftOpen, +} from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; + +interface SidebarProps { + isSidebarExpanded: boolean | string; + setIsSidebarExpanded: (expanded: boolean | string) => void; +} + +export const SideBar = ({ + isSidebarExpanded, + setIsSidebarExpanded, +}: SidebarProps) => { + return ( +
+ {/* Header Section */} +
+ {/* Logo */} + Acter Logo + {/* Toggle Button */} + {isSidebarExpanded && } +
+ + {/* Navigation Menu */} +
+
+ {/* Menu Items */} + +
+
+ + {/* Footer Section */} +
+ {!isSidebarExpanded && ( + + )} +
+
+ ); +}; diff --git a/components/custom/text.area.tsx b/components/custom/text.area.tsx index 8a7ffd0..d601e2b 100644 --- a/components/custom/text.area.tsx +++ b/components/custom/text.area.tsx @@ -17,7 +17,7 @@ const CustomTextArea = React.forwardRef( const minHeight = 20; // Default height // Reset the height to minHeight if there's no content - if (textarea.value.trim() === "" || null || undefined) { + if (textarea.value.trim() === "") { textarea.style.height = `${minHeight}px`; textarea.style.overflowY = "hidden"; } else { diff --git a/components/ui/command.tsx b/components/ui/command.tsx new file mode 100644 index 0000000..ade5441 --- /dev/null +++ b/components/ui/command.tsx @@ -0,0 +1,152 @@ +"use client" + +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" +import { MagnifyingGlassIcon } from "@radix-ui/react-icons" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +const CommandDialog = ({ children, ...props }: DialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx new file mode 100644 index 0000000..29c7bd2 --- /dev/null +++ b/components/ui/popover.tsx @@ -0,0 +1,33 @@ +"use client" + +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "@/lib/utils" + +const Popover = PopoverPrimitive.Root + +const PopoverTrigger = PopoverPrimitive.Trigger + +const PopoverAnchor = PopoverPrimitive.Anchor + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = PopoverPrimitive.Content.displayName + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx new file mode 100644 index 0000000..9e74821 --- /dev/null +++ b/components/ui/tooltip.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as TooltipPrimitive from "@radix-ui/react-tooltip" + +import { cn } from "@/lib/utils" + +const TooltipProvider = TooltipPrimitive.Provider + +const Tooltip = TooltipPrimitive.Root + +const TooltipTrigger = TooltipPrimitive.Trigger + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)) +TooltipContent.displayName = TooltipPrimitive.Content.displayName + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/db/defaults.ts b/db/defaults.ts index dbef003..1742ecb 100644 --- a/db/defaults.ts +++ b/db/defaults.ts @@ -1,3 +1,14 @@ +import { + PlusIcon, + BookOpenIcon, + LayoutDashboardIcon, +} from "lucide-react"; + +export const ragConfig = { + baseUrl: "https://api.deepseek.com", + model: "deepseek-ai/deepseek-llm-67b-chat", +} + export const OfcLinks = { github: "https://l.devwtf.in/discord", twitter: "https://x.com/SaidevDhal", @@ -7,6 +18,12 @@ export const OfcLinks = { discord: "https://l.devwtf.in/discord" }; +export const menuItems = [ + { label: "New Chat", icon: PlusIcon, href: "/chat", disabled: false }, + { label: "Docs", icon: BookOpenIcon, href: "/docs", disabled: true }, + { label: "Dashboard", icon: LayoutDashboardIcon, href: "/dashboard", disabled: true }, +]; + export const DATA = { name: "Acter", url: "https://acter.devwtf.in", diff --git a/lib/rag.ts b/lib/rag.ts new file mode 100644 index 0000000..0b13788 --- /dev/null +++ b/lib/rag.ts @@ -0,0 +1,45 @@ +import { ragConfig } from "@/db/defaults"; +import { RAGChat, togetherai } from "@upstash/rag-chat"; + +export const ragChat = new RAGChat({ + model: togetherai(ragConfig.model, { apiKey: process.env.RAG_API_KEY }), + promptFn: ({ context, question, chatHistory }) => + `You are Acter, an AI assistant created by Saidev Dhal. + Your task is to generate complete, high-quality code solutions based on the user's question or prompt. + Use the provided context and chat history to craft the code. + Ensure that the generated code incorporates and appropriately uses the components and styles mentioned in the context and explain all the details related to that component. + Only create new code without using any component from the context if it is absolutely necessary, and be sure to explain why. + If you lack sufficient information in the context or chat history, politely suggest the user provide additional details. + ------ + Chat history: + ${chatHistory} + ------ + Context: + ${context} + ------ + Question: + ${question} + ------ + Full Code:`, +}); + +async function AddTXTContext(data: string) { + await ragChat.context.add({ + type: "text", + data: data, + options: { namespace: "acter-db" }, + }); + return true; +} + +async function AddWEBContext(src: string) { + await ragChat.context.add({ + type: "html", + source: src, + config: { chunkOverlap: 50, chunkSize: 200 }, + options: { namespace: "acter-db" }, + }); + return true; +} + +export { AddTXTContext, AddWEBContext } \ No newline at end of file diff --git a/lib/use.local.ts b/lib/use.local.ts new file mode 100644 index 0000000..058fb7f --- /dev/null +++ b/lib/use.local.ts @@ -0,0 +1,53 @@ +import { useEffect, useState } from "react"; + +const useLocalStorage = (key: string, initialValue: string | boolean) => { + const [value, setValue] = useState(() => { + if (typeof window !== 'undefined') { + const storedValue = localStorage.getItem(key); + // Return the stored value if it exists, otherwise return the initial value + return storedValue !== null + ? typeof initialValue === "boolean" + ? storedValue === "true" + : storedValue + : initialValue; + } + return initialValue; + }); + + const setLocalStorageValue = (newValue: string | boolean) => { + setValue(newValue); + if (typeof window !== 'undefined') { + localStorage.setItem(key, newValue.toString()); + } + }; + + useEffect(() => { + const handleStorageChange = () => { + if (typeof window !== 'undefined') { + const newValue = localStorage.getItem(key); + // Update state based on the new value from localStorage + setValue( + newValue !== null + ? typeof initialValue === "boolean" + ? newValue === "true" + : newValue + : initialValue, + ); + } + }; + + if (typeof window !== 'undefined') { + window.addEventListener("storage", handleStorageChange); + } + + return () => { + if (typeof window !== 'undefined') { + window.removeEventListener("storage", handleStorageChange); + } + }; + }, [key, initialValue]); + + return [value, setLocalStorageValue] as const; +}; + +export default useLocalStorage; \ No newline at end of file diff --git a/package.json b/package.json index 638804f..7cd131d 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,22 @@ "@nextui-org/tabs": "^2.0.37", "@prisma/client": "^5.20.0", "@radix-ui/react-avatar": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.3", "@tabler/icons-react": "^3.19.0", - "@upstash/rag-chat": "^1.6.4", + "@upstash/rag-chat": "^2.0.0", + "@upstash/redis": "^1.34.2", + "@upstash/vector": "^1.1.7", + "ai": "^3.4.9", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "1.0.0", "framer-motion": "^11.11.1", + "fs": "^0.0.1-security", "lucide-react": "^0.446.0", "next": "14.2.13", "next-auth": "^5.0.0-beta.22", diff --git a/tsconfig.json b/tsconfig.json index e7ff90f..590f435 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -18,9 +22,18 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] }