@muzluk

Agent Questions for React.

Mood Wheel is a beautifully designed mood and emotion picker for agent check-ins. Agent Questions turns those check-ins into real follow-ups: choices, custom answers, text, scales, time, and mood—then gives your app structured receipts to act on.

Try it as a

Live demo

Switch packages and use the real published interactions before installing them.

Loading live preview…

Installation

npm install @muzluk/mood-wheel @muzluk/agent-questions

Inspiration

Mood Wheel is a mood picker by default, but really it is an expressive five-state input. Give it five ordered options and adapt the labels to the job your product is helping someone do.

Daily Check-in

A lightweight emotional check-in for journals, wellness tools, personal dashboards, or habit apps.

Awful → Low → Okay → Good → Great

Assistant Feedback

Let people rate an AI response with more nuance than thumbs up/down. The value can feed evaluation data, trigger a retry, or influence the next response.

Poor → Weak → Okay → Helpful → Excellent

Sprint Retro

A quick team pulse at the end of a sprint, release, project, or incident.

Rough → Hard → Okay → Smooth → Great

Lesson Check

Give students a low-friction way to communicate understanding without raising a hand or writing an explanation.

Lost → Unsure → Okay → Clear → Confident

Training Readiness

A pre-workout check-in that can adjust intensity or simply record how someone felt before training.

Exhausted → Low → Okay → Ready → Strong

Support Review

A more expressive customer-support rating than stars or a binary satisfaction question.

Awful → Poor → Okay → Good → Great

Setup Confidence

Use the wheel during onboarding to understand whether somebody is comfortable continuing.

Stuck → Unsure → Okay → Clear → Ready

Meal Reflection

A simple food-journal interaction focused on how a meal felt rather than calories or numerical scoring.

Bad → Off → Okay → Good → Great

Trip Check-in

Add a tiny reflection moment to a travel diary, itinerary, or trip-planning app.

Awful → Tiring → Okay → Lovely → Amazing

Team Pulse

A quick consensus check during meetings, planning sessions, workshops, or async collaboration.

Blocked → Concerned → Okay → Aligned → Excited

The same pattern works for confidence, urgency, difficulty, energy, agreement, satisfaction, severity, quality, readiness, preference, and progress.

Port it to React Native

Give this brief to your coding agent alongside the package source. It is intentionally specific about assets, gesture ownership, physics, timing, accessibility, audio, haptics, and validation so the native result keeps the source character instead of becoming a stock picker.

React Native port brief


You are a senior React Native interaction engineer. Port the published Muzluk
open-source package in this repository into a production-ready React Native
component with visual, behavioral, and tactile parity. The goal is not to make
a similar mobile control. The goal is to preserve the character of the source
interaction: the same assets, detents, timing, gesture ownership, velocity,
settling, sound, haptic rhythm, copy, spacing, and state transitions.

SOURCE OF TRUTH

1. Read the installed package README files, generated TypeScript declarations,
   source repository, and every first-party asset before writing code. Do not
   infer missing behavior from a generic slider or wheel library.
2. The source packages are @muzluk/mood-wheel and @muzluk/agent-questions.
   The source repository is https://github.com/iyiolacak/mood-wheel.
3. Treat the shipped wheel and pointer images, tick cues, answer/reveal cues,
   and their intrinsic dimensions as authoritative. Copy the actual files into
   the native asset pipeline at their original pixel dimensions and scale them
   only through the same layout rules. Do not redraw, rasterize, recolor,
   crop, stretch, or replace them with emoji, SF Symbols, Material icons, or
   third-party icon packs.
4. Preserve the MIT attribution and add the source package and its assets to
   the app's third-party/first-party notices as appropriate.

PUBLIC NATIVE API

Create a small, typed API with these layers:

- MoodWheel<Value extends string>
  - options: readonly five ordered { value, label, ariaLabel? } objects
  - value/defaultValue, disabled, class/style equivalent, messages
  - assets override with pointer, tick, and wheel only when a host genuinely
    needs it
  - intro: false | limited | always, introPlayLimit, introStorageKey
  - sound, showControls, showHint, layoutVariant: default | ultraWide
  - ambientVelocityY in pixels per second
  - onAttemptInteract(): boolean for save/busy gating
  - onChange and onDetent returning { index, option, source }
  - source must be control, drag, keyboard-equivalent, or wheel/scroll

- AdvancedSlider
  - aria/accessibility label, decrement/increment labels
  - min, max, step, optional majorStep, value/defaultValue
  - disabled, sound, unit, formatValue
  - optional milestone hooks and onValueChange/onValueCommit/onDetent
  - preserve every authored detent; do not silently coerce a one-minute range
    into five-minute increments

- AgentQuestions
  - receives the validated question set from the package schema/tool output
  - owns drafts, vertical paging, answer/skip state, retry presentation, and
    completion receipts
  - exposes onAnswer, onSkip, onComplete, sound, assets, messages, locale, and
    the transcribe(recording, { question, locale }) callback
  - the host owns model calls, authorization, persistence, and post-completion
    behavior; never hide network or storage policy inside the component

