Skip to content

feat: add the streaming-reveal extension to @tiptap/ai-toolkit - #8109

Open
ozdemircibaris wants to merge 12 commits into
mainfrom
feat/streaming-reveal-extension
Open

feat: add the streaming-reveal extension to @tiptap/ai-toolkit#8109
ozdemircibaris wants to merge 12 commits into
mainfrom
feat/streaming-reveal-extension

Conversation

@ozdemircibaris

Copy link
Copy Markdown
Contributor

Fixes

N/A (new feature)

Changes and Review

  • Adds the AiInsertReveal extension (@tiptap/ai-toolkit/streaming-reveal): fades in text as the AI streams it into a collaborative document.
  • View-only decorations from Yjs insert deltas, so it adds no marks and does not mutate the document. Requires the Collaboration extension.
  • Verify with the streaming demo (revealAiText) or streaming-reveal.spec.ts.

Checklist

  • I have added a changeset if necessary.
  • I have added tests if possible.
  • I have made sure to test my changes myself.

Responsibility

  • I have reviewed and understand these changes, and I take responsibility for this PR, even if an AI agent created it.

@ozdemircibaris
ozdemircibaris requested a review from a team as a code owner July 22, 2026 11:32
@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 696d2cf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tiptap/ai-toolkit Minor
@tiptap/server-ai-toolkit Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jul 22, 2026

Copy link
Copy Markdown

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit 696d2cf
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/6a63228cf9e2240008c26c4d
😎 Deploy Preview https://deploy-preview-8109--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 734602a4-afd2-4d6e-a341-f5f05a8de97b

📥 Commits

Reviewing files that changed from the base of the PR and between d6da91e and 696d2cf.

📒 Files selected for processing (2)
  • packages/ai-toolkit/src/streaming-reveal.spec.ts
  • packages/ai-toolkit/src/streaming-reveal.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ai-toolkit/src/streaming-reveal.spec.ts
  • packages/ai-toolkit/src/streaming-reveal.ts

📝 Walkthrough

Summary

Adds AiInsertReveal to @tiptap/ai-toolkit to fade in text that comes from AI streaming into a collaborative ProseMirror document via Yjs.

  • Adds a new @tiptap/ai-toolkit/streaming-reveal export.
  • Works by adding view-only inline decorations (no marks, no document changes).
  • Only reacts to remote Yjs inserts (ignores local typing), and removes the fade effect after durationMs.
  • Requires the Collaboration extension to resolve reveal positions.
  • Includes tests (streaming-reveal.spec.ts) and a minor-release changeset.

Walkthrough

Adds AiInsertReveal to fade in remote Yjs text insertions, with configurable styling and duration. The package builds and exports the new entry point, declares optional collaboration peers, and adds tests plus a minor changeset.

Changes

Streaming reveal

Layer / File(s) Summary
Reveal tracking and decorations
packages/ai-toolkit/src/streaming-reveal.ts
Tracks remote Yjs inserts and renders temporary inline decorations while ignoring local edits.
Collaborative behavior validation
packages/ai-toolkit/src/streaming-reveal.spec.ts
Tests configuration, remote and local edits, decoration ranges, expiration, size limits, non-collaborative behavior, and cleanup.
Build and package publication
packages/ai-toolkit/tsup.config.ts, packages/ai-toolkit/package.json, .changeset/new-pots-admire.md
Builds and exports streaming-reveal, declares optional collaboration peers, and records a minor release.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RemoteYjsDoc
  participant AiInsertReveal
  participant TiptapEditor
  RemoteYjsDoc->>AiInsertReveal: Apply remote Yjs insertion
  AiInsertReveal->>AiInsertReveal: Track inserted text run
  AiInsertReveal->>TiptapEditor: Schedule decoration render
  TiptapEditor->>AiInsertReveal: Resolve document positions
  AiInsertReveal-->>TiptapEditor: Render fading inline decoration
Loading

Suggested labels: area: ai, area: editor, complexity: hard, impact: low

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: adding the streaming-reveal extension to @tiptap/ai-toolkit.
Description check ✅ Passed The description follows the template with Fixes, Changes and Review, Checklist, and Responsibility, and includes the key testing notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added area: ai Related to AI/LLM features area: editor Editor behavior, commands, and transactions complexity: hard Multiple components, research needed impact: low No direct user impact labels Jul 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/ai-toolkit/src/streaming-reveal.spec.ts (1)

157-167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider fake timers for the expiry test.

This test relies on real wall-clock delays (30ms duration + 60ms wait) to prove expiry. It'll pass most of the time, but could get flaky on a slow/loaded CI runner. vi.useFakeTimers() + vi.advanceTimersByTime() (or mocking Date.now) would make it deterministic.

As per coding guidelines, "Add or update unit tests for deterministic user-visible behavior, preferring unit tests over end-to-end tests when appropriate."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-toolkit/src/streaming-reveal.spec.ts` around lines 157 - 167,
Update the “drops the reveal once its duration has elapsed” test to use Vitest
fake timers instead of real setTimeout delays. Enable fake timers before
creating the editor, advance them beyond the configured duration to trigger
expiry, assert the decoration is removed, and restore real timers afterward.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ai-toolkit/src/streaming-reveal.spec.ts`:
- Around line 157-167: Update the “drops the reveal once its duration has
elapsed” test to use Vitest fake timers instead of real setTimeout delays.
Enable fake timers before creating the editor, advance them beyond the
configured duration to trigger expiry, assert the decoration is removed, and
restore real timers afterward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: d98bc6de-cc92-432c-96ad-6088a795f294

