import-spinner-frames

Generated source view for the actual executable engineering/import-spinner-frames skill. The durable routing article is Cloud, Data, and Service Skills. Source: skills/engineering/import-spinner-frames/SKILL.md

Runtime Source

Field Value
Category engineering
Origin personal
Slug import-spinner-frames
Source slug import-spinner-frames
Family Cloud, Data, and Service Skills
Source skills/engineering/import-spinner-frames/SKILL.md

Bundled Resources

These files are part of the executable skill folder and must be preserved with the skill source.

File Role
scripts/extract-frames.mjs Executable helper

Description

Extract animation frame data from upstream Unicode spinner libraries into the wiki's runtime-agnostic spinners-catalog.json. Use when Kevin asks to "import spinners from X", "add more spinners", "pull the frames from this repo", evaluates a new spinner library, wants to expand the loading-screens catalog, or asks "should I swap unicode-animations for X?". Handles the full flow: 1v1 evaluation vs the canonical library, shallow clone, frame extraction from TSX/JS component files, JSON normalization, and wiki doc updates.

Skill Source

---
name: import-spinner-frames
description: Extract animation frame data from upstream Unicode spinner libraries into the wiki's runtime-agnostic `spinners-catalog.json`. Use when Kevin asks to "import spinners from X", "add more spinners", "pull the frames from this repo", evaluates a new spinner library, wants to expand the loading-screens catalog, or asks "should I swap unicode-animations for X?". Handles the full flow: 1v1 evaluation vs the canonical library, shallow clone, frame extraction from TSX/JS component files, JSON normalization, and wiki doc updates.
origin: personal
source_slug: import-spinner-frames
---

# Import Spinner Frames

## The Rule

Kevin's canonical spinner library is [`unicode-animations`](https://github.com/gunnargray-dev/unicode-animations)
(npm-installable, framework-agnostic, 18 braille spinners). Do not swap it out. Instead,
when a new spinner library is interesting, **extract the frame data into the wiki's
runtime-agnostic catalog** at `wiki/assets/spinners-catalog.json` and leave the canonical
library untouched.

This keeps `unicode-animations` as the one-line npm install for any project, while giving
every future project agent a palette of extra frames to adopt without taking on a new
dependency (especially important when the upstream is React Native–only, CLI-only, or
otherwise wrong-runtime).

## When to Trigger

- Kevin shares a spinner repo URL and asks for a comparison ("do a 1v1", "which should I use")
- Kevin says "import the spinners from X", "grab those frames", "add these to my catalog"
- Kevin mentions expanding the loading-screens catalog
- A signal-radar or frontier automation surfaces a new spinner library worth cataloguing

## Step 0: 1v1 evaluation (before extracting anything)

Before any git clone, do the comparison. The answer is almost always "keep the canonical
library, extract the frames into the catalog" — but the 1v1 forces an honest look. Check:

| Dimension | Why it matters |
|---|---|
| Target runtime | If it imports from `react-native`, `ink`, `blessed`, etc., it's the wrong stack for Kevin's Next.js web work. Never a library swap. |
| Distribution | npm-installable > copy-paste. Copy-paste libraries can only contribute frame data, not be adopted whole. |
| Spinner count & variety | Count per category (braille / ASCII / arrow / emoji). If it's < 5 new distinct frames, probably skip. |
| Frame data format | Are frames inline as `const FRAMES = [...]`? Good — extractable. If the frames are generated at runtime from shaders/canvas, skip — this skill doesn't apply. |
| License | MIT or compatible. Attribution is mandatory in the catalog JSON. |
| Stars / activity | Not decisive, but a rough health signal. |

Present the comparison as a table in chat. Then confirm with Kevin: extract all? a subset?
which categories? where should it land (wiki-only is the default)?

## Step 1: Shallow clone to /tmp

Never pollute the repo with an upstream clone. Always `/tmp` and never commit.

```bash
mkdir -p /tmp/<repo-slug>
git clone --depth 1 <repo-url> /tmp/<repo-slug>
```

## Step 2: Locate the frame source files

Most spinner libraries use one of two layouts:

1. **One file per spinner** — e.g., `src/components/spinners/*.tsx`, each with
   `const FRAMES = [...]` and `const INTERVAL = N`. (expo-agent-spinners, most React Native
   libraries.)
2. **Single catalog file** — e.g., `src/spinners.ts` with a big object literal mapping
   names to `{ frames, interval }`. (unicode-animations, cli-spinners, ora.)

Read one or two source files first to confirm the format before running the extraction
script. If it's layout 2, the extraction is a single file read + JSON stringify. If it's
layout 1, use the bundled extractor.

## Step 3: Run the extractor

