@lostgradient/cinder 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/components.json +82 -4
  3. package/dist/components/access-gate/index.js +381 -239
  4. package/dist/components/chat/builders.d.ts +17 -8
  5. package/dist/components/chat/conversation-model.d.ts +1 -1
  6. package/dist/components/chat/index.d.ts +1 -1
  7. package/dist/components/chat/index.js +300 -17
  8. package/dist/components/chat/utilities/conversation.d.ts +6 -6
  9. package/dist/components/chat-conversation-header/index.js +19 -6
  10. package/dist/components/chat-conversation-list/index.js +19 -6
  11. package/dist/components/connection-indicator/connection-indicator.css +136 -0
  12. package/dist/components/connection-indicator/connection-indicator.css.d.ts +1 -0
  13. package/dist/components/connection-indicator/connection-indicator.schema.d.ts +3 -0
  14. package/dist/components/connection-indicator/connection-indicator.schema.js +89 -0
  15. package/dist/components/connection-indicator/connection-indicator.svelte.d.ts +21 -0
  16. package/dist/components/connection-indicator/connection-indicator.types.d.ts +46 -0
  17. package/dist/components/connection-indicator/connection-indicator.variables.d.ts +2 -0
  18. package/dist/components/connection-indicator/connection-indicator.variables.js +61 -0
  19. package/dist/components/connection-indicator/index.d.ts +5 -0
  20. package/dist/components/connection-indicator/index.js +409 -0
  21. package/dist/components/container/container.schema.js +4 -4
  22. package/dist/components/invocation-rule-builder/index.d.ts +1 -1
  23. package/dist/components/invocation-rule-builder/index.js +305 -150
  24. package/dist/components/invocation-rule-builder/invocation-rule-builder.css +28 -0
  25. package/dist/components/invocation-rule-builder/invocation-rule-builder.schema.js +89 -5
  26. package/dist/components/invocation-rule-builder/invocation-rule-builder.svelte.d.ts +2 -1
  27. package/dist/components/invocation-rule-builder/invocation-rule-builder.types.d.ts +114 -16
  28. package/dist/components/run-step-timeline/index.d.ts +1 -1
  29. package/dist/components/run-step-timeline/index.js +992 -559
  30. package/dist/components/run-step-timeline/run-step-branch-disclosure.svelte.d.ts +17 -0
  31. package/dist/components/run-step-timeline/run-step-timeline.css +124 -0
  32. package/dist/components/run-step-timeline/run-step-timeline.schema.js +873 -350
  33. package/dist/components/run-step-timeline/run-step-timeline.svelte.d.ts +1 -15
  34. package/dist/components/run-step-timeline/run-step-timeline.types.d.ts +182 -5
  35. package/dist/components/run-step-timeline/run-step-timeline.utilities.d.ts +52 -0
  36. package/dist/components/schedule-builder/index.d.ts +5 -0
  37. package/dist/components/schedule-builder/index.js +3176 -0
  38. package/dist/components/schedule-builder/schedule-builder.css +121 -0
  39. package/dist/components/schedule-builder/schedule-builder.css.d.ts +1 -0
  40. package/dist/components/schedule-builder/schedule-builder.schema.d.ts +3 -0
  41. package/dist/components/schedule-builder/schedule-builder.schema.js +141 -0
  42. package/dist/components/schedule-builder/schedule-builder.svelte.d.ts +22 -0
  43. package/dist/components/schedule-builder/schedule-builder.types.d.ts +130 -0
  44. package/dist/components/schedule-builder/schedule-builder.utilities.d.ts +78 -0
  45. package/dist/components/schedule-builder/schedule-builder.variables.d.ts +2 -0
  46. package/dist/components/schedule-builder/schedule-builder.variables.js +61 -0
  47. package/dist/components/tag-input/index.js +22 -4
  48. package/dist/components/tag-input/tag-input.schema.js +5 -1
  49. package/dist/components/tag-input/tag-input.types.d.ts +4 -0
  50. package/dist/index.d.ts +6 -2
  51. package/dist/index.js +22704 -20574
  52. package/dist/server/components/access-gate/index.js +2 -1
  53. package/dist/server/components/chat/index.js +4 -4
  54. package/dist/server/components/chat-conversation-header/index.js +5 -5
  55. package/dist/server/components/chat-conversation-list/index.js +3 -3
  56. package/dist/server/components/connection-indicator/index.js +16 -0
  57. package/dist/server/components/data-table/index.js +3 -3
  58. package/dist/server/components/diff-viewer/index.js +4 -3
  59. package/dist/server/components/invocation-rule-builder/index.js +1 -1
  60. package/dist/server/components/json-schema-editor/index.js +4 -3
  61. package/dist/server/components/review-editor/index.js +4 -3
  62. package/dist/server/components/run-step-timeline/index.js +1 -1
  63. package/dist/server/components/schedule-builder/index.js +30 -0
  64. package/dist/server/components/side-navigation/index.js +2 -2
  65. package/dist/server/components/table/index.js +3 -3
  66. package/dist/server/components/tag-input/index.js +1 -1
  67. package/dist/server/components/time-field/index.js +5 -3
  68. package/dist/server/index.js +100 -87
  69. package/dist/server/index.server-0sx92swg.js +9 -0
  70. package/dist/server/{index.server-mzzb7dnm.js → index.server-2bbwajb2.js} +1 -1
  71. package/dist/server/{index.server-q7w4jw89.js → index.server-2s5h1d05.js} +2 -2
  72. package/dist/server/{index.server-dg83ke3k.js → index.server-2zdbz3rj.js} +1 -1
  73. package/dist/server/{index.server-0jhra85x.js → index.server-3w72dxz7.js} +4 -4
  74. package/dist/server/{index.server-qh9qzd5t.js → index.server-4x4hgvqe.js} +283 -13
  75. package/dist/server/{index.server-c6pe4kaf.js → index.server-5gzyqv78.js} +1 -1
  76. package/dist/server/{index.server-mqt1ngqw.js → index.server-6hnfzc7f.js} +19 -6
  77. package/dist/server/index.server-be9bpqeh.js +93 -0
  78. package/dist/server/{index.server-f3z7tpxr.js → index.server-c42b462p.js} +2 -4
  79. package/dist/server/{index.server-7vyzen5x.js → index.server-c5qv3p61.js} +171 -63
  80. package/dist/server/index.server-cch0eed5.js +144 -0
  81. package/dist/server/index.server-gmh5ar16.js +2 -0
  82. package/dist/server/{index.server-0atyznp7.js → index.server-h89zk5ev.js} +7 -7
  83. package/dist/server/{index.server-jxg42jpz.js → index.server-jdcq5zza.js} +7 -7
  84. package/dist/server/{index.server-2dga5yzr.js → index.server-jfrssta6.js} +1 -1
  85. package/dist/server/{index.server-fy068yne.js → index.server-jj76374p.js} +1 -1
  86. package/dist/server/index.server-qbkxz7r2.js +750 -0
  87. package/dist/server/{index.server-marjz8ps.js → index.server-tcay903b.js} +3 -2
  88. package/dist/server/index.server-z38bxms8.js +734 -0
  89. package/dist/server/{index.server-yz4k36cw.js → index.server-z7hxtqs1.js} +1 -1
  90. package/package.json +66 -3
  91. package/src/components/chat/builders.ts +445 -14
  92. package/src/components/chat/conversation-model.ts +1 -0
  93. package/src/components/chat/index.ts +1 -0
  94. package/src/components/chat/utilities/conversation.ts +33 -15
  95. package/src/components/connection-indicator/connection-indicator.css +136 -0
  96. package/src/components/connection-indicator/connection-indicator.examples.json +31 -0
  97. package/src/components/connection-indicator/connection-indicator.schema.json +29 -0
  98. package/src/components/connection-indicator/connection-indicator.schema.ts +36 -0
  99. package/src/components/connection-indicator/connection-indicator.svelte +63 -0
  100. package/src/components/connection-indicator/connection-indicator.types.ts +58 -0
  101. package/src/components/connection-indicator/connection-indicator.variables.json +1 -0
  102. package/src/components/connection-indicator/connection-indicator.variables.ts +3 -0
  103. package/src/components/connection-indicator/index.ts +10 -0
  104. package/src/components/container/container.schema.json +3 -3
  105. package/src/components/container/container.schema.ts +3 -3
  106. package/src/components/invocation-rule-builder/index.ts +4 -0
  107. package/src/components/invocation-rule-builder/invocation-rule-builder.css +28 -0
  108. package/src/components/invocation-rule-builder/invocation-rule-builder.examples.json +6 -0
  109. package/src/components/invocation-rule-builder/invocation-rule-builder.schema.json +76 -3
  110. package/src/components/invocation-rule-builder/invocation-rule-builder.schema.ts +83 -3
  111. package/src/components/invocation-rule-builder/invocation-rule-builder.svelte +326 -113
  112. package/src/components/invocation-rule-builder/invocation-rule-builder.types.ts +157 -43
  113. package/src/components/run-step-timeline/index.ts +8 -0
  114. package/src/components/run-step-timeline/run-step-branch-disclosure.svelte +16 -0
  115. package/src/components/run-step-timeline/run-step-timeline.css +124 -0
  116. package/src/components/run-step-timeline/run-step-timeline.examples.json +18 -0
  117. package/src/components/run-step-timeline/run-step-timeline.schema.json +872 -349
  118. package/src/components/run-step-timeline/run-step-timeline.schema.ts +931 -349
  119. package/src/components/run-step-timeline/run-step-timeline.svelte +387 -268
  120. package/src/components/run-step-timeline/run-step-timeline.types.ts +192 -5
  121. package/src/components/run-step-timeline/run-step-timeline.utilities.ts +242 -0
  122. package/src/components/schedule-builder/index.ts +14 -0
  123. package/src/components/schedule-builder/schedule-builder.css +121 -0
  124. package/src/components/schedule-builder/schedule-builder.examples.json +31 -0
  125. package/src/components/schedule-builder/schedule-builder.schema.json +78 -0
  126. package/src/components/schedule-builder/schedule-builder.schema.ts +86 -0
  127. package/src/components/schedule-builder/schedule-builder.svelte +629 -0
  128. package/src/components/schedule-builder/schedule-builder.types.ts +137 -0
  129. package/src/components/schedule-builder/schedule-builder.utilities.ts +344 -0
  130. package/src/components/schedule-builder/schedule-builder.variables.json +1 -0
  131. package/src/components/schedule-builder/schedule-builder.variables.ts +3 -0
  132. package/src/components/tag-input/tag-input.schema.json +4 -0
  133. package/src/components/tag-input/tag-input.schema.ts +4 -0
  134. package/src/components/tag-input/tag-input.svelte +20 -3
  135. package/src/components/tag-input/tag-input.types.ts +4 -0
  136. package/src/index.ts +23 -0
  137. package/src/styles/components.css +2 -0
  138. package/dist/server/index.server-qk1h0n02.js +0 -393
