@pilotiq/tiptap 3.7.0 → 3.8.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/dist/react/useAiInlineDiff.d.ts.map +1 -1
- package/dist/react/useAiInlineDiff.js +64 -10
- package/dist/react/useAiInlineDiff.js.map +1 -1
- package/dist/surgicalOps.d.ts +7 -4
- package/dist/surgicalOps.d.ts.map +1 -1
- package/dist/surgicalOps.js +39 -12
- package/dist/surgicalOps.js.map +1 -1
- package/package.json +1 -1
- package/src/react/useAiInlineDiff.ts +59 -10
- package/src/surgicalOps.ts +35 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAiInlineDiff.d.ts","sourceRoot":"","sources":["../../src/react/useAiInlineDiff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAkB7C,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;OAWG;IACH,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,IAAI,CAAA;CACjE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAMtE;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,sBAAsB,GAC9B,IAAI,
|
|
1
|
+
{"version":3,"file":"useAiInlineDiff.d.ts","sourceRoot":"","sources":["../../src/react/useAiInlineDiff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAkB7C,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;OAWG;IACH,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,IAAI,CAAA;CACjE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAMtE;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CA8GN"}
|
|
@@ -67,12 +67,41 @@ export function useAiInlineDiff(editor, fieldName, options) {
|
|
|
67
67
|
for (const s of diffable) {
|
|
68
68
|
if (startedRef.current.has(s.id))
|
|
69
69
|
continue;
|
|
70
|
-
|
|
71
|
-
// Producer should serialize calls; if not, the second suggestion
|
|
72
|
-
// sits in the queue until the first is approved/rejected.
|
|
73
|
-
if (aiInlineDiffPluginKey.getState(editor.state) !== null)
|
|
74
|
-
continue;
|
|
70
|
+
const diffActive = aiInlineDiffPluginKey.getState(editor.state) !== null;
|
|
75
71
|
const surgical = readSurgicalMeta(s);
|
|
72
|
+
// Cross-tool-call surgical stacking. When a diff is already active
|
|
73
|
+
// and a fresh surgical suggestion arrives (typically the model
|
|
74
|
+
// emitted a second `update_form_state` tool call instead of
|
|
75
|
+
// batching ops in one), fold the new modifier into the active
|
|
76
|
+
// diff. We dispatch a plain transaction with no extension meta;
|
|
77
|
+
// the plugin's existing "no explicit meta + tr.docChanged" branch
|
|
78
|
+
// adds the steps to the running changeset, so decorations update
|
|
79
|
+
// to cover both ops' ranges and the banner shows the combined
|
|
80
|
+
// count. Accept commits the union, Reject reverts to the same
|
|
81
|
+
// baseline captured when the FIRST suggestion started the diff —
|
|
82
|
+
// semantically "reject all pending suggested changes", which
|
|
83
|
+
// matches the banner copy.
|
|
84
|
+
//
|
|
85
|
+
// Whole-field suggestions still bail when a diff is active —
|
|
86
|
+
// dropping a fresh slice on top of an active review is too
|
|
87
|
+
// disruptive (it'd swap the entire doc mid-review).
|
|
88
|
+
if (diffActive) {
|
|
89
|
+
if (!surgical)
|
|
90
|
+
continue;
|
|
91
|
+
const modifier = planSurgicalModifier(editor, surgical);
|
|
92
|
+
if (!modifier)
|
|
93
|
+
continue;
|
|
94
|
+
editor.commands.command(({ tr, dispatch }) => {
|
|
95
|
+
modifier(tr);
|
|
96
|
+
if (!tr.docChanged)
|
|
97
|
+
return false;
|
|
98
|
+
if (dispatch)
|
|
99
|
+
dispatch(tr);
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
startedRef.current.add(s.id);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
76
105
|
if (surgical) {
|
|
77
106
|
const modifier = planSurgicalModifier(editor, surgical);
|
|
78
107
|
if (!modifier)
|
|
@@ -98,16 +127,41 @@ export function useAiInlineDiff(editor, fieldName, options) {
|
|
|
98
127
|
startedRef.current.delete(id);
|
|
99
128
|
}
|
|
100
129
|
}, [editor, list]);
|
|
101
|
-
// Cross-tree applier —
|
|
102
|
-
//
|
|
103
|
-
// accept
|
|
130
|
+
// Cross-tree applier — two paths:
|
|
131
|
+
//
|
|
132
|
+
// 1. Review-mode accept. Banner / chat-sidebar pill calls
|
|
133
|
+
// `pendingSuggestions.approve(id)` for a suggestion we've already
|
|
134
|
+
// started a diff for. Clear the diff state — current doc IS the
|
|
135
|
+
// accepted state.
|
|
136
|
+
// 2. Auto-mode direct apply. AI tool binding bypassed the queue and
|
|
137
|
+
// called the registry's applier with a synthetic suggestion
|
|
138
|
+
// carrying `meta.surgical` (the suggestion was never pushed to
|
|
139
|
+
// the context, so it's not in `startedRef`). Plan the modifier
|
|
140
|
+
// and dispatch it as a plain transaction — no diff overlay, no
|
|
141
|
+
// Accept / Reject step. Mirrors `set_value` auto-mode, which
|
|
142
|
+
// writes directly via the same registry.
|
|
104
143
|
useEffect(() => {
|
|
105
144
|
if (!editor)
|
|
106
145
|
return;
|
|
107
146
|
const applier = (suggestion) => {
|
|
108
|
-
if (
|
|
147
|
+
if (startedRef.current.has(suggestion.id)) {
|
|
148
|
+
editor.commands.acceptAiInlineDiff();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const surgical = readSurgicalMeta(suggestion);
|
|
152
|
+
if (!surgical)
|
|
153
|
+
return;
|
|
154
|
+
const modifier = planSurgicalModifier(editor, surgical);
|
|
155
|
+
if (!modifier)
|
|
109
156
|
return;
|
|
110
|
-
editor.commands.
|
|
157
|
+
editor.commands.command(({ tr, dispatch }) => {
|
|
158
|
+
modifier(tr);
|
|
159
|
+
if (!tr.docChanged)
|
|
160
|
+
return false;
|
|
161
|
+
if (dispatch)
|
|
162
|
+
dispatch(tr);
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
111
165
|
};
|
|
112
166
|
return registerPendingSuggestionApplier(undefined, fieldName, applier);
|
|
113
167
|
}, [editor, fieldName]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAiInlineDiff.js","sourceRoot":"","sources":["../../src/react/useAiInlineDiff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAGzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EACL,gCAAgC,EAChC,6BAA6B,GAG9B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GAGpB,MAAM,mBAAmB,CAAA;AAkB1B;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAqB;IAC3D,MAAM,MAAM,GAAG,cAAc,CAAC;QAC5B,MAAM;QACN,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,qBAAqB,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI;KACxF,CAAC,CAAA;IACF,OAAO,MAAM,IAAI,KAAK,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAqB,EACrB,SAAiB,EACjB,OAA+B;IAE/B,MAAM,EAAE,IAAI,EAAE,GAAG,6BAA6B,CAAC,SAAS,CAAC,CAAA;IAEzD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;IAChD,SAAS,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,eAAe,CAAA,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;IAE1F,kEAAkE;IAClE,mEAAmE;IACnE,eAAe;IACf,MAAM,UAAU,GAAG,MAAM,CAAc,IAAI,GAAG,EAAE,CAAC,CAAA;IAEjD,8DAA8D;IAC9D,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,oDAAoD;IACpD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;QACrD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,SAAQ;YAC1C,iEAAiE;YACjE,iEAAiE;YACjE,
|
|
1
|
+
{"version":3,"file":"useAiInlineDiff.js","sourceRoot":"","sources":["../../src/react/useAiInlineDiff.ts"],"names":[],"mappings":"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"}
|
package/dist/surgicalOps.d.ts
CHANGED
|
@@ -23,14 +23,17 @@ import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
|
|
23
23
|
export type TransactionModifier = (tr: Transaction) => void;
|
|
24
24
|
/**
|
|
25
25
|
* Replace the top-level block at `blockIndex` with the parsed content.
|
|
26
|
-
*
|
|
26
|
+
* `content` is HTML for `RichTextField` (Tiptap) editors and markdown
|
|
27
|
+
* source for `MarkdownField` (markdown-extension) editors —
|
|
28
|
+
* auto-detected by `parseContentToSlice`. Multiple top-level nodes are
|
|
27
29
|
* allowed and will all land where the original block was.
|
|
28
30
|
*/
|
|
29
31
|
export declare function planReplaceBlock(editor: Editor, blockIndex: number, content: string): TransactionModifier | null;
|
|
30
32
|
/**
|
|
31
|
-
* Insert one or more top-level nodes
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* Insert one or more top-level nodes before the block at `blockIndex`.
|
|
34
|
+
* `content` is HTML on `RichTextField` editors and markdown source on
|
|
35
|
+
* `MarkdownField` editors — auto-detected by `parseContentToSlice`.
|
|
36
|
+
* `blockIndex === doc.childCount` appends at the end.
|
|
34
37
|
*/
|
|
35
38
|
export declare function planInsertBlockBefore(editor: Editor, blockIndex: number, content: string): TransactionModifier | null;
|
|
36
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surgicalOps.d.ts","sourceRoot":"","sources":["../src/surgicalOps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAkB,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"surgicalOps.d.ts","sourceRoot":"","sources":["../src/surgicalOps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,KAAK,EAAkB,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;AA6C3D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAM,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAK,MAAM,GACjB,mBAAmB,GAAG,IAAI,CAQ5B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAM,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAK,MAAM,GACjB,mBAAmB,GAAG,IAAI,CAQ5B;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAM,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,mBAAmB,GAAG,IAAI,CAO5B;AAED,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,4BAA4B;IAC5B,EAAE,EAAI,MAAM,CAAA;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAM,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAQ,MAAM,EAClB,KAAK,EAAO,cAAc,EAC1B,KAAK,EAAO,OAAO,EACnB,KAAK,CAAC,EAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,mBAAmB,GAAG,IAAI,CAwB5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,SAAK,GAAG,MAAM,CAWnF"}
|
package/dist/surgicalOps.js
CHANGED
|
@@ -28,23 +28,49 @@ function blockStartPos(doc, blockIndex) {
|
|
|
28
28
|
return pos;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Parse
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Parse content into a doc-replaceable Slice against the editor's
|
|
32
|
+
* schema. Auto-detects markdown editors by sniffing for the
|
|
33
|
+
* `tiptap-markdown` extension's `storage.markdown.parser`: if present,
|
|
34
|
+
* the editor is a `MarkdownEditor` and AI-supplied `content` is
|
|
35
|
+
* markdown source — run it through the markdown parser to produce
|
|
36
|
+
* HTML first. Otherwise content is HTML (the `RichTextField` /
|
|
37
|
+
* `TiptapEditor` path).
|
|
34
38
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
39
|
+
* Mirrors the same auto-detect strategy `MarkdownEditor.tsx` uses for
|
|
40
|
+
* its `parseSuggestion` whole-field callback (see `useAiInlineDiff`),
|
|
41
|
+
* so surgical ops on markdown fields stay consistent with the
|
|
42
|
+
* existing whole-field replacement path.
|
|
43
|
+
*
|
|
44
|
+
* Returns `null` when DOM isn't available (SSR — shouldn't happen
|
|
45
|
+
* here, but keeps the planner safe) or when the markdown parser
|
|
46
|
+
* throws / returns a non-string (malformed content).
|
|
37
47
|
*/
|
|
38
|
-
function
|
|
48
|
+
function parseContentToSlice(editor, content) {
|
|
39
49
|
if (typeof document === 'undefined')
|
|
40
50
|
return null;
|
|
51
|
+
let html = content;
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
const mdParser = editor.storage?.markdown?.parser;
|
|
54
|
+
if (mdParser && typeof mdParser.parse === 'function') {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = mdParser.parse(content);
|
|
57
|
+
if (typeof parsed !== 'string')
|
|
58
|
+
return null;
|
|
59
|
+
html = parsed;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
41
65
|
const container = document.createElement('div');
|
|
42
66
|
container.innerHTML = html;
|
|
43
67
|
return PMDOMParser.fromSchema(editor.schema).parseSlice(container);
|
|
44
68
|
}
|
|
45
69
|
/**
|
|
46
70
|
* Replace the top-level block at `blockIndex` with the parsed content.
|
|
47
|
-
*
|
|
71
|
+
* `content` is HTML for `RichTextField` (Tiptap) editors and markdown
|
|
72
|
+
* source for `MarkdownField` (markdown-extension) editors —
|
|
73
|
+
* auto-detected by `parseContentToSlice`. Multiple top-level nodes are
|
|
48
74
|
* allowed and will all land where the original block was.
|
|
49
75
|
*/
|
|
50
76
|
export function planReplaceBlock(editor, blockIndex, content) {
|
|
@@ -52,22 +78,23 @@ export function planReplaceBlock(editor, blockIndex, content) {
|
|
|
52
78
|
const start = blockStartPos(doc, blockIndex);
|
|
53
79
|
if (start === null)
|
|
54
80
|
return null;
|
|
55
|
-
const slice =
|
|
81
|
+
const slice = parseContentToSlice(editor, content);
|
|
56
82
|
if (!slice)
|
|
57
83
|
return null;
|
|
58
84
|
const end = start + doc.child(blockIndex).nodeSize;
|
|
59
85
|
return (tr) => { tr.replace(start, end, slice); };
|
|
60
86
|
}
|
|
61
87
|
/**
|
|
62
|
-
* Insert one or more top-level nodes
|
|
63
|
-
*
|
|
64
|
-
*
|
|
88
|
+
* Insert one or more top-level nodes before the block at `blockIndex`.
|
|
89
|
+
* `content` is HTML on `RichTextField` editors and markdown source on
|
|
90
|
+
* `MarkdownField` editors — auto-detected by `parseContentToSlice`.
|
|
91
|
+
* `blockIndex === doc.childCount` appends at the end.
|
|
65
92
|
*/
|
|
66
93
|
export function planInsertBlockBefore(editor, blockIndex, content) {
|
|
67
94
|
const doc = editor.state.doc;
|
|
68
95
|
if (!Number.isInteger(blockIndex) || blockIndex < 0 || blockIndex > doc.childCount)
|
|
69
96
|
return null;
|
|
70
|
-
const slice =
|
|
97
|
+
const slice = parseContentToSlice(editor, content);
|
|
71
98
|
if (!slice)
|
|
72
99
|
return null;
|
|
73
100
|
let pos = 0;
|
package/dist/surgicalOps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surgicalOps.js","sourceRoot":"","sources":["../src/surgicalOps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAI3D,yEAAyE;AACzE,SAAS,aAAa,CAAC,GAAoB,EAAE,UAAkB;IAC7D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,IAAI,GAAG,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAChG,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;IACjE,OAAO,GAAG,CAAA;AACZ,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"surgicalOps.js","sourceRoot":"","sources":["../src/surgicalOps.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -105,12 +105,39 @@ export function useAiInlineDiff(
|
|
|
105
105
|
const diffable = list.filter(s => !hasEditorRange(s))
|
|
106
106
|
for (const s of diffable) {
|
|
107
107
|
if (startedRef.current.has(s.id)) continue
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
const diffActive = aiInlineDiffPluginKey.getState(editor.state) !== null
|
|
109
|
+
const surgical = readSurgicalMeta(s)
|
|
110
|
+
|
|
111
|
+
// Cross-tool-call surgical stacking. When a diff is already active
|
|
112
|
+
// and a fresh surgical suggestion arrives (typically the model
|
|
113
|
+
// emitted a second `update_form_state` tool call instead of
|
|
114
|
+
// batching ops in one), fold the new modifier into the active
|
|
115
|
+
// diff. We dispatch a plain transaction with no extension meta;
|
|
116
|
+
// the plugin's existing "no explicit meta + tr.docChanged" branch
|
|
117
|
+
// adds the steps to the running changeset, so decorations update
|
|
118
|
+
// to cover both ops' ranges and the banner shows the combined
|
|
119
|
+
// count. Accept commits the union, Reject reverts to the same
|
|
120
|
+
// baseline captured when the FIRST suggestion started the diff —
|
|
121
|
+
// semantically "reject all pending suggested changes", which
|
|
122
|
+
// matches the banner copy.
|
|
123
|
+
//
|
|
124
|
+
// Whole-field suggestions still bail when a diff is active —
|
|
125
|
+
// dropping a fresh slice on top of an active review is too
|
|
126
|
+
// disruptive (it'd swap the entire doc mid-review).
|
|
127
|
+
if (diffActive) {
|
|
128
|
+
if (!surgical) continue
|
|
129
|
+
const modifier = planSurgicalModifier(editor, surgical)
|
|
130
|
+
if (!modifier) continue
|
|
131
|
+
editor.commands.command(({ tr, dispatch }) => {
|
|
132
|
+
modifier(tr)
|
|
133
|
+
if (!tr.docChanged) return false
|
|
134
|
+
if (dispatch) dispatch(tr)
|
|
135
|
+
return true
|
|
136
|
+
})
|
|
137
|
+
startedRef.current.add(s.id)
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
112
140
|
|
|
113
|
-
const surgical = readSurgicalMeta(s)
|
|
114
141
|
if (surgical) {
|
|
115
142
|
const modifier = planSurgicalModifier(editor, surgical)
|
|
116
143
|
if (!modifier) continue
|
|
@@ -134,14 +161,36 @@ export function useAiInlineDiff(
|
|
|
134
161
|
}
|
|
135
162
|
}, [editor, list])
|
|
136
163
|
|
|
137
|
-
// Cross-tree applier —
|
|
138
|
-
//
|
|
139
|
-
// accept
|
|
164
|
+
// Cross-tree applier — two paths:
|
|
165
|
+
//
|
|
166
|
+
// 1. Review-mode accept. Banner / chat-sidebar pill calls
|
|
167
|
+
// `pendingSuggestions.approve(id)` for a suggestion we've already
|
|
168
|
+
// started a diff for. Clear the diff state — current doc IS the
|
|
169
|
+
// accepted state.
|
|
170
|
+
// 2. Auto-mode direct apply. AI tool binding bypassed the queue and
|
|
171
|
+
// called the registry's applier with a synthetic suggestion
|
|
172
|
+
// carrying `meta.surgical` (the suggestion was never pushed to
|
|
173
|
+
// the context, so it's not in `startedRef`). Plan the modifier
|
|
174
|
+
// and dispatch it as a plain transaction — no diff overlay, no
|
|
175
|
+
// Accept / Reject step. Mirrors `set_value` auto-mode, which
|
|
176
|
+
// writes directly via the same registry.
|
|
140
177
|
useEffect(() => {
|
|
141
178
|
if (!editor) return
|
|
142
179
|
const applier: PendingSuggestionApplier = (suggestion) => {
|
|
143
|
-
if (
|
|
144
|
-
|
|
180
|
+
if (startedRef.current.has(suggestion.id)) {
|
|
181
|
+
editor.commands.acceptAiInlineDiff()
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
const surgical = readSurgicalMeta(suggestion)
|
|
185
|
+
if (!surgical) return
|
|
186
|
+
const modifier = planSurgicalModifier(editor, surgical)
|
|
187
|
+
if (!modifier) return
|
|
188
|
+
editor.commands.command(({ tr, dispatch }) => {
|
|
189
|
+
modifier(tr)
|
|
190
|
+
if (!tr.docChanged) return false
|
|
191
|
+
if (dispatch) dispatch(tr)
|
|
192
|
+
return true
|
|
193
|
+
})
|
|
145
194
|
}
|
|
146
195
|
return registerPendingSuggestionApplier(undefined, fieldName, applier)
|
|
147
196
|
}, [editor, fieldName])
|
package/src/surgicalOps.ts
CHANGED
|
@@ -34,15 +34,35 @@ function blockStartPos(doc: ProseMirrorNode, blockIndex: number): number | null
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Parse
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* Parse content into a doc-replaceable Slice against the editor's
|
|
38
|
+
* schema. Auto-detects markdown editors by sniffing for the
|
|
39
|
+
* `tiptap-markdown` extension's `storage.markdown.parser`: if present,
|
|
40
|
+
* the editor is a `MarkdownEditor` and AI-supplied `content` is
|
|
41
|
+
* markdown source — run it through the markdown parser to produce
|
|
42
|
+
* HTML first. Otherwise content is HTML (the `RichTextField` /
|
|
43
|
+
* `TiptapEditor` path).
|
|
40
44
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
45
|
+
* Mirrors the same auto-detect strategy `MarkdownEditor.tsx` uses for
|
|
46
|
+
* its `parseSuggestion` whole-field callback (see `useAiInlineDiff`),
|
|
47
|
+
* so surgical ops on markdown fields stay consistent with the
|
|
48
|
+
* existing whole-field replacement path.
|
|
49
|
+
*
|
|
50
|
+
* Returns `null` when DOM isn't available (SSR — shouldn't happen
|
|
51
|
+
* here, but keeps the planner safe) or when the markdown parser
|
|
52
|
+
* throws / returns a non-string (malformed content).
|
|
43
53
|
*/
|
|
44
|
-
function
|
|
54
|
+
function parseContentToSlice(editor: Editor, content: string): ReturnType<typeof PMDOMParser.prototype.parseSlice> | null {
|
|
45
55
|
if (typeof document === 'undefined') return null
|
|
56
|
+
let html = content
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
const mdParser = (editor.storage as any)?.markdown?.parser
|
|
59
|
+
if (mdParser && typeof mdParser.parse === 'function') {
|
|
60
|
+
try {
|
|
61
|
+
const parsed: unknown = mdParser.parse(content)
|
|
62
|
+
if (typeof parsed !== 'string') return null
|
|
63
|
+
html = parsed
|
|
64
|
+
} catch { return null }
|
|
65
|
+
}
|
|
46
66
|
const container = document.createElement('div')
|
|
47
67
|
container.innerHTML = html
|
|
48
68
|
return PMDOMParser.fromSchema(editor.schema).parseSlice(container)
|
|
@@ -50,7 +70,9 @@ function parseHtmlToSlice(editor: Editor, html: string): ReturnType<typeof PMDOM
|
|
|
50
70
|
|
|
51
71
|
/**
|
|
52
72
|
* Replace the top-level block at `blockIndex` with the parsed content.
|
|
53
|
-
*
|
|
73
|
+
* `content` is HTML for `RichTextField` (Tiptap) editors and markdown
|
|
74
|
+
* source for `MarkdownField` (markdown-extension) editors —
|
|
75
|
+
* auto-detected by `parseContentToSlice`. Multiple top-level nodes are
|
|
54
76
|
* allowed and will all land where the original block was.
|
|
55
77
|
*/
|
|
56
78
|
export function planReplaceBlock(
|
|
@@ -61,16 +83,17 @@ export function planReplaceBlock(
|
|
|
61
83
|
const doc = editor.state.doc
|
|
62
84
|
const start = blockStartPos(doc, blockIndex)
|
|
63
85
|
if (start === null) return null
|
|
64
|
-
const slice =
|
|
86
|
+
const slice = parseContentToSlice(editor, content)
|
|
65
87
|
if (!slice) return null
|
|
66
88
|
const end = start + doc.child(blockIndex).nodeSize
|
|
67
89
|
return (tr) => { tr.replace(start, end, slice) }
|
|
68
90
|
}
|
|
69
91
|
|
|
70
92
|
/**
|
|
71
|
-
* Insert one or more top-level nodes
|
|
72
|
-
*
|
|
73
|
-
*
|
|
93
|
+
* Insert one or more top-level nodes before the block at `blockIndex`.
|
|
94
|
+
* `content` is HTML on `RichTextField` editors and markdown source on
|
|
95
|
+
* `MarkdownField` editors — auto-detected by `parseContentToSlice`.
|
|
96
|
+
* `blockIndex === doc.childCount` appends at the end.
|
|
74
97
|
*/
|
|
75
98
|
export function planInsertBlockBefore(
|
|
76
99
|
editor: Editor,
|
|
@@ -79,7 +102,7 @@ export function planInsertBlockBefore(
|
|
|
79
102
|
): TransactionModifier | null {
|
|
80
103
|
const doc = editor.state.doc
|
|
81
104
|
if (!Number.isInteger(blockIndex) || blockIndex < 0 || blockIndex > doc.childCount) return null
|
|
82
|
-
const slice =
|
|
105
|
+
const slice = parseContentToSlice(editor, content)
|
|
83
106
|
if (!slice) return null
|
|
84
107
|
let pos = 0
|
|
85
108
|
for (let i = 0; i < blockIndex; i++) pos += doc.child(i).nodeSize
|