@echothink-ui/motion 0.1.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 (37) hide show
  1. package/README.md +5 -0
  2. package/dist/components/AgentThinkingAnimation.d.ts +2 -0
  3. package/dist/components/AttentionPulse.d.ts +2 -0
  4. package/dist/components/DAGStatusTransition.d.ts +2 -0
  5. package/dist/components/DocumentLockPulse.d.ts +2 -0
  6. package/dist/components/PipelineFlowAnimation.d.ts +2 -0
  7. package/dist/components/ProgressTransition.d.ts +2 -0
  8. package/dist/components/SkeletonLoadingPattern.d.ts +2 -0
  9. package/dist/components/StatusChangeAnimation.d.ts +2 -0
  10. package/dist/components/StepCompletionAnimation.d.ts +2 -0
  11. package/dist/components/StreamingText.d.ts +2 -0
  12. package/dist/components/SyncProgressAnimation.d.ts +2 -0
  13. package/dist/components/motionUtils.d.ts +5 -0
  14. package/dist/components/types.d.ts +82 -0
  15. package/dist/index.cjs +2381 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.js +2333 -0
  19. package/dist/index.js.map +1 -0
  20. package/package.json +38 -0
  21. package/src/components/AgentThinkingAnimation.tsx +59 -0
  22. package/src/components/AttentionPulse.tsx +57 -0
  23. package/src/components/DAGStatusTransition.tsx +292 -0
  24. package/src/components/DocumentLockPulse.tsx +72 -0
  25. package/src/components/PipelineFlowAnimation.tsx +243 -0
  26. package/src/components/ProgressTransition.tsx +51 -0
  27. package/src/components/SkeletonLoadingPattern.tsx +248 -0
  28. package/src/components/StatusChangeAnimation.test.tsx +20 -0
  29. package/src/components/StatusChangeAnimation.tsx +89 -0
  30. package/src/components/StepCompletionAnimation.tsx +75 -0
  31. package/src/components/StreamingText.tsx +77 -0
  32. package/src/components/SyncProgressAnimation.test.tsx +49 -0
  33. package/src/components/SyncProgressAnimation.tsx +256 -0
  34. package/src/components/motionUtils.tsx +942 -0
  35. package/src/components/types.ts +111 -0
  36. package/src/index.test.tsx +97 -0
  37. package/src/index.tsx +44 -0
