@lostgradient/cinder 0.9.0 → 0.12.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 (169) hide show
  1. package/CHANGELOG.md +26 -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/code-block/code-block.css +15 -9
  12. package/dist/components/code-block/code-block.variables.js +8 -2
  13. package/dist/components/confirm-dialog/confirm-dialog.css +2 -0
  14. package/dist/components/confirm-dialog/confirm-dialog.schema.js +10 -1
  15. package/dist/components/confirm-dialog/confirm-dialog.types.d.ts +7 -0
  16. package/dist/components/confirm-dialog/index.js +478 -106
  17. package/dist/components/connection-indicator/connection-indicator.css +136 -0
  18. package/dist/components/connection-indicator/connection-indicator.css.d.ts +1 -0
  19. package/dist/components/connection-indicator/connection-indicator.schema.d.ts +3 -0
  20. package/dist/components/connection-indicator/connection-indicator.schema.js +89 -0
  21. package/dist/components/connection-indicator/connection-indicator.svelte.d.ts +21 -0
  22. package/dist/components/connection-indicator/connection-indicator.types.d.ts +46 -0
  23. package/dist/components/connection-indicator/connection-indicator.variables.d.ts +2 -0
  24. package/dist/components/connection-indicator/connection-indicator.variables.js +61 -0
  25. package/dist/components/connection-indicator/index.d.ts +5 -0
  26. package/dist/components/connection-indicator/index.js +409 -0
  27. package/dist/components/container/container.schema.js +4 -4
  28. package/dist/components/faceted-filter-bar/faceted-filter-bar.schema.js +5 -1
  29. package/dist/components/faceted-filter-bar/faceted-filter-bar.types.d.ts +2 -0
  30. package/dist/components/faceted-filter-bar/index.js +40 -31
  31. package/dist/components/invocation-rule-builder/index.d.ts +1 -1
  32. package/dist/components/invocation-rule-builder/index.js +305 -150
  33. package/dist/components/invocation-rule-builder/invocation-rule-builder.css +28 -0
  34. package/dist/components/invocation-rule-builder/invocation-rule-builder.schema.js +89 -5
  35. package/dist/components/invocation-rule-builder/invocation-rule-builder.svelte.d.ts +2 -1
  36. package/dist/components/invocation-rule-builder/invocation-rule-builder.types.d.ts +114 -16
  37. package/dist/components/navigation-bar/index.js +11 -17
  38. package/dist/components/run-step-timeline/index.d.ts +1 -1
  39. package/dist/components/run-step-timeline/index.js +1052 -560
  40. package/dist/components/run-step-timeline/run-step-branch-disclosure.svelte.d.ts +17 -0
  41. package/dist/components/run-step-timeline/run-step-timeline.css +124 -0
  42. package/dist/components/run-step-timeline/run-step-timeline.schema.js +873 -350
  43. package/dist/components/run-step-timeline/run-step-timeline.svelte.d.ts +1 -15
  44. package/dist/components/run-step-timeline/run-step-timeline.types.d.ts +182 -5
  45. package/dist/components/run-step-timeline/run-step-timeline.utilities.d.ts +60 -0
  46. package/dist/components/schedule-builder/index.d.ts +5 -0
  47. package/dist/components/schedule-builder/index.js +3176 -0
  48. package/dist/components/schedule-builder/schedule-builder.css +121 -0
  49. package/dist/components/schedule-builder/schedule-builder.css.d.ts +1 -0
  50. package/dist/components/schedule-builder/schedule-builder.schema.d.ts +3 -0
  51. package/dist/components/schedule-builder/schedule-builder.schema.js +141 -0
  52. package/dist/components/schedule-builder/schedule-builder.svelte.d.ts +22 -0
  53. package/dist/components/schedule-builder/schedule-builder.types.d.ts +130 -0
  54. package/dist/components/schedule-builder/schedule-builder.utilities.d.ts +78 -0
  55. package/dist/components/schedule-builder/schedule-builder.variables.d.ts +2 -0
  56. package/dist/components/schedule-builder/schedule-builder.variables.js +61 -0
  57. package/dist/components/tag-input/index.js +22 -4
  58. package/dist/components/tag-input/tag-input.schema.js +5 -1
  59. package/dist/components/tag-input/tag-input.types.d.ts +4 -0
  60. package/dist/index.d.ts +6 -2
  61. package/dist/index.js +23069 -20833
  62. package/dist/server/components/access-gate/index.js +2 -1
  63. package/dist/server/components/chat/index.js +4 -4
  64. package/dist/server/components/chat-conversation-header/index.js +5 -5
  65. package/dist/server/components/chat-conversation-list/index.js +3 -3
  66. package/dist/server/components/color-field/index.js +2 -2
  67. package/dist/server/components/confirm-dialog/index.js +7 -2
  68. package/dist/server/components/connection-indicator/index.js +16 -0
  69. package/dist/server/components/data-table/index.js +3 -3
  70. package/dist/server/components/diff-viewer/index.js +4 -3
  71. package/dist/server/components/faceted-filter-bar/index.js +1 -1
  72. package/dist/server/components/invocation-rule-builder/index.js +1 -1
  73. package/dist/server/components/json-schema-editor/index.js +4 -3
  74. package/dist/server/components/navigation-bar/index.js +1 -1
  75. package/dist/server/components/review-editor/index.js +4 -3
  76. package/dist/server/components/run-step-timeline/index.js +1 -1
  77. package/dist/server/components/schedule-builder/index.js +30 -0
  78. package/dist/server/components/side-navigation/index.js +2 -2
  79. package/dist/server/components/table/index.js +3 -3
  80. package/dist/server/components/tag-input/index.js +1 -1
  81. package/dist/server/components/time-field/index.js +5 -3
  82. package/dist/server/index.js +105 -92
  83. package/dist/server/index.server-0sx92swg.js +9 -0
  84. package/dist/server/{index.server-mzzb7dnm.js → index.server-2bbwajb2.js} +1 -1
  85. package/dist/server/{index.server-q7w4jw89.js → index.server-2s5h1d05.js} +2 -2
  86. package/dist/server/{index.server-dg83ke3k.js → index.server-2zdbz3rj.js} +1 -1
  87. package/dist/server/{index.server-0jhra85x.js → index.server-3w72dxz7.js} +4 -4
  88. package/dist/server/{index.server-5tf22d6e.js → index.server-4wx0qven.js} +3 -3
  89. package/dist/server/{index.server-qh9qzd5t.js → index.server-4x4hgvqe.js} +283 -13
  90. package/dist/server/{index.server-c6pe4kaf.js → index.server-5gzyqv78.js} +1 -1
  91. package/dist/server/{index.server-mqt1ngqw.js → index.server-6hnfzc7f.js} +19 -6
  92. package/dist/server/{index.server-894t97kv.js → index.server-7yvpb6z7.js} +4 -4
  93. package/dist/server/index.server-be9bpqeh.js +93 -0
  94. package/dist/server/{index.server-f3z7tpxr.js → index.server-c42b462p.js} +2 -4
  95. package/dist/server/{index.server-7vyzen5x.js → index.server-c5qv3p61.js} +171 -63
  96. package/dist/server/index.server-cch0eed5.js +144 -0
  97. package/dist/server/{index.server-btgb3d56.js → index.server-d5a5ccz7.js} +18 -12
  98. package/dist/server/index.server-g5jgq3jp.js +793 -0
  99. package/dist/server/index.server-gmh5ar16.js +2 -0
  100. package/dist/server/{index.server-0atyznp7.js → index.server-h89zk5ev.js} +7 -7
  101. package/dist/server/{index.server-jxg42jpz.js → index.server-jdcq5zza.js} +7 -7
  102. package/dist/server/{index.server-2dga5yzr.js → index.server-jfrssta6.js} +1 -1
  103. package/dist/server/{index.server-fy068yne.js → index.server-jj76374p.js} +1 -1
  104. package/dist/server/{index.server-39ne5cd1.js → index.server-pxq277t5.js} +32 -1
  105. package/dist/server/index.server-qbkxz7r2.js +750 -0
  106. package/dist/server/{index.server-marjz8ps.js → index.server-tcay903b.js} +3 -2
  107. package/dist/server/{index.server-yz4k36cw.js → index.server-z7hxtqs1.js} +1 -1
  108. package/package.json +87 -97
  109. package/src/components/chat/builders.ts +445 -14
  110. package/src/components/chat/conversation-model.ts +1 -0
  111. package/src/components/chat/index.ts +1 -0
  112. package/src/components/chat/utilities/conversation.ts +33 -15
  113. package/src/components/code-block/code-block.css +15 -9
  114. package/src/components/code-block/code-block.variables.json +7 -1
  115. package/src/components/code-block/code-block.variables.ts +7 -1
  116. package/src/components/confirm-dialog/confirm-dialog.css +2 -0
  117. package/src/components/confirm-dialog/confirm-dialog.schema.json +8 -0
  118. package/src/components/confirm-dialog/confirm-dialog.schema.ts +10 -0
  119. package/src/components/confirm-dialog/confirm-dialog.svelte +31 -3
  120. package/src/components/confirm-dialog/confirm-dialog.types.ts +7 -0
  121. package/src/components/connection-indicator/connection-indicator.css +136 -0
  122. package/src/components/connection-indicator/connection-indicator.examples.json +31 -0
  123. package/src/components/connection-indicator/connection-indicator.schema.json +29 -0
  124. package/src/components/connection-indicator/connection-indicator.schema.ts +36 -0
  125. package/src/components/connection-indicator/connection-indicator.svelte +63 -0
  126. package/src/components/connection-indicator/connection-indicator.types.ts +58 -0
  127. package/src/components/connection-indicator/connection-indicator.variables.json +1 -0
  128. package/src/components/connection-indicator/connection-indicator.variables.ts +3 -0
  129. package/src/components/connection-indicator/index.ts +10 -0
  130. package/src/components/container/container.schema.json +3 -3
  131. package/src/components/container/container.schema.ts +3 -3
  132. package/src/components/faceted-filter-bar/faceted-filter-bar.schema.json +4 -0
  133. package/src/components/faceted-filter-bar/faceted-filter-bar.schema.ts +4 -0
  134. package/src/components/faceted-filter-bar/faceted-filter-bar.svelte +13 -11
  135. package/src/components/faceted-filter-bar/faceted-filter-bar.types.ts +2 -0
  136. package/src/components/invocation-rule-builder/index.ts +4 -0
  137. package/src/components/invocation-rule-builder/invocation-rule-builder.css +28 -0
  138. package/src/components/invocation-rule-builder/invocation-rule-builder.examples.json +6 -0
  139. package/src/components/invocation-rule-builder/invocation-rule-builder.schema.json +76 -3
  140. package/src/components/invocation-rule-builder/invocation-rule-builder.schema.ts +83 -3
  141. package/src/components/invocation-rule-builder/invocation-rule-builder.svelte +326 -113
  142. package/src/components/invocation-rule-builder/invocation-rule-builder.types.ts +157 -43
  143. package/src/components/navigation-bar/navigation-bar.svelte +2 -2
  144. package/src/components/run-step-timeline/index.ts +8 -0
  145. package/src/components/run-step-timeline/run-step-branch-disclosure.svelte +16 -0
  146. package/src/components/run-step-timeline/run-step-timeline.css +124 -0
  147. package/src/components/run-step-timeline/run-step-timeline.examples.json +18 -0
  148. package/src/components/run-step-timeline/run-step-timeline.schema.json +872 -349
  149. package/src/components/run-step-timeline/run-step-timeline.schema.ts +931 -349
  150. package/src/components/run-step-timeline/run-step-timeline.svelte +388 -269
  151. package/src/components/run-step-timeline/run-step-timeline.types.ts +192 -5
  152. package/src/components/run-step-timeline/run-step-timeline.utilities.ts +310 -0
  153. package/src/components/schedule-builder/index.ts +14 -0
  154. package/src/components/schedule-builder/schedule-builder.css +121 -0
  155. package/src/components/schedule-builder/schedule-builder.examples.json +31 -0
  156. package/src/components/schedule-builder/schedule-builder.schema.json +78 -0
  157. package/src/components/schedule-builder/schedule-builder.schema.ts +86 -0
  158. package/src/components/schedule-builder/schedule-builder.svelte +629 -0
  159. package/src/components/schedule-builder/schedule-builder.types.ts +137 -0
  160. package/src/components/schedule-builder/schedule-builder.utilities.ts +344 -0
  161. package/src/components/schedule-builder/schedule-builder.variables.json +1 -0
  162. package/src/components/schedule-builder/schedule-builder.variables.ts +3 -0
  163. package/src/components/tag-input/tag-input.schema.json +4 -0
  164. package/src/components/tag-input/tag-input.schema.ts +4 -0
  165. package/src/components/tag-input/tag-input.svelte +20 -3
  166. package/src/components/tag-input/tag-input.types.ts +4 -0
  167. package/src/index.ts +23 -0
  168. package/src/styles/components.css +2 -0
  169. package/dist/server/index.server-qk1h0n02.js +0 -393