@@ -48,6 +48,27 @@ export type InvocationRule = {
48
48
  actions: InvocationRuleAction[];
49
49
  };
50
50
 
51
+ /**
52
+ * The value type a condition field represents. Determines which typed value
53
+ * control renders for that field in conditions-only mode (`mode="conditions"`).
54
+ * Ignored in full mode, where the condition value is always a free-text input.
55
+ */
56
+ export type InvocationRuleFieldType = 'string' | 'number' | 'boolean' | 'enum';
57
+
58
+ /**
59
+ * A single selectable choice for an `'enum'`-typed field's value, rendered as
60
+ * a select in conditions-only mode. A flat value/label pair — enum choices do
61
+ * not nest further.
62
+ *
63
+ * @schemaObject
64
+ */
65
+ export type InvocationRuleValueChoice = {
66
+ /** The value stored on the condition. */
67
+ value: string;
68
+ /** The human-readable label shown in the select. */
69
+ label: string;
70
+ };
71
+
51
72
  /**
52
73
  * An option in a field, operator, or action-target select list.
53
74
  *
@@ -58,8 +79,41 @@ export type InvocationRuleOption = {
58
79
  value: string;
59
80
  /** The human-readable label shown in the select. */
60
81
  label: string;
82
+ /**
83
+ * The value type this option represents when used as a field option in
84
+ * conditions-only mode (`mode="conditions"`). Defaults to `'string'` when
85
+ * omitted. Ignored for operator and action-target options, and ignored
86
+ * entirely in full mode.
87
+ */
88
+ type?: InvocationRuleFieldType;
89
+ /**
90
+ * Enum choices for this field's value, rendered as a select in
91
+ * conditions-only mode when `type` is `'enum'`. Provide these for an enum
92
+ * field; if omitted or empty, the value select renders with no choices.
93
+ * Ignored for non-enum field types.
94
+ */
95
+ options?: InvocationRuleValueChoice[];
61
96
  };
