File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -107,3 +107,28 @@ body {
107107.animate-scan {
108108 animation : scan 1.6s ease-in-out infinite;
109109}
110+
111+ /* Soft "breath" glow on the idle Run CTA — a slow pulsing halo that says the
112+ button is the thing to click, without the jitter of a hard pulse. */
113+ @keyframes breath {
114+ 0% ,
115+ 100% {
116+ box-shadow : 0 0 0 0 rgba (79 , 70 , 229 , 0.35 );
117+ }
118+ 50% {
119+ box-shadow : 0 0 0 6px rgba (79 , 70 , 229 , 0 );
120+ }
121+ }
122+
123+ .animate-breath {
124+ animation : breath 2.4s ease-in-out infinite;
125+ }
126+
127+ /* Respect users who prefer no motion. */
128+ @media (prefers-reduced-motion : reduce) {
129+ .animate-breath ,
130+ .animate-scan ,
131+ .animate-pulse-ring {
132+ animation : none;
133+ }
134+ }
Original file line number Diff line number Diff line change @@ -58,6 +58,39 @@ function readIntake(
5858 } ;
5959}
6060
61+ /** Solid play triangle for the Run button. */
62+ function PlayIcon ( ) {
63+ return (
64+ < svg aria-hidden viewBox = "0 0 12 12" className = "h-3 w-3 fill-current" >
65+ < path d = "M3 1.8v8.4a.6.6 0 0 0 .92.5l6.4-4.2a.6.6 0 0 0 0-1L3.92 1.3A.6.6 0 0 0 3 1.8Z" />
66+ </ svg >
67+ ) ;
68+ }
69+
70+ /** Small spinner shown while a run is in flight. */
71+ function Spinner ( ) {
72+ return (
73+ < svg aria-hidden viewBox = "0 0 16 16" className = "h-3.5 w-3.5 animate-spin" >
74+ < circle
75+ cx = "8"
76+ cy = "8"
77+ r = "6"
78+ fill = "none"
79+ stroke = "currentColor"
80+ strokeOpacity = "0.3"
81+ strokeWidth = "2"
82+ />
83+ < path
84+ d = "M8 2a6 6 0 0 1 6 6"
85+ fill = "none"
86+ stroke = "currentColor"
87+ strokeWidth = "2"
88+ strokeLinecap = "round"
89+ />
90+ </ svg >
91+ ) ;
92+ }
93+
6194export function Dashboard ( { queue } : { queue : QueueItem [ ] } ) {
6295 const [ selectedId , setSelectedId ] = useState < string | null > (
6396 queue [ 0 ] ?. id ?? null ,
@@ -313,13 +346,21 @@ export function Dashboard({ queue }: { queue: QueueItem[] }) {
313346 data-testid = "run-btn"
314347 disabled = { state . status === "running" }
315348 onClick = { ( ) => run ( selected . id ) }
316- className = "shrink-0 rounded-lg bg-accent px-3.5 py-2 text-[13px] font-medium text-accent-fg shadow-sm transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40"
349+ className = { `flex shrink-0 items-center gap-1.5 rounded-lg bg-accent px-3.5 py-2 text-[13px] font-medium text-accent-fg shadow-sm transition-all hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40 ${
350+ state . status === "idle" ? "animate-breath" : ""
351+ } `}
317352 >
318- { state . status === "running"
319- ? "Running…"
320- : state . status === "idle"
321- ? "Run pipeline"
322- : "Run again" }
353+ { state . status === "running" ? (
354+ < >
355+ < Spinner />
356+ Running…
357+ </ >
358+ ) : (
359+ < >
360+ < PlayIcon />
361+ { state . status === "idle" ? "Run pipeline" : "Run again" }
362+ </ >
363+ ) }
323364 </ button >
324365 )
325366 ) }
You can’t perform that action at this time.
0 commit comments