AI Caret

The AiCaret extension displays a cursor decoration at the position where the AI is currently inserting content. It provides real-time visual feedback during streaming operations, similar to the Collaboration Caret extension.

Usage

Import the AiCaret extension from @tiptap-pro/ai-toolkit and add it to your editor extensions.

import { AiCaret, AiToolkit } from '@tiptap-pro/ai-toolkit'

const editor = useEditor({
  extensions: [StarterKit, AiToolkit, AiCaret],
})

The caret appears automatically during streaming operations like streamHtml, streamTool, and tiptapEditWorkflow. It disappears after the streaming finishes.

Configuration

You can configure the caret's timeout, user label, and rendering.

AiCaret.configure({
  // How long the caret stays visible after the last update (ms)
  timeout: 2000,
  // User details for the caret label
  user: {
    name: 'AI',
    color: '#a5b4fc',
  },
})

Options

OptionTypeDefaultDescription
timeoutnumber2000How long the caret stays visible after the last update (ms).
user{ name: string, color: string }{ name: 'AI', color: '#a5b4fc' }The name and color displayed on the caret label.
render(user: AiCaretUser) => HTMLElementBuilt-in render functionCustom function to render the caret DOM element.

CSS styles

The default render function creates a DOM element with the class tiptap-ai-caret and a label with the class tiptap-ai-caret__label. Add the following CSS to style the caret:

.tiptap .tiptap-ai-caret {
  border-left: 1px solid #0d0d0d;
  border-right: 1px solid #0d0d0d;
  margin-left: -1px;
  margin-right: -1px;
  pointer-events: none;
  position: relative;
  word-break: normal;
}

.tiptap .tiptap-ai-caret__label {
  border-radius: 3px 3px 3px 0;
  color: #0d0d0d;
  font-size: 12px;
  font-style: normal;
  font-weight: 600;
  left: -1px;
  line-height: normal;
  padding: 0.1rem 0.3rem;
  position: absolute;
  top: -1.4em;
  user-select: none;
  white-space: nowrap;
}

The border-color and background-color of the caret and label are set inline based on the user.color option.

Custom rendering

You can provide a custom render function to fully control the caret DOM element.

AiCaret.configure({
  render: (user) => {
    const el = document.createElement('span')
    el.className = 'my-custom-ai-caret'
    el.style.borderColor = user.color
    el.textContent = user.name
    return el
  },
})

Events

The AiCaret extension emits events through the Tiptap events API. You can subscribe to them to react to the caret's movement — for example, to follow the AI's writing position with custom UI behavior.

Available events

EventDescriptionPayload
aiCaret:positionChangedFired whenever the AI caret position is updated during a streaming or edit operation.{ position: number }

The position value is a ProseMirror document position pointing to where the AI caret is currently placed.

Subscribing to events

Use editor.on to listen for the event, and editor.off to unsubscribe when no longer needed.

editor.on('aiCaret:positionChanged', ({ position }) => {
  console.log('AI caret moved to position', position)
})

The aiCaret:positionChanged event can fire frequently during streaming — once per streamed chunk. If your handler performs expensive work, consider throttling it with requestAnimationFrame or a utility like lodash.throttle.