diff --git a/src/components/LogTerminal.svelte b/src/components/LogTerminal.svelte index 0b69054..ef53fce 100644 --- a/src/components/LogTerminal.svelte +++ b/src/components/LogTerminal.svelte @@ -86,10 +86,10 @@ {#if activity}
{activity}
diff --git a/src/components/LogTerminal.test.ts b/src/components/LogTerminal.test.ts index 52bc175..9cedffb 100644 --- a/src/components/LogTerminal.test.ts +++ b/src/components/LogTerminal.test.ts @@ -10,7 +10,12 @@ describe("LogTerminal", () => { const label = screen.getByText("audit running"); expect(label.closest(".term-line")).toBe(container.querySelector(".term-line:last-child")); - expect(container.querySelector(".term-spinner")).toHaveAttribute("aria-hidden", "true"); + const spinner = container.querySelector(".term-spinner"); + expect(spinner).toHaveAttribute("aria-hidden", "true"); + expect(spinner?.querySelector(".term-spinner__dot")).toHaveTextContent("•"); + const arcs = spinner?.querySelectorAll(".term-spinner__arc"); + expect(arcs).toHaveLength(3); + for (const arc of arcs ?? []) expect(arc).toHaveTextContent(")"); await rerender({ activity: null }); diff --git a/src/styles.css b/src/styles.css index feef49c..72b898f 100644 --- a/src/styles.css +++ b/src/styles.css @@ -951,34 +951,72 @@ textarea { color: var(--term-fg); } .term-spinner { - display: inline-grid; - flex: 0 0 1ch; + display: inline-flex; + flex: 0 0 4ch; + width: 4ch; +} +.term-spinner__dot, +.term-spinner__arc { + display: inline-block; width: 1ch; + text-align: center; +} +.term-spinner__dot { color: var(--term-done); } -.term-spinner__frame { - grid-area: 1 / 1; +.term-spinner__arc { opacity: 0; - animation: term-spinner-frame 0.8s linear infinite; + animation-duration: 1.2s; + animation-timing-function: step-end; + animation-iteration-count: infinite; +} +.term-spinner__arc--inner { + color: var(--term-external); + animation-name: term-signal-inner; } -.term-spinner__frame--two { - animation-delay: -0.6s; +.term-spinner__arc--middle { + color: var(--term-user); + animation-name: term-signal-middle; } -.term-spinner__frame--three { - animation-delay: -0.4s; +.term-spinner__arc--outer { + color: var(--term-trust); + animation-name: term-signal-outer; } -.term-spinner__frame--four { - animation-delay: -0.2s; +@keyframes term-signal-inner { + 0%, + 19.999%, + 85%, + 100% { + opacity: 0; + } + 20%, + 84.999% { + opacity: 1; + } } -@keyframes term-spinner-frame { +@keyframes term-signal-middle { 0%, - 24.999% { + 39.999%, + 85%, + 100% { + opacity: 0; + } + 40%, + 84.999% { opacity: 1; } - 25%, +} +@keyframes term-signal-outer { + 0%, + 59.999%, + 85%, 100% { opacity: 0; } + 60%, + 84.999% { + opacity: 1; + } } .report-ready { @@ -1071,11 +1109,8 @@ textarea { .toast { animation: none; } - .term-spinner__frame { + .term-spinner__arc { animation: none; - opacity: 0; - } - .term-spinner__frame:first-child { opacity: 1; } }