chore: formatting the code of recent changes
This commit is contained in:
@ -53,7 +53,7 @@ export default function AIChat({
|
||||
// scroll to bottom of chat when messages change
|
||||
const scrollToBottom = (force: boolean = false) => {
|
||||
if (!chatContainerRef.current || (!autoScroll && !force)) return
|
||||
|
||||
|
||||
chatContainerRef.current.scrollTo({
|
||||
top: chatContainerRef.current.scrollHeight,
|
||||
behavior: force ? "smooth" : "auto",
|
||||
@ -63,10 +63,10 @@ export default function AIChat({
|
||||
// function to handle scroll events
|
||||
const handleScroll = () => {
|
||||
if (!chatContainerRef.current) return
|
||||
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = chatContainerRef.current
|
||||
const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 50
|
||||
|
||||
|
||||
setAutoScroll(isAtBottom)
|
||||
setShowScrollButton(!isAtBottom)
|
||||
}
|
||||
@ -75,8 +75,8 @@ export default function AIChat({
|
||||
useEffect(() => {
|
||||
const container = chatContainerRef.current
|
||||
if (container) {
|
||||
container.addEventListener('scroll', handleScroll)
|
||||
return () => container.removeEventListener('scroll', handleScroll)
|
||||
container.addEventListener("scroll", handleScroll)
|
||||
return () => container.removeEventListener("scroll", handleScroll)
|
||||
}
|
||||
}, [])
|
||||
|
||||
@ -200,7 +200,7 @@ export default function AIChat({
|
||||
/>
|
||||
))}
|
||||
{isLoading && <LoadingDots />}
|
||||
|
||||
|
||||
{/* Add scroll to bottom button */}
|
||||
{showScrollButton && (
|
||||
<button
|
||||
|
@ -131,22 +131,20 @@ export const handleSend = async (
|
||||
}))
|
||||
|
||||
// Fetch AI response for chat message component
|
||||
const response = await fetch("/api/ai",
|
||||
{
|
||||
const response = await fetch("/api/ai", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: anthropicMessages,
|
||||
context: context || undefined,
|
||||
activeFileContent: activeFileContent,
|
||||
isEditMode: isEditMode,
|
||||
templateType: templateType,
|
||||
}),
|
||||
signal: abortControllerRef.current.signal,
|
||||
}
|
||||
)
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: anthropicMessages,
|
||||
context: context || undefined,
|
||||
activeFileContent: activeFileContent,
|
||||
isEditMode: isEditMode,
|
||||
templateType: templateType,
|
||||
}),
|
||||
signal: abortControllerRef.current.signal,
|
||||
})
|
||||
|
||||
// Throw error if response is not ok
|
||||
if (!response.ok) {
|
||||
@ -201,7 +199,8 @@ export const handleSend = async (
|
||||
console.error("Error fetching AI response:", error)
|
||||
const errorMessage = {
|
||||
role: "assistant" as const,
|
||||
content: error.message || "Sorry, I encountered an error. Please try again.",
|
||||
content:
|
||||
error.message || "Sorry, I encountered an error. Please try again.",
|
||||
}
|
||||
setMessages((prev) => [...prev, errorMessage])
|
||||
}
|
||||
|
@ -66,15 +66,17 @@ export default function GenerateInput({
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [{
|
||||
role: "user",
|
||||
content: regenerate ? currentPrompt : input
|
||||
}],
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: regenerate ? currentPrompt : input,
|
||||
},
|
||||
],
|
||||
context: null,
|
||||
activeFileContent: data.code,
|
||||
isEditMode: true,
|
||||
fileName: data.fileName,
|
||||
line: data.line
|
||||
line: data.line,
|
||||
}),
|
||||
})
|
||||
|
||||
|
@ -1087,62 +1087,62 @@ export default function CodeEditor({
|
||||
</div>
|
||||
</>
|
||||
) : // Note clerk.loaded is required here due to a bug: https://github.com/clerk/javascript/issues/1643
|
||||
clerk.loaded ? (
|
||||
<>
|
||||
{/* {provider && userInfo ? (
|
||||
clerk.loaded ? (
|
||||
<>
|
||||
{/* {provider && userInfo ? (
|
||||
<Cursors yProvider={provider} userInfo={userInfo} />
|
||||
) : null} */}
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editorLanguage}
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorMount}
|
||||
onChange={(value) => {
|
||||
// If the new content is different from the cached content, update it
|
||||
if (value !== fileContents[activeFileId]) {
|
||||
setActiveFileContent(value ?? "") // Update the active file content
|
||||
// Mark the file as unsaved by setting 'saved' to false
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: false }
|
||||
: tab
|
||||
)
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editorLanguage}
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorMount}
|
||||
onChange={(value) => {
|
||||
// If the new content is different from the cached content, update it
|
||||
if (value !== fileContents[activeFileId]) {
|
||||
setActiveFileContent(value ?? "") // Update the active file content
|
||||
// Mark the file as unsaved by setting 'saved' to false
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: false }
|
||||
: tab
|
||||
)
|
||||
} else {
|
||||
// If the content matches the cached content, mark the file as saved
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: true }
|
||||
: tab
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// If the content matches the cached content, mark the file as saved
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: true }
|
||||
: tab
|
||||
)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
tabSize: 2,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
padding: {
|
||||
bottom: 4,
|
||||
top: 4,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
fixedOverflowWidgets: true,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
}}
|
||||
theme={theme === "light" ? "vs" : "vs-dark"}
|
||||
value={activeFileContent}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<Loader2 className="animate-spin w-6 h-6 mr-3" />
|
||||
Waiting for Clerk to load...
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
tabSize: 2,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
padding: {
|
||||
bottom: 4,
|
||||
top: 4,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
fixedOverflowWidgets: true,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
}}
|
||||
theme={theme === "light" ? "vs" : "vs-dark"}
|
||||
value={activeFileContent}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<Loader2 className="animate-spin w-6 h-6 mr-3" />
|
||||
Waiting for Clerk to load...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
|
@ -231,7 +231,10 @@ function ProfileCard({
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<p className="text-xs text-muted-foreground">{joinedAt}</p>
|
||||
{typeof generations === "number" && (
|
||||
<SubscriptionBadge generations={generations} tier={tier as keyof typeof TIERS} />
|
||||
<SubscriptionBadge
|
||||
generations={generations}
|
||||
tier={tier as keyof typeof TIERS}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
@ -445,7 +448,13 @@ const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
|
||||
// #endregion
|
||||
|
||||
// #region Sub Badge
|
||||
const SubscriptionBadge = ({ generations, tier = "FREE" }: { generations: number, tier?: keyof typeof TIERS }) => {
|
||||
const SubscriptionBadge = ({
|
||||
generations,
|
||||
tier = "FREE",
|
||||
}: {
|
||||
generations: number
|
||||
tier?: keyof typeof TIERS
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Badge variant="secondary" className="text-sm cursor-pointer">
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { User } from "@/lib/types"
|
||||
import { useClerk } from "@clerk/nextjs"
|
||||
import {
|
||||
Crown,
|
||||
Crown,
|
||||
LayoutDashboard,
|
||||
LogOut,
|
||||
Sparkles,
|
||||
@ -43,7 +43,11 @@ const TIER_INFO = {
|
||||
},
|
||||
} as const
|
||||
|
||||
export default function UserButton({ userData: initialUserData }: { userData: User }) {
|
||||
export default function UserButton({
|
||||
userData: initialUserData,
|
||||
}: {
|
||||
userData: User
|
||||
}) {
|
||||
const [userData, setUserData] = useState<User>(initialUserData)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { signOut } = useClerk()
|
||||
@ -57,7 +61,7 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
headers: {
|
||||
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||
},
|
||||
cache: 'no-store'
|
||||
cache: "no-store",
|
||||
}
|
||||
)
|
||||
if (res.ok) {
|
||||
@ -75,9 +79,12 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const tierInfo = TIER_INFO[userData.tier as keyof typeof TIER_INFO] || TIER_INFO.FREE
|
||||
const tierInfo =
|
||||
TIER_INFO[userData.tier as keyof typeof TIER_INFO] || TIER_INFO.FREE
|
||||
const TierIcon = tierInfo.icon
|
||||
const usagePercentage = Math.floor((userData.generations || 0) * 100 / tierInfo.limit)
|
||||
const usagePercentage = Math.floor(
|
||||
((userData.generations || 0) * 100) / tierInfo.limit
|
||||
)
|
||||
|
||||
const handleUpgrade = async () => {
|
||||
router.push(`/@${userData.username}`)
|
||||
@ -98,7 +105,6 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
|
||||
<DropdownMenuItem className="cursor-pointer" asChild>
|
||||
<Link href={"/dashboard"}>
|
||||
<LayoutDashboard className="mr-2 size-4" />
|
||||
@ -114,12 +120,13 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
|
||||
<div className="py-1.5 px-2 w-full">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<TierIcon className={`h-4 w-4 ${tierInfo.color}`} />
|
||||
<span className="text-sm font-medium">{userData.tier || "FREE"} Plan</span>
|
||||
<span className="text-sm font-medium">
|
||||
{userData.tier || "FREE"} Plan
|
||||
</span>
|
||||
</div>
|
||||
{(userData.tier === "FREE" || userData.tier === "PRO") && (
|
||||
<Button
|
||||
@ -139,16 +146,20 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
<div className="w-full">
|
||||
<div className="flex items-center justify-between text-sm text-muted-foreground mb-2">
|
||||
<span>AI Usage</span>
|
||||
<span>{userData.generations}/{tierInfo.limit}</span>
|
||||
<span>
|
||||
{userData.generations}/{tierInfo.limit}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="rounded-full w-full h-2 overflow-hidden bg-secondary mb-1">
|
||||
<div className="rounded-full w-full h-2 overflow-hidden bg-secondary mb-1">
|
||||
<div
|
||||
className={`h-full rounded-full transition-all duration-300 ${
|
||||
usagePercentage > 90 ? 'bg-red-500' :
|
||||
usagePercentage > 75 ? 'bg-yellow-500' :
|
||||
tierInfo.color.replace('text-', 'bg-')
|
||||
}`}
|
||||
usagePercentage > 90
|
||||
? "bg-red-500"
|
||||
: usagePercentage > 75
|
||||
? "bg-yellow-500"
|
||||
: tierInfo.color.replace("text-", "bg-")
|
||||
}`}
|
||||
style={{
|
||||
width: `${Math.min(usagePercentage, 100)}%`,
|
||||
}}
|
||||
@ -173,4 +184,3 @@ export default function UserButton({ userData: initialUserData }: { userData: Us
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user