WHEEL GEOMETRY AND GESTURES

1. The supplied wheel has exactly five physical hit/trigger points. Enforce
   exactly five options at the public boundary. Never serve four, six, or a
   dynamically resized wheel to fit a different option count.
2. Build the wheel in a rounded, black viewport with the exact source width,
   height, asset alignment, bottom anchor, and pointer resting position. The
   wheel art and pointer must remain optically locked to the original.
3. A horizontal drag owns the wheel. A vertical gesture must be allowed to
   bubble to AgentQuestions when the wheel is embedded in the question reel.
   Do not make the entire screen compete for the same gesture.
4. During direct travel, map translation to the physical wheel angle. On
   release, project a short window of release velocity, clamp it to the five
   authored detents, then settle with one interruptible damped spring. A new
   touch must interrupt the spring immediately and continue from the current
   visual position, never from a stale selected value.
5. The pointer is a loose physical part. It should lag, lean, and shake from
   horizontal wheel velocity, then recoil and settle with the wheel. When the
   host passes ambientVelocityY, add that vertical host motion to the pointer's
   inertial lean without changing the wheel's selected detent.
6. Preserve the first-use intro sweep and its cubic-bezier feel. Respect the
   source introPlayLimit and storage key. Reduced motion must disable the sweep,
   spring overshoot, and shake while keeping the control usable.
7. Keep edge labels, center label, controls, hint, hit targets, and focus state
   in the same hierarchy and optical positions. Do not substitute a native
   picker whose layout or momentum changes the experience.

QUESTION REEL MOTION

1. AgentQuestions is a vertically draggable reel. The current card, adjacent
   card, progress indicator, and height window must move as one continuous
   physical system. Do not teleport between questions or let the card settle at
   several different heights.
2. Measure card heights before paging and keep a stable clipping window during
   a transition. Answered cards remain visually answered; they do not collapse
   into a boring receipt that causes layout shift.
3. Vertical release velocity chooses the next/previous question only when the
   gesture crosses the authored threshold. Otherwise the current card springs
   back. A drag can navigate a question after it is answered; an unanswered
   required question still needs the source confirmation/skip rules.
4. Keep horizontal wheel ownership inside MoodWheel. Keep text fields, buttons,
   microphone controls, and sliders from accidentally starting reel paging.
5. Reproduce the answer cue, answered sound, reveal motion, progress changes,
   and retry/error states. Each transition must be interruptible and driven by
   one source of truth for the active index.

ADVANCED TIMER RULER

1. Render a moving tick rail inside a full-width rounded black viewport with
   black edge fades on both sides. The edge fades mask overflow; they are not
   extra layout columns and must not intercept touches.
2. Short ranges expand each tick slot to fill the measured viewport. Long
   ranges retain the compact native tick width and remain freely browsable by
   dragging. For min=1, max=90, step=1, retain all 90 selectable minute
   detents. majorStep changes labels only; it never removes ticks.
3. Keep the fixed selected detent, rolling numeric readout, tabular numerals,
   source tick heights, selected color, sound variants, and release projection.
   Do not add red threshold lines or a custom blue pointer/cursor. The selected
   tick itself is enough feedback.
4. Edge dragging uses continuous rubber-band resistance. Release velocity is
   projected for the source short window and settles to one normalized detent.
   Arrow/button equivalents must use the same model as a gesture, not a second
   hand-written value path.
5. The text entry path uses a black field with white semibold text. The submit
   action is wider and centered. The optional microphone action records locally
   and delegates transcription to the host callback; never hard-code a speech
   provider.

PHYSICAL FEEDBACK

1. Use the project's existing Reanimated/gesture/audio/haptics stack when one
   exists. Do not introduce a second animation runtime just for this port.
2. Tick sound is a small pooled set of the supplied cues. Reuse/preload them,
   reset currentTime before playback, and fail silently if audio is unavailable.
   The interaction must never wait for sound.
3. Pair detent crossings, milestone impacts, answer commits, and reel landings
   with short haptic events that have a clear physical cause. Avoid a constant
   vibration loop or arbitrary “success” decoration.
4. Gate audio/haptics behind host settings and platform capability checks. Clean
   up players, streams, timers, and gesture worklets on unmount.

VISUAL PARITY

- Start from the source screenshots and asset files, not a generic design system.
- Keep the default system font used by the host app; do not add a display font.
- Preserve the compact mobile-first dimensions, dark surfaces, black wheel/ruler
  viewports, rounded corners, muted labels, blue action, and semibold controls.
- Use no third-party icons. If an icon is needed, use the source-owned SVG/path
  geometry or a platform-safe textless primitive that matches it optically.
- Keep button hit areas at least 40×40 points even when the visible icon is small.
- Avoid gradients, shadows, borders, and decorative cards unless the source
  component already has that exact surface treatment. Do not invent a calmer or
  more generic productivity UI.

ACCESSIBILITY AND EDGE CASES

- Expose correct accessibilityRole, accessibilityLabel, value/min/max, and
  state for wheel, slider, buttons, text, microphone, and question progress.