@@ -103,12 +103,96 @@ export type RunStep = {
103
103
  * Optional link to logs, traces, or a step detail route.
104
104
  */
105
105
  link?: RunStepLink | undefined;
106
+ /**
107
+ * Marks a step that was speculatively executed and then unwound (rolled
108
+ * back). Orthogonal to `status`: the step keeps its real terminal status
109
+ * underneath, but renders struck-through and de-emphasized while staying
110
+ * inspectable. A rewound step announces its unwound state to assistive
111
+ * technology in addition to its status.
112
+ */
113
+ rewound?: boolean | undefined;
114
+ /**
115
+ * Id of the forward step that this step compensates (reverses), as in a
116
+ * saga rollback. When set, the step renders inset beneath its forward step
117
+ * with a dashed reversal connector. The referenced id should be a sibling
118
+ * step's `id`; an unmatched id renders the step in place without inset.
119
+ */
120
+ compensates?: string | undefined;
106
121
  /**
107
122
  * Nested child-workflow steps rendered as indented lanes.
108
123
  */
109
124
  children?: RunStep[] | undefined;
110
125
  };
111
126
 
127
+ /**
128
+ * Outcome of a single sub-lane within a {@link RunStepBranchGroup}.
129
+ *
130
+ * - `won` — this lane produced the committed result; emphasized.
131
+ * - `lost` — this lane was superseded by the winner; muted.
132
+ * - `settled` — this lane completed without a competitive winner/loser
133
+ * distinction (e.g. all lanes were kept). Neutral emphasis.
134
+ *
135
+ * Omit the outcome entirely while a race is still in flight.
136
+ */
137
+ export type RunStepBranchLaneOutcome = 'won' | 'lost' | 'settled';
138
+
139
+ /**
140
+ * One parallel sub-lane inside a {@link RunStepBranchGroup}. Each lane is an
141
+ * ordered sequence of steps that executed concurrently with its siblings.
142
+ */
143
+ export type RunStepBranchLane = {
144
+ /** Stable identity; used as the keyed list identity within the group. */
145
+ id: string;
146
+ /** Optional display label for the lane (e.g. the candidate or strategy name). */
147
+ label?: string | undefined;
148
+ /**
149
+ * Competitive outcome for the lane. Drives winner emphasis / loser muting.
150
+ * Omit while the branch is still racing.
151
+ */
152
+ outcome?: RunStepBranchLaneOutcome | undefined;
153
+ /** Ordered steps that ran within this lane. */
154
+ steps: RunStep[];
155
+ };
156
+
157
+ /**
158
+ * A branch/coordination group: a single timeline entry that fans out into N
159
+ * parallel sub-lanes (e.g. a speculative race or a scatter/gather). Rendered
160
+ * with the lanes side by side conceptually — the winning lane emphasized and
161
+ * the losers muted — and collapsible. Distinguished from a plain {@link RunStep}
162
+ * by its `kind` discriminator, mirroring the additive-entry pattern used by
163
+ * other cinder timelines.
164
+ *
165
+ * @schemaObject
166
+ */
167
+ export type RunStepBranchGroup = {
168
+ /** Discriminator identifying a branch-group entry. */
169
+ kind: 'branch';
170
+ /** Stable identity; used as the keyed list identity. */
171
+ id: string;
172
+ /** Display label for the branch group (e.g. "Race deploy candidates"). */
173
+ label: string;
174
+ /** The parallel sub-lanes. Order is presentational only. */
175
+ lanes: RunStepBranchLane[];
176
+ /**
177
+ * Collapse the group by default once the lane count reaches this threshold.
178
+ * Defaults to 3. Set to a large number to effectively disable auto-collapse.
179
+ */
180
+ collapseThreshold?: number | undefined;
181
+ /**
182
+ * Force the initial collapsed (`true`) or expanded (`false`) state,
183
+ * overriding `collapseThreshold`. The group remains user-togglable.
184
+ */
185
+ collapsed?: boolean | undefined;
186
+ };
187
+
188
+ /**
189
+ * A single top-level entry in a RunStepTimeline: either a normal step or a
190
+ * branch/coordination group. Existing `RunStep[]` arrays remain valid because
191
+ * the union is additive and branch groups are opt-in via the `kind`
192
+ * discriminator.
193
+ */
194
+ export type RunStepTimelineEntry = RunStep | RunStepBranchGroup;
195
+
112
196
  /**
113
197
  * Schema generator surface for one top-level step.
114
198
  * Public `RunStep` stays recursive; this finite shape keeps JSON Schema generation bounded.
@@ -163,6 +247,10 @@ export type RunStepTimelineSchemaStep = {
163
247
  * Optional link to logs, traces, or a step detail route.
164
248
  */
