@pilotiq/tiptap 3.10.8 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @pilotiq/tiptap
2
2
 
3
+ ## 3.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 30a802e: `CollabTextRenderer` (the Tiptap surface behind collab/AI `TextField` / `TextareaField`) now renders whole-field AI suggestions through `AiInlineDiffExtension` + `AiSuggestionBanner` — the same red/green inline diff and amber Accept/Reject banner `RichTextField` uses — instead of the legacy green-pill chip with ✓/✕. One review surface across every text shape. The chip bridge stays mounted for producer-supplied `editorRange` suggestions; `onApplyWholeField` remains the fallback when a suggestion can't parse.
8
+
3
9
  ## 3.10.8
4
10
 
5
11
  ### Patch Changes
@@ -129,7 +129,7 @@ var require_markdown_it_task_lists = __commonJS({
129
129
  }
130
130
  });
131
131
 
132
- // ../../node_modules/.pnpm/tiptap-markdown@0.9.0_@tiptap+core@3.22.4_@tiptap+pm@3.22.4_/node_modules/tiptap-markdown/dist/tiptap-markdown.es.js
132
+ // ../../node_modules/.pnpm/tiptap-markdown@0.9.0_@tiptap+core@3.26.0_@tiptap+pm@3.26.0_/node_modules/tiptap-markdown/dist/tiptap-markdown.es.js
133
133
  import { Extension, Mark as Mark2, getHTMLFromFragment, Node as Node$1, extensions } from "@tiptap/core";
134
134
 
135
135
  // ../../node_modules/.pnpm/orderedmap@2.1.1/node_modules/orderedmap/dist/index.js
@@ -8859,7 +8859,7 @@ var MarkdownSerializerState = class {
8859
8859
  }
8860
8860
  };
8861
8861
 
8862
- // ../../node_modules/.pnpm/tiptap-markdown@0.9.0_@tiptap+core@3.22.4_@tiptap+pm@3.22.4_/node_modules/tiptap-markdown/dist/tiptap-markdown.es.js
8862
+ // ../../node_modules/.pnpm/tiptap-markdown@0.9.0_@tiptap+core@3.26.0_@tiptap+pm@3.26.0_/node_modules/tiptap-markdown/dist/tiptap-markdown.es.js
8863
8863
  var import_markdown_it_task_lists = __toESM(require_markdown_it_task_lists(), 1);
8864
8864
  import { Fragment as Fragment2, DOMParser } from "@tiptap/pm/model";
8865
8865
  import { Plugin, PluginKey } from "@tiptap/pm/state";
@@ -1,11 +1,15 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef } from 'react';
3
3
  import { useEditor, EditorContent } from '@tiptap/react';
4
+ import { Slice } from '@tiptap/pm/model';
4
5
  import { useCollabRoom, getCollabExtensions, } from '@pilotiq/pilotiq/react';
5
6
  import { useCollabSeed } from '@rudderjs/sync/react';
6
7
  import { createPlainTextEditor, plainTextOf, plainTextToDoc } from '../PlainTextEditor.js';
7
8
  import { AiSuggestionExtension } from '../extensions/AiSuggestionExtension.js';
9
+ import { AiInlineDiffExtension } from '../extensions/AiInlineDiffExtension.js';
8
10
  import { useAiSuggestionBridge } from './useAiSuggestionBridge.js';
