mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
feat(frontend): open foreign-workspace runs links in a new tab
When a runs-page link targets a workspace other than the active one,
navigating in the same tab would switch the current tab's workspace. Open
those links in a new tab instead so the current workspace is preserved.
- RunsTable dropdown ("Show run details", "Go to script/flow page"):
window.open when the job's workspace differs from the active one.
- RunRow parent-job links and RunBadges script link: target=_blank +
rel=noopener when foreign (the main run link in RunRow was already _blank).
JobDetailHeader already opens its links in new tabs; FlowMetadata is only
used on public /approve pages (not the runs page) so it is left unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,11 +29,20 @@
|
||||
onFilterByConcurrencyKey,
|
||||
large = false
|
||||
}: Props = $props()
|
||||
|
||||
// Open links to another workspace in a new tab so we don't switch the active
|
||||
// workspace of the current tab.
|
||||
let isForeignWorkspace = $derived(
|
||||
job.workspace_id != undefined && job.workspace_id !== $workspaceStore
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if job.script_hash && showScriptHash && job.job_kind !== 'aiagent'}
|
||||
{#if job.job_kind == 'script'}
|
||||
<a href="{base}/scripts/get/{job.script_hash}?workspace={job.workspace_id ?? $workspaceStore}"
|
||||
<a
|
||||
href="{base}/scripts/get/{job.script_hash}?workspace={job.workspace_id ?? $workspaceStore}"
|
||||
target={isForeignWorkspace ? '_blank' : undefined}
|
||||
rel={isForeignWorkspace ? 'noopener noreferrer' : undefined}
|
||||
><Badge color="gray" title={`Script hash: ${job.script_hash}`} {large}
|
||||
>{truncateHash(job.script_hash)}
|
||||
</Badge></a
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$lib/base'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { goto } from '$lib/navigation'
|
||||
import type { Job } from '$lib/gen'
|
||||
import {
|
||||
@@ -55,6 +56,12 @@
|
||||
|
||||
let isExternal = $derived(job && job.id === '-')
|
||||
|
||||
// Open links to another workspace in a new tab so we don't switch the active
|
||||
// workspace of the current tab.
|
||||
let isForeignWorkspace = $derived(
|
||||
job?.workspace_id != undefined && job.workspace_id !== $workspaceStore
|
||||
)
|
||||
|
||||
let labelWidth = $state(0)
|
||||
|
||||
let isJobRecent = $state(true)
|
||||
@@ -156,19 +163,27 @@
|
||||
{/if}
|
||||
<JobKindIcon size={14} />
|
||||
</div>
|
||||
{#snippet text()}
|
||||
<span>
|
||||
{#if job && job.job_kind}
|
||||
{getJobKindDisplayLabel(job.job_kind, job.script_path)}
|
||||
{/if}
|
||||
{#if job && job.is_flow_step && job.parent_job}
|
||||
{#snippet text()}
|
||||
<span>
|
||||
{#if job && job.job_kind}
|
||||
{getJobKindDisplayLabel(job.job_kind, job.script_path)}
|
||||
{/if}
|
||||
{#if job && job.is_flow_step && job.parent_job}
|
||||
<br /> Step of flow
|
||||
<a href={`${base}/run/${job.parent_job}?workspace=${job.workspace_id}`}>
|
||||
<a
|
||||
href={`${base}/run/${job.parent_job}?workspace=${job.workspace_id}`}
|
||||
target={isForeignWorkspace ? '_blank' : undefined}
|
||||
rel={isForeignWorkspace ? 'noopener noreferrer' : undefined}
|
||||
>
|
||||
{truncateRev(job.parent_job, 10)}
|
||||
</a>
|
||||
{:else if job && job.parent_job}
|
||||
<br /> Parent
|
||||
<a href={`${base}/run/${job.parent_job}?workspace=${job.workspace_id}`}>
|
||||
<a
|
||||
href={`${base}/run/${job.parent_job}?workspace=${job.workspace_id}`}
|
||||
target={isForeignWorkspace ? '_blank' : undefined}
|
||||
rel={isForeignWorkspace ? 'noopener noreferrer' : undefined}
|
||||
>
|
||||
{truncateRev(job.parent_job, 10)}
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
import DropdownMenu, { type Props as DropdownMenuProps } from '../DropdownMenu.svelte'
|
||||
import { clickOutside, isJobCancelable, isJobReRunnable } from '$lib/utils'
|
||||
import { goto } from '$lib/navigation'
|
||||
import { base } from '$lib/base'
|
||||
import BarsStaggered from '../icons/BarsStaggered.svelte'
|
||||
|
||||
interface Props {
|
||||
@@ -266,24 +267,35 @@
|
||||
// one, so target the job's own workspace (not the active one).
|
||||
const jobWorkspace =
|
||||
(job?.type === 'job' ? job.job.workspace_id : undefined) ?? $workspaceStore
|
||||
// When the job lives in a different workspace, open in a new tab so we
|
||||
// don't switch the active workspace of the current tab.
|
||||
const isForeignWorkspace = jobWorkspace != undefined && jobWorkspace !== $workspaceStore
|
||||
const navigateTo = (path: string) => {
|
||||
if (isForeignWorkspace) {
|
||||
window.open(`${base}${path}`, '_blank')
|
||||
} else {
|
||||
goto(path)
|
||||
}
|
||||
}
|
||||
actions.push({
|
||||
label: 'Show run details',
|
||||
icon: ExternalLinkIcon,
|
||||
onClick: () => goto(`/run/${selectedIds[0]}?workspace=${jobWorkspace}`)
|
||||
onClick: () => navigateTo(`/run/${selectedIds[0]}?workspace=${jobWorkspace}`)
|
||||
})
|
||||
if (job?.type === 'job') {
|
||||
if (job.job.job_kind === 'script') {
|
||||
actions.push({
|
||||
label: 'Go to script page',
|
||||
icon: Code2Icon,
|
||||
onClick: () => goto(`/scripts/get/${job.job.script_hash}?workspace=${jobWorkspace}`)
|
||||
onClick: () =>
|
||||
navigateTo(`/scripts/get/${job.job.script_hash}?workspace=${jobWorkspace}`)
|
||||
})
|
||||
}
|
||||
if (job.job.job_kind === 'flow') {
|
||||
actions.push({
|
||||
label: 'Go to flow page',
|
||||
icon: BarsStaggered,
|
||||
onClick: () => goto(`/flows/get/${job.job.script_path}?workspace=${jobWorkspace}`)
|
||||
onClick: () => navigateTo(`/flows/get/${job.job.script_path}?workspace=${jobWorkspace}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user