62
97
 
98
+ /**
99
+ * Rendering mode for InvocationRuleBuilder.
100
+ *
101
+ * - `'full'` (default) renders both conditions and actions, matching the
102
+ * component's original behavior exactly.
103
+ * - `'conditions'` renders conditions only: action controls are hidden
104
+ * entirely, rules never emit action descriptors, the operator set is
105
+ * fixed to {@link InvocationRuleConditionsOnlyOperator}, and condition
106
+ * value controls are inferred from each field option's `type`.
107
+ */
108
+ export type InvocationRuleBuilderMode = 'full' | 'conditions';
109
+
110
+ /**
111
+ * The fixed operator vocabulary used in conditions-only mode
112
+ * (`mode="conditions"`). Cinder supplies these five operators with default
113
+ * labels; the `operatorOptions` prop is not accepted in this mode.
114
+ */
115
+ export type InvocationRuleConditionsOnlyOperator = 'eq' | 'gt' | 'lt' | 'gte' | 'lte';
116
+
63
117
  /**
64
118
  * Describes the change that caused an `onchange` call.
65
119
  * Consumers use the type to determine what to persist.
@@ -105,6 +159,34 @@ type InvocationRuleBuilderBaseProps = Omit<
105
159
  */
106
160
  fieldOptions: InvocationRuleOption[];
