From d251a522f712b2383f9e970f3c930f9193acd9b6 Mon Sep 17 00:00:00 2001 From: Karl Kemister-Sheppard Date: Fri, 24 Jul 2026 14:17:06 +1000 Subject: [PATCH 1/5] TINYDOC-3562 - Document beforeSetContent listener for AI-only revision history --- modules/ROOT/pages/revisionhistory.adoc | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index af86dd2938..d1a97bd5d5 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -155,6 +155,45 @@ const saveRevision = () => { }; ---- +==== Capturing content before and after an AI edit + +The previous example marks the next saved revision as AI-assisted, but that revision can combine human edits made since the last save with the AI-generated change. To keep the two apart, save a revision on the `+BeforeSetContent+` event as well. Both events carry an `+ai+` property that is `+true+` when the xref:tinymceai.adoc[{productname} AI] plugin inserts content: + +* `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point captures the latest non-AI content, which can be saved as a revision that reflects the human-authored state. +* `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point captures the AI-generated result, which can be saved as an AI-assisted revision. + +Listening for both events produces a finer-grained history that distinguishes purely AI-generated content from the human content that preceded it: + +[source,js] +---- +// Save the pre-AI (human) content before the TinyMCE AI plugin inserts its edit. +editor.on('BeforeSetContent', (e) => { + if (e.ai) { + const revision = { + revisionId: createRevisionId(), // Replace with your ID generation + createdAt: new Date().toISOString(), + content: editor.getContent() + }; + saveToStorage(revision); // Replace with your storage call + } +}); + +// Save the post-AI content and mark it as AI-assisted. +editor.on('SetContent', (e) => { + if (e.ai) { + const revision = { + revisionId: createRevisionId(), // Replace with your ID generation + createdAt: new Date().toISOString(), + content: editor.getContent(), + metadata: { source: 'ai' } + }; + saveToStorage(revision); // Replace with your storage call + } +}); +---- + +Listening for `+BeforeSetContent+` is optional. Use `+SetContent+` alone to record only the AI-assisted result, or pair it with `+BeforeSetContent+` when the revision history should also preserve the content as it was before each AI edit. Choose the approach that suits the application's use case. + === Restoring an AI-assisted revision Restoring a revision sets its content back into the editor and fires the `+VersionRestored+` event. The restored content is not flagged as AI-assisted, so look up the restored revision and carry its marker forward to the next save: From 4e828ec6675dbb64da17a85358f63eae0812371a Mon Sep 17 00:00:00 2001 From: Karl Kemister-Sheppard Date: Thu, 30 Jul 2026 10:20:49 +1000 Subject: [PATCH 2/5] TINYDOC-3562 - Present BeforeSetContent and SetContent as equal options for AI attribution Move the explanation of both content events into the Attributing AI-assisted revisions introduction and remove the framing that treated BeforeSetContent as an optional addition to SetContent. --- modules/ROOT/pages/revisionhistory.adoc | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index d1a97bd5d5..a581789261 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -116,9 +116,14 @@ The metadata is an `+Object+` that holds additional information about a revision A revision is identified as AI-assisted through the `+source+` field of its xref:#metadata[`+metadata+`] object. When `+metadata.source+` is `+'ai'+`, an AI badge appears on that revision card by default. Set xref:revisionhistory_ai_attribution[`+revisionhistory_ai_attribution+`] to `+false+` to hide the badge. -The {pluginname} plugin does not set this marker automatically. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on the `+SetContent+` event for any content it inserts. Detecting that signal and recording it as `+metadata.source+` when saving a revision is the responsibility of the application. There are three steps to keep attribution accurate: +The {pluginname} plugin does not set this marker automatically. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on both the `+BeforeSetContent+` and `+SetContent+` events for any content it inserts, so either event can signal AI involvement: -. **Detect**: Listen for the `+SetContent+` event and record when its `+ai+` property is `+true+`. +* `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point returns the content as it stood before the AI edit. +* `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point returns the AI-generated result. + +Either event can drive attribution, and both can be used together. Listening for `+SetContent+` alone records the AI-assisted result; listening for both also preserves the content that preceded each AI edit as its own revision. Detecting the signal and recording it as `+metadata.source+` when saving a revision is the responsibility of the application. There are three steps to keep attribution accurate: + +. **Detect**: Listen for `+BeforeSetContent+`, `+SetContent+`, or both, and record when the `+ai+` property is `+true+`. . **Save**: When saving a revision, set `+metadata.source+` to `+'ai'+` if AI contributed since the last save. . **Restore**: When a revision is restored, carry its marker forward so later revisions remain correctly attributed. @@ -155,14 +160,7 @@ const saveRevision = () => { }; ---- -==== Capturing content before and after an AI edit - -The previous example marks the next saved revision as AI-assisted, but that revision can combine human edits made since the last save with the AI-generated change. To keep the two apart, save a revision on the `+BeforeSetContent+` event as well. Both events carry an `+ai+` property that is `+true+` when the xref:tinymceai.adoc[{productname} AI] plugin inserts content: - -* `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point captures the latest non-AI content, which can be saved as a revision that reflects the human-authored state. -* `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point captures the AI-generated result, which can be saved as an AI-assisted revision. - -Listening for both events produces a finer-grained history that distinguishes purely AI-generated content from the human content that preceded it: +A revision saved this way can combine human edits made since the last save with the AI-generated change. To keep the two apart, save a revision from `+BeforeSetContent+` as well. This records the content that preceded each AI edit as its own revision, producing a finer-grained history: [source,js] ---- @@ -192,8 +190,6 @@ editor.on('SetContent', (e) => { }); ---- -Listening for `+BeforeSetContent+` is optional. Use `+SetContent+` alone to record only the AI-assisted result, or pair it with `+BeforeSetContent+` when the revision history should also preserve the content as it was before each AI edit. Choose the approach that suits the application's use case. - === Restoring an AI-assisted revision Restoring a revision sets its content back into the editor and fires the `+VersionRestored+` event. The restored content is not flagged as AI-assisted, so look up the restored revision and carry its marker forward to the next save: From 3ef8a4c60d643f56f325fd4155f47f0ee82786d0 Mon Sep 17 00:00:00 2001 From: Karl Kemister-Sheppard Date: Thu, 30 Jul 2026 11:30:59 +1000 Subject: [PATCH 3/5] TINYDOC-3562 - Share a single saveRevision helper and clarify revision granularity --- modules/ROOT/pages/revisionhistory.adoc | 65 ++++++++++++------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index a581789261..22933cf8e7 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -121,85 +121,82 @@ The {pluginname} plugin does not set this marker automatically. The xref:tinymce * `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point returns the content as it stood before the AI edit. * `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point returns the AI-generated result. -Either event can drive attribution, and both can be used together. Listening for `+SetContent+` alone records the AI-assisted result; listening for both also preserves the content that preceded each AI edit as its own revision. Detecting the signal and recording it as `+metadata.source+` when saving a revision is the responsibility of the application. There are three steps to keep attribution accurate: +Either event can drive attribution, and both can be used together. Which to listen for depends on the granularity the application requires. A revision covers everything that changed since the previous revision, so listening for a single event produces revisions that can hold both AI-generated and human content: -. **Detect**: Listen for `+BeforeSetContent+`, `+SetContent+`, or both, and record when the `+ai+` property is `+true+`. -. **Save**: When saving a revision, set `+metadata.source+` to `+'ai'+` if AI contributed since the last save. -. **Restore**: When a revision is restored, carry its marker forward so later revisions remain correctly attributed. +* Listening for `+BeforeSetContent+` alone saves the human edits made since the last revision, together with the AI edit that preceded them. +* Listening for `+SetContent+` alone saves the AI edit, together with the human edits that preceded it. +* Listening for both separates the two, saving the content as it stood before each AI edit and the AI-generated result as distinct revisions. + +Where the aim is to save a revision whenever content changes, either event is suitable. Where AI-generated and human content must be distinguished, listen for both. Detecting the signal and recording it as `+metadata.source+` when saving a revision is the responsibility of the application. There are three steps to keep attribution accurate: + +. **Detect**: Listen for `+BeforeSetContent+`, `+SetContent+`, or both, and check whether the `+ai+` property is `+true+`. Both events also fire for content changes that the AI plugin did not make, so this check keeps unrelated changes out of the attributed revisions. +. **Save**: When the check passes, save a revision, setting `+metadata.source+` to `+'ai'+` for the AI-generated content. +. **Restore**: When a revision is restored, save it with its marker preserved so the restored content remains correctly attributed. [NOTE] The AI badge appears only on `+saved+` revisions. The `+draft+` and `+initial+` revisions are generated from the editor's current content and do not carry `+metadata+`, so they never display the badge, even when that content was AI-assisted. See xref:#understanding-revision-types[Understanding revision types]. === Saving an AI-assisted revision -The application is responsible for saving revisions and returns them through the xref:revisionhistory_fetch[`+revisionhistory_fetch+`] option. The following example tracks AI involvement from the `+SetContent+` event and attaches the `+metadata+` marker when saving a snapshot: +The application is responsible for saving revisions and returns them through the xref:revisionhistory_fetch[`+revisionhistory_fetch+`] option. The following examples share a single helper that snapshots the current content and marks the revision as AI-assisted when its `+aiAssisted+` parameter is `+true+`, so the same function can save both AI-assisted and human revisions: [source,js] ---- -let aiAssisted = false; - -// The TinyMCE AI plugin sets `ai: true` on the SetContent event for content it inserts. -editor.on('SetContent', (e) => { - if (e.ai) { - aiAssisted = true; - } -}); - // Call this when saving a snapshot of the current content as a revision. -const saveRevision = () => { +const saveRevision = (aiAssisted) => { const revision = { revisionId: createRevisionId(), // Replace with your ID generation createdAt: new Date().toISOString(), content: editor.getContent(), - // Mark the revision as AI-assisted if AI contributed since the last save. + // Mark the revision as AI-assisted when the content came from the AI plugin. ...(aiAssisted ? { metadata: { source: 'ai' } } : {}) }; saveToStorage(revision); // Replace with your storage call - aiAssisted = false; // Reset state of AI assistance for new content }; ---- -A revision saved this way can combine human edits made since the last save with the AI-generated change. To keep the two apart, save a revision from `+BeforeSetContent+` as well. This records the content that preceded each AI edit as its own revision, producing a finer-grained history: +Listening for `+SetContent+` alone saves the AI-generated result. Because the snapshot covers everything changed since the previous revision, it can also include human edits made before the AI edit: + +[source,js] +---- +// The TinyMCE AI plugin sets `ai: true` on the SetContent event for content it inserts. +editor.on('SetContent', (e) => { + if (e.ai) { + saveRevision(true); + } +}); +---- + +To keep AI-generated and human content apart, listen for both events. `+BeforeSetContent+` saves the content that preceded each AI edit as its own revision, and `+SetContent+` saves the AI-generated result, producing a finer-grained history: [source,js] ---- // Save the pre-AI (human) content before the TinyMCE AI plugin inserts its edit. editor.on('BeforeSetContent', (e) => { if (e.ai) { - const revision = { - revisionId: createRevisionId(), // Replace with your ID generation - createdAt: new Date().toISOString(), - content: editor.getContent() - }; - saveToStorage(revision); // Replace with your storage call + saveRevision(false); } }); // Save the post-AI content and mark it as AI-assisted. editor.on('SetContent', (e) => { if (e.ai) { - const revision = { - revisionId: createRevisionId(), // Replace with your ID generation - createdAt: new Date().toISOString(), - content: editor.getContent(), - metadata: { source: 'ai' } - }; - saveToStorage(revision); // Replace with your storage call + saveRevision(true); } }); ---- === Restoring an AI-assisted revision -Restoring a revision sets its content back into the editor and fires the `+VersionRestored+` event. The restored content is not flagged as AI-assisted, so look up the restored revision and carry its marker forward to the next save: +Restoring a revision sets its content back into the editor and fires the `+VersionRestored+` event. The restored content is not flagged as AI-assisted, so look up the revision that was restored and save it with its marker preserved, using the `+saveRevision()+` helper shown in xref:#saving-an-ai-assisted-revision[Saving an AI-assisted revision]: [source,js] ---- editor.on('VersionRestored', (e) => { const restored = getFromStorage(e.revisionId); // Replace with your storage lookup - // Preserve attribution so the next saved revision reflects the restored content. - aiAssisted = restored?.metadata?.source === 'ai'; + // Preserve attribution so the restored content keeps its marker. + saveRevision(restored?.metadata?.source === 'ai'); }); ---- From 0519b80f00bf203564e7051a7fc73490bc32df51 Mon Sep 17 00:00:00 2001 From: Karl Kemister-Sheppard Date: Thu, 30 Jul 2026 13:12:02 +1000 Subject: [PATCH 4/5] TINYDOC-3562 - Restore early context for content events and consolidate the save example --- modules/ROOT/pages/revisionhistory.adoc | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index 22933cf8e7..eeba943e3e 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -116,7 +116,7 @@ The metadata is an `+Object+` that holds additional information about a revision A revision is identified as AI-assisted through the `+source+` field of its xref:#metadata[`+metadata+`] object. When `+metadata.source+` is `+'ai'+`, an AI badge appears on that revision card by default. Set xref:revisionhistory_ai_attribution[`+revisionhistory_ai_attribution+`] to `+false+` to hide the badge. -The {pluginname} plugin does not set this marker automatically. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on both the `+BeforeSetContent+` and `+SetContent+` events for any content it inserts, so either event can signal AI involvement: +The {pluginname} plugin does not set this marker automatically. Listening for the relevant content events and saving revisions with the marker attached is the responsibility of the application. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on both the `+BeforeSetContent+` and `+SetContent+` events for any content it inserts, so either event can signal AI involvement: * `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point returns the content as it stood before the AI edit. * `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point returns the AI-generated result. @@ -127,7 +127,7 @@ Either event can drive attribution, and both can be used together. Which to list * Listening for `+SetContent+` alone saves the AI edit, together with the human edits that preceded it. * Listening for both separates the two, saving the content as it stood before each AI edit and the AI-generated result as distinct revisions. -Where the aim is to save a revision whenever content changes, either event is suitable. Where AI-generated and human content must be distinguished, listen for both. Detecting the signal and recording it as `+metadata.source+` when saving a revision is the responsibility of the application. There are three steps to keep attribution accurate: +Where the aim is to save a revision whenever content changes, either event is suitable. Where AI-generated and human content must be distinguished, listen for both. There are three steps to keep attribution accurate: . **Detect**: Listen for `+BeforeSetContent+`, `+SetContent+`, or both, and check whether the `+ai+` property is `+true+`. Both events also fire for content changes that the AI plugin did not make, so this check keeps unrelated changes out of the attributed revisions. . **Save**: When the check passes, save a revision, setting `+metadata.source+` to `+'ai'+` for the AI-generated content. @@ -138,7 +138,7 @@ The AI badge appears only on `+saved+` revisions. The `+draft+` and `+initial+` === Saving an AI-assisted revision -The application is responsible for saving revisions and returns them through the xref:revisionhistory_fetch[`+revisionhistory_fetch+`] option. The following examples share a single helper that snapshots the current content and marks the revision as AI-assisted when its `+aiAssisted+` parameter is `+true+`, so the same function can save both AI-assisted and human revisions: +The application is responsible for saving revisions and returns them through the xref:revisionhistory_fetch[`+revisionhistory_fetch+`] option. The following example listens for both events, sharing a single helper that snapshots the current content and marks the revision as AI-assisted when its `+aiAssisted+` parameter is `+true+`. `+BeforeSetContent+` saves the content that preceded each AI edit as its own revision, and `+SetContent+` saves the AI-generated result: [source,js] ---- @@ -154,24 +154,7 @@ const saveRevision = (aiAssisted) => { saveToStorage(revision); // Replace with your storage call }; ----- - -Listening for `+SetContent+` alone saves the AI-generated result. Because the snapshot covers everything changed since the previous revision, it can also include human edits made before the AI edit: -[source,js] ----- -// The TinyMCE AI plugin sets `ai: true` on the SetContent event for content it inserts. -editor.on('SetContent', (e) => { - if (e.ai) { - saveRevision(true); - } -}); ----- - -To keep AI-generated and human content apart, listen for both events. `+BeforeSetContent+` saves the content that preceded each AI edit as its own revision, and `+SetContent+` saves the AI-generated result, producing a finer-grained history: - -[source,js] ----- // Save the pre-AI (human) content before the TinyMCE AI plugin inserts its edit. editor.on('BeforeSetContent', (e) => { if (e.ai) { @@ -187,6 +170,8 @@ editor.on('SetContent', (e) => { }); ---- +To drive attribution from a single event, keep only the listener required. The revisions this produces combine AI-generated and human content, as described in xref:#attributing-ai-assisted-revisions[Attributing AI-assisted revisions]. + === Restoring an AI-assisted revision Restoring a revision sets its content back into the editor and fires the `+VersionRestored+` event. The restored content is not flagged as AI-assisted, so look up the revision that was restored and save it with its marker preserved, using the `+saveRevision()+` helper shown in xref:#saving-an-ai-assisted-revision[Saving an AI-assisted revision]: From 919fd129138532acb58b810b6934aa3a73b29d6c Mon Sep 17 00:00:00 2001 From: Karl Kemister-Sheppard Date: Thu, 30 Jul 2026 13:18:43 +1000 Subject: [PATCH 5/5] TINYDOC-3562 - Use active voice for the application's attribution responsibility --- modules/ROOT/pages/revisionhistory.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/revisionhistory.adoc b/modules/ROOT/pages/revisionhistory.adoc index eeba943e3e..16d0a897d8 100644 --- a/modules/ROOT/pages/revisionhistory.adoc +++ b/modules/ROOT/pages/revisionhistory.adoc @@ -116,7 +116,7 @@ The metadata is an `+Object+` that holds additional information about a revision A revision is identified as AI-assisted through the `+source+` field of its xref:#metadata[`+metadata+`] object. When `+metadata.source+` is `+'ai'+`, an AI badge appears on that revision card by default. Set xref:revisionhistory_ai_attribution[`+revisionhistory_ai_attribution+`] to `+false+` to hide the badge. -The {pluginname} plugin does not set this marker automatically. Listening for the relevant content events and saving revisions with the marker attached is the responsibility of the application. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on both the `+BeforeSetContent+` and `+SetContent+` events for any content it inserts, so either event can signal AI involvement: +The {pluginname} plugin does not set this marker automatically. Instead, the application must listen for content events and save each revision with the marker attached. The xref:tinymceai.adoc[{productname} AI] plugin sets `+ai+` to `+true+` on both the `+BeforeSetContent+` and `+SetContent+` events for any content it inserts, so either event can signal AI involvement: * `+BeforeSetContent+` fires immediately before the AI content is inserted. Calling `+editor.getContent()+` at this point returns the content as it stood before the AI edit. * `+SetContent+` fires immediately after the AI content is inserted. Calling `+editor.getContent()+` at this point returns the AI-generated result.