Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,28 @@ function parseMarkdown(markdown: string) {
}
})

return { heading, content: withoutFrontmatter.trim() }
}

/**
* Wraps content in a markdown code block.
*
* Uses `mdast` to ensure proper escaping of backticks within the content.
*/
export function wrapInMarkdownCodeBlock(content: string) {
const mdast = fromMarkdown(content)

const codeBlock: Code = {
type: 'code',
lang: 'markdown',
value: markdown,
value: content,
}
const root: Root = {
type: 'root',
children: [codeBlock],
}
const content = toMarkdown(root)

return { heading, content }
return toMarkdown(root)
}

async function getAiPromptsImpl() {
Expand Down Expand Up @@ -106,3 +116,28 @@ export async function generateAiPromptsStaticParams() {
}
})
}

/**
* Generates a deep link URL for Cursor that preloads the given prompt text.
*
* Cursor deep links have a maximum URL length of 8000 characters.
* If the generated URL exceeds this length, `url` will be undefined
* and an error will be returned.
*/
export function generateCursorPromptDeepLink(promptText: string) {
// Temporarily reject prompts that contain ".env" due to a bug in Cursor
if (promptText.includes('.env')) {
return { error: new Error('Prompt text cannot contain the text .env due to a temporary bug') }
}

const url = new URL('cursor://anysphere.cursor-deeplink/prompt')
url.searchParams.set('text', promptText)
const urlString = url.toString()

// Cursor has a max URL length of 8000 characters for deep links
if (urlString.length > 8000) {
return { error: new Error('Prompt text is too long to generate a Cursor deep link.') }
}

return { url: urlString }
}
28 changes: 21 additions & 7 deletions apps/docs/app/guides/getting-started/ai-prompts/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { source } from 'common-tags'
import { notFound } from 'next/navigation'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import {
generateAiPromptMetadata,
generateAiPromptsStaticParams,
generateCursorPromptDeepLink,
getAiPrompt,
wrapInMarkdownCodeBlock,
} from './AiPrompts.utils'

