Phrase Cycle

Discrete phrase-state cycling. Replaces the entire element's text with phrase A, then phrase B, etc., at a fixed interval, then settles on finalText. Useful for loading screens, boot sequences, and "decrypting" preambles. Provided by src/lib/phrase-cycle.js.

3-way distinction: TypingAnimation grows the string char-by-char (streaming/typed, string length increases). DecryptReveal shows the string at final length, scrambled, resolving left-to-right (char-level). PhraseCycle replaces the entire element text each tick — string length may vary between phrases.

Loading Sequence

Cycles through status phrases and settles on a final "ready" message. interval: 400ms.

Loading · Settle

Initializing → Connecting → Authenticating → Ready.

Click Start to begin
const cycle = new PhraseCycle(element, {
  phrases: ['Initializing...', 'Connecting...', 'Authenticating...'],
  interval: 400,
  finalText: 'Ready.',
});
cycle.start();

Boot Sequence

System boot preamble with a welcome message as the final settled state. interval: 500ms.

Boot · Settle

BIOS check → Loading kernel → Mounting filesystems → Welcome

Click Start to begin
const boot = new PhraseCycle(element, {
  phrases: [
    'BIOS check OK',
    'Loading kernel...',
    'Mounting filesystems...',
    'Starting services...',
  ],
  interval: 500,
  finalText: 'WELCOME.',
});
boot.start();

Looping Spinner

Loops indefinitely with no finalText. Perfect for persistent loading indicators. interval: 300ms.

Spinner · Loop

loop: true — cycles forever, ignores duration and finalText

Click Start to begin
const spinner = new PhraseCycle(element, {
  phrases: ['Loading.', 'Loading..', 'Loading...'],
  interval: 300,
  loop: true,         // cycle forever — no finalText, no duration limit
});
spinner.start();

Full API Reference

import { PhraseCycle } from '@whykusanagi/corrupted-theme/phrase-cycle';

// ── Settling cycle (default): one full pass then display finalText ─────────
const cycle = new PhraseCycle(element, {
  phrases:   ['Initializing...', 'Connecting...', 'Authenticating...'],
  interval:  400,          // ms between phrase swaps (default: 200)
  duration:  null,         // null = phrases.length × interval (one full pass)
  finalText: 'Ready.',     // text written after settle; null = leave last phrase
  loop:      false,        // false = settle after duration (default)
});
cycle.start();             // idempotent — safe to call while already running

// ── Looping (no settle): cycle forever ───────────────────────────────────
const spinner = new PhraseCycle(element, {
  phrases:  ['Loading.', 'Loading..', 'Loading...'],
  interval: 300,
  loop:     true,          // ignores duration + finalText
});
spinner.start();
spinner.stop();            // pause; leaves last phrase visible; reusable
spinner.start();           // restart from first phrase

// ── Lifecycle ─────────────────────────────────────────────────────────────
cycle.stop();              // stop timers; last phrase stays visible; reusable
cycle.destroy();           // teardown + release element reference; not reusable
cycle.isRunning();         // → boolean