diff --git a/frontend/src/lib/components/S3FilePreview.svelte b/frontend/src/lib/components/S3FilePreview.svelte index 6e3de40668..2a7c3977cd 100644 --- a/frontend/src/lib/components/S3FilePreview.svelte +++ b/frontend/src/lib/components/S3FilePreview.svelte @@ -37,6 +37,11 @@ // metadata out themselves (the picker already has its own). showMetadata?: boolean class?: string + // Bump this to force a re-fetch (metadata + preview). Used by the + // asset detail pane after an upstream producer run completes — + // without it, the "Asset not yet materialized" empty state stays + // pinned until the user re-selects the asset. + refreshKey?: any } let { @@ -45,7 +50,8 @@ loadFilePreviewRequest = HelpersService.loadFilePreview, loadFileMetadataRequest = HelpersService.loadFileMetadata, showMetadata = false, - class: className = '' + class: className = '', + refreshKey }: Props = $props() let csvSeparatorChar: string = $state(',') @@ -91,12 +97,15 @@ return body.includes('not found') || body.includes('404') } - // Reload whenever the file key changes. Tracking the workspace too — - // the asset graph spans workspaces so a re-mount with the same key but - // a different ws should refetch. + // Reload whenever the file key, workspace, or external refreshKey + // changes. The refreshKey path is what lets the asset pane re-check + // existence after an upstream run completes — moving from the + // "not yet materialized" empty state to the actual preview without + // requiring the user to re-click the asset. $effect(() => { const key = fileKey const ws = $workspaceStore + void refreshKey if (!key || !ws) { fileMetadata = undefined filePreview = undefined diff --git a/frontend/src/lib/components/assets/AssetGraph/AssetGraphDetailsPane.svelte b/frontend/src/lib/components/assets/AssetGraph/AssetGraphDetailsPane.svelte index 777c8fba17..67719e94f2 100644 --- a/frontend/src/lib/components/assets/AssetGraph/AssetGraphDetailsPane.svelte +++ b/frontend/src/lib/components/assets/AssetGraph/AssetGraphDetailsPane.svelte @@ -23,6 +23,7 @@ import { parsePipelineAnnotations, type PipelineAnnotations } from './parsePipelineAnnotations' import SummaryPathDisplay from '$lib/components/SummaryPathDisplay.svelte' import S3FilePreview from '$lib/components/S3FilePreview.svelte' + import DataTablePreview from './DataTablePreview.svelte' import AssetRunsPanel from './AssetRunsPanel.svelte' import { Pane, Splitpanes } from 'svelte-splitpanes' import { fade } from 'svelte/transition' @@ -69,6 +70,9 @@ // re-fetches the listing immediately (rather than waiting on its // background poll tick). runsRefreshKey?: any + // Job id of the most recently dispatched run. Forwarded to the + // runs panel so that clicking play auto-selects the new run. + runsPendingJobId?: string | undefined } let { selection, @@ -81,9 +85,17 @@ onScriptRenamed, onScriptRemoved, selectionProducers = [], - runsRefreshKey + runsRefreshKey, + runsPendingJobId }: Props = $props() + // Bumped when the runs panel reports a watched job has reached a + // terminal state. Drives S3FilePreview's refreshKey so the preview + // re-checks existence after a producer run finishes — moves the + // "not yet materialized" empty state to the actual preview without + // requiring the user to re-click the asset. + let previewRefreshKey = $state(0) + // When `draftScript` is provided we bypass the fetch entirely and edit // it locally; saving calls ScriptService.createScript to deploy it. let scriptRes = resource( @@ -334,25 +346,38 @@