107
161
 
162
+ /**
163
+ * Label for the "Add rule" button. Defaults to "Add rule".
164
+ */
165
+ addRuleLabel?: string;
166
+
167
+ /**
168
+ * Label for the "Add condition" button. Defaults to "Add condition".
169
+ */
170
+ addConditionLabel?: string;
171
+
172
+ /** Accessible label for the entire rule builder region. */
173
+ label?: string;
174
+
175
+ /** Additional CSS classes applied to the root element. */
176
+ class?: string;
177
+ };
178
+
179
+ /**
180
+ * Mode-specific props for `mode="full"` (the default). Behavior and prop
181
+ * shape are unchanged from before conditions-only mode existed.
182
+ */
183
+ type InvocationRuleBuilderFullModeProps = {
184
+ /**
185
+ * Rendering mode. Omit or pass `'full'` to render both conditions and
186
+ * actions — the component's original, unchanged behavior.
187
+ */
188
+ mode?: 'full';
189
+
108
190
  /**
109
191
  * Options for the condition operator selector. Consumer-provided list
110
192
  * of operators, e.g. "matches", "is", "is-not", "contains".
@@ -118,61 +200,78 @@ type InvocationRuleBuilderBaseProps = Omit<
118
200
  actionOptions: InvocationRuleOption[];
119
201
 
120
202
  /**
121
- * Label for the "Add rule" button. Defaults to "Add rule".
203
+ * Label for the "Add action" button. Defaults to "Add action".
122
204
  */
123
- addRuleLabel?: string;
205
+ addActionLabel?: string;
206
+ };
124
207
 
208
+ /**
209
+ * Mode-specific props for `mode="conditions"`. Actions are not rendered, so
210
+ * action-related props are not accepted — cinder owns the operator
211
+ * vocabulary and there is nothing to configure for actions.
212
+ */
213
+ type InvocationRuleBuilderConditionsOnlyModeProps = {
125
214
  /**
126
- * Label for the "Add condition" button. Defaults to "Add condition".
215
+ * Renders conditions only: action controls are hidden entirely and rules
216
+ * never emit action descriptors.
127
217
  */
128
- addConditionLabel?: string;
218
+ mode: 'conditions';
129
219
 
130
220
  /**
131
- * Label for the "Add action" button. Defaults to "Add action".
221
+ * Not accepted in conditions-only mode. Cinder supplies the fixed
222
+ * eq/gt/lt/gte/lte operator set internally; see
223
+ * {@link InvocationRuleConditionsOnlyOperator}.
132
224
  */
133
- addActionLabel?: string;
225
+ operatorOptions?: never;
134
226
 
135
- /** Accessible label for the entire rule builder region. */
136
- label?: string;
227
+ /** Not accepted in conditions-only mode action controls are not rendered. */
228
+ actionOptions?: never;
137
229
 
138
- /** Additional CSS classes applied to the root element. */
139
- class?: string;
230
+ /** Not accepted in conditions-only mode action controls are not rendered. */
231
+ addActionLabel?: never;
140
232
  };
141
233
 
234
+ type InvocationRuleBuilderModeProps =
235
+ | InvocationRuleBuilderFullModeProps
236
+ | InvocationRuleBuilderConditionsOnlyModeProps;
237
+
142
238
  type InvocationRuleBuilderChangeHandler = (
143
239
  nextRules: InvocationRule[],
144
240
  change: InvocationRuleChange,
145
241
  ) => void;
146
242
 
