Files
supabase/apps/studio/components/ui/AIAssistantPanel/EdgeFunctionRenderer.tsx
Saxon Fletcher f0cb024139 feat(studio): preserve assistant tool previews after completion (#49352)
<img width="2337" height="1005" alt="image"
src="https://github.com/user-attachments/assets/08298850-715e-4b31-866d-186d73266305"
/>


## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Assistant execution feedback improvement.

## Stack context

Builds on #49351.

## What is the current behavior?

When an Assistant query, notebook, Edge Function deployment, or log
query completes or fails, the preview can be replaced by a terse text
result.

## What is the new behavior?

- Retains the original query, log-query, notebook, and Edge Function
preview after the tool resolves.
- Replaces confirmation actions with a success, error, or skipped footer
state.
- Keeps the Open notebook action available after a successful notebook
creation or update.

## To test

1. Ask the Assistant to run a valid SQL query, approve it, and confirm
the query cell remains visible with a Query executed footer.
2. Trigger a failed SQL or log query and confirm the original preview
remains visible with an error footer and error result.
3. Ask the Assistant to create or update a notebook, approve it, and
confirm the preview remains visible with a completed footer and Open
notebook action.
4. Skip any approval and confirm the preview remains visible with a
skipped footer instead of being replaced by plain text.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Assistant actions now show clear success, error, or denied-status
messages.
* Completed actions retain relevant previews and provide follow-up
actions, such as opening a created notebook.
* SQL, log-query, Edge Function, and notebook errors appear within their
respective result views.
* Status updates are announced more clearly as actions progress and
complete.

* **Bug Fixes**
* Preserved submitted tool details when execution fails or original
input is unavailable.
* Improved handling of failed and denied operations across assistant
workflows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-21 10:12:00 +00:00

117 lines
3.6 KiB
TypeScript

import { useParams } from 'common'
import { useMemo, useState } from 'react'
import { EdgeFunctionBlock } from '../EdgeFunctionBlock/EdgeFunctionBlock'
import { Confirm } from './Confirm'
import { type ConfirmFooterApprovalState } from './Confirm.utils'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query'
import { useTrack } from '@/lib/telemetry/track'
interface EdgeFunctionRendererProps {
label: string
code: string
functionName: string
onApprove?: () => void
onDeny?: () => void
isDeploying?: boolean
initialIsDeployed?: boolean
errorText?: string
confirmState?: ConfirmFooterApprovalState
}
export const EdgeFunctionRenderer = ({
label,
code,
functionName,
onApprove,
onDeny,
isDeploying = false,
initialIsDeployed,
errorText,
confirmState,
}: EdgeFunctionRendererProps) => {
const { ref } = useParams()
const track = useTrack()
const [showReplaceWarning, setShowReplaceWarning] = useState(false)
const { data: settings } = useProjectSettingsV2Query({ projectRef: ref }, { enabled: !!ref })
const { data: existingFunction } = useEdgeFunctionQuery(
{ projectRef: ref, slug: functionName },
{ enabled: !!ref && !!functionName && !initialIsDeployed }
)
const functionUrl = useMemo(() => {
const endpoint = settings?.app_config?.endpoint
const protocol = settings?.app_config?.protocol ?? 'https'
if (!endpoint || !ref || !functionName) return undefined
return `${protocol}://${endpoint}/functions/v1/${functionName}`
}, [settings?.app_config?.endpoint, settings?.app_config?.protocol, ref, functionName])
const deploymentDetailsUrl = useMemo(() => {
if (!ref || !functionName) return undefined
return `/project/${ref}/functions/${functionName}/details`
}, [ref, functionName])
const downloadCommand = useMemo(() => {
if (!functionName) return undefined
return `supabase functions download ${functionName}`
}, [functionName])
const approveDeploy = () => {
if (!code || isDeploying || !ref || !functionName) return
setShowReplaceWarning(false)
track('edge_function_deploy_button_clicked', { origin: 'functions_ai_assistant' })
onApprove?.()
}
const handleDeploy = () => {
if (!code || isDeploying || !ref || !functionName) return
if (existingFunction) {
setShowReplaceWarning(true)
return
}
approveDeploy()
}
const isConfirming = confirmState !== undefined
return (
<Confirm
className="my-4"
state={confirmState}
message="Assistant wants to deploy this Edge Function"
cancelLabel="Skip"
confirmLabel="Deploy"
confirmLabelLoading="Deploying..."
successMessage="Edge Function deployed"
errorMessage="Failed to deploy Edge Function"
deniedMessage="Skipped Edge Function deployment"
isLoading={isDeploying}
onCancel={onDeny}
onConfirm={handleDeploy}
>
<EdgeFunctionBlock
className="rounded-none border-0 shadow-none"
label={label}
code={code}
functionName={functionName}
disabled={isConfirming}
isDeploying={isDeploying}
isDeployed={initialIsDeployed}
errorText={errorText}
functionUrl={functionUrl}
deploymentDetailsUrl={deploymentDetailsUrl}
downloadCommand={downloadCommand}
hideDeployButton={isConfirming || initialIsDeployed}
showReplaceWarning={showReplaceWarning}
onCancelReplace={() => setShowReplaceWarning(false)}
onConfirmReplace={approveDeploy}
/>
</Confirm>
)
}