# 004: Add working/spinning indicators for async operations

**Reporter:** cthegray
**Date:** 2026-03-03
**Priority:** medium
**Status:** open
**Affects:** All platforms, UX

## Description

Icons that trigger async operations (TTS generation, page reading, etc.) give no visual feedback that work is in progress. The user taps and nothing visibly happens until the operation completes (or fails silently).

The book icon for "page read" already has an hourglass indicator. Other icons need the same treatment.

## Requirements

- TTS icon (per-message): show spinning/pulsing animation while audio is being generated
- Audio icon (bottom bar): show spinning/pulsing animation while audio is loading
- Any other async operation icons should show a working state
- Animation should stop when the operation completes or errors
- On error, briefly show an error state (red flash, shake, etc.) before returning to idle

## Design Notes

Keep consistent with the existing hourglass pattern on the book icon. Options:
- CSS spin animation on the icon itself
- Small spinner overlay on the icon
- Icon swap to an animated version during loading
- Pulsing opacity animation (subtle but effective)

## Implementation

CSS animations added to both theme files (`uncloseai-modal-builtin.css`, `uncloseai-modal-pico.css`):
- `.working` class: pulse animation (opacity 1 to 0.4), disables pointer events
- `.uncloseai-spinner` class: continuous rotation for inline spinner elements
- Apply to `.uncloseai-btn-small` or `.uncloseai-btn-primary` elements

Usage: `btn.classList.add("working")` when async starts, `.remove("working")` when done.

TTS buttons (primary consumers) are currently hidden via feature flag. When TTS is re-enabled, wire the `.working` class into the TTS button click handlers.

## Notes

CSS is ready. JS wiring deferred until TTS is re-enabled (tickets #002, #003).