📥 Commits

Reviewing files that changed from the base of the PR and between a963d48 and 326505e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • .changeset/new-pots-admire.md
  • packages/ai-toolkit/package.json
  • packages/ai-toolkit/src/streaming-reveal.spec.ts
  • packages/ai-toolkit/src/streaming-reveal.ts
  • packages/ai-toolkit/tsup.config.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 22, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 22, 2026
bdbch
bdbch previously approved these changes Jul 23, 2026

@bdbch bdbch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM just a few nitpicks around comment lengths

Comment on lines +25 to +27
* Upper bound (chars) on a single revealed run. A normal streamed token is a
* few chars; this only guards against a mis-resolved relative position yielding
* an absurd range (e.g. spanning the whole document).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what exactly is a revealed run? Sometimes I think these comments should describe things more general so someone who doesn't already have a large grasp of the context of the feature can understand the code.

Comment on lines +178 to +180
// Seed the CSS animation from the run's real age so a re-render
// (y-tiptap rebuilds the doc on every token) resumes the fade at the
// correct point instead of restarting it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Seed the CSS animation from the run's real age so a re-render
// (y-tiptap rebuilds the doc on every token) resumes the fade at the
// correct point instead of restarting it.
// Offset the animation by the run's age so token updates resume the fade instead of restarting it.

@ozdemircibaris
ozdemircibaris dismissed stale reviews from bdbch and coderabbitai[bot] via d6da91e July 23, 2026 12:17
@coderabbitai coderabbitai Bot added complexity: medium Moderate change, possibly multiple files and removed area: editor Editor behavior, commands, and transactions complexity: hard Multiple components, research needed labels Jul 23, 2026
@coderabbitai coderabbitai Bot removed the impact: low No direct user impact label Jul 23, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 23, 2026
@ozdemircibaris
ozdemircibaris requested a review from bdbch July 23, 2026 13:22

@bdbch bdbch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd shorten the comments.

Comment on lines +65 to +69
/**
* Applies a remote (non-local) insert into the first paragraph's text, mimicking
* an AI streaming into the shared document from another peer. Uses a second Y.Doc
* synced from `ydoc` so the resulting transaction has `local === false`.
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Applies a remote (non-local) insert into the first paragraph's text, mimicking
* an AI streaming into the shared document from another peer. Uses a second Y.Doc
* synced from `ydoc` so the resulting transaction has `local === false`.
*/
/** Applies a remote insert via a synced second Y.Doc. */

Comment on lines +24 to +29
/**
* Upper bound (chars) on one revealed run: the text from a single streamed
* insert, usually a token of a few chars. This only guards against a
* mis-resolved relative position yielding an absurd range (e.g. spanning the
* whole document).
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Upper bound (chars) on one revealed run: the text from a single streamed
* insert, usually a token of a few chars. This only guards against a
* mis-resolved relative position yielding an absurd range (e.g. spanning the
* whole document).
*/
/** Limits each revealed streamed insert to guard against mis-resolved positions spanning the document. */

Comment on lines +32 to +36
/**
* One freshly-inserted text run, anchored by Yjs relative positions so it stays
* valid across the whole-document rebuild that y-tiptap applies on every remote
* sync (a plain ProseMirror position would be collapsed by that rebuild).
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* One freshly-inserted text run, anchored by Yjs relative positions so it stays
* valid across the whole-document rebuild that y-tiptap applies on every remote
* sync (a plain ProseMirror position would be collapsed by that rebuild).
*/
/** Anchors each inserted run with Yjs relative positions so it survives y-tiptap document rebuilds. */

Comment on lines +126 to +132
/**
* Fades in text inserted by remote Yjs transactions using view-only inline
* decorations anchored by relative positions. It never mutates the document,
* so it stays inert to accept/reject and to persistence, and local edits are
* ignored so a user's own typing does not fade. Requires the Collaboration
* extension and CSS on the configured `className` (default `ai-insert-reveal`).
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Fades in text inserted by remote Yjs transactions using view-only inline
* decorations anchored by relative positions. It never mutates the document,
* so it stays inert to accept/reject and to persistence, and local edits are
* ignored so a user's own typing does not fade. Requires the Collaboration
* extension and CSS on the configured `className` (default `ai-insert-reveal`).
*/
/**
* Fades in remote Yjs inserts using view-only decorations, ignoring local edits and document persistence.
* Requires Collaboration and CSS for `className` (default: `ai-insert-reveal`).
*/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is outdated, but interestingly it doesn’t show as outdated

@coderabbitai coderabbitai Bot added area: editor Editor behavior, commands, and transactions complexity: hard Multiple components, research needed impact: low No direct user impact and removed complexity: medium Moderate change, possibly multiple files labels Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ai Related to AI/LLM features area: editor Editor behavior, commands, and transactions complexity: hard Multiple components, research needed impact: low No direct user impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants