ToneSlider is a dynamic web application that allows users to transform the tone of their text using a slider interface β shifting from formal to casual language or anywhere in between. Powered by Mistral AI and optimized with Upstash Redis caching, ToneSlider also provides an intuitive undo/redo experience to enable flexible and safe experimentation.
FInal.Output.-.Made.with.Clipchamp.mp4
- ποΈ Adjust tone via slider (formal β casual)
- β‘ Uses Mistral AI to rewrite text with natural tone changes
- β»οΈ Undo, Redo, and Reset state at any point
- π§ Caches results using Upstash Redis to reduce API calls
- πΎ Persists local session state using
localStorage
- π§― Friendly error handling with
sonner
toasts - π Deployed on Vercel
/adjust-tone
endpoint:- Receives text and
toneLevel
- Classifies tone level (0β100) into tone buckets (e.g., "very formal", "casual")
- Constructs a prompt for the Mistral AI API
- Returns the rewritten text to the client
- Caches results in Upstash Redis using a composite key:
text:toneLevel
- Receives text and
- Prevents redundant tone adjustments for the same input and level
- Uses
client.setEx()
to expire cached results after 1 hour (3600s) - Benefits:
- Improves response time
- Reduces cost and API call rate
useToneAdjustment()
manages:- State (
content
,toneLevel
) - API interaction
- Undo/Redo stack
- Toast notifications
- State (
- UI includes:
- Text input area
- Tone level slider
- Undo/Redo buttons
- Reset button with confirmation toast
- EmailJs for subscribing newsletters
-
Frontend sends a
POST
request to/adjust-tone
with:text
: User input texttoneLevel
: Slider value (0β100)
-
The backend:
- Generates a cache key:
text:toneLevel
- Checks Upstash Redis: If the response exists, it returns cached content.
- If not cached:
- Constructs a tone descriptor (e.g., "very formal", "casual", etc.)
- Sends the prompt to the Mistral API
- Caches the result for 1 hour using
client.setEx()
- Returns the rewritten text to the frontend
- Generates a cache key:
We use Upstash Redis for:
- Low latency: Cache responses close to users (global edge caching)
- Cost efficiency: Reduces repeated API calls to Mistral
- TTL (Time-To-Live): Cached responses expire after
3600 seconds (1 hour)
Cache Key Format:
${text}:${toneLevel}
Redis.-.Made.with.Clipchamp.mp4
Tone is decided based on the toneLevel
value:
Tone Level | Description |
---|---|
0β24 | Very formal, professional, precise |
25β49 | Somewhat formal and professional |
50β74 | Conversational and friendly |
75β100 | Casual, relaxed, informal |
ToneSlider offers non-destructive editing through a custom undo/redo stack, enabling users to explore different tone levels freely and revert changes anytime. This ensures that users always feel safe making adjustments, knowing they can go back to previous versions of their text.
- The main state (
content
andtoneLevel
) is managed using a custom React hook:useUndoRedo(initialState)
. - This hook maintains a history array and a
currentIndex
pointer that lets us track and move through previous states like a version history. - The
useToneAdjustment()
hook wrapsuseUndoRedo
and provides logic to:- Trigger tone adjustment using the Mistral API.
- Update state when the text or tone level changes.
- Handle undo, redo, and reset operations.
- Persist the state in
localStorage
so changes aren't lost on refresh.
- On component mount, the saved state is retrieved from
localStorage
and loaded. - Any change to the state is automatically saved back to
localStorage
.
-
Initial text is loaded or typed by the user
-
When tone is adjusted, the result is stored as a new state in the history
User can now: β©οΈ Undo β Go back in history (previous state) βͺοΈ Redo β Move forward in history π Reset β Go back to the very first state(default)
UndoRedo.-.Made.with.Clipchamp.mp4
Edge Case | Handling Strategy |
---|---|
π Repeated request | Redis returns cached response instantly |
π Mistral API fails | Fallback to original text + error message |
π Redis not reachable | Try-catch handles it and sends original text |
β CORS issues | cors() is enabled for all origins (origin: '*' ) |
π§ Unexpected model behavior | Prompt instructs Mistral to only change tone, not meaning |
To enhance user experience, use toasts for feedback:
- β
Tone adjusted successfully!
β οΈ Failed to adjust tone. Showing original text.
- β
Failed to adjust tone. Please reload and try again
ErrorHandling_1.-.Made.with.Clipchamp.mp4
EmailJS.Success.-.Made.with.Clipchamp.mp4
EmailJS.Error.-.Made.with.Clipchamp.mp4
- Avoids frustration from accidental changes
- Encourages experimentation with tone variations
- Makes the app feel responsive and intelligent
Before setting up the project, ensure you have the following installed:
Node.js (version 16 or higher)
npm (comes with Node.js)
Before getting into frontend part , setup the backend run it perfectly. Backend repo : https://github.com/isid555/ToneSlider-Backend
- Clone the repository:
git clone https://github.com/isid555/ToneSlider.git
cd ToneSlider
- Install dependencies:
npm install
- Start the server
npm run dev
Weβd love your feedback, bug reports, and contributions! If you want to suggest tone mapping improvements, caching strategies, or new UI features, feel free to open an issue or a PR on GitHub.
π GitHub Repo β https://github.com/isid555/ToneSlider
Whether you're into frontend, backend, prompt engineering, or UX designβwe'd love your help making ToneSlider even better!
π§ Open a PR π Report an issue β Star the repo π¬ Suggest a feature
π Try It Live π https://tone-slider.vercel.app/
π οΈ Repo & Backend π https://github.com/isid555/ToneSlider-Backend