+ This run exists in {workspaceId}, but you don't have
+ permission to view it.
+
+
+ Ask a colleague who can see it to open the run and use the
+ Share button to send you a read-only link. Opening that
+ link will grant you access to this run (and its steps).
+
+ /*
+ * A run in a session preview tab, rendered by the component the workspace `/run/` page
+ * renders. Everything it reaches is scoped to the run's workspace through `workspace`, and
+ * every navigation it attempts is caught here and turned into a move inside the preview
+ * panel, so nothing takes the browser out of the session.
+ */
+ import { setContext, untrack } from 'svelte'
+ import RunDetail from '$lib/components/runs/RunDetail.svelte'
+ import type { SessionRuntime } from './sessionRuntime.svelte'
+ import {
+ parseHistoricalScriptEdit,
+ parsePreviewItemRoute,
+ parseRunRoute,
+ previewLocationLabel,
+ barePath
+ } from './previewRouter'
+
+ let {
+ runtime,
+ jobId,
+ workspaceId,
+ tabId,
+ container,
+ reloadNonce = 0
+ }: {
+ runtime: SessionRuntime
+ jobId: string
+ workspaceId: string
+ /** The preview tab hosting this run, which a move to another run re-points. */
+ tabId: string
+ /** The tab's host element, which also holds the overlays the page portals out. */
+ container: HTMLElement | undefined
+ /** Bumped to load the run again from the server. */
+ reloadNonce?: number
+ } = $props()
+
+ // Mounted in the preview panel, outside the chat's own subtree, as ItemViewerView is: the
+ // run page's AI entry points would otherwise offer a session from inside one.
+ setContext(
+ 'aiChatManager',
+ untrack(() => runtime.manager)
+ )
+
+ // Everything the run page navigates to, resolved against the preview panel. Another run —
+ // a re-run, a retry, the parent job — is still this tab's subject, so it re-points in
+ // place; an item or any other page opens beside it. Nothing reaches the top-level router.
+ function onNavigate(url: string): void {
+ const parsed = new URL(url, window.location.origin)
+ const href = `${parsed.pathname}${parsed.search}${parsed.hash}`
+ if (parseRunRoute(href)) {
+ runtime.previewTabs.navigate({ type: 'page', href, label: previewLocationLabel(href) }, tabId)
+ return
+ }
+ const route = parsePreviewItemRoute(href)
+ // Legacy drag-and-drop apps and historical script edits have no in-panel host, so they
+ // fall through to a page tab. So does `Run again`, whose args ride in the fragment, and
+ // a link to one version: opening an item focuses a tab already on it as it stands,
+ // where a page tab's URL is written whole, pin and fragment included.
+ if (
+ route &&
+ (route.kind !== 'app' || route.raw_app) &&
+ !parseHistoricalScriptEdit(href) &&
+ !parsed.hash &&
+ !parsed.searchParams.has('version')
+ ) {
+ runtime.previewTabs.open({
+ type: 'item',
+ item: { path: route.itemPath, summary: '', kind: route.kind, raw_app: route.raw_app },
+ mode: route.mode
+ })
+ return
+ }
+ runtime.previewTabs.open({ type: 'page', href, label: previewLocationLabel(barePath(href)) })
+ }
+
+ // Every in-app link under the tab, not only the ones that call `onNavigate`: a plain anchor
+ // followed by the browser would take it out of the session. Listened for on `body`, as
+ // ItemViewerView does and for the same reason.
+ $effect(() => {
+ const el = container
+ if (!el) return
+ function onClick(e: MouseEvent) {
+ if (e.defaultPrevented || e.button !== 0) return
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return
+ const target = e.target as Element | null
+ if (!target || !el!.contains(target)) return
+ const a = target.closest?.('a[href]') as HTMLAnchorElement | null
+ if (!a || (a.target && a.target !== '_self') || a.hasAttribute('download')) return
+ const url = new URL(a.href, window.location.href)
+ if (url.origin !== window.location.origin) return
+ e.preventDefault()
+ if (a.getAttribute('href')?.startsWith('#')) return
+ onNavigate(`${url.pathname}${url.search}${url.hash}`)
+ }
+ document.body.addEventListener('click', onClick)
+ return () => document.body.removeEventListener('click', onClick)
+ })
+
+
+
- This run exists in {$workspaceStore}, but you don't
- have permission to view it.
-
-
- Ask a colleague who can see it to open the run and use the
- Share button to send you a read-only link. Opening that
- link will grant you access to this run (and its steps).
-
- No run with ID
- {page.params.run}
- exists in
- {currentWorkspace?.name ?? $workspaceStore}. It may belong to another workspace, or it may have been deleted.
-
-
-
- {#if otherWorkspaces.length > 0}
-
-
Look in another workspace
-
- {#each otherWorkspaces as workspace (workspace.id)}
-
- {/each}
-
-
- {/if}
-
-
-
-
-
-{:else}
-
-
-
-
- {#snippet runActions()}
- {@const isScript = job?.job_kind === 'script'}
- {@const isHubFlowPreview = isFlowPreview(job?.job_kind) && isHubFlowPath(job?.script_path)}
- {@const runsHref = `/runs/${job?.script_path}${!isScript ? '?jobKind=flow' : ''}`}
- {#if job && 'deleted' in job && !job?.deleted && ($superadmin || ($userStore?.is_admin ?? false))}
- {
- job?.id && deleteCompletedJob(job.id)
- },
- type: 'delete'
- }
- ]}
- >
- {#snippet buttonReplacement()}
-
- {/snippet}
-
- {#if job?.job_kind === 'script' || job?.job_kind === 'flow'}
-
- {/if}
- {/if}
- {#if job}
- job && shareReadLink(job.id)
- },
- {
- displayName: 'Copy public link',
- icon: Globe,
- disabled: !canSharePublicly,
- tooltip: canSharePublicly
- ? "Read-only link that anyone on the internet can open, without logging in. It shows a minimal version of this page: this run's inputs, result and logs, and for a flow its graph plus every step's inputs, result, logs and code. The link cannot be revoked."
- : 'Sharing a run publicly is restricted in this workspace. Ask an admin to share it, or to grant you a bypass on the ruleset.',
- action: () => job && sharePublicLink(job.id)
- }
- ]}
- >
- {#snippet buttonReplacement()}
-
- {/snippet}
-
- {/if}
- {@const stem = job?.job_kind === 'script_hub' ? '/scripts' : `/${job?.job_kind}s`}
- {@const viewHref = `${stem}/get/${isScript ? job?.script_hash : job?.script_path}`}
- {#if (job?.job_kind == 'flow' || isFlowPreview(job?.job_kind)) && job?.['running'] && job?.parent_job == undefined}
-