package/dist/index.js ADDED
@@ -0,0 +1,2333 @@
1
+ // src/components/motionUtils.tsx
2
+ import * as React from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ var reducedMotionQuery = "(prefers-reduced-motion: reduce)";
5
+ function usePrefersReducedMotion() {
6
+ return React.useSyncExternalStore(
7
+ subscribeToReducedMotion,
8
+ getReducedMotionSnapshot,
9
+ getReducedMotionServerSnapshot
10
+ );
11
+ }
12
+ function subscribeToReducedMotion(onStoreChange) {
13
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
14
+ return () => {
15
+ };
16
+ }
17
+ const media = window.matchMedia(reducedMotionQuery);
18
+ const listener = () => onStoreChange();
19
+ media.addEventListener?.("change", listener);
20
+ return () => media.removeEventListener?.("change", listener);
21
+ }
22
+ function getReducedMotionSnapshot() {
23
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
24
+ return window.matchMedia(reducedMotionQuery).matches;
25
+ }
26
+ function getReducedMotionServerSnapshot() {
27
+ return false;
28
+ }
29
+ function MotionStyles() {
30
+ return /* @__PURE__ */ jsx("style", { children: `
31
+ @keyframes eth-motion-flow-dash { to { stroke-dashoffset: -24; } }
32
+ @keyframes eth-motion-pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.035); opacity: .72; } 100% { transform: scale(1); opacity: 1; } }
33
+ @keyframes eth-motion-attention-ring { 0% { opacity: .52; transform: scale(.64); } 70%, 100% { opacity: 0; transform: scale(1.8); } }
34
+ @keyframes eth-motion-flow-node-pulse { 0% { opacity: .42; stroke-width: 1; } 100% { opacity: 0; stroke-width: 7; } }
35
+ @keyframes eth-motion-dot { 0%, 80%, 100% { opacity: .28; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
36
+ @keyframes eth-motion-spin { to { transform: rotate(360deg); } }
37
+ @keyframes eth-motion-sync-sweep { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }
38
+ @keyframes eth-motion-shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
39
+ @keyframes eth-motion-check { 0% { transform: scale(.78); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
40
+ @keyframes eth-motion-step-check { 0% { stroke-dashoffset: 18; } 100% { stroke-dashoffset: 0; } }
41
+ @keyframes eth-motion-step-current { 0% { box-shadow: 0 0 0 0 rgba(15, 98, 254, .24); } 100% { box-shadow: 0 0 0 .375rem rgba(15, 98, 254, 0); } }
42
+ @keyframes eth-motion-status-ring { 0% { stroke-dashoffset: 18; opacity: .18; } 55% { opacity: .48; } 100% { stroke-dashoffset: 0; opacity: .32; } }
43
+ @keyframes eth-motion-status-halo { 0% { opacity: .36; transform: scale(.68); } 100% { opacity: 0; transform: scale(1.85); } }
44
+ @keyframes eth-motion-lock-pulse { 0% { opacity: .34; transform: scale(.72); } 70%, 100% { opacity: 0; transform: scale(1.72); } }
45
+ @keyframes eth-motion-stream-cursor { 0%, 46% { opacity: 1; } 47%, 100% { opacity: 0; } }
46
+ .eth-motion-streaming-text {
47
+ align-items: baseline;
48
+ color: var(--cds-text-primary, #161616);
49
+ display: inline-flex;
50
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
51
+ font-size: inherit;
52
+ line-height: inherit;
53
+ max-inline-size: 100%;
54
+ min-inline-size: 0;
55
+ overflow-wrap: anywhere;
56
+ white-space: pre-wrap;
57
+ }
58
+ .eth-motion-streaming-text__content {
59
+ min-inline-size: 0;
60
+ }
61
+ .eth-motion-streaming-text__cursor {
62
+ background: var(--cds-interactive, #0f62fe);
63
+ block-size: 1em;
64
+ display: inline-block;
65
+ flex: 0 0 auto;
66
+ inline-size: 2px;
67
+ margin-inline-start: .125rem;
68
+ transform: translateY(.125em);
69
+ }
70
+ .eth-motion-streaming-text--streaming .eth-motion-streaming-text__cursor {
71
+ animation: eth-motion-stream-cursor 1s steps(1, end) infinite;
72
+ }
73
+ .eth-motion-streaming-text--complete .eth-motion-streaming-text__cursor {
74
+ inline-size: 0;
75
+ margin-inline-start: 0;
76
+ opacity: 0;
77
+ }
78
+ .eth-motion-pipeline-flow {
79
+ color: var(--cds-text-primary, #161616);
80
+ display: block;
81
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
82
+ inline-size: fit-content;
83
+ max-inline-size: 100%;
84
+ }
85
+ .eth-motion-pipeline-flow__svg {
86
+ block-size: auto;
87
+ display: block;
88
+ font-family: inherit;
89
+ max-inline-size: 100%;
90
+ overflow: visible;
91
+ }
92
+ .eth-motion-pipeline-flow__edge {
93
+ stroke-linecap: round;
94
+ stroke-linejoin: round;
95
+ vector-effect: non-scaling-stroke;
96
+ }
97
+ .eth-motion-pipeline-flow__card {
98
+ fill: var(--cds-layer-02, #ffffff);
99
+ shape-rendering: crispEdges;
100
+ stroke: var(--cds-border-subtle, #e0e0e0);
101
+ stroke-width: 1;
102
+ vector-effect: non-scaling-stroke;
103
+ }
104
+ .eth-motion-pipeline-flow__node--active .eth-motion-pipeline-flow__card {
105
+ fill: var(--cds-layer-01, #f4f4f4);
106
+ stroke: var(--cds-border-strong, #8d8d8d);
107
+ }
108
+ .eth-motion-pipeline-flow__stripe {
109
+ shape-rendering: crispEdges;
110
+ }
111
+ .eth-motion-pipeline-flow__pulse {
112
+ animation: eth-motion-flow-node-pulse 1.3s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) infinite;
113
+ vector-effect: non-scaling-stroke;
114
+ }
115
+ .eth-motion-pipeline-flow__dot {
116
+ stroke: var(--cds-layer-02, #ffffff);
117
+ stroke-width: 2;
118
+ vector-effect: non-scaling-stroke;
119
+ }
120
+ .eth-motion-pipeline-flow__label {
121
+ fill: var(--cds-text-primary, #161616);
122
+ font-size: 13px;
123
+ font-weight: 600;
124
+ }
125
+ .eth-motion-pipeline-flow__status {
126
+ fill: var(--cds-text-secondary, #525252);
127
+ font-size: 11px;
128
+ }
129
+ .eth-motion-pipeline-flow__empty {
130
+ fill: var(--cds-text-secondary, #525252);
131
+ font-size: 13px;
132
+ }
133
+ .eth-motion-pipeline-flow__fallback {
134
+ block-size: 1px;
135
+ clip: rect(0 0 0 0);
136
+ clip-path: inset(50%);
137
+ inline-size: 1px;
138
+ margin: 0;
139
+ overflow: hidden;
140
+ padding: 0;
141
+ position: absolute;
142
+ white-space: nowrap;
143
+ }
144
+ .eth-motion-dag-status {
145
+ color: var(--cds-text-primary, #161616);
146
+ display: block;
147
+ max-inline-size: 100%;
148
+ }
149
+ .eth-motion-dag-status__svg {
150
+ block-size: auto;
151
+ display: block;
152
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
153
+ max-inline-size: 100%;
154
+ overflow: visible;
155
+ }
156
+ .eth-motion-dag-status__edge {
157
+ stroke-linecap: round;
158
+ stroke-linejoin: round;
159
+ vector-effect: non-scaling-stroke;
160
+ }
161
+ .eth-motion-dag-status__card {
162
+ shape-rendering: crispEdges;
163
+ stroke-width: 1;
164
+ vector-effect: non-scaling-stroke;
165
+ }
166
+ .eth-motion-dag-status__change-ring {
167
+ animation: eth-motion-status-ring 700ms var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) 1;
168
+ stroke-dasharray: 4 4;
169
+ stroke-width: 1;
170
+ vector-effect: non-scaling-stroke;
171
+ }
172
+ .eth-motion-dag-status__label {
173
+ fill: var(--cds-text-primary, #161616);
174
+ font-size: 13px;
175
+ font-weight: 600;
176
+ }
177
+ .eth-motion-dag-status__status {
178
+ fill: var(--cds-text-secondary, #525252);
179
+ font-size: 11px;
180
+ }
181
+ .eth-motion-dag-status__fallback {
182
+ block-size: 1px;
183
+ clip: rect(0 0 0 0);
184
+ clip-path: inset(50%);
185
+ inline-size: 1px;
186
+ margin: 0;
187
+ overflow: hidden;
188
+ padding: 0;
189
+ position: absolute;
190
+ white-space: nowrap;
191
+ }
192
+ .eth-motion-sync-progress {
193
+ --eth-motion-sync-accent: var(--cds-interactive, #0f62fe);
194
+ --eth-motion-sync-accent-soft: var(--cds-highlight, #edf5ff);
195
+
196
+ box-sizing: border-box;
197
+ color: var(--cds-text-primary, #161616);
198
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
199
+ line-height: 1.2857;
200
+ max-inline-size: 100%;
201
+ min-inline-size: 0;
202
+ }
203
+ .eth-motion-sync-progress *,
204
+ .eth-motion-sync-progress *::before,
205
+ .eth-motion-sync-progress *::after {
206
+ box-sizing: border-box;
207
+ }
208
+ .eth-motion-sync-progress--compact {
209
+ align-items: center;
210
+ background: var(--cds-layer-01, #ffffff);
211
+ border: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
212
+ border-inline-start: 3px solid var(--eth-motion-sync-accent);
213
+ display: inline-flex;
214
+ gap: var(--cds-spacing-03, .5rem);
215
+ inline-size: fit-content;
216
+ min-block-size: 2rem;
217
+ padding: .375rem .75rem .375rem .625rem;
218
+ vertical-align: middle;
219
+ }
220
+ .eth-motion-sync-progress--detailed {
221
+ align-items: stretch;
222
+ background: var(--cds-layer-01, #ffffff);
223
+ border: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
224
+ border-inline-start: 4px solid var(--eth-motion-sync-accent);
225
+ display: grid;
226
+ grid-template-columns: 1.5rem minmax(0, 1fr);
227
+ inline-size: 100%;
228
+ max-inline-size: 52rem;
229
+ min-block-size: 7.5rem;
230
+ }
231
+ .eth-motion-sync-progress__indicator {
232
+ align-items: center;
233
+ color: var(--eth-motion-sync-accent);
234
+ display: inline-flex;
235
+ flex: 0 0 auto;
236
+ inline-size: 1.125rem;
237
+ justify-content: center;
238
+ }
239
+ .eth-motion-sync-progress--detailed .eth-motion-sync-progress__indicator {
240
+ align-items: start;
241
+ inline-size: auto;
242
+ justify-content: center;
243
+ padding-block-start: var(--cds-spacing-05, 1rem);
244
+ padding-inline-start: var(--cds-spacing-04, .75rem);
245
+ }
246
+ .eth-motion-sync-progress__spinner,
247
+ .eth-motion-sync-progress__glyph {
248
+ block-size: 1rem;
249
+ border-radius: 50%;
250
+ display: inline-block;
251
+ inline-size: 1rem;
252
+ }
253
+ .eth-motion-sync-progress__spinner {
254
+ animation: eth-motion-spin 850ms linear infinite;
255
+ border: 2px solid var(--cds-border-subtle-01, #c6c6c6);
256
+ border-block-start-color: var(--eth-motion-sync-accent);
257
+ }
258
+ .eth-motion-sync-progress__glyph {
259
+ background: var(--eth-motion-sync-accent);
260
+ box-shadow: inset 0 0 0 3px var(--cds-layer-01, #ffffff);
261
+ }
262
+ .eth-motion-sync-progress__compact-copy {
263
+ align-items: baseline;
264
+ display: inline-flex;
265
+ gap: var(--cds-spacing-03, .5rem);
266
+ min-inline-size: 0;
267
+ }
268
+ .eth-motion-sync-progress__label {
269
+ color: var(--cds-text-primary, #161616);
270
+ font-size: var(--cds-body-compact-01-font-size, .875rem);
271
+ font-weight: 600;
272
+ line-height: var(--cds-body-compact-01-line-height, 1.2857);
273
+ min-inline-size: 0;
274
+ overflow-wrap: anywhere;
275
+ }
276
+ .eth-motion-sync-progress__compact-state {
277
+ color: var(--cds-text-secondary, #525252);
278
+ flex: 0 0 auto;
279
+ font-size: var(--cds-label-01-font-size, .75rem);
280
+ line-height: var(--cds-label-01-line-height, 1.3333);
281
+ white-space: nowrap;
282
+ }
283
+ .eth-motion-sync-progress__body {
284
+ display: grid;
285
+ gap: var(--cds-spacing-04, .75rem);
286
+ min-inline-size: 0;
287
+ padding: var(--cds-spacing-05, 1rem);
288
+ }
289
+ .eth-motion-sync-progress__header {
290
+ align-items: start;
291
+ display: flex;
292
+ gap: var(--cds-spacing-04, .75rem);
293
+ justify-content: space-between;
294
+ min-inline-size: 0;
295
+ }
296
+ .eth-motion-sync-progress__title-group {
297
+ display: grid;
298
+ gap: var(--cds-spacing-01, .125rem);
299
+ min-inline-size: 0;
300
+ }
301
+ .eth-motion-sync-progress__eyebrow,
302
+ .eth-motion-sync-progress__description,
303
+ .eth-motion-sync-progress__progress-copy,
304
+ .eth-motion-sync-progress__meta dt {
305
+ color: var(--cds-text-secondary, #525252);
306
+ font-size: var(--cds-label-01-font-size, .75rem);
307
+ line-height: var(--cds-label-01-line-height, 1.3333);
308
+ }
309
+ .eth-motion-sync-progress__eyebrow,
310
+ .eth-motion-sync-progress__meta dt {
311
+ font-weight: 600;
312
+ text-transform: uppercase;
313
+ }
314
+ .eth-motion-sync-progress__description {
315
+ margin: 0;
316
+ max-inline-size: 38rem;
317
+ overflow-wrap: anywhere;
318
+ }
319
+ .eth-motion-sync-progress__chip {
320
+ align-items: center;
321
+ background: var(--eth-motion-sync-accent-soft);
322
+ border: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
323
+ color: var(--cds-text-primary, #161616);
324
+ display: inline-flex;
325
+ flex: 0 0 auto;
326
+ font-size: var(--cds-label-01-font-size, .75rem);
327
+ font-weight: 600;
328
+ gap: var(--cds-spacing-02, .25rem);
329
+ line-height: var(--cds-label-01-line-height, 1.3333);
330
+ min-block-size: 1.5rem;
331
+ padding: .1875rem var(--cds-spacing-03, .5rem);
332
+ white-space: nowrap;
333
+ }
334
+ .eth-motion-sync-progress__chip-dot {
335
+ background: var(--eth-motion-sync-accent);
336
+ block-size: .5rem;
337
+ border-radius: 50%;
338
+ display: inline-block;
339
+ inline-size: .5rem;
340
+ }
341
+ .eth-motion-sync-progress__meter {
342
+ display: grid;
343
+ gap: var(--cds-spacing-03, .5rem);
344
+ min-inline-size: 0;
345
+ }
346
+ .eth-motion-sync-progress__track {
347
+ background: var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
348
+ block-size: .5rem;
349
+ inline-size: 100%;
350
+ overflow: hidden;
351
+ position: relative;
352
+ }
353
+ .eth-motion-sync-progress__bar {
354
+ background: var(--eth-motion-sync-accent);
355
+ block-size: 100%;
356
+ display: block;
357
+ min-inline-size: .25rem;
358
+ transition: inline-size var(--cds-duration-fast-02, 110ms)
359
+ var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9));
360
+ }
361
+ .eth-motion-sync-progress__bar--indeterminate {
362
+ animation: eth-motion-sync-sweep 1.35s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) infinite;
363
+ inline-size: 34%;
364
+ }
365
+ .eth-motion-sync-progress__progress-copy {
366
+ font-variant-numeric: tabular-nums;
367
+ justify-self: end;
368
+ }
369
+ .eth-motion-sync-progress__meta {
370
+ background: var(--cds-layer-02, #f4f4f4);
371
+ border-block-start: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
372
+ display: grid;
373
+ gap: 0;
374
+ grid-column: 1 / -1;
375
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
376
+ margin: 0;
377
+ min-inline-size: 0;
378
+ }
379
+ .eth-motion-sync-progress__meta-item {
380
+ display: grid;
381
+ gap: var(--cds-spacing-02, .25rem);
382
+ min-inline-size: 0;
383
+ padding: var(--cds-spacing-04, .75rem) var(--cds-spacing-05, 1rem);
384
+ }
385
+ .eth-motion-sync-progress__meta-item + .eth-motion-sync-progress__meta-item {
386
+ border-inline-start: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
387
+ }
388
+ .eth-motion-sync-progress__meta dd {
389
+ color: var(--cds-text-primary, #161616);
390
+ font-size: var(--cds-body-compact-01-font-size, .875rem);
391
+ font-weight: 600;
392
+ line-height: var(--cds-body-compact-01-line-height, 1.2857);
393
+ margin: 0;
394
+ min-inline-size: 0;
395
+ overflow-wrap: anywhere;
396
+ }
397
+ .eth-motion-sync-progress.eth-motion-reduced .eth-motion-sync-progress__bar--indeterminate {
398
+ animation: none;
399
+ inline-size: 38%;
400
+ }
401
+ .eth-motion-skeleton-loading {
402
+ color: var(--cds-text-primary, #161616);
403
+ display: block;
404
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
405
+ inline-size: 100%;
406
+ max-inline-size: 100%;
407
+ min-inline-size: 0;
408
+ }
409
+ .eth-motion-skeleton-loading__surface {
410
+ background: var(--cds-layer-01, #f4f4f4);
411
+ border: 1px solid var(--cds-border-subtle, #e0e0e0);
412
+ display: grid;
413
+ inline-size: 100%;
414
+ min-inline-size: 0;
415
+ overflow: hidden;
416
+ }
417
+ .eth-motion-skeleton-loading__block {
418
+ background: var(--cds-skeleton-background, #e0e0e0);
419
+ block-size: .875rem;
420
+ display: block;
421
+ inline-size: 100%;
422
+ min-inline-size: .75rem;
423
+ }
424
+ .eth-motion-skeleton-loading:not(.eth-motion-reduced) .eth-motion-skeleton-loading__block {
425
+ animation: eth-motion-shimmer 1.2s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) infinite;
426
+ background-image: linear-gradient(
427
+ 90deg,
428
+ var(--cds-skeleton-background, #e0e0e0) 0%,
429
+ var(--cds-skeleton-element, #f4f4f4) 46%,
430
+ var(--cds-skeleton-background, #e0e0e0) 82%
431
+ );
432
+ background-size: 200% 100%;
433
+ }
434
+ .eth-motion-skeleton-loading__surface--article {
435
+ gap: var(--cds-spacing-05, 1rem);
436
+ padding: var(--cds-spacing-05, 1rem);
437
+ }
438
+ .eth-motion-skeleton-loading__article-header,
439
+ .eth-motion-skeleton-loading__article-body,
440
+ .eth-motion-skeleton-loading__card-body,
441
+ .eth-motion-skeleton-loading__list-copy {
442
+ display: grid;
443
+ gap: var(--cds-spacing-03, .5rem);
444
+ min-inline-size: 0;
445
+ }
446
+ .eth-motion-skeleton-loading__article-body,
447
+ .eth-motion-skeleton-loading__card-body {
448
+ gap: var(--cds-spacing-04, .75rem);
449
+ }
450
+ .eth-motion-skeleton-loading__block--eyebrow {
451
+ block-size: .5rem;
452
+ }
453
+ .eth-motion-skeleton-loading__block--title {
454
+ block-size: 1.125rem;
455
+ }
456
+ .eth-motion-skeleton-loading__block--caption,
457
+ .eth-motion-skeleton-loading__block--column {
458
+ block-size: .625rem;
459
+ }
460
+ .eth-motion-skeleton-loading__surface--card {
461
+ background: var(--cds-layer-01, #ffffff);
462
+ }
463
+ .eth-motion-skeleton-loading__block--media {
464
+ block-size: 6rem;
465
+ inline-size: 100%;
466
+ }
467
+ .eth-motion-skeleton-loading__card-body {
468
+ padding: var(--cds-spacing-05, 1rem);
469
+ }
470
+ .eth-motion-skeleton-loading__card-metrics {
471
+ border-block-start: 1px solid var(--cds-border-subtle, #e0e0e0);
472
+ display: grid;
473
+ gap: var(--cds-spacing-05, 1rem);
474
+ grid-template-columns: repeat(3, minmax(0, 1fr));
475
+ padding: var(--cds-spacing-04, .75rem) var(--cds-spacing-05, 1rem);
476
+ }
477
+ .eth-motion-skeleton-loading__block--metric {
478
+ block-size: 1.5rem;
479
+ }
480
+ .eth-motion-skeleton-loading__list-row {
481
+ align-items: center;
482
+ display: grid;
483
+ gap: var(--cds-spacing-04, .75rem);
484
+ grid-template-columns: 2rem minmax(0, 1fr) minmax(3rem, 5rem);
485
+ min-block-size: 4rem;
486
+ padding: var(--cds-spacing-04, .75rem);
487
+ }
488
+ .eth-motion-skeleton-loading__list-row + .eth-motion-skeleton-loading__list-row {
489
+ border-block-start: 1px solid var(--cds-border-subtle, #e0e0e0);
490
+ }
491
+ .eth-motion-skeleton-loading__block--avatar {
492
+ block-size: 2rem;
493
+ inline-size: 2rem;
494
+ }
495
+ .eth-motion-skeleton-loading__block--tag {
496
+ block-size: 1.25rem;
497
+ justify-self: end;
498
+ }
499
+ .eth-motion-skeleton-loading__surface--table {
500
+ background: var(--cds-layer-01, #ffffff);
501
+ }
502
+ .eth-motion-skeleton-loading__table-header,
503
+ .eth-motion-skeleton-loading__table-row {
504
+ display: grid;
505
+ gap: var(--cds-spacing-05, 1rem);
506
+ min-inline-size: 0;
507
+ }
508
+ .eth-motion-skeleton-loading__table-header {
509
+ align-items: center;
510
+ background: var(--cds-layer-02, #f4f4f4);
511
+ min-block-size: 2.5rem;
512
+ padding: var(--cds-spacing-04, .75rem);
513
+ }
514
+ .eth-motion-skeleton-loading__table-row {
515
+ align-items: center;
516
+ border-block-start: 1px solid var(--cds-border-subtle, #e0e0e0);
517
+ min-block-size: 3rem;
518
+ padding: var(--cds-spacing-04, .75rem);
519
+ }
520
+ .eth-motion-skeleton-loading__block--cell {
521
+ block-size: .75rem;
522
+ }
523
+ @media (max-width: 42rem) {
524
+ .eth-motion-sync-progress__header {
525
+ align-items: stretch;
526
+ flex-direction: column;
527
+ }
528
+ .eth-motion-sync-progress__chip {
529
+ align-self: start;
530
+ }
531
+ .eth-motion-sync-progress__meta {
532
+ grid-template-columns: minmax(0, 1fr);
533
+ }
534
+ .eth-motion-sync-progress__meta-item + .eth-motion-sync-progress__meta-item {
535
+ border-block-start: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
536
+ border-inline-start: 0;
537
+ }
538
+ .eth-motion-skeleton-loading__list-row {
539
+ grid-template-columns: 2rem minmax(0, 1fr);
540
+ }
541
+ .eth-motion-skeleton-loading__block--tag {
542
+ display: none;
543
+ }
544
+ .eth-motion-skeleton-loading__table-header,
545
+ .eth-motion-skeleton-loading__table-row {
546
+ gap: var(--cds-spacing-04, .75rem);
547
+ }
548
+ }
549
+ .eth-motion-status-change {
550
+ --eth-motion-status-color: var(--cds-interactive, #0f62fe);
551
+ --eth-motion-status-previous-color: var(--cds-text-secondary, #525252);
552
+
553
+ align-items: center;
554
+ background: var(--cds-layer-01, #ffffff);
555
+ border: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
556
+ border-inline-start: 3px solid var(--eth-motion-status-color);
557
+ border-radius: var(--cds-radius-sm, 2px);
558
+ box-sizing: border-box;
559
+ color: var(--cds-text-primary, #161616);
560
+ display: inline-flex;
561
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
562
+ gap: var(--cds-spacing-04, .75rem);
563
+ inline-size: fit-content;
564
+ line-height: 1.2857;
565
+ max-inline-size: 100%;
566
+ min-block-size: 2.5rem;
567
+ padding: .5rem .75rem .5rem .625rem;
568
+ vertical-align: middle;
569
+ }
570
+ .eth-motion-status-change--changed {
571
+ animation: eth-motion-pulse 650ms var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) 1;
572
+ background: var(--cds-layer-02, #f4f4f4);
573
+ }
574
+ .eth-motion-status-change__indicator {
575
+ align-items: center;
576
+ display: inline-flex;
577
+ flex: 0 0 auto;
578
+ inline-size: 2.5rem;
579
+ }
580
+ .eth-motion-status-change__previous-dot,
581
+ .eth-motion-status-change__current-dot {
582
+ border-radius: 50%;
583
+ box-sizing: border-box;
584
+ display: inline-block;
585
+ flex: 0 0 auto;
586
+ }
587
+ .eth-motion-status-change__previous-dot {
588
+ background: var(--eth-motion-status-previous-color);
589
+ block-size: .5rem;
590
+ inline-size: .5rem;
591
+ opacity: .64;
592
+ }
593
+ .eth-motion-status-change__connector {
594
+ background: var(--cds-border-strong-01, #8d8d8d);
595
+ block-size: 1px;
596
+ flex: 1 1 auto;
597
+ margin-inline: .25rem;
598
+ min-inline-size: .625rem;
599
+ }
600
+ .eth-motion-status-change__current-dot {
601
+ background: var(--eth-motion-status-color);
602
+ block-size: .625rem;
603
+ box-shadow: 0 0 0 2px var(--cds-layer-01, #ffffff);
604
+ inline-size: .625rem;
605
+ position: relative;
606
+ }
607
+ .eth-motion-status-change--changed .eth-motion-status-change__current-dot::after {
608
+ animation: eth-motion-status-halo 700ms var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) 1;
609
+ border: 1px solid var(--eth-motion-status-color);
610
+ border-radius: 50%;
611
+ content: "";
612
+ inset: -.375rem;
613
+ opacity: 0;
614
+ position: absolute;
615
+ }
616
+ .eth-motion-status-change__content {
617
+ display: grid;
618
+ gap: var(--cds-spacing-01, .125rem);
619
+ min-inline-size: 0;
620
+ }
621
+ .eth-motion-status-change__label {
622
+ color: var(--cds-text-primary, #161616);
623
+ font-size: var(--cds-body-compact-01-font-size, .875rem);
624
+ font-weight: 600;
625
+ line-height: var(--cds-body-compact-01-line-height, 1.2857);
626
+ min-inline-size: 0;
627
+ overflow-wrap: anywhere;
628
+ }
629
+ .eth-motion-status-change__state {
630
+ align-items: center;
631
+ color: var(--cds-text-secondary, #525252);
632
+ display: flex;
633
+ flex-wrap: wrap;
634
+ font-size: var(--cds-label-01-font-size, .75rem);
635
+ gap: var(--cds-spacing-02, .25rem);
636
+ line-height: var(--cds-label-01-line-height, 1.3333);
637
+ min-inline-size: 0;
638
+ }
639
+ .eth-motion-status-change__previous,
640
+ .eth-motion-status-change__current {
641
+ overflow-wrap: anywhere;
642
+ }
643
+ .eth-motion-status-change__current {
644
+ color: var(--cds-text-primary, #161616);
645
+ font-weight: 600;
646
+ }
647
+ .eth-motion-status-change__separator {
648
+ color: var(--cds-text-secondary, #525252);
649
+ }
650
+ .eth-motion-attention-pulse {
651
+ --eth-motion-attention-color: var(--cds-support-warning, #f1c21b);
652
+ --eth-motion-attention-border: #8a6a00;
653
+ --eth-motion-attention-ink: var(--cds-text-primary, #161616);
654
+ --eth-motion-attention-repeat: infinite;
655
+
656
+ align-items: center;
657
+ background: var(--cds-layer-02, #ffffff);
658
+ border: 1px solid var(--cds-border-subtle, #e0e0e0);
659
+ border-radius: var(--cds-radius-sm, 2px);
660
+ box-shadow: inset 3px 0 0 var(--eth-motion-attention-color);
661
+ color: var(--cds-text-primary, #161616);
662
+ display: inline-flex;
663
+ font-size: .875rem;
664
+ font-weight: 500;
665
+ gap: var(--cds-spacing-03, .5rem);
666
+ inline-size: fit-content;
667
+ line-height: 1.2857;
668
+ max-inline-size: 100%;
669
+ min-block-size: 2rem;
670
+ padding: .375rem .75rem .375rem .625rem;
671
+ vertical-align: middle;
672
+ }
673
+ .eth-motion-attention-pulse__marker {
674
+ align-items: center;
675
+ block-size: 1rem;
676
+ display: inline-flex;
677
+ flex: 0 0 auto;
678
+ inline-size: 1rem;
679
+ justify-content: center;
680
+ position: relative;
681
+ }
682
+ .eth-motion-attention-pulse__marker::before {
683
+ animation: eth-motion-attention-ring 1.4s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) var(--eth-motion-attention-repeat);
684
+ border: 1px solid var(--eth-motion-attention-color);
685
+ border-radius: 50%;
686
+ content: "";
687
+ inset: -.125rem;
688
+ position: absolute;
689
+ }
690
+ .eth-motion-attention-pulse__dot {
691
+ align-items: center;
692
+ background: var(--eth-motion-attention-color);
693
+ block-size: 1rem;
694
+ border: 1px solid var(--eth-motion-attention-border);
695
+ border-radius: 50%;
696
+ color: var(--eth-motion-attention-ink);
697
+ display: inline-flex;
698
+ font-size: .6875rem;
699
+ font-weight: 700;
700
+ inline-size: 1rem;
701
+ justify-content: center;
702
+ line-height: 1;
703
+ position: relative;
704
+ }
705
+ .eth-motion-attention-pulse__label {
706
+ min-inline-size: 0;
707
+ overflow-wrap: anywhere;
708
+ }
709
+ .eth-motion-attention-pulse.eth-motion-reduced .eth-motion-attention-pulse__marker::before {
710
+ animation: none;
711
+ opacity: .32;
712
+ transform: scale(1);
713
+ }
714
+ .eth-motion-document-lock {
715
+ --eth-motion-document-lock-accent: var(--cds-interactive, #0f62fe);
716
+
717
+ align-items: center;
718
+ background: var(--cds-layer-01, #ffffff);
719
+ border: 1px solid var(--cds-border-subtle-01, var(--cds-border-subtle, #e0e0e0));
720
+ border-inline-start: 3px solid var(--cds-border-strong-01, #8d8d8d);
721
+ box-sizing: border-box;
722
+ color: var(--cds-text-primary, #161616);
723
+ display: inline-flex;
724
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
725
+ gap: var(--cds-spacing-03, .5rem);
726
+ inline-size: fit-content;
727
+ line-height: 1.2857;
728
+ max-inline-size: 100%;
729
+ min-block-size: 2.5rem;
730
+ padding: .375rem .75rem .375rem .625rem;
731
+ vertical-align: middle;
732
+ }
733
+ .eth-motion-document-lock--active {
734
+ background: var(--cds-layer-02, #f4f4f4);
735
+ border-inline-start-color: var(--eth-motion-document-lock-accent);
736
+ }
737
+ .eth-motion-document-lock__indicator {
738
+ align-items: center;
739
+ block-size: 1.25rem;
740
+ color: var(--cds-icon-secondary, #525252);
741
+ display: inline-flex;
742
+ flex: 0 0 auto;
743
+ inline-size: 1.25rem;
744
+ justify-content: center;
745
+ position: relative;
746
+ }
747
+ .eth-motion-document-lock--active .eth-motion-document-lock__indicator {
748
+ color: var(--eth-motion-document-lock-accent);
749
+ }
750
+ .eth-motion-document-lock__pulse {
751
+ border: 1px solid currentColor;
752
+ border-radius: 50%;
753
+ inset: .125rem;
754
+ opacity: 0;
755
+ position: absolute;
756
+ }
757
+ .eth-motion-document-lock--active .eth-motion-document-lock__pulse {
758
+ animation: eth-motion-lock-pulse 1.45s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) infinite;
759
+ opacity: .34;
760
+ }
761
+ .eth-motion-document-lock__icon {
762
+ block-size: 1rem;
763
+ inline-size: 1rem;
764
+ position: relative;
765
+ }
766
+ .eth-motion-document-lock__content {
767
+ display: grid;
768
+ gap: 0;
769
+ min-inline-size: 0;
770
+ }
771
+ .eth-motion-document-lock__state {
772
+ color: var(--cds-text-secondary, #525252);
773
+ font-size: .75rem;
774
+ font-weight: 600;
775
+ line-height: 1.3333;
776
+ }
777
+ .eth-motion-document-lock--active .eth-motion-document-lock__state {
778
+ color: var(--cds-link-primary, #0f62fe);
779
+ }
780
+ .eth-motion-document-lock__owner {
781
+ color: var(--cds-text-primary, #161616);
782
+ font-size: .875rem;
783
+ font-weight: 600;
784
+ line-height: 1.2857;
785
+ overflow-wrap: anywhere;
786
+ }
787
+ .eth-motion-document-lock.eth-motion-reduced .eth-motion-document-lock__pulse {
788
+ animation: none;
789
+ opacity: .18;
790
+ transform: scale(1);
791
+ }
792
+ .eth-motion-step-completion {
793
+ align-items: start;
794
+ color: var(--cds-text-primary, #161616);
795
+ display: inline-grid;
796
+ font-family: var(--cds-font-family, "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
797
+ font-size: var(--cds-body-compact-01-font-size, .875rem);
798
+ gap: var(--cds-spacing-03, .5rem);
799
+ grid-template-columns: 1.5rem minmax(0, 1fr);
800
+ line-height: var(--cds-body-compact-01-line-height, 1.4286);
801
+ max-inline-size: 100%;
802
+ min-inline-size: 0;
803
+ vertical-align: middle;
804
+ }
805
+ .eth-motion-step-completion__marker {
806
+ align-items: center;
807
+ background: var(--cds-layer-01, #ffffff);
808
+ block-size: 1.5rem;
809
+ border: 1px solid var(--cds-border-strong-01, #8d8d8d);
810
+ border-radius: 50%;
811
+ color: var(--cds-icon-secondary, #525252);
812
+ display: inline-flex;
813
+ flex: 0 0 auto;
814
+ inline-size: 1.5rem;
815
+ justify-content: center;
816
+ margin-block-start: .0625rem;
817
+ }
818
+ .eth-motion-step-completion--completed .eth-motion-step-completion__marker {
819
+ animation: eth-motion-check 220ms var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) 1;
820
+ background: var(--cds-support-success, #24a148);
821
+ border-color: var(--cds-support-success, #24a148);
822
+ color: var(--cds-text-on-color, #ffffff);
823
+ }
824
+ .eth-motion-step-completion--current .eth-motion-step-completion__marker {
825
+ animation: eth-motion-step-current 1.25s var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) infinite;
826
+ background: var(--cds-highlight, #edf5ff);
827
+ border-color: var(--cds-interactive, #0f62fe);
828
+ color: var(--cds-interactive, #0f62fe);
829
+ }
830
+ .eth-motion-step-completion--pending .eth-motion-step-completion__marker {
831
+ background: var(--cds-layer-02, #f4f4f4);
832
+ border-color: var(--cds-border-strong-01, #8d8d8d);
833
+ color: var(--cds-icon-secondary, #525252);
834
+ }
835
+ .eth-motion-step-completion__check {
836
+ block-size: 1rem;
837
+ display: block;
838
+ inline-size: 1rem;
839
+ }
840
+ .eth-motion-step-completion__check-path {
841
+ animation: eth-motion-step-check 260ms var(--cds-productive-standard-easing, cubic-bezier(.2, 0, .38, .9)) 90ms both;
842
+ fill: none;
843
+ stroke: currentColor;
844
+ stroke-dasharray: 18;
845
+ stroke-dashoffset: 18;
846
+ stroke-linecap: round;
847
+ stroke-linejoin: round;
848
+ stroke-width: 2;
849
+ }
850
+ .eth-motion-step-completion__dot {
851
+ background: currentColor;
852
+ block-size: .4375rem;
853
+ border-radius: 50%;
854
+ display: block;
855
+ inline-size: .4375rem;
856
+ }
857
+ .eth-motion-step-completion--current .eth-motion-step-completion__dot {
858
+ block-size: .5rem;
859
+ inline-size: .5rem;
860
+ }
861
+ .eth-motion-step-completion__content {
862
+ display: grid;
863
+ gap: .125rem;
864
+ min-inline-size: 0;
865
+ }
866
+ .eth-motion-step-completion__label {
867
+ color: var(--cds-text-primary, #161616);
868
+ font-weight: 600;
869
+ overflow-wrap: anywhere;
870
+ }
871
+ .eth-motion-step-completion__state {
872
+ color: var(--cds-text-secondary, #525252);
873
+ font-size: var(--cds-label-01-font-size, .75rem);
874
+ font-weight: 600;
875
+ line-height: var(--cds-label-01-line-height, 1.3333);
876
+ }
877
+ .eth-motion-step-completion--completed .eth-motion-step-completion__state {
878
+ color: var(--cds-support-success-strong, #198038);
879
+ }
880
+ .eth-motion-step-completion--current .eth-motion-step-completion__state {
881
+ color: var(--cds-link-primary, #0f62fe);
882
+ }
883
+ .eth-motion-step-completion__description {
884
+ color: var(--cds-text-secondary, #525252);
885
+ font-size: var(--cds-label-01-font-size, .75rem);
886
+ line-height: var(--cds-label-01-line-height, 1.3333);
887
+ overflow-wrap: anywhere;
888
+ }
889
+ .eth-motion-step-completion.eth-motion-reduced .eth-motion-step-completion__check-path {
890
+ stroke-dashoffset: 0;
891
+ }
892
+ .eth-motion-reduced,
893
+ .eth-motion-reduced * { animation: none !important; transition: none !important; }
894
+ ` });
895
+ }
896
+ function statusColor(status) {
897
+ switch (status) {
898
+ case "succeeded":
899
+ case "completed":
900
+ case "synced":
901
+ return "#24a148";
902
+ case "failed":
903
+ case "blocked":
904
+ return "#da1e28";
905
+ case "warning":
906
+ case "pending-approval":
907
+ case "approval-required":
908
+ return "#f1c21b";
909
+ case "running":
910
+ case "in-progress":
911
+ case "active":
912
+ return "#0f62fe";
913
+ case "paused":
914
+ case "stale":
915
+ return "#8d8d8d";
916
+ default:
917
+ return "#6f6f6f";
918
+ }
919
+ }
920
+ function severityColor(severity) {
921
+ switch (severity) {
922
+ case "danger":
923
+ return "#da1e28";
924
+ case "warning":
925
+ return "#f1c21b";
926
+ case "success":
927
+ return "#24a148";
928
+ case "info":
929
+ return "#0f62fe";
930
+ default:
931
+ return "#6f6f6f";
932
+ }
933
+ }
934
+
935
+ // src/components/AgentThinkingAnimation.tsx
936
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
937
+ function AgentThinkingAnimation({
938
+ label = "Agent thinking",
939
+ className,
940
+ style,
941
+ ...props
942
+ }) {
943
+ const reduced = usePrefersReducedMotion();
944
+ const classes = [
945
+ "eth-motion-agent-thinking",
946
+ reduced ? "eth-motion-reduced" : void 0,
947
+ className
948
+ ].filter(Boolean).join(" ");
949
+ return /* @__PURE__ */ jsxs(
950
+ "span",
951
+ {
952
+ ...props,
953
+ className: classes,
954
+ role: "status",
955
+ "aria-label": label,
956
+ "data-eth-component": "AgentThinkingAnimation",
957
+ style: {
958
+ alignItems: "center",
959
+ color: "var(--cds-interactive, #0f62fe)",
960
+ display: "inline-flex",
961
+ justifyContent: "center",
962
+ lineHeight: 1,
963
+ minBlockSize: 16,
964
+ verticalAlign: "middle",
965
+ ...style
966
+ },
967
+ children: [
968
+ /* @__PURE__ */ jsx2(MotionStyles, {}),
969
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": true, style: { alignItems: "center", display: "inline-flex", gap: 4 }, children: [
970
+ /* @__PURE__ */ jsx2("span", { className: "eth-motion-dot", style: dotStyle(reduced, 0) }),
971
+ /* @__PURE__ */ jsx2("span", { className: "eth-motion-dot", style: dotStyle(reduced, 1) }),
972
+ /* @__PURE__ */ jsx2("span", { className: "eth-motion-dot", style: dotStyle(reduced, 2) })
973
+ ] })
974
+ ]
975
+ }
976
+ );
977
+ }
978
+ function dotStyle(reduced, index) {
979
+ return {
980
+ display: "inline-block",
981
+ width: 6,
982
+ height: 6,
983
+ borderRadius: "50%",
984
+ background: "currentColor",
985
+ flex: "0 0 auto",
986
+ animation: reduced ? void 0 : "eth-motion-dot 1s ease-in-out infinite",
987
+ animationDelay: reduced ? void 0 : `${index * 120}ms`
988
+ };
989
+ }
990
+
991
+ // src/components/AttentionPulse.tsx
992
+ import * as React2 from "react";
993
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
994
+ function AttentionPulse({
995
+ severity = "warning",
996
+ maxRepeats,
997
+ className,
998
+ children,
999
+ role = "status",
1000
+ style,
1001
+ "aria-label": ariaLabel,
1002
+ ...props
1003
+ }) {
1004
+ const reduced = usePrefersReducedMotion();
1005
+ const hasLabel = React2.Children.count(children) > 0;
1006
+ const classes = [
1007
+ "eth-motion-attention-pulse",
1008
+ `eth-motion-attention-pulse--${severity}`,
1009
+ reduced ? "eth-motion-reduced" : null,
1010
+ className
1011
+ ].filter(Boolean).join(" ");
1012
+ const attentionStyle = {
1013
+ "--eth-motion-attention-color": severityColor(severity),
1014
+ "--eth-motion-attention-border": severityBorderColor(severity),
1015
+ "--eth-motion-attention-ink": severityInkColor(severity),
1016
+ "--eth-motion-attention-repeat": String(maxRepeats ?? "infinite"),
1017
+ ...style
1018
+ };
1019
+ return /* @__PURE__ */ jsxs2(
1020
+ "span",
1021
+ {
1022
+ ...props,
1023
+ "aria-label": ariaLabel ?? (hasLabel ? void 0 : "Attention required"),
1024
+ className: classes,
1025
+ "data-eth-component": "AttentionPulse",
1026
+ role,
1027
+ style: attentionStyle,
1028
+ children: [
1029
+ /* @__PURE__ */ jsx3(MotionStyles, {}),
1030
+ /* @__PURE__ */ jsx3("span", { className: "eth-motion-attention-pulse__marker", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("span", { className: "eth-motion-attention-pulse__dot", children: "!" }) }),
1031
+ hasLabel ? /* @__PURE__ */ jsx3("span", { className: "eth-motion-attention-pulse__label", children }) : null
1032
+ ]
1033
+ }
1034
+ );
1035
+ }
1036
+ function severityBorderColor(severity) {
1037
+ return severity === "warning" ? "#8a6a00" : severityColor(severity);
1038
+ }
1039
+ function severityInkColor(severity) {
1040
+ return severity === "warning" ? "#161616" : "#ffffff";
1041
+ }
1042
+
1043
+ // src/components/DAGStatusTransition.tsx
1044
+ import * as React3 from "react";
1045
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1046
+ function DAGStatusTransition({
1047
+ nodes,
1048
+ previousNodes,
1049
+ edges = [],
1050
+ className,
1051
+ ...props
1052
+ }) {
1053
+ const reduced = usePrefersReducedMotion();
1054
+ const previousStatus = new Map(previousNodes?.map((node) => [node.id, node.status]) ?? []);
1055
+ const positions = layoutDag(nodes, edges);
1056
+ const frame = viewBoxFor(nodes, positions);
1057
+ const titleId = React3.useId();
1058
+ const descriptionId = React3.useId();
1059
+ const markerBaseId = React3.useId();
1060
+ const changedCount = nodes.filter(
1061
+ (node) => previousStatus.has(node.id) && previousStatus.get(node.id) !== node.status
1062
+ ).length;
1063
+ return /* @__PURE__ */ jsxs3(
1064
+ "section",
1065
+ {
1066
+ ...props,
1067
+ className: `eth-motion-dag-status ${reduced ? "eth-motion-reduced" : ""} ${className ?? ""}`,
1068
+ "data-eth-component": "DAGStatusTransition",
1069
+ "aria-label": "DAG status transition",
1070
+ children: [
1071
+ /* @__PURE__ */ jsx4(MotionStyles, {}),
1072
+ /* @__PURE__ */ jsxs3(
1073
+ "svg",
1074
+ {
1075
+ className: "eth-motion-dag-status__svg",
1076
+ viewBox: frame.viewBox,
1077
+ width: frame.width,
1078
+ height: frame.height,
1079
+ role: "img",
1080
+ "aria-labelledby": `${titleId} ${descriptionId}`,
1081
+ children: [
1082
+ /* @__PURE__ */ jsx4("title", { id: titleId, children: "DAG status transition" }),
1083
+ /* @__PURE__ */ jsx4("desc", { id: descriptionId, children: `${nodes.length} nodes, ${edges.length} edges, ${changedCount} changed node statuses.` }),
1084
+ /* @__PURE__ */ jsx4("defs", { children: edges.map((edge, index) => {
1085
+ const to = positions.get(edge.to);
1086
+ if (!to) return null;
1087
+ const color = edgeColor(edge, nodes.find((node) => node.id === edge.to)?.status);
1088
+ return /* @__PURE__ */ jsx4(
1089
+ "marker",
1090
+ {
1091
+ id: markerIdFromUseId(markerBaseId, index),
1092
+ markerWidth: "8",
1093
+ markerHeight: "8",
1094
+ refX: "7",
1095
+ refY: "3",
1096
+ orient: "auto",
1097
+ markerUnits: "strokeWidth",
1098
+ children: /* @__PURE__ */ jsx4("path", { d: "M0,0 L0,6 L7,3 z", fill: color })
1099
+ },
1100
+ `${edge.from}-${edge.to}-${index}`
1101
+ );
1102
+ }) }),
1103
+ edges.map((edge, index) => {
1104
+ const from = positions.get(edge.from);
1105
+ const to = positions.get(edge.to);
1106
+ if (!from || !to) return null;
1107
+ const color = edgeColor(edge, nodes.find((node) => node.id === edge.to)?.status);
1108
+ const active = edge.active && !reduced;
1109
+ return /* @__PURE__ */ jsx4(
1110
+ "path",
1111
+ {
1112
+ className: "eth-motion-dag-status__edge",
1113
+ d: edgePath(from, to),
1114
+ fill: "none",
1115
+ stroke: color,
1116
+ strokeWidth: active ? 2 : 1.25,
1117
+ strokeDasharray: active ? "6 6" : void 0,
1118
+ markerEnd: `url(#${markerIdFromUseId(markerBaseId, index)})`,
1119
+ style: active ? { animation: "eth-motion-flow-dash 900ms linear infinite" } : void 0
1120
+ },
1121
+ `${edge.from}-${edge.to}-${index}`
1122
+ );
1123
+ }),
1124
+ nodes.map((node) => {
1125
+ const point = positions.get(node.id);
1126
+ if (!point) return null;
1127
+ const previous = previousStatus.get(node.id);
1128
+ const changed = previous !== void 0 && previous !== node.status;
1129
+ const statusText = changed ? `${compactLabelForStatus(previous)} -> ${compactLabelForStatus(node.status)}` : labelForStatus(node.status);
1130
+ return /* @__PURE__ */ jsxs3(
1131
+ "g",
1132
+ {
1133
+ className: `eth-motion-dag-status__node ${changed ? "eth-motion-dag-status__node--changed" : ""}`,
1134
+ transform: `translate(${point.x}, ${point.y})`,
1135
+ children: [
1136
+ changed ? /* @__PURE__ */ jsx4(
1137
+ "rect",
1138
+ {
1139
+ className: "eth-motion-dag-status__change-ring",
1140
+ x: -NODE_WIDTH / 2 - 4,
1141
+ y: -NODE_HEIGHT / 2 - 4,
1142
+ width: NODE_WIDTH + 8,
1143
+ height: NODE_HEIGHT + 8,
1144
+ rx: 0,
1145
+ fill: "none",
1146
+ stroke: statusColor(node.status)
1147
+ }
1148
+ ) : null,
1149
+ /* @__PURE__ */ jsx4(
1150
+ "rect",
1151
+ {
1152
+ className: "eth-motion-dag-status__card",
1153
+ x: -NODE_WIDTH / 2,
1154
+ y: -NODE_HEIGHT / 2,
1155
+ width: NODE_WIDTH,
1156
+ height: NODE_HEIGHT,
1157
+ rx: 0,
1158
+ fill: "var(--cds-layer-02, #ffffff)",
1159
+ stroke: "var(--cds-border-subtle, #e0e0e0)"
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ jsx4(
1163
+ "rect",
1164
+ {
1165
+ x: -NODE_WIDTH / 2,
1166
+ y: -NODE_HEIGHT / 2,
1167
+ width: 4,
1168
+ height: NODE_HEIGHT,
1169
+ rx: 0,
1170
+ fill: statusColor(node.status)
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ jsx4("circle", { cx: -NODE_WIDTH / 2 + 20, cy: -9, r: 4.5, fill: statusColor(node.status) }),
1174
+ /* @__PURE__ */ jsx4("text", { className: "eth-motion-dag-status__label", x: -NODE_WIDTH / 2 + 34, y: -7, children: truncate(node.label, 18) }),
1175
+ /* @__PURE__ */ jsx4("text", { className: "eth-motion-dag-status__status", x: -NODE_WIDTH / 2 + 34, y: 15, children: truncate(statusText, 24) }),
1176
+ /* @__PURE__ */ jsx4("title", { children: changed && previous ? `${node.label}: ${labelForStatus(previous)} to ${labelForStatus(node.status)}` : `${node.label}: ${labelForStatus(node.status)}` })
1177
+ ]
1178
+ },
1179
+ node.id
1180
+ );
1181
+ })
1182
+ ]
1183
+ }
1184
+ ),
1185
+ /* @__PURE__ */ jsx4("ol", { className: "eth-motion-dag-status__fallback", children: nodes.map((node) => {
1186
+ const previous = previousStatus.get(node.id);
1187
+ const changed = previous !== void 0 && previous !== node.status;
1188
+ return /* @__PURE__ */ jsx4("li", { children: changed && previous ? `${node.label}: ${labelForStatus(previous)} to ${labelForStatus(node.status)}` : `${node.label}: ${labelForStatus(node.status)}` }, node.id);
1189
+ }) })
1190
+ ]
1191
+ }
1192
+ );
1193
+ }
1194
+ var NODE_WIDTH = 160;
1195
+ var NODE_HEIGHT = 64;
1196
+ var NODE_GAP_X = 190;
1197
+ var NODE_GAP_Y = 92;
1198
+ function layoutDag(nodes, edges = []) {
1199
+ const map = /* @__PURE__ */ new Map();
1200
+ const ids = new Set(nodes.map((node) => node.id));
1201
+ const ranks = new Map(nodes.map((node) => [node.id, 0]));
1202
+ const validEdges = edges.filter((edge) => ids.has(edge.from) && ids.has(edge.to));
1203
+ for (let pass = 0; pass < nodes.length; pass += 1) {
1204
+ for (const edge of validEdges) {
1205
+ const fromRank = ranks.get(edge.from) ?? 0;
1206
+ const toRank = ranks.get(edge.to) ?? 0;
1207
+ if (toRank < fromRank + 1) ranks.set(edge.to, fromRank + 1);
1208
+ }
1209
+ }
1210
+ const groups = /* @__PURE__ */ new Map();
1211
+ for (const node of nodes) {
1212
+ const rank = ranks.get(node.id) ?? 0;
1213
+ groups.set(rank, [...groups.get(rank) ?? [], node]);
1214
+ }
1215
+ const maxRows = Math.max(1, ...Array.from(groups.values()).map((group) => group.length));
1216
+ nodes.forEach((node, index) => {
1217
+ const rank = ranks.get(node.id) ?? index;
1218
+ const group = groups.get(rank) ?? [node];
1219
+ const rowIndex = Math.max(
1220
+ 0,
1221
+ group.findIndex((candidate) => candidate.id === node.id)
1222
+ );
1223
+ const row = group.length === 1 && maxRows > 1 ? (maxRows - 1) / 2 : rowIndex;
1224
+ map.set(node.id, {
1225
+ x: node.x ?? 88 + rank * NODE_GAP_X,
1226
+ y: node.y ?? 64 + row * NODE_GAP_Y
1227
+ });
1228
+ });
1229
+ return map;
1230
+ }
1231
+ function viewBoxFor(nodes, positions) {
1232
+ if (!nodes.length) {
1233
+ return { viewBox: "0 0 320 120", width: 320, height: 120 };
1234
+ }
1235
+ const points = nodes.map((node) => positions.get(node.id)).filter((point) => Boolean(point));
1236
+ const minX = Math.floor(Math.min(...points.map((point) => point.x)) - NODE_WIDTH / 2 - 24);
1237
+ const minY = Math.floor(Math.min(...points.map((point) => point.y)) - NODE_HEIGHT / 2 - 20);
1238
+ const maxX = Math.ceil(Math.max(...points.map((point) => point.x)) + NODE_WIDTH / 2 + 30);
1239
+ const maxY = Math.ceil(Math.max(...points.map((point) => point.y)) + NODE_HEIGHT / 2 + 20);
1240
+ const width = maxX - minX;
1241
+ const height = maxY - minY;
1242
+ return { viewBox: `${minX} ${minY} ${width} ${height}`, width, height };
1243
+ }
1244
+ function edgePath(from, to) {
1245
+ const horizontal = Math.abs(to.x - from.x) >= Math.abs(to.y - from.y);
1246
+ if (horizontal) {
1247
+ const direction2 = to.x >= from.x ? 1 : -1;
1248
+ const start2 = { x: from.x + NODE_WIDTH / 2 * direction2, y: from.y };
1249
+ const end2 = { x: to.x - NODE_WIDTH / 2 * direction2, y: to.y };
1250
+ const controlOffset2 = Math.max(32, Math.abs(end2.x - start2.x) * 0.5);
1251
+ return `M ${start2.x} ${start2.y} C ${start2.x + controlOffset2 * direction2} ${start2.y}, ${end2.x - controlOffset2 * direction2} ${end2.y}, ${end2.x} ${end2.y}`;
1252
+ }
1253
+ const direction = to.y >= from.y ? 1 : -1;
1254
+ const start = { x: from.x, y: from.y + NODE_HEIGHT / 2 * direction };
1255
+ const end = { x: to.x, y: to.y - NODE_HEIGHT / 2 * direction };
1256
+ const controlOffset = Math.max(28, Math.abs(end.y - start.y) * 0.5);
1257
+ return `M ${start.x} ${start.y} C ${start.x} ${start.y + controlOffset * direction}, ${end.x} ${end.y - controlOffset * direction}, ${end.x} ${end.y}`;
1258
+ }
1259
+ function edgeColor(edge, toStatus) {
1260
+ if (edge.status) return statusColor(edge.status);
1261
+ if (edge.active) return "var(--cds-interactive, #0f62fe)";
1262
+ if (toStatus === "failed" || toStatus === "blocked") return statusColor(toStatus);
1263
+ return "var(--cds-border-strong, #8d8d8d)";
1264
+ }
1265
+ function markerIdFromUseId(id, index) {
1266
+ return `eth-motion-dag-arrow-${id.replace(/[^a-zA-Z0-9_-]/g, "")}-${index}`;
1267
+ }
1268
+ function labelForStatus(status) {
1269
+ switch (status) {
1270
+ case "approval-required":
1271
+ return "Approval required";
1272
+ case "pending-approval":
1273
+ return "Pending approval";
1274
+ case "in-progress":
1275
+ return "In progress";
1276
+ case "not-started":
1277
+ return "Not started";
1278
+ default:
1279
+ return status.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
1280
+ }
1281
+ }
1282
+ function compactLabelForStatus(status) {
1283
+ switch (status) {
1284
+ case "approval-required":
1285
+ return "Approval";
1286
+ case "pending-approval":
1287
+ return "Pending";
1288
+ case "in-progress":
1289
+ return "Progress";
1290
+ case "not-started":
1291
+ return "Not started";
1292
+ default:
1293
+ return labelForStatus(status);
1294
+ }
1295
+ }
1296
+ function truncate(value, maxLength) {
1297
+ if (value.length <= maxLength) return value;
1298
+ return `${value.slice(0, Math.max(0, maxLength - 3))}...`;
1299
+ }
1300
+
1301
+ // src/components/DocumentLockPulse.tsx
1302
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1303
+ function DocumentLockPulse({
1304
+ lockedBy,
1305
+ active = true,
1306
+ className,
1307
+ children,
1308
+ role = "status",
1309
+ style,
1310
+ "aria-label": ariaLabel,
1311
+ ...props
1312
+ }) {
1313
+ const reduced = usePrefersReducedMotion();
1314
+ const owner = children ?? lockedBy ?? "Document locked";
1315
+ const ownerLabel = plainText(children ?? lockedBy);
1316
+ const classes = [
1317
+ "eth-motion-document-lock",
1318
+ active ? "eth-motion-document-lock--active" : "eth-motion-document-lock--idle",
1319
+ reduced ? "eth-motion-reduced" : void 0,
1320
+ className
1321
+ ].filter(Boolean).join(" ");
1322
+ const defaultLabel = ownerLabel ? `Document lock ${active ? "active" : "held"} by ${ownerLabel}` : active ? "Document lock active" : "Document locked";
1323
+ return /* @__PURE__ */ jsxs4(
1324
+ "span",
1325
+ {
1326
+ ...props,
1327
+ "aria-label": ariaLabel ?? defaultLabel,
1328
+ className: classes,
1329
+ "data-eth-component": "DocumentLockPulse",
1330
+ role,
1331
+ style,
1332
+ children: [
1333
+ /* @__PURE__ */ jsx5(MotionStyles, {}),
1334
+ /* @__PURE__ */ jsxs4("span", { className: "eth-motion-document-lock__indicator", "aria-hidden": "true", children: [
1335
+ /* @__PURE__ */ jsx5("span", { className: "eth-motion-document-lock__pulse" }),
1336
+ /* @__PURE__ */ jsx5(LockGlyph, { className: "eth-motion-document-lock__icon" })
1337
+ ] }),
1338
+ /* @__PURE__ */ jsxs4("span", { className: "eth-motion-document-lock__content", children: [
1339
+ /* @__PURE__ */ jsx5("span", { className: "eth-motion-document-lock__state", children: active ? "Active edit" : "Lock held" }),
1340
+ /* @__PURE__ */ jsx5("span", { className: "eth-motion-document-lock__owner", children: owner })
1341
+ ] })
1342
+ ]
1343
+ }
1344
+ );
1345
+ }
1346
+ function LockGlyph(props) {
1347
+ return /* @__PURE__ */ jsx5("svg", { ...props, "aria-hidden": "true", focusable: "false", viewBox: "0 0 16 16", children: /* @__PURE__ */ jsx5(
1348
+ "path",
1349
+ {
1350
+ d: "M5 7V5a3 3 0 0 1 6 0v2h1a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h1Zm1.5 0h3V5a1.5 1.5 0 0 0-3 0v2Z",
1351
+ fill: "currentColor"
1352
+ }
1353
+ ) });
1354
+ }
1355
+ function plainText(value) {
1356
+ if (typeof value === "string" || typeof value === "number") {
1357
+ return String(value);
1358
+ }
1359
+ return void 0;
1360
+ }
1361
+
1362
+ // src/components/PipelineFlowAnimation.tsx
1363
+ import * as React4 from "react";
1364
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1365
+ function PipelineFlowAnimation({
1366
+ nodes,
1367
+ edges,
1368
+ className,
1369
+ "aria-label": ariaLabel = "Pipeline flow",
1370
+ ...props
1371
+ }) {
1372
+ const reduced = usePrefersReducedMotion();
1373
+ const positions = layoutNodes(nodes);
1374
+ const frame = viewBoxFor2(nodes, positions);
1375
+ const titleId = React4.useId();
1376
+ const descriptionId = React4.useId();
1377
+ const markerBaseId = React4.useId();
1378
+ const activeNodeIds = new Set(
1379
+ edges.filter((edge) => edge.active).flatMap((edge) => [edge.from, edge.to]).concat(nodes.filter((node) => isActiveStatus(node.status)).map((node) => node.id))
1380
+ );
1381
+ return /* @__PURE__ */ jsxs5(
1382
+ "section",
1383
+ {
1384
+ ...props,
1385
+ className: `eth-motion-pipeline-flow ${reduced ? "eth-motion-reduced" : ""} ${className ?? ""}`,
1386
+ "data-eth-component": "PipelineFlowAnimation",
1387
+ "aria-label": ariaLabel,
1388
+ children: [
1389
+ /* @__PURE__ */ jsx6(MotionStyles, {}),
1390
+ /* @__PURE__ */ jsxs5(
1391
+ "svg",
1392
+ {
1393
+ className: "eth-motion-pipeline-flow__svg",
1394
+ viewBox: frame.viewBox,
1395
+ width: frame.width,
1396
+ height: frame.height,
1397
+ role: "img",
1398
+ "aria-labelledby": `${titleId} ${descriptionId}`,
1399
+ children: [
1400
+ /* @__PURE__ */ jsx6("title", { id: titleId, children: "Pipeline flow" }),
1401
+ /* @__PURE__ */ jsx6("desc", { id: descriptionId, children: nodes.length ? `${nodes.length} stages and ${edges.length} handoffs. ${nodes.map((node) => `${node.label}: ${labelForStatus2(node.status)}`).join(", ")}.` : "No pipeline stages." }),
1402
+ /* @__PURE__ */ jsx6("defs", { children: edges.map((edge, index) => {
1403
+ const toNode = nodes.find((node) => node.id === edge.to);
1404
+ const color = edgeColor2(edge, toNode?.status);
1405
+ return /* @__PURE__ */ jsx6(
1406
+ "marker",
1407
+ {
1408
+ id: markerIdFromUseId2(markerBaseId, index),
1409
+ markerWidth: "7",
1410
+ markerHeight: "7",
1411
+ refX: "6",
1412
+ refY: "3.5",
1413
+ orient: "auto",
1414
+ markerUnits: "strokeWidth",
1415
+ children: /* @__PURE__ */ jsx6("path", { d: "M0,0 L0,7 L7,3.5 z", fill: color })
1416
+ },
1417
+ `${edge.from}-${edge.to}-${index}`
1418
+ );
1419
+ }) }),
1420
+ edges.map((edge, index) => {
1421
+ const from = positions.get(edge.from);
1422
+ const to = positions.get(edge.to);
1423
+ if (!from || !to) return null;
1424
+ const toNode = nodes.find((node) => node.id === edge.to);
1425
+ const color = edgeColor2(edge, toNode?.status);
1426
+ const active = Boolean(edge.active);
1427
+ return /* @__PURE__ */ jsx6(
1428
+ "path",
1429
+ {
1430
+ className: "eth-motion-pipeline-flow__edge",
1431
+ d: edgePath2(from, to),
1432
+ fill: "none",
1433
+ markerEnd: `url(#${markerIdFromUseId2(markerBaseId, index)})`,
1434
+ stroke: color,
1435
+ strokeWidth: active ? 2.5 : 1.5,
1436
+ strokeDasharray: active ? "7 7" : void 0,
1437
+ style: active && !reduced ? { animation: "eth-motion-flow-dash 900ms linear infinite" } : void 0
1438
+ },
1439
+ `${edge.from}-${edge.to}-${index}`
1440
+ );
1441
+ }),
1442
+ !nodes.length ? /* @__PURE__ */ jsx6("text", { className: "eth-motion-pipeline-flow__empty", x: "16", y: "52", children: "No pipeline stages" }) : null,
1443
+ nodes.map((node) => {
1444
+ const point = positions.get(node.id);
1445
+ if (!point) return null;
1446
+ const active = activeNodeIds.has(node.id);
1447
+ const cardX = -NODE_WIDTH2 / 2;
1448
+ const cardY = -NODE_HEIGHT2 / 2;
1449
+ return /* @__PURE__ */ jsxs5(
1450
+ "g",
1451
+ {
1452
+ className: `eth-motion-pipeline-flow__node ${active ? "eth-motion-pipeline-flow__node--active" : ""}`,
1453
+ transform: `translate(${point.x}, ${point.y})`,
1454
+ children: [
1455
+ /* @__PURE__ */ jsx6(
1456
+ "rect",
1457
+ {
1458
+ className: "eth-motion-pipeline-flow__card",
1459
+ x: cardX,
1460
+ y: cardY,
1461
+ width: NODE_WIDTH2,
1462
+ height: NODE_HEIGHT2,
1463
+ rx: 0
1464
+ }
1465
+ ),
1466
+ /* @__PURE__ */ jsx6(
1467
+ "rect",
1468
+ {
1469
+ className: "eth-motion-pipeline-flow__stripe",
1470
+ x: cardX,
1471
+ y: cardY,
1472
+ width: 4,
1473
+ height: NODE_HEIGHT2,
1474
+ rx: 0,
1475
+ fill: statusColor(node.status)
1476
+ }
1477
+ ),
1478
+ active && !reduced ? /* @__PURE__ */ jsx6(
1479
+ "circle",
1480
+ {
1481
+ className: "eth-motion-pipeline-flow__pulse",
1482
+ cx: cardX + 22,
1483
+ cy: cardY + 20,
1484
+ r: 9,
1485
+ fill: "none",
1486
+ stroke: statusColor(node.status)
1487
+ }
1488
+ ) : null,
1489
+ /* @__PURE__ */ jsx6(
1490
+ "circle",
1491
+ {
1492
+ className: "eth-motion-pipeline-flow__dot",
1493
+ cx: cardX + 22,
1494
+ cy: cardY + 20,
1495
+ r: 5,
1496
+ fill: statusColor(node.status)
1497
+ }
1498
+ ),
1499
+ /* @__PURE__ */ jsx6("text", { className: "eth-motion-pipeline-flow__label", x: cardX + 36, y: cardY + 24, children: truncate2(node.label, 15) }),
1500
+ /* @__PURE__ */ jsx6("text", { className: "eth-motion-pipeline-flow__status", x: cardX + 14, y: cardY + 48, children: truncate2(labelForStatus2(node.status), 20) }),
1501
+ /* @__PURE__ */ jsx6("title", { children: `${node.label}: ${labelForStatus2(node.status)}` })
1502
+ ]
1503
+ },
1504
+ node.id
1505
+ );
1506
+ })
1507
+ ]
1508
+ }
1509
+ ),
1510
+ /* @__PURE__ */ jsx6("ol", { className: "eth-motion-pipeline-flow__fallback", children: nodes.map((node) => /* @__PURE__ */ jsxs5("li", { children: [
1511
+ node.label,
1512
+ ": ",
1513
+ labelForStatus2(node.status)
1514
+ ] }, node.id)) })
1515
+ ]
1516
+ }
1517
+ );
1518
+ }
1519
+ var NODE_WIDTH2 = 144;
1520
+ var NODE_HEIGHT2 = 64;
1521
+ var NODE_GAP_X2 = 188;
1522
+ function layoutNodes(nodes) {
1523
+ const map = /* @__PURE__ */ new Map();
1524
+ nodes.forEach((node, index) => {
1525
+ map.set(node.id, {
1526
+ x: node.x ?? NODE_WIDTH2 / 2 + 20 + NODE_GAP_X2 * index,
1527
+ y: node.y ?? NODE_HEIGHT2 / 2 + 18
1528
+ });
1529
+ });
1530
+ return map;
1531
+ }
1532
+ function viewBoxFor2(nodes, positions) {
1533
+ if (!nodes.length) {
1534
+ return { viewBox: "0 0 320 96", width: 320, height: 96 };
1535
+ }
1536
+ const points = nodes.map((node) => positions.get(node.id)).filter((point) => Boolean(point));
1537
+ const minX = Math.floor(Math.min(...points.map((point) => point.x)) - NODE_WIDTH2 / 2 - 18);
1538
+ const minY = Math.floor(Math.min(...points.map((point) => point.y)) - NODE_HEIGHT2 / 2 - 16);
1539
+ const maxX = Math.ceil(Math.max(...points.map((point) => point.x)) + NODE_WIDTH2 / 2 + 26);
1540
+ const maxY = Math.ceil(Math.max(...points.map((point) => point.y)) + NODE_HEIGHT2 / 2 + 16);
1541
+ const width = maxX - minX;
1542
+ const height = maxY - minY;
1543
+ return { viewBox: `${minX} ${minY} ${width} ${height}`, width, height };
1544
+ }
1545
+ function edgePath2(from, to) {
1546
+ const direction = to.x >= from.x ? 1 : -1;
1547
+ const start = { x: from.x + NODE_WIDTH2 / 2 * direction, y: from.y };
1548
+ const end = { x: to.x - NODE_WIDTH2 / 2 * direction, y: to.y };
1549
+ const controlOffset = Math.max(26, Math.abs(end.x - start.x) * 0.5);
1550
+ return `M ${start.x} ${start.y} C ${start.x + controlOffset * direction} ${start.y}, ${end.x - controlOffset * direction} ${end.y}, ${end.x} ${end.y}`;
1551
+ }
1552
+ function edgeColor2(edge, toStatus) {
1553
+ if (edge.status) return statusColor(edge.status);
1554
+ if (edge.active) return "var(--cds-interactive, #0f62fe)";
1555
+ if (toStatus === "failed" || toStatus === "blocked") return statusColor(toStatus);
1556
+ return "var(--cds-border-strong, #8d8d8d)";
1557
+ }
1558
+ function markerIdFromUseId2(id, index) {
1559
+ return `eth-motion-pipeline-arrow-${id.replace(/[^a-zA-Z0-9_-]/g, "")}-${index}`;
1560
+ }
1561
+ function isActiveStatus(status) {
1562
+ return status === "running" || status === "in-progress" || status === "active";
1563
+ }
1564
+ function labelForStatus2(status) {
1565
+ switch (status) {
1566
+ case "approval-required":
1567
+ return "Approval required";
1568
+ case "pending-approval":
1569
+ return "Pending approval";
1570
+ case "in-progress":
1571
+ return "In progress";
1572
+ case "not-started":
1573
+ return "Not started";
1574
+ default:
1575
+ return status.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
1576
+ }
1577
+ }
1578
+ function truncate2(value, maxLength) {
1579
+ if (value.length <= maxLength) return value;
1580
+ return `${value.slice(0, Math.max(0, maxLength - 3))}...`;
1581
+ }
1582
+
1583
+ // src/components/ProgressTransition.tsx
1584
+ import * as React5 from "react";
1585
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1586
+ function ProgressTransition({
1587
+ from,
1588
+ to,
1589
+ durationMs = 320,
1590
+ children,
1591
+ className,
1592
+ ...props
1593
+ }) {
1594
+ const reduced = usePrefersReducedMotion();
1595
+ const [current, setCurrent] = React5.useState(from ?? to);
1596
+ React5.useEffect(() => {
1597
+ if (reduced || durationMs <= 0 || typeof window === "undefined") {
1598
+ setCurrent(to);
1599
+ return;
1600
+ }
1601
+ const startValue = from ?? current;
1602
+ const delta = to - startValue;
1603
+ const startedAt = performance.now();
1604
+ let frame = 0;
1605
+ const tick = (now) => {
1606
+ const progress = Math.min(1, (now - startedAt) / durationMs);
1607
+ const eased = 1 - Math.pow(1 - progress, 3);
1608
+ setCurrent(startValue + delta * eased);
1609
+ if (progress < 1) frame = window.requestAnimationFrame(tick);
1610
+ };
1611
+ frame = window.requestAnimationFrame(tick);
1612
+ return () => window.cancelAnimationFrame(frame);
1613
+ }, [to, from, durationMs, reduced]);
1614
+ return /* @__PURE__ */ jsxs6(
1615
+ "section",
1616
+ {
1617
+ ...props,
1618
+ className: `eth-motion-progress-transition ${reduced ? "eth-motion-reduced" : ""} ${className ?? ""}`,
1619
+ "data-eth-component": "ProgressTransition",
1620
+ children: [
1621
+ /* @__PURE__ */ jsx7(MotionStyles, {}),
1622
+ children ? children(current) : /* @__PURE__ */ jsxs6("progress", { value: current, max: 100, "aria-label": "Progress", children: [
1623
+ Math.round(current),
1624
+ "%"
1625
+ ] })
1626
+ ]
1627
+ }
1628
+ );
1629
+ }
1630
+
1631
+ // src/components/SkeletonLoadingPattern.tsx
1632
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1633
+ var articleLineWidths = ["96%", "88%", "100%", "92%", "76%", "84%", "68%", "80%"];
1634
+ var listLineWidths = ["76%", "88%", "64%", "82%", "70%", "90%"];
1635
+ var tableCellWidths = ["72%", "92%", "58%", "84%", "66%", "78%"];
1636
+ function SkeletonLoadingPattern({
1637
+ columns = 4,
1638
+ label = "Loading content",
1639
+ lines = 3,
1640
+ rows,
1641
+ variant = "article",
1642
+ className,
1643
+ style,
1644
+ role = "status",
1645
+ "aria-busy": ariaBusy = true,
1646
+ "aria-label": ariaLabel,
1647
+ ...props
1648
+ }) {
1649
+ const reduced = usePrefersReducedMotion();
1650
+ const pattern = normalizePattern(variant);
1651
+ const lineCount = clampCount(lines, 1, 8);
1652
+ const rowCount = clampCount(rows ?? (pattern === "table" ? 4 : 3), 1, 8);
1653
+ const columnCount = clampCount(columns, 2, 6);
1654
+ const classes = [
1655
+ "eth-motion-skeleton-loading",
1656
+ `eth-motion-skeleton-loading--${pattern}`,
1657
+ reduced ? "eth-motion-reduced" : void 0,
1658
+ className
1659
+ ].filter(Boolean).join(" ");
1660
+ return /* @__PURE__ */ jsxs7(
1661
+ "section",
1662
+ {
1663
+ ...props,
1664
+ "aria-busy": ariaBusy,
1665
+ "aria-label": ariaLabel ?? label,
1666
+ className: classes,
1667
+ "data-eth-component": "SkeletonLoadingPattern",
1668
+ "data-pattern": pattern,
1669
+ role,
1670
+ style,
1671
+ children: [
1672
+ /* @__PURE__ */ jsx8(MotionStyles, {}),
1673
+ renderPattern(pattern, lineCount, rowCount, columnCount)
1674
+ ]
1675
+ }
1676
+ );
1677
+ }
1678
+ function renderPattern(pattern, lines, rows, columns) {
1679
+ switch (pattern) {
1680
+ case "card":
1681
+ return /* @__PURE__ */ jsx8(CardPattern, { lines });
1682
+ case "list":
1683
+ return /* @__PURE__ */ jsx8(ListPattern, { rows });
1684
+ case "table":
1685
+ return /* @__PURE__ */ jsx8(TablePattern, { columns, rows });
1686
+ case "article":
1687
+ default:
1688
+ return /* @__PURE__ */ jsx8(ArticlePattern, { lines });
1689
+ }
1690
+ }
1691
+ function ArticlePattern({ lines }) {
1692
+ return /* @__PURE__ */ jsxs7(
1693
+ "div",
1694
+ {
1695
+ "aria-hidden": "true",
1696
+ className: "eth-motion-skeleton-loading__surface eth-motion-skeleton-loading__surface--article",
1697
+ children: [
1698
+ /* @__PURE__ */ jsxs7("div", { className: "eth-motion-skeleton-loading__article-header", children: [
1699
+ /* @__PURE__ */ jsx8(
1700
+ SkeletonBlock,
1701
+ {
1702
+ className: "eth-motion-skeleton-loading__block--eyebrow",
1703
+ segment: "eyebrow",
1704
+ style: { inlineSize: "7rem" }
1705
+ }
1706
+ ),
1707
+ /* @__PURE__ */ jsx8(
1708
+ SkeletonBlock,
1709
+ {
1710
+ className: "eth-motion-skeleton-loading__block--title",
1711
+ segment: "title",
1712
+ style: { inlineSize: "min(22rem, 74%)" }
1713
+ }
1714
+ )
1715
+ ] }),
1716
+ /* @__PURE__ */ jsx8("div", { className: "eth-motion-skeleton-loading__article-body", children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ jsx8(
1717
+ SkeletonBlock,
1718
+ {
1719
+ className: "eth-motion-skeleton-loading__block--line",
1720
+ segment: "text-line",
1721
+ style: { inlineSize: articleLineWidths[index % articleLineWidths.length] }
1722
+ },
1723
+ index
1724
+ )) })
1725
+ ]
1726
+ }
1727
+ );
1728
+ }
1729
+ function CardPattern({ lines }) {
1730
+ return /* @__PURE__ */ jsxs7(
1731
+ "div",
1732
+ {
1733
+ "aria-hidden": "true",
1734
+ className: "eth-motion-skeleton-loading__surface eth-motion-skeleton-loading__surface--card",
1735
+ children: [
1736
+ /* @__PURE__ */ jsx8(SkeletonBlock, { className: "eth-motion-skeleton-loading__block--media", segment: "media" }),
1737
+ /* @__PURE__ */ jsxs7("div", { className: "eth-motion-skeleton-loading__card-body", children: [
1738
+ /* @__PURE__ */ jsx8(
1739
+ SkeletonBlock,
1740
+ {
1741
+ className: "eth-motion-skeleton-loading__block--title",
1742
+ segment: "title",
1743
+ style: { inlineSize: "78%" }
1744
+ }
1745
+ ),
1746
+ Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ jsx8(
1747
+ SkeletonBlock,
1748
+ {
1749
+ className: "eth-motion-skeleton-loading__block--line",
1750
+ segment: "text-line",
1751
+ style: { inlineSize: articleLineWidths[(index + 2) % articleLineWidths.length] }
1752
+ },
1753
+ index
1754
+ ))
1755
+ ] }),
1756
+ /* @__PURE__ */ jsx8("div", { className: "eth-motion-skeleton-loading__card-metrics", children: Array.from({ length: 3 }).map((_, index) => /* @__PURE__ */ jsx8(
1757
+ SkeletonBlock,
1758
+ {
1759
+ className: "eth-motion-skeleton-loading__block--metric",
1760
+ segment: "metric"
1761
+ },
1762
+ index
1763
+ )) })
1764
+ ]
1765
+ }
1766
+ );
1767
+ }
1768
+ function ListPattern({ rows }) {
1769
+ return /* @__PURE__ */ jsx8(
1770
+ "div",
1771
+ {
1772
+ "aria-hidden": "true",
1773
+ className: "eth-motion-skeleton-loading__surface eth-motion-skeleton-loading__surface--list",
1774
+ children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ jsxs7(
1775
+ "div",
1776
+ {
1777
+ className: "eth-motion-skeleton-loading__list-row",
1778
+ "data-skeleton-row": "list",
1779
+ children: [
1780
+ /* @__PURE__ */ jsx8(SkeletonBlock, { className: "eth-motion-skeleton-loading__block--avatar", segment: "avatar" }),
1781
+ /* @__PURE__ */ jsxs7("div", { className: "eth-motion-skeleton-loading__list-copy", children: [
1782
+ /* @__PURE__ */ jsx8(
1783
+ SkeletonBlock,
1784
+ {
1785
+ className: "eth-motion-skeleton-loading__block--line",
1786
+ segment: "text-line",
1787
+ style: { inlineSize: listLineWidths[index % listLineWidths.length] }
1788
+ }
1789
+ ),
1790
+ /* @__PURE__ */ jsx8(
1791
+ SkeletonBlock,
1792
+ {
1793
+ className: "eth-motion-skeleton-loading__block--caption",
1794
+ segment: "caption",
1795
+ style: { inlineSize: listLineWidths[(index + 2) % listLineWidths.length] }
1796
+ }
1797
+ )
1798
+ ] }),
1799
+ /* @__PURE__ */ jsx8(
1800
+ SkeletonBlock,
1801
+ {
1802
+ className: "eth-motion-skeleton-loading__block--tag",
1803
+ segment: "tag",
1804
+ style: { inlineSize: index % 2 === 0 ? "4.25rem" : "3.25rem" }
1805
+ }
1806
+ )
1807
+ ]
1808
+ },
1809
+ index
1810
+ ))
1811
+ }
1812
+ );
1813
+ }
1814
+ function TablePattern({ columns, rows }) {
1815
+ const gridStyle = { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` };
1816
+ return /* @__PURE__ */ jsxs7(
1817
+ "div",
1818
+ {
1819
+ "aria-hidden": "true",
1820
+ className: "eth-motion-skeleton-loading__surface eth-motion-skeleton-loading__surface--table",
1821
+ children: [
1822
+ /* @__PURE__ */ jsx8("div", { className: "eth-motion-skeleton-loading__table-header", style: gridStyle, children: Array.from({ length: columns }).map((_, index) => /* @__PURE__ */ jsx8(
1823
+ SkeletonBlock,
1824
+ {
1825
+ className: "eth-motion-skeleton-loading__block--column",
1826
+ segment: "column-header",
1827
+ style: { inlineSize: tableCellWidths[(index + 1) % tableCellWidths.length] }
1828
+ },
1829
+ index
1830
+ )) }),
1831
+ Array.from({ length: rows }).map((_, rowIndex) => /* @__PURE__ */ jsx8(
1832
+ "div",
1833
+ {
1834
+ className: "eth-motion-skeleton-loading__table-row",
1835
+ "data-skeleton-row": "table",
1836
+ style: gridStyle,
1837
+ children: Array.from({ length: columns }).map((_2, columnIndex) => /* @__PURE__ */ jsx8(
1838
+ SkeletonBlock,
1839
+ {
1840
+ cell: true,
1841
+ className: "eth-motion-skeleton-loading__block--cell",
1842
+ segment: "cell",
1843
+ style: {
1844
+ inlineSize: tableCellWidths[(rowIndex + columnIndex) % tableCellWidths.length]
1845
+ }
1846
+ },
1847
+ columnIndex
1848
+ ))
1849
+ },
1850
+ rowIndex
1851
+ ))
1852
+ ]
1853
+ }
1854
+ );
1855
+ }
1856
+ function SkeletonBlock({
1857
+ cell,
1858
+ className,
1859
+ segment,
1860
+ style
1861
+ }) {
1862
+ return /* @__PURE__ */ jsx8(
1863
+ "span",
1864
+ {
1865
+ className: `eth-motion-skeleton-loading__block ${className ?? ""}`,
1866
+ "data-skeleton-cell": cell ? "" : void 0,
1867
+ "data-skeleton-segment": segment,
1868
+ style
1869
+ }
1870
+ );
1871
+ }
1872
+ function clampCount(value, min, max) {
1873
+ if (!Number.isFinite(value)) return min;
1874
+ return Math.min(max, Math.max(min, Math.floor(value)));
1875
+ }
1876
+ function normalizePattern(variant) {
1877
+ if (variant === "card" || variant === "list" || variant === "table") return variant;
1878
+ return "article";
1879
+ }
1880
+
1881
+ // src/components/StatusChangeAnimation.tsx
1882
+ import { Fragment, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1883
+ function StatusChangeAnimation({
1884
+ status,
1885
+ previousStatus,
1886
+ label,
1887
+ className,
1888
+ children,
1889
+ style,
1890
+ "aria-label": ariaLabel,
1891
+ "aria-labelledby": ariaLabelledBy,
1892
+ ...props
1893
+ }) {
1894
+ const reduced = usePrefersReducedMotion();
1895
+ const changed = previousStatus !== void 0 && previousStatus !== status;
1896
+ const statusLabel = formatStatusLabel(status);
1897
+ const previousStatusLabel = previousStatus ? formatStatusLabel(previousStatus) : void 0;
1898
+ const displayLabel = children ?? label ?? "Status";
1899
+ const announcementLabel = textFromNode(label) ?? textFromNode(children) ?? "Status";
1900
+ const announcement = changed && previousStatusLabel ? `${announcementLabel} changed from ${previousStatusLabel} to ${statusLabel}` : `${announcementLabel} is ${statusLabel}`;
1901
+ const rootClassName = [
1902
+ "eth-motion-status-change",
1903
+ `eth-motion-status-change--${status}`,
1904
+ changed ? "eth-motion-status-change--changed" : null,
1905
+ reduced ? "eth-motion-reduced" : null,
1906
+ className
1907
+ ].filter(Boolean).join(" ");
1908
+ return /* @__PURE__ */ jsxs8(
1909
+ "span",
1910
+ {
1911
+ ...props,
1912
+ "aria-atomic": "true",
1913
+ "aria-label": ariaLabel ?? (ariaLabelledBy ? void 0 : announcement),
1914
+ "aria-labelledby": ariaLabelledBy,
1915
+ className: rootClassName,
1916
+ "data-current-status": status,
1917
+ "data-eth-component": "StatusChangeAnimation",
1918
+ "data-previous-status": previousStatus,
1919
+ role: "status",
1920
+ style: {
1921
+ "--eth-motion-status-color": statusColor(status),
1922
+ "--eth-motion-status-previous-color": previousStatus ? statusColor(previousStatus) : statusColor(status),
1923
+ ...style
1924
+ },
1925
+ children: [
1926
+ /* @__PURE__ */ jsx9(MotionStyles, {}),
1927
+ /* @__PURE__ */ jsxs8("span", { className: "eth-motion-status-change__indicator", "aria-hidden": "true", children: [
1928
+ changed ? /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__previous-dot" }) : null,
1929
+ changed ? /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__connector" }) : null,
1930
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__current-dot" })
1931
+ ] }),
1932
+ /* @__PURE__ */ jsxs8("span", { className: "eth-motion-status-change__content", children: [
1933
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__label", children: displayLabel }),
1934
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__state", children: changed && previousStatusLabel ? /* @__PURE__ */ jsxs8(Fragment, { children: [
1935
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__previous", children: previousStatusLabel }),
1936
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__separator", children: "to" }),
1937
+ /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__current", children: statusLabel })
1938
+ ] }) : /* @__PURE__ */ jsx9("span", { className: "eth-motion-status-change__current", children: statusLabel }) })
1939
+ ] })
1940
+ ]
1941
+ }
1942
+ );
1943
+ }
1944
+ function formatStatusLabel(status) {
1945
+ return status.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
1946
+ }
1947
+ function textFromNode(node) {
1948
+ if (typeof node === "string" || typeof node === "number") return String(node);
1949
+ return void 0;
1950
+ }
1951
+
1952
+ // src/components/StepCompletionAnimation.tsx
1953
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1954
+ var stateLabels = {
1955
+ completed: "Completed",
1956
+ current: "In progress",
1957
+ pending: "Pending"
1958
+ };
1959
+ function StepCompletionAnimation({
1960
+ completed = true,
1961
+ description,
1962
+ label = "Step completed",
1963
+ state,
1964
+ className,
1965
+ children,
1966
+ role = "status",
1967
+ "aria-live": ariaLive = "polite",
1968
+ ...props
1969
+ }) {
1970
+ const reduced = usePrefersReducedMotion();
1971
+ const resolvedState = state ?? (completed ? "completed" : "pending");
1972
+ const stateLabel = stateLabels[resolvedState];
1973
+ const stepClassName = [
1974
+ "eth-motion-step-completion",
1975
+ `eth-motion-step-completion--${resolvedState}`,
1976
+ reduced ? "eth-motion-reduced" : void 0,
1977
+ className
1978
+ ].filter(Boolean).join(" ");
1979
+ return /* @__PURE__ */ jsxs9(
1980
+ "span",
1981
+ {
1982
+ ...props,
1983
+ "aria-live": ariaLive,
1984
+ className: stepClassName,
1985
+ "data-eth-component": "StepCompletionAnimation",
1986
+ "data-state": resolvedState,
1987
+ role,
1988
+ children: [
1989
+ /* @__PURE__ */ jsx10(MotionStyles, {}),
1990
+ /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__marker", "aria-hidden": "true", children: resolvedState === "completed" ? /* @__PURE__ */ jsx10(
1991
+ "svg",
1992
+ {
1993
+ className: "eth-motion-step-completion__check",
1994
+ focusable: "false",
1995
+ height: "16",
1996
+ viewBox: "0 0 16 16",
1997
+ width: "16",
1998
+ children: /* @__PURE__ */ jsx10(
1999
+ "path",
2000
+ {
2001
+ className: "eth-motion-step-completion__check-path",
2002
+ d: "M3.5 8.4 6.5 11.4 12.8 4.6"
2003
+ }
2004
+ )
2005
+ }
2006
+ ) : /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__dot" }) }),
2007
+ /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__content", children: children ?? /* @__PURE__ */ jsxs9(Fragment2, { children: [
2008
+ /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__label", children: label }),
2009
+ /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__state", children: stateLabel }),
2010
+ description ? /* @__PURE__ */ jsx10("span", { className: "eth-motion-step-completion__description", children: description }) : null
2011
+ ] }) })
2012
+ ]
2013
+ }
2014
+ );
2015
+ }
2016
+
2017
+ // src/components/StreamingText.tsx
2018
+ import * as React6 from "react";
2019
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2020
+ function StreamingText({
2021
+ text,
2022
+ intervalMs = 18,
2023
+ onDone,
2024
+ className,
2025
+ role = "status",
2026
+ "aria-busy": ariaBusy,
2027
+ "aria-live": ariaLive = "polite",
2028
+ ...props
2029
+ }) {
2030
+ const reduced = usePrefersReducedMotion();
2031
+ const shouldAnimate = !reduced && intervalMs > 0 && text.length > 0;
2032
+ const [visible, setVisible] = React6.useState(shouldAnimate ? "" : text);
2033
+ const [done, setDone] = React6.useState(!shouldAnimate);
2034
+ const onDoneRef = React6.useRef(onDone);
2035
+ React6.useEffect(() => {
2036
+ onDoneRef.current = onDone;
2037
+ }, [onDone]);
2038
+ React6.useEffect(() => {
2039
+ if (!shouldAnimate) {
2040
+ setVisible(text);
2041
+ setDone(true);
2042
+ onDoneRef.current?.();
2043
+ return;
2044
+ }
2045
+ setVisible("");
2046
+ setDone(false);
2047
+ let index = 0;
2048
+ const timer = window.setInterval(() => {
2049
+ index += 1;
2050
+ setVisible(text.slice(0, index));
2051
+ if (index >= text.length) {
2052
+ window.clearInterval(timer);
2053
+ setDone(true);
2054
+ onDoneRef.current?.();
2055
+ }
2056
+ }, intervalMs);
2057
+ return () => window.clearInterval(timer);
2058
+ }, [text, intervalMs, shouldAnimate]);
2059
+ const streaming = shouldAnimate && !done;
2060
+ const classes = [
2061
+ "eth-motion-streaming-text",
2062
+ streaming ? "eth-motion-streaming-text--streaming" : "eth-motion-streaming-text--complete",
2063
+ reduced ? "eth-motion-reduced" : void 0,
2064
+ className
2065
+ ].filter(Boolean).join(" ");
2066
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
2067
+ /* @__PURE__ */ jsx11(MotionStyles, {}),
2068
+ /* @__PURE__ */ jsxs10(
2069
+ "span",
2070
+ {
2071
+ ...props,
2072
+ "aria-busy": ariaBusy ?? streaming,
2073
+ "aria-live": ariaLive,
2074
+ className: classes,
2075
+ "data-eth-component": "StreamingText",
2076
+ "data-streaming": streaming ? "true" : "false",
2077
+ role,
2078
+ children: [
2079
+ /* @__PURE__ */ jsx11("span", { className: "eth-motion-streaming-text__content", children: visible }),
2080
+ /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: "eth-motion-streaming-text__cursor" })
2081
+ ]
2082
+ }
2083
+ )
2084
+ ] });
2085
+ }
2086
+
2087
+ // src/components/SyncProgressAnimation.tsx
2088
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2089
+ var statusContent = {
2090
+ syncing: {
2091
+ accent: "#0f62fe",
2092
+ description: "Replicating source updates and applying remote changes.",
2093
+ label: "Syncing",
2094
+ soft: "#edf5ff"
2095
+ },
2096
+ synced: {
2097
+ accent: "#24a148",
2098
+ description: "Replica is current with the source.",
2099
+ label: "Synced",
2100
+ soft: "#defbe6"
2101
+ },
2102
+ paused: {
2103
+ accent: "#8d8d8d",
2104
+ description: "Sync is paused. No changes are being applied.",
2105
+ label: "Paused",
2106
+ soft: "#f4f4f4"
2107
+ },
2108
+ stale: {
2109
+ accent: "#f1c21b",
2110
+ description: "Replica is outside the expected freshness window.",
2111
+ label: "Stale",
2112
+ soft: "#fcf4d6"
2113
+ },
2114
+ failed: {
2115
+ accent: "#da1e28",
2116
+ description: "Sync stopped before completion. Review the connection and retry.",
2117
+ label: "Failed",
2118
+ soft: "#fff1f1"
2119
+ },
2120
+ error: {
2121
+ accent: "#da1e28",
2122
+ description: "Sync stopped before completion. Review the connection and retry.",
2123
+ label: "Needs attention",
2124
+ soft: "#fff1f1"
2125
+ }
2126
+ };
2127
+ function SyncProgressAnimation({
2128
+ children,
2129
+ className,
2130
+ completed,
2131
+ description,
2132
+ label = "Syncing",
2133
+ progress,
2134
+ rate,
2135
+ remaining,
2136
+ role = "status",
2137
+ source,
2138
+ stage,
2139
+ status = "syncing",
2140
+ style,
2141
+ target,
2142
+ total,
2143
+ variant,
2144
+ "aria-busy": ariaBusy,
2145
+ "aria-label": ariaLabel,
2146
+ "aria-live": ariaLive,
2147
+ ...props
2148
+ }) {
2149
+ const reduced = usePrefersReducedMotion();
2150
+ const content = statusContent[status];
2151
+ const progressValue = normalizeProgress(progress ?? progressFromCounts(completed, total, status));
2152
+ const hasProgress = progressValue !== void 0;
2153
+ const progressText = hasProgress ? `${progressValue}% complete` : content.label;
2154
+ const countText = formatCountProgress(completed, total);
2155
+ const descriptionNode = description ?? children ?? content.description;
2156
+ const hasDetailedContent = [
2157
+ description,
2158
+ children,
2159
+ source,
2160
+ target,
2161
+ stage,
2162
+ rate,
2163
+ remaining,
2164
+ completed,
2165
+ total,
2166
+ progress
2167
+ ].some(hasValue);
2168
+ const resolvedVariant = variant ?? (hasDetailedContent ? "detailed" : "compact");
2169
+ const labelText = textFromNode2(label) ?? "Sync";
2170
+ const announcement = [content.label, hasProgress ? progressText : void 0, countText].filter(Boolean).join(", ");
2171
+ const classes = [
2172
+ "eth-motion-sync-progress",
2173
+ `eth-motion-sync-progress--${resolvedVariant}`,
2174
+ `eth-motion-sync-progress--${status}`,
2175
+ reduced ? "eth-motion-reduced" : void 0,
2176
+ className
2177
+ ].filter(Boolean).join(" ");
2178
+ const rootStyle = {
2179
+ "--eth-motion-sync-accent": content.accent,
2180
+ "--eth-motion-sync-accent-soft": content.soft,
2181
+ ...style
2182
+ };
2183
+ const busy = ariaBusy ?? (status === "syncing" ? true : void 0);
2184
+ if (resolvedVariant === "compact") {
2185
+ return /* @__PURE__ */ jsxs11(
2186
+ "span",
2187
+ {
2188
+ ...props,
2189
+ "aria-atomic": "true",
2190
+ "aria-busy": busy,
2191
+ "aria-label": ariaLabel ?? `${labelText} ${announcement}`,
2192
+ "aria-live": ariaLive ?? (status === "syncing" ? "polite" : void 0),
2193
+ className: classes,
2194
+ "data-eth-component": "SyncProgressAnimation",
2195
+ "data-sync-progress": progressValue,
2196
+ "data-sync-status": status,
2197
+ role,
2198
+ style: rootStyle,
2199
+ children: [
2200
+ /* @__PURE__ */ jsx12(MotionStyles, {}),
2201
+ /* @__PURE__ */ jsx12(SyncIndicator, { status }),
2202
+ /* @__PURE__ */ jsxs11("span", { className: "eth-motion-sync-progress__compact-copy", children: [
2203
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__label", children: label }),
2204
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__compact-state", children: hasProgress ? `${progressValue}%` : content.label })
2205
+ ] })
2206
+ ]
2207
+ }
2208
+ );
2209
+ }
2210
+ const metaItems = [
2211
+ { label: "Source", value: source },
2212
+ { label: "Target", value: target },
2213
+ { label: "Rate", value: rate },
2214
+ { label: "Remaining", value: remaining }
2215
+ ].filter((item) => hasValue(item.value));
2216
+ return /* @__PURE__ */ jsxs11(
2217
+ "section",
2218
+ {
2219
+ ...props,
2220
+ "aria-atomic": "true",
2221
+ "aria-busy": busy,
2222
+ "aria-label": ariaLabel ?? `${labelText} ${announcement}`,
2223
+ "aria-live": ariaLive ?? (status === "syncing" ? "polite" : void 0),
2224
+ className: classes,
2225
+ "data-eth-component": "SyncProgressAnimation",
2226
+ "data-sync-progress": progressValue,
2227
+ "data-sync-status": status,
2228
+ role,
2229
+ style: rootStyle,
2230
+ children: [
2231
+ /* @__PURE__ */ jsx12(MotionStyles, {}),
2232
+ /* @__PURE__ */ jsx12(SyncIndicator, { status }),
2233
+ /* @__PURE__ */ jsxs11("div", { className: "eth-motion-sync-progress__body", children: [
2234
+ /* @__PURE__ */ jsxs11("div", { className: "eth-motion-sync-progress__header", children: [
2235
+ /* @__PURE__ */ jsxs11("div", { className: "eth-motion-sync-progress__title-group", children: [
2236
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__eyebrow", children: stage ?? "Replication" }),
2237
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__label", children: label })
2238
+ ] }),
2239
+ /* @__PURE__ */ jsxs11("span", { className: "eth-motion-sync-progress__chip", children: [
2240
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__chip-dot", "aria-hidden": "true" }),
2241
+ content.label
2242
+ ] })
2243
+ ] }),
2244
+ /* @__PURE__ */ jsx12("p", { className: "eth-motion-sync-progress__description", children: descriptionNode }),
2245
+ /* @__PURE__ */ jsxs11("div", { className: "eth-motion-sync-progress__meter", children: [
2246
+ /* @__PURE__ */ jsx12(
2247
+ "div",
2248
+ {
2249
+ "aria-label": "Sync transfer progress",
2250
+ "aria-valuemax": 100,
2251
+ "aria-valuemin": 0,
2252
+ "aria-valuenow": progressValue,
2253
+ "aria-valuetext": hasProgress ? progressText : content.label,
2254
+ className: "eth-motion-sync-progress__track",
2255
+ role: "progressbar",
2256
+ children: /* @__PURE__ */ jsx12(
2257
+ "span",
2258
+ {
2259
+ className: `eth-motion-sync-progress__bar ${hasProgress ? "" : "eth-motion-sync-progress__bar--indeterminate"}`,
2260
+ style: hasProgress ? { inlineSize: `${progressValue}%` } : void 0
2261
+ }
2262
+ )
2263
+ }
2264
+ ),
2265
+ /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__progress-copy", children: [hasProgress ? `${progressValue}%` : content.label, countText].filter(Boolean).join(" | ") })
2266
+ ] })
2267
+ ] }),
2268
+ metaItems.length ? /* @__PURE__ */ jsx12("dl", { className: "eth-motion-sync-progress__meta", "aria-label": "Sync metadata", children: metaItems.map((item) => /* @__PURE__ */ jsxs11("div", { className: "eth-motion-sync-progress__meta-item", children: [
2269
+ /* @__PURE__ */ jsx12("dt", { children: item.label }),
2270
+ /* @__PURE__ */ jsx12("dd", { children: item.value })
2271
+ ] }, item.label)) }) : null
2272
+ ]
2273
+ }
2274
+ );
2275
+ }
2276
+ function SyncIndicator({ status }) {
2277
+ return /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__indicator", "aria-hidden": "true", children: status === "syncing" ? /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__spinner" }) : /* @__PURE__ */ jsx12("span", { className: "eth-motion-sync-progress__glyph" }) });
2278
+ }
2279
+ function progressFromCounts(completed, total, status) {
2280
+ if (status === "synced") return 100;
2281
+ if (!isFiniteNumber(completed) || !isFiniteNumber(total) || total <= 0) return void 0;
2282
+ return completed / total * 100;
2283
+ }
2284
+ function normalizeProgress(value) {
2285
+ if (!isFiniteNumber(value)) return void 0;
2286
+ return Math.min(100, Math.max(0, Math.round(value)));
2287
+ }
2288
+ function formatCountProgress(completed, total) {
2289
+ if (!isFiniteNumber(completed) || !isFiniteNumber(total) || total <= 0) return void 0;
2290
+ const safeCompleted = Math.max(0, Math.floor(completed));
2291
+ const safeTotal = Math.max(0, Math.floor(total));
2292
+ return `${safeCompleted} of ${safeTotal} items`;
2293
+ }
2294
+ function isFiniteNumber(value) {
2295
+ return typeof value === "number" && Number.isFinite(value);
2296
+ }
2297
+ function hasValue(value) {
2298
+ return value !== void 0 && value !== null && value !== false && value !== "";
2299
+ }
2300
+ function textFromNode2(node) {
2301
+ if (typeof node === "string" || typeof node === "number") return String(node);
2302
+ return void 0;
2303
+ }
2304
+
2305
+ // src/index.tsx
2306
+ var MotionComponentNames = [
2307
+ "ProgressTransition",
2308
+ "PipelineFlowAnimation",
2309
+ "DAGStatusTransition",
2310
+ "AgentThinkingAnimation",
2311
+ "SyncProgressAnimation",
2312
+ "SkeletonLoadingPattern",
2313
+ "AttentionPulse",
2314
+ "DocumentLockPulse",
2315
+ "StreamingText",
2316
+ "StepCompletionAnimation",
2317
+ "StatusChangeAnimation"
2318
+ ];
2319
+ export {
2320
+ AgentThinkingAnimation,
2321
+ AttentionPulse,
2322
+ DAGStatusTransition,
2323
+ DocumentLockPulse,
2324
+ MotionComponentNames,
2325
+ PipelineFlowAnimation,
2326
+ ProgressTransition,
2327
+ SkeletonLoadingPattern,
2328
+ StatusChangeAnimation,
2329
+ StepCompletionAnimation,
2330
+ StreamingText,
2331
+ SyncProgressAnimation
2332
+ };
2333
+ //# sourceMappingURL=index.js.map