transitions-refine 0.3.3 → 0.3.5
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.
- package/demo.html +773 -251
- package/package.json +1 -1
- package/server/inject.mjs +7 -3
- package/server/motion-tokens.mjs +4 -2
package/demo.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Timeline Inspector — Demo</title>
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
10
10
|
<style>
|
|
11
11
|
/* @inject-skip-start (demo-only global resets — excluded from the injected build) */
|
|
12
12
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
--text-swap-ease: ease-in-out;
|
|
77
77
|
--shimmer-ease: linear;
|
|
78
78
|
/* transitions.dev — panel reveal (per-phase distance/ease/scale) */
|
|
79
|
-
--panel-open-dur: 400ms; --panel-close-dur:
|
|
79
|
+
--panel-open-dur: 400ms; --panel-close-dur: 350ms;
|
|
80
80
|
/* refine panel close runs a touch longer than the main panel close */
|
|
81
81
|
--refine-close-dur: 350ms;
|
|
82
82
|
--panel-open-distance: 100px; --panel-close-distance: 10px;
|
|
@@ -213,12 +213,32 @@
|
|
|
213
213
|
width: 40px; height: 3px; border-radius: 2px; background: #dcdce2; pointer-events: none;
|
|
214
214
|
transition: background 0.15s;
|
|
215
215
|
}
|
|
216
|
-
.tl-resize-handle:hover::after, .tl-resize-handle.dragging::after { background: var(--c-
|
|
216
|
+
.tl-resize-handle:hover::after, .tl-resize-handle.dragging::after { background: var(--c-thumb); }
|
|
217
217
|
.tl-panel-body { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: row; gap: 0; pointer-events: auto;
|
|
218
|
-
background: #fff; border-radius: 12px;
|
|
218
|
+
background: #fff; border-radius: 12px;
|
|
219
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08), 0 2px 6px 0 rgba(0, 0, 0, 0.05), 0 4px 42px 0 rgba(0, 0, 0, 0.06);
|
|
220
|
+
overflow: hidden; }
|
|
219
221
|
.tl-panel-body > * { pointer-events: auto; }
|
|
220
222
|
/* timeline content column — full width; the refine panel overlays it without reflow */
|
|
221
223
|
.tl-panel-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 0; overflow: hidden; }
|
|
224
|
+
/* transient toast (Copy values → "Values copied"). enter 250ms Fast,
|
|
225
|
+
exit 350ms Medium (toast-close token), Smooth ease out both ways. */
|
|
226
|
+
.tl-toast-wrap { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
|
|
227
|
+
z-index: 2147483600; pointer-events: none; }
|
|
228
|
+
.tl-toast { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 14px 0 10px;
|
|
229
|
+
border-radius: 60px; background: #fff; color: var(--c-text-strong);
|
|
230
|
+
font: inherit; font-size: 13px; font-weight: 400; line-height: 14px; white-space: nowrap;
|
|
231
|
+
box-shadow: 0 0 0 1px rgba(0,0,0,0.08), 0 2px 6px 0 rgba(0,0,0,0.05), 0 4px 42px 0 rgba(0,0,0,0.06);
|
|
232
|
+
animation: tl-toast-in 250ms cubic-bezier(0.22,1,0.36,1) both; }
|
|
233
|
+
.tl-toast.is-closing { animation: tl-toast-out 350ms cubic-bezier(0.22,1,0.36,1) both; }
|
|
234
|
+
/* gray icon to match the Accept button's icon (#17181C) */
|
|
235
|
+
.tl-toast-ic { display: flex; flex: none; color: #17181C; }
|
|
236
|
+
@keyframes tl-toast-in { from { opacity: 0; transform: translateY(8px) scale(0.98); }
|
|
237
|
+
to { opacity: 1; transform: none; } }
|
|
238
|
+
@keyframes tl-toast-out { from { opacity: 1; transform: none; }
|
|
239
|
+
to { opacity: 0; transform: translateY(8px) scale(0.98); } }
|
|
240
|
+
@media (prefers-reduced-motion: reduce) {
|
|
241
|
+
.tl-toast, .tl-toast.is-closing { animation: none; } }
|
|
222
242
|
.tl-pill { position: fixed; bottom: 16px; right: 16px; z-index: 99999;
|
|
223
243
|
height: 40px; background: #fff; border-radius: 36px;
|
|
224
244
|
padding: 6px 10px 6px 16px;
|
|
@@ -242,7 +262,7 @@
|
|
|
242
262
|
.tl-header { flex: none; position: relative; z-index: 40; display: flex; align-items: center; gap: 8px; padding: 7.5px 14px;
|
|
243
263
|
border-bottom: 1px solid var(--c-line); }
|
|
244
264
|
.tl-header-label { font-size: 13px; font-weight: 500; line-height: 18px; color: var(--c-text-mut2); white-space: nowrap; }
|
|
245
|
-
.tl-header-count { margin-left: auto; font-size: 13px; line-height: 18px; color: var(--c-count); white-space: nowrap; }
|
|
265
|
+
.tl-header-count { margin-left: auto; display: inline-flex; align-items: center; font-size: 13px; line-height: 18px; color: var(--c-count); white-space: nowrap; }
|
|
246
266
|
|
|
247
267
|
/* ── buttons ── */
|
|
248
268
|
/* topbar buttons share a fully rounded 60px corner radius */
|
|
@@ -306,7 +326,6 @@
|
|
|
306
326
|
display: inline-flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px;
|
|
307
327
|
border: none; border-radius: 8px; background: transparent; color: var(--c-blue);
|
|
308
328
|
box-shadow: var(--drop-btn);
|
|
309
|
-
box-shadow: 0 1px 3px 0 color(display-p3 0.0157 0.1608 0.4588 / 0.08);
|
|
310
329
|
font: inherit; font-size: 13px; font-weight: 500; line-height: 14px;
|
|
311
330
|
cursor: pointer; white-space: nowrap; transition: color 0.12s ease; }
|
|
312
331
|
.tl-refine-btn::before { content: ""; position: absolute; inset: 0; border-radius: inherit;
|
|
@@ -315,7 +334,6 @@
|
|
|
315
334
|
transition: background 0.12s ease; z-index: 0; }
|
|
316
335
|
.tl-refine-btn::after { content: ""; position: absolute; inset: 0; border-radius: inherit;
|
|
317
336
|
box-shadow: var(--ring-blue);
|
|
318
|
-
box-shadow: inset 0 0 0 1px color(display-p3 0 0.3961 0.8157 / 0.10), inset 0 -1px 0 0 color(display-p3 0.0118 0.2588 0.5569 / 0.15);
|
|
319
337
|
pointer-events: none; z-index: 2; }
|
|
320
338
|
.tl-refine-btn > * { position: relative; z-index: 1; }
|
|
321
339
|
/* Figma: wand icon stroke is #0073E5, distinct from the #0071e2 label */
|
|
@@ -326,9 +344,9 @@
|
|
|
326
344
|
.tl-refine-btn:disabled { cursor: default; color: var(--c-blue); }
|
|
327
345
|
.tl-refine-btn:disabled::before { background: var(--c-blue-bg); background: color(display-p3 0 0.451 0.898 / 0.06); }
|
|
328
346
|
.tl-refine-btn:disabled span { color: var(--c-blue-pressed); opacity: 0.5; }
|
|
329
|
-
/* tiny
|
|
347
|
+
/* tiny 1.5px sparks flying out along each of the wand's spark rays on hover */
|
|
330
348
|
.tl-refine-sparks { position: absolute; left: 23px; top: 15px; width: 0; height: 0; z-index: 3; pointer-events: none; }
|
|
331
|
-
.tl-refine-sparks i { position: absolute; left: 0; top: 0; width:
|
|
349
|
+
.tl-refine-sparks i { position: absolute; left: 0; top: 0; width: 1.5px; height: 1.5px; border-radius: 50%;
|
|
332
350
|
background: var(--c-blue); opacity: 0; }
|
|
333
351
|
@keyframes tl-refine-spark {
|
|
334
352
|
0% { opacity: 0; transform: translate(var(--ox, 0), var(--oy, 0)) scale(0.4); }
|
|
@@ -344,9 +362,14 @@
|
|
|
344
362
|
/* ── body / floating cards row ── */
|
|
345
363
|
.tl-body { display: flex; flex: 1; min-height: 0; gap: 0; }
|
|
346
364
|
.tl-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
347
|
-
.tl-inspector { flex: 0 0 280px; padding:
|
|
365
|
+
.tl-inspector { flex: 0 0 280px; padding: 16px 16px; display: flex; flex-direction: column;
|
|
348
366
|
border-left: 1px solid var(--c-line); min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
|
|
349
|
-
|
|
367
|
+
/* title sits centered in the ~52px band between the Refine toolbar and the
|
|
368
|
+
Duration field: 16px above + 20px title + 16px below = 52px */
|
|
369
|
+
.tl-insp-title { display: flex; align-items: center; font-size: 13px; font-weight: 500; line-height: 18px; color: #171717; margin-bottom: 16px; text-transform: capitalize; }
|
|
370
|
+
/* full-bleed divider between the Delay field and the Easing/Springs tabs
|
|
371
|
+
(Figma 580:9049 — spans the inspector edge-to-edge past the 16px padding) */
|
|
372
|
+
.tl-inspector-divider { height: 1px; background: var(--c-line); margin: 0 -16px; }
|
|
350
373
|
.tl-insp-label { font-size: 12px; line-height: 18px; color: #737373; margin: 10px 0 6px; }
|
|
351
374
|
|
|
352
375
|
/* ── tracks / ruler ── */
|
|
@@ -411,18 +434,18 @@
|
|
|
411
434
|
.tl-col-resizer-line { width: 1px; height: 100%; background: transparent;
|
|
412
435
|
transition: background 0.12s ease, width 0.12s ease; }
|
|
413
436
|
.tl-col-resizer:hover .tl-col-resizer-line,
|
|
414
|
-
.tl-col-resizer.dragging .tl-col-resizer-line { width: 2px; background:
|
|
437
|
+
.tl-col-resizer.dragging .tl-col-resizer-line { width: 2px; background: var(--c-thumb); }
|
|
415
438
|
@media (prefers-reduced-motion: reduce) { .tl-col-resizer-line { transition: none; } }
|
|
416
439
|
|
|
417
440
|
/* ── value field (slider + input) — exact Figma "Value slider and input" ── */
|
|
418
|
-
.tl-field-wrap { display: flex; align-items: center; gap:
|
|
419
|
-
.tl-field { position: relative; flex: 1; min-width: 0; height:
|
|
441
|
+
.tl-field-wrap { display: flex; align-items: center; gap: 6px; margin-bottom: 12px; }
|
|
442
|
+
.tl-field { position: relative; flex: 1; min-width: 0; height: 32px; border-radius: 8px;
|
|
420
443
|
background: var(--c-field-bg); transition: box-shadow 0.12s ease; }
|
|
421
444
|
/* focused: dual inset ring (Figma Focused state) */
|
|
422
445
|
.tl-field.is-editing { box-shadow: inset 0 0 0 1px rgba(0,0,0,0.14), inset 0 0 0 0.5px rgba(255,255,255,0.06); }
|
|
423
446
|
/* inner slider fill — Figma button/tiny: soft drop shadow + inset ring give the
|
|
424
447
|
"raised block" look (shadow 0 1px 3px @4%, hairline border, bottom 1px). */
|
|
425
|
-
.tl-field-fill { position: absolute; left: 0; top: 0; bottom: 0; min-width:
|
|
448
|
+
.tl-field-fill { position: absolute; left: 0; top: 0; bottom: 0; min-width: 32px; border-radius: 8px;
|
|
426
449
|
background: var(--c-fill);
|
|
427
450
|
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.02), inset 0 -1px 0 0 rgba(0,0,0,0.08), inset 0 0 0 1px rgba(191,191,191,0.10), 0 1px 3px 0 rgba(0,0,0,0.06);
|
|
428
451
|
pointer-events: none; transition: background 0.12s ease, opacity 0.12s ease; z-index: 1; }
|
|
@@ -439,20 +462,20 @@
|
|
|
439
462
|
text-align: right; border: none; background: transparent; font: inherit; font-size: 13px; font-weight: 500;
|
|
440
463
|
color: var(--c-text); outline: none; z-index: 4; font-variant-numeric: tabular-nums; }
|
|
441
464
|
/* thumb at the right edge of the fill (Figma #767676 2×20px, inset 8px, hover 60% / drag 100%) */
|
|
442
|
-
.tl-field-thumb { position: absolute; right: 8px; top:
|
|
465
|
+
.tl-field-thumb { position: absolute; right: 8px; top: 7px; height: 18px; width: 2px;
|
|
443
466
|
background: var(--c-thumb); border-radius: 20px; opacity: 0; pointer-events: none;
|
|
444
467
|
transition: opacity 0.12s ease; z-index: 3; }
|
|
445
468
|
.tl-field:hover .tl-field-thumb { opacity: 0.6; }
|
|
446
469
|
.tl-field.is-dragging .tl-field-thumb { opacity: 1; }
|
|
447
470
|
.tl-field.is-editing .tl-field-thumb { opacity: 0; }
|
|
448
|
-
.tl-field-chevron { flex: none; width:
|
|
471
|
+
.tl-field-chevron { flex: none; width: 32px; height: 32px; border-radius: 8px; display: flex;
|
|
449
472
|
align-items: center; justify-content: center; border: none; background: var(--c-field-bg);
|
|
450
473
|
color: var(--c-ruler); cursor: pointer; transition: background 0.12s ease, scale 0.12s ease; }
|
|
451
474
|
.tl-field-chevron:hover { background: var(--c-sec-h); }
|
|
452
475
|
.tl-field-chevron:active { scale: 0.96; }
|
|
453
476
|
|
|
454
477
|
/* ── easing editor ── */
|
|
455
|
-
.tl-ease { display: flex; flex-direction: column; gap:
|
|
478
|
+
.tl-ease { display: flex; flex-direction: column; gap: 12px; }
|
|
456
479
|
|
|
457
480
|
/* tab content page-slide (transitions.dev · 08-page-side-by-side) */
|
|
458
481
|
.tl-ease-pages.t-page-slide {
|
|
@@ -472,6 +495,7 @@
|
|
|
472
495
|
.tl-ease-pages .t-page[data-page-id="2"] { --t-page-from-x: var(--page-slide-distance); }
|
|
473
496
|
.tl-ease-pages .t-page {
|
|
474
497
|
position: absolute; top: 0; left: 0; right: 0;
|
|
498
|
+
display: flex; flex-direction: column; gap: 12px;
|
|
475
499
|
opacity: 0; pointer-events: none;
|
|
476
500
|
transform: translateX(calc(var(--t-page-from-x, 0px) * var(--page-exit-enabled)));
|
|
477
501
|
filter: blur(calc(var(--page-blur) * var(--page-exit-enabled)));
|
|
@@ -492,7 +516,7 @@
|
|
|
492
516
|
}
|
|
493
517
|
/* easing select trigger (custom dropdown) */
|
|
494
518
|
/* select trigger — Logram design system (node 13064:2418): flat #f7f7f7 fill */
|
|
495
|
-
.tl-select { display: flex; align-items: center; justify-content: space-between; width: 100%; height:
|
|
519
|
+
.tl-select { display: flex; align-items: center; justify-content: space-between; width: 100%; height: 32px;
|
|
496
520
|
border: none; border-radius: 8px; background: #f7f7f7; padding: 6px 10px 6px 12px;
|
|
497
521
|
font: inherit; font-size: 13px; font-weight: 500; line-height: 16px; color: #1b1b1b; cursor: pointer; text-align: left;
|
|
498
522
|
transition: background 0.12s ease; }
|
|
@@ -501,55 +525,95 @@
|
|
|
501
525
|
.tl-select:disabled { opacity: 0.5; cursor: default; }
|
|
502
526
|
.tl-select-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
503
527
|
.tl-select-chev { display: flex; color: var(--c-ruler); flex: none; }
|
|
504
|
-
/* easing curve — exact Figma (node
|
|
505
|
-
.tl-curve { background: #f6f6f7; border-radius:
|
|
528
|
+
/* easing curve — exact Figma (node 580:11158): #f6f6f7, 8px radius, no grid */
|
|
529
|
+
.tl-curve { background: #f6f6f7; border-radius: 8px;
|
|
506
530
|
padding: 0; position: relative; overflow: hidden; }
|
|
507
531
|
.tl-curve svg { display: block; cursor: crosshair; width: 100%; height: auto; }
|
|
508
532
|
.tl-curve-handle { cursor: grab; }
|
|
509
533
|
.tl-curve-handle:active { cursor: grabbing; }
|
|
510
534
|
.tl-cubic-row { display: flex; gap: 4px; }
|
|
511
|
-
/* number field —
|
|
512
|
-
|
|
513
|
-
.tl-cubic-
|
|
514
|
-
box-sizing: border-box;
|
|
515
|
-
|
|
516
|
-
|
|
535
|
+
/* number field — Figma node 580:4814: value left-aligned (#1b1b1b),
|
|
536
|
+
32px #f7f7f7 pill, hover overlay + inset focus ring. */
|
|
537
|
+
.tl-cubic-cell { flex: 1; min-width: 0; display: flex; flex-direction: row; align-items: center;
|
|
538
|
+
gap: 4px; height: 32px; box-sizing: border-box;
|
|
539
|
+
padding: 0 8px; border-radius: 8px; background: #f7f7f7;
|
|
540
|
+
box-shadow: inset 0 0 0 0 rgba(0,0,0,0.14); transition: box-shadow 0.12s ease, background 0.12s ease; }
|
|
541
|
+
.tl-cubic-cell:hover { background: linear-gradient(rgba(170,170,170,0.2),rgba(170,170,170,0.2)), #f7f7f7; }
|
|
542
|
+
.tl-cubic-cell:focus-within { box-shadow: inset 0 0 0 1px rgba(0,0,0,0.14); }
|
|
543
|
+
.tl-cubic-row input { flex: 1; min-width: 0; width: 100%; height: 100%; border: none; background: transparent;
|
|
544
|
+
box-sizing: border-box; padding: 0; text-align: left;
|
|
545
|
+
font: inherit; font-size: 12px; font-weight: 500; line-height: 16px; color: #1b1b1b; outline: none;
|
|
546
|
+
-moz-appearance: textfield; appearance: textfield; }
|
|
547
|
+
.tl-cubic-row input::-webkit-outer-spin-button, .tl-cubic-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; width: 0; }
|
|
517
548
|
.tl-cubic-row input::placeholder { color: #979797; }
|
|
518
|
-
.tl-cubic-row input:hover { background: linear-gradient(rgba(170,170,170,0.2),rgba(170,170,170,0.2)), #f7f7f7; }
|
|
519
|
-
.tl-cubic-row input:focus { box-shadow: inset 0 0 0 1px rgba(0,0,0,0.14); }
|
|
520
549
|
.tl-custom-input { height: 30px; border: none; border-radius: 6px; background: var(--c-field-bg); padding: 0 8px;
|
|
521
550
|
font: inherit; font-size: 12px; font-family: monospace; color: var(--c-text); outline: none;
|
|
522
551
|
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.04); }
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
.tl-
|
|
526
|
-
.tl-
|
|
527
|
-
.tl-bounce-row label { font-size: 11px; color: #737373; width: 54px; flex: none; }
|
|
528
|
-
.tl-bounce-row input[type="range"] { flex: 1; height: 4px; -webkit-appearance: none; appearance: none;
|
|
529
|
-
background: #e3e3e8; border-radius: 2px; outline: none; }
|
|
530
|
-
.tl-bounce-row input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none;
|
|
531
|
-
width: 12px; height: 12px; border-radius: 50%; background: var(--c-blue); cursor: pointer; }
|
|
532
|
-
.tl-bounce-val { font-size: 11px; color: var(--c-blue); width: 34px; text-align: right; }
|
|
533
|
-
.tl-bounce select { height: 26px; border: none; border-radius: 6px; background: var(--c-field-bg);
|
|
534
|
-
font: inherit; font-size: 11px; color: var(--c-text); outline: none; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.04); flex: 1; }
|
|
552
|
+
/* Spring physics controls — exact Figma "Value slider and input" scrubbers
|
|
553
|
+
(same .tl-field component as Duration/Delay; no chevron stepper). */
|
|
554
|
+
.tl-spring-fields { display: flex; flex-direction: column; gap: 10px; }
|
|
555
|
+
.tl-spring-field { margin-bottom: 0; }
|
|
535
556
|
|
|
536
557
|
/* ── Easing / Springs tabs — Logram design system (node 13064:2552): trackless toggle pills ── */
|
|
537
|
-
.tl-seg { display: flex; align-items: center; gap: 4px; margin-top:
|
|
558
|
+
.tl-seg { display: flex; align-items: center; gap: 4px; margin-top: 0; }
|
|
538
559
|
.tl-seg-btn { height: 32px; padding: 6px 12px; border: none; border-radius: 8px; background: transparent;
|
|
539
560
|
font: inherit; font-size: 13px; font-weight: 500; line-height: 14px; color: #676767; cursor: pointer;
|
|
540
561
|
transition: background 0.14s ease, color 0.14s ease; }
|
|
541
562
|
.tl-seg-btn:hover:not(.is-active) { background: rgba(170,170,170,0.06); color: #17181c; }
|
|
542
563
|
.tl-seg-btn.is-active { background: rgba(170,170,170,0.1); color: #17181c; }
|
|
543
564
|
|
|
565
|
+
/* position preview (animated marker à la easing.dev) — collapsible accordion
|
|
566
|
+
(transitions.dev · 21-accordion: grid-rows 0fr↔1fr + vertical chevron flip) */
|
|
567
|
+
.tl-preview { margin-top: 0; background: transparent;
|
|
568
|
+
--acc-dur: 260ms; --acc-ease: cubic-bezier(0.22, 1, 0.36, 1); }
|
|
569
|
+
.tl-preview-head { display: flex; align-items: center; justify-content: flex-start; gap: 6px; width: 100%;
|
|
570
|
+
padding: 0 0 9px; border: none; background: transparent; cursor: pointer; font: inherit;
|
|
571
|
+
transition: color 0.12s ease; }
|
|
572
|
+
.tl-preview-head:hover .tl-preview-title,
|
|
573
|
+
.tl-preview-head:hover .tl-preview-chevron { color: #000; }
|
|
574
|
+
.tl-preview-title { font-size: 13px; font-weight: 500; color: #171717; letter-spacing: 0; line-height: 18px; }
|
|
575
|
+
.tl-preview-chevron { display: inline-flex; color: #171717;
|
|
576
|
+
transform: scaleY(1); transform-origin: center; transition: transform var(--acc-dur) var(--acc-ease); }
|
|
577
|
+
.tl-preview[data-open="true"] .tl-preview-chevron { transform: scaleY(-1); }
|
|
578
|
+
.tl-preview-panel { display: grid; grid-template-rows: 0fr;
|
|
579
|
+
transition: grid-template-rows var(--acc-dur) var(--acc-ease); }
|
|
580
|
+
.tl-preview[data-open="true"] .tl-preview-panel { grid-template-rows: 1fr; }
|
|
581
|
+
.tl-preview-panel-inner { overflow: hidden; opacity: 0; filter: blur(2px);
|
|
582
|
+
transition: opacity var(--acc-dur) var(--acc-ease), filter var(--acc-dur) var(--acc-ease); }
|
|
583
|
+
.tl-preview[data-open="true"] .tl-preview-panel-inner { opacity: 1; filter: blur(0); }
|
|
584
|
+
.tl-preview-body { padding: 0; }
|
|
585
|
+
.tl-preview-curve { margin-bottom: 12px; min-height: 162px; display: flex; align-items: center; }
|
|
586
|
+
.tl-preview-sub { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
|
587
|
+
.tl-preview-sub-label { font-size: 13px; font-weight: 500; color: #171717; line-height: 18px; }
|
|
588
|
+
.tl-preview-play { border: none; background: transparent; padding: 0; font: inherit; font-size: 13px;
|
|
589
|
+
font-weight: 500; color: var(--c-text-mut); cursor: pointer; line-height: 18px;
|
|
590
|
+
transition: color 0.12s ease; }
|
|
591
|
+
.tl-preview-play:hover { color: #171717; }
|
|
592
|
+
.tl-preview-play:active { color: var(--c-blue); }
|
|
593
|
+
/* position-preview track — Figma 580:9117: dot + dashed rail inside a
|
|
594
|
+
#f6f6f7 rounded card (58px tall, 21px horizontal inset for the dot) */
|
|
595
|
+
.tl-preview-track { height: 58px; background: #f6f6f7; border-radius: 8px;
|
|
596
|
+
display: flex; align-items: center; padding: 0 21px; }
|
|
597
|
+
.tl-preview-rail-wrap { position: relative; flex: 1; height: 28px; }
|
|
598
|
+
.tl-preview-rail { position: absolute; left: 14px; right: 0; top: 50%; height: 0; transform: translateY(-50%);
|
|
599
|
+
border-top: 1.5px dashed #d4d4d8; }
|
|
600
|
+
.tl-preview-dot { position: absolute; left: 0; top: 50%; width: 28px; height: 28px; margin-top: -14px;
|
|
601
|
+
border-radius: 50%; background: var(--c-blue);
|
|
602
|
+
box-shadow: 0 2px 8px rgba(0,113,226,0.28), 0 0 0 0.5px rgba(0,0,0,0.04);
|
|
603
|
+
will-change: transform; }
|
|
604
|
+
@media (prefers-reduced-motion: reduce) {
|
|
605
|
+
.tl-preview-panel, .tl-preview-panel-inner, .tl-preview-chevron { transition: none !important; }
|
|
606
|
+
}
|
|
607
|
+
|
|
544
608
|
/* menu section header (non-clickable) — Figma node 580:1696 dropdown */
|
|
545
609
|
.tl-menu-group { padding: 12px 8px 8px; font-size: 11px; font-weight: 400; line-height: 14px;
|
|
546
610
|
color: #8b8b8b; pointer-events: none; }
|
|
547
611
|
.tl-menu-group:first-child { padding-top: 4px; }
|
|
548
612
|
|
|
549
|
-
/* spring
|
|
550
|
-
.tl-spring-dur {
|
|
551
|
-
|
|
552
|
-
.tl-spring-dur b { color: var(--c-text); font-weight:
|
|
613
|
+
/* derived-duration caption under the spring fields (Figma 580:9116 — muted) */
|
|
614
|
+
.tl-spring-dur { font-size: 12px; line-height: 16px; color: var(--c-text-faint);
|
|
615
|
+
font-variant-numeric: tabular-nums; }
|
|
616
|
+
.tl-spring-dur b { color: var(--c-text-mut); font-weight: 500; font-variant-numeric: tabular-nums; }
|
|
553
617
|
.tl-spring-dur .tl-spring-dur-hint { color: var(--c-text-faint); }
|
|
554
618
|
|
|
555
619
|
/* read-only (spring-driven) value field */
|
|
@@ -560,7 +624,11 @@
|
|
|
560
624
|
.tl-field.is-readonly .tl-field-value { color: var(--c-text-mut); }
|
|
561
625
|
.tl-field-lock { position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
|
|
562
626
|
display: flex; color: var(--c-text-faint); pointer-events: none; z-index: 4; }
|
|
563
|
-
|
|
627
|
+
/* render below the field: the locked Duration field sits at the top of the
|
|
628
|
+
scrollable .tl-inspector (overflow-y:auto), so an above-tooltip gets its
|
|
629
|
+
top clipped. Anchor to top:100% instead and grow downward. */
|
|
630
|
+
.tl-field-wrap.is-locked .t-tt { left: 0; bottom: auto; top: calc(100% + 8px);
|
|
631
|
+
transform: translate(0, -4px) scale(var(--tt-scale));
|
|
564
632
|
transform-origin: 0 0; white-space: normal; width: 244px; text-align: left; font-size: 12px;
|
|
565
633
|
line-height: 1.45; padding: 9px 11px; }
|
|
566
634
|
.tl-field-wrap.is-locked:hover .t-tt { opacity: 1; transform: translate(0, 0) scale(1);
|
|
@@ -571,10 +639,11 @@
|
|
|
571
639
|
display: flex; align-items: center; justify-content: center;
|
|
572
640
|
background: #fff; border-radius: 12px; box-shadow: var(--card-shadow); }
|
|
573
641
|
|
|
574
|
-
/* ── shared dropdown surface (Figma node 580:
|
|
575
|
-
container: rounded-12
|
|
576
|
-
Inter
|
|
577
|
-
Inter Regular 11/14 #8b8b8b, pt-12 pb-8 pl-12
|
|
642
|
+
/* ── shared dropdown surface (Figma node 580:4723) ──
|
|
643
|
+
container: rounded-12 px-6 py-8 + menu shadow; rows: h-32 rounded-8,
|
|
644
|
+
Inter 13/16 #1b1b1b label with #979797 suffix; section headers:
|
|
645
|
+
Inter Regular 11/14 #8b8b8b, pt-12 pb-8 pl-12. Group divider (580:4779)
|
|
646
|
+
is a full-width #f0f0f0 hairline (see .tl-menu-divider). */
|
|
578
647
|
.tl-menu { background: #fff; border-radius: 12px; padding: 6px; box-shadow: var(--menu-shadow); }
|
|
579
648
|
/* full-bleed divider: negative side margins cancel the .tl-menu 6px padding so
|
|
580
649
|
the line spans the whole dropdown width; no top gap. */
|
|
@@ -582,8 +651,8 @@
|
|
|
582
651
|
.tl-menu-search-input { width: 100%; height: 32px; box-sizing: border-box; border: none; outline: none;
|
|
583
652
|
background: transparent; font: inherit; font-size: 13px; line-height: 16px; color: #1b1b1b; padding: 0 8px; }
|
|
584
653
|
.tl-menu-search-input::placeholder { color: #999; }
|
|
585
|
-
.tl-menu-item { display: flex; align-items: center; gap: 8px; min-height:
|
|
586
|
-
border-radius: 8px; font-size: 13px; font-weight:
|
|
654
|
+
.tl-menu-item { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 8px;
|
|
655
|
+
border-radius: 8px; font-size: 13px; font-weight: 400; line-height: 16px; color: #1b1b1b; cursor: pointer;
|
|
587
656
|
transition: background 0.1s ease, box-shadow 0.1s ease; }
|
|
588
657
|
.tl-menu-item:hover { background: #f4f4f5; }
|
|
589
658
|
.tl-menu-item:active { background: #ededee; }
|
|
@@ -592,13 +661,25 @@
|
|
|
592
661
|
.tl-menu-item-label { flex: 1; min-width: 0; display: flex; align-items: center; }
|
|
593
662
|
.tl-menu-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
594
663
|
.tl-menu-dim { color: #979797; }
|
|
595
|
-
.tl-menu-help { color: #c4c4cc; margin-left: 10px; flex: none; cursor: help; }
|
|
596
|
-
.tl-menu-help:hover { color: var(--c-ruler); }
|
|
597
|
-
.tl-menu-help svg { display: block; }
|
|
598
664
|
.tl-menu-check { display: flex; color: var(--c-text-strong); flex: none; }
|
|
599
665
|
.tl-menu-empty { padding: 10px 8px; color: var(--c-disabled); font-size: 13px; }
|
|
600
666
|
.tl-menu-section { padding: 12px 8px 8px; font-size: 11px; font-weight: 400; line-height: 14px;
|
|
601
667
|
color: #8b8b8b; }
|
|
668
|
+
/* full-bleed group divider (Figma 580:4779) — #f0f0f0 1px hairline spanning
|
|
669
|
+
the whole dropdown width; the -6px sides cancel the .tl-menu 6px padding. */
|
|
670
|
+
.tl-menu-divider { height: 1px; background: #f0f0f0; margin: 6px -6px; }
|
|
671
|
+
/* leading row icon (Figma copy-03 / refresh-cw-03, 16px, #696969 stroke) */
|
|
672
|
+
.tl-menu-icon { display: flex; flex: none; color: #696969; }
|
|
673
|
+
/* dropdown header row (Figma 580:4781) — product name + version, non-clickable.
|
|
674
|
+
pt-12 pb-8 px-12, Inter Regular 11/14; "Refine" reads darker than the rest. */
|
|
675
|
+
.tl-menu-head { display: flex; align-items: center; justify-content: space-between;
|
|
676
|
+
gap: 12px; padding: 10px 12px; font-size: 11px; line-height: 14px;
|
|
677
|
+
font-weight: 400; color: #8b8b8b; white-space: nowrap; pointer-events: none;
|
|
678
|
+
user-select: none; }
|
|
679
|
+
.tl-menu-head-name { color: #4c4c4c; }
|
|
680
|
+
.tl-menu-head-ver { flex: none; line-height: 14px; font-variant-numeric: tabular-nums; }
|
|
681
|
+
/* trailing chevron on link rows (Figma 581:2828 — #696969 right chevron) */
|
|
682
|
+
.tl-menu-chevr { display: flex; flex: none; color: #696969; }
|
|
602
683
|
|
|
603
684
|
/* ═════ transitions.dev — menu dropdown (verbatim) ═════ */
|
|
604
685
|
.t-dropdown {
|
|
@@ -702,11 +783,16 @@
|
|
|
702
783
|
}
|
|
703
784
|
/* below-variant for triggers near the panel top edge */
|
|
704
785
|
.t-tt.tl-tt-below { bottom: auto; top: calc(100% + 8px); transform-origin: 50% 0; font-size: 12px; }
|
|
705
|
-
/*
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
.t-tt
|
|
786
|
+
/* multi-line message variant (e.g. Accept error) — long text wraps to a
|
|
787
|
+
readable measure instead of one overflowing line. Right-anchored so the
|
|
788
|
+
wide bubble grows leftward and never clips off the panel edge. Mirrors the
|
|
789
|
+
.tl-field-wrap.is-locked .t-tt wrapping pattern. */
|
|
790
|
+
.t-tt.tl-tt-msg { left: auto; right: 0; width: 252px; white-space: normal;
|
|
791
|
+
overflow-wrap: break-word; text-wrap: pretty; text-align: left; line-height: 1.45;
|
|
792
|
+
padding: 9px 11px; transform-origin: 100% 0;
|
|
793
|
+
transform: translate(0, 0) scale(var(--tt-scale)); }
|
|
794
|
+
.t-tt-wrap:hover .t-tt.tl-tt-msg,
|
|
795
|
+
.t-tt-trigger:focus-visible + .t-tt.tl-tt-msg { transform: translate(0, 0) scale(1); }
|
|
710
796
|
|
|
711
797
|
/* ═════ transitions.dev — panel reveal (per-phase distance/ease/scale) ═════ */
|
|
712
798
|
/* base = closed / close-end state: resolved travel + scale come from the
|
|
@@ -796,6 +882,9 @@
|
|
|
796
882
|
/* must sit above the header (z-index:40) so it overlays the whole panel */
|
|
797
883
|
position: absolute; top: 0; right: 0; bottom: 0; z-index: 50;
|
|
798
884
|
width: var(--refine-w, 360px); max-width: 100%; overflow: hidden;
|
|
885
|
+
/* shadow lives on the panel (not the inner) so its own overflow:hidden
|
|
886
|
+
doesn't clip it; the left edge then casts over the timeline */
|
|
887
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08), 0 2px 6px 0 rgba(0, 0, 0, 0.05), 0 4px 42px 0 rgba(0, 0, 0, 0.06);
|
|
799
888
|
--panel-translate-x: var(--panel-close-distance);
|
|
800
889
|
--panel-scale-now: var(--panel-scale-close);
|
|
801
890
|
transform: translateX(calc(100% + var(--panel-translate-x))) scale(var(--panel-scale-now));
|
|
@@ -830,14 +919,12 @@
|
|
|
830
919
|
width: var(--refine-w, 360px); height: 100%; box-sizing: border-box;
|
|
831
920
|
display: flex; flex-direction: column;
|
|
832
921
|
background: #fff; color: var(--c-text);
|
|
833
|
-
border-left: 1px solid #f0f0f0;
|
|
834
|
-
box-shadow: -10px 0 30px rgba(0,0,0,0.05);
|
|
835
922
|
font-family: "Inter", system-ui, -apple-system, sans-serif;
|
|
836
923
|
}
|
|
837
924
|
/* header: title block (left) + Agent/Deterministic mode dropdown (right) */
|
|
838
|
-
/* match the timeline top bar exactly:
|
|
925
|
+
/* match the timeline top bar exactly: 7.5px/14px padding, centered, 52px tall (36px row + 15 + 1px border) */
|
|
839
926
|
.tl-refine-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
|
|
840
|
-
gap: 8px; box-sizing: border-box; padding:
|
|
927
|
+
gap: 8px; box-sizing: border-box; padding: 7.5px 14px;
|
|
841
928
|
border-bottom: 1px solid #f0f0f0; }
|
|
842
929
|
.tl-refine-titles { min-width: 0; }
|
|
843
930
|
.tl-refine-titles h3 { font-size: 13px; font-weight: 500; line-height: 14px; color: #17181c; }
|
|
@@ -898,26 +985,46 @@
|
|
|
898
985
|
.tl-scan-morph { position: relative; isolation: isolate; box-sizing: border-box;
|
|
899
986
|
display: inline-flex; align-items: center; justify-content: center;
|
|
900
987
|
margin-bottom: 20px;
|
|
901
|
-
width: var(--scan-idle-w, 122px); height:
|
|
988
|
+
width: var(--scan-idle-w, 122px); height: 36px; padding: 0 12px;
|
|
902
989
|
border: none; border-radius: 60px; cursor: pointer;
|
|
903
|
-
font: inherit; font-size: 13px; font-weight: 500; line-height: 14px;
|
|
904
|
-
|
|
905
|
-
box-shadow: var(--drop-btn)
|
|
990
|
+
font: inherit; font-size: 13px; font-weight: 500; line-height: 14px;
|
|
991
|
+
color: var(--c-blue); background: transparent;
|
|
992
|
+
box-shadow: var(--drop-btn);
|
|
993
|
+
white-space: nowrap;
|
|
906
994
|
--resize-dur: 300ms; --resize-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
907
995
|
transition: width var(--resize-dur) var(--resize-ease),
|
|
908
996
|
height var(--resize-dur) var(--resize-ease),
|
|
909
997
|
border-radius var(--resize-dur) var(--resize-ease),
|
|
910
|
-
background var(--resize-dur) var(--resize-ease),
|
|
911
998
|
color var(--resize-dur) var(--resize-ease),
|
|
912
999
|
box-shadow var(--resize-dur) var(--resize-ease); }
|
|
1000
|
+
.tl-scan-morph::before { content: ""; position: absolute; inset: 0; border-radius: inherit;
|
|
1001
|
+
background: var(--c-blue-bg);
|
|
1002
|
+
background: color(display-p3 0 0.451 0.898 / 0.06);
|
|
1003
|
+
transition: background 0.12s ease; z-index: 0; }
|
|
1004
|
+
.tl-scan-morph::after { content: ""; position: absolute; inset: 0; border-radius: inherit;
|
|
1005
|
+
box-shadow: var(--ring-blue);
|
|
1006
|
+
pointer-events: none; z-index: 2; }
|
|
913
1007
|
/* explicit refine-footer spacing for both idle + scanning states */
|
|
914
1008
|
.tl-refine-foot .tl-scan-morph { margin-bottom: 20px; }
|
|
915
|
-
.tl-scan-morph:hover:not(:disabled)
|
|
916
|
-
|
|
917
|
-
.tl-scan-morph:
|
|
1009
|
+
.tl-scan-morph:hover:not(:disabled):not(.is-scanning)::before {
|
|
1010
|
+
background: var(--c-blue-bg-h); background: color(display-p3 0 0.451 0.898 / 0.10); }
|
|
1011
|
+
.tl-scan-morph:active:not(.is-scanning):not(:disabled) { color: var(--c-blue-pressed); }
|
|
1012
|
+
.tl-scan-morph:active:not(.is-scanning):not(:disabled)::before {
|
|
1013
|
+
background: var(--c-blue-bg-h); background: color(display-p3 0 0.451 0.898 / 0.10); }
|
|
1014
|
+
.tl-scan-morph:disabled:not(.is-scanning) { cursor: default; color: var(--c-blue); }
|
|
1015
|
+
.tl-scan-morph:disabled:not(.is-scanning)::before {
|
|
1016
|
+
background: var(--c-blue-bg); background: color(display-p3 0 0.451 0.898 / 0.06); }
|
|
1017
|
+
.tl-scan-morph:disabled:not(.is-scanning) .tl-scan-label {
|
|
1018
|
+
color: var(--c-blue-pressed); opacity: 0.5; }
|
|
918
1019
|
.tl-scan-morph.is-scanning { width: 100%; height: 49px; border-radius: 60px;
|
|
919
|
-
padding: 8px 16px; cursor: default; color: #676767; background:
|
|
920
|
-
|
|
1020
|
+
padding: 8px 16px; cursor: default; color: #676767; background: transparent;
|
|
1021
|
+
box-shadow: none; }
|
|
1022
|
+
.tl-scan-morph.is-scanning::before {
|
|
1023
|
+
background: #fff;
|
|
1024
|
+
transition: background var(--resize-dur) var(--resize-ease); }
|
|
1025
|
+
.tl-scan-morph.is-scanning::after {
|
|
1026
|
+
box-shadow: inset 0 0 0 1px #f0f0f0;
|
|
1027
|
+
transition: box-shadow var(--resize-dur) var(--resize-ease); }
|
|
921
1028
|
/* Two faces share one grid cell and cross-blur (icon swap, 09) between the
|
|
922
1029
|
label and the icon+status; whichever is hidden fades out with blur+scale.
|
|
923
1030
|
overflow:hidden + nowrap keep the status copy on a single line while the
|
|
@@ -946,13 +1053,67 @@
|
|
|
946
1053
|
beam stays an absolute overlay filling the button instead of collapsing
|
|
947
1054
|
into flow as a flex sibling (which hid the beam and skewed the width). */
|
|
948
1055
|
.tl-scan-morph .tl-scan-beam { position: absolute; inset: 0; z-index: 0;
|
|
949
|
-
border-radius:
|
|
1056
|
+
border-radius: inherit; pointer-events: none;
|
|
950
1057
|
animation: tl-fade-in 360ms var(--resize-ease) both; }
|
|
951
|
-
|
|
1058
|
+
/* Beam radius is auto-detected from this fill child; inherit keeps it locked
|
|
1059
|
+
to the button's own border-radius so the hairline traces the edge at any
|
|
1060
|
+
size (idle pill ↔ scanning rect) without a hardcoded magic number. */
|
|
1061
|
+
.tl-scan-beam-fill { display: block; width: 100%; height: 100%; border-radius: inherit; }
|
|
952
1062
|
@keyframes tl-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1063
|
+
/* ── gated states (Figma 580:3595) — the panel is blocked until a live agent
|
|
1064
|
+
is connected (/refine live) AND it has scanned the page's transitions.
|
|
1065
|
+
Both screens center their content in the panel body. */
|
|
1066
|
+
.tl-gate { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center;
|
|
1067
|
+
padding: 24px 16px; animation: tl-fade-in 260ms var(--resize-ease, cubic-bezier(0.22,1,0.36,1)) both; }
|
|
1068
|
+
.tl-gate-col { display: flex; flex-direction: column; align-items: center; text-align: center; }
|
|
1069
|
+
/* "Before we start" */
|
|
1070
|
+
.tl-gate-title { font-size: 15px; font-weight: 500; line-height: 21px; color: #2c2c2c;
|
|
1071
|
+
text-wrap: balance; }
|
|
1072
|
+
.tl-gate-text { margin: 4px 0 0; max-width: 250px; font-size: 13px; font-weight: 400; line-height: 21px;
|
|
1073
|
+
color: #676767; text-wrap: pretty; }
|
|
1074
|
+
.tl-gate-text .tl-code { color: #4b4b4b; }
|
|
1075
|
+
/* "Agent is scanning your transitions" — pill with dot-matrix loader + a
|
|
1076
|
+
line-style border beam tracing the capsule edge, with a subtext below. */
|
|
1077
|
+
.tl-gate-pill-wrap { position: relative; }
|
|
1078
|
+
.tl-gate-pill { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 6px;
|
|
1079
|
+
height: 36px; padding: 6px 12px; border-radius: 36px; background: #fff; color: #17181c;
|
|
1080
|
+
font-size: 13px; font-weight: 400; line-height: 14px; white-space: nowrap;
|
|
1081
|
+
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), inset 0 0 0 1px rgba(196,196,196,0.10); }
|
|
1082
|
+
.tl-gate-pill .tl-dotm { color: #17181c; }
|
|
1083
|
+
.tl-gate-pill-wrap .tl-gate-beam { position: absolute; inset: 0; z-index: 0; border-radius: 36px;
|
|
1084
|
+
pointer-events: none; animation: tl-fade-in 360ms var(--resize-ease, cubic-bezier(0.22,1,0.36,1)) both; }
|
|
1085
|
+
.tl-gate-beam-fill { display: block; width: 100%; height: 100%; border-radius: 36px; background: #fff; }
|
|
1086
|
+
.tl-gate-sub { margin: 16px 0 0; max-width: 244px; font-size: 12px; font-weight: 400; line-height: 14px;
|
|
1087
|
+
color: #6f6f6f; text-wrap: pretty; }
|
|
1088
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1089
|
+
.tl-gate, .tl-gate-pill-wrap .tl-gate-beam { animation: none !important; } }
|
|
1090
|
+
/* dot-matrix loader — ported from @dotmatrix/dotm-square-14
|
|
1091
|
+
(github.com/zzzzshawn/matrix, MIT). A 5×5 dot grid cross-fades through four
|
|
1092
|
+
frame masks in the sequence 0→1→2→3→2→1, dot opacities x=1 / o=0.52 / .=0.08.
|
|
1093
|
+
The original drives this with a JS stepped cycle; here each symmetry group of
|
|
1094
|
+
dots owns one opacity @keyframes over a single 1.36s loop (no JS timer). */
|
|
1095
|
+
.tl-dotm { flex: 0 0 auto; display: grid; width: 16px; height: 16px;
|
|
1096
|
+
grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(5, 1fr);
|
|
1097
|
+
gap: 1px; }
|
|
1098
|
+
.tl-dotm-dot { align-self: center; justify-self: center; width: 100%; height: 100%;
|
|
1099
|
+
border-radius: 50%; background: currentColor; opacity: 0.08;
|
|
1100
|
+
animation-duration: 1.36s; animation-timing-function: linear; animation-iteration-count: infinite; }
|
|
1101
|
+
.tl-dotm-a { animation-name: tl-dotm-a; }
|
|
1102
|
+
.tl-dotm-b { animation-name: tl-dotm-b; }
|
|
1103
|
+
.tl-dotm-c { animation-name: tl-dotm-c; }
|
|
1104
|
+
.tl-dotm-d { animation-name: tl-dotm-d; }
|
|
1105
|
+
.tl-dotm-e { animation-name: tl-dotm-e; }
|
|
1106
|
+
.tl-dotm-f { animation-name: tl-dotm-f; }
|
|
1107
|
+
.tl-dotm-g { animation-name: tl-dotm-g; }
|
|
1108
|
+
.tl-dotm-h { opacity: 0.52; }
|
|
1109
|
+
@keyframes tl-dotm-a { 0%{opacity:1} 16.667%{opacity:.08} 33.333%{opacity:.08} 50%{opacity:1} 66.667%{opacity:.08} 83.333%{opacity:.08} 100%{opacity:1} }
|
|
1110
|
+
@keyframes tl-dotm-b { 0%{opacity:.08} 16.667%{opacity:.08} 33.333%{opacity:1} 50%{opacity:.08} 66.667%{opacity:1} 83.333%{opacity:.08} 100%{opacity:.08} }
|
|
1111
|
+
@keyframes tl-dotm-c { 0%{opacity:.08} 16.667%{opacity:1} 33.333%{opacity:.08} 50%{opacity:1} 66.667%{opacity:.08} 83.333%{opacity:1} 100%{opacity:.08} }
|
|
1112
|
+
@keyframes tl-dotm-d { 0%{opacity:.08} 16.667%{opacity:1} 33.333%{opacity:1} 50%{opacity:.08} 66.667%{opacity:1} 83.333%{opacity:1} 100%{opacity:.08} }
|
|
1113
|
+
@keyframes tl-dotm-e { 0%{opacity:1} 16.667%{opacity:.52} 33.333%{opacity:.08} 50%{opacity:.52} 66.667%{opacity:.08} 83.333%{opacity:.52} 100%{opacity:1} }
|
|
1114
|
+
@keyframes tl-dotm-f { 0%{opacity:.08} 16.667%{opacity:1} 33.333%{opacity:.52} 50%{opacity:.08} 66.667%{opacity:.52} 83.333%{opacity:1} 100%{opacity:.08} }
|
|
1115
|
+
@keyframes tl-dotm-g { 0%{opacity:.08} 16.667%{opacity:.52} 33.333%{opacity:.08} 50%{opacity:.08} 66.667%{opacity:.08} 83.333%{opacity:.52} 100%{opacity:.08} }
|
|
1116
|
+
.tl-scanning-row { display: inline-flex; align-items: center; gap: 6px; }
|
|
956
1117
|
/* loading status text — transitions.dev shimmer text (15) + text states swap (04) */
|
|
957
1118
|
.tl-refine-status-text {
|
|
958
1119
|
--shimmer-dur: 2000ms; --shimmer-base: #9a9a9a; --shimmer-highlight: #17181c;
|
|
@@ -988,42 +1149,106 @@
|
|
|
988
1149
|
.tl-mode-row-check { flex: 0 0 auto; width: 16px; height: 16px; display: flex; color: #17181c; }
|
|
989
1150
|
@keyframes tl-fade-up { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
|
|
990
1151
|
|
|
991
|
-
/* suggestion cards */
|
|
992
|
-
.tl-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
.tl-sug-
|
|
997
|
-
.tl-sug-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
.tl-sug-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
.tl-sug-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
.tl-sug-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
.tl-sug-
|
|
1011
|
-
.tl-sug-
|
|
1012
|
-
|
|
1013
|
-
|
|
1152
|
+
/* suggestion cards (Figma node 580:9158) */
|
|
1153
|
+
.tl-sug { box-sizing: border-box; display: flex; flex-direction: column; gap: 10px;
|
|
1154
|
+
padding: 10px 12px 12px; background: #fff; border-radius: 12px; overflow: clip;
|
|
1155
|
+
box-shadow: 0 0 0 1px rgba(0,0,0,0.08); }
|
|
1156
|
+
/* top row: member chip + property (or a single heading for replace cards) */
|
|
1157
|
+
.tl-sug-head { display: flex; align-items: center; gap: 8px; min-height: 20px; }
|
|
1158
|
+
.tl-sug-member { flex: none; display: inline-flex; align-items: center; height: 20px; padding: 0 6px;
|
|
1159
|
+
border-radius: 50px; background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.08);
|
|
1160
|
+
font-size: 11px; font-weight: 400; line-height: 18px; color: #696969; white-space: nowrap; }
|
|
1161
|
+
.tl-sug-prop { font-size: 13px; font-weight: 500; line-height: 18px; color: #171717; white-space: nowrap; }
|
|
1162
|
+
/* full-bleed hairline (Figma 581:5970) — cancels the 12px card side padding */
|
|
1163
|
+
.tl-sug-divider { height: 1px; background: #f1f1f1; margin: 0 -12px; }
|
|
1164
|
+
/* colored category chip — color set by kind modifier below */
|
|
1165
|
+
.tl-sug-cat { align-self: flex-start; display: inline-flex; align-items: center; height: 24px;
|
|
1166
|
+
padding: 0 10px; border-radius: 50px; font-size: 13px; font-weight: 500; line-height: 16px;
|
|
1167
|
+
white-space: nowrap; }
|
|
1168
|
+
.tl-sug-cat.is-easing { color: #a830fd; background: rgba(173,68,247,0.06); box-shadow: inset 0 0 0 1px rgba(173,68,247,0.18); }
|
|
1169
|
+
.tl-sug-cat.is-duration { color: #3097fd; background: rgba(111,183,255,0.08); box-shadow: inset 0 0 0 1px rgba(48,151,253,0.18); }
|
|
1170
|
+
.tl-sug-cat.is-scale { color: #26953b; background: rgba(38,149,59,0.06); box-shadow: inset 0 0 0 1px rgba(38,149,59,0.18); }
|
|
1171
|
+
.tl-sug-cat.is-distance, .tl-sug-cat.is-delay { color: #c35c00; background: rgba(247,152,68,0.06); box-shadow: inset 0 0 0 1px rgba(205,115,36,0.18); }
|
|
1172
|
+
.tl-sug-cat.is-replace { color: #3097fd; background: rgba(111,183,255,0.08); box-shadow: inset 0 0 0 1px rgba(48,151,253,0.18); }
|
|
1173
|
+
/* content stack */
|
|
1174
|
+
.tl-sug-body { display: flex; flex-direction: column; gap: 8px; width: 100%; }
|
|
1175
|
+
.tl-sug-text { display: flex; flex-direction: column; gap: 8px; width: 100%; }
|
|
1176
|
+
.tl-sug-title { font-size: 12px; font-weight: 500; line-height: 16px; color: #1b1b1b; }
|
|
1177
|
+
/* before/after delta — two stacked Roboto Mono lines (Figma 581:4469) */
|
|
1178
|
+
.tl-sug-delta { display: flex; flex-direction: column;
|
|
1179
|
+
font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1180
|
+
font-size: 13px; font-weight: 400; line-height: 16px; }
|
|
1181
|
+
.tl-sug-from { color: #7b7b7b; text-decoration: line-through; }
|
|
1182
|
+
.tl-sug-to { color: #1b1b1b; }
|
|
1183
|
+
.tl-sug-reason { font-size: 12px; font-weight: 400; line-height: 18px; color: #6d6d6d; }
|
|
1184
|
+
/* shared white pill button (Figma 580:757) — card Apply + bottom-bar actions */
|
|
1185
|
+
.tl-pill-btn { align-self: flex-start; box-sizing: border-box; display: inline-flex; align-items: center;
|
|
1186
|
+
justify-content: center; gap: 8px; height: 32px; padding: 6px 12px; border: none; cursor: pointer;
|
|
1187
|
+
border-radius: 36px; background: #fff;
|
|
1188
|
+
font: inherit; font-size: 13px; font-weight: 500; line-height: 14px; color: #2b2b2b; white-space: nowrap;
|
|
1189
|
+
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.04),
|
|
1190
|
+
inset 0 0 0 1px rgba(0,0,0,0.06), inset 0 -1px 0 0 rgba(0,0,0,0.1), inset 0 0 0 1px rgba(196,196,196,0.1);
|
|
1191
|
+
transition: background 0.12s ease, scale 0.12s ease, opacity 0.12s ease; }
|
|
1192
|
+
.tl-pill-btn .tl-dim { color: #696969; }
|
|
1193
|
+
.tl-pill-btn:hover:not(:disabled) { background: #fafafa; }
|
|
1194
|
+
.tl-pill-btn:active:not(:disabled) { scale: 0.98; }
|
|
1195
|
+
.tl-pill-btn:disabled { opacity: 0.45; cursor: default; }
|
|
1196
|
+
/* applied state — GRAY, never green (matches Accept icon --c-text #17181c) */
|
|
1197
|
+
.tl-pill-btn.is-applied { color: var(--c-text); cursor: default; }
|
|
1198
|
+
.tl-pill-btn.is-applied svg path { stroke: var(--c-text); }
|
|
1199
|
+
/* card resize (transitions.dev 01-card-resize): width tween on Apply -> Applied.
|
|
1200
|
+
250ms cubic-bezier(0.22,1,0.36,1); width is driven explicitly from JS. */
|
|
1201
|
+
.tl-pill-btn.tl-apply-btn {
|
|
1202
|
+
transition: background 0.12s ease, scale 0.12s ease, opacity 0.12s ease,
|
|
1203
|
+
width 250ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
1204
|
+
will-change: width; }
|
|
1205
|
+
/* success check (transitions.dev 10-success-check): fade + rotate + Y-bob +
|
|
1206
|
+
blur on the wrapper, stroke-draw on the path. GRAY stroke. */
|
|
1207
|
+
.tl-sug-check { display: inline-flex; transform-origin: center; opacity: 0;
|
|
1208
|
+
will-change: transform, opacity, filter; }
|
|
1209
|
+
.tl-sug-check svg { display: block; overflow: visible; }
|
|
1210
|
+
/* stroke-dasharray = path.getTotalLength() of ICONS.accept "M4 8.4268 L6.46155
|
|
1211
|
+
11.19223 L12 4.97001" (~12.03 user units, rounded up to 13). JS also sets it
|
|
1212
|
+
inline per-render via getTotalLength() for sub-pixel safety. */
|
|
1213
|
+
.tl-sug-check svg path { stroke-dasharray: 13; stroke-dashoffset: 13; }
|
|
1214
|
+
.tl-sug-check[data-state="in"] {
|
|
1215
|
+
animation:
|
|
1216
|
+
tl-check-fade 500ms cubic-bezier(0.22, 1, 0.36, 1) forwards,
|
|
1217
|
+
tl-check-rotate 500ms cubic-bezier(0.22, 1, 0.36, 1) forwards,
|
|
1218
|
+
tl-check-blur 500ms cubic-bezier(0.22, 1, 0.36, 1) forwards,
|
|
1219
|
+
tl-check-bob 500ms cubic-bezier(0.34, 1.35, 0.64, 1) forwards; }
|
|
1220
|
+
.tl-sug-check[data-state="in"] svg path {
|
|
1221
|
+
animation: tl-check-draw 500ms cubic-bezier(0.22, 1, 0.36, 1) 80ms forwards; }
|
|
1222
|
+
@keyframes tl-check-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
1223
|
+
@keyframes tl-check-rotate { from { transform: rotate(80deg); } to { transform: rotate(0deg); } }
|
|
1224
|
+
@keyframes tl-check-blur { from { filter: blur(10px); } to { filter: blur(0); } }
|
|
1225
|
+
@keyframes tl-check-bob { from { translate: 0 40px; } to { translate: 0 0; } }
|
|
1226
|
+
@keyframes tl-check-draw { to { stroke-dashoffset: 0; } }
|
|
1227
|
+
/* results action bar (Figma node 581:6285): white sticky bar with top hairline
|
|
1228
|
+
holding two equal-width pill buttons (Apply all + Scan again). */
|
|
1229
|
+
.tl-refine-foot.is-bar { padding: 8px 16px; gap: 8px; justify-content: stretch;
|
|
1230
|
+
background: #fff; border-top: 1px solid #f0f0f0; }
|
|
1231
|
+
.tl-refine-foot.is-bar .tl-pill-btn { flex: 1; align-self: stretch; }
|
|
1014
1232
|
.tl-refine-empty { font-size: 13px; color: var(--c-text-mut); padding: 18px 4px; text-align: center; line-height: 1.5; text-wrap: balance; }
|
|
1015
1233
|
.tl-refine-error { font-size: 13px; color: #c0392b; padding: 14px; line-height: 1.5;
|
|
1016
1234
|
background: rgba(192,57,43,0.06); border-radius: 10px; }
|
|
1017
1235
|
.tl-refine-error code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
|
|
1018
1236
|
.tl-refine-foot { padding: 12px 20px; display: flex; gap: 8px; }
|
|
1237
|
+
/* results action bar (Figma 581:6285) — sticky white bar, top hairline, two equal pills */
|
|
1238
|
+
.tl-refine-foot.is-bar { padding: 8px 16px; gap: 8px; justify-content: stretch;
|
|
1239
|
+
background: #fff; border-top: 1px solid #f0f0f0; }
|
|
1240
|
+
.tl-refine-foot.is-bar .tl-pill-btn { flex: 1; align-self: stretch; }
|
|
1019
1241
|
@media (prefers-reduced-motion: reduce) {
|
|
1020
1242
|
.tl-refine-panel { transition: none; }
|
|
1021
|
-
.tl-
|
|
1243
|
+
.tl-dotm-dot { animation: none !important; }
|
|
1022
1244
|
.tl-refine-status-text, .tl-sug { animation: none; }
|
|
1023
1245
|
.t-shimmer::before { animation: none !important; }
|
|
1024
1246
|
.t-text-swap { transition: none !important; }
|
|
1025
1247
|
.tl-refine-results .t-stagger-line { transition: none !important; }
|
|
1026
1248
|
.t-resize, .tl-scan-morph { transition: none !important; }
|
|
1249
|
+
.tl-pill-btn.tl-apply-btn { transition: background 0.12s ease, scale 0.12s ease, opacity 0.12s ease !important; }
|
|
1250
|
+
.tl-sug-check { animation: none !important; opacity: 1; }
|
|
1251
|
+
.tl-sug-check svg path { animation: none !important; stroke-dashoffset: 0 !important; }
|
|
1027
1252
|
.tl-scan-face { transition: none !important; }
|
|
1028
1253
|
.tl-scan-beam { animation: none !important; }
|
|
1029
1254
|
.tl-pill { animation: none !important; }
|
|
@@ -1049,6 +1274,12 @@
|
|
|
1049
1274
|
const { createElement: h, useState, useEffect, useLayoutEffect, useRef, useMemo,
|
|
1050
1275
|
useCallback, useSyncExternalStore, createContext, useContext } = React;
|
|
1051
1276
|
|
|
1277
|
+
// Single source of truth for the displayed version. When served via the relay
|
|
1278
|
+
// (inject.js), the build injects window.__TX_REFINE_VERSION from package.json so
|
|
1279
|
+
// it never drifts; the fallback mirrors the current package.json version for the
|
|
1280
|
+
// standalone demo.html.
|
|
1281
|
+
const REFINE_VERSION = (typeof window !== "undefined" && window.__TX_REFINE_VERSION) || "0.3.5";
|
|
1282
|
+
|
|
1052
1283
|
// ── helpers ──
|
|
1053
1284
|
function parseCssTime(v) { const t=v.trim(); if(t.endsWith("ms")) return parseFloat(t); if(t.endsWith("s")) return parseFloat(t)*1000; const n=parseFloat(t); return isNaN(n)?0:n; }
|
|
1054
1285
|
function formatCssTime(ms) { if(ms>=1000&&ms%1000===0) return `${ms/1000}s`; if(ms>=100) return `${ms/1000}s`; return `${ms}ms`; }
|
|
@@ -1325,8 +1556,8 @@
|
|
|
1325
1556
|
play: {vb:"0 0 10 12", svg:`<path d="M9.49453 6.92883L1.54117 11.8508C0.866207 12.2681 0 11.7628 0 10.9216V1.07765C0 0.237764 0.864957 -0.268832 1.54117 0.149776L9.49453 5.07175C9.64807 5.16524 9.7757 5.30037 9.86447 5.46344C9.95324 5.62651 10 5.81172 10 6.00029C10 6.18885 9.95324 6.37407 9.86447 6.53714C9.7757 6.70021 9.64807 6.83534 9.49453 6.92883Z" fill="currentColor"/>`},
|
|
1326
1557
|
pause: {vb:"0 0 24 24", svg:`<path d="M5.75 3C4.7835 3 4 3.7835 4 4.75V19.25C4 20.2165 4.7835 21 5.75 21H8.25C9.2165 21 10 20.2165 10 19.25V4.75C10 3.7835 9.2165 3 8.25 3H5.75Z" fill="currentColor"/><path d="M15.75 3C14.7835 3 14 3.7835 14 4.75V19.25C14 20.2165 14.7835 21 15.75 21H18.25C19.2165 21 20 20.2165 20 19.25V4.75C20 3.7835 19.2165 3 18.25 3H15.75Z" fill="currentColor"/>`},
|
|
1327
1558
|
stop: {vb:"0 0 24 24", svg:`<path fill-rule="evenodd" clip-rule="evenodd" d="M5.32378 3C5.3325 3 5.34124 3 5.35 3L18.6762 3C18.9337 2.99998 19.1702 2.99997 19.3679 3.01612C19.581 3.03353 19.8142 3.07339 20.0445 3.19074C20.3738 3.35852 20.6415 3.62624 20.8093 3.95552C20.9266 4.18583 20.9665 4.419 20.9839 4.63213C21 4.82981 21 5.06629 21 5.32377V18.6762C21 18.9337 21 19.1702 20.9839 19.3679C20.9665 19.581 20.9266 19.8142 20.8093 20.0445C20.6415 20.3738 20.3738 20.6415 20.0445 20.8093C19.8142 20.9266 19.581 20.9665 19.3679 20.9839C19.1702 21 18.9337 21 18.6762 21H5.32377C5.06629 21 4.82981 21 4.63213 20.9839C4.419 20.9665 4.18583 20.9266 3.95552 20.8093C3.62624 20.6415 3.35852 20.3738 3.19074 20.0445C3.07339 19.8142 3.03353 19.581 3.01612 19.3679C2.99997 19.1702 2.99998 18.9337 3 18.6762L3 5.35C3 5.34124 3 5.3325 3 5.32379C2.99998 5.0663 2.99997 4.82982 3.01612 4.63213C3.03353 4.419 3.07339 4.18583 3.19074 3.95552C3.35852 3.62624 3.62624 3.35852 3.95552 3.19074C4.18583 3.07339 4.419 3.03353 4.63213 3.01612C4.82982 2.99997 5.0663 2.99998 5.32378 3Z" fill="currentColor"/>`},
|
|
1328
|
-
restart: {vb:"0 0 16 16", svg:`<path d="
|
|
1329
|
-
copy: {vb:"0 0 16 16", svg:`<path d="M5.
|
|
1559
|
+
restart: {vb:"0 0 16 16", svg:`<g transform="translate(1.75,1.25)"><path d="M7.47222 12.75C7.47222 12.75 7.9912 12.6772 10.1391 10.5684C12.287 8.45955 12.287 5.04046 10.1391 2.93162C9.37809 2.18446 8.44956 1.70202 7.47222 1.4843M7.47222 9.15L7.47222 12.75H11.1389M5.02778 0.750117C5.02778 0.750117 4.5088 0.82291 2.36091 2.93174C0.213029 5.04057 0.213029 8.45966 2.36091 10.5685C3.12191 11.3157 4.05044 11.7981 5.02778 12.0158M5.02778 4.35L5.02778 0.750117L1.36111 0.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></g>`},
|
|
1560
|
+
copy: {vb:"0 0 16 16", svg:`<path d="M5.6 5.6V3.92C5.6 3.24794 5.6 2.91191 5.73079 2.65521C5.84584 2.42942 6.02942 2.24584 6.25521 2.13079C6.51191 2 6.84794 2 7.52 2H12.08C12.7521 2 13.0881 2 13.3448 2.13079C13.5706 2.24584 13.7542 2.42942 13.8692 2.65521C14 2.91191 14 3.24794 14 3.92V8.48C14 9.15206 14 9.4881 13.8692 9.74479C13.7542 9.97058 13.5706 10.1542 13.3448 10.2692C13.0881 10.4 12.7521 10.4 12.08 10.4H10.4M3.92 14H8.48C9.15206 14 9.48809 14 9.74479 13.8692C9.97058 13.7542 10.1542 13.5706 10.2692 13.3448C10.4 13.0881 10.4 12.7521 10.4 12.08V7.52C10.4 6.84794 10.4 6.51191 10.2692 6.25521C10.1542 6.02942 9.97058 5.84584 9.74479 5.73079C9.48809 5.6 9.15206 5.6 8.48 5.6H3.92C3.24794 5.6 2.91191 5.6 2.65521 5.73079C2.42942 5.84584 2.24584 6.02942 2.13079 6.25521C2 6.51191 2 6.84794 2 7.52V12.08C2 12.7521 2 13.0881 2.13079 13.3448C2.24584 13.5706 2.42942 13.7542 2.65521 13.8692C2.91191 14 3.24794 14 3.92 14Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>`},
|
|
1330
1561
|
check: {vb:"0 0 16 16", svg:`<path d="M3 7.88889L5.76923 11L12 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`},
|
|
1331
1562
|
chevron: {vb:"0 0 16 16", svg:`<path fill-rule="evenodd" clip-rule="evenodd" d="M4.46967 6.46967C4.76256 6.17678 5.23744 6.17678 5.53033 6.46967L8 8.93934L10.4697 6.46967C10.7626 6.17678 11.2374 6.17678 11.5303 6.46967C11.8232 6.76256 11.8232 7.23744 11.5303 7.53033L8.53033 10.5303C8.23744 10.8232 7.76256 10.8232 7.46967 10.5303L4.46967 7.53033C4.17678 7.23744 4.17678 6.76256 4.46967 6.46967Z" fill="currentColor"/>`},
|
|
1332
1563
|
gear: {vb:"0 0 16 16", svg:`<path d="M8.00016 9.99992C9.10473 9.99992 10.0002 9.10449 10.0002 7.99992C10.0002 6.89535 9.10473 5.99992 8.00016 5.99992C6.89559 5.99992 6.00016 6.89535 6.00016 7.99992C6.00016 9.10449 6.89559 9.99992 8.00016 9.99992Z" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.485 9.8181C12.4043 10.0009 12.3803 10.2037 12.4159 10.4003C12.4516 10.5969 12.5453 10.7783 12.685 10.9211L12.7214 10.9575C12.8341 11.0701 12.9235 11.2038 12.9845 11.3509C13.0455 11.498 13.0769 11.6558 13.0769 11.8151C13.0769 11.9744 13.0455 12.1321 12.9845 12.2792C12.9235 12.4264 12.8341 12.5601 12.7214 12.6726C12.6088 12.7853 12.4751 12.8747 12.328 12.9357C12.1808 12.9967 12.0231 13.0281 11.8638 13.0281C11.7045 13.0281 11.5468 12.9967 11.3996 12.9357C11.2525 12.8747 11.1188 12.7853 11.0062 12.6726L10.9699 12.6363C10.827 12.4966 10.6456 12.4028 10.449 12.3672C10.2524 12.3315 10.0496 12.3556 9.86683 12.4363C9.68757 12.5131 9.5347 12.6407 9.42702 12.8033C9.31933 12.9659 9.26155 13.1564 9.26077 13.3514V13.4545C9.26077 13.7759 9.13306 14.0842 8.90575 14.3116C8.67843 14.5389 8.37012 14.6666 8.04865 14.6666C7.72717 14.6666 7.41887 14.5389 7.19155 14.3116C6.96423 14.0842 6.83653 13.7759 6.83653 13.4545V13.3999C6.83183 13.1993 6.7669 13.0048 6.65017 12.8416C6.53344 12.6783 6.37031 12.554 6.18198 12.4848C5.99918 12.4041 5.79641 12.38 5.59981 12.4157C5.4032 12.4513 5.22179 12.545 5.07895 12.6848L5.04259 12.7211C4.93001 12.8338 4.79633 12.9232 4.64918 12.9842C4.50203 13.0452 4.3443 13.0766 4.18501 13.0766C4.02572 13.0766 3.86799 13.0452 3.72084 12.9842C3.57369 12.9232 3.44001 12.8338 3.32744 12.7211C3.21474 12.6086 3.12533 12.4749 3.06433 12.3277C3.00333 12.1806 2.97194 12.0228 2.97194 11.8636C2.97194 11.7043 3.00333 11.5465 3.06433 11.3994C3.12533 11.2522 3.21474 11.1186 3.32744 11.006L3.3638 10.9696C3.50352 10.8268 3.59724 10.6454 3.63289 10.4488C3.66854 10.2522 3.64447 10.0494 3.5638 9.86658C3.48697 9.68733 3.35941 9.53445 3.19681 9.42677C3.03421 9.31909 2.84367 9.2613 2.64865 9.26052H2.54562C2.22414 9.26052 1.91583 9.13282 1.68852 8.9055C1.4612 8.67819 1.3335 8.36988 1.3335 8.0484C1.3335 7.72693 1.4612 7.41862 1.68852 7.1913C1.91583 6.96399 2.22414 6.83628 2.54562 6.83628H2.60016C2.80077 6.83159 2.99532 6.76666 3.15853 6.64992C3.32173 6.53319 3.44605 6.37006 3.51531 6.18174C3.59599 5.99894 3.62006 5.79616 3.58441 5.59956C3.54876 5.40296 3.45503 5.22154 3.31531 5.07871L3.27895 5.04234C3.16625 4.92977 3.07685 4.79609 3.01585 4.64894C2.95485 4.50179 2.92345 4.34406 2.92345 4.18477C2.92345 4.02548 2.95485 3.86775 3.01585 3.7206C3.07685 3.57345 3.16625 3.43976 3.27895 3.32719C3.39152 3.21449 3.52521 3.12509 3.67236 3.06409C3.81951 3.00309 3.97723 2.97169 4.13653 2.97169C4.29582 2.97169 4.45355 3.00309 4.6007 3.06409C4.74785 3.12509 4.88153 3.21449 4.9941 3.32719L5.03047 3.36355C5.1733 3.50327 5.35472 3.597 5.55132 3.63265C5.74792 3.6683 5.9507 3.64423 6.1335 3.56355H6.18198C6.36123 3.48673 6.51411 3.35916 6.62179 3.19656C6.72948 3.03396 6.78726 2.84343 6.78804 2.6484V2.54537C6.78804 2.2239 6.91575 1.91559 7.14306 1.68827C7.37038 1.46096 7.67869 1.33325 8.00016 1.33325C8.32164 1.33325 8.62994 1.46096 8.85726 1.68827C9.08458 1.91559 9.21228 2.2239 9.21228 2.54537V2.59992C9.21306 2.79494 9.27085 2.98548 9.37853 3.14808C9.48621 3.31068 9.63909 3.43824 9.81834 3.51507C10.0011 3.59575 10.2039 3.61981 10.4005 3.58416C10.5971 3.54852 10.7785 3.45479 10.9214 3.31507L10.9577 3.27871C11.0703 3.16601 11.204 3.0766 11.3511 3.0156C11.4983 2.9546 11.656 2.92321 11.8153 2.92321C11.9746 2.92321 12.1323 2.9546 12.2795 3.0156C12.4266 3.0766 12.5603 3.16601 12.6729 3.27871C12.7856 3.39128 12.875 3.52496 12.936 3.67211C12.997 3.81926 13.0284 3.97699 13.0284 4.13628C13.0284 4.29557 12.997 4.4533 12.936 4.60045C12.875 4.7476 12.7856 4.88128 12.6729 4.99386L12.6365 5.03022C12.4968 5.17306 12.4031 5.35447 12.3674 5.55108C12.3318 5.74768 12.3558 5.95045 12.4365 6.13325V6.18174C12.5134 6.36099 12.6409 6.51387 12.8035 6.62155C12.9661 6.72923 13.1567 6.78702 13.3517 6.7878H13.4547C13.7762 6.7878 14.0845 6.9155 14.3118 7.14282C14.5391 7.37014 14.6668 7.67844 14.6668 7.99992C14.6668 8.32139 14.5391 8.6297 14.3118 8.85702C14.0845 9.08433 13.7762 9.21204 13.4547 9.21204H13.4002C13.2051 9.21282 13.0146 9.2706 12.852 9.37829C12.6894 9.48597 12.5618 9.63885 12.485 9.8181Z" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>`},
|
|
@@ -1337,9 +1568,17 @@
|
|
|
1337
1568
|
scan: {vb:"0 0 13 15", svg:`<path fill-rule="evenodd" clip-rule="evenodd" d="M6.76556 1.2204C6.60067 1.12222 6.39933 1.12222 6.23444 1.2204L4.94588 1.98765C4.68515 2.1429 4.35488 2.04501 4.20822 1.76901C4.06156 1.49301 4.15403 1.14341 4.41477 0.988161L5.70333 0.220909C6.198 -0.0736362 6.802 -0.0736362 7.29667 0.220909L8.58523 0.988161C8.84597 1.14341 8.93844 1.49301 8.79178 1.76901C8.64512 2.04501 8.31485 2.1429 8.05412 1.98765L6.76556 1.2204ZM2.94914 2.51871C3.0958 2.79471 3.00333 3.14431 2.74259 3.29956L1.35944 4.12314C1.18888 4.2247 1.08333 4.41574 1.08333 4.62289L1.08333 6.16211C1.08333 6.47878 0.840821 6.73549 0.541667 6.73549C0.242513 6.73549 4.30478e-07 6.47878 4.73526e-07 6.16211L6.02669e-07 4.62289C6.45717e-07 4.00144 0.316652 3.42832 0.828326 3.12365L2.21148 2.30007C2.47221 2.14482 2.80248 2.24271 2.94914 2.51871ZM10.0509 2.51871C10.1975 2.24271 10.5278 2.14482 10.7885 2.30007L12.1717 3.12365C12.6833 3.42832 13 4.00144 13 4.62288V6.16211C13 6.47878 12.7575 6.73549 12.4583 6.73549C12.1592 6.73549 11.9167 6.47878 11.9167 6.16211V4.62288C11.9167 4.41574 11.8111 4.2247 11.6406 4.12314L10.2574 3.29956C9.99667 3.14431 9.9042 2.79471 10.0509 2.51871ZM12.4583 8.26451C12.7575 8.26451 13 8.52122 13 8.83789V10.3771C13 10.9986 12.6833 11.5717 12.1717 11.8763L10.7885 12.6999C10.5278 12.8552 10.1975 12.7573 10.0509 12.4813C9.9042 12.2053 9.99668 11.8557 10.2574 11.7004L11.6406 10.8769C11.8111 10.7753 11.9167 10.5843 11.9167 10.3771V8.83789C11.9167 8.52122 12.1592 8.26451 12.4583 8.26451ZM0.541667 8.26451C0.840821 8.26451 1.08333 8.52122 1.08333 8.83789L1.08333 10.3771C1.08333 10.5843 1.18888 10.7753 1.35944 10.8769L2.74259 11.7004C3.00332 11.8557 3.0958 12.2053 2.94913 12.4813C2.80247 12.7573 2.47221 12.8552 2.21147 12.6999L0.828326 11.8763C0.31665 11.5717 -4.30478e-08 10.9986 0 10.3771L1.29143e-07 8.83789C1.72191e-07 8.52122 0.242513 8.26451 0.541667 8.26451ZM4.20821 13.231C4.35488 12.955 4.68514 12.8571 4.94588 13.0123L6.23444 13.7796C6.39933 13.8778 6.60067 13.8778 6.76556 13.7796L8.05412 13.0123C8.31486 12.8571 8.64512 12.955 8.79179 13.231C8.93845 13.507 8.84598 13.8566 8.58524 14.0118L7.29667 14.7791C6.802 15.0736 6.198 15.0736 5.70333 14.7791L4.41476 14.0118C4.15402 13.8566 4.06155 13.507 4.20821 13.231Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M4.62357 6.35324C4.77315 6.07899 5.10443 5.98503 5.3635 6.14337L6.49997 6.83792L7.63643 6.14337C7.8955 5.98503 8.22678 6.07899 8.37636 6.35324C8.52594 6.62748 8.43717 6.97816 8.1781 7.13649L7.04164 7.83104V9.22014C7.04164 9.53681 6.79912 9.79353 6.49997 9.79353C6.20082 9.79353 5.9583 9.53681 5.9583 9.22014V7.83104L4.82184 7.13649C4.56276 6.97816 4.474 6.62748 4.62357 6.35324Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M6.49998 0.213281C6.79913 0.213281 7.04164 0.469993 7.04164 0.786663V3.10408C7.04164 3.42075 6.79913 3.67746 6.49998 3.67746C6.20082 3.67746 5.95831 3.42075 5.95831 3.10408V0.786663C5.95831 0.469993 6.20082 0.213281 6.49998 0.213281ZM0.349927 3.83504C0.499504 3.56079 0.830782 3.46683 1.08986 3.62516L3.02114 4.80548C3.28022 4.96381 3.36898 5.31449 3.21941 5.58873C3.06983 5.86297 2.73855 5.95694 2.47948 5.7986L0.548191 4.61829C0.289116 4.45995 0.20035 4.10928 0.349927 3.83504ZM12.65 3.83504C12.7996 4.10928 12.7108 4.45995 12.4518 4.61829L10.5205 5.7986C10.2614 5.95694 9.93012 5.86297 9.78055 5.58873C9.63097 5.31449 9.71974 4.96381 9.97881 4.80548L11.9101 3.62516C12.1692 3.46683 12.5004 3.56079 12.65 3.83504ZM3.24873 9.39335C3.3983 9.6676 3.30954 10.0183 3.05046 10.1766L1.08986 11.3748C0.830782 11.5332 0.499504 11.4392 0.349927 11.165C0.20035 10.8907 0.289115 10.54 0.548191 10.3817L2.50879 9.18348C2.76787 9.02515 3.09915 9.11911 3.24873 9.39335ZM9.75123 9.39335C9.90081 9.11911 10.2321 9.02515 10.4912 9.18348L12.4518 10.3817C12.7108 10.54 12.7996 10.8907 12.65 11.165C12.5004 11.4392 12.1692 11.5332 11.9101 11.3748L9.94949 10.1766C9.69042 10.0183 9.60165 9.6676 9.75123 9.39335ZM6.49998 11.2867C6.79913 11.2867 7.04164 11.5434 7.04164 11.8601V14.2133C7.04164 14.53 6.79913 14.7867 6.49998 14.7867C6.20082 14.7867 5.95831 14.53 5.95831 14.2133V11.8601C5.95831 11.5434 6.20082 11.2867 6.49998 11.2867Z" fill="currentColor"/>`}
|
|
1338
1569
|
};
|
|
1339
1570
|
ICONS.minimize = ICONS.chevron;
|
|
1571
|
+
// right chevron — exact Figma artwork (581:2828 "Chevron small down (Stroke)")
|
|
1572
|
+
// rotated -90° about its centre and centred in a 16px box.
|
|
1573
|
+
ICONS.chevronr = {vb:"0 0 16 16", svg:`<g transform="translate(8,8) rotate(-90) translate(-3.75,-2.25)"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.21967 0.21967C0.512563 -0.0732233 0.987437 -0.0732233 1.28033 0.21967L3.75 2.68934L6.21967 0.21967C6.51256 -0.0732233 6.98744 -0.0732233 7.28033 0.21967C7.57322 0.512563 7.57322 0.987437 7.28033 1.28033L4.28033 4.28033C3.98744 4.57322 3.51256 4.57322 3.21967 4.28033L0.21967 1.28033C-0.0732233 0.987437 -0.0732233 0.512563 0.21967 0.21967Z" fill="currentColor"/></g>`};
|
|
1340
1574
|
ICONS.close = {vb:"0 0 16 16", svg:`<path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>`};
|
|
1575
|
+
ICONS.play = {vb:"0 0 16 16", svg:`<path d="M5 3.5L12 8L5 12.5V3.5Z" fill="currentColor"/>`};
|
|
1576
|
+
ICONS.pause = {vb:"0 0 16 16", svg:`<path d="M5 3.5H6.8V12.5H5V3.5ZM9.2 3.5H11V12.5H9.2V3.5Z" fill="currentColor"/>`};
|
|
1341
1577
|
// dots-vertical (Figma node 580:4819) — vertical "⋮" overflow icon
|
|
1342
1578
|
ICONS.dotsv = {vb:"0 0 16 16", svg:`<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 8C6.66667 7.26362 7.26362 6.66667 8 6.66667C8.73638 6.66667 9.33333 7.26362 9.33333 8C9.33333 8.73638 8.73638 9.33333 8 9.33333C7.26362 9.33333 6.66667 8.73638 6.66667 8Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 3.33333C6.66667 2.59695 7.26362 2 8 2C8.73638 2 9.33333 2.59695 9.33333 3.33333C9.33333 4.06971 8.73638 4.66667 8 4.66667C7.26362 4.66667 6.66667 4.06971 6.66667 3.33333Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 12.6667C6.66667 11.9303 7.26362 11.3333 8 11.3333C8.73638 11.3333 9.33333 11.9303 9.33333 12.6667C9.33333 13.403 8.73638 14 8 14C7.26362 14 6.66667 13.403 6.66667 12.6667Z" fill="currentColor"/>`};
|
|
1579
|
+
// accept checkmark (Figma node 580:9057) — slimmer 1.5px check centered in a
|
|
1580
|
+
// square 16-frame (the glyph spans ~50% width, like the source icon frame).
|
|
1581
|
+
ICONS.accept = {vb:"0 0 16 16", svg:`<path d="M4 8.4268L6.46155 11.19223L12 4.97001" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>`};
|
|
1343
1582
|
function Ic({name, size=16}){
|
|
1344
1583
|
const ic = ICONS[name];
|
|
1345
1584
|
if(!ic) return null;
|
|
@@ -1351,6 +1590,15 @@
|
|
|
1351
1590
|
style:{display:"block"},dangerouslySetInnerHTML:{__html:ic.svg}});
|
|
1352
1591
|
}
|
|
1353
1592
|
|
|
1593
|
+
// dot-matrix loader (ported from @dotmatrix/dotm-square-14). 25 dots laid out
|
|
1594
|
+
// 5×5; each cell maps to a symmetry group a–h whose opacity @keyframes replays
|
|
1595
|
+
// the source's 0→1→2→3→2→1 frame sequence. Inherits text color via currentColor.
|
|
1596
|
+
const DOTM_GROUPS = "ABCBADEFEDCGHGCBEFEBABCBA";
|
|
1597
|
+
function DotmLoader({className}){
|
|
1598
|
+
return h("span",{className:cx("tl-dotm",className),"aria-hidden":"true"},
|
|
1599
|
+
...DOTM_GROUPS.split("").map((g,i)=>h("span",{key:i,className:"tl-dotm-dot tl-dotm-"+g.toLowerCase()})));
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1354
1602
|
// portaled, origin-aware dropdown surface (transitions.dev menu-dropdown)
|
|
1355
1603
|
function Dropdown({open,onClose,triggerRef,width,align,children}){
|
|
1356
1604
|
const ref=useRef(null);
|
|
@@ -1420,8 +1668,9 @@
|
|
|
1420
1668
|
target);
|
|
1421
1669
|
}
|
|
1422
1670
|
|
|
1423
|
-
function MenuItem({active,disabled,onClick,right,children}){
|
|
1671
|
+
function MenuItem({active,disabled,onClick,left,right,children}){
|
|
1424
1672
|
return h("div",{className:cx("tl-menu-item",disabled&&"disabled"),onClick:disabled?undefined:onClick},
|
|
1673
|
+
left,
|
|
1425
1674
|
h("span",{className:"tl-menu-item-label"},children),
|
|
1426
1675
|
right);
|
|
1427
1676
|
}
|
|
@@ -1481,9 +1730,64 @@
|
|
|
1481
1730
|
},[text]);
|
|
1482
1731
|
return h("span",{ref:setEl,className:"tl-refine-status-text t-shimmer t-text-swap"});
|
|
1483
1732
|
}
|
|
1484
|
-
// Results entrance — transitions.dev texts reveal (18):
|
|
1485
|
-
//
|
|
1486
|
-
function
|
|
1733
|
+
// Results entrance — transitions.dev texts reveal (18): each suggestion card
|
|
1734
|
+
// is a stagger line that rises + unblur in sequence on mount.
|
|
1735
|
+
function resolveSugMember(s, lanes){
|
|
1736
|
+
const direct=(s.patch&&s.patch.member)||s.member;
|
|
1737
|
+
if(direct) return direct;
|
|
1738
|
+
if(!lanes||!s.property||s.property==="all") return null;
|
|
1739
|
+
const matches=lanes.filter(t=>t.property===s.property&&t.member);
|
|
1740
|
+
if(!matches.length) return null;
|
|
1741
|
+
const names=[...new Set(matches.map(t=>t.member))];
|
|
1742
|
+
return names.length===1?names[0]:matches[0].member;
|
|
1743
|
+
}
|
|
1744
|
+
// Apply button — card resize (transitions.dev 01) tweens the width on
|
|
1745
|
+
// Apply -> Applied, and the gray checkmark plays the success check (10):
|
|
1746
|
+
// fade + rotate + Y-bob + blur on the wrapper, stroke-draw on the path.
|
|
1747
|
+
// The same <button> node is reused across states so width can tween.
|
|
1748
|
+
function ApplyBtn({applied,onApply}){
|
|
1749
|
+
const ref=useRef(null);
|
|
1750
|
+
const lastW=useRef(0); // width captured while showing "Apply"
|
|
1751
|
+
const prev=useRef(applied);
|
|
1752
|
+
useLayoutEffect(()=>{
|
|
1753
|
+
const btn=ref.current;if(!btn)return;
|
|
1754
|
+
const justApplied=applied&&!prev.current;
|
|
1755
|
+
if(applied){
|
|
1756
|
+
const chk=btn.querySelector(".tl-sug-check");
|
|
1757
|
+
if(chk){
|
|
1758
|
+
const path=chk.querySelector("svg path");
|
|
1759
|
+
if(path){ const len=Math.ceil(path.getTotalLength()); // exact path length
|
|
1760
|
+
path.style.strokeDasharray=len; path.style.strokeDashoffset=len; }
|
|
1761
|
+
if(justApplied){
|
|
1762
|
+
// card-resize: explicit from(old "Apply") -> to(new auto) width tween
|
|
1763
|
+
const to=btn.getBoundingClientRect().width, from=lastW.current;
|
|
1764
|
+
if(from&&to&&Math.abs(from-to)>0.5){
|
|
1765
|
+
btn.style.width=from+"px";
|
|
1766
|
+
void btn.offsetWidth; // reflow so the tween starts from `from`
|
|
1767
|
+
btn.style.width=to+"px";
|
|
1768
|
+
const onEnd=(e)=>{ if(e.target===btn&&e.propertyName==="width"){
|
|
1769
|
+
btn.style.width=""; btn.removeEventListener("transitionend",onEnd); } };
|
|
1770
|
+
btn.addEventListener("transitionend",onEnd);
|
|
1771
|
+
}
|
|
1772
|
+
// success-check replay: reset, force reflow, play from offset 0
|
|
1773
|
+
chk.setAttribute("data-state","out");
|
|
1774
|
+
void chk.offsetWidth;
|
|
1775
|
+
chk.setAttribute("data-state","in");
|
|
1776
|
+
} else { chk.setAttribute("data-state","in"); }
|
|
1777
|
+
}
|
|
1778
|
+
} else { lastW.current=btn.getBoundingClientRect().width; }
|
|
1779
|
+
prev.current=applied;
|
|
1780
|
+
});
|
|
1781
|
+
if(applied) return h("button",{ref,className:"tl-pill-btn tl-apply-btn is-applied",disabled:true},
|
|
1782
|
+
h("span",{className:"tl-sug-check","data-state":"out","aria-hidden":"true"},
|
|
1783
|
+
h("svg",{width:13,height:13,viewBox:"0 0 16 16",fill:"none",
|
|
1784
|
+
xmlns:"http://www.w3.org/2000/svg",style:{display:"block"}},
|
|
1785
|
+
h("path",{d:"M4 8.4268L6.46155 11.19223L12 4.97001",stroke:"currentColor",
|
|
1786
|
+
strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"}))),
|
|
1787
|
+
"Applied");
|
|
1788
|
+
return h("button",{ref,className:"tl-pill-btn tl-apply-btn",onClick:onApply},"Apply");
|
|
1789
|
+
}
|
|
1790
|
+
function RefineResults({suggestions,appliedIds,onApply,lanes}){
|
|
1487
1791
|
const ref=useRef(null);
|
|
1488
1792
|
useEffect(()=>{
|
|
1489
1793
|
const el=ref.current;if(!el)return;
|
|
@@ -1492,27 +1796,45 @@
|
|
|
1492
1796
|
el.classList.add("is-shown");
|
|
1493
1797
|
},[]);
|
|
1494
1798
|
const delay=(i)=>({transitionDelay:"calc(var(--stagger-stagger) * "+i+")"});
|
|
1799
|
+
// category chip label + color modifier, keyed by the suggestion kind
|
|
1800
|
+
const CAT={easing:["Easing","is-easing"],duration:["Duration","is-duration"],
|
|
1801
|
+
delay:["Delay","is-delay"],scale:["Scale","is-scale"],distance:["Distance","is-distance"],
|
|
1802
|
+
replace:["Transition replacement","is-replace"]};
|
|
1803
|
+
const cap=(p)=>p&&p!=="all"?p.charAt(0).toUpperCase()+p.slice(1):"";
|
|
1495
1804
|
let i=0;
|
|
1496
1805
|
return h("div",{ref,className:"tl-refine-results t-stagger"},
|
|
1497
|
-
summary&&h("div",{className:"tl-refine-summary t-stagger-line",style:delay(i++)},summary),
|
|
1498
1806
|
suggestions.map(s=>{
|
|
1499
1807
|
const applied=!!appliedIds[s.id];
|
|
1808
|
+
const isReplace=s.kind==="replace";
|
|
1809
|
+
const member=resolveSugMember(s,lanes);
|
|
1810
|
+
const prop=cap(s.property);
|
|
1811
|
+
const cat=CAT[s.kind]||CAT.easing;
|
|
1812
|
+
const applyBtn=h(ApplyBtn,{applied,onApply:()=>onApply(s)});
|
|
1500
1813
|
return h("div",{className:"tl-sug t-stagger-line",key:s.id,style:delay(i++)},
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1814
|
+
// top row: member chip + property, or a single heading for replace cards
|
|
1815
|
+
h("div",{className:"tl-sug-head"},
|
|
1816
|
+
isReplace
|
|
1817
|
+
? h("span",{className:"tl-sug-prop"},s.title||"Transition replacement")
|
|
1818
|
+
: h(React.Fragment,null,
|
|
1819
|
+
member&&h("span",{className:"tl-sug-member"},member),
|
|
1820
|
+
prop&&h("span",{className:"tl-sug-prop"},prop))),
|
|
1821
|
+
h("div",{className:"tl-sug-divider"}),
|
|
1822
|
+
h("span",{className:cx("tl-sug-cat",cat[1])},cat[0]),
|
|
1823
|
+
h("div",{className:"tl-sug-body"},
|
|
1824
|
+
h("div",{className:"tl-sug-text"},
|
|
1825
|
+
isReplace
|
|
1826
|
+
? h("div",{className:"tl-sug-title"},
|
|
1827
|
+
"Current transition",h("br"),"\u2192 "+(s.title||"transition")+" from transitions.dev")
|
|
1828
|
+
: h(React.Fragment,null,
|
|
1829
|
+
s.title&&h("div",{className:"tl-sug-title"},s.title),
|
|
1830
|
+
(s.from||s.to)&&h("div",{className:"tl-sug-delta"},
|
|
1831
|
+
s.from&&h("span",{className:"tl-sug-from"},s.from),
|
|
1832
|
+
s.to&&h("span",{className:"tl-sug-to"},s.to))),
|
|
1833
|
+
s.reason&&h("div",{className:"tl-sug-reason"},s.reason)),
|
|
1834
|
+
applyBtn));
|
|
1513
1835
|
}));
|
|
1514
1836
|
}
|
|
1515
|
-
function RefinePanel({open,onClose,phase,label,refineType,onType,suggestions,summary,error,appliedIds,onApply,onApplyAll,mode,onMode,llmAvailable,cliInstalled,onStart}){
|
|
1837
|
+
function RefinePanel({open,onClose,phase,label,refineType,onType,suggestions,summary,error,appliedIds,onApply,onApplyAll,mode,onMode,llmAvailable,cliInstalled,onStart,lanes}){
|
|
1516
1838
|
// mount-on-open; keep mounted through the panel-reveal slide-out, then unmount
|
|
1517
1839
|
const[render,setRender]=useState(open);
|
|
1518
1840
|
const[panelOpen,setPanelOpen]=useState(false);
|
|
@@ -1562,6 +1884,12 @@
|
|
|
1562
1884
|
const agentReady = !agentMode || llmAvailable!==false;
|
|
1563
1885
|
const typeDesc = (REFINE_TYPES.find(t=>t.key===refineType)||REFINE_TYPES[0]).desc;
|
|
1564
1886
|
const modeLabel = (REFINE_MODES.find(m=>m.key===mode)||REFINE_MODES[0]).label;
|
|
1887
|
+
// Idle empty-state copy is mode-aware: Agent (llm) keeps the type's
|
|
1888
|
+
// agent-oriented desc; Deterministic has no agent, so it describes the
|
|
1889
|
+
// math-snap behavior (and that "replace" needs Agent mode).
|
|
1890
|
+
const idleDesc = agentMode ? typeDesc : (refineType==="replace"
|
|
1891
|
+
? "Deterministic mode can't pick a recipe to replace the transition — switch to Agent mode for replacements."
|
|
1892
|
+
: "Deterministic mode snaps your transition's timing to the nearest Transitions.dev motion tokens — mathematically, with no agent or credits.");
|
|
1565
1893
|
// One persistent control for the whole foot: in idle/done/error it's the
|
|
1566
1894
|
// pill button; while scanning it carries `.is-scanning` and the same DOM
|
|
1567
1895
|
// node morphs (card resize) into the loading rectangle with the border-beam.
|
|
@@ -1572,17 +1900,17 @@
|
|
|
1572
1900
|
return h("button",{className:cx("tl-scan-morph","t-resize",scanning&&"is-scanning"),
|
|
1573
1901
|
disabled:(!!o.disabled)||scanning,"aria-busy":scanning?"true":undefined,
|
|
1574
1902
|
onClick:(o.disabled||scanning)?undefined:onStart},
|
|
1575
|
-
scanning&&h(BorderBeam,{size:"md",colorVariant:"ocean",theme:"light",
|
|
1903
|
+
scanning&&h(BorderBeam,{size:"md",colorVariant:"ocean",theme:"light",duration:2.6,saturation:2,brightness:1.6,className:"tl-scan-beam"},
|
|
1576
1904
|
h("span",{className:"tl-scan-beam-fill"})),
|
|
1577
1905
|
h("span",{className:"tl-scan-content"},
|
|
1578
1906
|
h("span",{className:"tl-scan-face tl-scan-face-label","aria-hidden":scanning?"true":undefined},
|
|
1579
1907
|
h("span",{className:"tl-scan-label"},o.label||"Start scanning")),
|
|
1580
1908
|
h("span",{className:"tl-scan-face tl-scan-face-status","aria-hidden":scanning?undefined:"true"},
|
|
1581
|
-
h(
|
|
1909
|
+
h(DotmLoader),
|
|
1582
1910
|
h(ShimmerSwapText,{text:REFINE_STATUS[statusIx]}))));
|
|
1583
1911
|
};
|
|
1584
1912
|
|
|
1585
|
-
let body, foot;
|
|
1913
|
+
let body, foot, footBar=false;
|
|
1586
1914
|
if(phase==="scanning"){
|
|
1587
1915
|
body = h("div",{className:"tl-refine-center"});
|
|
1588
1916
|
foot = startBtn();
|
|
@@ -1600,10 +1928,13 @@
|
|
|
1600
1928
|
h("p",{className:"tl-refine-idle-text"},emptyMsg));
|
|
1601
1929
|
foot = startBtn({label:"Scan again"});
|
|
1602
1930
|
} else {
|
|
1603
|
-
body = h(RefineResults,{
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1931
|
+
body = h(RefineResults,{suggestions:visible,appliedIds,onApply,lanes:lanes||[]});
|
|
1932
|
+
footBar = true;
|
|
1933
|
+
foot = h(React.Fragment,null,
|
|
1934
|
+
h("button",{className:"tl-pill-btn",disabled:pending.length===0,
|
|
1935
|
+
onClick:()=>pending.forEach(onApply)},
|
|
1936
|
+
h("span",null,"Apply all ",h("span",{className:"tl-dim"},"("+pending.length+")"))),
|
|
1937
|
+
h("button",{className:"tl-pill-btn",onClick:onStart},"Scan again"));
|
|
1607
1938
|
}
|
|
1608
1939
|
} else { // idle
|
|
1609
1940
|
if(!agentReady){
|
|
@@ -1616,7 +1947,7 @@
|
|
|
1616
1947
|
foot = startBtn({disabled:true});
|
|
1617
1948
|
} else {
|
|
1618
1949
|
body = h("div",{className:"tl-refine-center"},
|
|
1619
|
-
h("p",{className:"tl-refine-idle-text"},
|
|
1950
|
+
h("p",{className:"tl-refine-idle-text"},idleDesc));
|
|
1620
1951
|
foot = startBtn({});
|
|
1621
1952
|
}
|
|
1622
1953
|
}
|
|
@@ -1641,13 +1972,13 @@
|
|
|
1641
1972
|
h("div",{className:"tl-mode-row-main"},
|
|
1642
1973
|
h("div",{className:"tl-mode-row-title"},m.label),
|
|
1643
1974
|
h("div",{className:"tl-mode-row-desc"},m.desc)),
|
|
1644
|
-
(mode===m.key)&&h("span",{className:"tl-mode-row-check"},h(Ic,{name:"
|
|
1975
|
+
(mode===m.key)&&h("span",{className:"tl-mode-row-check"},h(Ic,{name:"accept",size:16}))))),
|
|
1645
1976
|
h("div",{className:"tl-refine-tabs",role:"tablist","aria-label":"Refinement type"},
|
|
1646
1977
|
REFINE_TYPES.map(t=>h("button",{key:t.key,className:"tl-refine-tab",role:"tab",
|
|
1647
1978
|
"aria-selected":refineType===t.key?"true":"false",
|
|
1648
1979
|
onClick:()=>{if(t.key!==refineType)onType(t.key);}},t.label))),
|
|
1649
1980
|
h("div",{className:"tl-refine-body"},body),
|
|
1650
|
-
h("div",{className:"tl-refine-foot"},foot)));
|
|
1981
|
+
h("div",{className:cx("tl-refine-foot",footBar&&"is-bar")},foot)));
|
|
1651
1982
|
}
|
|
1652
1983
|
|
|
1653
1984
|
// Diff the active item's effective (edited/refined) lanes against their
|
|
@@ -1693,10 +2024,10 @@
|
|
|
1693
2024
|
const fFlat=flatItems.filter(e=>!ql||(e.label||"").toLowerCase().includes(ql));
|
|
1694
2025
|
const noMatches=!!ql&&fGroups.length===0&&fFlat.length===0;
|
|
1695
2026
|
const phaseItem=(e)=>h(MenuItem,{key:e.id,active:active&&e.id===active.id,onClick:()=>{onSelect(e.id);setPick(false);},
|
|
1696
|
-
right:active&&e.id===active.id&&h("span",{className:"tl-menu-check"},h(Ic,{name:"
|
|
2027
|
+
right:active&&e.id===active.id&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},
|
|
1697
2028
|
h("span",null,e.phaseLabel,h("span",{className:"tl-menu-dim"}," "+e.durationMs+"ms")));
|
|
1698
2029
|
const flatItem=(e)=>h(MenuItem,{key:e.id,active:active&&e.id===active.id,onClick:()=>{onSelect(e.id);setPick(false);},
|
|
1699
|
-
right:active&&e.id===active.id&&h("span",{className:"tl-menu-check"},h(Ic,{name:"
|
|
2030
|
+
right:active&&e.id===active.id&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},
|
|
1700
2031
|
h("span",null,e.label,h("span",{className:"tl-menu-dim"}," "+e.durationMs+"ms")));
|
|
1701
2032
|
return h("div",{className:"tl-header"},
|
|
1702
2033
|
h("span",{className:"tl-header-label"},"Selected"),
|
|
@@ -1720,14 +2051,24 @@
|
|
|
1720
2051
|
fFlat.length>0&&fGroups.length>0&&h("div",{className:"tl-menu-section"},"Ungrouped"),
|
|
1721
2052
|
...fFlat.map(flatItem))),
|
|
1722
2053
|
h("span",{className:"tl-header-count"},
|
|
1723
|
-
scanning
|
|
2054
|
+
scanning
|
|
2055
|
+
? h("span",{className:"tl-scanning-row"},h(DotmLoader),h("span",null,"Scanning transitions…"))
|
|
2056
|
+
: entries.length+" transition"+(entries.length===1?"":"s")+" found"),
|
|
1724
2057
|
h("button",{ref:gearRef,className:cx("tl-icon-btn",setg&&"is-active"),title:"Settings",onClick:()=>setSetg(v=>!v)},h(Ic,{name:"dotsv"})),
|
|
1725
2058
|
h(Dropdown,{open:setg,onClose:()=>setSetg(false),triggerRef:gearRef,width:210,align:"right"},
|
|
1726
|
-
h(
|
|
2059
|
+
h("div",{className:"tl-menu-head"},
|
|
2060
|
+
h("span",null,"Transitions.dev ",h("span",{className:"tl-menu-head-name"},"Refine")),
|
|
2061
|
+
h("span",{className:"tl-menu-head-ver"},REFINE_VERSION)),
|
|
2062
|
+
h("div",{className:"tl-menu-divider"}),
|
|
1727
2063
|
h(MenuItem,{disabled:!active,onClick:()=>{onCopy&&onCopy();setSetg(false);},
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
2064
|
+
left:h("span",{className:"tl-menu-icon"},h(Ic,{name:"copy"})),
|
|
2065
|
+
right:copied&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},copied?"Copied":"Copy values"),
|
|
2066
|
+
h(MenuItem,{disabled:scanning,onClick:()=>{setSetg(false);onRescan&&onRescan();},
|
|
2067
|
+
left:h("span",{className:"tl-menu-icon"},h(Ic,{name:"restart"}))},
|
|
2068
|
+
scanning?"Rescanning…":"Rescan transitions"),
|
|
2069
|
+
h("div",{className:"tl-menu-divider"}),
|
|
2070
|
+
h(MenuItem,{onClick:()=>{setSetg(false);window.open("https://transitions.dev","_blank","noopener");},
|
|
2071
|
+
right:h("span",{className:"tl-menu-chevr"},h(Ic,{name:"chevronr",size:16}))},"Learn more")),
|
|
1731
2072
|
h("span",{className:"t-tt-wrap"},
|
|
1732
2073
|
h("button",{className:"tl-sec-btn t-tt-trigger",disabled:!active,onClick:onReset},"Reset"),
|
|
1733
2074
|
h("span",{className:"t-tt tl-tt-below",role:"tooltip"},"Reset values")),
|
|
@@ -1736,9 +2077,9 @@
|
|
|
1736
2077
|
disabled:!active||acceptDisabled||acceptState==="saving"||acceptState==="done",onClick:onAccept,"aria-label":"Accept changes to your code"},
|
|
1737
2078
|
acceptState==="saving"
|
|
1738
2079
|
? h("span",{className:"tl-accept-spin","aria-hidden":"true"})
|
|
1739
|
-
: h(Ic,{name:"
|
|
2080
|
+
: h(Ic,{name:"accept"}),
|
|
1740
2081
|
h("span",null,acceptState==="done"?"Done":"Accept")),
|
|
1741
|
-
h("span",{className:"t-tt
|
|
2082
|
+
h("span",{className:cx("t-tt","tl-tt-below",acceptState==="error"&&acceptError&&"tl-tt-msg"),role:"tooltip"},
|
|
1742
2083
|
acceptState==="error"&&acceptError?acceptError
|
|
1743
2084
|
:acceptState==="done"?"Saved to your code"
|
|
1744
2085
|
:acceptDisabled?"No changes to save"
|
|
@@ -1867,12 +2208,8 @@
|
|
|
1867
2208
|
!readOnly&&h(Dropdown,{open:menu,onClose:()=>setMenu(false),triggerRef:chevRef,
|
|
1868
2209
|
width:(wrapRef.current&&wrapRef.current.offsetWidth)||220,align:"right"},
|
|
1869
2210
|
(tokens||[]).map(tk=>h(MenuItem,{key:tk.label,active:value===tk.ms,onClick:()=>{onChange(tk.ms);setMenu(false);},
|
|
1870
|
-
right:value===tk.ms&&h("span",{className:"tl-menu-check"},h(Ic,{name:"
|
|
1871
|
-
h("span",{className:"tl-menu-text"},tk.label,h("span",{className:"tl-menu-dim"}," "+tk.ms+"ms")),
|
|
1872
|
-
tk.usage&&h("span",{className:"t-tt-wrap tl-menu-help",
|
|
1873
|
-
onClick:e=>e.stopPropagation(),onMouseDown:e=>e.stopPropagation()},
|
|
1874
|
-
h(Ic,{name:"help",size:13}),
|
|
1875
|
-
h("span",{className:"t-tt tl-tt-usage",role:"tooltip"},tk.usage)))) ),
|
|
2211
|
+
right:value===tk.ms&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},
|
|
2212
|
+
h("span",{className:"tl-menu-text"},tk.label,h("span",{className:"tl-menu-dim"}," "+tk.ms+"ms")))) ),
|
|
1876
2213
|
readOnly&&readOnlyHint&&h("span",{className:"t-tt",role:"tooltip"},readOnlyHint),
|
|
1877
2214
|
);
|
|
1878
2215
|
}
|
|
@@ -1885,6 +2222,131 @@
|
|
|
1885
2222
|
// -0.5..1.5 range) stay visible instead of being clipped at the edges.
|
|
1886
2223
|
const CURVE = { VBW:269, VBH:150, PAD_X:30, PAD_Y:42 };
|
|
1887
2224
|
|
|
2225
|
+
// Unified "Preview" section (Figma node 580:11158) — collapsible, collapsed by
|
|
2226
|
+
// default (transitions.dev · 21-accordion). Holds BOTH the timing curve card
|
|
2227
|
+
// (cubic w/ draggable handles, or the spring curve) AND the animated position
|
|
2228
|
+
// preview: a big blue marker travels along a dashed rail using the live timing
|
|
2229
|
+
// function + duration, pauses, returns, and loops.
|
|
2230
|
+
function Preview({easing, cubic, spring, durationMs, apply}){
|
|
2231
|
+
const trackRef = useRef(null);
|
|
2232
|
+
const dotRef = useRef(null);
|
|
2233
|
+
const svgRef = useRef(null);
|
|
2234
|
+
const [playing, setPlaying] = useState(false);
|
|
2235
|
+
const [open, setOpen] = useState(false);
|
|
2236
|
+
const safeEasing = (!easing || easing.trim()==="") ? "linear" : easing;
|
|
2237
|
+
const dur = Math.max(120, durationMs || 0);
|
|
2238
|
+
|
|
2239
|
+
const { VBW, VBH, PAD_X, PAD_Y } = CURVE;
|
|
2240
|
+
const plotW = VBW - 2*PAD_X, plotH = VBH - 2*PAD_Y;
|
|
2241
|
+
const originX = PAD_X, originY = VBH - PAD_Y, endX = VBW - PAD_X, endY = PAD_Y;
|
|
2242
|
+
const fx = f => PAD_X + f*plotW;
|
|
2243
|
+
const fy = v => (VBH - PAD_Y) - v*plotH;
|
|
2244
|
+
const cb = cubic || [0.25,0.1,0.25,1];
|
|
2245
|
+
const setEasing = useCallback(v=>apply({easing:v, spring:null}),[apply]);
|
|
2246
|
+
|
|
2247
|
+
const startHandleDrag = useCallback((pointIdx, e)=>{
|
|
2248
|
+
e.preventDefault(); e.stopPropagation();
|
|
2249
|
+
const svg = svgRef.current; if(!svg) return;
|
|
2250
|
+
const calc = (clientX, clientY) => {
|
|
2251
|
+
const rect = svg.getBoundingClientRect();
|
|
2252
|
+
const vbx = (clientX - rect.left) / rect.width * VBW;
|
|
2253
|
+
const vby = (clientY - rect.top) / rect.height * VBH;
|
|
2254
|
+
let x = (vbx - PAD_X) / plotW;
|
|
2255
|
+
let y = ((VBH - PAD_Y) - vby) / plotH;
|
|
2256
|
+
x = Math.round(Math.max(0, Math.min(1, x)) * 100) / 100;
|
|
2257
|
+
y = Math.round(Math.max(-0.5, Math.min(1.5, y)) * 100) / 100;
|
|
2258
|
+
const next = [...cb];
|
|
2259
|
+
next[pointIdx*2] = x; next[pointIdx*2+1] = y;
|
|
2260
|
+
setEasing(`cubic-bezier(${next.join(", ")})`);
|
|
2261
|
+
};
|
|
2262
|
+
const onMove = e2 => calc(e2.clientX, e2.clientY);
|
|
2263
|
+
const onUp = () => { window.removeEventListener("mousemove",onMove); window.removeEventListener("mouseup",onUp); };
|
|
2264
|
+
window.addEventListener("mousemove",onMove);
|
|
2265
|
+
window.addEventListener("mouseup",onUp);
|
|
2266
|
+
},[cb,setEasing,plotW,plotH,VBW,VBH,PAD_X,PAD_Y]);
|
|
2267
|
+
|
|
2268
|
+
// spring curve (read-only) — auto-fit so overshoot above 1 stays in frame
|
|
2269
|
+
const springCurve = useMemo(()=>{
|
|
2270
|
+
if(!spring) return null;
|
|
2271
|
+
const sim = simulateSpring(spring.stiffness, spring.damping, spring.mass ?? 1);
|
|
2272
|
+
const vals = sim.values;
|
|
2273
|
+
const hi = Math.max(1, ...vals), lo = Math.min(0, ...vals);
|
|
2274
|
+
const range = (hi - lo) || 1;
|
|
2275
|
+
const sx = i => PAD_X + (i/(vals.length-1))*plotW;
|
|
2276
|
+
const sy = v => (VBH - PAD_Y) - ((v - lo)/range)*plotH;
|
|
2277
|
+
return { d: vals.map((v,i)=>`${i===0?"M":"L"} ${sx(i).toFixed(2)} ${sy(v).toFixed(2)}`).join(" "),
|
|
2278
|
+
yOne: sy(1), yZero: sy(0) };
|
|
2279
|
+
},[spring,plotW,plotH,VBW,VBH,PAD_X,PAD_Y]);
|
|
2280
|
+
|
|
2281
|
+
useEffect(()=>{
|
|
2282
|
+
if(!playing || !open) return;
|
|
2283
|
+
const dot = dotRef.current, track = trackRef.current;
|
|
2284
|
+
if(!dot || !track) return;
|
|
2285
|
+
let cancelled = false, anim = null, timer = null, atRight = false;
|
|
2286
|
+
const GAP = 480, DOT = 28;
|
|
2287
|
+
const travel = ()=> Math.max(0, track.clientWidth - DOT);
|
|
2288
|
+
const step = ()=>{
|
|
2289
|
+
if(cancelled) return;
|
|
2290
|
+
const from = atRight ? travel() : 0;
|
|
2291
|
+
const to = atRight ? 0 : travel();
|
|
2292
|
+
try {
|
|
2293
|
+
anim = dot.animate(
|
|
2294
|
+
[{transform:`translateX(${from}px)`},{transform:`translateX(${to}px)`}],
|
|
2295
|
+
{duration:dur, easing:safeEasing, fill:"forwards"});
|
|
2296
|
+
} catch {
|
|
2297
|
+
anim = dot.animate(
|
|
2298
|
+
[{transform:`translateX(${from}px)`},{transform:`translateX(${to}px)`}],
|
|
2299
|
+
{duration:dur, easing:"linear", fill:"forwards"});
|
|
2300
|
+
}
|
|
2301
|
+
anim.onfinish = ()=>{ if(cancelled) return; atRight = !atRight; timer = setTimeout(step, GAP); };
|
|
2302
|
+
};
|
|
2303
|
+
step();
|
|
2304
|
+
return ()=>{ cancelled = true; if(anim){try{anim.cancel();}catch{}} if(timer)clearTimeout(timer); };
|
|
2305
|
+
},[playing, open, safeEasing, dur]);
|
|
2306
|
+
|
|
2307
|
+
const p1x = fx(cb[0]), p1y = fy(cb[1]), p2x = fx(cb[2]), p2y = fy(cb[3]);
|
|
2308
|
+
|
|
2309
|
+
return h("div",{className:"tl-preview","data-open":open?"true":"false"},
|
|
2310
|
+
h("button",{className:"tl-preview-head","aria-expanded":open,onClick:()=>setOpen(o=>!o)},
|
|
2311
|
+
h("span",{className:"tl-preview-title"},"Preview"),
|
|
2312
|
+
h("span",{className:"tl-preview-chevron"},h(Ic,{name:"chevron",size:16})),
|
|
2313
|
+
),
|
|
2314
|
+
h("div",{className:"tl-preview-panel"},
|
|
2315
|
+
h("div",{className:"tl-preview-panel-inner"},
|
|
2316
|
+
h("div",{className:"tl-preview-body"},
|
|
2317
|
+
h("div",{className:"tl-curve tl-preview-curve"},
|
|
2318
|
+
spring
|
|
2319
|
+
? h("svg",{viewBox:`0 0 ${VBW} ${VBH}`},
|
|
2320
|
+
h("line",{x1:PAD_X,y1:springCurve.yOne,x2:VBW-PAD_X,y2:springCurve.yOne,stroke:"#1A7AFF",strokeWidth:1,strokeDasharray:"3 4",style:{opacity:0.5,pointerEvents:"none"}}),
|
|
2321
|
+
h("line",{x1:PAD_X,y1:springCurve.yZero,x2:VBW-PAD_X,y2:springCurve.yZero,stroke:"rgba(0,0,0,0.18)",strokeWidth:1,strokeDasharray:"3 4",style:{pointerEvents:"none"}}),
|
|
2322
|
+
h("path",{d:springCurve.d,fill:"none",stroke:"#181a1e",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),
|
|
2323
|
+
)
|
|
2324
|
+
: h("svg",{ref:svgRef,viewBox:`0 0 ${VBW} ${VBH}`},
|
|
2325
|
+
h("path",{d:`M ${originX} ${originY} C ${p1x} ${p1y}, ${p2x} ${p2y}, ${endX} ${endY}`,
|
|
2326
|
+
fill:"none",stroke:"#181a1e",strokeWidth:2,strokeLinecap:"round"}),
|
|
2327
|
+
h("line",{x1:p1x,y1:p1y,x2:originX,y2:originY,stroke:"#1A7AFF",strokeWidth:2,strokeLinecap:"round",style:{pointerEvents:"none"}}),
|
|
2328
|
+
h("line",{x1:p2x,y1:p2y,x2:endX,y2:endY,stroke:"#1A7AFF",strokeWidth:2,strokeLinecap:"round",style:{pointerEvents:"none"}}),
|
|
2329
|
+
h("circle",{cx:p1x,cy:p1y,r:5.5,fill:"#1A7AFF",style:{pointerEvents:"none",filter:"drop-shadow(0 1px 3px rgba(0,0,0,.25))"}}),
|
|
2330
|
+
h("circle",{cx:p2x,cy:p2y,r:5.5,fill:"#1A7AFF",style:{pointerEvents:"none",filter:"drop-shadow(0 1px 3px rgba(0,0,0,.25))"}}),
|
|
2331
|
+
h("circle",{className:"tl-curve-handle",cx:p1x,cy:p1y,r:9,fill:"transparent",onMouseDown:e=>startHandleDrag(0,e)}),
|
|
2332
|
+
h("circle",{className:"tl-curve-handle",cx:p2x,cy:p2y,r:9,fill:"transparent",onMouseDown:e=>startHandleDrag(1,e)}),
|
|
2333
|
+
),
|
|
2334
|
+
),
|
|
2335
|
+
h("div",{className:"tl-preview-sub"},
|
|
2336
|
+
h("span",{className:"tl-preview-sub-label"},"Position preview"),
|
|
2337
|
+
h("button",{className:"tl-preview-play",onClick:()=>setPlaying(p=>!p)},playing?"Pause":"Play"),
|
|
2338
|
+
),
|
|
2339
|
+
h("div",{className:"tl-preview-track"},
|
|
2340
|
+
h("div",{className:"tl-preview-rail-wrap",ref:trackRef},
|
|
2341
|
+
h("span",{className:"tl-preview-rail"}),
|
|
2342
|
+
h("span",{className:"tl-preview-dot",ref:dotRef})),
|
|
2343
|
+
),
|
|
2344
|
+
),
|
|
2345
|
+
),
|
|
2346
|
+
),
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
|
|
1888
2350
|
function EasingEditor({easing, cubic, spring, durationMs, propKey, apply}){
|
|
1889
2351
|
const [tab, setTab] = useState(spring ? "springs" : "easing");
|
|
1890
2352
|
// when the user selects a different property, reflect that property's mode
|
|
@@ -1927,6 +2389,7 @@
|
|
|
1927
2389
|
});
|
|
1928
2390
|
|
|
1929
2391
|
return h("div",{className:"tl-ease"},
|
|
2392
|
+
h("div",{className:"tl-inspector-divider"}),
|
|
1930
2393
|
h("div",{className:"tl-seg",role:"tablist"},
|
|
1931
2394
|
h("button",{className:cx("tl-seg-btn",tab==="easing"&&"is-active"),role:"tab",
|
|
1932
2395
|
"aria-selected":tab==="easing",onClick:()=>selectTab("easing")},"Easing"),
|
|
@@ -1939,6 +2402,7 @@
|
|
|
1939
2402
|
h("div",{className:"t-page",ref:springPageRef,"data-page-id":"2","aria-hidden":tab!=="springs"},
|
|
1940
2403
|
h(SpringTab,{spring,applySpring})),
|
|
1941
2404
|
),
|
|
2405
|
+
h(Preview,{easing, cubic, spring, durationMs, apply}),
|
|
1942
2406
|
);
|
|
1943
2407
|
}
|
|
1944
2408
|
|
|
@@ -2010,36 +2474,63 @@
|
|
|
2010
2474
|
? h("div",{key:"g"+i,className:"tl-menu-group"},o.group)
|
|
2011
2475
|
: h(MenuItem,{key:"e"+i,active:selEaseIdx>=0?i===selEaseIdx:(o.value===mode),
|
|
2012
2476
|
onClick:()=>pickEase(o.value),
|
|
2013
|
-
right:(i===selEaseIdx)&&h("span",{className:"tl-menu-check"},h(Ic,{name:"
|
|
2014
|
-
h("span",{className:"tl-menu-text"},o.label),
|
|
2015
|
-
o.usage&&h("span",{className:"t-tt-wrap tl-menu-help",
|
|
2016
|
-
onClick:e=>e.stopPropagation(),onMouseDown:e=>e.stopPropagation()},
|
|
2017
|
-
h(Ic,{name:"help",size:13}),
|
|
2018
|
-
h("span",{className:"t-tt tl-tt-usage",role:"tooltip"},o.usage))))),
|
|
2477
|
+
right:(i===selEaseIdx)&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},
|
|
2478
|
+
h("span",{className:"tl-menu-text"},o.label)))),
|
|
2019
2479
|
h("div",{className:"tl-cubic-row"},
|
|
2020
|
-
...[0,1,2,3].map(i=>h("
|
|
2021
|
-
|
|
2480
|
+
...[0,1,2,3].map(i=>h("div",{key:i,className:"tl-cubic-cell"},
|
|
2481
|
+
h("input",{type:"number",step:0.05,min:i%2===0?0:undefined,max:i%2===0?1:undefined,
|
|
2482
|
+
value:cb[i],onChange:e=>setCubicVal(i,e.target.value)}))),
|
|
2022
2483
|
),
|
|
2023
2484
|
mode==="__custom" && h("input",{className:"tl-custom-input",value:easing,
|
|
2024
2485
|
placeholder:"e.g. steps(4, end)",
|
|
2025
2486
|
onChange:e=>setEasing(e.target.value)}),
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// Spring physics scrubber — reuses the exact Figma "Value slider and input"
|
|
2491
|
+
// (.tl-field) component used by Duration/Delay, minus the chevron stepper.
|
|
2492
|
+
// Supports fractional values (Mass) via `decimals`.
|
|
2493
|
+
function SpringField({label, value, min, max, step, decimals, onChange}){
|
|
2494
|
+
const trackRef = useRef(null);
|
|
2495
|
+
const inputRef = useRef(null);
|
|
2496
|
+
const [editing,setEditing]=useState(false);
|
|
2497
|
+
const [draft,setDraft]=useState("");
|
|
2498
|
+
const [dragging,setDragging]=useState(false);
|
|
2499
|
+
const pct = Math.min(Math.max((value - min) / (max - min), 0), 1) * 100;
|
|
2500
|
+
const fmt = v => decimals ? Number(v).toFixed(decimals) : String(Math.round(v));
|
|
2501
|
+
const clampSnap = raw => {
|
|
2502
|
+
const snapped = Math.round(raw / step) * step;
|
|
2503
|
+
const c = Math.max(min, Math.min(max, snapped));
|
|
2504
|
+
const p = Math.pow(10, decimals || 0);
|
|
2505
|
+
return Math.round(c * p) / p;
|
|
2506
|
+
};
|
|
2507
|
+
const setFromX = useCallback(clientX => {
|
|
2508
|
+
if(!trackRef.current) return;
|
|
2509
|
+
const rect = trackRef.current.getBoundingClientRect();
|
|
2510
|
+
const ratio = Math.max(0, Math.min((clientX - rect.left) / rect.width, 1));
|
|
2511
|
+
onChange(clampSnap(min + ratio * (max - min)));
|
|
2512
|
+
},[min,max,step,decimals,onChange]);
|
|
2513
|
+
const startDrag = useCallback(e=>{
|
|
2514
|
+
if(editing) return;
|
|
2515
|
+
e.preventDefault(); setDragging(true); setFromX(e.clientX);
|
|
2516
|
+
const onMove = e2 => setFromX(e2.clientX);
|
|
2517
|
+
const onUp = () => { setDragging(false); window.removeEventListener("mousemove",onMove); window.removeEventListener("mouseup",onUp); };
|
|
2518
|
+
window.addEventListener("mousemove",onMove); window.addEventListener("mouseup",onUp);
|
|
2519
|
+
},[setFromX,editing]);
|
|
2520
|
+
const selectAll = useCallback(e=>{ const el=e&&e.target?e.target:inputRef.current; if(!el)return; requestAnimationFrame(()=>el.select()); },[]);
|
|
2521
|
+
const beginEdit = useCallback(e=>{ e.stopPropagation(); setDraft(fmt(value)); setEditing(true); },[value,decimals]);
|
|
2522
|
+
const commit = useCallback(()=>{ const n=parseFloat(draft); if(!isNaN(n)) onChange(clampSnap(n)); setEditing(false); },[draft,min,max,step,decimals,onChange]);
|
|
2523
|
+
return h("div",{className:cx("tl-field-wrap","tl-spring-field")},
|
|
2524
|
+
h("div",{className:cx("tl-field",dragging&&"is-dragging",editing&&"is-editing")},
|
|
2525
|
+
h("div",{className:"tl-field-fill",style:{width:pct+"%"}},
|
|
2526
|
+
h("div",{className:"tl-field-thumb"})),
|
|
2527
|
+
h("div",{ref:trackRef,className:"tl-field-track",onMouseDown:startDrag}),
|
|
2528
|
+
h("span",{className:"tl-field-label"},label),
|
|
2529
|
+
editing
|
|
2530
|
+
? h("input",{ref:inputRef,className:"tl-field-input",autoFocus:true,value:draft,
|
|
2531
|
+
onChange:e=>setDraft(e.target.value),onBlur:commit,onFocus:selectAll,
|
|
2532
|
+
onKeyDown:e=>{if(e.key==="Enter")commit();if(e.key==="Escape")setEditing(false);}})
|
|
2533
|
+
: h("span",{className:"tl-field-value",onMouseDown:e=>e.stopPropagation(),onClick:beginEdit},fmt(value)),
|
|
2043
2534
|
),
|
|
2044
2535
|
);
|
|
2045
2536
|
}
|
|
@@ -2060,17 +2551,6 @@
|
|
|
2060
2551
|
applySpring(next.stiffness, next.damping, next.mass, matchSpringPreset(next));
|
|
2061
2552
|
},[stiffness,damping,mass,applySpring]);
|
|
2062
2553
|
|
|
2063
|
-
// spring curve — auto-fit vertically so overshoot above 1 stays in frame.
|
|
2064
|
-
const { VBW, VBH, PAD_X, PAD_Y } = CURVE;
|
|
2065
|
-
const plotW = VBW - 2*PAD_X, plotH = VBH - 2*PAD_Y;
|
|
2066
|
-
const vals = sim.values;
|
|
2067
|
-
const hi = Math.max(1, ...vals), lo = Math.min(0, ...vals);
|
|
2068
|
-
const range = (hi - lo) || 1;
|
|
2069
|
-
const sx = i => PAD_X + (i/(vals.length-1))*plotW;
|
|
2070
|
-
const sy = v => (VBH - PAD_Y) - ((v - lo)/range)*plotH;
|
|
2071
|
-
const yOne = sy(1), yZero = sy(0);
|
|
2072
|
-
const curveD = vals.map((v,i)=>`${i===0?"M":"L"} ${sx(i).toFixed(2)} ${sy(v).toFixed(2)}`).join(" ");
|
|
2073
|
-
|
|
2074
2554
|
return h(React.Fragment,null,
|
|
2075
2555
|
h("button",{ref:spRef,className:cx("tl-select",spOpen&&"is-open"),onClick:()=>setSpOpen(v=>!v)},
|
|
2076
2556
|
h("span",{className:"tl-select-label"},presetName==="Custom"?"Custom spring":presetName),
|
|
@@ -2079,51 +2559,19 @@
|
|
|
2079
2559
|
width:Math.max(248,(spRef.current&&spRef.current.offsetWidth)||248),align:"left"},
|
|
2080
2560
|
SPRING_PRESETS.map(p=>h(MenuItem,{key:p.label,active:p.label===presetName,
|
|
2081
2561
|
onClick:()=>{applySpring(p.stiffness,p.damping,p.mass,p.label);setSpOpen(false);},
|
|
2082
|
-
right:p.label===presetName&&h("span",{className:"tl-menu-check"},h(Ic,{name:"
|
|
2083
|
-
h("span",{className:"tl-menu-text"},p.label),
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
h(
|
|
2090
|
-
|
|
2091
|
-
h("label",null,"Stiffness"),
|
|
2092
|
-
h("input",{type:"range",min:20,max:400,step:5,value:stiffness,
|
|
2093
|
-
onChange:e=>setParam("stiffness",parseFloat(e.target.value))}),
|
|
2094
|
-
h("span",{className:"tl-bounce-val"},Math.round(stiffness)),
|
|
2095
|
-
),
|
|
2096
|
-
h("div",{className:"tl-bounce-row"},
|
|
2097
|
-
h("label",null,"Damping"),
|
|
2098
|
-
h("input",{type:"range",min:1,max:120,step:1,value:damping,
|
|
2099
|
-
onChange:e=>setParam("damping",parseFloat(e.target.value))}),
|
|
2100
|
-
h("span",{className:"tl-bounce-val"},Math.round(damping)),
|
|
2101
|
-
),
|
|
2102
|
-
h("div",{className:"tl-bounce-row"},
|
|
2103
|
-
h("label",null,"Mass"),
|
|
2104
|
-
h("input",{type:"range",min:0.2,max:5,step:0.1,value:mass,
|
|
2105
|
-
onChange:e=>setParam("mass",parseFloat(e.target.value))}),
|
|
2106
|
-
h("span",{className:"tl-bounce-val"},mass.toFixed(1)),
|
|
2107
|
-
),
|
|
2108
|
-
h("div",{className:"tl-spring-dur"},
|
|
2109
|
-
h("span",null,"Duration"),
|
|
2110
|
-
h("b",null,"~"+sim.durationMs+"ms"),
|
|
2111
|
-
h("span",{className:"tl-spring-dur-hint"},"· derived"+(overshoots?" · overshoots":"")),
|
|
2112
|
-
),
|
|
2562
|
+
right:p.label===presetName&&h("span",{className:"tl-menu-check"},h(Ic,{name:"accept"}))},
|
|
2563
|
+
h("span",{className:"tl-menu-text"},p.label)))),
|
|
2564
|
+
h("div",{className:"tl-spring-fields"},
|
|
2565
|
+
h(SpringField,{label:"Stiffness",value:stiffness,min:20,max:400,step:5,
|
|
2566
|
+
onChange:v=>setParam("stiffness",v)}),
|
|
2567
|
+
h(SpringField,{label:"Damping",value:damping,min:1,max:120,step:1,
|
|
2568
|
+
onChange:v=>setParam("damping",v)}),
|
|
2569
|
+
h(SpringField,{label:"Mass",value:mass,min:0.2,max:5,step:0.1,decimals:1,
|
|
2570
|
+
onChange:v=>setParam("mass",v)}),
|
|
2113
2571
|
),
|
|
2114
|
-
h("div",{className:"tl-
|
|
2115
|
-
h("
|
|
2116
|
-
|
|
2117
|
-
h("pattern",{id:"tl-spring-dots",width:14,height:14,patternUnits:"userSpaceOnUse",patternTransform:"translate(7 7)"},
|
|
2118
|
-
h("circle",{cx:0,cy:0,r:1,fill:"rgba(0,0,0,0.10)"}))),
|
|
2119
|
-
h("rect",{x:0,y:0,width:VBW,height:VBH,fill:"url(#tl-spring-dots)",style:{pointerEvents:"none"}}),
|
|
2120
|
-
// target (value = 1) and baseline (value = 0) reference lines
|
|
2121
|
-
h("line",{x1:PAD_X,y1:yOne,x2:VBW-PAD_X,y2:yOne,stroke:"#1A7AFF",strokeWidth:1,
|
|
2122
|
-
strokeDasharray:"3 4",style:{opacity:0.5,pointerEvents:"none"}}),
|
|
2123
|
-
h("line",{x1:PAD_X,y1:yZero,x2:VBW-PAD_X,y2:yZero,stroke:"rgba(0,0,0,0.18)",strokeWidth:1,
|
|
2124
|
-
strokeDasharray:"3 4",style:{pointerEvents:"none"}}),
|
|
2125
|
-
h("path",{d:curveD,fill:"none",stroke:"#181a1e",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),
|
|
2126
|
-
),
|
|
2572
|
+
h("div",{className:"tl-spring-dur"},
|
|
2573
|
+
"Duration ", h("b",null,sim.durationMs+"ms"), " Derived",
|
|
2574
|
+
overshoots&&h("span",{className:"tl-spring-dur-hint"}," · overshoots"),
|
|
2127
2575
|
),
|
|
2128
2576
|
);
|
|
2129
2577
|
}
|
|
@@ -2237,6 +2685,22 @@
|
|
|
2237
2685
|
const{setPropOverride}=usePropOverride();
|
|
2238
2686
|
const{registry}=useContext(TimelineCtx);
|
|
2239
2687
|
const[copied,setCopied]=useState(false);
|
|
2688
|
+
// tiny transient toast (e.g. "Values copied"); {msg,id,closing,loader}
|
|
2689
|
+
// loader toasts (e.g. "Agent accepting changes") are sticky — they stay up
|
|
2690
|
+
// with a dot-matrix loader until dismissed by the caller, no auto-hide.
|
|
2691
|
+
const[toast,setToast]=useState(null);
|
|
2692
|
+
const showToast=useCallback((msg,opts)=>{setToast({msg,id:Date.now(),closing:false,loader:!!(opts&&opts.loader)});},[]);
|
|
2693
|
+
const dismissToast=useCallback(()=>{setToast(t=>t&&!t.closing?{...t,closing:true}:t);},[]);
|
|
2694
|
+
useEffect(()=>{
|
|
2695
|
+
if(!toast||toast.closing||toast.loader)return;
|
|
2696
|
+
const hide=setTimeout(()=>setToast(t=>t&&t.id===toast.id?{...t,closing:true}:t),1800);
|
|
2697
|
+
return()=>clearTimeout(hide);
|
|
2698
|
+
},[toast&&toast.id,toast&&toast.closing,toast&&toast.loader]);
|
|
2699
|
+
useEffect(()=>{
|
|
2700
|
+
if(!toast||!toast.closing)return;
|
|
2701
|
+
const kill=setTimeout(()=>setToast(t=>t&&t.id===toast.id?null:t),360);
|
|
2702
|
+
return()=>clearTimeout(kill);
|
|
2703
|
+
},[toast&&toast.id,toast&&toast.closing]);
|
|
2240
2704
|
const[minimized,setMinimized]=useState(false);
|
|
2241
2705
|
const[panelHeight,setPanelHeight]=useState(440);
|
|
2242
2706
|
const[resizing,setResizing]=useState(false);
|
|
@@ -2267,6 +2731,17 @@
|
|
|
2267
2731
|
setCliInstalled(j.cliInstalled==null?null:!!j.cliInstalled);return j;}
|
|
2268
2732
|
catch{setLlmAvailable(false);setCliInstalled(false);return null;}
|
|
2269
2733
|
},[]);
|
|
2734
|
+
// a live agent = the relay reports an agent it can drive (REFINE_AGENT_CMD
|
|
2735
|
+
// wired OR a `/refine live` loop polling). Until then the panel is gated.
|
|
2736
|
+
const live = llmAvailable===true;
|
|
2737
|
+
// poll /health continuously so the panel reacts when `/refine live` is run
|
|
2738
|
+
// (gate → scanning) or when the agent disconnects (re-gate). 2s cadence.
|
|
2739
|
+
useEffect(()=>{
|
|
2740
|
+
let alive=true;
|
|
2741
|
+
refreshHealth();
|
|
2742
|
+
const id=setInterval(()=>{if(alive)refreshHealth();},2000);
|
|
2743
|
+
return()=>{alive=false;clearInterval(id);};
|
|
2744
|
+
},[refreshHealth]);
|
|
2270
2745
|
// the Refine button toggles the panel; opening drops into the idle state
|
|
2271
2746
|
// (description + Start scanning) rather than auto-scanning.
|
|
2272
2747
|
const openRefine=useCallback(()=>{
|
|
@@ -2289,7 +2764,7 @@
|
|
|
2289
2764
|
if(mode!==refineMode)setRefineMode(mode);
|
|
2290
2765
|
try{
|
|
2291
2766
|
const et=active.effectiveTimings||[];
|
|
2292
|
-
const timings=et.map(t=>({property:t.property,durationMs:t.durationMs,delayMs:t.delayMs,easing:t.easing}));
|
|
2767
|
+
const timings=et.map(t=>({property:t.property,member:t.member||null,durationMs:t.durationMs,delayMs:t.delayMs,easing:t.easing}));
|
|
2293
2768
|
const selector=(active.bindings&&active.bindings.selector)||(et[0]&&et[0].selector)||null;
|
|
2294
2769
|
// always scan for BOTH kinds (token tweaks + a whole-transition replacement)
|
|
2295
2770
|
// in one pass; the tabs just filter which kind they show.
|
|
@@ -2421,8 +2896,9 @@
|
|
|
2421
2896
|
}else{
|
|
2422
2897
|
text="transition: "+et.map(decl).join(",\n ")+";";
|
|
2423
2898
|
}
|
|
2424
|
-
navigator.clipboard.writeText(text).then(()=>{setCopied(true);setTimeout(()=>setCopied(false),1500);});
|
|
2425
|
-
|
|
2899
|
+
navigator.clipboard.writeText(text).then(()=>{setCopied(true);setTimeout(()=>setCopied(false),1500);}).catch(()=>{});
|
|
2900
|
+
showToast("Values copied");
|
|
2901
|
+
},[active,showToast]);
|
|
2426
2902
|
const resetOverrides=useCallback(()=>{if(active)registry.clearOverride(active.id);},[registry,active]);
|
|
2427
2903
|
// Accept → send an "apply" job so the agent writes the edited timings into
|
|
2428
2904
|
// the user's source, then reflect saving / done / error on the button.
|
|
@@ -2455,6 +2931,15 @@
|
|
|
2455
2931
|
},[active]);
|
|
2456
2932
|
// reset Accept feedback when switching transitions
|
|
2457
2933
|
useEffect(()=>{setAcceptState("idle");setAcceptError(null);},[active&&active.id]);
|
|
2934
|
+
// while the agent is applying changes, surface the sticky loader toast
|
|
2935
|
+
// ("Agent accepting changes"); dismiss only when leaving saving (not on
|
|
2936
|
+
// every idle tick — that would kill unrelated toasts like "Values copied").
|
|
2937
|
+
const prevAcceptRef=useRef(acceptState);
|
|
2938
|
+
useEffect(()=>{
|
|
2939
|
+
if(acceptState==="saving"){showToast("Agent accepting changes",{loader:true});}
|
|
2940
|
+
else if(prevAcceptRef.current==="saving"){dismissToast();}
|
|
2941
|
+
prevAcceptRef.current=acceptState;
|
|
2942
|
+
},[acceptState,showToast,dismissToast]);
|
|
2458
2943
|
// The grouped scan asks the agent to read the source and return Open/Close
|
|
2459
2944
|
// phases — an expensive LLM round-trip. So it runs ONLY:
|
|
2460
2945
|
// 1. the first time this page is ever opened (nothing cached), or
|
|
@@ -2527,16 +3012,18 @@
|
|
|
2527
3012
|
registry.clearGroups();
|
|
2528
3013
|
runGroupScan();
|
|
2529
3014
|
},[runGroupScan,GROUP_STORE_KEY,registry]);
|
|
2530
|
-
//
|
|
2531
|
-
//
|
|
2532
|
-
//
|
|
2533
|
-
//
|
|
3015
|
+
// Gate the auto group-scan behind a live agent: the panel stays on the
|
|
3016
|
+
// "Before we start" screen until `/refine live` (or a wired REFINE_AGENT_CMD)
|
|
3017
|
+
// makes the relay report an agent. The first time we see a live agent we run
|
|
3018
|
+
// the resolver once — it settles the flat scan, then re-applies cached groups
|
|
3019
|
+
// (signature match, no agent) or kicks off a fresh agent scan. Rescan is
|
|
3020
|
+
// manual after that.
|
|
2534
3021
|
useEffect(()=>{
|
|
2535
|
-
if(didGroupScanRef.current)return;
|
|
3022
|
+
if(!live||didGroupScanRef.current)return;
|
|
2536
3023
|
didGroupScanRef.current=true;
|
|
2537
3024
|
runGroupScan();
|
|
2538
|
-
|
|
2539
|
-
},[
|
|
3025
|
+
},[live,runGroupScan]);
|
|
3026
|
+
useEffect(()=>()=>{scanTokenRef.current++;},[]);
|
|
2540
3027
|
|
|
2541
3028
|
// whole-component open/close uses the transitions.dev panel reveal:
|
|
2542
3029
|
// keep the panel mounted while it animates, flip data-open on the next
|
|
@@ -2578,23 +3065,58 @@
|
|
|
2578
3065
|
return()=>window.removeEventListener("tl-toggle-panel",onToggle);
|
|
2579
3066
|
},[]);
|
|
2580
3067
|
|
|
3068
|
+
// gate: the panel is unusable until a live agent is connected AND it has
|
|
3069
|
+
// scanned the page's transitions.
|
|
3070
|
+
// loading → first /health probe pending (blank, avoids a text flash)
|
|
3071
|
+
// blocked → no live agent → "Before we start" (run /refine live)
|
|
3072
|
+
// scanning → live agent, scan in flight → "Agent is scanning…"
|
|
3073
|
+
// ready → live + scan done → the real timeline UI
|
|
3074
|
+
const gate = !live
|
|
3075
|
+
? (llmAvailable===null ? "loading" : "blocked")
|
|
3076
|
+
: (groupScanState==="done" ? "ready" : "scanning");
|
|
2581
3077
|
return h(React.Fragment,null,
|
|
2582
3078
|
render&&h("div",{className:"t-panel-slide","data-timeline-panel":true,
|
|
2583
3079
|
"data-open":panelOpen?"true":"false","data-phase":phase,style:{height:panelHeight+"px"}},
|
|
2584
3080
|
h("div",{className:cx("tl-resize-handle",resizing&&"dragging"),onMouseDown:startResize,title:"Drag to resize"}),
|
|
2585
3081
|
h("div",{className:"tl-panel-body"},
|
|
2586
|
-
|
|
2587
|
-
h(
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
3082
|
+
gate==="ready"
|
|
3083
|
+
? h(React.Fragment,null,
|
|
3084
|
+
h("div",{className:"tl-panel-main"},
|
|
3085
|
+
h(Header,{entries,active,onSelect:setActiveId,onReset:resetOverrides,onCopy:copyValues,copied,
|
|
3086
|
+
snap,setSnap,onMinimize:()=>setMinimized(true),onRefine:openRefine,refineActive:refineOpen,
|
|
3087
|
+
onAccept,acceptState,acceptDisabled:computeChanges(active).length===0,acceptError,
|
|
3088
|
+
scanning:groupScanState==="scanning",onRescan:rescanTransitions}),
|
|
3089
|
+
active
|
|
3090
|
+
?h(Body,{entry:active,onPropChange:(prop,o)=>setPropOverride(prop,o),snap})
|
|
3091
|
+
:h("div",{className:"tl-empty"},"Select a transition to inspect and edit it")),
|
|
3092
|
+
h(RefinePanel,{open:refineOpen,onClose:()=>setRefineOpen(false),phase:refinePhase,label:refineLabel,
|
|
3093
|
+
refineType,onType:changeRefineType,suggestions:refineSuggestions,summary:refineSummary,error:refineError,
|
|
3094
|
+
appliedIds,onApply:applySuggestion,onApplyAll:applyAllSuggestions,
|
|
3095
|
+
mode:refineMode,onMode:changeRefineMode,llmAvailable,cliInstalled,onStart:startScan,
|
|
3096
|
+
lanes:active?.effectiveTimings||[]}))
|
|
3097
|
+
: h("div",{className:"tl-panel-main"},
|
|
3098
|
+
gate==="blocked" && h("div",{className:"tl-gate"},
|
|
3099
|
+
h("div",{className:"tl-gate-col"},
|
|
3100
|
+
h("div",{className:"tl-gate-title"},"Before we start"),
|
|
3101
|
+
h("p",{className:"tl-gate-text"},
|
|
3102
|
+
"Please run the ",h("code",{className:"tl-code"},"/refine live"),
|
|
3103
|
+
" command in your agent to enable live features, such as scanning and refining transitions."))),
|
|
3104
|
+
gate==="scanning" && h("div",{className:"tl-gate"},
|
|
3105
|
+
h("div",{className:"tl-gate-col"},
|
|
3106
|
+
h("div",{className:"tl-gate-pill-wrap"},
|
|
3107
|
+
h(BorderBeam,{size:"sm",colorVariant:"ocean",theme:"light",borderRadius:18,duration:2.6,saturation:2,brightness:1.6,className:"tl-gate-beam"},
|
|
3108
|
+
h("span",{className:"tl-gate-beam-fill"})),
|
|
3109
|
+
h("span",{className:"tl-gate-pill"},
|
|
3110
|
+
h(DotmLoader),
|
|
3111
|
+
h("span",{className:"tl-gate-pill-label"},"Agent is scanning your transitions"))),
|
|
3112
|
+
h("p",{className:"tl-gate-sub"},"Just a moment while we get things ready.")))),
|
|
3113
|
+
toast&&createPortal(
|
|
3114
|
+
h("div",{className:"tl-toast-wrap","aria-live":"polite"},
|
|
3115
|
+
h("div",{className:cx("tl-toast",toast.closing&&"is-closing")},
|
|
3116
|
+
h("span",{className:"tl-toast-ic"},
|
|
3117
|
+
toast.loader?h(DotmLoader):h(Ic,{name:"accept",size:14})),
|
|
3118
|
+
h("span",null,toast.msg))),
|
|
3119
|
+
document.body),
|
|
2598
3120
|
),
|
|
2599
3121
|
),
|
|
2600
3122
|
minimized&&h("div",{className:"tl-pill",onClick:()=>setMinimized(false)},
|