Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
86b5759
WIP purple button timeline
Alex-Tideman Oct 24, 2025
ce9644b
Add purple for bg
Alex-Tideman Oct 28, 2025
21eb11f
Move input/result to top of card
Alex-Tideman Oct 29, 2025
1ddff20
Fix pending activities
Alex-Tideman Oct 29, 2025
fbdaa1e
Lighter purple
Alex-Tideman Oct 30, 2025
65ac34c
Fix the hover length, add durations
Alex-Tideman Oct 31, 2025
7c91583
Add the colors per category
Alex-Tideman Oct 31, 2025
6f29967
Better titles
Alex-Tideman Oct 31, 2025
696a65c
More pretty things
Alex-Tideman Oct 31, 2025
f6f1511
More styles
Alex-Tideman Oct 31, 2025
79d6c2d
Remove inspect
Alex-Tideman Nov 3, 2025
7c5d88c
Merge branch 'main' into timeline-purple-hover
Alex-Tideman Nov 4, 2025
b632bc4
Move group title into event-details-full
Alex-Tideman Nov 4, 2025
368682f
Styling things
Alex-Tideman Nov 5, 2025
86628ea
Fix category check
Alex-Tideman Nov 5, 2025
ee69a2d
More refactoring, fix task failure card
Alex-Tideman Nov 6, 2025
5fdbb20
Go back to cards
Alex-Tideman Nov 6, 2025
9a87cc3
More leading-3
Alex-Tideman Nov 6, 2025
5ebb5f5
Better colors
Alex-Tideman Nov 6, 2025
b5a9314
Move things around again
Alex-Tideman Nov 6, 2025
2340a1a
Merge branch 'main' into event-history-colors
Alex-Tideman Nov 10, 2025
5097e76
Merge branch 'main' into event-history-colors
Alex-Tideman Nov 10, 2025
8d9fc86
Move timeline to it's own tab, refactor to svelte5, rearrange
Alex-Tideman Nov 12, 2025
b075e16
Fix merge conflicts
Alex-Tideman Nov 18, 2025
db36867
Style updates
laurakwhit Nov 20, 2025
231ed7c
Adjust some colors
laurakwhit Nov 22, 2025
86aa56c
WIP toggle timeline and history buttons
laurakwhit Nov 22, 2025
ca99f90
Fix merge conflicts?
Alex-Tideman Dec 1, 2025
1bfe182
Fix pending activities to show input. Fix pending nexus operations
Alex-Tideman Dec 1, 2025
16f29b7
Merge branch 'main' into event-history-colors
Alex-Tideman Dec 4, 2025
bef2ffa
Remove mb
Alex-Tideman Dec 4, 2025
88b4242
Remove inspect
Alex-Tideman Dec 4, 2025
00f326a
Update src/lib/components/event/event-details-full.svelte
Alex-Tideman Dec 4, 2025
3be6fa9
Change dot to rect
Alex-Tideman Dec 12, 2025
570c156
Move things around
Alex-Tideman Dec 12, 2025
1c5c71d
Move things around more
Alex-Tideman Dec 12, 2025
a499da9
Make overlay resizeable
Alex-Tideman Dec 15, 2025
1e0cdcc
Merge main
Alex-Tideman Dec 15, 2025
1eb869d
Merge main
Alex-Tideman Jan 5, 2026
a253c68
Fix type errors
Alex-Tideman Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/lib/components/activity/activity-commands.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
>
<Button
variant="secondary"
size="sm"
size="xs"
leadingIcon={activity.paused ? 'play' : 'pause'}
on:click={onPause}
>
Expand All @@ -66,20 +66,20 @@
: translate('workflows.pause')}
</Button>
</Tooltip>
<Tooltip bottomLeft width={200} text="Update this Activity Options.">
<Tooltip bottom width={200} text="Update this Activity Options">
<Button
variant="secondary"
size="sm"
size="xs"
leadingIcon="pencil"
on:click={onUpdate}
>
{translate('common.update')}
</Button>
</Tooltip>
<Tooltip bottom width={200} text="Reset this Activity.">
<Tooltip bottom width={200} text="Reset this Activity">
<Button
variant="secondary"
size="sm"
size="xs"
leadingIcon="retry"
on:click={onReset}
>
Expand Down
208 changes: 133 additions & 75 deletions src/lib/components/event/event-card.svelte
Original file line number Diff line number Diff line change
@@ -1,46 +1,53 @@
<script lang="ts">
import { cva } from 'class-variance-authority';
import { type ClassNameValue, twMerge as merge } from 'tailwind-merge';
import { page } from '$app/state';
import Timestamp from '$lib/components/timestamp.svelte';
import Badge from '$lib/holocene/badge.svelte';
import CodeBlock from '$lib/holocene/code-block.svelte';
import Copyable from '$lib/holocene/copyable/index.svelte';
import Icon from '$lib/holocene/icon/icon.svelte';
import Link from '$lib/holocene/link.svelte';
import { translate } from '$lib/i18n/translate';
import type { EventLink as ELink } from '$lib/types';
import { type Payload } from '$lib/types';
import type { WorkflowEvent } from '$lib/types/events';
import { getEventLinkHref } from '$lib/utilities/event-link-href';
import {
format,
spaceBetweenCapitalLetters,
} from '$lib/utilities/format-camel-case';
import { format } from '$lib/utilities/format-camel-case';
import { formatAttributes } from '$lib/utilities/format-event-attributes';
import { formatDistanceAbbreviated } from '$lib/utilities/format-time';
import {
displayLinkType,
getCodeBlockValue,
getStackTrace,
shouldDisplayAsTime,
} from '$lib/utilities/get-single-attribute-for-event';
import { isLocalActivityMarkerEvent } from '$lib/utilities/is-event-type';
import { routeForNamespace } from '$lib/utilities/route-for';
import Timestamp from '../timestamp.svelte';
import EventDetailsLink from './event-details-link.svelte';
import MetadataDecoder from './metadata-decoder.svelte';
import PayloadDecoder from './payload-decoder.svelte';
let { event }: { event: WorkflowEvent } = $props();
interface Props {
event: WorkflowEvent;
nextEvent?: WorkflowEvent;
class?: ClassNameValue;
}
let { event, nextEvent, class: className = '' }: Props = $props();
const displayName = $derived(
isLocalActivityMarkerEvent(event)
? translate('events.category.local-activity')
: spaceBetweenCapitalLetters(event.name),
);
const attributes = $derived(formatAttributes(event));
const fields = $derived(Object.entries(attributes));
const payloadFields = $derived(
fields.filter(
([_key, value]) =>
typeof value === 'object' && Object.keys(value).length > 0,
([key, value]) =>
typeof value === 'object' &&
Object.keys(value).length > 0 &&
key !== 'input' &&
key !== 'result',
),
);
const linkFields = $derived(
Expand All @@ -49,6 +56,16 @@
),
);
const durationBetweenEvents = (
eventA: WorkflowEvent,
eventB: WorkflowEvent,
) =>
formatDistanceAbbreviated({
start: eventA?.eventTime,
end: eventB?.eventTime,
includeMilliseconds: true,
});
const hiddenDetailFields = $derived.by(() => {
if (event.category === 'activity')
return ['scheduledEventId', 'startedEventId', 'namespaceId'];
Expand All @@ -66,74 +83,115 @@
(key === 'namespace' && page.params.namespace !== value)),
),
);
const eventCategory = cva(
['flex flex-1 flex-col overflow-hidden rounded-t-lg pb-2'],
{
variants: {
classification: {
Failed: 'bg-red-800',
Canceled: 'bg-orange-900/80',
TimedOut: 'bg-orange-700',
Completed: 'bg-green-700',
New: 'bg-blue-800/50',
Open: 'bg-blue-800/50',
Running: 'bg-blue-800/50',
Terminated: 'bg-gray-300',
Scheduled: 'bg-orange-900/50',
Initiated: 'bg-orange-900/50',
Started: 'bg-orange-900/60',
Fired: 'bg-orange-900/90',
Signaled: 'bg-orange-900/50',
CancelRequested: 'bg-orange-900/70',
Unspecified: 'bg-orange-900/60',
},
},
},
);
</script>

