mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## Summary - Unify Explorer toolbar actions: 16px / 2px Lucide icons, `text-tertiary-foreground` that becomes `text-foreground` on hover (Analyze icon goes brand on hover). - Soften chat scroll edges with top/bottom fades, and align the composer width with the conversation content (`px-7` + `max-w-3xl`). - Put **Run SQL** first on Explorer home, and rename the tab-bar new-tab item from “New query” to **Run SQL** so it matches. ## Test plan - [ ] Open Explorer and check query, notebook, and chat toolbars: icons are 16px, muted by default, and go to foreground on hover. Analyze on a notebook with cells: icon goes brand on hover; empty notebook still disables Analyze. - [ ] Open a query tab: source menu, result settings, save, and more-options all look like the other toolbar actions (including while the dropdown is open). - [ ] Open Explorer chat: scroll a long thread and confirm top/bottom fades sit on the chat surface. Composer lines up with message width (not inset extra). - [ ] On Explorer home, **Run SQL** is the first card; clicking it still opens a SQL tab. - [ ] From the tab bar **+** menu, the first item is **Run SQL** (not “New query”); it still creates a SQL tab. **New notebook** and **New chat** still work. Made with [Cursor](https://cursor.com) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **UI Improvements** * Standardized Explorer toolbar icons for consistent sizing and visual weight. * Updated toolbar action colors, hover states, and keyboard-focus visibility. * Reordered Explorer home actions so “Run SQL” appears first. * Renamed “New query” to “Run SQL” in the new-tab menu. * Improved query source and settings toolbar controls. * Refined AI assistant chat layout with centered content, decorative gradients, and improved focus styling. * Added hover styling for the notebook Analyze action. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
5.4 KiB
TypeScript
151 lines
5.4 KiB
TypeScript
import { useFlag, useParams } from 'common'
|
|
import { Check, ChevronDown } from 'lucide-react'
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from 'ui'
|
|
|
|
import { RowLimitSubMenu } from '../../SQLEditor/UtilityPanel/QuerySourceMenu/RowLimitSubMenu'
|
|
import { RunAsSubMenu } from '../../SQLEditor/UtilityPanel/QuerySourceMenu/RunAsSubMenu'
|
|
import { ExplorerToolbarAction } from '../ExplorerToolbar'
|
|
import { DatabaseParametersSubMenu } from '@/components/interfaces/QuerySources/DatabaseParametersSubMenu'
|
|
import { LogsCustomRangeDialog } from '@/components/interfaces/QuerySources/LogsCustomRangeDialog'
|
|
import { LogsTimeRangeSubMenu } from '@/components/interfaces/QuerySources/LogsTimeRangeSubMenu'
|
|
import { QuerySourceIcon } from '@/components/interfaces/QuerySources/QuerySourceIcon'
|
|
import { useLogsCustomRange } from '@/components/interfaces/QuerySources/useLogsCustomRange'
|
|
import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
|
|
import {
|
|
createDefaultSourceBinding,
|
|
QUERY_SOURCE_LABELS,
|
|
QUERY_SOURCES,
|
|
type QuerySourceBinding,
|
|
} from '@/data/query-sources/query-source-registry'
|
|
import { type RoleImpersonationController } from '@/state/role-impersonation-state'
|
|
|
|
export type QuerySourceMenuProps = {
|
|
disabled?: boolean
|
|
rowLimit?: number
|
|
onRowLimitChange?: (val: number) => void
|
|
roleImpersonationState?: RoleImpersonationController
|
|
source: QuerySourceBinding
|
|
onSourceChange: (source: QuerySourceBinding) => void
|
|
}
|
|
|
|
/**
|
|
* Source binding and parameter controls shared by standalone Explorer queries
|
|
* and notebook query-cell toolbars. The consumer owns the binding; this menu
|
|
* only emits complete, validated-by-construction `QuerySourceBinding` values.
|
|
*
|
|
* Selecting a different backend emits that backend's default binding — deciding
|
|
* what happens to the query body is the consumer's call, since a notebook cell
|
|
* has SQL to preserve or discard and a fresh draft does not.
|
|
*/
|
|
export const QuerySourceMenu = ({
|
|
disabled = false,
|
|
rowLimit = 100,
|
|
onRowLimitChange,
|
|
roleImpersonationState,
|
|
source,
|
|
onSourceChange,
|
|
}: QuerySourceMenuProps) => {
|
|
const { ref } = useParams()
|
|
const isLogsSourceEnabled = useFlag('sqlEditorLogsSource')
|
|
const isOtelLogsEnabled = useFlag('otelLegacyLogs')
|
|
const {
|
|
isCustomRangeOpen,
|
|
setIsCustomRangeOpen,
|
|
showUpgradePrompt,
|
|
setShowUpgradePrompt,
|
|
handleApplyCustomRange,
|
|
} = useLogsCustomRange({
|
|
onRangeChange: (time_range) => onSourceChange({ _tag: 'logs', time_range }),
|
|
})
|
|
|
|
const availableSources = QUERY_SOURCES.filter(
|
|
(candidate) =>
|
|
candidate._tag !== 'logs' ||
|
|
(isLogsSourceEnabled && isOtelLogsEnabled) ||
|
|
source._tag === 'logs'
|
|
)
|
|
|
|
return (
|
|
<>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<ExplorerToolbarAction
|
|
disabled={disabled}
|
|
aria-label={`Query source: ${QUERY_SOURCE_LABELS[source._tag]}`}
|
|
icon={<QuerySourceIcon source={source._tag} />}
|
|
iconRight={<ChevronDown size={16} strokeWidth={2} />}
|
|
>
|
|
{QUERY_SOURCE_LABELS[source._tag]}
|
|
</ExplorerToolbarAction>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-60">
|
|
{availableSources.map((candidate) => (
|
|
<DropdownMenuItem
|
|
key={candidate._tag}
|
|
className="justify-between"
|
|
onSelect={(event) => {
|
|
event.preventDefault()
|
|
if (candidate._tag !== source._tag) {
|
|
onSourceChange(createDefaultSourceBinding(candidate._tag))
|
|
}
|
|
}}
|
|
>
|
|
<span className="flex items-center gap-x-2">
|
|
<QuerySourceIcon source={candidate._tag} className="text-foreground-light" />
|
|
{QUERY_SOURCE_LABELS[candidate._tag]}
|
|
</span>
|
|
{source._tag === candidate._tag && <Check size={14} />}
|
|
</DropdownMenuItem>
|
|
))}
|
|
|
|
<DropdownMenuSeparator />
|
|
|
|
{source._tag === 'database' ? (
|
|
<>
|
|
<DatabaseParametersSubMenu
|
|
identifier={source.database_identifier ?? ref}
|
|
onIdentifierChange={(database_identifier) =>
|
|
onSourceChange({ _tag: 'database', database_identifier })
|
|
}
|
|
/>
|
|
{roleImpersonationState !== undefined && (
|
|
<RunAsSubMenu controlled state={roleImpersonationState} />
|
|
)}
|
|
{onRowLimitChange !== undefined && (
|
|
<RowLimitSubMenu
|
|
value={rowLimit}
|
|
onValueChange={(val) => onRowLimitChange(Number(val))}
|
|
/>
|
|
)}
|
|
</>
|
|
) : (
|
|
<LogsTimeRangeSubMenu
|
|
range={source.time_range}
|
|
onRangeChange={(time_range) => onSourceChange({ _tag: 'logs', time_range })}
|
|
onOpenCustomRange={() => setIsCustomRangeOpen(true)}
|
|
onShowUpgrade={() => setShowUpgradePrompt(true)}
|
|
/>
|
|
)}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
|
|
{source._tag === 'logs' && (
|
|
<>
|
|
<LogsCustomRangeDialog
|
|
open={isCustomRangeOpen}
|
|
onOpenChange={setIsCustomRangeOpen}
|
|
onApply={handleApplyCustomRange}
|
|
/>
|
|
<UpgradePrompt show={showUpgradePrompt} setShowUpgradePrompt={setShowUpgradePrompt} />
|
|
</>
|
|
)}
|
|
</>
|
|
)
|
|
}
|