-
- {visiblePlanStepRows.map(({ key, step }) => (
-
+ {visiblePlanStepRows.map(({ key, step }, index) => (
+
}
+ nodeOffset={TASK_STEP_NODE_OFFSET_PX}
+ connectTop={index > 0}
+ connectBottom={index < visiblePlanStepRows.length - 1}
+ style={
+ liveStepIndex >= 0
+ ? spineAccentRowStyle(Math.abs(index - liveStepIndex))
+ : undefined
+ }
>
-
{taskStatusIcon(step.status)}
+ {taskStatusLabel(step.status)}:
{step.step}
-
- {taskStatusLabel(step.status)}
-
-
+
))}
{shouldCollapsePlanSteps ? (
diff --git a/apps/web/src/components/ui/threadline.tsx b/apps/web/src/components/ui/threadline.tsx
index 2713f5d6..b0cc06c8 100644
--- a/apps/web/src/components/ui/threadline.tsx
+++ b/apps/web/src/components/ui/threadline.tsx
@@ -152,6 +152,49 @@ function SpineRow({
);
}
+type SpineNodeKind = "done" | "running" | "warning" | "error" | "group" | "pending";
+
+const TONE_SPINE_DOT_CLASS_NAME = {
+ warning: "size-[6px] rounded-full bg-warning",
+ error: "size-[6px] rounded-full bg-destructive",
+} as const satisfies Record<"warning" | "error", string>;
+
+/** The glyph that sits on a spine for one row. The accent halo is reserved for
+ * the surface's single live node; a still-running step gets a small accent
+ * tick, settled steps are quiet solid dots, warnings/errors are compact tone
+ * dots, a collapsed group of steps is a hollow ring (same family, reads as
+ * "openable"), and a step not yet started is a fainter hollow ring. */
+function SpineNode({ kind }: { kind: SpineNodeKind }) {
+ if (kind === "running") {
+ return (
+
+ );
+ }
+ if (kind === "warning" || kind === "error") {
+ return
;
+ }
+ if (kind === "group" || kind === "pending") {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
+
/**
* Accent fade for the connectors approaching a live terminus: the spine warms
* toward accent as it nears the row where work is happening now, and settles
@@ -198,4 +241,13 @@ function CurrentMarker({ className, ...props }: React.ComponentPropsWithoutRef<"
);
}
-export { CurrentMarker, LiveNode, SectionLabel, SectionTick, SpineRow, spineAccentRowStyle };
+export {
+ CurrentMarker,
+ LiveNode,
+ SectionLabel,
+ SectionTick,
+ SpineNode,
+ SpineRow,
+ spineAccentRowStyle,
+ type SpineNodeKind,
+};