- Provide VoiceOver/TalkBack actions for previous/next and selecting detents.
- Respect reduced motion, no-audio, denied-microphone, interrupted-recording,
  empty transcription, disabled, saving, retry, and unmounted states.
- Keep controlled values authoritative without cancelling a live gesture or
  release spring when the host echoes onChange.
- Normalize malformed ranges and never allow NaN, an out-of-range detent, or an
  impossible question index to reach the UI.
- Preserve translated labels as presentation while stable values remain the
  data contract.

IMPLEMENTATION PLAN

1. Inventory the web package source, declarations, tests, assets, dimensions,
   CSS variables, sound files, and motion constants. Write a short parity map
   before coding.
2. Build pure range/wheel physics helpers first and unit-test normalization,
   detent generation, velocity projection, rubber-banding, and five-stop
   enforcement.
3. Build MoodWheel, then AdvancedSlider, then AgentQuestions from those helpers.
   Keep transient gesture values in shared values/refs so React renders do not
   interrupt 60fps motion.
4. Add sound, haptic, accessibility, reduced-motion, microphone, and retry
   behavior after the state contracts are stable.
5. Compare Android and iOS screenshots at the same viewport dimensions. Test
   slow drags, fast flicks, interrupted springs, edge drags, rotation, long
   labels, 1–90 time ranges, answered cards, skipped cards, and screen readers.

DELIVERABLES AND ACCEPTANCE

Return the changed files, the public TypeScript API, the asset attribution, and
the test commands. The port is complete only when:

- the five wheel stops and pointer motion match the source;
- a drag can be interrupted at any point without a jump;
- a 1–90 step-1 ruler still exposes every minute;
- short and long ruler geometry follows the viewport rules;
- question paging has no visible height/layout shift;
- answer/skip/retry receipts match the web contract;
- audio, haptics, reduced motion, accessibility, and microphone failure paths
  are covered;
- iOS and Android screenshots are reviewed against the web reference; and
- unit, component, and gesture tests pass without weakening assertions.

If a native platform constraint prevents literal parity, document the constraint
and reproduce the same perceptual result with the smallest possible adapter.
Do not silently simplify the interaction or replace it with a stock control.

Usage

Pass exactly five options. Labels can be translated; the value is what your application receives.

import { MoodWheel } from "@muzluk/mood-wheel";
import "@muzluk/mood-wheel/styles.css";

const moods = [
  { value: "awful", label: "Awful" },
  { value: "rough", label: "Rough" },
  { value: "okay", label: "Okay" },
  { value: "good", label: "Good" },
  { value: "great", label: "Great" },
] as const;

export function CheckIn() {
  return (
    <MoodWheel
      options={moods}
      onChange={({ option }) => save(option.value)}
    />
  );
}

Connect an agent

Validate agent output before rendering it. Answer and completion callbacks return structured receipts that your application owns.

import {
  AgentQuestions,
  parseAgentQuestions,
} from "@muzluk/agent-questions";
import "@muzluk/agent-questions/styles.css";

const { questions } = parseAgentQuestions(agentOutput);

<AgentQuestions
  questions={questions}
  sound
  onAnswer={(answer) => saveAnswer(answer)}
  onComplete={(result) => continueAgent(result)}
/>

API

Mood Wheel

options
Exactly five value and label pairs.
value / defaultValue
Choose controlled or uncontrolled state.
onChange / onDetent
Receive the option, index, and input source after a selection or detent.
sound
Enables the optional detent sound.
messages / assets
Replace accessible copy or the exact shipped pointer, wheel, and tick assets.
intro
Set limited or always; tune its play limit and storage key.
presentation
Use disabled, controls, hint, layout, CSS class/style, custom icons, ambient velocity, and interaction gating.

Agent Questions

questions
A validated set of choice, text, scale, time, and five-stop mood questions.
initial state
Resume with initialAnswers and initialStep.
sound / assets
Enable the bundled reveal and answer cues, or supply your own URLs.
copy / theme
Set title, status, locale, messages, aria label, and classic or ribbon theme.
callbacks
Handle answer, skip, complete, step-change, and dismissal receipts in your app.
voice / options
Provide transcribe for local browser recordings and renderOption for custom choice rendering.
agent schema
Export AGENT_QUESTIONS_TOOL to your model and parse its result before rendering.

Both packages are MIT licensed and built for React 18 and 19. Read the source and API documentation.

Your choice

Optional PostHog analytics

If you allow it, PostHog EU Cloud receives article views and Muzluk link clicks with the page language, article slug, category or link intent, an anonymous browser identifier, and ordinary device, browser, URL, and network metadata. We use it only to understand and improve this Help Center, and retain events for no more than 12 months.

PostHog receives nothing unless you choose Allow. Autocapture, profiles, advertising, automatic page views, performance capture, and session replay are disabled. You can withdraw later on the Privacy page.

Separate from this choice, cookie-free Vercel Web Analytics counts aggregate visits and Core Web Vitals under our legitimate interest. It receives no search text or query strings and uses no persistent cross-site identifier.