<script lang="ts">
import type { Attachment } from "svelte/attachments";
import type { PipelineSummary, WorkflowSummary } from "#lib/components/repo/types.ts";
import { goto } from "$app/navigation";
import { resolve } from "$app/paths";
import DropdownItem from "#lib/components/ui/DropdownItem.svelte";
import Popover from "#lib/components/repo/file/Popover.svelte";
import Elapsed from "#lib/components/repo/pipelines/Elapsed.svelte";
import PipelineMark from "#lib/components/repo/pipelines/PipelineMark.svelte";
import PipelineTriggerSeg from "#lib/components/repo/pipelines/PipelineTriggerSeg.svelte";
import PipelineRetryButton from "#lib/components/repo/pipelines/PipelineRetryButton.svelte";
);
const base = $derived(`/${ownerHandle}/${repoName}`);
const workflowHref = (name: string) => resolve(workflowPath(base, pipeline.id, name) as "/");
const centreCurrent =
(name: string): Attachment<HTMLElement> =>
(nav) => {
const current = nav.querySelector<HTMLElement>(`[title="${CSS.escape(name)}"]`);
if (!current) return;
const box = nav.getBoundingClientRect();
const at = current.getBoundingClientRect();
nav.scrollLeft += at.left - box.left - (box.width - at.width) / 2;
};
const others = $derived(
currentPipeline.workflows.filter((other) => other.name !== workflow.name)
);
const stamp = (time?: string) => (time ? Date.parse(time) : undefined);
const retryToNewRun = async () => {
if (!retry) return;
return () => current.destroy();
});
</script>
<div class="mb-3 flex flex-wrap items-center gap-x-3 gap-y-2 max-md:flex-nowrap">
{#snippet tab(other: WorkflowSummary)}
{@const open = other.name === workflow.name}
<a
href={workflowHref(other.name)}
class={["y-seg max-w-[32ch]", open && "y-seg-b font-semibold"]}
aria-current={open ? "page" : undefined}
title={other.name}
>
<PipelineMark workflows={[other]} swatch />
<span class="truncate">{other.name}</span>
</a>
{/snippet}
<div class="mb-3 flex items-center gap-x-3">
<span class="flex shrink-0 self-center">
<PipelineMark
workflows={currentPipeline.workflows}
run={{ base, id: currentPipeline.id }}
/>
</span>
<span class="y-rb min-w-0" data-testid="runRibbon">
<span class="y-seg min-w-[6ch] shrink-4 font-semibold" title={currentWorkflow.name}
><span class="truncate">{currentWorkflow.name}</span></span
>
<span class="y-rb min-w-0 flex-1" data-testid="runRibbon">
<!-- the open workflow leads, the others follow while the line has room, and all of them
are in the menu after -->
<nav class="tabs" aria-label="workflows in this run">
{@render tab(currentWorkflow)}
{#each others as other (other.name)}
{@render tab(other)}
{/each}
</nav>
{#if others.length}
<Popover label="all {currentPipeline.workflows.length} workflows">
{#snippet summary()}all {currentPipeline.workflows.length} ⌄{/snippet}
{#each currentPipeline.workflows as other (other.name)}
<DropdownItem
href={workflowHref(other.name)}
current={other.name === workflow.name}
>
{#snippet lead()}<PipelineMark workflows={[other]} swatch />{/snippet}
{other.name}
</DropdownItem>
{/each}
</Popover>
{/if}
<a
href={resolve(`${base}/commit/${currentPipeline.sha}` as "/")}
class="y-seg shrink-0 font-mono max-md:hidden">{currentPipeline.sha.slice(0, 8)}</a
>
{/if}
<TimeAgo value={currentPipeline.createdAt} class="ml-auto shrink-0 text-(--y-muted)" />
</div>
{#if currentPipeline.workflows.length > 1}
<nav
class="y-rb mb-3 overflow-x-auto"
aria-label="workflows in this run"
{@attach centreCurrent(workflow.name)}
>
{#each currentPipeline.workflows as other (other.name)}
<a
href={workflowHref(other.name)}
class={["y-seg max-w-[32ch] shrink-0", other.name === workflow.name && "y-seg-b"]}
aria-current={other.name === workflow.name ? "page" : undefined}
title={other.name}
>
<PipelineMark workflows={[other]} swatch />
<span class="truncate">{other.name}</span>
</a>
{/each}
</nav>
{/if}
<WorkflowView
workflow={currentWorkflow}
steps={stream.steps}
status={stream.status}
error={stream.error}
/>
<style>
/* one line tall: a workflow that wraps past it is clipped, not squeezed, and the room the
clipped ones leave stays the ribbon's, with the menu at its end */
.tabs {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
min-width: 0;
height: 1lh;
overflow: hidden;
}
.tabs > :first-child {
min-width: 6ch;
}
.tabs ~ :global(.y-seg) {
border-left: 1px solid var(--y-border);
}
</style>