MimoProvider

Wraps your wallpaper in Mimo, the Fluxlay SDK's synthetic input forwarder, so that ordinary React DOM events (onClick, onPointerDown, onKeyDown, drag-and-drop libraries, focus rings, etc.) fire inside the wallpaper window even though the OS cursor never enters it.

For background on what Mimo is and why a wallpaper needs it, see Mimo: Interactive UI inside Wallpapers.

Import

MimoProvider lives on a separate subpath from the rest of the SDK to keep the main entry point free of Mimo's dependency tree:

import { MimoProvider } from "@fluxlay/react/mimo";

Quick start

Drop it at the root of your wallpaper. With no extra props, OS pointer and keyboard events are wired into the forwarder automatically:

import { MimoProvider } from "@fluxlay/react/mimo";
 
export function Root() {
  return (
    <MimoProvider cursor>
      <App />
    </MimoProvider>
  );
}

<App /> can now use plain React handlers — <button onClick={...}>, dnd-kit, Framer Motion drag, <input> focus rings via useIsFocused, etc. The cursor prop renders a virtual cursor div that follows the synthetic pointer, since the OS does not paint one inside wallpaper windows.

IME (Japanese / Chinese / Korean text input) is handled automatically: focusing an <input> or <textarea> activates the IME proxy, the user types, and the committed text is written into the field. No separate wiring is required. See useImeInput if you need direct control.

Signature

function MimoProvider(props: MimoProviderProps): ReactNode;
 
interface MimoProviderProps extends Omit<MimoForwarderOptions, "pointerSource" | "keyboardSource"> {
  pointer?: boolean;
  keyboard?: boolean;
  children?: ReactNode;
}

pointerSource / keyboardSource are intentionally omitted — MimoProvider owns the input source and wires it to Fluxlay's OS hooks (useMousePosition, useMouseEvents, useKeyboard). To plug in a custom source (tests, replay, non-Fluxlay environments), use MimoForwarderProvider instead (see below).

Props

PropTypeDefaultDescription
pointerbooleantrueWhen false, OS pointer events are not injected into the forwarder. Useful for temporarily detaching the cursor (demo toggles, modal overlays).
keyboardbooleantrueWhen false, OS keyboard events are not injected.
cursorboolean | CursorOptionsfalseRender a virtual cursor element following the synthetic pointer. Useful because wallpaper windows have no real OS cursor visible inside them.
autoFocusbooleantrueFocus the nearest focusable ancestor on pointerdown, mirroring real browser behavior.
doubleClickThresholdMsnumber500Maximum gap that still counts as a double click.
clickMoveThresholdPxnumber5Beyond this drift, the up is treated as a drag and click is suppressed.
keyboardTargetKeyboardTargetStrategy"active-element"Where to dispatch keyboard events: "active-element" (default — works with useIsFocused / <input> focus rings), "pointer-target", or "document".
pointerCapturebooleantrueInstall a process-wide patch on Element.prototype.{set,release,has}PointerCapture to route captures through the forwarder. Required for dnd-kit, react-dnd Pointer backend, Framer Motion drag.
childrenReactNodeYour wallpaper tree.

useMimoForwarder

Returns the underlying MimoForwarder instance owned by the surrounding provider. Throws if used outside one. Useful when you need to imperatively call methods on the forwarder (e.g. injectPointer / injectKeyboard for tests or replay).

import { useMimoForwarder } from "@fluxlay/react/mimo";
 
function ReplayButton() {
  const forwarder = useMimoForwarder();
  return (
    <button
      onClick={() => {
        forwarder.injectPointer({ kind: "move", x: 100, y: 100 });
        forwarder.injectPointer({ kind: "button", x: 100, y: 100, button: "left", pressed: true });
        forwarder.injectPointer({ kind: "button", x: 100, y: 100, button: "left", pressed: false });
      }}
    >
      Replay click
    </button>
  );
}

MimoForwarderContext is also exported if you need optional access (returns null outside a provider).

Custom input source: MimoForwarderProvider

The opinionated MimoProvider always pumps Fluxlay's OS hooks into the forwarder. When you need to provide your own pointerSource / keyboardSource — for unit tests with a synthetic stream, for replaying recorded sessions, or for embedding the wallpaper outside the Fluxlay desktop app — use the lower-level MimoForwarderProvider:

import { MimoForwarderProvider, type PointerSource, type KeyboardSource } from "@fluxlay/react/mimo";
 
const pointerSource: PointerSource = handler => {
  const ws = new WebSocket("ws://localhost:9999/pointer");
  ws.onmessage = ev => handler(JSON.parse(ev.data));
  return () => ws.close();
};
 
const keyboardSource: KeyboardSource = handler => {
  // ...
  return () => {};
};
 
export function ReplayRoot() {
  return (
    <MimoForwarderProvider pointerSource={pointerSource} keyboardSource={keyboardSource}>
      <App />
    </MimoForwarderProvider>
  );
}

MimoForwarderProvider accepts the full MimoForwarderOptions set (the same prop list as MimoProvider plus pointerSource / keyboardSource). A PointerSource is a function that receives a handler and returns an unsubscribe; coordinates are CSS pixels in the target document's viewport. KeyboardSource follows the same shape.

Exported types

The following identifiers are exported from @fluxlay/react/mimo:

  • MimoForwarder, MimoForwarderContext, useMimoForwarder
  • MimoForwarderProvider, MimoForwarderProviderProps
  • MimoForwarderOptions, CursorOptions, KeyboardTargetStrategy
  • PointerSource, PointerSourceEvent, PointerSourceMoveEvent, PointerSourceButtonEvent, PointerSourceWheelEvent, PointerSourceCancelEvent, PointerButton, PointerType
  • KeyboardSource, KeyboardSourceEvent, KeyboardModifiers
  • Unsubscribe

Notes

  • MimoProvider mounts a single forwarder per wallpaper window. Mounting multiple is supported (the most recently started one captures), but uncommon.
  • The forwarder synthesizes events and is therefore subject to the browser's isTrusted gate. A handful of behaviors (HTML5 native drag-and-drop, raw text entry into <input> / <textarea>, OS context menus, autoplay unlock) cannot be triggered through synthetic events. Most have Fluxlay-side workarounds — IME composition is handled by useImeInput, the OS hover ring is replaced by useIsFocused plus the virtual cursor.