The extractor at `scripts/extract-frames.mjs` handles layout 1. Invoke it with the source
directory and output path:

```bash
node skills/engineering/import-spinner-frames/scripts/extract-frames.mjs \
  --src /tmp/<repo-slug>/src/components/spinners \
  --out /Users/kevinliu/Documents/GitHub/kevin-wiki/wiki/assets/spinners-catalog.json \
  --source-url <repo-url> \
  --license MIT \
  --attribution "frames derived from <owner>/<repo>"
```

The script:

- Walks every `*.tsx` / `*.ts` / `*.jsx` / `*.js` file in the source dir
- Matches `const FRAMES = [...]` (single- or multi-line) and `const INTERVAL = N`
- Uses a safe `Function(...)` eval on the array literal (no expressions, just string lits)
- Writes a JSON object `{ source, license, attribution, extracted, count, spinners: {...} }`
- Each spinner entry is `{ category, frames, interval }` — category defaults to
  `"uncategorized"` and gets set via the optional category map below

### Categorizing

If you know the library's category structure from its README, pass a category map as a
JSON file:

```bash
--categories /tmp/categories.json
```

Where `categories.json` looks like:

```json
{
  "braille": ["dots", "dots2", "sand", ...],
  "ascii":   ["arc", "rolling_line", ...],
  "arrow":   ["arrow", "double_arrow"],
  "emoji":   ["moon", "earth", "hearts"]
}
```

If omitted, everything lands in `"uncategorized"` and you can categorize later by hand
in the JSON.

### Merging with an existing catalog

If `--out` already exists, the script reads it first and merges. Same-name spinners are
overwritten (upstream is authoritative). A run summary is printed showing adds, updates,
and skips.

## Step 4: Verify extraction

Sanity-check the JSON before touching docs:

```bash
node -e 'const c = require("<path>/spinners-catalog.json");
const counts = {}; for (const s of Object.values(c.spinners)) counts[s.category] = (counts[s.category]||0)+1;
console.log("total:", c.count, "by category:", counts);
const pick = Object.keys(c.spinners)[0];
console.log("sample:", pick, "→", c.spinners[pick].frames.slice(0,8).join(" "));'
```

Render a few frames in the terminal to confirm the Unicode didn't get mangled. If
anything looks broken (question-mark boxes, escape sequences), the source file probably
had a multi-character frame split across template literal expressions — re-read that
source file and extract manually.

## Step 5: Update the wiki doc

`wiki/design/loading-screens.md` is the canonical reference. Update:

1. Bump `updated:` to today's date in frontmatter
2. Add any new category tags (e.g., `emoji`, `ascii`) to the `tags:` list
3. Reaffirm `unicode-animations` as the default at the top of the "Braille Spinners"
   section — do not let the expanded catalog displace the canonical recommendation
4. Under the "Expanded Catalog" section:
   - Update the category counts
   - Add/update the "Highlights per category" tables with a representative sample
     (4-8 spinners per category, with preview frames and interval)
   - Keep the selection guide ("When to use what") current
5. Attribute the new library in the Expanded Catalog intro paragraph (name + URL + license)

The JSON is the source of truth for frame data. The doc should not duplicate every
frame — it curates and points agents at the JSON for the full set.

Also update `wiki/skills/loading-screens.md` if a new category appears (ASCII, arrow,
emoji were the initial additions from expo-agent-spinners).

## Step 6: Log it

Append a `[YYYY-MM-DD] asset-add` entry to `wiki/log.md` explaining:

- What was extracted (count, source repo, commit)
- Why the swap was rejected (usually: wrong runtime)
- What landed in the catalog and what changed in the docs
- Attribution + license

## Anti-patterns

- **Adopting the upstream library as a dependency.** Almost never correct. Extract the
  data, keep the canonical.
- **Letting the new library redefine the default.** `unicode-animations` stays default.
  The catalog is a palette, not a replacement.
- **Committing the /tmp clone.** Shallow clone, extract, never add to git.
- **Hand-typing frame arrays.** Use the extractor. Unicode characters silently break
  when transcribed; the extractor reads the source bytes directly.
- **Extracting without a 1v1 comparison.** If the 1v1 would have concluded "adopt this
  instead," the whole catalog-merge approach might be wrong. Don't skip Step 0.
- **Losing attribution.** License + attribution go in the JSON top-level and in the
  doc's Expanded Catalog intro. MIT is permissive but still requires attribution.

## Generalization note

The same extraction pattern works for any component library that inlines animation
frame data: keyframe arrays, ASCII art sequences, easing tables, sprite sheet indices.
If a second use case emerges (e.g., extracting color palettes, icon sets, sound
libraries), generalize this skill at that point — don't pre-generalize now.

Timeline

1 page links here