11
+ import { useAiInlineDiff, useIsAiInlineDiffActive } from './useAiInlineDiff.js';
12
+ import { AiSuggestionBanner } from './AiSuggestionBanner.js';
9
13
  /**
10
14
  * Tiptap-backed plain-text editor for pilotiq's `TextField` / `TextareaField`
11
15
  * / similar single-line / multi-line text fields when collab is on.
@@ -93,12 +97,14 @@ export function CollabTextRenderer({ name, fragmentKey, multiline, defaultValue,
93
97
  // seeds the fragment on first connect when it's still empty. When
94
98
  // collab is off, seed from defaultValue directly.
95
99
  content: collabActive ? '' : defaultValue,
96
- // AI suggestions — always-on extension that tracks suggested edits as
97
- // inline strikethrough + Approve/Reject chip widgets. Idle until the
98
- // host calls `editor.commands.addAiSuggestion(...)` via the bridge below.
99
- // Matches the `TiptapEditor` wiring so suggestion mode works uniformly
100
+ // AI suggestions — chip extension (producer-supplied range
101
+ // suggestions) + inline-diff extension (whole-field suggestions:
102
+ // red strikethrough on removed runs, green on inserted, with the
103
+ // `<AiSuggestionBanner>` Accept / Reject below). Both idle until
104
+ // a suggestion arrives via the bridges below. Matches the
105
+ // `TiptapEditor` wiring so the review surface reads identically
100
106
  // across RichTextField / MarkdownField / TextField+TextareaField.
101
- extensions: [...collabExtensions, AiSuggestionExtension],
107
+ extensions: [...collabExtensions, AiSuggestionExtension, AiInlineDiffExtension],
102
108
  onUpdate: (text) => onChange(text),
103
109
  ...(onSubmit ? { onSubmit: () => { onSubmit(); return false; } } : {}),
104
110
  ...(className || editorAttributes
@@ -125,26 +131,35 @@ export function CollabTextRenderer({ name, fragmentKey, multiline, defaultValue,
125
131
  // `<PendingSuggestionsContext>` queue with the editor's `AiSuggestion`
126
132
  // extension. No-op when no provider is mounted (default no-op context).
127
133
  //
128
- // Whole-field fallback: chat-driven suggestions (e.g. `update_form_state`)
129
- // arrive without `meta.editorRange`. Plain-text editors opt into a
130
- // synthesized full-doc range so the inline-diff chip (red strikethrough on
131
- // the current value + green chip with the suggested text + ✓/✕ buttons)
132
- // renders BEFORE the user approves. The extension's `applyApprove` is
133
- // text-node-based which fits the plain-text schema exactly. The
134
- // `onApplyWholeField` callback stays as a fallback for cases that don't
135
- // synthesize (e.g. an empty doc — `from === to` skips the chip but the
136
- // applier still needs to swap content).
134
+ // Whole-field suggestions do NOT synthesize a chip range anymore —
135
+ // they render through `useAiInlineDiff` below (same red/green inline
136
+ // diff + banner as `TiptapEditor`), replacing the old green-pill chip
137
+ // that read differently from the rich-text surface. The bridge stays
138
+ // mounted for producer-supplied `meta.editorRange` suggestions (precise
139
+ // anchors worth visualizing in place) and as the `onApplyWholeField`
140
+ // fallback when the diff path can't parse a suggestion.
141
+ const applyWholeField = (value) => {
142
+ if (!editor || editor.isDestroyed)
143
+ return;
144
+ editor.commands.setContent(plainTextToDoc(value, !!multiline));
145
+ };
137
146
  useAiSuggestionBridge(editor ?? null, name, {
138
- synthesizeWholeFieldRange: (ed) => ({
139
- from: 0,
140
- to: ed.state.doc.content.size,
141
- }),
142
- onApplyWholeField: (value) => {
143
- if (!editor || editor.isDestroyed)
144
- return;
145
- editor.commands.setContent(plainTextToDoc(value, !!multiline));
147
+ onApplyWholeField: applyWholeField,
148
+ });
149
+ // Inline diff for whole-field suggestions — plain-text shape: each
150
+ // line wraps in a `paragraph` node, mirroring `plainTextToDoc`.
151
+ useAiInlineDiff(editor ?? null, name, {
152
+ parseSuggestion: (ed, value) => {
153
+ try {
154
+ const node = ed.schema.nodeFromJSON(plainTextToDoc(value, !!multiline));
155
+ return new Slice(node.content, 0, 0);
156
+ }
157
+ catch {
158
+ return null;
159
+ }
146
160
  },
147
161
  });
162
+ const isDiffActive = useIsAiInlineDiffActive(editor ?? null);
148
163
  // First-load seed when collab is active. Collaboration starts the editor
149
164
  // empty regardless of `defaultValue`; once the room's first sync
150
165
  // resolves, `useCollabSeed` runs the callback inside `ydoc.transact`.
@@ -197,5 +212,13 @@ export function CollabTextRenderer({ name, fragmentKey, multiline, defaultValue,
197
212
  onChange(plainTextOf(editor));
198
213
  // eslint-disable-next-line react-hooks/exhaustive-deps
199
214
  }, [editor]);
200
- return _jsx(EditorContent, { editor: editor });
215
+ // Banner mounts below the editor exactly like `TiptapEditor`'s — it
216
+ // renders nothing while no suggestion is pending for this field, so
217
+ // the single-line text surface keeps its normal footprint.
218
+ return (_jsxs(_Fragment, { children: [_jsx(EditorContent, { editor: editor }), _jsx(AiSuggestionBanner, { fieldName: name, onApplyWholeField: applyWholeField, ...(isDiffActive && editor
219
+ ? {
220
+ onAcceptViaEditor: () => editor.commands.acceptAiInlineDiff(),
221
+ onRejectViaEditor: () => editor.commands.rejectAiInlineDiff(),
222
+ }
223
+ : {}) })] }));
201
224
  }
@@ -40,6 +40,9 @@ const globals = {
40
40
  KeyboardEvent: window.KeyboardEvent,
41
41
  CustomEvent: window.CustomEvent,
42
42
  DocumentFragment: window.DocumentFragment,
43
+ // prosemirror-view ≥1.41 probes `root instanceof ShadowRoot` while
44
+ // resolving the editor's event root.
45
+ ShadowRoot: window.ShadowRoot,
43
46
  Range: window.Range,
44
47
  Selection: window.Selection,
45
48
  MutationObserver: window.MutationObserver,
@@ -57,6 +60,15 @@ const globals = {
57
60
  for (const [k, v] of Object.entries(globals)) {
58
61
  Object.defineProperty(globalThis, k, { value: v, writable: true, configurable: true });
59
62
  }
63
+ // jsdom has no layout engine and never implements elementFromPoint;
64
+ // prosemirror-view ≥1.41 calls it during view initialization.
65
+ if (typeof window.document.elementFromPoint !== 'function') {
66
+ Object.defineProperty(window.document, 'elementFromPoint', {
67
+ value: () => null,
68
+ writable: true,
69
+ configurable: true,
70
+ });
71
+ }
60
72
  // React 19 + RTL require `IS_REACT_ACT_ENVIRONMENT` so `act()` warnings
61
73
  // don't fire on every render. Without it, Tiptap's mount cascade
62
74
  // produces dozens of warnings that swamp real test failures.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilotiq/tiptap",
3
- "version": "3.10.8",
3
+ "version": "3.11.0",
4
4
  "description": "Tiptap rich-text editor adapter for @pilotiq/pilotiq — slash menu, draggable blocks, custom-block API",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -91,7 +91,7 @@
91
91
  "react-dom": "^19",
92
92
  "tiptap-markdown": "^0.9",
93
93
  "typescript": "^5",
94
- "@pilotiq/pilotiq": "^0.35.2"
94
+ "@pilotiq/pilotiq": "^0.37.0"
95
95
  },
96
96
  "author": "Suleiman Shahbari",
97
97
  "scripts": {