Vidura.ai·Case study

An editor that waits for you to decide.

A hiring assignment asked for a callout component in a headless text editor. The checklist was the easy part — the real problem was that every callout forces a decision at the exact moment a writer is trying not to break their flow.

Role
Interaction design, UX & frontend build
Origin
Technical assessment → personal project
Built with
Tiptap · ProseMirror · Next.js
Focus
Editor interaction design
Vidura editor — a formatting toolbar, a green best-practice callout nested inside a blue information callout, and the Ask Vidura inline prompt reading “Make it sound more exciting and professional”
01Overview

The brief asked me to build a callout component. I spent my time on the moment you insert one.

Vidura began as a founding-frontend-engineer assessment: build an editor on Tiptap, add a custom callout node, and design an AI-powered inline edit feature. Those requirements were a checklist, and checklists are straightforward. What wasn’t on the list was the interesting part — inserting a callout secretly bundles two decisions into one keystroke, and most editors make you pay for both separately.

The ask

A callout node with four types.

The reframe

A decision the writer hasn't made yet.

The result

Insertion and choice as one gesture.

02The Brief

Three parts, one underlying constraint.

Proteanics’ assessment for a founding frontend engineer. The deliverables were specific; the interface was entirely undefined — which is the whole point of building on a headless framework.

Part 1

Editor foundation

Stand up a working editor on Tiptap's StarterKit — paragraphs, headings, lists, the usual node types.

TiptapStarterKit
Part 2

A custom callout node

One node with a calloutType attribute covering four types. Insertable by keyboard shortcut, switchable between types, styled per type, aware of which type is active — and nestable inside itself.

Custom nodeKeyboardNesting
Part 3

AI inline edits

Select text, invoke by shortcut, describe a change, send it to an LLM, show a diff, accept or reject — with a hard rule that the result must stay valid for the editor's schema. Tiptap's own AI plugin was off-limits.

LLMDiff viewSchema safety

The underlying constraint

Tiptap is headless. It ships behaviour, not interface — no buttons, no menus, no opinions about how any of it should look or feel. Every affordance in Vidura had to be designed from nothing. That’s a burden and a gift: there was no default to inherit, so there was no default to hide behind.

03The Name

Named after a counsellor, not a clerk.

In the Mahabharata, Vidura is the advisor known for intelligence, moral clarity, and the willingness to give honest counsel to people who didn’t always want to hear it. The name came before most of the interface did — and then quietly set the standard for it.

“A fitting namesake for a smart editor designed to assist and guide users through content creation.”
From the project’s design rationale

An assistant that counsels behaves differently from one that merely obeys. It offers, it doesn’t interrupt. It waits for you to be ready. It stays out of the way until asked, and when asked, it shows its reasoning rather than silently overwriting your work.

Every decision that follows — the deferred insert, the diff view, the shortcut that doesn’t fire until you release it — traces back to that one word.

04Decision Timing

Inserting a callout is two decisions wearing one costume.

“I want a callout here” and “it should be a warning” feel like a single action, but they aren’t. The first is instant. The second often isn’t — you may not know the type until you’ve started writing it. Most editors resolve this by making you commit twice.

The conventional path

  1. 1Press the shortcut — a default callout appears.
  2. 2Realise it's the wrong type.
  3. 3Stop writing, reach for a menu, change it.

Two commitments, one interruption — and the interruption lands mid-sentence.

Vidura’s path

  1. 1Hold Ctrl Shift Enter — the callout opens.
  2. 2Still holding, tap Ctrl Shift \ to cycle types.
  3. 3Release. The type you landed on is the one that commits.

One continuous gesture. The decision resolves before your hands ever leave the keys.

The shortcut deliberately does not fire on key-down. Deferring the commit to key-release is what turns two actions into one.

Why this particular chord

Ctrl Shift Enter is reachable with both hands at a natural span — no thumb contortion, no strain, and little chance of triggering it by accident. It also stays clear of the shortcuts browsers and editors have already claimed.

Friction in the right place

A shortcut that’s trivial to hit by accident is worse than one that takes a beat to learn. This one asks for a deliberate press — and rewards it by never needing a follow-up correction.

05Callout System

One node, four states — not four nodes.

The brief specified a single callout node carrying a calloutType attribute. That constraint is doing real work: because the type is an attribute rather than a separate node, switching between types is a change of state, not a delete-and-reinsert. Your content, your cursor, and your undo history all survive the switch.

calloutType = “info”

Tiptap is headless — it ships behaviour, not interface.

calloutType = “best”

Reach for Tiptap first; drop to ProseMirror only when you must.

calloutType = “warning”

An LLM returns text. Your editor needs a valid document tree.

calloutType = “error”

Invalid nodes get silently dropped on paste. Guard the schema.

The four callout types, recreated in code. Type drives the container styling — fill, rule, icon, and text colour move together.

Callouts inside callouts