147
- export type InvocationRuleBuilderProps =
148
- | (InvocationRuleBuilderBaseProps & {
149
- /**
150
- * Called whenever the user makes any edit. Required for editable runtime
151
- * usage because editable controls must commit controlled state changes.
152
- * Receives the next rule array (pure, not mutated) and a change descriptor.
153
- * Consumer owns persistence, validation, and execution.
154
- */
155
- onchange: InvocationRuleBuilderChangeHandler;
156
-
157
- /**
158
- * When false or omitted, renders editable controls. Editable mode requires
159
- * `onchange` so controls cannot become interactive-but-no-op.
160
- */
161
- readonly?: false;
162
- })
163
- | (InvocationRuleBuilderBaseProps & {
164
- /**
165
- * Optional in readonly usage because no edit controls are rendered.
166
- * Runtime consumers may still pass it when sharing props between modes.
167
- */
168
- onchange?: InvocationRuleBuilderChangeHandler;
169
-
170
- /**
171
- * When true, renders a readonly summary of each rule instead of editable
172
- * controls.
173
- */
174
- readonly: true;
175
- });
243
+ export type InvocationRuleBuilderProps = InvocationRuleBuilderBaseProps &
244
+ InvocationRuleBuilderModeProps &
245
+ (
246
+ | {
247
+ /**
248
+ * Called whenever the user makes any edit. Required for editable runtime
249
+ * usage because editable controls must commit controlled state changes.
250
+ * Receives the next rule array (pure, not mutated) and a change descriptor.
251
+ * Consumer owns persistence, validation, and execution.
252
+ */
253
+ onchange: InvocationRuleBuilderChangeHandler;
254
+
255
+ /**
256
+ * When false or omitted, renders editable controls. Editable mode requires
257
+ * `onchange` so controls cannot become interactive-but-no-op.
258
+ */
259
+ readonly?: false;
260
+ }
261
+ | {
262
+ /**
263
+ * Optional in readonly usage because no edit controls are rendered.
264
+ * Runtime consumers may still pass it when sharing props between modes.
265
+ */
266
+ onchange?: InvocationRuleBuilderChangeHandler;
267
+
268
+ /**
269
+ * When true, renders a readonly summary of each rule instead of editable
270
+ * controls.
271
+ */
272
+ readonly: true;
273
+ }
274
+ );
176
275
 
177
276
  /**
178
277
  * Cinder-specific schema surface for InvocationRuleBuilder.
@@ -193,17 +292,32 @@ export type InvocationRuleBuilderSchemaProps = {
193
292
  */
194
293
  fieldOptions: InvocationRuleOption[];
195
294
 
295
+ /**
296
+ * Rendering mode. Omit or pass `'full'` for the original conditions +
297
+ * actions behavior; pass `'conditions'` to render conditions only. Not
298
+ * expressible as a schema-driven discriminant here because JSON Schema
299
+ * validation of this component is documented, not enforced at runtime
300
+ * against `operatorOptions`/`actionOptions` presence; see the component
301
+ * types for the full mode-discriminated prop shape.
302
+ */
303
+ mode?: InvocationRuleBuilderMode;
304
+
196
305
  /**
197
306
  * Options for the condition operator selector. Consumer-provided list
198
- * of operators, e.g. "matches", "is", "is-not", "contains".
307
+ * of operators, e.g. "matches", "is", "is-not", "contains". Required in
308
+ * full mode; optional (and ignored) when `mode` is `'conditions'`, since
309
+ * cinder supplies a fixed operator set in that mode — so a conditions-only
310
+ * configuration validates without it.
199
311
  */
200
- operatorOptions: InvocationRuleOption[];
312
+ operatorOptions?: InvocationRuleOption[];
201
313
 
202
314
  /**
203
315
  * Options for the action target selector. Consumer-provided list of
204
- * targets, e.g. review-agent slugs or step identifiers.
316
+ * targets, e.g. review-agent slugs or step identifiers. Required in full
317
+ * mode; optional (and ignored) when `mode` is `'conditions'`, since actions
318
+ * are not rendered — so a conditions-only configuration validates without it.
205
319
  */
206
- actionOptions: InvocationRuleOption[];
320
+ actionOptions?: InvocationRuleOption[];
207
321
 
