feat(ui): replace hourglass animation with pickaxe mining swing

Replace the dead hourglass CSS animation with a thematic pickaxe mining
animation. The pickaxe swings from resting position up to -45deg then
strikes down, mimicking a mining motion. Used in the generatingNdebit
loading state; other loading states still use BounceDots pending review.

Refs #33

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-03-14 00:15:59 -04:00
commit de6a9559ff
3 changed files with 24 additions and 33 deletions

View file

@ -526,39 +526,32 @@
animation: float 3s ease-in-out infinite; animation: float 3s ease-in-out infinite;
} }
/* Hourglass 45-degree step rotation */ /* Pickaxe mining swing */
@keyframes hourglass { @keyframes pickaxe {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);
} }
12.5% { 20% {
transform: rotate(45deg); transform: rotate(15deg);
} }
25% { 40% {
transform: rotate(90deg); transform: rotate(-60deg);
} }
37.5% { 45% {
transform: rotate(135deg); transform: rotate(-30deg);
} }
50% { 50% {
transform: rotate(180deg); transform: rotate(-30deg);
}
62.5% {
transform: rotate(225deg);
}
75% {
transform: rotate(270deg);
}
87.5% {
transform: rotate(315deg);
} }
100% { 100% {
transform: rotate(360deg); transform: rotate(0deg);
} }
} }
@utility animate-hourglass { @utility animate-pickaxe {
animation: hourglass 4s steps(8) infinite; animation: pickaxe 0.8s ease-in-out infinite;
transform-origin: bottom right;
display: inline-block;
} }
/* Monospace utility for addresses/codes */ /* Monospace utility for addresses/codes */

View file

@ -6,7 +6,6 @@ import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert' import { Alert, AlertDescription } from '@/components/ui/alert'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import QRCode from '@/components/QRCode.vue' import QRCode from '@/components/QRCode.vue'
import BounceDots from '@/components/BounceDots.vue'
import StepIndicator from '@/components/StepIndicator.vue' import StepIndicator from '@/components/StepIndicator.vue'
const router = useRouter() const router = useRouter()
@ -197,7 +196,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice)
key="fetchingRate" key="fetchingRate"
class="flex flex-1 flex-col items-center justify-center gap-6" class="flex flex-1 flex-col items-center justify-center gap-6"
> >
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-2xl text-muted-foreground">Fetching exchange rate...</p> <p class="text-2xl text-muted-foreground">Fetching exchange rate...</p>
</div> </div>
@ -301,7 +300,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice)
key="generatingNdebit" key="generatingNdebit"
class="flex flex-1 flex-col items-center justify-center gap-6" class="flex flex-1 flex-col items-center justify-center gap-6"
> >
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-2xl text-muted-foreground">Preparing your withdraw code...</p> <p class="text-2xl text-muted-foreground">Preparing your withdraw code...</p>
</div> </div>
@ -327,7 +326,7 @@ const isProcessing = computed(() => atmStore.isPayingInvoice)
<!-- Waiting indicator --> <!-- Waiting indicator -->
<div class="flex items-center gap-3 pt-2 lg:pt-4"> <div class="flex items-center gap-3 pt-2 lg:pt-4">
<BounceDots /> <span class="text-2xl animate-pickaxe">⛏️</span>
<p class="text-sm lg:text-xl text-muted-foreground">Waiting for wallet scan...</p> <p class="text-sm lg:text-xl text-muted-foreground">Waiting for wallet scan...</p>
</div> </div>
@ -365,13 +364,13 @@ const isProcessing = computed(() => atmStore.isPayingInvoice)
class="flex w-full lg:w-[48%] flex-col items-center justify-center bg-foreground/5 p-4 lg:p-6" class="flex w-full lg:w-[48%] flex-col items-center justify-center bg-foreground/5 p-4 lg:p-6"
> >
<div v-if="isGeneratingLnurl && !context?.ndebitUri"> <div v-if="isGeneratingLnurl && !context?.ndebitUri">
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
</div> </div>
<div v-else-if="currentQrValue"> <div v-else-if="currentQrValue">
<QRCode :value="currentQrValue" :size="380" /> <QRCode :value="currentQrValue" :size="380" />
</div> </div>
<div v-else class="flex flex-col items-center gap-4"> <div v-else class="flex flex-col items-center gap-4">
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-xl text-muted-foreground">Generating...</p> <p class="text-xl text-muted-foreground">Generating...</p>
</div> </div>
</div> </div>

View file

@ -6,7 +6,6 @@ import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { Alert, AlertDescription } from '@/components/ui/alert' import { Alert, AlertDescription } from '@/components/ui/alert'
import QRCode from '@/components/QRCode.vue' import QRCode from '@/components/QRCode.vue'
import BounceDots from '@/components/BounceDots.vue'
import StepIndicator from '@/components/StepIndicator.vue' import StepIndicator from '@/components/StepIndicator.vue'
const router = useRouter() const router = useRouter()
@ -163,7 +162,7 @@ function formatFiat(cents: number): string {
key="fetchingRate" key="fetchingRate"
class="flex flex-1 flex-col items-center justify-center gap-6" class="flex flex-1 flex-col items-center justify-center gap-6"
> >
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-2xl text-muted-foreground">Fetching exchange rate...</p> <p class="text-2xl text-muted-foreground">Fetching exchange rate...</p>
</div> </div>
@ -270,7 +269,7 @@ function formatFiat(cents: number): string {
key="generatingInvoice" key="generatingInvoice"
class="flex flex-1 flex-col items-center justify-center gap-6" class="flex flex-1 flex-col items-center justify-center gap-6"
> >
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-2xl text-muted-foreground">Creating invoice...</p> <p class="text-2xl text-muted-foreground">Creating invoice...</p>
</div> </div>
@ -297,7 +296,7 @@ function formatFiat(cents: number): string {
<!-- Waiting indicator --> <!-- Waiting indicator -->
<div class="flex items-center gap-3 pt-2 lg:pt-4"> <div class="flex items-center gap-3 pt-2 lg:pt-4">
<BounceDots /> <span class="text-2xl animate-pickaxe">⛏️</span>
<p class="text-sm lg:text-xl text-muted-foreground">Waiting for payment...</p> <p class="text-sm lg:text-xl text-muted-foreground">Waiting for payment...</p>
</div> </div>
@ -334,7 +333,7 @@ function formatFiat(cents: number): string {
<QRCode :value="context.invoice" :size="380" /> <QRCode :value="context.invoice" :size="380" />
</div> </div>
<div v-else class="flex flex-col items-center gap-4"> <div v-else class="flex flex-col items-center gap-4">
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-xl text-muted-foreground">Generating invoice...</p> <p class="text-xl text-muted-foreground">Generating invoice...</p>
</div> </div>
</div> </div>
@ -346,7 +345,7 @@ function formatFiat(cents: number): string {
key="dispensingCash" key="dispensingCash"
class="flex flex-1 flex-col items-center justify-center gap-6" class="flex flex-1 flex-col items-center justify-center gap-6"
> >
<BounceDots class="scale-150" /> <span class="text-[8vh] animate-pickaxe">⛏️</span>
<p class="text-2xl text-muted-foreground">Dispensing cash...</p> <p class="text-2xl text-muted-foreground">Dispensing cash...</p>
<p class="text-xl text-muted-foreground">Please wait</p> <p class="text-xl text-muted-foreground">Please wait</p>
</div> </div>