npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fluxlay/react

v1.3.13

Published

SDK for building Fluxlay wallpapers with React

Readme

@fluxlay/react

SDK for building Fluxlay wallpapers with React.

Installation

npm install @fluxlay/react
# or
bun add @fluxlay/react

The SDK ships its own styles (xterm CSS is imported automatically by useTerminal); no additional stylesheet import is required.

Runtime constraints

Wallpapers run in an isolated fluxlay:// origin with a minimal Content Security Policy enforced by the desktop app. As a result:

  • No external hosts. fetch / XMLHttpRequest / WebSocket to arbitrary origins is blocked by connect-src. The only non-'self' endpoint allowed by default is the local Fluxlay API (http://127.0.0.1:*), which the SDK uses internally. To reach an external host, declare it under network: in fluxlay.yaml — its scheme + host[:port] is then injected into connect-src / img-src / media-src / font-src (passive resources only; never script-src). For declared hosts that don't return Access-Control-Allow-Origin (e.g. Google Calendar ICS feeds), use proxiedFetch to route the request through the host process and bypass CORS.
  • No eval / new Function. script-src is 'self' only — no 'unsafe-eval', no 'unsafe-inline'. Bundlers, template engines, and runtime-compiled code paths that rely on eval will not run. (Dev mode loosens this for HMR; production does not.)
  • No __TAURI__.core.invoke. Wallpaper windows are excluded from every Tauri capability, so invoke is unavailable. All host interactions must go through SDK hooks, which talk to the local API over HTTP / WebSocket.
  • Inline styles are allowed (style-src 'self' 'unsafe-inline') so React style={...} and CSS-in-JS keep working.

API

useMousePosition()

Subscribes to mouse position events for the current wallpaper window. Both axes are normalized to [-1, 1] (x: -1 left → 1 right; y: -1 bottom → 1 top — the Y axis is inverted to match a standard mathematical coordinate system, opposite to CSS). Returns { x: 0, y: 0 } until the first event is received or when running outside the Fluxlay desktop app.

import { useMousePosition } from "@fluxlay/react";

function Wallpaper() {
  const { x, y } = useMousePosition();
  // Convert normalized coordinates to pixels, flipping Y back to CSS orientation.
  const px = ((x + 1) / 2) * window.innerWidth;
  const py = (1 - (y + 1) / 2) * window.innerHeight;
  return <div style={{ transform: `translate(${px}px, ${py}px)` }} />;
}

useMouseEvents(handlers)

Subscribes to mouse click and wheel events for the current wallpaper window. Coordinates are normalized to [-1, 1] with the Y axis inverted (positive Y points upward).

import { useMouseEvents } from "@fluxlay/react";

function Wallpaper() {
  useMouseEvents({
    onButton: event => {
      if (event.pressed) console.log("pressed", event.button, event.x, event.y);
    },
    onWheel: event => {
      console.log("scroll", event.deltaX, event.deltaY);
    }
  });
  return <div />;
}

On macOS, mouse events require the user to grant Fluxlay "Input Monitoring" permission. On Windows, events from windows running with elevated privileges are not delivered (UIPI).

useKeyboard(handlers)

Subscribes to global keyboard events. Events are delivered to every subscriber on every window while the user is anywhere on the desktop, which is useful for game-like wallpapers. event.code follows the Web KeyboardEvent.code convention (e.g. "KeyA", "Space", "ArrowLeft", "ShiftLeft") and identifies a physical key independent of layout.

Because this hook receives every keystroke the user types anywhere on the desktop — including text typed into other applications — it requires the wallpaper to declare the keyboard permission in fluxlay.yaml. Without the declaration, the backend rejects the subscription with HTTP 403 and no events are delivered.

# fluxlay.yaml
schemaVersion: 1
name: My Wallpaper
slug: my-wallpaper
version: 1.0.0
permissions:
  - keyboard
import { useKeyboard } from "@fluxlay/react";

function Wallpaper() {
  useKeyboard({
    onKeyDown: event => console.log("down", event.code, event.modifiers, event.repeat),
    onKeyUp: event => console.log("up", event.code)
  });
  return <div />;
}

Same platform requirements as useMouseEvents (macOS Input Monitoring permission; UIPI on Windows).

useImeInput()

Receives IME-composed text (Japanese / Chinese / Korean / etc.) for wallpapers that want to accept text input — for example a wallpaper-resident notepad, search box, or chat input.

Unlike useKeyboard, which sees raw pre-IME keystrokes from every window on the desktop, useImeInput only delivers text that the user explicitly directs at the wallpaper. Internally Fluxlay creates a tiny invisible proxy window per wallpaper; calling activate() (typically in response to the user clicking an in-wallpaper input field) gives that proxy keyboard focus so IME composition is routed through it. The wallpaper window itself never becomes a key window.

Wallpapers must declare the ime-input permission in fluxlay.yaml:

# fluxlay.yaml
permissions:
  - ime-input
import { useImeInput } from "@fluxlay/react";
import { useEffect, useState } from "react";

function NotePad() {
  const ime = useImeInput();
  const [text, setText] = useState("");

  useEffect(() => {
    return ime.onCommit(committed => setText(prev => prev + committed));
  }, [ime]);

  return (
    <div onClick={ime.activate} onBlur={ime.deactivate}>
      <span>{text}</span>
      {ime.composition !== null && <span style={{ opacity: 0.5 }}>{ime.composition}</span>}
    </div>
  );
}

The hook returns:

  • composition: string | null — the in-progress composition text (e.g. "か" while typing "漢字"), or null when no composition is active
  • cursor: number — caret position within composition, in code points
  • activate() / deactivate() — start / stop receiving IME input. The hook automatically calls deactivate() on unmount
  • onCommit(handler) — register a callback for finalized text. Returns a cleanup function

While activate() is in effect, useKeyboard events are paused on the same wallpaper to avoid IME candidate-window keys (arrows, Enter, Esc) double-firing as raw key events.

If the wallpaper does not declare ime-input, the hook becomes a no-op and emits one console.warn describing the missing declaration.

useProperties<T>()

Returns the current values of the custom properties declared under properties: in fluxlay.yaml. Updates in real time when the user adjusts settings in the Fluxlay app.

import { useProperties } from "@fluxlay/react";

type Props = {
  particleCount: number;
  themeColor: string;
  showClock: boolean;
};

function Wallpaper() {
  const { particleCount, themeColor, showClock } = useProperties<Props>();
  return <div style={{ color: themeColor }}>{particleCount}</div>;
}

For image / file properties, the value is a local file path on the host OS (or null if the user has not selected a file). Pass it through getPropertyFileUrl() to obtain a URL the webview can load.

getPropertyFileUrl(path)

Builds a URL the wallpaper webview can use to load a local file referenced by an image or file property. Returns null when the path is missing.

import { useProperties, getPropertyFileUrl } from "@fluxlay/react";

function Wallpaper() {
  const { customLogo } = useProperties<{ customLogo: string | null }>();
  const url = getPropertyFileUrl(customLogo);
  return url ? <img src={url} alt="logo" /> : null;
}

useSystemMonitor(options?)

Subscribes to real-time system and hardware metrics streamed from the Fluxlay backend. Returns CPU, memory, battery, network, disk, and other system information.

import { useSystemMonitor } from "@fluxlay/react";

function Wallpaper() {
  const { cpuUsage, memoryUsage, batteryLevel } = useSystemMonitor({
    cpuIntervalMs: 500,
    memoryIntervalMs: 1000,
    batteryIntervalMs: 10000
  });
  return (
    <div style={{ fontFamily: "monospace" }}>
      <p>CPU: {cpuUsage.toFixed(1)}%</p>
      <p>Memory: {memoryUsage.toFixed(1)}%</p>
      <p>Battery: {batteryLevel ?? "N/A"}</p>
    </div>
  );
}

| Option | Type | Default | Description | | ----------------------- | -------- | ------- | ----------------------------------------- | | cpuIntervalMs | number | 500 | CPU usage, per-core usage, and frequency. | | memoryIntervalMs | number | 1000 | Memory and swap usage. | | networkIntervalMs | number | 1000 | Network receive/transmit speed. | | diskIoIntervalMs | number | 2000 | Disk read/write speed. | | diskSpaceIntervalMs | number | 30000 | Disk capacity per mount point. | | batteryIntervalMs | number | 10000 | Battery level and charging status. | | processIntervalMs | number | 10000 | Running process count. | | loadAverageIntervalMs | number | 5000 | System load average. |

useAudio(options?)

Subscribes to real-time system audio information streamed from the Fluxlay backend. Returns RMS volume, peak level, and a logarithmically-scaled frequency spectrum useful for building audio visualizers.

import { useAudio } from "@fluxlay/react";

function Wallpaper() {
  const { rms, peak, spectrum } = useAudio({ numBands: 64 });
  return (
    <div>
      <p>Volume: {(rms * 100).toFixed(0)}%</p>
      <div style={{ display: "flex", gap: 2, alignItems: "flex-end", height: 100 }}>
        {spectrum.map((v, i) => (
          <div key={i} style={{ width: 4, height: `${v * 100}%`, background: "white" }} />
        ))}
      </div>
    </div>
  );
}

| Option | Type | Default | Description | | ---------- | -------- | ------- | ----------------------------------- | | numBands | number | 32 | Number of frequency spectrum bands. |

| Return field | Type | Description | | ------------ | ---------- | -------------------------------------------------------------------------------------------- | | rms | number | RMS (Root Mean Square) volume level (0.0 – 1.0). | | peak | number | Peak volume level (0.0 – 1.0). | | spectrum | number[] | Frequency spectrum bands on a logarithmic scale (0.0 – 1.0 each). Length matches numBands. |

On macOS, the Fluxlay desktop app captures system audio via the Core Audio Tap API and prompts the user for audio capture permission (NSAudioCaptureUsageDescription); macOS 14.2+ is required. The spectrum is A-weighted (IEC 61672) so the frequency balance matches human hearing.

useMediaMetadata(options?)

Subscribes to media metadata for the currently playing track from system media players (e.g. Spotify, Apple Music). Returns title, artist, album artwork, playback progress, and playback state.

import { useMediaMetadata } from "@fluxlay/react";

function Wallpaper() {
  const { title, artist, artwork, isPlaying, elapsedTime, duration } = useMediaMetadata({
    intervalMs: 1000
  });
  return (
    <div>
      {artwork && <img src={artwork} alt="Album art" width={200} />}
      <p>{title ?? "Not playing"}</p>
      <p>{artist}</p>
    </div>
  );
}

| Option | Type | Default | Description | | ------------ | -------- | ------- | --------------------------------- | | intervalMs | number | 1000 | Polling interval in milliseconds. |

| Field | Type | Description | | -------------- | ---------------- | ---------------------------------------------------- | | title | string \| null | Track title. | | artist | string \| null | Artist name. | | album | string \| null | Album name. | | artwork | string \| null | Artwork as a data URL (data:image/...;base64,...). | | duration | number \| null | Track duration in seconds. | | elapsedTime | number \| null | Elapsed playback time in seconds. | | playbackRate | number \| null | Playback rate (0.0 = paused, 1.0 = playing). | | isPlaying | boolean | Whether media is currently playing. |

useShell(commandId, options?)

Executes a shell command declared in fluxlay.yaml and optionally renders its output into a terminal.

import { useShell } from "@fluxlay/react";

function Wallpaper() {
  const { result, isRunning } = useShell("my-command", {
    refreshInterval: 5000 // re-run every 5 seconds
  });
  return <pre>{result?.stdout}</pre>;
}

| Option | Type | Default | Description | | ----------------- | -------------------------- | ------- | ------------------------------------------------------------------------------------------------------- | | refreshInterval | number | 30000 | Auto-refresh interval in ms. 0 to disable. | | showStdout | boolean | true | Write stdout to the linked terminal. | | showStderr | boolean | false | Write stderr to the linked terminal. | | terminal | TerminalInstance \| null | — | Terminal instance from useTerminal. When set, the terminal's dimensions override columns / lines. | | columns | number | — | Pseudo-terminal column width. | | lines | number | — | Pseudo-terminal line height. |

Returns { execute, isRunning, error, result }.

useTerminal(options?)

Creates a read-only xterm terminal. Attach terminalRef to a <div> container.

import { useTerminal, useShell, TerminalThemes } from "@fluxlay/react";

function Wallpaper() {
  const { terminalRef, instance } = useTerminal({
    fontSize: 13,
    theme: TerminalThemes.tokyoNight
  });
  useShell("my-command", { terminal: instance });
  return <div ref={terminalRef} />;
}

| Option | Type | Default | Description | | ------------ | -------- | ----------------------------- | ----------------------- | | fontSize | number | 12 | Font size in pixels. | | fontFamily | string | system monospace | CSS font-family string. | | theme | ITheme | TerminalThemes.dark.default | xterm color theme. |

Returns { terminalRef, instance }.

proxiedFetch(input, init?)

fetch-compatible wrapper that routes the request through the Fluxlay host process. Use this for declared network: origins that don't return Access-Control-Allow-Origin (e.g. ICS feeds, public RSS endpoints) — the host issues the upstream request as a regular HTTP client, so browser CORS does not apply. The host validates that the target URL's origin is declared in this wallpaper's network: manifest entries; non-declared origins return 403.

import { proxiedFetch } from "@fluxlay/react";

const res = await proxiedFetch("https://calendar.google.com/calendar/ical/.../basic.ics");
const text = await res.text();

Restrictions vs. native fetch:

  • Only http: / https: URLs are accepted.
  • Cookie / Origin / Host / Referer request headers are stripped.
  • Response body is capped at 10 MiB; streaming is not supported (the upstream body is fully buffered).
  • Forwarded response headers are limited to Content-Type, Cache-Control, ETag, Last-Modified.

openUrl(url)

Opens an http: / https: URL in the user's default browser via tauri-plugin-opener. Wallpapers cannot navigate the OS shell directly, so this is the only path to make in-wallpaper links clickable.

import { openUrl } from "@fluxlay/react";

<button onClick={() => void openUrl("https://fluxlay.com")}>Open</button>;

Throws when the host rejects the URL (non-http(s) scheme) or fails to launch the browser.

notify(options)

Posts an OS-level notification (Notification Center on macOS, Action Center on Windows) via tauri-plugin-notification. The browser Notification API is not usable from sandboxed wallpaper origins, so this is the supported way to surface events from a running wallpaper.

import { notify } from "@fluxlay/react";

await notify({ title: "Build finished", body: "All tests passed" });

| Option | Type | Description | | ------- | -------- | ------------------------------------------ | | title | string | Required. Shown as the notification title. | | body | string | Optional. Shown as the notification body. |

The first call from a freshly installed Fluxlay app may trigger an OS-level permission prompt; subsequent calls succeed silently if the user grants notification permission.

runShell(commandId, options?)

Low-level function to run a shell command imperatively.

import { runShell } from "@fluxlay/react";

const result = await runShell("my-command", { columns: 80, lines: 24 });
console.log(result.stdout);

TerminalThemes

Pre-built xterm color themes to pass to useTerminal.

| Key | Variants | | ------------ | --------------------------------------- | | dark | default, modern | | light | default, modern | | nord | — | | dracula | — | | gruvbox | dark, light | | solarized | dark, light | | oneDark | — | | catppuccin | latte, frappe, macchiato, mocha | | monokaiPro | — | | tokyoNight | — | | nightOwl | — | | everforest | dark, light |