<div
class="surface-primary flex flex-1 cursor-default flex-col gap-2 border-b border-subtle p-4"
class={merge(
eventCategory({
classification: event.classification,
}),
className,
)}
>
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="flex items-center gap-2 text-base">
<p class="font-mono">{event.id}</p>
<p class="font-medium">
{displayName}
</p>
</div>
<Timestamp as="p" class="text-sm" dateTime={event.eventTime} />
</div>
<div class="flex flex-col gap-1 xl:flex-row">
<div class="flex w-full flex-col gap-1 xl:w-1/2">
{#if event?.links?.length}
{@render eventLinks(event.links)}
{/if}
{#if event?.userMetadata?.summary}
{@render eventSummary(event.userMetadata.summary)}
<div class="bg-slate-900/60 p-2 pb-1 text-left">
<div class="flex flex-col items-center justify-between lg:flex-row">
<div>
<p class="leading-tight">
<span class="font-mono">{event.id}</span>
<span class="font-medium">
{event.name}
</span>
</p>
<p class="text-xs text-white/70">
<Timestamp dateTime={event.eventTime} />
</p>
</div>
{#if nextEvent}
<Badge type="default" class="flex items-center gap-1">
<Icon name="clock" />
{durationBetweenEvents(event, nextEvent) || '0ms'}
</Badge>
{/if}
{#each detailFields as [key, value] (key)}
{@render details(key, value)}
{/each}
{#each linkFields as [key, value] (key)}
{@render link(key, value)}
{/each}
</div>
{#if payloadFields.length}
<div class="flex w-full flex-col gap-1 xl:w-1/2">
{#each payloadFields as [key, value] (key)}
{@render payloads(key, value)}
{/each}
</div>
</div>

<div class="grid grid-cols-1 gap-0.5 p-2 md:grid-cols-2 xl:grid-cols-1">
{#if event?.links?.length}
{@render eventLinks(event.links)}
{/if}
{#if event?.userMetadata?.summary}
{@render eventSummary(event.userMetadata.summary)}
{/if}
{#each detailFields as [key, value] (key)}
{@render details(key, value)}
{/each}
{#each linkFields as [key, value] (key)}
{@render link(key, value)}
{/each}
</div>
{#if payloadFields.length}
<div class="flex w-full flex-col gap-0.5 px-2">
{#each payloadFields as [key, value] (key)}
{@render payloads(key, value)}
{/each}
</div>
{/if}
</div>

{#snippet eventLink(link: ELink)}
{@const href = getEventLinkHref(link)}
{@const value = href.split('workflows/')?.[1] || href}
<div class="flex items-start gap-4">
<p class="min-w-56 text-sm text-secondary/80">
<div class="leading-tight">
<p class="text-sm text-white/70">
{translate('nexus.link')}
</p>
<Copyable
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
content={value}
>
<Link {href} class="whitespace-pre-line">{value}</Link>
<Link {href} class="whitespace-pre-line !text-white">{value}</Link>
</Copyable>
</div>
{/snippet}

{#snippet eventNamespaceLink(link: ELink)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ 'link.workflowEvent' is possibly 'null' or 'undefined'.
  • ⚠️ Type 'string | null | undefined' is not assignable to type 'string'.

{@const href = routeForNamespace({ namespace: link.workflowEvent.namespace })}
<div class="flex items-start gap-4">
<p class="min-w-56 text-sm text-secondary/80">
<div class="leading-tight">
<p class="text-sm text-white/70">
{translate('nexus.link-namespace')}
</p>
<Copyable
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
content={link.workflowEvent.namespace}
>
<Link {href} class="whitespace-pre-line"
<Link {href} class="whitespace-pre-line !text-white"
Copy link
Contributor

@temporal-cicd temporal-cicd bot Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ 'link.workflowEvent' is possibly 'null' or 'undefined'.
  • ⚠️ Type 'string | null | undefined' is not assignable to type 'string'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ 'link.workflowEvent' is possibly 'null' or 'undefined'.

>{link.workflowEvent.namespace}</Link
>
</Copyable>
Expand All @@ -148,8 +206,8 @@
{/snippet}

{#snippet eventSummary(value: Payload)}
<div class="flex items-start gap-4">
<p class="min-w-56 text-sm text-secondary/80">Summary</p>
<div class="leading-tight">
<p class="text-sm text-white/70">Summary</p>
<p class="whitespace-pre-line">
<MetadataDecoder
{value}
Expand All @@ -165,16 +223,30 @@
{#snippet payloads(key, value)}
{@const codeBlockValue = getCodeBlockValue(value)}
{@const stackTrace = getStackTrace(codeBlockValue)}
<div>
<p class="mb-1 min-w-56 text-sm text-secondary/80">
{#if stackTrace}
<div class="leading-tight">
<p class="mb-1 text-sm text-white/70">
{translate('workflows.call-stack-tab')}
</p>
<CodeBlock
content={stackTrace}
language="text"
maxHeight={320}
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
/>
</div>
{/if}
<div class="leading-tight">
<p class="mb-1 text-sm text-white/70">
{format(key)}
</p>
{#if value?.payloads}
<PayloadDecoder {value} key="payloads">
{#snippet children(decodedValue)}
<CodeBlock
content={decodedValue}
maxHeight={384}
maxHeight={320}
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
/>
Expand All @@ -188,7 +260,7 @@
{#snippet children(decodedValue)}
<CodeBlock
content={decodedValue}
maxHeight={384}
maxHeight={320}
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
/>
Expand All @@ -199,33 +271,19 @@
{#snippet children(decodedValue)}
<CodeBlock
content={decodedValue}
maxHeight={384}
maxHeight={320}
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
/>
{/snippet}
</PayloadDecoder>
{/if}
</div>
{#if stackTrace}
<div>
<p class="mb-1 min-w-56 text-sm text-secondary/80">
{translate('workflows.call-stack-tab')}
</p>
<CodeBlock
content={stackTrace}
language="text"
maxHeight={384}
copyIconTitle={translate('common.copy-icon-title')}
copySuccessIconTitle={translate('common.copy-success-icon-title')}
/>
</div>
{/if}
{/snippet}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Parameter 'key' implicitly has an 'any' type.
  • ⚠️ Parameter 'value' implicitly has an 'any' type.

{#snippet link(key, value)}
<div class="flex items-start gap-4">
<p class="min-w-56 text-sm text-secondary/80">
<div class="leading-tight">
<p class="text-sm text-white/70">
{format(key)}
</p>
<Copyable
Expand All @@ -237,15 +295,15 @@
value={String(value)}
{attributes}
type={displayLinkType(key, attributes)}
class="whitespace-pre-line"
class="whitespace-pre-line break-all !text-white"
/>
</Copyable>
</div>
{/snippet}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Parameter 'key' implicitly has an 'any' type.
  • ⚠️ Parameter 'value' implicitly has an 'any' type.

{#snippet details(key, value)}
<div class="flex items-start gap-4">
<p class="min-w-56 text-sm text-secondary/80">
<div class="leading-tight">
<p class="text-sm text-white/70">
{format(key)}
</p>
<p class="whitespace-pre-line break-all">
Expand Down
Loading
Loading