diff --git a/frontend/src/lib/components/sessions/PreviewTabHost.svelte b/frontend/src/lib/components/sessions/PreviewTabHost.svelte index 893e85e798..282815a165 100644 --- a/frontend/src/lib/components/sessions/PreviewTabHost.svelte +++ b/frontend/src/lib/components/sessions/PreviewTabHost.svelte @@ -22,6 +22,7 @@ import type { EntityEditorKind } from './previewRouter' import type { EntityToolEffect } from './previewReload' import { withMenuHidden } from './sessionMode.svelte' + import { pageHref, RUNS_PATH } from './previewPaths' import ArtifactViewer from '../copilot/chat/artifacts/ArtifactViewer.svelte' import { setOverlayHost } from '../common/overlayHost.svelte' @@ -193,6 +194,21 @@ : undefined ) + // "View runs" from a hosted editor: the Runs page is a preview page like any + // other, so it belongs in the preview rather than in the top-level document the + // anchor would navigate — and the frame gives it the acting workspace. Re-points + // this tab, like the way back to the list: the runs are where the editor was. + const viewRuns = $derived( + runtime + ? (query: string) => + runtime.previewTabs.navigate({ + type: 'page', + href: `${pageHref(RUNS_PATH)}?${query}`, + label: 'Runs' + }) + : undefined + ) + // What an editor reports is about an item, not about this tab: other warm // sessions can hold the same one, and by the time the write it awaited returns // this tab may hold something else. The page resolves both by matching on the @@ -413,6 +429,7 @@ onBack={backToList} onRemoved={(from, ws) => reportRemoved?.('trigger_schedule', from, ws)} onSavedTo={(to, from, ws) => reportSaved?.('trigger_schedule', to, from, ws)} + onViewRuns={viewRuns} /> {/await} {:else if slot.entityKind === 'resource'} diff --git a/frontend/src/lib/components/sessions/ScheduleEditorView.svelte b/frontend/src/lib/components/sessions/ScheduleEditorView.svelte index d9836e1182..b350889961 100644 --- a/frontend/src/lib/components/sessions/ScheduleEditorView.svelte +++ b/frontend/src/lib/components/sessions/ScheduleEditorView.svelte @@ -10,7 +10,8 @@ workspaceId, onBack, onRemoved, - onSavedTo + onSavedTo, + onViewRuns }: { /** The schedule this tab edits (the row its location deep-links). */ path: string @@ -29,6 +30,8 @@ * ACTIVE PREVIEW and the draft key all do — and a plain save moves none but * leaves the others holding a baseline the deploy has replaced. */ onSavedTo?: (newPath: string, fromPath: string, fromWorkspace: string) => void + /** Show this schedule's runs, given the Runs page's filter query. */ + onViewRuns?: (query: string) => void } = $props() // Captured at init, so it must read the current prop rather than close over it. @@ -70,6 +73,7 @@ useDrawer={false} showDraftBanner {onRemoved} + {onViewRuns} onUpdate={(saved: string | undefined, from: string, fromWs: string, keptEdit: boolean) => { if (from === path && !keptEdit) generation++ // A draft-only schedule opens with its path editable (saving CREATEs), so diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index e6beb46430..beaf2b5c89 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -76,7 +76,12 @@ // The schedule is gone — a draft-only one whose draft the banner discarded — // with the workspace and path it was showing. A host addressing it by those // (a session tab) has to stop showing it. - onRemoved = undefined + onRemoved = undefined, + // Where "View runs" goes, given the filter query for this schedule. A host + // mounted inside a page of its own (a session tab) takes it: the plain + // anchor navigates the whole app, which from there means leaving the session + // — and lands on the navigation workspace rather than the acting one. + onViewRuns = undefined } = $props() let optionTabSelected: @@ -893,13 +898,15 @@ > {#snippet extra()} {#if !drawerLoading && edit} + {@const runsQuery = `schedule_path=${path}&job_trigger_kind=schedule&show_future_jobs=true`}