+
+
+
+
Assistant
+
+
+
+
+
+
+ The Assistant is in Alpha and your prompts might be rate limited.{' '}
+ {includeSchemaMetadata
+ ? 'Project metadata is being shared to improve Assistant responses.'
+ : 'Project metadata is not being shared. Opt in to improve Assistant responses.'}
+
+
-
{hasMessages ? 'New chat' : 'Assistant'}
-
{(hasMessages || suggestions || sqlSnippets) && (
Reset
@@ -351,42 +355,41 @@ export const AIAssistant = ({
)}
+ {!hasMessages && (
+
+
+
+ )}
{hasMessages ? (
-
-
- {new Date(messages[0].createdAt || new Date()).toLocaleDateString('en-US', {
- month: 'long',
- day: 'numeric',
- year: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- })}
-
+
{renderedMessages}
{(last(messages)?.role === 'user' || last(messages)?.content?.length === 0) && (
-
- Thinking
-
-
- .
-
-
- .
-
-
- .
-
-
-
+
+
+
+ Thinking
+
+
+ .
+
+
+ .
+
+
+ .
+
+
+
+
)}
@@ -417,48 +420,15 @@ export const AIAssistant = ({
))}
+ ) : isLoadingTables ? (
+
+ {/* [Joshen] We could try play around with a custom loader for the assistant here */}
+
+
) : (tables ?? [])?.length > 0 ? (
) : (
-
-
+
Welcome to Supabase!
This is the Supabase assistant which will help you create, debug and modify tables,
@@ -590,11 +560,6 @@ export const AIAssistant = ({
}
}}
/>
- {!hasMessages && (
-
- The Assistant is in Alpha and your prompts might be rate limited
-
- )}
diff --git a/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx b/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx
index 0d330085b60fe..cab0382de7d6e 100644
--- a/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx
+++ b/apps/studio/components/ui/AIAssistantPanel/AIOnboarding.tsx
@@ -1,7 +1,6 @@
import { motion } from 'framer-motion'
import { FileText, MessageCircleMore, WandSparkles } from 'lucide-react'
-import DotGrid from 'components/ui/DotGrid'
import { Button } from 'ui'
import {
InnerSideMenuCollapsible,
@@ -20,10 +19,7 @@ export default function AIOnboarding({ setMessages, onSendMessage }: AIOnboardin
}
return (
-
-
-
-
+
-
+
-
+
-
+
-
+
{children}
-
- {variant === 'warning' &&
}
- {action}
+ {variant === 'warning' &&
}
+
+ {action}
+
+ {isUser ? (
+
+
+
+ ) : (
+
+ )}
{
const language = props.children[0].props.className?.replace('language-', '') || 'sql'
- if (language === 'sql') {
- return readOnly ? (
-
-
+ {language === 'sql' ? (
+ readOnly ? (
+
+ ) : (
+
+ )
+ ) : (
+ code]:m-0 [&>code>span]:flex [&>code>span]:flex-wrap [&>code]:block [&>code>span]:text-foreground'
+ )}
/>
-
- ) : (
-
- )
- }
-
- return (
-
- code]:m-0 [&>code>span]:flex [&>code>span]:flex-wrap [&>code]:block [&>code>span]:text-foreground'
- )}
- />
+ )}
)
},
diff --git a/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx b/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx
index 5cbde110e8bb2..9e162427f3bd8 100644
--- a/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx
+++ b/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx
@@ -55,7 +55,7 @@ const SqlSnippetWrapper = ({
const updatedFormatted = formatted?.replace(/--\s*props:\s*\{[^}]+\}/, '').trim()
return (
-
+
-
+
+
{showWarning ? (
This query contains write operations. Are you sure you want to execute it?
@@ -223,7 +223,7 @@ export const SqlCard = ({
size={16}
strokeWidth={1.5}
/>
- {title}
+ {title}
{!readOnly && (
@@ -336,7 +336,7 @@ export const SqlCard = ({
{/* Results Section */}
{results !== undefined && results.length > 0 && isChart && xAxis && yAxis ? (
-
+
{
const item = {
hidden: { opacity: 0 },
- visible: { opacity: 0.5 },
- }
-
- const highlightedVariants = {
visible: {
opacity: [1, 0.5, 1],
transition: {
@@ -60,7 +56,7 @@ const DotGrid = ({ rows, columns, count }: DotGridProps) => {
return (
)
diff --git a/apps/studio/data/branches/branch-delete-mutation.ts b/apps/studio/data/branches/branch-delete-mutation.ts
index b6682c4f915e9..5a2a2815fe4ee 100644
--- a/apps/studio/data/branches/branch-delete-mutation.ts
+++ b/apps/studio/data/branches/branch-delete-mutation.ts
@@ -3,6 +3,7 @@ import { toast } from 'sonner'
import { del, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
+import { BranchesData } from './branches-query'
import { branchKeys } from './keys'
export type BranchDeleteVariables = {
@@ -35,7 +36,18 @@ export const useBranchDeleteMutation = ({
{
async onSuccess(data, variables, context) {
const { projectRef } = variables
- await queryClient.invalidateQueries(branchKeys.list(projectRef))
+ setTimeout(() => {
+ queryClient.invalidateQueries(branchKeys.list(projectRef))
+ }, 5000)
+
+ const branches: BranchesData | undefined = queryClient.getQueryData(
+ branchKeys.list(projectRef)
+ )
+ if (branches) {
+ const updatedBranches = branches.filter((branch) => branch.id !== variables.id)
+ queryClient.setQueryData(branchKeys.list(projectRef), updatedBranches)
+ }
+
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
diff --git a/apps/studio/pages/api/ai/sql/generate-v3.ts b/apps/studio/pages/api/ai/sql/generate-v3.ts
index 917c3d07bf51e..0e4f60b6af7f1 100644
--- a/apps/studio/pages/api/ai/sql/generate-v3.ts
+++ b/apps/studio/pages/api/ai/sql/generate-v3.ts
@@ -76,8 +76,8 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
- Always use semicolons
- Output as markdown
- Always include code snippets if available
- - If a code snippet is SQL, the first line of the snippet should always be -- props: {"title": "Query title", "isChart": "true", "xAxis": "columnName", "yAxis": "columnName"}
- - Only set chart to true if the query makes sense as a chart
+ - If a code snippet is SQL, the first line of the snippet should always be -- props: {"title": "Query title", "isChart": "true", "xAxis": "columnOrAlias", "yAxis": "columnOrAlias"}
+ - Only set chart to true if the query makes sense as a chart. xAxis and yAxis need to be columns or aliases returned by the query.
- Explain what the snippet does in a sentence or two before showing it
- Use vector(384) data type for any embedding/vector related query
- When debugging, retrieve sql schema details to ensure sql is correct
diff --git a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/[childId]/index.tsx b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/[childId]/index.tsx
index e25dc2980fead..99f17904eb3df 100644
--- a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/[childId]/index.tsx
+++ b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/[childId]/index.tsx
@@ -53,7 +53,7 @@ const IntegrationPage: NextPageWithLayout = () => {
return Integration not found
}
- if (!Component) return Component not found
+ if (!Component) return Component not found
return
}
diff --git a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx
index e25dc2980fead..99f17904eb3df 100644
--- a/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx
+++ b/apps/studio/pages/project/[ref]/integrations/[id]/[pageId]/index.tsx
@@ -53,7 +53,7 @@ const IntegrationPage: NextPageWithLayout = () => {
return Integration not found
}
- if (!Component) return Component not found
+ if (!Component) return Component not found
return
}
diff --git a/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx b/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx
index e25dc2980fead..99f17904eb3df 100644
--- a/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx
+++ b/apps/studio/pages/project/[ref]/integrations/[id]/index.tsx
@@ -53,7 +53,7 @@ const IntegrationPage: NextPageWithLayout = () => {
return Integration not found
}
- if (!Component) return Component not found
+ if (!Component) return Component not found
return
}
diff --git a/apps/studio/static-data/integrations/cron-jobs/overview.md b/apps/studio/static-data/integrations/cron-jobs/overview.md
index b9cfc653dfa2f..dfb3bbbf8336b 100644
--- a/apps/studio/static-data/integrations/cron-jobs/overview.md
+++ b/apps/studio/static-data/integrations/cron-jobs/overview.md
@@ -1 +1 @@
-Cron jobs uses the `pg_cron` Postgres extension, which is a simple cron-based job scheduler for Postgres that runs inside the database. It uses the same syntax as a regular cron, but it allows you to schedule Postgres SQL commands directly from the database.
+Supabase Cron uses the `pg_cron` Postgres extension, which is a simple cron-based job scheduler for Postgres that runs inside the database. It uses the same syntax as a regular cron, but it allows you to schedule Postgres SQL commands directly from the database.
diff --git a/packages/ui/src/layout/ai-icon-animation/ai-icon-animation.tsx b/packages/ui/src/layout/ai-icon-animation/ai-icon-animation.tsx
index 32e2e1fb3bd77..3f838bada84ad 100644
--- a/packages/ui/src/layout/ai-icon-animation/ai-icon-animation.tsx
+++ b/packages/ui/src/layout/ai-icon-animation/ai-icon-animation.tsx
@@ -1,97 +1,124 @@
'use client'
-import styles from './ai-icon-animation-style.module.css'
-import { useEffect, useState } from 'react'
-import { cn } from '../../lib/utils/cn'
+import { motion, useMotionValue, useSpring } from 'framer-motion'
+import { useRef, useState } from 'react'
+import { cn } from '../../lib/utils'
-interface Props {
+interface AiIconAnimationProps {
+ size?: number
loading?: boolean
className?: string
allowHoverEffect?: boolean
}
-const AiIconAnimation = ({ loading = false, className, allowHoverEffect = false }: Props) => {
- const [step, setStep] = useState(1)
- const [exitStep, setExitStep] = useState(1)
- const [isAnimating, setIsAnimating] = useState(true)
+export const AiIconAnimation = ({
+ size = 24,
+ loading = false,
+ className,
+ allowHoverEffect = false,
+}: AiIconAnimationProps) => {
+ const strokeWidth = Math.max(1, size / 46) // Ensure minimum stroke width of 1
+ const containerRef = useRef(null)
+ const [isHovering, setIsHovering] = useState(false)
- useEffect(() => {
- const interval = setInterval(() => {
- if (loading) {
- setIsAnimating(true)
- setStep((step) => {
- return (step % 5) + 1
- })
- setExitStep((step) => {
- return (step % 5) + 1
- })
- }
- }, 500)
- return () => clearInterval(interval)
- }, [loading])
+ const x = useMotionValue(0)
+ const y = useMotionValue(0)
- useEffect(() => {
- if (loading === false) {
- setTimeout(() => {
- setIsAnimating(false)
- }, 500)
- setStep(1)
- }
- }, [loading])
+ const springX = useSpring(x, { stiffness: 300, damping: 30 })
+ const springY = useSpring(y, { stiffness: 300, damping: 30 })
+
+ const handleMouseMove = (event: React.MouseEvent) => {
+ if (!allowHoverEffect) return
+ if (!containerRef.current) return
+
+ const rect = containerRef.current.getBoundingClientRect()
+ const centerX = rect.left + rect.width / 2
+ const centerY = rect.top + rect.height / 2
+
+ const mouseX = event.clientX - centerX
+ const mouseY = event.clientY - centerY
+
+ x.set(mouseX / 5)
+ y.set(mouseY / 5)
+ }
+
+ const outerVariants = {
+ rest: { rotate: 0 },
+ loading: { rotate: 360 },
+ hover: { rotate: 10 },
+ }
+
+ const innerVariants = {
+ rest: { scale: 1, x: 0, y: 0 },
+ loading: { scale: [1, 1.1, 1], x: 0, y: 0 },
+ hover: { scale: 1.1 },
+ }
return (
-
+
setIsHovering(true)}
+ onMouseLeave={() => {
+ setIsHovering(false)
+ x.set(0)
+ y.set(0)
+ }}
+ />
+
+
+
+
+
)
}
-
-export { AiIconAnimation }