Mimo: Interactive UI inside Wallpapers
Wallpaper windows are click-through and never become the OS key window. As a result, the browser inside them never sees real pointermove, click, or keydown events — your <button> does not click, your <input> does not focus, and dnd-kit cannot start a drag. Mimo is the layer bundled with the Fluxlay SDK that turns Fluxlay's OS input streams (useMousePosition, useMouseEvents, useKeyboard) into synthetic DOM events so that ordinary React UI works inside a wallpaper.
Wrap your wallpaper root once with <MimoProvider> and forget about the wiring.
When you need it
Add <MimoProvider> whenever your wallpaper renders interactive UI:
- buttons, links, accordions, tabs
- text inputs (with
useImeInputhandling IME) - dnd-kit, react-dnd Pointer backend, Framer Motion drag
- focus rings via
useIsFocused - anything that relies on
onPointerDown,onClick,onKeyDown, etc.
If your wallpaper is purely visual (a particle effect, a clock face, animated art with no UI), you do not need Mimo.
Quick start
MimoProvider lives on a separate subpath:
import { MimoProvider } from "@fluxlay/react/mimo";
export function Root() {
return (
<MimoProvider cursor>
<App />
</MimoProvider>
);
}That is the entire setup. Inside <App /> you can write ordinary React:
function App() {
return (
<div>
<button onClick={() => alert("clicked")}>Click me</button>
<input placeholder="日本語入力もできます" />
</div>
);
}The button fires real click events, the input gets focused on click, and IME composition routes into the input automatically — all without extra wiring.
The cursor prop renders a virtual cursor element following the synthetic pointer. It is recommended because the OS cursor is invisible inside wallpaper windows, leaving users without a visual reference.
How it works
- The Fluxlay desktop app captures OS-level mouse and keyboard input and pushes them to the wallpaper as event streams.
MimoProvidersubscribes to those streams viauseMousePosition,useMouseEvents, anduseKeyboard.- Each event is fed into the underlying
MimoForwarder. - The forwarder hit-tests the DOM (descending into open shadow roots) and dispatches a synthetic
PointerEvent/MouseEvent/WheelEvent/KeyboardEventon the target. - React's event system sees those events as if a real user produced them and runs your handlers.
Mimo covers the full set of expected behaviors: click / dblclick / contextmenu / auxclick, ancestor chain semantics for pointerover / pointerleave, pointer capture (setPointerCapture & friends), modifier propagation across pointer + keyboard, movement deltas, and pointer cancel.
Limitations
Some behaviors cannot be triggered from synthetic events because of the browser's isTrusted gate. Most have Fluxlay-side workarounds:
| Limitation | Workaround |
|---|---|
<input> / <textarea> raw text entry (synthetic KeyboardEvent does not mutate value) | IME route handles Japanese / Chinese / Korean automatically. Use useImeInput for direct control. |
OS-level CSS :hover (the OS cursor never enters the window) | Pass cursor to MimoProvider for a virtual cursor; toggle hover state imperatively if needed. |
| OS context menus, browser shortcuts, autoplay unlock | Build them in-app instead of relying on the OS chrome. |
HTML5 native drag-and-drop (draggable attribute, dataTransfer, file drops) | Use dnd-kit / react-dnd Pointer backend / Framer Motion drag — these go through pointer capture and work. |
Customization
Most knobs (pointer, keyboard, cursor, autoFocus, keyboardTarget, pointerCapture, click thresholds) are props on <MimoProvider>. See the MimoProvider reference for the full list.
If you need to provide your own input source — for tests, replay, or running the wallpaper outside the Fluxlay desktop app — use the lower-level MimoForwarderProvider exported from @fluxlay/react/mimo.