Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.icon {
display: inline-grid;
place-items: center;
flex-shrink: 0;
border-radius: var(--wb-ds-radius-full);
color: var(--ai-studio-status-color--tone);
background: color-mix(in srgb, var(--ai-studio-status-color--tone), transparent 88%);

/* Phosphor sizes by width/height attributes, which a class rule overrides. */
svg {
width: 70%;
height: 70%;
}
}

.s {
/* missing token */
width: 0.9375rem;
height: 0.9375rem;
}

.m {
width: var(--wb-ds-size-250);
height: var(--wb-ds-size-250);
}

.running {
--ai-studio-status-color--tone: var(--ai-studio-status-color--running);
}

.completed {
--ai-studio-status-color--tone: var(--ai-studio-status-color--completed);
}

.waiting {
--ai-studio-status-color--tone: var(--ai-studio-status-color--human);
}

.failed {
--ai-studio-status-color--tone: var(--ai-studio-status-color--failed);
}

.warning {
--ai-studio-status-color--tone: var(--ai-studio-status-color--incomplete);
}

.incomplete {
--ai-studio-status-color--tone: var(--ai-studio-status-color--incomplete);
}

.branch,
.join,
.info {
--ai-studio-status-color--tone: var(--ai-studio-status-color--running);
}

.skipped,
.neutral {
--ai-studio-status-color--tone: var(--wb-ds-ui-text-muted-default);
}

.dot {
width: 30%;
height: 30%;
border-radius: var(--wb-ds-radius-full);
background: currentColor;
}

/* Phosphor draws the split and merge top to bottom; the diagram flows left to right. */
.rightward {
transform: rotate(-90deg);
}

.spin {
animation: spin 1.6s linear infinite;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
ArrowsMerge,
ArrowsSplit,
Check,
Clock,
ExclamationMark,
LinkBreak,
SkipForward,
Spinner,
} from '@phosphor-icons/react';
import clsx from 'clsx';

import styles from './execution-status-icon.module.css';

export type ExecutionStatusTone =
| 'running'
| 'completed'
| 'waiting'
| 'failed'
| 'warning'
| 'skipped'
| 'incomplete'
| 'branch'
| 'join'
| 'info'
| 'neutral';

type Props = {
tone: ExecutionStatusTone;
/**
* `s` is the execution log row (15 px), `m` the node header (20 px).
* @default 's'
*/
size?: 's' | 'm';
className?: string;
};

// A bare "i" in Phosphor's 256 grid and bold stroke; a text glyph sits on the font's baseline, off centre.
function InfoGlyph() {
return (
<svg viewBox="0 0 256 256" fill="currentColor">
<circle cx="128" cy="68" r="18" />
<rect x="116" y="108" width="24" height="104" rx="12" />
</svg>
);
}

// Phosphor, not the SDK `Icon`: that one sets its size inline, so the glyphs would not follow the badge.
function Glyph({ tone }: { tone: ExecutionStatusTone }) {
switch (tone) {
case 'running': {
return <Spinner weight="bold" className={styles['spin']} />;
}
case 'completed': {
return <Check weight="bold" />;
}
case 'waiting': {
return <Clock weight="bold" />;
}
case 'failed':
case 'warning': {
return <ExclamationMark weight="bold" />;
}
case 'skipped': {
return <SkipForward weight="bold" />;
}
case 'incomplete': {
return <LinkBreak weight="bold" />;
}
case 'branch': {
return <ArrowsSplit weight="bold" className={styles['rightward']} />;
}
case 'join': {
return <ArrowsMerge weight="bold" className={styles['rightward']} />;
}
case 'info': {
return <InfoGlyph />;
}
case 'neutral': {
return <span className={styles['dot']} />;
}
}
}

// One status language for the node header and the execution log rows.
export function ExecutionStatusIcon({ tone, size = 's', className }: Props) {
return (
<span className={clsx(styles['icon'], styles[size], styles[tone], className)} aria-hidden="true">
<Glyph tone={tone} />
</span>
);
}
14 changes: 14 additions & 0 deletions apps/ai-studio/src/components/execution/highlighting.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
html[data-theme='light'] {
--ai-studio-edge-color--active: var(--wb-ds-colors-green-400);
--ai-studio-status-color--completed: var(--wb-ds-colors-green-400);
/* missing token */
--ai-studio-status-color--running: var(--wb-ds-colors-acc1-500);
/* missing token */
--ai-studio-status-color--human: var(--wb-ds-colors-acc4-700);
/* missing token */
--ai-studio-log-detail-bg: var(--wb-ds-colors-gray-200);
}

html[data-theme='dark'] {
--ai-studio-edge-color--active: var(--wb-ds-colors-green-200);
--ai-studio-status-color--completed: var(--wb-ds-colors-green-200);
/* missing token */
--ai-studio-status-color--running: var(--wb-ds-colors-acc1-400);
/* missing token */
--ai-studio-status-color--human: var(--wb-ds-colors-acc4-300);
/* missing token */
--ai-studio-log-detail-bg: var(--wb-ds-colors-gray-800);
}

:root {
Expand All @@ -15,6 +27,8 @@ html[data-theme='dark'] {
--ai-studio-status-bg--completed: color-mix(in srgb, var(--ai-studio-status-color--completed), transparent 85%);
--ai-studio-status-bg--waiting: color-mix(in srgb, var(--ai-studio-status-color--waiting), transparent 85%);
--ai-studio-status-bg--incomplete: color-mix(in srgb, var(--ai-studio-status-color--incomplete), transparent 85%);
--ai-studio-status-bg--running: color-mix(in srgb, var(--ai-studio-status-color--running), transparent 85%);
--ai-studio-status-bg--human: color-mix(in srgb, var(--ai-studio-status-color--human), transparent 85%);
--ai-studio-status-bg--failed: color-mix(in srgb, var(--ai-studio-status-color--failed), transparent 85%);

--ai-studio-node-shadow-color--active: var(--ai-studio-edge-color--active);
Expand Down
Loading
Loading