208
322
  /**
209
323
  * Must be true for schema-driven usage because editable mode requires
@@ -4,13 +4,21 @@ import RunStepTimeline from './run-step-timeline.svelte';
4
4
  export default RunStepTimeline;
5
5
  export type {
6
6
  RunStep,
7
+ RunStepBranchGroup,
8
+ RunStepBranchLane,
9
+ RunStepBranchLaneOutcome,
7
10
  RunStepDetail,
8
11
  RunStepLink,
9
12
  RunStepStatus,
13
+ RunStepTimelineEntry,
10
14
  RunStepTimelineProps,
15
+ RunStepTimelineSchemaBranchGroup,
16
+ RunStepTimelineSchemaBranchLane,
11
17
  RunStepTimelineSchemaChildStep,
18
+ RunStepTimelineSchemaEntry,
12
19
  RunStepTimelineSchemaGrandchildStep,
13
20
  RunStepTimelineSchemaGreatGrandchildStep,
21
+ RunStepTimelineSchemaLaneStep,
14
22
  RunStepTimelineSchemaProps,
15
23
  RunStepTimelineSchemaStep,
16
24
  } from './run-step-timeline.types.ts';
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import Collapsible from '../collapsible/collapsible.svelte';
3
+ let { initialOpen, triggerAriaLabel, class: className, trigger, children, } = $props();
4
+ // Seed the disclosure state exactly once, from the group's initial auto-open
5
+ // decision. Binding `open` directly to a value derived from live step status
6
+ // (the collapsible seeds *and* re-syncs from an un-`bind:`-ed `open` prop) would
7
+ // slam the group shut the moment its active lane finished — overriding the
8
+ // reader's own toggle. Owning the state locally via `bind:open` keeps
9
+ // `collapsed`/`collapseThreshold` as documented *initial* behavior while leaving
10
+ // the group freely user-togglable for the rest of its life.
11
+ let open = $state(initialOpen);
12
+ </script>
13
+
14
+ <Collapsible bind:open {trigger} {triggerAriaLabel} class={className}>
15
+ {@render children()}
16
+ </Collapsible>
@@ -212,6 +212,130 @@
212
212
  color: var(--cinder-text-muted);
213
213
  }
214
214
 
215
+ /* ----------------------------------------
216
+ * Rewound steps: speculatively executed, then unwound. Struck-through and
217
+ * de-emphasized but still inspectable (details/collapsibles keep working).
218
+ * The "Rewound" badge carries the state as text so it is never color-only.
219
+ * ---------------------------------------- */
220
+
221
+ .cinder-run-step-timeline__item[data-cinder-rewound] .cinder-run-step-timeline__label {
222
+ text-decoration: line-through;
223
+ color: var(--cinder-text-muted);
224
+ }
225
+
226
+ .cinder-run-step-timeline__item[data-cinder-rewound] .cinder-run-step-timeline__marker {
227
+ opacity: 0.55;
228
+ }
229
+
230
+ .cinder-run-step-timeline__item[data-cinder-rewound] .cinder-run-step-timeline__meta {
231
+ opacity: 0.7;
232
+ }
233
+
234
+ /* ----------------------------------------
235
+ * Compensation steps: inset beneath their forward step with a dashed
236
+ * reversal connector. The "Compensates <step>" badge names the relationship.
237
+ * ---------------------------------------- */
238
+
239
+ .cinder-run-step-timeline__item[data-cinder-compensation] {
240
+ --_cinder-rst-compensation-inset: var(--cinder-space-5);
241
+
242
+ padding-inline-start: var(--_cinder-rst-compensation-inset);
243
+ }
244
+
245
+ .cinder-run-step-timeline__item[data-cinder-compensation]::after {
246
+ content: '';
247
+ position: absolute;
248
+ inset-inline-start: calc(var(--_cinder-rst-depth-offset) + var(--_cinder-rst-rail-center));
249
+ inset-block-start: -0.5rem;
250
+ inline-size: var(--cinder-space-5);
251
+ block-size: 1.5rem;
252
+ border-inline-start: 1px dashed var(--cinder-border-strong);
253
+ border-block-end: 1px dashed var(--cinder-border-strong);
254
+ border-end-start-radius: var(--cinder-radius-md);
255
+ pointer-events: none;
256
+ }
257
+
258
+ /* ----------------------------------------
259
+ * Branch / coordination groups: N parallel sub-lanes with won/lost/settled
260
+ * outcomes. Winner emphasized, losers muted — driven by tokens.
261
+ * ---------------------------------------- */
262
+
263
+ .cinder-run-step-timeline__branch {
264
+ min-width: 0;
265
+ }
266
+
267
+ .cinder-run-step-timeline__branch-heading {
268
+ display: inline-flex;
269
+ align-items: center;
270
+ gap: var(--cinder-space-2);
271
+ }
272
+
273
+ .cinder-run-step-timeline__lanes {
274
+ list-style: none;
275
+ margin: var(--cinder-space-2) 0 0;
276
+ padding: 0;
277
+ display: grid;
278
+ grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
279
+ gap: var(--cinder-space-3);
280
+ }
281
+
282
+ .cinder-run-step-timeline__lane {
283
+ border: 1px solid var(--cinder-border-muted);
284
+ border-radius: var(--cinder-radius-md);
285
+ padding: var(--cinder-space-3);
286
+ background: var(--cinder-surface-inset);
287
+ min-width: 0;
288
+ }
289
+
290
+ .cinder-run-step-timeline__lane-header {
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: space-between;
294
+ gap: var(--cinder-space-2);
295
+ margin-block-end: var(--cinder-space-2);
296
+ }
297
+
298
+ .cinder-run-step-timeline__lane-label {
299
+ font-size: var(--cinder-text-xs);
300
+ font-weight: var(--cinder-font-semibold);
301
+ color: var(--cinder-text);
302
+ min-width: 0;
303
+ overflow-wrap: anywhere;
304
+ }
305
+
306
+ .cinder-run-step-timeline__lane-steps {
307
+ margin: 0;
308
+ }
309
+
310
+ /* Winner: emphasized border + full-strength text. */
311
+ .cinder-run-step-timeline__lane[data-cinder-outcome='won'] {
312
+ border-color: var(--cinder-success);
313
+ border-inline-start-width: 3px;
314
+ }
315
+
316
+ /* Loser: muted via a muted border and lane label — not a blanket opacity, so
317
+ * step text, links, detail triggers, and focus rings keep full contrast. The
318
+ * "Lost" outcome badge carries the outcome as text. */
319
+ .cinder-run-step-timeline__lane[data-cinder-outcome='lost'] {
320
+ border-color: var(--cinder-border-muted);
321
+ }
322
+
323
+ .cinder-run-step-timeline__lane[data-cinder-outcome='lost']
324
+ .cinder-run-step-timeline__lane-label {
325
+ color: var(--cinder-text-muted);
326
+ }
327
+
328
+ /* Settled: neutral, no competitive emphasis. */
329
+ .cinder-run-step-timeline__lane[data-cinder-outcome='settled'] {
330
+ border-color: var(--cinder-border);
331
+ }
332
+
333
+ /* Still racing: accent edge to signal in-flight. */
334
+ .cinder-run-step-timeline__lane[data-cinder-outcome='racing'] {
335
+ border-color: var(--cinder-accent);
336
+ border-inline-start-width: 3px;
337
+ }
338
+
215
339
  /* ----------------------------------------
216
340
  * Focus ring (not applicable to the root list, but included for
217
341
  * completeness if the component is ever given an interactive element)
@@ -9,12 +9,30 @@
9
9
  "description": "A multi-step async run with pending, running, and succeeded steps.",
10
10
  "code": "<script lang=\"ts\">\n import { RunStepTimeline } from '@lostgradient/cinder/run-step-timeline';\n import type { RunStep } from '@lostgradient/cinder/run-step-timeline';\n\n const steps: RunStep[] = [\n {\n id: 'validate',\n label: 'Validate configuration',\n status: 'succeeded',\n startTime: '2026-06-01T11:00:00Z',\n endTime: '2026-06-01T11:01:30Z',\n duration: '1m 30s',\n },\n {\n id: 'build',\n label: 'Build Docker image',\n status: 'running',\n startTime: '2026-06-01T11:02:00Z',\n progress: 40,\n },\n {\n id: 'deploy',\n label: 'Deploy to staging',\n status: 'pending',\n },\n ];\n</script>\n\n<RunStepTimeline {steps} label=\"Deployment run\" />\n"
11
11
  },
12
+ {
13
+ "id": "branch-group",
14
+ "title": "Branch group with a winning lane",
15
+ "description": "A speculative race across three parallel deploy candidates. The winning lane is emphasized and the losers are muted, with the group outcome summarized as text.",
16
+ "code": "<script lang=\"ts\">\n import { RunStepTimeline } from '@lostgradient/cinder/run-step-timeline';\n import type { RunStepTimelineEntry } from '@lostgradient/cinder/run-step-timeline';\n\n const steps: RunStepTimelineEntry[] = [\n {\n id: 'plan',\n label: 'Plan rollout',\n status: 'succeeded',\n startTime: '2026-06-01T09:00:00Z',\n endTime: '2026-06-01T09:00:04Z',\n duration: '4s',\n },\n {\n kind: 'branch',\n id: 'race',\n label: 'Race deploy candidates',\n lanes: [\n {\n id: 'us-east',\n label: 'us-east-1',\n outcome: 'won',\n steps: [\n {\n id: 'ue-deploy',\n label: 'Deploy to us-east-1',\n status: 'succeeded',\n duration: '38s',\n },\n { id: 'ue-health', label: 'Health check', status: 'succeeded', duration: '3s' },\n ],\n },\n {\n id: 'eu-west',\n label: 'eu-west-1',\n outcome: 'lost',\n steps: [\n {\n id: 'ew-deploy',\n label: 'Deploy to eu-west-1',\n status: 'succeeded',\n duration: '52s',\n },\n { id: 'ew-cancel', label: 'Cancelled after us-east-1 won', status: 'cancelled' },\n ],\n },\n {\n id: 'ap-south',\n label: 'ap-south-1',\n outcome: 'lost',\n steps: [{ id: 'as-deploy', label: 'Deploy to ap-south-1', status: 'cancelled' }],\n },\n ],\n },\n {\n id: 'promote',\n label: 'Promote winning region',\n status: 'running',\n startTime: '2026-06-01T09:01:10Z',\n progress: 45,\n },\n ];\n</script>\n\n<RunStepTimeline {steps} label=\"Speculative deploy run\" />\n"
17
+ },
12
18
  {
13
19
  "id": "failed-run",
14
20
  "title": "Failed run with error details",
15
21
  "description": "A completed run where one step failed after retries, with expandable error output and environment info.",
16
22
  "code": "<script lang=\"ts\">\n import { RunStepTimeline } from '@lostgradient/cinder/run-step-timeline';\n import type { RunStep } from '@lostgradient/cinder/run-step-timeline';\n\n const steps: RunStep[] = [\n {\n id: 'lint',\n label: 'Lint source files',\n status: 'succeeded',\n startTime: '2026-06-01T10:00:00Z',\n endTime: '2026-06-01T10:00:22Z',\n duration: '22s',\n },\n {\n id: 'typecheck',\n label: 'Type-check',\n status: 'succeeded',\n startTime: '2026-06-01T10:00:23Z',\n endTime: '2026-06-01T10:00:51Z',\n duration: '28s',\n },\n {\n id: 'test',\n label: 'Run unit tests',\n status: 'failed',\n startTime: '2026-06-01T10:00:52Z',\n endTime: '2026-06-01T10:05:10Z',\n duration: '4m 18s',\n attemptCount: 2,\n details: [\n {\n id: 'test-error',\n label: 'Error output',\n content:\n 'AssertionError: expected 1 to equal 2\\n at src/math.test.ts:12:5\\n at test (bun:test/runner.ts:88)',\n },\n {\n id: 'test-env',\n label: 'Environment',\n content: 'NODE_ENV=test\\nCI=true\\nBUN_VERSION=1.2.0',\n },\n ],\n },\n {\n id: 'publish',\n label: 'Publish artifacts',\n status: 'cancelled',\n },\n ];\n</script>\n\n<RunStepTimeline {steps} label=\"CI run — failed\" />\n"
17
23
  },
24
+ {
25
+ "id": "rewound-speculation",
26
+ "title": "Rewound speculative step",
27
+ "description": "A step that was speculatively executed and then unwound after a conflict. It renders struck-through and de-emphasized but stays inspectable, and announces its rewound state as text.",
28
+ "code": "<script lang=\"ts\">\n import { RunStepTimeline } from '@lostgradient/cinder/run-step-timeline';\n import type { RunStepTimelineEntry } from '@lostgradient/cinder/run-step-timeline';\n\n const steps: RunStepTimelineEntry[] = [\n {\n id: 'read',\n label: 'Read current inventory',\n status: 'succeeded',\n duration: '2s',\n },\n {\n id: 'reserve',\n label: 'Reserve stock (speculative)',\n status: 'succeeded',\n rewound: true,\n duration: '1s',\n details: [\n {\n id: 'reason',\n label: 'Why it was unwound',\n content:\n 'A concurrent order won the reservation. This speculative branch was rolled back before commit.',\n },\n ],\n },\n {\n id: 'retry',\n label: 'Re-read inventory and reserve',\n status: 'running',\n progress: 30,\n },\n ];\n</script>\n\n<RunStepTimeline {steps} label=\"Speculative reservation\" />\n"
29
+ },
30
+ {
31
+ "id": "saga-compensation",
32
+ "title": "Saga compensation",
33
+ "description": "A saga where a later step compensates (reverses) an earlier forward step by id. The compensating step renders inset beneath its forward step with a dashed reversal connector.",
34
+ "code": "<script lang=\"ts\">\n import { RunStepTimeline } from '@lostgradient/cinder/run-step-timeline';\n import type { RunStepTimelineEntry } from '@lostgradient/cinder/run-step-timeline';\n\n const steps: RunStepTimelineEntry[] = [\n {\n id: 'charge',\n label: 'Charge customer card',\n status: 'succeeded',\n duration: '1s',\n },\n {\n id: 'reserve-seat',\n label: 'Reserve seat',\n status: 'failed',\n duration: '2s',\n details: [\n {\n id: 'err',\n label: 'Error',\n content: 'Seat 14C was taken before the reservation committed.',\n },\n ],\n },\n {\n id: 'refund',\n label: 'Refund customer card',\n status: 'succeeded',\n compensates: 'charge',\n duration: '1s',\n },\n ];\n</script>\n\n<RunStepTimeline {steps} label=\"Booking saga\" />\n"
35
+ },
18
36
  {
19
37
  "id": "workflow-run",
20
38
  "title": "Temporal child workflow with approval wait",