export const dynamicParams = false
Expand All @@ -19,17 +22,28 @@ export default async function AiPromptsPage(props: { params: Promise<{ slug: str
}

let { heading, content } = prompt
content = `
## How to use
const { url: cursorUrl } = generateCursorPromptDeepLink(content)

Copy the prompt to a file in your repo.
content = source`
## How to use

Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, with GitHub Copilot, use \`#<filename>\`, in Cursor, use \`@Files\`, and in Zed, use \`/file\`.
Copy the prompt to a file in your repo.

## Prompt
Use the "include file" feature from your AI tool to include the prompt when chatting with your AI assistant. For example, with GitHub Copilot, use \`#<filename>\`, in Cursor, use \`@Files\`, and in Zed, use \`/file\`.

${content}
`.trim()
${
cursorUrl
? source`
You can also load the prompt directly into your IDE via the following links:
- [Open in Cursor](${cursorUrl})
`
: ''
}

## Prompt

${wrapInMarkdownCodeBlock(content)}
`

return (
<GuideTemplate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
title = "How to delete Vercel linked projects"
topics = [ "platform" ]
keywords = [ "vercel", "delete", "marketplace", "projects", "organization" ]
database_id = "9d08aa12-3456-789a-bcde-f012345678ab"

[api]
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import Image from 'next/image'
import { useParams } from 'common'
import { Markdown } from 'components/interfaces/Markdown'
import { InlineLink } from 'components/ui/InlineLink'
import { useCustomContent } from 'hooks/custom-content/useCustomContent'
import { BASE_PATH, DOCS_URL } from 'lib/constants'
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, WarningIcon } from 'ui'

export const CLSPreview = () => {
const { ref } = useParams()

const { docsRowLevelSecurityGuidePath } = useCustomContent(['docs:row_level_security_guide_path'])

return (
<div className="flex flex-col gap-2">
<div className="mb-4 flex flex-col gap-y-2">
Expand All @@ -18,7 +21,7 @@ export const CLSPreview = () => {
/>
<Markdown
className="text-foreground-light max-w-full"
content={`This is an advanced feature and should be used with caution. Unless you have a very specific use case, we recommend just using [Row-Level Security](${DOCS_URL}/guides/auth/row-level-security).`}
content={`This is an advanced feature and should be used with caution. Unless you have a very specific use case, we recommend just using [Row-Level Security](${DOCS_URL}${docsRowLevelSecurityGuidePath}).`}
/>
<Alert_Shadcn_ variant="warning" className="mt-2">
<WarningIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,29 @@ import { Logs } from 'icons'
import { BASE_PATH } from 'lib/constants'
import { useParams } from 'common'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'

export const OverviewLearnMore = () => {
const [isMounted, setIsMounted] = useState(false)
const { ref } = useParams()
const aiSnap = useAiAssistantStateSnapshot()
const { theme, resolvedTheme } = useTheme()

useEffect(() => {
setIsMounted(true)
}, [])

const isLight = resolvedTheme === 'light'

const LearnMoreCards = [
{
label: 'Docs',
title: 'Auth docs',
description: 'Read more on Supabase auth, managing users and more.',
image: `${BASE_PATH}/img/auth-overview/auth-overview-docs.jpg`,
image: isLight
? `${BASE_PATH}/img/auth-overview/auth-overview-docs-light.jpg`
: `${BASE_PATH}/img/auth-overview/auth-overview-docs.jpg`,
actions: [
{
label: 'Docs',
Expand All @@ -29,7 +41,9 @@ export const OverviewLearnMore = () => {
label: 'Assistant',
title: 'Explain auth errors',
description: 'Our Assistant can help you debug and fix authentication errors.',
image: `${BASE_PATH}/img/auth-overview/auth-overview-assistant.jpg`,
image: isLight
? `${BASE_PATH}/img/auth-overview/auth-overview-assistant-light.jpg`
: `${BASE_PATH}/img/auth-overview/auth-overview-assistant.jpg`,
actions: [
{
label: 'Ask Assistant',
Expand Down Expand Up @@ -70,7 +84,9 @@ export const OverviewLearnMore = () => {
label: 'Logs',
title: 'Dive into the logs',
description: 'Auth logs provide a deeper view into your auth requests.',
image: `${BASE_PATH}/img/auth-overview/auth-overview-logs.jpg`,
image: isLight
? `${BASE_PATH}/img/auth-overview/auth-overview-logs-light.jpg`
: `${BASE_PATH}/img/auth-overview/auth-overview-logs.jpg`,
actions: [
{
label: 'Go to logs',
Expand All @@ -81,6 +97,8 @@ export const OverviewLearnMore = () => {
},
]

if (!isMounted) return null

return (
<ScaffoldSection isFullWidth>
<ScaffoldSectionTitle className="mb-4">Learn more</ScaffoldSectionTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ const NewPaymentMethodElement = forwardRef(
}, [availableTaxIds, stripeAddress])

return (
<div className="space-y-4">
<p className="text-sm text-foreground-light mb-2">
Please ensure CVC and postal codes match what is on file for your card.
<div className="space-y-2">
<p className="text-sm text-foreground-lighter">
Please ensure CVC and postal codes match what’s on file for your card.
</p>

<PaymentElement
Expand All @@ -256,14 +256,14 @@ const NewPaymentMethodElement = forwardRef(
/>

{fullyLoaded && (
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2 py-4">
<Checkbox_Shadcn_
id="business"
checked={purchasingAsBusiness}
onCheckedChange={() => setPurchasingAsBusiness(!purchasingAsBusiness)}
/>
<label htmlFor="business" className="text-foreground-light text-sm leading-none">
I'm purchasing as a business
<label htmlFor="business" className="text-foreground text-sm leading-none">
Im purchasing as a business
</label>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useQueryClient } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { Clock } from 'lucide-react'
import { useRouter } from 'next/router'
Expand All @@ -10,7 +9,7 @@ import Panel from 'components/ui/Panel'
import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useBackupRestoreMutation } from 'data/database/backup-restore-mutation'
import { DatabaseBackup, useBackupsQuery } from 'data/database/backups-query'
import { setProjectStatus } from 'data/projects/projects-query'
import { useSetProjectStatus } from 'data/projects/project-detail-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
Expand All @@ -20,14 +19,13 @@ import { BackupsStorageAlert } from './BackupsStorageAlert'

const BackupsList = () => {
const router = useRouter()
const queryClient = useQueryClient()
const { ref: projectRef } = useParams()
const [selectedBackup, setSelectedBackup] = useState<DatabaseBackup>()

const { setProjectStatus } = useSetProjectStatus()
const { data: selectedProject } = useSelectedProjectQuery()
const isHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY

const [selectedBackup, setSelectedBackup] = useState<DatabaseBackup>()

const { data: backups } = useBackupsQuery({ projectRef })
const {
mutate: restoreFromBackup,
Expand All @@ -37,7 +35,7 @@ const BackupsList = () => {
onSuccess: () => {
if (projectRef) {
setTimeout(() => {
setProjectStatus(queryClient, projectRef, PROJECT_STATUS.RESTORING)
setProjectStatus({ ref: projectRef, status: PROJECT_STATUS.RESTORING })
toast.success(
`Restoring database back to ${dayjs(selectedBackup?.inserted_at).format(
'DD MMM YYYY HH:mm:ss'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useQueryClient } from '@tanstack/react-query'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
Expand All @@ -7,7 +6,7 @@ import { useParams } from 'common'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { useBackupsQuery } from 'data/database/backups-query'
import { usePitrRestoreMutation } from 'data/database/pitr-restore-mutation'
import { setProjectStatus } from 'data/projects/projects-query'
import { useSetProjectStatus } from 'data/projects/project-detail-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { PROJECT_STATUS } from 'lib/constants'
import {
Expand All @@ -28,10 +27,11 @@ import { PITRForm } from './pitr-form'
const PITRSelection = () => {
const router = useRouter()
const { ref } = useParams()
const queryClient = useQueryClient()

const { data: backups } = useBackupsQuery({ projectRef: ref })
const { data: databases } = useReadReplicasQuery({ projectRef: ref })
const { setProjectStatus } = useSetProjectStatus()

const [showConfiguration, setShowConfiguration] = useState(false)
const [showConfirmation, setShowConfirmation] = useState(false)
const [selectedTimezone, setSelectedTimezone] = useState<Timezone>(getClientTimezone())
Expand All @@ -48,10 +48,10 @@ const PITRSelection = () => {
isLoading: isRestoring,
isSuccess: isSuccessPITR,
} = usePitrRestoreMutation({
onSuccess: (res, variables) => {
onSuccess: (_, variables) => {
setTimeout(() => {
setShowConfirmation(false)
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.RESTORING)
setProjectStatus({ ref: variables.ref, status: PROJECT_STATUS.RESTORING })
router.push(`/project/${variables.ref}`)
}, 3000)
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { AnimatePresence, motion } from 'framer-motion'
import { ChevronRight } from 'lucide-react'
import { useEffect, useState } from 'react'
Expand All @@ -19,7 +18,7 @@ import { useUpdateDiskAttributesMutation } from 'data/config/disk-attributes-upd
import { useDiskAutoscaleCustomConfigQuery } from 'data/config/disk-autoscale-config-query'
import { useUpdateDiskAutoscaleConfigMutation } from 'data/config/disk-autoscale-config-update-mutation'
import { useDiskUtilizationQuery } from 'data/config/disk-utilization-query'
import { setProjectStatus } from 'data/projects/projects-query'
import { useSetProjectStatus } from 'data/projects/project-detail-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
Expand Down Expand Up @@ -67,10 +66,10 @@ import { NoticeBar } from './ui/NoticeBar'
import { SpendCapDisabledSection } from './ui/SpendCapDisabledSection'

export function DiskManagementForm() {
const { ref: projectRef } = useParams()
const { data: project } = useSelectedProjectQuery()
const { data: org } = useSelectedOrganizationQuery()
const { ref: projectRef } = useParams()
const queryClient = useQueryClient()
const { setProjectStatus } = useSetProjectStatus()

const { data: resourceWarnings } = useResourceWarningsQuery()
const projectResourceWarnings = (resourceWarnings ?? [])?.find(
Expand Down Expand Up @@ -230,7 +229,7 @@ export function DiskManagementForm() {
onError: () => {},
onSuccess: () => {
//Manually set project status to RESIZING, Project status should be RESIZING on next project status request.
setProjectStatus(queryClient, projectRef!, PROJECT_STATUS.RESIZING)
if (projectRef) setProjectStatus({ ref: projectRef, status: PROJECT_STATUS.RESIZING })
},
})
const { mutateAsync: updateDiskAutoscaleConfig, isLoading: isUpdatingDiskAutoscaleConfig } =
Expand Down
4 changes: 2 additions & 2 deletions apps/studio/components/interfaces/HomePageActions.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useDebounce } from '@uidotdev/usehooks'
import { Filter, Grid, List, Loader2, Plus, Search, X } from 'lucide-react'
import Link from 'next/link'
import { parseAsArrayOf, parseAsString, useQueryState } from 'nuqs'

import { useDebounce } from '@uidotdev/usehooks'
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useOrgProjectsInfiniteQuery } from 'data/projects/org-projects-infinite-query'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { PROJECT_STATUS } from 'lib/constants'
import { parseAsArrayOf, parseAsString, useQueryState } from 'nuqs'
import {
Button,
Checkbox_Shadcn_,
Expand Down
Loading
Loading