Nesting was an explicit requirement, and it’s the case that breaks naïve implementations — a callout has to be able to contain block content, including another callout, without the styling or the schema collapsing.

Tiptap is headless — it ships behaviour, not interface.

Reach for Tiptap first; drop to ProseMirror only when you must.

A best-practice callout nested inside an information callout — each keeps its own type, styling, and behaviour.

06Interactions

Three ways in, because hands are already somewhere.

A writer mid-paragraph wants the keyboard. A writer scanning the page wants the toolbar. A writer who half-remembers the feature wants to type / and be reminded. Vidura supports all three rather than picking a favourite.

Toolbar dropdownSlash commandCtrl + Shift + Enter

Orientation

A toolbar that mirrors where you are

The callout button restyles itself to match the type of the callout your cursor is sitting in. In a long document, it doubles as a position indicator — you can tell what kind of block you're inside without looking away from the text.

Discovery

Slash commands, hand-rolled

Built on a custom suggestion system rather than an off-the-shelf plugin. That cost more time, and bought full control over keyboard navigation, selection behaviour, and styling — the three things that decide whether a slash menu feels native or bolted on.

Familiarity

Backspace to dissolve

Press Backspace in an empty callout and the whole block disappears. It's the pattern Notion uses, which means most writers already have the muscle memory. Borrowed familiarity beats invented cleverness.

07Ask Vidura

The AI proposes. You still decide.

Part 3 of the brief: let a writer select text, describe a change, send it to an LLM, and see the result as a reviewable diff — built from scratch, without Tiptap’s own AI plugin. The counsel principle lands hardest here. An advisor that rewrites your document behind your back isn’t an advisor.

01

Select

Highlight the text you want changed.

02

Invoke

A shortcut opens a small prompt beside it.

03

Instruct

Describe the change in your own words.

04

Review

A diff shows exactly what would change.

05

Decide

Accept it, or reject it and keep your draft.

A prompt that stays next to the sentence

The input appears beside the selection rather than in a sidebar or modal, so the text being changed never leaves your eye-line.

Ask Vidura

Make it sound more exciting and professional
RunEnter

A diff you can read at a glance

Removals and additions sit inline, in place, so the change is legible as a change — not as a mysterious new paragraph.

This is a text editor that helps you write better content. Vidura is a writing environment that sharpens every draft you bring to it.

AcceptReject

The hard part

An LLM returns a blob of text. An editor needs a valid document tree — one that still understands custom nodes it has never heard of.

A rewrite that quietly flattens a nested warning callout into a paragraph is a data-loss bug wearing the costume of a feature. Any honest implementation has to treat the model’s output as untrusted input and reconcile it against the editor’s schema before a single character reaches the document.

08Under the Hood

Built to be extended, not just demonstrated.

Tiptap sits on top of ProseMirror, so the rule of thumb was to stay in Tiptap’s ergonomics wherever it could express the idea, and drop to ProseMirror’s primitives only where it genuinely couldn’t.

Next.js 14

App Router

React 19

UI layer

TypeScript

Type safety

Tiptap

Editor framework

ProseMirror

Document model

Tailwind CSS

Styling

shadcn/ui

Accessible primitives

Why headless at all?

A pre-built editor would have made Parts 1 and 2 trivial and the design work impossible. Headless means every affordance is a decision you have to justify.

Why hand-roll the slash menu?

Off-the-shelf suggestion plugins impose their own keyboard and selection behaviour. Owning that layer was the only way to make the menu feel like part of the editor.

Why shadcn/ui?

Accessible, unopinionated primitives that don't fight Tailwind — polish without inheriting somebody else's theme.

Why keep styling minimal?

The interesting work here is behavioural. Heavy theming would have dressed up the demo while hiding whether the interactions actually hold together.

09Reflection

The assignment ended. The project didn’t.

Vidura was submitted as a technical assessment, and it would have been reasonable to archive it there. I kept building instead — because somewhere between the callout node and the diff view it stopped being a deliverable and started being a question I actually wanted to answer: how much of a writing tool’s quality lives in the half-second around a keystroke?

01

Constraints are a design brief in disguise

“One node, one calloutType attribute” reads like a technical detail. It's actually the thing that makes switching types non-destructive.

02

The interesting problems hide between the requirements

Nothing in the brief mentioned when a decision should commit. That gap turned out to hold the best idea in the project.

03

Familiarity is a feature worth borrowing

Backspace-to-delete wasn't invention, it was recognition — and it needed no explanation to anyone who has used Notion.

Still in progress

What exists today is the shape the assessment asked for. The next pass is mine — extending Vidura past the brief into the writing tool I’d actually want to use every day.

In closing

Good tools don’t interrupt. They wait, and then they counsel.

Vidura is a small editor with an unreasonable amount of thought in its keystrokes — a callout that commits when you’re ready, a toolbar that tells you where you are, and an AI that shows its work before it touches yours.

Designed & built by Divyansh Mudgil

Connect