mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 20:05:06 +03:00
- Enable DevToolbar in Vercel preview deployments by checking NEXT_PUBLIC_VERCEL_ENV === 'preview' alongside NODE_ENV === 'development' in all guards and the index.ts tree-shaking condition - Rename "Dev Telemetry" → "Dev Toolbar"; badge shows "Preview" or "Local" - Add ExtraTab extension point to DevToolbar so consumers can inject tabs - Add ResourceWarningsTab in studio: same off/warn/crit toggles for all resource warning types, wired into the existing DevToolbar via _app.tsx - Remove standalone SupabaseDevToolbar (superseded by the tab above) - localStorage persistence already handled by the existing STORAGE_KEY logic in DevToolbarContext — enabled state survives refreshes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
'use client'
|
|
|
|
import { Activity, EyeOff } from 'lucide-react'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import type { MouseEvent } from 'react'
|
|
import { Button, cn, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
|
|
|
|
import { useDevToolbar } from './DevToolbarContext'
|
|
|
|
const IS_DEV =
|
|
process.env.NODE_ENV === 'development' || process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
|
|
|
|
export function DevToolbarTrigger() {
|
|
const { isEnabled, isOpen, setIsOpen, events, dismissToolbar } = useDevToolbar()
|
|
const [popoverOpen, setPopoverOpen] = useState(false)
|
|
const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (closeTimeoutRef.current) {
|
|
clearTimeout(closeTimeoutRef.current)
|
|
closeTimeoutRef.current = null
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
if (!IS_DEV || !isEnabled) {
|
|
return null
|
|
}
|
|
|
|
const eventCount = events.length
|
|
|
|
const handleClick = () => {
|
|
setPopoverOpen(false)
|
|
setIsOpen(true)
|
|
}
|
|
|
|
const handleDismiss = () => {
|
|
setPopoverOpen(false)
|
|
dismissToolbar()
|
|
}
|
|
|
|
const handleMouseEnter = () => {
|
|
if (closeTimeoutRef.current) {
|
|
clearTimeout(closeTimeoutRef.current)
|
|
closeTimeoutRef.current = null
|
|
}
|
|
setPopoverOpen(true)
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
closeTimeoutRef.current = setTimeout(() => {
|
|
setPopoverOpen(false)
|
|
}, 100)
|
|
}
|
|
|
|
const handleTriggerMouseLeave = (event: MouseEvent<HTMLButtonElement>) => {
|
|
handleMouseLeave()
|
|
event.currentTarget.blur()
|
|
}
|
|
|
|
return (
|
|
<Popover_Shadcn_ open={popoverOpen} onOpenChange={setPopoverOpen}>
|
|
<PopoverTrigger_Shadcn_ asChild>
|
|
<Button
|
|
type="text"
|
|
className={cn(
|
|
'relative rounded-full h-[32px] px-2',
|
|
'text-foreground-light hover:text-foreground',
|
|
'focus-visible:outline-0 focus-visible:outline-transparent focus-visible:outline-offset-0',
|
|
isOpen && 'text-foreground bg-surface-300'
|
|
)}
|
|
aria-label="Open dev toolbar"
|
|
onClick={handleClick}
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleTriggerMouseLeave}
|
|
>
|
|
<Activity className="w-4 h-4" />
|
|
{eventCount > 0 && (
|
|
<span
|
|
className={cn(
|
|
'absolute -top-1 -right-1',
|
|
'h-4 min-w-4 px-0.5',
|
|
'inline-flex items-center justify-center',
|
|
'rounded-full bg-destructive text-white',
|
|
'text-[10px] font-medium leading-none'
|
|
)}
|
|
>
|
|
{eventCount > 99 ? '99+' : eventCount}
|
|
</span>
|
|
)}
|
|
</Button>
|
|
</PopoverTrigger_Shadcn_>
|
|
<PopoverContent_Shadcn_
|
|
side="left"
|
|
align="center"
|
|
sideOffset={4}
|
|
className="w-auto p-1"
|
|
onMouseEnter={handleMouseEnter}
|
|
onMouseLeave={handleMouseLeave}
|
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
>
|
|
<button
|
|
type="button"
|
|
className="flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-surface-200 text-foreground-light hover:text-foreground whitespace-nowrap"
|
|
onClick={handleDismiss}
|
|
>
|
|
<EyeOff className="w-4 h-4 shrink-0" />
|
|
<span>Hide</span>
|
|
</button>
|
|
</PopoverContent_Shadcn_>
|
|
</Popover_Shadcn_>
|
|
)
|
|
}
|