165
249
  link?: RunStepLink | undefined;
250
+ /** Marks a step that was speculatively executed and then unwound (rolled back). */
251
+ rewound?: boolean | undefined;
252
+ /** Id of the forward step that this step compensates (reverses). */
253
+ compensates?: string | undefined;
166
254
  /**
167
255
  * Schema-bounded nested child-workflow steps.
168
256
  */
@@ -198,6 +286,10 @@ export type RunStepTimelineSchemaChildStep = {
198
286
  details?: RunStepDetail[] | undefined;
199
287
  /** Optional link to logs, traces, or a step detail route. */
200
288
  link?: RunStepLink | undefined;
289
+ /** Marks a step that was speculatively executed and then unwound (rolled back). */
290
+ rewound?: boolean | undefined;
291
+ /** Id of the forward step that this step compensates (reverses). */
292
+ compensates?: string | undefined;
201
293
  /**
202
294
  * Nested child-workflow steps rendered at depth 2.
203
295
  */
@@ -233,6 +325,10 @@ export type RunStepTimelineSchemaGrandchildStep = {
233
325
  details?: RunStepDetail[] | undefined;
234
326
  /** Optional link to logs, traces, or a step detail route. */
235
327
  link?: RunStepLink | undefined;
328
+ /** Marks a step that was speculatively executed and then unwound (rolled back). */
329
+ rewound?: boolean | undefined;
330
+ /** Id of the forward step that this step compensates (reverses). */
331
+ compensates?: string | undefined;
236
332
  /**
237
333
  * Nested child-workflow steps rendered at depth 3.
238
334
  */
@@ -268,8 +364,98 @@ export type RunStepTimelineSchemaGreatGrandchildStep = {
268
364
  details?: RunStepDetail[] | undefined;
269
365
  /** Optional link to logs, traces, or a step detail route. */
270
366
  link?: RunStepLink | undefined;
367
+ /** Marks a step that was speculatively executed and then unwound (rolled back). */
368
+ rewound?: boolean | undefined;
369
+ /** Id of the forward step that this step compensates (reverses). */
370
+ compensates?: string | undefined;
271
371
  };
272
372
 
373
+ /**
374
+ * Schema-facing step used for branch-lane sequences. Lane steps are
375
+ * `RunStep[]` at runtime and can nest, so this type carries an optional
376
+ * `children` array of the same shape (recursive at the type level, for
377
+ * consumer-typing convenience) that validates lane-step children as steps.
378
+ * The generated JSON Schema depth-caps this nesting to the same rendered depth
379
+ * as the main rail; the recursive TYPE is the ergonomic mirror of that
380
+ * depth-capped schema, not an assertion that arbitrary depth is rendered.
381
+ * @schemaObject
382
+ */
383
+ export type RunStepTimelineSchemaLaneStep = {
384
+ /** Stable identity; used as the keyed list identity. */
385
+ id: string;
386
+ /** Display label for this step. */
387
+ label: string;
388
+ /** Generic execution state. */
389
+ status: RunStepStatus;
390
+ /** ISO datetime string for when this step started. */
391
+ startTime?: string | undefined;
392
+ /** ISO datetime string for when this step ended. */
393
+ endTime?: string | undefined;
394
+ /** Human-readable duration string, e.g. "1m 23s". */
395
+ duration?: string | undefined;
396
+ /** Number of attempts made so far, including any retries. */
397
+ attemptCount?: number | undefined;
398
+ /** Number of actions associated with this step. */
399
+ actionsCount?: number | undefined;
400
+ /** Optional determinate progress value between 0 and `progressMax`. */
401
+ progress?: number | undefined;
402
+ /** Maximum value for the progress bar. Defaults to 100. */
403
+ progressMax?: number | undefined;
404
+ /** Expandable detail panels (logs, payloads, errors) shown inline. */
405
+ details?: RunStepDetail[] | undefined;
406
+ /** Optional link to logs, traces, or a step detail route. */
407
+ link?: RunStepLink | undefined;
408
+ /** Marks a step that was speculatively executed and then unwound (rolled back). */
409
+ rewound?: boolean | undefined;
410
+ /** Id of the forward step that this step compensates (reverses). */
411
+ compensates?: string | undefined;
412
+ /** Nested steps within a branch lane. */
413
+ children?: RunStepTimelineSchemaLaneStep[] | undefined;
414
+ };
415
+
416
+ /**
417
+ * Schema-bounded sub-lane inside a {@link RunStepTimelineSchemaBranchGroup}.
418
+ * Lane steps are bounded to a leaf surface so JSON Schema generation stays
419
+ * finite.
420
+ * @schemaObject
421
+ */
422
+ export type RunStepTimelineSchemaBranchLane = {
423
+ /** Stable identity; used as the keyed list identity within the group. */
424
+ id: string;
425
+ /** Optional display label for the lane. */
426
+ label?: string | undefined;
427
+ /** Competitive outcome for the lane. Omit while the branch is still racing. */
428
+ outcome?: RunStepBranchLaneOutcome | undefined;
429
+ /** Ordered steps that ran within this lane. */
430
+ steps: RunStepTimelineSchemaLaneStep[];
431
+ };
432
+
433
+ /**
434
+ * Schema generator surface for one top-level branch/coordination group.
435
+ * @schemaObject
436
+ */
437
+ export type RunStepTimelineSchemaBranchGroup = {
438
+ /** Discriminator identifying a branch-group entry. */
439
+ kind: 'branch';
440
+ /** Stable identity; used as the keyed list identity. */
441
+ id: string;
442
+ /** Display label for the branch group. */
443
+ label: string;
444
+ /** The parallel sub-lanes. */
445
+ lanes: RunStepTimelineSchemaBranchLane[];
446
+ /** Collapse the group by default once the lane count reaches this threshold. Defaults to 3. */
447
+ collapseThreshold?: number | undefined;
448
+ /** Force the initial collapsed (`true`) or expanded (`false`) state. */
449
+ collapsed?: boolean | undefined;
450
+ };
451
+
452
+ /**
453
+ * A single schema-bounded top-level entry: either a step or a branch group.
454
+ */
455
+ export type RunStepTimelineSchemaEntry =
456
+ | RunStepTimelineSchemaStep
457
+ | RunStepTimelineSchemaBranchGroup;
458
+
273
459
  /**
274
460
  * Props for the RunStepTimeline component.
275
461
  *
@@ -279,10 +465,12 @@ export type RunStepTimelineSchemaGreatGrandchildStep = {
279
465
  */
280
466
  export type RunStepTimelineProps = Omit<HTMLAttributes<HTMLOListElement>, 'class' | 'children'> & {
281
467
  /**
282
- * Ordered list of steps to render.
468
+ * Ordered list of timeline entries to render. Each entry is either a
469
+ * {@link RunStep} or a {@link RunStepBranchGroup}. Plain `RunStep[]` arrays
470
+ * remain valid — branch groups are opt-in via the `kind` discriminator.
283
471
  * @schemaObject
284
472
  */
285
- steps: RunStep[];
473
+ steps: RunStepTimelineEntry[];
286
474
  /**
287
475
  * Accessible label for the timeline list.
288
476
  * Used as `aria-label` when `aria-labelledby` is absent.
@@ -299,10 +487,9 @@ export type RunStepTimelineProps = Omit<HTMLAttributes<HTMLOListElement>, 'class
299
487
  /** Schema generator surface for RunStepTimeline — excludes snippet props. */
300
488
  export interface RunStepTimelineSchemaProps {
301
489
  /**
302
- * Ordered list of steps to render.
303
- * @schemaObject
490
+ * Ordered list of timeline entries to render — either steps or branch groups.
304
491
  */
305
- steps: RunStepTimelineSchemaStep[];
492
+ steps: RunStepTimelineSchemaEntry[];
306
493
  /** Accessible label for the timeline list. */
307
494
  label?: string | undefined;
308
495
  /** Additional CSS classes applied to the root element. */
@@ -0,0 +1,310 @@
1
+ import type { StatusDotStatus } from '../status-dot/status-dot.types.ts';
2
+ import type {
3
+ RunStep,
4
+ RunStepBranchGroup,
5
+ RunStepBranchLane,
6
+ RunStepBranchLaneOutcome,
7
+ RunStepStatus,
8
+ RunStepTimelineEntry,
9
+ } from './run-step-timeline.types.ts';
10
+
11
+ /**
12
+ * Relocate resolved compensation steps immediately after the subtree of the
13
+ * sibling they reverse. Unresolved, self-referential, and cyclic links retain
14
+ * consumer order. Multiple compensations retain their relative input order.
15
+ */
16
+ export function relocateCompensationSteps(steps: RunStep[]): RunStep[] {
17
+ return relocateSiblingItems(steps, (step) => step);
18
+ }
19
+
20
+ function relocateSiblingItems<T>(items: T[], getStep: (item: T) => RunStep | undefined): T[] {
21
+ if (!items.some((item) => getStep(item)?.compensates !== undefined)) return items;
22
+
23
+ const stepById = new Map<string, RunStep>();
24
+ for (const item of items) {
25
+ const step = getStep(item);
26
+ if (step !== undefined) {
27
+ stepById.set(step.id, step);
28
+ }
29
+ }
30
+ const canRelocate = (step: RunStep): boolean => {
31
+ const visited = new Set<string>([step.id]);
32
+ let targetId = step.compensates;
33
+ while (targetId !== undefined) {
34
+ const target = stepById.get(targetId);
35
+ if (target === undefined || visited.has(targetId)) return false;
36
+ visited.add(targetId);
37
+ targetId = target.compensates;
38
+ }
39
+ return true;
40
+ };
41
+ const relocatable = new Set<RunStep>();
42
+ const compensationsByTarget = new Map<string, T[]>();
43
+ for (const item of items) {
44
+ const step = getStep(item);
45
+ if (step === undefined || step.compensates === undefined || !canRelocate(step)) continue;
46
+ relocatable.add(step);
47
+ const compensations = compensationsByTarget.get(step.compensates) ?? [];
48
+ compensations.push(item);
49
+ compensationsByTarget.set(step.compensates, compensations);
50
+ }
51
+ const result: T[] = [];
52
+ const appendWithCompensations = (item: T): void => {
53
+ result.push(item);
54
+ const step = getStep(item);
55
+ if (step === undefined) return;
56
+ for (const compensation of compensationsByTarget.get(step.id) ?? []) {
57
+ appendWithCompensations(compensation);
58
+ }
59
+ };
60
+ for (const item of items) {
61
+ const step = getStep(item);
62
+ if (step === undefined || !relocatable.has(step)) appendWithCompensations(item);
63
+ }
64
+ return result;
65
+ }
66
+
67
+ /** Relocate top-level compensations while preserving branch rows as rail entries. */
68
+ export function relocateCompensationEntries(
69
+ entries: RunStepTimelineEntry[],
70
+ ): RunStepTimelineEntry[] {
71
+ return relocateSiblingItems(entries, (entry) => (isBranchGroup(entry) ? undefined : entry));
72
+ }
73
+
74
+ function isBranchGroup(entry: RunStepTimelineEntry): entry is RunStepBranchGroup {
75
+ return 'kind' in entry && entry.kind === 'branch';
76
+ }
77
+
78
+ /** Badge variants used for the per-step status chip. */
79
+ export type RunStepBadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'accent';
80
+
81
+ /** Map a generic {@link RunStepStatus} onto a StatusDot status token. */
82
+ export function statusDotStatus(status: RunStepStatus): StatusDotStatus {
83
+ switch (status) {
84
+ case 'succeeded':
85
+ return 'success';
86
+ case 'failed':
87
+ return 'danger';
88
+ case 'running':
89
+ return 'online';
90
+ case 'retrying':
91
+ return 'warning';
92
+ case 'waiting_approval':
93
+ return 'accent';
94
+ case 'cancelled':
95
+ return 'offline';
96
+ case 'skipped':
97
+ return 'neutral';
98
+ case 'pending':
99
+ return 'pending';
100
+ }
101
+ }
102
+
103
+ /** Human-readable label for the StatusDot accessible name. */
104
+ export function statusLabel(status: RunStepStatus): string {
105
+ switch (status) {
106
+ case 'pending':
107
+ return 'Pending';
108
+ case 'running':
109
+ return 'Running';
110
+ case 'succeeded':
111
+ return 'Succeeded';
112
+ case 'failed':
113
+ return 'Failed';
114
+ case 'cancelled':
115
+ return 'Cancelled';
116
+ case 'skipped':
117
+ return 'Skipped';
118
+ case 'retrying':
119
+ return 'Retrying';
120
+ case 'waiting_approval':
121
+ return 'Waiting approval';
122
+ }
123
+ }
124
+
125
+ /** Map a {@link RunStepStatus} to a Badge variant for the status chip. */
126
+ export function badgeVariant(status: RunStepStatus): RunStepBadgeVariant {
127
+ switch (status) {
128
+ case 'succeeded':
129
+ return 'success';
130
+ case 'failed':
131
+ return 'danger';
132
+ case 'running':
133
+ return 'info';
134
+ case 'retrying':
135
+ return 'warning';
136
+ case 'waiting_approval':
137
+ return 'accent';
138
+ case 'cancelled':
139
+ return 'neutral';
140
+ case 'skipped':
141
+ return 'neutral';
142
+ case 'pending':
143
+ return 'neutral';
144
+ }
145
+ }
146
+
147
+ /** Whether this status is a terminal state (no further changes expected). */
148
+ export function isTerminal(status: RunStepStatus): boolean {
149
+ return (
150
+ status === 'succeeded' || status === 'failed' || status === 'cancelled' || status === 'skipped'
151
+ );
152
+ }
153
+
154
+ /** Whether this status represents an in-flight ("current") step. */
155
+ export function isCurrent(status: RunStepStatus): boolean {
156
+ return status === 'running' || status === 'retrying' || status === 'waiting_approval';
157
+ }
158
+
159
+ /** Whether this step should render a progress bar. */
160
+ export function hasProgress(step: RunStep): boolean {
161
+ return step.progress !== undefined && isCurrent(step.status);
162
+ }
163
+
164
+ /** Metadata items for a step, as term/definition pairs. */
165
+ export function metadataItems(step: RunStep): { term: string; definition: string }[] {
166
+ const items: { term: string; definition: string }[] = [];
167
+ if (step.startTime !== undefined) {
168
+ items.push({ term: 'Started', definition: step.startTime });
169
+ }
170
+ if (step.endTime !== undefined) {
171
+ items.push({ term: 'Ended', definition: step.endTime });
172
+ }
173
+ if (step.duration !== undefined) {
174
+ items.push({ term: 'Duration', definition: step.duration });
175
+ }
176
+ if (step.attemptCount !== undefined && step.attemptCount > 1) {
177
+ items.push({ term: 'Attempts', definition: String(step.attemptCount) });
178
+ }
179
+ return items;
180
+ }
181
+
182
+ /** Pluralized "N action(s)" label. */
183
+ export function actionsCountLabel(actionsCount: number): string {
184
+ return actionsCount === 1 ? '1 action' : `${actionsCount} actions`;
185
+ }
186
+
187
+ /** Pluralized "N nested step(s) hidden" label for the depth-cap row. */
188
+ export function hiddenNestedStepLabel(hiddenStepCount: number): string {
189
+ return hiddenStepCount === 1 ? '1 nested step hidden' : `${hiddenStepCount} nested steps hidden`;
190
+ }
191
+
192
+ /** Human-readable label for a branch-lane outcome, used in badges and announcements. */
193
+ export function laneOutcomeLabel(outcome: RunStepBranchLaneOutcome): string {
194
+ switch (outcome) {
195
+ case 'won':
196
+ return 'Won';
197
+ case 'lost':
198
+ return 'Lost';
199
+ case 'settled':
200
+ return 'Settled';
201
+ }
202
+ }
203
+
204
+ /** Badge variant for a branch-lane outcome. */
205
+ export function laneOutcomeBadgeVariant(outcome: RunStepBranchLaneOutcome): RunStepBadgeVariant {
206
+ switch (outcome) {
207
+ case 'won':
208
+ return 'success';
209
+ case 'lost':
210
+ return 'neutral';
211
+ case 'settled':
212
+ return 'info';
213
+ }
214
+ }
215
+
216
+ /**
217
+ * One-line accessible summary of a branch group's lane outcomes, e.g.
218
+ * "1 won, 2 lost" or "3 lanes racing" while outcomes are still pending.
219
+ */
220
+ export function branchOutcomeSummary(lanes: RunStepBranchLane[]): string {
221
+ const counts = { won: 0, lost: 0, settled: 0, pending: 0 };
222
+ for (const lane of lanes) {
223
+ if (lane.outcome === undefined) counts.pending += 1;
224
+ else counts[lane.outcome] += 1;
225
+ }
226
+ const parts: string[] = [];
227
+ if (counts.won > 0) parts.push(`${counts.won} won`);
228
+ if (counts.lost > 0) parts.push(`${counts.lost} lost`);
229
+ if (counts.settled > 0) parts.push(`${counts.settled} settled`);
230
+ if (counts.pending > 0) {
231
+ parts.push(counts.pending === 1 ? '1 lane racing' : `${counts.pending} lanes racing`);
232
+ }
233
+ return parts.length > 0 ? parts.join(', ') : 'No lanes';
234
+ }
235
+
236
+ /**
237
+ * Whether any step in any lane of a branch group is currently in-flight.
238
+ * Iterative (explicit stack) so a deeply-nested lane can't overflow the call
239
+ * stack — this runs before the render path's depth cap.
240
+ */
241
+ export function branchGroupHasCurrentStep(group: RunStepBranchGroup): boolean {
242
+ const stack: RunStep[] = group.lanes.flatMap((lane) => lane.steps);
243
+ while (stack.length > 0) {
244
+ const step = stack.pop();
245
+ if (step === undefined) continue;
246
+ if (isCurrent(step.status)) return true;
247
+ if (step.children) {
248
+ for (const child of step.children) stack.push(child);
249
+ }
250
+ }
251
+ return false;
252
+ }
253
+
254
+ /**
255
+ * Resolve whether a branch group starts collapsed. An explicit `collapsed`
256
+ * wins; otherwise the group collapses once its lane count reaches the
257
+ * threshold (default 3).
258
+ */
259
+ export function branchStartsCollapsed(
260
+ laneCount: number,
261
+ collapseThreshold: number | undefined,
262
+ collapsed: boolean | undefined,
263
+ ): boolean {
264
+ if (collapsed !== undefined) return collapsed;
265
+ // Normalize the public `collapseThreshold` to a finite positive integer so a
266
+ // stray 0, negative, NaN, or fractional value can't produce surprising
267
+ // always-/never-collapsed behavior; fall back to the default of 3.
268
+ const threshold =
269
+ collapseThreshold !== undefined && Number.isInteger(collapseThreshold) && collapseThreshold > 0
270
+ ? collapseThreshold
271
+ : 3;
272
+ return laneCount >= threshold;
273
+ }
274
+
275
+ /** Whether the string contains an ASCII control character (0x00-0x1F or 0x7F). */
276
+ function hasControlCharacter(value: string): boolean {
277
+ for (let index = 0; index < value.length; index += 1) {
278
+ const code = value.charCodeAt(index);
279
+ if (code <= 0x1f || code === 0x7f) return true;
280
+ }
281
+ return false;
282
+ }
283
+
284
+ /**
285
+ * Normalize a step link href to a safe value, or `undefined` when the href is
286
+ * empty, control-character-laden, backslash-bearing, protocol-relative, or a
287
+ * non-http(s) scheme.
288
+ */
289
+ export function safeStepLinkHref(href: string): string | undefined {
290
+ const trimmedHref = href.trim();
291
+ if (trimmedHref === '') return undefined;
292
+ // Reject control characters (0x00-0x1F, 0x7F) without a control-char regex.
293
+ if (hasControlCharacter(trimmedHref)) return undefined;
294
+ if (trimmedHref.includes('\\')) return undefined;
295
+ const leadingSeparators = trimmedHref.match(/^[\\/]+/)?.[0] ?? '';
296
+ if (leadingSeparators.length > 1) return undefined;
297
+
298
+ if (/^[A-Za-z][A-Za-z\d+.-]*:/.test(trimmedHref)) {
299
+ try {
300
+ const parsedUrl = new URL(trimmedHref);
301
+ return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:'
302
+ ? trimmedHref
303
+ : undefined;
304
+ } catch {
305
+ return undefined;
306
+ }
307
+ }
308
+
309
+ return trimmedHref;
310
+ }
@@ -0,0 +1,14 @@
1
+ import './schedule-builder.css';
2
+ import ScheduleBuilder from './schedule-builder.svelte';
3
+
4
+ export default ScheduleBuilder;
5
+ export type {
6
+ ScheduleAuthoringMode,
7
+ ScheduleBuilderProps,
8
+ ScheduleBuilderSchemaProps,
9
+ ScheduleFire,
10
+ ScheduleIntervalUnit,
11
+ ScheduleValue,
12
+ ScheduleValueSchema,
13
+ } from './schedule-builder.types.ts';
14
+ export { ScheduleBuilder };
@@ -0,0 +1,121 @@
1
+ @layer cinder.tokens, cinder.foundation, cinder.components, cinder.utilities;
2
+ @import '../chip/chip.css';
3
+ @import '../input/input.css';
4
+ @import '../number-input/number-input.css';
5
+ @import '../segmented-control/segmented-control.css';
6
+ @import '../select/select.css';
7
+ @import '../time-field/time-field.css';
8
+
9
+ @layer cinder.components {
10
+ /* ========================================
11
+ * SCHEDULE-BUILDER
12
+ * Recurrence-definition control: presets, cron, and interval authoring
13
+ * modes behind a tablist, with an always-visible summary line, next-fires
14
+ * preview, and timezone display slot.
15
+ * ======================================== */
16
+
17
+ .cinder-schedule-builder {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: var(--cinder-space-4);
21
+ }
22
+
23
+ /* ----------------------------------------
24
+ * Mode panel
25
+ * ---------------------------------------- */
26
+
27
+ .cinder-schedule-builder__panel {
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: var(--cinder-space-3);
31
+ padding-block-start: var(--cinder-space-1);
32
+ }
33
+
34
+ .cinder-schedule-builder__preset-kind {
35
+ margin-block-end: var(--cinder-space-1);
36
+ }
37
+
38
+ .cinder-schedule-builder__field-row {
39
+ display: flex;
40
+ flex-wrap: wrap;
41
+ align-items: flex-end;
42
+ gap: var(--cinder-space-3);
43
+ }
44
+
45
+ .cinder-schedule-builder__field-row > * {
46
+ flex: 1 1 10rem;
47
+ min-inline-size: 8rem;
48
+ }
49
+
50
+ .cinder-schedule-builder__weekday-group {
51
+ display: flex;
52
+ flex-wrap: wrap;
53
+ gap: var(--cinder-space-2);
54
+ }
55
+
56
+ .cinder-schedule-builder__cron-fields {
57
+ display: grid;
58
+ grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
59
+ gap: var(--cinder-space-3);
60
+ }
61
+
62
+ /* ----------------------------------------
63
+ * Always-visible informational blocks: summary, preview, timezone
64
+ * ---------------------------------------- */
65
+
66
+ .cinder-schedule-builder__summary,
67
+ .cinder-schedule-builder__preview,
68
+ .cinder-schedule-builder__timezone {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: var(--cinder-space-1);
72
+ margin: 0;
73
+ padding: var(--cinder-space-3);
74
+ border: 1px solid var(--cinder-border-muted);
75
+ border-radius: var(--cinder-radius-md);
76
+ background: var(--cinder-surface-inset);
77
+ }
78
+
79
+ .cinder-schedule-builder__section-label {
80
+ display: block;
81
+ margin: 0;
82
+ font-size: var(--cinder-text-xs);
83
+ font-weight: var(--cinder-font-semibold);
84
+ text-transform: uppercase;
85
+ color: var(--cinder-text-muted);
86
+ }
87
+
88
+ .cinder-schedule-builder__summary-text {
89
+ margin: 0;
90
+ font-size: var(--cinder-text-sm);
91
+ color: var(--cinder-text);
92
+ }
93
+
94
+ .cinder-schedule-builder__preview-list {
95
+ margin: 0;
96
+ padding: 0;
97
+ list-style: none;
98
+ display: flex;
99
+ flex-direction: column;
100
+ gap: var(--cinder-space-1);
101
+ }
102
+
103
+ .cinder-schedule-builder__preview-item {
104
+ font-size: var(--cinder-text-sm);
105
+ color: var(--cinder-text);
106
+ font-variant-numeric: tabular-nums;
107
+ }
108
+
109
+ .cinder-schedule-builder__timezone-value {
110
+ margin: 0;
111
+ font-size: var(--cinder-text-sm);
112
+ color: var(--cinder-text);
113
+ font-family: var(--cinder-font-mono);
114
+ }
115
+
116
+ .cinder-schedule-builder__empty {
117
+ font-size: var(--cinder-text-sm);
118
+ color: var(--cinder-text-muted);
119
+ font-style: italic;
120
+ }
121
+ }