@richscripts/richtexteditor 2.14.0 → 2.15.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/README.md +173 -10
- package/angular/build-manifest.json +23 -0
- package/angular/index.d.ts +71 -76
- package/angular/index.mjs +2 -0
- package/angular/package.json +4 -0
- package/headless/index.d.ts +3 -1
- package/headless/index.js +72 -16
- package/index.d.ts +33 -54
- package/integrations/initialization-scope.js +27 -0
- package/integrations/native-mount.js +30 -0
- package/integrations/shared.d.ts +23 -1
- package/integrations/shared.js +58 -22
- package/package.json +25 -3
- package/react/index.js +118 -37
- package/react/index.mjs +29 -0
- package/richtexteditor/all_plugins.js +7113 -1111
- package/richtexteditor/all_plugins.min.js +62 -59
- package/richtexteditor/lang/emoji/LICENSE-CLDR.txt +41 -0
- package/richtexteditor/lang/emoji/rte-emoji-de.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-es.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-fr.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-it.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ja.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ko.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-nl.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-pt-pt.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-pt.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ru.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-zh-Hans.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-zh-Hant.js +3 -0
- package/richtexteditor/lang/rte-lang-af.js +1 -0
- package/richtexteditor/lang/rte-lang-ar.js +1 -0
- package/richtexteditor/lang/rte-lang-bg.js +1 -0
- package/richtexteditor/lang/rte-lang-bn.js +1 -0
- package/richtexteditor/lang/rte-lang-bs.js +1 -0
- package/richtexteditor/lang/rte-lang-ca.js +1 -0
- package/richtexteditor/lang/rte-lang-cs.js +1 -0
- package/richtexteditor/lang/rte-lang-cy.js +1 -0
- package/richtexteditor/lang/rte-lang-da.js +1 -0
- package/richtexteditor/lang/rte-lang-de.js +1017 -2
- package/richtexteditor/lang/rte-lang-el.js +1 -0
- package/richtexteditor/lang/rte-lang-en.js +1 -0
- package/richtexteditor/lang/rte-lang-es.js +1023 -2
- package/richtexteditor/lang/rte-lang-et.js +1 -0
- package/richtexteditor/lang/rte-lang-fa.js +1 -0
- package/richtexteditor/lang/rte-lang-fi.js +1 -0
- package/richtexteditor/lang/rte-lang-fil.js +1 -0
- package/richtexteditor/lang/rte-lang-fj.js +1 -0
- package/richtexteditor/lang/rte-lang-fr.js +1023 -2
- package/richtexteditor/lang/rte-lang-ga.js +1 -0
- package/richtexteditor/lang/rte-lang-gu.js +1 -0
- package/richtexteditor/lang/rte-lang-he.js +1 -0
- package/richtexteditor/lang/rte-lang-hi.js +1 -0
- package/richtexteditor/lang/rte-lang-hr.js +1 -0
- package/richtexteditor/lang/rte-lang-ht.js +1 -0
- package/richtexteditor/lang/rte-lang-hu.js +1 -0
- package/richtexteditor/lang/rte-lang-id.js +1 -0
- package/richtexteditor/lang/rte-lang-is.js +1 -0
- package/richtexteditor/lang/rte-lang-it.js +1023 -2
- package/richtexteditor/lang/rte-lang-ja.js +1023 -2
- package/richtexteditor/lang/rte-lang-kn.js +1 -0
- package/richtexteditor/lang/rte-lang-ko.js +1023 -2
- package/richtexteditor/lang/rte-lang-lt.js +1 -0
- package/richtexteditor/lang/rte-lang-lv.js +1 -0
- package/richtexteditor/lang/rte-lang-mg.js +1 -0
- package/richtexteditor/lang/rte-lang-mi.js +1 -0
- package/richtexteditor/lang/rte-lang-ml.js +1 -0
- package/richtexteditor/lang/rte-lang-mr.js +1 -0
- package/richtexteditor/lang/rte-lang-ms.js +1 -0
- package/richtexteditor/lang/rte-lang-mt.js +1 -0
- package/richtexteditor/lang/rte-lang-mww.js +1 -0
- package/richtexteditor/lang/rte-lang-nb.js +1 -0
- package/richtexteditor/lang/rte-lang-nl.js +1023 -2
- package/richtexteditor/lang/rte-lang-otq.js +1 -0
- package/richtexteditor/lang/rte-lang-pa.js +1 -0
- package/richtexteditor/lang/rte-lang-pl.js +1 -0
- package/richtexteditor/lang/rte-lang-pt-pt.js +1023 -2
- package/richtexteditor/lang/rte-lang-pt.js +1023 -2
- package/richtexteditor/lang/rte-lang-ro.js +1 -0
- package/richtexteditor/lang/rte-lang-ru.js +1023 -2
- package/richtexteditor/lang/rte-lang-sk.js +1 -0
- package/richtexteditor/lang/rte-lang-sl.js +1 -0
- package/richtexteditor/lang/rte-lang-sm.js +1 -0
- package/richtexteditor/lang/rte-lang-sr-Cyrl.js +1 -0
- package/richtexteditor/lang/rte-lang-sr-Latn.js +1 -0
- package/richtexteditor/lang/rte-lang-sv.js +1 -0
- package/richtexteditor/lang/rte-lang-sw.js +1 -0
- package/richtexteditor/lang/rte-lang-ta.js +1 -0
- package/richtexteditor/lang/rte-lang-te.js +1 -0
- package/richtexteditor/lang/rte-lang-th.js +1 -0
- package/richtexteditor/lang/rte-lang-tlh-Latn.js +1 -0
- package/richtexteditor/lang/rte-lang-tlh-Piqd.js +1 -0
- package/richtexteditor/lang/rte-lang-to.js +1 -0
- package/richtexteditor/lang/rte-lang-tr.js +1 -0
- package/richtexteditor/lang/rte-lang-ty.js +1 -0
- package/richtexteditor/lang/rte-lang-uk.js +1 -0
- package/richtexteditor/lang/rte-lang-ur.js +1 -0
- package/richtexteditor/lang/rte-lang-vi.js +1 -0
- package/richtexteditor/lang/rte-lang-yua.js +1 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +1017 -2
- package/richtexteditor/lang/rte-lang-zh-Hant.js +1017 -2
- package/richtexteditor/plugins/a11yenhance.js +2 -1
- package/richtexteditor/plugins/a11yenhance.min.js +1 -1
- package/richtexteditor/plugins/accessibilitychecker.js +252 -60
- package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
- package/richtexteditor/plugins/aitoolkit.js +966 -303
- package/richtexteditor/plugins/aitoolkit.min.js +7 -7
- package/richtexteditor/plugins/all_plugins.js +7113 -1111
- package/richtexteditor/plugins/all_plugins.min.js +62 -59
- package/richtexteditor/plugins/autocorrect.js +66 -31
- package/richtexteditor/plugins/autocorrect.min.js +1 -1
- package/richtexteditor/plugins/autoembed.js +17 -0
- package/richtexteditor/plugins/autoembed.min.js +1 -1
- package/richtexteditor/plugins/blocktypes.js +18 -9
- package/richtexteditor/plugins/blocktypes.min.js +1 -1
- package/richtexteditor/plugins/bookmarkcard.js +12 -5
- package/richtexteditor/plugins/bookmarkcard.min.js +2 -2
- package/richtexteditor/plugins/changecase.js +72 -19
- package/richtexteditor/plugins/changecase.min.js +1 -1
- package/richtexteditor/plugins/charlimit.js +62 -2
- package/richtexteditor/plugins/charlimit.min.js +2 -2
- package/richtexteditor/plugins/comments.js +458 -59
- package/richtexteditor/plugins/comments.min.js +4 -3
- package/richtexteditor/plugins/contentminimap.js +25 -6
- package/richtexteditor/plugins/contentminimap.min.js +1 -1
- package/richtexteditor/plugins/crossreference.js +1 -1
- package/richtexteditor/plugins/crossreference.min.js +1 -1
- package/richtexteditor/plugins/dialoga11y.js +26 -4
- package/richtexteditor/plugins/dialoga11y.min.js +1 -1
- package/richtexteditor/plugins/dictation.js +1 -1
- package/richtexteditor/plugins/documentimport.js +220 -51
- package/richtexteditor/plugins/documentimport.min.js +4 -4
- package/richtexteditor/plugins/documentoutline.js +88 -38
- package/richtexteditor/plugins/documentoutline.min.js +1 -1
- package/richtexteditor/plugins/docxexport.js +204 -32
- package/richtexteditor/plugins/docxexport.min.js +2 -2
- package/richtexteditor/plugins/draghandle.js +267 -3
- package/richtexteditor/plugins/draghandle.min.js +1 -1
- package/richtexteditor/plugins/emailtoolkit.js +28 -14
- package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
- package/richtexteditor/plugins/emojiautocomplete.js +110 -56
- package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
- package/richtexteditor/plugins/footnotes.js +65 -11
- package/richtexteditor/plugins/footnotes.min.js +1 -1
- package/richtexteditor/plugins/formatpainter.js +10 -0
- package/richtexteditor/plugins/formatpainter.min.js +1 -1
- package/richtexteditor/plugins/formattingmarks.js +53 -8
- package/richtexteditor/plugins/formattingmarks.min.js +1 -1
- package/richtexteditor/plugins/gapcursor.js +60 -0
- package/richtexteditor/plugins/gapcursor.min.js +1 -1
- package/richtexteditor/plugins/ghostcomplete.js +1 -1
- package/richtexteditor/plugins/html2pdf.js +1 -1
- package/richtexteditor/plugins/html2pdf.min.js +1 -1
- package/richtexteditor/plugins/imageeditor.js +12 -6
- package/richtexteditor/plugins/imageeditor.min.js +1 -1
- package/richtexteditor/plugins/importstyles.js +1 -1
- package/richtexteditor/plugins/inlinecode.js +25 -0
- package/richtexteditor/plugins/inlinecode.min.js +1 -1
- package/richtexteditor/plugins/insertcode.js +21 -3
- package/richtexteditor/plugins/insertcode.min.js +4 -4
- package/richtexteditor/plugins/insertemoji.js +112 -15
- package/richtexteditor/plugins/insertemoji.min.js +1 -1
- package/richtexteditor/plugins/insertgallery.js +42 -36
- package/richtexteditor/plugins/insertgallery.min.js +1 -1
- package/richtexteditor/plugins/inserttemplate.js +33 -17
- package/richtexteditor/plugins/inserttemplate.min.js +1 -1
- package/richtexteditor/plugins/keyboarda11y.js +66 -3
- package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
- package/richtexteditor/plugins/layouttable.js +20 -0
- package/richtexteditor/plugins/layouttable.min.js +1 -1
- package/richtexteditor/plugins/linkchecker.js +29 -9
- package/richtexteditor/plugins/linkchecker.min.js +1 -1
- package/richtexteditor/plugins/localdraft.js +51 -7
- package/richtexteditor/plugins/localdraft.min.js +1 -1
- package/richtexteditor/plugins/markdownexport.js +159 -0
- package/richtexteditor/plugins/markdownexport.min.js +2 -2
- package/richtexteditor/plugins/mathdialog.js +11 -5
- package/richtexteditor/plugins/mathdialog.min.js +1 -1
- package/richtexteditor/plugins/mentionplugin.js +88 -25
- package/richtexteditor/plugins/mentionplugin.min.js +1 -1
- package/richtexteditor/plugins/mermaiddiagram.js +9 -3
- package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
- package/richtexteditor/plugins/multilevellist.js +4 -0
- package/richtexteditor/plugins/multilevellist.min.js +1 -1
- package/richtexteditor/plugins/pdfexport.js +235 -35
- package/richtexteditor/plugins/pdfexport.min.js +12 -12
- package/richtexteditor/plugins/permanentpen.js +3 -3
- package/richtexteditor/plugins/permanentpen.min.js +1 -1
- package/richtexteditor/plugins/readabilitystats.js +25 -11
- package/richtexteditor/plugins/readabilitystats.min.js +1 -1
- package/richtexteditor/plugins/readaloud.js +29 -15
- package/richtexteditor/plugins/readaloud.min.js +1 -1
- package/richtexteditor/plugins/restrictedediting.js +166 -0
- package/richtexteditor/plugins/restrictedediting.min.js +2 -2
- package/richtexteditor/plugins/revisionhistory.js +169 -37
- package/richtexteditor/plugins/revisionhistory.min.js +3 -3
- package/richtexteditor/plugins/rubyannotation.js +19 -9
- package/richtexteditor/plugins/rubyannotation.min.js +1 -1
- package/richtexteditor/plugins/sanitizer.js +86 -4
- package/richtexteditor/plugins/sanitizer.min.js +1 -1
- package/richtexteditor/plugins/slashcommand.js +30 -16
- package/richtexteditor/plugins/slashcommand.min.js +2 -2
- package/richtexteditor/plugins/smartchips.js +36 -22
- package/richtexteditor/plugins/smartchips.min.js +2 -2
- package/richtexteditor/plugins/spellcheck.js +17 -8
- package/richtexteditor/plugins/spellcheck.min.js +2 -2
- package/richtexteditor/plugins/stateinspector.js +19 -9
- package/richtexteditor/plugins/stateinspector.min.js +2 -2
- package/richtexteditor/plugins/structured-content-bridge.js +56 -24
- package/richtexteditor/plugins/structured-content-bridge.min.js +7 -7
- package/richtexteditor/plugins/tableofcontents.js +5 -2
- package/richtexteditor/plugins/tableofcontents.min.js +1 -1
- package/richtexteditor/plugins/tabletools.js +388 -3
- package/richtexteditor/plugins/tabletools.min.js +1 -1
- package/richtexteditor/plugins/textdirection.js +18 -5
- package/richtexteditor/plugins/textdirection.min.js +1 -1
- package/richtexteditor/plugins/textpartlanguage.js +161 -32
- package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
- package/richtexteditor/plugins/todolist.js +90 -6
- package/richtexteditor/plugins/todolist.min.js +1 -1
- package/richtexteditor/plugins/trackedchanges.js +1812 -25
- package/richtexteditor/plugins/trackedchanges.min.js +5 -3
- package/richtexteditor/plugins/wordexport.js +2 -2
- package/richtexteditor/plugins/yjscollab.js +9 -4
- package/richtexteditor/plugins/yjscollab.min.js +1 -1
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
- package/richtexteditor/rte-config.js +131 -50
- package/richtexteditor/rte-headless.js +19 -4
- package/richtexteditor/rte-webcomponent.js +89 -16
- package/richtexteditor/rte.js +133 -52
- package/richtexteditor/runtime/colorpicker_more_ns.htm +93 -16
- package/scripts/build-angular.mjs +97 -0
- package/svelte/index.d.ts +2 -1
- package/svelte/index.js +80 -9
- package/vue/index.js +130 -34
- package/angular/component.ts +0 -297
- package/angular/index.ts +0 -24
|
@@ -22,7 +22,10 @@ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
|
22
22
|
var scripts = document.getElementsByTagName("script");
|
|
23
23
|
for (var j = scripts.length - 1; j >= 0; j--) {
|
|
24
24
|
var src = scripts[j].getAttribute("src") || "";
|
|
25
|
-
|
|
25
|
+
// 2026-10-17 the .min names too: a page loading all_plugins.min.js matched nothing and fell back to the fixed
|
|
26
|
+
// /richtexteditor path - the drive root over file://, so the AI stylesheet never loaded from a download opened
|
|
27
|
+
// from disk. tools/ai/aitoolkit-css-location.mjs
|
|
28
|
+
if (/(?:^|\/)(?:aitoolkit|all_plugins)(?:\.min)?\.js(?:[?#].*)?$/i.test(src)) {
|
|
26
29
|
ourScript = scripts[j];
|
|
27
30
|
break;
|
|
28
31
|
}
|
|
@@ -33,11 +36,13 @@ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
|
33
36
|
// paying for it. The readable .css is still shipped and still the one to edit
|
|
34
37
|
// or override; this only changes what gets fetched by default.
|
|
35
38
|
function resolve(name) {
|
|
36
|
-
return srcAttr
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if (srcAttr) return srcAttr.replace(/[?#].*$/, "").replace(/[^/]+$/, name);
|
|
40
|
+
// no plugin script of ours (bundled into an app script): the folder rte.js was loaded from, then the old default
|
|
41
|
+
var base = (typeof RichTextEditor !== "undefined" && RichTextEditor.__scriptBase) ||
|
|
42
|
+
(window.RTE_DefaultConfig && window.RTE_DefaultConfig.url_base) || "/richtexteditor";
|
|
43
|
+
return base + "/plugins/" + name;
|
|
39
44
|
}
|
|
40
|
-
function bust(href) { return href.indexOf("?") < 0 ? href + "?v=
|
|
45
|
+
function bust(href) { return href.indexOf("?") < 0 ? href + "?v=20261018a" : href; }
|
|
41
46
|
|
|
42
47
|
var link = document.createElement("link");
|
|
43
48
|
link.rel = "stylesheet";
|
|
@@ -127,26 +132,26 @@ function RTE_Plugin_AIToolkit() {
|
|
|
127
132
|
// in a later pass. Customer overrides via `aiToolkitActions` keep
|
|
128
133
|
// any legacy long-form labels untouched.
|
|
129
134
|
config.aiToolkitActions = config.aiToolkitActions || [
|
|
130
|
-
{ id: "chat-panel", section: "Start", icon: "chat", title: "Chat", description: "Ask follow-up questions about this document.", target: "chat-panel" },
|
|
131
|
-
{ id: "open-dialog", section: "Start", icon: "open_dialog", title: "Draft", description: "Generate, rewrite, translate, or summarize before applying.", target: "dialog" },
|
|
132
|
-
{ id: "review-panel", section: "Start", icon: "review", title: "Review", description: "Accept or reject prepared AI edits.", target: "review-panel" },
|
|
133
|
-
{ id: "proofread", section: "Quick edit", icon: "proofread", title: "Proofread", description: "Check grammar, spacing, and clarity.", resolverMode: "proofread", target: "selection-preview" },
|
|
134
|
-
{ id: "rewrite", section: "Quick edit", icon: "rewrite", title: "Rewrite", description: "Improve clarity without changing intent.", resolverMode: "rewrite", target: "selection-preview" },
|
|
135
|
-
{ id: "translate", section: "Quick edit", icon: "translate", title: "Translate", description: "Choose a language and preview the result.", resolverMode: "translate", target: "dialog", autoRun: false },
|
|
136
|
-
{ id: "justify", section: "Quick edit", icon: "justify", title: "Explain", description: "Rewrite and show why it changed.", resolverMode: "justify", target: "dialog", autoRun: false },
|
|
137
|
-
{ id: "comment", section: "Insert", icon: "comment", title: "Add note", description: "Insert a short comment near the selection.", resolverMode: "comment", target: "comment" },
|
|
138
|
-
{ id: "paragraph", section: "Insert", icon: "paragraph", title: "Add paragraph", description: "Draft a supporting paragraph below.", resolverMode: "paragraph", target: "insert" }
|
|
135
|
+
{ id: "chat-panel", section: aiT("aia_sec_start", "Start"), icon: "chat", title: aiT("aia_chat_panel_t", "Chat"), description: aiT("aia_chat_panel_d", "Ask follow-up questions about this document."), target: "chat-panel" },
|
|
136
|
+
{ id: "open-dialog", section: aiT("aia_sec_start", "Start"), icon: "open_dialog", title: aiT("aia_open_dialog_t", "Draft"), description: aiT("aia_open_dialog_d", "Generate, rewrite, translate, or summarize before applying."), target: "dialog" },
|
|
137
|
+
{ id: "review-panel", section: aiT("aia_sec_start", "Start"), icon: "review", title: aiT("aia_review_panel_t", "Review"), description: aiT("aia_review_panel_d", "Accept or reject prepared AI edits."), target: "review-panel" },
|
|
138
|
+
{ id: "proofread", section: aiT("aia_sec_quick", "Quick edit"), icon: "proofread", title: aiT("aia_proofread_t", "Proofread"), description: aiT("aia_proofread_d", "Check grammar, spacing, and clarity."), resolverMode: "proofread", target: "selection-preview" },
|
|
139
|
+
{ id: "rewrite", section: aiT("aia_sec_quick", "Quick edit"), icon: "rewrite", title: aiT("aia_rewrite_t", "Rewrite"), description: aiT("aia_rewrite_d", "Improve clarity without changing intent."), resolverMode: "rewrite", target: "selection-preview" },
|
|
140
|
+
{ id: "translate", section: aiT("aia_sec_quick", "Quick edit"), icon: "translate", title: aiT("aia_translate_t", "Translate"), description: aiT("aia_translate_d", "Choose a language and preview the result."), resolverMode: "translate", target: "dialog", autoRun: false },
|
|
141
|
+
{ id: "justify", section: aiT("aia_sec_quick", "Quick edit"), icon: "justify", title: aiT("aia_justify_t", "Explain"), description: aiT("aia_justify_d", "Rewrite and show why it changed."), resolverMode: "justify", target: "dialog", autoRun: false },
|
|
142
|
+
{ id: "comment", section: aiT("aia_sec_insert", "Insert"), icon: "comment", title: aiT("aia_comment_t", "Add note"), description: aiT("aia_comment_d", "Insert a short comment near the selection."), resolverMode: "comment", target: "comment" },
|
|
143
|
+
{ id: "paragraph", section: aiT("aia_sec_insert", "Insert"), icon: "paragraph", title: aiT("aia_paragraph_t", "Add paragraph"), description: aiT("aia_paragraph_d", "Draft a supporting paragraph below."), resolverMode: "paragraph", target: "insert" }
|
|
139
144
|
];
|
|
140
145
|
// 2026-05-09 (v20260509j): single-verb default chip labels matching
|
|
141
146
|
// the toolbar dropdown idiom — chips render on one line at the
|
|
142
147
|
// default 390px chat panel width. Customer overrides via
|
|
143
148
|
// aiToolkitChatPrompts keep their own labels untouched.
|
|
144
149
|
config.aiToolkitChatPrompts = config.aiToolkitChatPrompts || [
|
|
145
|
-
{ id: "summarize", label: "Summarize", prompt: "Summarize the current content and suggest the clearest next edit." },
|
|
146
|
-
{ id: "proofread", label: "Proofread", prompt: "Proofread the current content and prepare a cleaner version I can review." },
|
|
147
|
-
{ id: "translate", label: "Translate", prompt: "Translate the current content into Spanish and prepare a reviewable version." },
|
|
148
|
-
{ id: "headings", label: "Headings", prompt: "Suggest better section titles and subheadings for this content." },
|
|
149
|
-
{ id: "expand", label: "Expand", prompt: "Add a short supporting paragraph that strengthens this content." }
|
|
150
|
+
{ id: "summarize", label: aiT("aip_summarize", "Summarize"), prompt: "Summarize the current content and suggest the clearest next edit." },
|
|
151
|
+
{ id: "proofread", label: aiT("aip_proofread", "Proofread"), prompt: "Proofread the current content and prepare a cleaner version I can review." },
|
|
152
|
+
{ id: "translate", label: aiT("aip_translate", "Translate"), prompt: "Translate the current content into Spanish and prepare a reviewable version." },
|
|
153
|
+
{ id: "headings", label: aiT("aip_headings", "Headings"), prompt: "Suggest better section titles and subheadings for this content." },
|
|
154
|
+
{ id: "expand", label: aiT("aip_expand", "Expand"), prompt: "Add a short supporting paragraph that strengthens this content." }
|
|
150
155
|
];
|
|
151
156
|
// 2026-05-20 (v20260520c): descriptions trimmed to ~5-8 words so
|
|
152
157
|
// each fits the one-line mode caption without wrapping at the 480px
|
|
@@ -165,16 +170,23 @@ function RTE_Plugin_AIToolkit() {
|
|
|
165
170
|
// on narrow viewports. Same brevity Notion AI inline composer subtitles
|
|
166
171
|
// ship — "Improve writing", "Make shorter" — single 2-4 word verb
|
|
167
172
|
// phrases.
|
|
173
|
+
// 2026-10-18 the Ask AI dialog's words read config.text_ keys where they are SHOWN (English as the fallback);
|
|
174
|
+
// labels that also feed logic (Document / Selection) are translated at the display sites only.
|
|
175
|
+
// tools/i18n/ask-ai-language.mjs
|
|
176
|
+
function aiT(key, fallback, arg) { var s = (config && config["text_" + key]) || fallback; return arg === undefined ? s : s.split("{0}").join(String(arg)); }
|
|
177
|
+
obj.__aiT = aiT;
|
|
168
178
|
config.aiToolkitDialogModes = config.aiToolkitDialogModes || [
|
|
169
|
-
{ id: "proofread", title: "Proofread", description: "Fix grammar & clarity." },
|
|
170
|
-
{ id: "rewrite", title: "Rewrite", description: "Rewrite for clarity." },
|
|
171
|
-
{ id: "translate", title: "Translate", description: "Translate to a language." },
|
|
172
|
-
{ id: "justify", title: "Explain rewrite", description: "Rewrite and explain why." },
|
|
173
|
-
{ id: "shorten", title: "Shorten", description: "Compress to be shorter." },
|
|
174
|
-
{ id: "expand", title: "Expand", description: "Add supporting detail." },
|
|
175
|
-
{ id: "summarize", title: "Summarize", description: "Summarize the source." }
|
|
179
|
+
{ id: "proofread", title: aiT("aim_proofread_t", "Proofread"), description: aiT("aim_proofread_d", "Fix grammar & clarity.") },
|
|
180
|
+
{ id: "rewrite", title: aiT("aim_rewrite_t", "Rewrite"), description: aiT("aim_rewrite_d", "Rewrite for clarity.") },
|
|
181
|
+
{ id: "translate", title: aiT("aim_translate_t", "Translate"), description: aiT("aim_translate_d", "Translate to a language.") },
|
|
182
|
+
{ id: "justify", title: aiT("aim_justify_t", "Explain rewrite"), description: aiT("aim_justify_d", "Rewrite and explain why.") },
|
|
183
|
+
{ id: "shorten", title: aiT("aim_shorten_t", "Shorten"), description: aiT("aim_shorten_d", "Compress to be shorter.") },
|
|
184
|
+
{ id: "expand", title: aiT("aim_expand_t", "Expand"), description: aiT("aim_expand_d", "Add supporting detail.") },
|
|
185
|
+
{ id: "summarize", title: aiT("aim_summarize_t", "Summarize"), description: aiT("aim_summarize_d", "Summarize the source.") }
|
|
176
186
|
];
|
|
177
187
|
config.aiToolkitResolver = config.aiToolkitResolver || null;
|
|
188
|
+
// 2026-10-13 image generation: the host's provider call, bring your own key. null = not configured.
|
|
189
|
+
config.aiToolkitImageResolver = config.aiToolkitImageResolver || null;
|
|
178
190
|
config.aiToolkitOperationHandlers = config.aiToolkitOperationHandlers || {};
|
|
179
191
|
config.aiToolkitPersistenceKey = config.aiToolkitPersistenceKey || "";
|
|
180
192
|
config.aiToolkitReviewSyncInterval = config.aiToolkitReviewSyncInterval || 15000;
|
|
@@ -200,10 +212,129 @@ function RTE_Plugin_AIToolkit() {
|
|
|
200
212
|
|
|
201
213
|
obj.InitEditor = function (argeditor) {
|
|
202
214
|
editor = argeditor;
|
|
215
|
+
|
|
216
|
+
// 2026-10-13 Image generation (tools/ai/image-generation.mjs). The host supplies the provider call through
|
|
217
|
+
// config.aiToolkitImageResolver, so the editor makes no outbound request of its own. What comes back is checked
|
|
218
|
+
// before anything is inserted: an image data URL (png, jpeg, webp or gif) or an https address, at most 6 million
|
|
219
|
+
// characters.
|
|
220
|
+
var IMAGE_SRC_MAX = 6000000;
|
|
221
|
+
function checkGeneratedImageSrc(src) {
|
|
222
|
+
var s = typeof src === "string" ? src.replace(/^\s+|\s+$/g, "") : "";
|
|
223
|
+
if (!s) throw new Error("The image generator returned no image.");
|
|
224
|
+
if (s.length > IMAGE_SRC_MAX) throw new Error("The generated image is too large to insert.");
|
|
225
|
+
if (/^data:image\/(png|jpe?g|webp|gif);base64,[A-Za-z0-9+\/=]+$/i.test(s)) return s;
|
|
226
|
+
if (/^https:\/\/[^\s"'<>]+$/i.test(s)) return s;
|
|
227
|
+
throw new Error("The image generator returned something that is not an image address.");
|
|
228
|
+
}
|
|
229
|
+
function generateImage(prompt) {
|
|
230
|
+
var text = String(prompt || "").replace(/^\s+|\s+$/g, "");
|
|
231
|
+
if (!text) return Promise.reject(new Error("Describe the image first."));
|
|
232
|
+
var resolver = config.aiToolkitImageResolver;
|
|
233
|
+
if (typeof resolver !== "function") {
|
|
234
|
+
return Promise.reject(new Error("No image generator is configured (config.aiToolkitImageResolver)."));
|
|
235
|
+
}
|
|
236
|
+
return Promise.resolve().then(function () { return resolver(text); }).then(checkGeneratedImageSrc);
|
|
237
|
+
}
|
|
238
|
+
function insertGeneratedImage(src, alt) {
|
|
239
|
+
editor.insertHTML('<img src="' + escapeAttribute(src) + '" alt="' + escapeAttribute(alt || "") + '">');
|
|
240
|
+
try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) { /* ignore */ }
|
|
241
|
+
}
|
|
242
|
+
var imageComposer = null;
|
|
243
|
+
function closeImageComposer() {
|
|
244
|
+
// 2026-10-14 Focus inside the card (Escape, Cancel) goes back to the editor when it closes, so a
|
|
245
|
+
// keyboard user does not land on the page body (WCAG 2.4.3). tools/ai/image-generator-focus.mjs
|
|
246
|
+
var hadFocus = false;
|
|
247
|
+
try { hadFocus = !!(imageComposer && imageComposer.contains(imageComposer.ownerDocument.activeElement)); } catch (e) { /* ignore */ }
|
|
248
|
+
if (imageComposer && imageComposer.parentNode) imageComposer.parentNode.removeChild(imageComposer);
|
|
249
|
+
imageComposer = null;
|
|
250
|
+
if (hadFocus) { try { editor.focus(); } catch (e) { /* ignore */ } }
|
|
251
|
+
}
|
|
252
|
+
// A small prompt card over the editor. Generate inserts the image at the caret and closes the card; Cancel and
|
|
253
|
+
// Escape close it without inserting anything.
|
|
254
|
+
function openImageComposer() {
|
|
255
|
+
closeImageComposer();
|
|
256
|
+
var frame = editor.iframe;
|
|
257
|
+
if (!frame || !frame.ownerDocument) return;
|
|
258
|
+
var host = frame.ownerDocument;
|
|
259
|
+
var box = host.createElement("div");
|
|
260
|
+
box.className = "rte-image-gen";
|
|
261
|
+
box.setAttribute("role", "dialog");
|
|
262
|
+
box.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aig_title", "Generate an image") : "Generate an image"));
|
|
263
|
+
var bs = box.style;
|
|
264
|
+
bs.position = "fixed"; bs.zIndex = "2147483000"; bs.width = "320px"; bs.padding = "12px";
|
|
265
|
+
bs.background = "#fff"; bs.border = "1px solid rgba(0,0,0,.15)"; bs.borderRadius = "8px";
|
|
266
|
+
bs.boxShadow = "0 8px 24px rgba(15,23,42,.18)"; bs.font = "13px/1.4 system-ui, sans-serif"; bs.color = "#1f2937";
|
|
267
|
+
var fr = frame.getBoundingClientRect();
|
|
268
|
+
bs.left = Math.round(fr.left + 16) + "px";
|
|
269
|
+
bs.top = Math.round(fr.top + 16) + "px";
|
|
270
|
+
var label = host.createElement("label");
|
|
271
|
+
label.textContent = (obj.__aiT ? obj.__aiT("aig_describe", "Describe the image") : "Describe the image");
|
|
272
|
+
label.style.display = "block";
|
|
273
|
+
label.style.marginBottom = "6px";
|
|
274
|
+
var area = host.createElement("textarea");
|
|
275
|
+
area.rows = 3;
|
|
276
|
+
area.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aig_describe", "Describe the image") : "Describe the image"));
|
|
277
|
+
area.style.width = "100%"; area.style.boxSizing = "border-box"; area.style.font = "inherit";
|
|
278
|
+
var status = host.createElement("div");
|
|
279
|
+
status.className = "rte-image-gen-status";
|
|
280
|
+
status.setAttribute("aria-live", "polite");
|
|
281
|
+
status.style.minHeight = "18px"; status.style.margin = "6px 0"; status.style.color = "#b91c1c";
|
|
282
|
+
var row = host.createElement("div");
|
|
283
|
+
row.style.textAlign = "right";
|
|
284
|
+
var cancel = host.createElement("button");
|
|
285
|
+
cancel.type = "button";
|
|
286
|
+
cancel.className = "rte-image-gen-cancel";
|
|
287
|
+
cancel.textContent = (obj.__aiT ? obj.__aiT("aig_cancel", "Cancel") : "Cancel");
|
|
288
|
+
cancel.style.marginRight = "6px";
|
|
289
|
+
var go = host.createElement("button");
|
|
290
|
+
go.type = "button";
|
|
291
|
+
go.className = "rte-image-gen-go";
|
|
292
|
+
go.textContent = (obj.__aiT ? obj.__aiT("aig_generate", "Generate") : "Generate");
|
|
293
|
+
go.style.background = "#1d67ba"; go.style.color = "#fff"; go.style.border = "0";
|
|
294
|
+
go.style.borderRadius = "4px"; go.style.padding = "4px 12px";
|
|
295
|
+
go.addEventListener("click", function () {
|
|
296
|
+
var text = area.value.replace(/^\s+|\s+$/g, "");
|
|
297
|
+
if (!text) { status.textContent = (obj.__aiT ? obj.__aiT("aig_describefirst", "Describe the image first.") : "Describe the image first."); return; }
|
|
298
|
+
go.disabled = true;
|
|
299
|
+
status.style.color = "#475569";
|
|
300
|
+
status.textContent = (obj.__aiT ? obj.__aiT("aig_generating", "Generating...") : "Generating...");
|
|
301
|
+
generateImage(text).then(function (src) {
|
|
302
|
+
insertGeneratedImage(src, text);
|
|
303
|
+
closeImageComposer();
|
|
304
|
+
// the Generate button was disabled while it ran, which moved focus out of the card: return it here
|
|
305
|
+
try { editor.focus(); } catch (e) { /* ignore */ }
|
|
306
|
+
}, function (err) {
|
|
307
|
+
go.disabled = false;
|
|
308
|
+
status.style.color = "#b91c1c";
|
|
309
|
+
status.textContent = (err && err.message) || (obj.__aiT ? obj.__aiT("aig_failed", "The image could not be generated.") : "The image could not be generated.");
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
cancel.addEventListener("click", closeImageComposer);
|
|
313
|
+
box.addEventListener("keydown", function (e) {
|
|
314
|
+
if (e.key === "Escape" || e.key === "Esc") { e.preventDefault(); closeImageComposer(); }
|
|
315
|
+
});
|
|
316
|
+
row.appendChild(cancel);
|
|
317
|
+
row.appendChild(go);
|
|
318
|
+
box.appendChild(label);
|
|
319
|
+
box.appendChild(area);
|
|
320
|
+
box.appendChild(status);
|
|
321
|
+
box.appendChild(row);
|
|
322
|
+
host.body.appendChild(box);
|
|
323
|
+
imageComposer = box;
|
|
324
|
+
try { area.focus(); } catch (e) { /* focus is best effort */ }
|
|
325
|
+
}
|
|
326
|
+
editor.generateImage = function (prompt) {
|
|
327
|
+
return generateImage(prompt).then(function (src) { insertGeneratedImage(src, prompt); return true; });
|
|
328
|
+
};
|
|
329
|
+
editor.openImageGenerator = function () { openImageComposer(); };
|
|
330
|
+
|
|
203
331
|
editor.aiToolkit = {
|
|
332
|
+
generateImage: generateImage,
|
|
333
|
+
setImageResolver: function (fn) { config.aiToolkitImageResolver = typeof fn === "function" ? fn : null; },
|
|
204
334
|
normalizeText: normalizeText,
|
|
205
335
|
ensureSentence: ensureSentence,
|
|
206
336
|
buildDemoResult: buildDemoResult,
|
|
337
|
+
isDemoMode: isDemoMode,
|
|
207
338
|
getTranslateLanguages: getTranslateLanguages,
|
|
208
339
|
getTranslateLanguageLabel: getTranslateLanguageLabel,
|
|
209
340
|
getOperationContract: getOperationContract,
|
|
@@ -690,6 +821,21 @@ function RTE_Plugin_AIToolkit() {
|
|
|
690
821
|
config[key] = current ? current + token : token;
|
|
691
822
|
}
|
|
692
823
|
|
|
824
|
+
// 2026-10-15 with no resolver the toolkit answers from built-in samples, and nothing said so: an evaluating
|
|
825
|
+
// customer reported the AI feature as "not working". The dialog and the chat panel now carry a notice while
|
|
826
|
+
// no resolver is set. tools/ai/demo-mode-notice.mjs
|
|
827
|
+
function isDemoMode() {
|
|
828
|
+
return typeof config.aiToolkitResolver !== "function";
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function appendDemoNotice(parent) {
|
|
832
|
+
if (!isDemoMode()) return null;
|
|
833
|
+
var notice = append(parent, "div", "", "rte-ai-demo-notice",
|
|
834
|
+
config.text_aitoolkitdemonotice || "Demo mode: these are built-in sample results, not a real AI model. Connect an AI provider with the aiToolkitResolver setting to get real suggestions.");
|
|
835
|
+
notice.setAttribute("role", "note");
|
|
836
|
+
return notice;
|
|
837
|
+
}
|
|
838
|
+
|
|
693
839
|
function append(parent, tagName, cssText, className, text) {
|
|
694
840
|
var node = parent.ownerDocument.createElement(tagName);
|
|
695
841
|
if (cssText) {
|
|
@@ -1100,7 +1246,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1100
1246
|
if (mode === "insert" && editor && editor.insertHTML) {
|
|
1101
1247
|
editor.insertHTML(html);
|
|
1102
1248
|
} else if (editor && editor.setHTMLCode) {
|
|
1103
|
-
|
|
1249
|
+
// undoable: Ctrl+Z gives back exactly the document this replaced
|
|
1250
|
+
// (tools/import/replace-undo-plugins.mjs)
|
|
1251
|
+
editor.setHTMLCode(html, { undoable: true });
|
|
1104
1252
|
}
|
|
1105
1253
|
return html;
|
|
1106
1254
|
}).catch(function (err) {
|
|
@@ -1137,6 +1285,385 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1137
1285
|
.trim();
|
|
1138
1286
|
}
|
|
1139
1287
|
|
|
1288
|
+
// 2026-10-18 Spoken labels are phrases joined into sentences. A phrase that already ended one ("... is ready.")
|
|
1289
|
+
// gave "ready.. Document thread"; after such a phrase the next follows with a space. tools/ai/aria-double-period.mjs
|
|
1290
|
+
function joinSentences(parts) {
|
|
1291
|
+
var out = "";
|
|
1292
|
+
for (var i = 0; i < parts.length; i++) {
|
|
1293
|
+
var s = String(parts[i] == null ? "" : parts[i]).replace(/[ \t\r\n]+$/, "");
|
|
1294
|
+
if (!s) continue;
|
|
1295
|
+
out += !out ? s : (/[.?!\u2026\u3002\uff01\uff1f]$/.test(out) ? " " : ". ") + s;
|
|
1296
|
+
}
|
|
1297
|
+
return out;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// 2026-10-18 The inline AI suggestion card and the AI chat compose most of their words - shown and announced -
|
|
1301
|
+
// from many small builders. With a language pack loaded, this catalog translates them where they are displayed:
|
|
1302
|
+
// a whole string, else sentence by sentence, with patterns for the counts ("Queue 1 of 2"). Text a person wrote
|
|
1303
|
+
// (the card's Current / Suggested compare, the chat's messages) is never touched; anything not catalogued stays
|
|
1304
|
+
// as it is; an English page does nothing here. tools/i18n/ai-card-chat-language.mjs
|
|
1305
|
+
var AIQ_EXACT = [
|
|
1306
|
+
["air_accept", "Accept"],
|
|
1307
|
+
["air_reject", "Reject"],
|
|
1308
|
+
["air_current", "Current"],
|
|
1309
|
+
["air_suggested", "Suggested"],
|
|
1310
|
+
["air_why", "Why"],
|
|
1311
|
+
["air_scope_sel", "Selection"],
|
|
1312
|
+
["air_scope_doc", "Document"],
|
|
1313
|
+
["air_type_proofread", "Proofread"],
|
|
1314
|
+
["air_type_rewrite", "Rewrite"],
|
|
1315
|
+
["air_type_translate", "Translate"],
|
|
1316
|
+
["air_type_comment", "Comment"],
|
|
1317
|
+
["air_type_paragraph", "Paragraph"],
|
|
1318
|
+
["air_type_summarize", "Summarize"],
|
|
1319
|
+
["air_type_expand", "Expand"],
|
|
1320
|
+
["air_type_shorten", "Shorten"],
|
|
1321
|
+
["aip_headings", "Headings"],
|
|
1322
|
+
["aichat", "AI Chat"],
|
|
1323
|
+
["aic_newchat", "Start a fresh chat"],
|
|
1324
|
+
["aic_closechat", "Close AI chat"],
|
|
1325
|
+
["aic_close", "Close"],
|
|
1326
|
+
["aic_usesel", "Use selection"],
|
|
1327
|
+
["aic_usedoc", "Use document"],
|
|
1328
|
+
["aic_copymsg", "Copy message text"],
|
|
1329
|
+
["aic_copy", "Copy"],
|
|
1330
|
+
["aic_regen", "Regenerate response"],
|
|
1331
|
+
["aid_regenerate", "Regenerate"],
|
|
1332
|
+
["aic_actions", "AI chat actions"],
|
|
1333
|
+
["aic_send", "Send"],
|
|
1334
|
+
["aic_starttitle", "Start with AI Chat"],
|
|
1335
|
+
["aic_pill_doc", "Document context"],
|
|
1336
|
+
["aic_pill_sel", "Selection context"],
|
|
1337
|
+
["aic_compose_doc", "Compose next AI request for the current document"],
|
|
1338
|
+
["aic_compose_sel", "Compose next AI request for the current selection"],
|
|
1339
|
+
["aic_emptytitle_doc", "Type a prompt or load a starter for the current document first"],
|
|
1340
|
+
["aic_emptytitle_sel", "Type a prompt or load a starter for the current selection first"],
|
|
1341
|
+
["aic_empty_doc", "Ask anything about the current document"],
|
|
1342
|
+
["aic_empty_sel", "Ask anything about the current selection"],
|
|
1343
|
+
["air_focusaria", "Focus the current AI review item"],
|
|
1344
|
+
["aiq_preview", "AI suggestion preview"],
|
|
1345
|
+
["aiq_proposed", "Proposed edit"],
|
|
1346
|
+
["aiq_curandsug", "Current and suggested text"],
|
|
1347
|
+
["aiq_curtext", "Current text"],
|
|
1348
|
+
["aiq_sugtext", "Suggested text"],
|
|
1349
|
+
["aiq_ready", "Ready to review"],
|
|
1350
|
+
["aiq_curfocus", "Current focus"],
|
|
1351
|
+
["aiq_seldraft", "Selection draft"],
|
|
1352
|
+
["aiq_docdraft", "Document draft"],
|
|
1353
|
+
["aiq_prepdraft", "Prepared draft"],
|
|
1354
|
+
["aiq_customdraft", "Custom draft"],
|
|
1355
|
+
["aiq_draftready", "Draft ready"],
|
|
1356
|
+
["aiq_glance", "Change at a glance"],
|
|
1357
|
+
["aiq_surface", "Surface edit"],
|
|
1358
|
+
["aiq_textrewrite", "Text rewrite"],
|
|
1359
|
+
["aiq_textchange", "Text change"],
|
|
1360
|
+
["aiq_langchange", "Language change"],
|
|
1361
|
+
["aiq_reviewchange", "Reviewable change"],
|
|
1362
|
+
["aiq_acceptedchange", "Accepted change"],
|
|
1363
|
+
["aiq_rejectedchange", "Rejected change"],
|
|
1364
|
+
["aiq_lowimpact", "Low impact"],
|
|
1365
|
+
["aiq_medimpact", "Medium impact"],
|
|
1366
|
+
["aiq_highimpact", "High impact"],
|
|
1367
|
+
["aiq_whatnext", "What happens next"],
|
|
1368
|
+
["aiq_qcomplete", "Queue complete"],
|
|
1369
|
+
["aiq_qopened", "Queue opened"],
|
|
1370
|
+
["aiq_qhandoff", "Queue handoff"],
|
|
1371
|
+
["aiq_qstate", "Queue state"],
|
|
1372
|
+
["aiq_recready", "Recovery ready"],
|
|
1373
|
+
["aiq_recavail", "Recovery available"],
|
|
1374
|
+
["aiq_finalitem", "Accepting or rejecting this final pending item finishes the current review run"],
|
|
1375
|
+
["aiq_moveson", "Accepting or rejecting this item moves review straight to the next pending suggestion"],
|
|
1376
|
+
["aiq_nolater", "No later pending items remain in this queue"],
|
|
1377
|
+
["aiq_thenrec", "Then recovery and next queues appear"],
|
|
1378
|
+
["aiq_thencomplete", "Then queue complete"],
|
|
1379
|
+
["aiq_undolast", "Undo the last decision or open another queue"],
|
|
1380
|
+
["aiq_afterfinal", "After reviewing the final pending item, recovery and next queues appear"],
|
|
1381
|
+
["aiq_afterdec", "After this decision"],
|
|
1382
|
+
["aiq_handoff", "Action handoff"],
|
|
1383
|
+
["aiq_decidelane", "Decide lane"],
|
|
1384
|
+
["aiq_curlane", "Current lane"],
|
|
1385
|
+
["aiq_curactionlane", "Current action lane"],
|
|
1386
|
+
["aiq_kblane", "Keyboard lane"],
|
|
1387
|
+
["aiq_curenter", "Current Enter target"],
|
|
1388
|
+
["aiq_spaceact", "Enter or Space activates this action"],
|
|
1389
|
+
["aiq_acceptfinish", "Accept & finish"],
|
|
1390
|
+
["aiq_rejectfinish", "Reject & finish"],
|
|
1391
|
+
["aiq_acceptnext", "Accept & next"],
|
|
1392
|
+
["aiq_rejectnext", "Reject & next"],
|
|
1393
|
+
["aiq_accfinishdesc", "Accept & finish this AI change and finish the current review queue"],
|
|
1394
|
+
["aiq_rejfinishdesc", "Reject & finish this AI change and finish the current review queue"],
|
|
1395
|
+
["aiq_open", "Open"],
|
|
1396
|
+
["aiq_review", "Review"],
|
|
1397
|
+
["aiq_move", "Move"],
|
|
1398
|
+
["aiq_previous", "Previous"],
|
|
1399
|
+
["aiq_next", "Next"],
|
|
1400
|
+
["aiq_decide", "Decide"],
|
|
1401
|
+
["aiq_recover", "Recover"],
|
|
1402
|
+
["aiq_actions", "AI suggestion actions"],
|
|
1403
|
+
["aiq_openactions", "Open actions"],
|
|
1404
|
+
["aiq_moveactions", "Move actions"],
|
|
1405
|
+
["aiq_decideactions", "Decide actions"],
|
|
1406
|
+
["aiq_recoveractions", "Recover actions"],
|
|
1407
|
+
["aiq_whysug", "Why this suggestion"],
|
|
1408
|
+
["aiq_upnext", "Up next"],
|
|
1409
|
+
["aiq_upnextcur", "Up next becomes current"],
|
|
1410
|
+
["aiq_follows", "This follows after the current decision"],
|
|
1411
|
+
["aiq_nextitem", "Next item"],
|
|
1412
|
+
["aiq_reviewnow", "Review this item now"],
|
|
1413
|
+
["aiq_reopen", "Reopen this suggestion in the current review lane"],
|
|
1414
|
+
["aiq_jump", "Jump earlier or later in this queue before deciding"],
|
|
1415
|
+
["aiq_previtem", "Previous AI review item"],
|
|
1416
|
+
["aiq_nextitemai", "Next AI review item"],
|
|
1417
|
+
["aiq_noprev", "No previous AI review item in the current queue"],
|
|
1418
|
+
["aiq_nonext", "No next AI review item in the current queue"],
|
|
1419
|
+
["aiq_shortcuts", "Inline AI review shortcuts"],
|
|
1420
|
+
["aiq_kbguide", "Keyboard guide"],
|
|
1421
|
+
["aiq_move_l", "move"],
|
|
1422
|
+
["aiq_jump_l", "jump"],
|
|
1423
|
+
["aiq_enter", "Enter"],
|
|
1424
|
+
["aiq_custom", "Custom"],
|
|
1425
|
+
["aiq_reason_proof", "Normalizes spacing, punctuation, and capitalization while keeping the original meaning intact"],
|
|
1426
|
+
["aiq_reason_rewrite", "Tightens the wording by removing filler phrases so the point comes through faster"],
|
|
1427
|
+
["aiq_c_ready", "Ready"],
|
|
1428
|
+
["aiq_c_docfocus", "Document focus"],
|
|
1429
|
+
["aiq_c_selfocus", "Selection focus"],
|
|
1430
|
+
["aiq_c_inplace", "Ask, rewrite, or review the current document in place"],
|
|
1431
|
+
["aiq_c_docthread", "Document thread"],
|
|
1432
|
+
["aiq_c_selthread", "Selection thread"],
|
|
1433
|
+
["aiq_c_newask", "Ready for a new ask"],
|
|
1434
|
+
["aiq_c_latest", "Latest reply is ready"],
|
|
1435
|
+
["aiq_c_context", "Context"],
|
|
1436
|
+
["aiq_c_docactive", "Document active"],
|
|
1437
|
+
["aiq_c_selactive", "Selection active"],
|
|
1438
|
+
["aiq_c_nosel", "No current selection"],
|
|
1439
|
+
["aiq_c_selready", "Selection ready"],
|
|
1440
|
+
["aiq_c_wholedoc", "Using the whole document as the active source"],
|
|
1441
|
+
["aiq_c_narrow", "Select text any time to narrow the ask"],
|
|
1442
|
+
["aiq_c_snapshot", "Document snapshot"],
|
|
1443
|
+
["aiq_c_scopeswitch", "Context scope switch"],
|
|
1444
|
+
["aiq_c_unavail", "Unavailable until text is selected"],
|
|
1445
|
+
["aiq_c_activescope", "Active scope"],
|
|
1446
|
+
["aiq_c_usingwhole", "AI Chat is using the whole document"],
|
|
1447
|
+
["aiq_c_quickstarts", "Quick starts"],
|
|
1448
|
+
["aiq_c_readychoose", "Ready to choose"],
|
|
1449
|
+
["aiq_c_choosestarter", "Choose a starter for the current document or keep typing your own prompt in Compose"],
|
|
1450
|
+
["aiq_c_starterbtns", "Starter buttons for the current document"],
|
|
1451
|
+
["aiq_c_loadsstarter", "Loads this starter into Compose for the current document"],
|
|
1452
|
+
["aiq_c_summarystarter", "Summary starter"],
|
|
1453
|
+
["aiq_c_reviewstarter", "Review starter"],
|
|
1454
|
+
["aiq_c_langstarter", "Language starter"],
|
|
1455
|
+
["aiq_c_structstarter", "Structure starter"],
|
|
1456
|
+
["aiq_c_expandstarter", "Expand starter"],
|
|
1457
|
+
["aiq_c_starterready", "Starter ready"],
|
|
1458
|
+
["aiq_c_p_summarize", "Summarize the current content and suggest the clearest next edit"],
|
|
1459
|
+
["aiq_c_p_proofread", "Proofread the current content and prepare a cleaner version I can review"],
|
|
1460
|
+
["aiq_c_p_translate", "Translate the current content into Spanish and prepare a reviewable version"],
|
|
1461
|
+
["aiq_c_p_headings", "Suggest better section titles and subheadings for this content"],
|
|
1462
|
+
["aiq_c_p_expand", "Add a short supporting paragraph that strengthens this content"],
|
|
1463
|
+
["aiq_c_conversation", "Conversation"],
|
|
1464
|
+
["aiq_c_firstex", "First exchange"],
|
|
1465
|
+
["aiq_c_waitfirst", "Waiting for first ask"],
|
|
1466
|
+
["aiq_c_firstanswer", "Your first answer, draft, or reviewable change for the current document will appear here"],
|
|
1467
|
+
["aiq_c_askexplore", "Ask and explore"],
|
|
1468
|
+
["aiq_c_getanswers", "Get answers, summaries, headings, or rewrite direction grounded in the current context"],
|
|
1469
|
+
["aiq_c_prepedits", "Prepare reviewable edits"],
|
|
1470
|
+
["aiq_c_previewinline", "Preview inline changes, apply structured plans, or turn responses into review-ready suggestions"],
|
|
1471
|
+
["aiq_c_usestarters", "Use starters or type your own"],
|
|
1472
|
+
["aiq_c_choosecurated", "Choose one of the curated quick starts above or write a custom prompt in the composer below"],
|
|
1473
|
+
["aiq_c_compose", "Compose"],
|
|
1474
|
+
["aiq_c_readyask", "Ready to ask about the current document"],
|
|
1475
|
+
["aiq_c_entersends", "Enter sends"],
|
|
1476
|
+
["aiq_c_composeactions", "Compose actions"],
|
|
1477
|
+
["aiq_c_openask", "Open Ask AI"],
|
|
1478
|
+
["aiq_c_senddisabled", "Send disabled"],
|
|
1479
|
+
["aiq_c_responseready", "AI response ready"],
|
|
1480
|
+
["aiq_c_replyready", "Reply ready"],
|
|
1481
|
+
["aiq_c_textchange", "This is a text change"],
|
|
1482
|
+
["aiq_c_exchange", "AI chat exchange"],
|
|
1483
|
+
["aiq_c_customex", "Custom exchange"],
|
|
1484
|
+
["aiq_c_request", "Request"],
|
|
1485
|
+
["aiq_c_curexchange", "Current exchange"],
|
|
1486
|
+
["aiq_c_you", "You"],
|
|
1487
|
+
["aiq_c_prompt", "Prompt"],
|
|
1488
|
+
["aiq_c_promptsent", "Prompt sent"],
|
|
1489
|
+
["aiq_c_waitingai", "Waiting on AI for the current document context"],
|
|
1490
|
+
["aiq_c_whatyouasked", "What you asked"],
|
|
1491
|
+
["aiq_c_usingdoc", "Using current document"],
|
|
1492
|
+
["aiq_c_response", "Response"],
|
|
1493
|
+
["aiq_c_answer", "Answer"],
|
|
1494
|
+
["aiq_c_answerready", "Answer ready"],
|
|
1495
|
+
["aiq_c_readanswer", "Read the answer grounded in the current document context"],
|
|
1496
|
+
["aiq_c_copyonly", "Copy only"],
|
|
1497
|
+
["aiq_c_copyonlygroup", "Copy only group"],
|
|
1498
|
+
["aiq_c_copyprep", "Copy this prepared reply without changing the document"],
|
|
1499
|
+
["aiq_c_copyprepai", "Copy this prepared AI reply without changing the document"],
|
|
1500
|
+
["aiq_c_copytext", "Copy text"],
|
|
1501
|
+
["aiq_c_actions", "Actions"],
|
|
1502
|
+
["aiq_c_ai", "AI"]
|
|
1503
|
+
];
|
|
1504
|
+
var AIQ_PATTERNS = [
|
|
1505
|
+
["aiq_p_queue", new RegExp("^Queue (\\d+) of (\\d+)$")],
|
|
1506
|
+
["aiq_p_step", new RegExp("^(\\d+) steps?$")],
|
|
1507
|
+
["aiq_p_keeps", new RegExp("^Keeps (\\d+) words? across (\\d+) sentences?$")],
|
|
1508
|
+
["aiq_p_queuein", new RegExp("^It is queue (\\d+) of (\\d+) in this view$")],
|
|
1509
|
+
["aiq_p_enterfocuses", new RegExp("^Enter focuses (.+)$")],
|
|
1510
|
+
["aiq_p_entercolon", new RegExp("^Enter: (.+)$")],
|
|
1511
|
+
["aiq_p_focus", new RegExp("^[Ff]ocus (.+)$")],
|
|
1512
|
+
["aiq_p_reviewopens", new RegExp("^Review opens on (.+)$")],
|
|
1513
|
+
["aiq_p_openfocus", new RegExp("^Open this AI suggestion in review and focus (.+)$")],
|
|
1514
|
+
["aiq_p_auses", new RegExp("^A uses (.+)$")],
|
|
1515
|
+
["aiq_p_ruses", new RegExp("^R uses (.+)$")],
|
|
1516
|
+
["aiq_p_exchanges", new RegExp("^(\\d+) exchanges?$")],
|
|
1517
|
+
["aiq_p_messages", new RegExp("^(\\d+) messages?$")],
|
|
1518
|
+
["aiq_p_words", new RegExp("^(\\d+) words?$")],
|
|
1519
|
+
["aid_chars", new RegExp("^(\\d+) chars?$")],
|
|
1520
|
+
["aiq_p_paragraphs", new RegExp("^(\\d+) paragraphs?$")],
|
|
1521
|
+
["aic_pill_starters", new RegExp("^(\\d+) starters?$")],
|
|
1522
|
+
["aic_pill_words", new RegExp("^(\\d+) words in scope$")],
|
|
1523
|
+
["aiq_p_exchangen", new RegExp("^Exchange (\\d+)$")],
|
|
1524
|
+
["aiq_p_turn", new RegExp("^Turn (\\d+)$")],
|
|
1525
|
+
["aiq_p_convdoc", new RegExp("^(\\d+) exchanges?, (\\d+) messages? in this conversation for the current document$")],
|
|
1526
|
+
["aiq_p_convsel", new RegExp("^(\\d+) exchanges?, (\\d+) messages? in this conversation for the current selection$")],
|
|
1527
|
+
["aiq_p_threaddoc", new RegExp("^(\\d+) exchanges?, (\\d+) messages? in this document thread$")],
|
|
1528
|
+
["aiq_p_threadsel", new RegExp("^(\\d+) exchanges?, (\\d+) messages? in this selection thread$")],
|
|
1529
|
+
["aiq_p_responding", new RegExp("^Responding to (.+) request$")],
|
|
1530
|
+
["aiq_p_actions", new RegExp("^Actions: (.+)$")]
|
|
1531
|
+
];
|
|
1532
|
+
var aiqIndex = null;
|
|
1533
|
+
function aiqLocale() {
|
|
1534
|
+
var l = config && config.text_uilocale;
|
|
1535
|
+
return l && !/^en(-|$)/i.test(l) ? l : "";
|
|
1536
|
+
}
|
|
1537
|
+
function aiqCjk() { return /^(zh|ja)/i.test(aiqLocale()); }
|
|
1538
|
+
function aiqExact(s) {
|
|
1539
|
+
if (!aiqIndex) {
|
|
1540
|
+
aiqIndex = {};
|
|
1541
|
+
for (var i = 0; i < AIQ_EXACT.length; i++) {
|
|
1542
|
+
var en = AIQ_EXACT[i][1];
|
|
1543
|
+
if (!aiqIndex["=" + en]) aiqIndex["=" + en] = AIQ_EXACT[i][0];
|
|
1544
|
+
if (!aiqIndex["~" + en.toLowerCase()]) aiqIndex["~" + en.toLowerCase()] = AIQ_EXACT[i][0];
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
var key = aiqIndex["=" + s] || aiqIndex["~" + s.toLowerCase()];
|
|
1548
|
+
return (key && config["text_" + key]) || null;
|
|
1549
|
+
}
|
|
1550
|
+
// a phrase without its closing stop: the catalog, then the patterns (a word capture must translate too)
|
|
1551
|
+
function aiqCore(s) {
|
|
1552
|
+
var v = aiqExact(s);
|
|
1553
|
+
if (v) return v;
|
|
1554
|
+
for (var i = 0; i < AIQ_PATTERNS.length; i++) {
|
|
1555
|
+
var m = AIQ_PATTERNS[i][1].exec(s);
|
|
1556
|
+
if (!m) continue;
|
|
1557
|
+
var tpl = config["text_" + AIQ_PATTERNS[i][0]];
|
|
1558
|
+
if (!tpl) return null;
|
|
1559
|
+
var out = tpl;
|
|
1560
|
+
for (var j = 1; j < m.length; j++) {
|
|
1561
|
+
var cap = m[j], tr = /^[0-9]+$/.test(cap) ? cap : aiqCore(cap);
|
|
1562
|
+
if (tr == null) return null;
|
|
1563
|
+
out = out.split("{" + (j - 1) + "}").join(tr);
|
|
1564
|
+
}
|
|
1565
|
+
return out;
|
|
1566
|
+
}
|
|
1567
|
+
return null;
|
|
1568
|
+
}
|
|
1569
|
+
function aiqPiece(piece) {
|
|
1570
|
+
var body = piece, punct = "";
|
|
1571
|
+
var last = body.charAt(body.length - 1);
|
|
1572
|
+
if (last === "." || last === ":") { punct = last; body = body.substring(0, body.length - 1); }
|
|
1573
|
+
var v = aiqCore(body);
|
|
1574
|
+
if (v == null) return null;
|
|
1575
|
+
if (punct) {
|
|
1576
|
+
var end = v.charAt(v.length - 1);
|
|
1577
|
+
var closed = ".:!?".indexOf(end) >= 0 || end === String.fromCharCode(0x3002) || end === String.fromCharCode(0xff1a);
|
|
1578
|
+
if (!closed) v += aiqCjk() ? String.fromCharCode(punct === ":" ? 0xff1a : 0x3002) : punct;
|
|
1579
|
+
}
|
|
1580
|
+
return v;
|
|
1581
|
+
}
|
|
1582
|
+
function aiqText(text) {
|
|
1583
|
+
var t = String(text == null ? "" : text).replace(/^\s+|\s+$/g, "");
|
|
1584
|
+
if (!t || !/[A-Za-z]/.test(t)) return null;
|
|
1585
|
+
var whole = aiqPiece(t);
|
|
1586
|
+
if (whole != null) return whole;
|
|
1587
|
+
var dash = t.indexOf(" - ");
|
|
1588
|
+
if (dash > 0) {
|
|
1589
|
+
var a = aiqText(t.substring(0, dash)), b = aiqText(t.substring(dash + 3));
|
|
1590
|
+
if (a != null || b != null) return (a != null ? a : t.substring(0, dash)) + " - " + (b != null ? b : t.substring(dash + 3));
|
|
1591
|
+
}
|
|
1592
|
+
var pieces = t.match(/[^.:!?]+[.:!?]*/g);
|
|
1593
|
+
if (!pieces || pieces.length < 2) return null;
|
|
1594
|
+
var out = "", prevTranslated = false, changed = false;
|
|
1595
|
+
for (var i = 0; i < pieces.length; i++) {
|
|
1596
|
+
var p = pieces[i].replace(/^\s+|\s+$/g, "");
|
|
1597
|
+
if (!p) continue;
|
|
1598
|
+
var tr = aiqPiece(p);
|
|
1599
|
+
if (tr != null) changed = true;
|
|
1600
|
+
var local = tr != null || !/[A-Za-z]/.test(p);
|
|
1601
|
+
var cur = tr != null ? tr : p;
|
|
1602
|
+
out += !out ? cur : ((local && prevTranslated && aiqCjk()) ? "" : " ") + cur;
|
|
1603
|
+
prevTranslated = local;
|
|
1604
|
+
}
|
|
1605
|
+
return changed ? out : null;
|
|
1606
|
+
}
|
|
1607
|
+
var aiqCjkStop = null, aiqCjkGap = null;
|
|
1608
|
+
function aiqFinish(s) {
|
|
1609
|
+
if (s == null || !aiqCjk()) return s;
|
|
1610
|
+
if (!aiqCjkStop) {
|
|
1611
|
+
var cjk = String.fromCharCode(0x4e00) + "-" + String.fromCharCode(0x9fff) + String.fromCharCode(0x3040) + "-" + String.fromCharCode(0x30ff) + String.fromCharCode(0x201d) + String.fromCharCode(0x300d);
|
|
1612
|
+
aiqCjkStop = new RegExp("([" + cjk + "])\\.", "g");
|
|
1613
|
+
aiqCjkGap = new RegExp("([" + String.fromCharCode(0x3002) + String.fromCharCode(0xff1a) + "])\\s+(?=[^A-Za-z0-9\\s])", "g");
|
|
1614
|
+
}
|
|
1615
|
+
return s.replace(aiqCjkStop, "$1" + String.fromCharCode(0x3002)).replace(aiqCjkGap, "$1");
|
|
1616
|
+
}
|
|
1617
|
+
var AIQ_SKIP = ".rte-ai-inline-preview-compare-text, .rte-ai-chat-message-text, .rte-ai-chat-message-request-body, textarea, input, script, style, svg";
|
|
1618
|
+
function aiqLocalize(root) {
|
|
1619
|
+
if (!root || !aiqLocale()) return;
|
|
1620
|
+
var stack = [root];
|
|
1621
|
+
while (stack.length) {
|
|
1622
|
+
var el = stack.pop();
|
|
1623
|
+
if (el.nodeType !== 1) continue;
|
|
1624
|
+
if (el !== root && el.matches && el.matches(AIQ_SKIP)) continue;
|
|
1625
|
+
for (var a = 0; a < 3; a++) {
|
|
1626
|
+
var name = a === 0 ? "aria-label" : (a === 1 ? "title" : "placeholder");
|
|
1627
|
+
var val = el.getAttribute(name);
|
|
1628
|
+
if (val) { var tv = aiqFinish(aiqText(val) || val); if (tv !== val) el.setAttribute(name, tv); }
|
|
1629
|
+
}
|
|
1630
|
+
for (var c = el.firstChild; c; c = c.nextSibling) {
|
|
1631
|
+
if (c.nodeType === 1) stack.push(c);
|
|
1632
|
+
else if (c.nodeType === 3 && /[A-Za-z.]/.test(c.data)) {
|
|
1633
|
+
var tt = aiqFinish(aiqText(c.data) || c.data.replace(/^\s+|\s+$/g, ""));
|
|
1634
|
+
if (tt) {
|
|
1635
|
+
var lead = /^\s*/.exec(c.data)[0], trail = /\s*$/.exec(c.data)[0];
|
|
1636
|
+
if (lead + tt + trail !== c.data) c.data = lead + tt + trail;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
// keep a root localized as the toolkit rebuilds it (idempotent: a translated string has nothing left to match)
|
|
1643
|
+
function aiqWatch(root, select) {
|
|
1644
|
+
if (!root || !aiqLocale() || root.__aiqWatched) return;
|
|
1645
|
+
root.__aiqWatched = true;
|
|
1646
|
+
var run = function () {
|
|
1647
|
+
if (!select) { aiqLocalize(root); return; }
|
|
1648
|
+
var list = root.querySelectorAll(select);
|
|
1649
|
+
for (var i = 0; i < list.length; i++) aiqLocalize(list[i]);
|
|
1650
|
+
};
|
|
1651
|
+
run();
|
|
1652
|
+
var win = root.ownerDocument && root.ownerDocument.defaultView;
|
|
1653
|
+
var MO = win && win.MutationObserver;
|
|
1654
|
+
if (!MO) return;
|
|
1655
|
+
var pending = false;
|
|
1656
|
+
new MO(function () {
|
|
1657
|
+
if (pending) return;
|
|
1658
|
+
pending = true;
|
|
1659
|
+
win.setTimeout(function () { pending = false; run(); }, 0);
|
|
1660
|
+
}).observe(root, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["aria-label", "title", "placeholder"] });
|
|
1661
|
+
}
|
|
1662
|
+
function aiqWatchCards() {
|
|
1663
|
+
var editable = editor.getEditable ? editor.getEditable() : null;
|
|
1664
|
+
if (editable) aiqWatch(editable, ".rte-ai-inline-preview");
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1140
1667
|
function ensureSentence(text) {
|
|
1141
1668
|
var clean = normalizeText(text);
|
|
1142
1669
|
if (!clean) {
|
|
@@ -1157,6 +1684,24 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1157
1684
|
return normalizeText(language || "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
1158
1685
|
}
|
|
1159
1686
|
|
|
1687
|
+
// 2026-10-18 the Translate-to list names the built-in languages in the page's language (Intl.DisplayNames in the
|
|
1688
|
+
// pack's locale, text_uilocale) at the display site only; values and the language a request names are unchanged,
|
|
1689
|
+
// and an application's own label is kept. tools/i18n/ai-translate-languages.mjs
|
|
1690
|
+
var TRANSLATE_BUILTIN = { spanish: ["es", "Spanish"], french: ["fr", "French"], german: ["de", "German"], italian: ["it", "Italian"], portuguese: ["pt", "Portuguese"], japanese: ["ja", "Japanese"] };
|
|
1691
|
+
function getTranslateLanguageDisplay(entry) {
|
|
1692
|
+
var label = entry.label || entry.value;
|
|
1693
|
+
var builtIn = TRANSLATE_BUILTIN[normalizeLanguageValue(entry.value)];
|
|
1694
|
+
var locale = config && config.text_uilocale;
|
|
1695
|
+
if (!builtIn || label !== builtIn[1] || !locale || /^en(-|$)/i.test(locale)) return label;
|
|
1696
|
+
try {
|
|
1697
|
+
if (typeof Intl !== "undefined" && Intl.DisplayNames) {
|
|
1698
|
+
var n = new Intl.DisplayNames([locale], { type: "language" }).of(builtIn[0]);
|
|
1699
|
+
if (n && n !== builtIn[0]) return n;
|
|
1700
|
+
}
|
|
1701
|
+
} catch (e) { /* the English label */ }
|
|
1702
|
+
return label;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1160
1705
|
function getTranslateLanguageLabel(language) {
|
|
1161
1706
|
var normalized = normalizeLanguageValue(language);
|
|
1162
1707
|
var languages = getTranslateLanguages();
|
|
@@ -1187,27 +1732,27 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1187
1732
|
function getSuggestionTypeLabel(type) {
|
|
1188
1733
|
switch (getSuggestionTypeValue(type)) {
|
|
1189
1734
|
case "proofread":
|
|
1190
|
-
return "Proofread";
|
|
1735
|
+
return (obj.__aiT ? obj.__aiT("air_type_proofread", "Proofread") : "Proofread");
|
|
1191
1736
|
case "rewrite":
|
|
1192
|
-
return "Rewrite";
|
|
1737
|
+
return (obj.__aiT ? obj.__aiT("air_type_rewrite", "Rewrite") : "Rewrite");
|
|
1193
1738
|
case "translate":
|
|
1194
|
-
return "Translate";
|
|
1739
|
+
return (obj.__aiT ? obj.__aiT("air_type_translate", "Translate") : "Translate");
|
|
1195
1740
|
case "justify":
|
|
1196
|
-
return "Justify";
|
|
1741
|
+
return (obj.__aiT ? obj.__aiT("air_type_justify", "Justify") : "Justify");
|
|
1197
1742
|
case "summarize":
|
|
1198
|
-
return "Summarize";
|
|
1743
|
+
return (obj.__aiT ? obj.__aiT("air_type_summarize", "Summarize") : "Summarize");
|
|
1199
1744
|
case "shorten":
|
|
1200
|
-
return "Shorten";
|
|
1745
|
+
return (obj.__aiT ? obj.__aiT("air_type_shorten", "Shorten") : "Shorten");
|
|
1201
1746
|
case "expand":
|
|
1202
|
-
return "Expand";
|
|
1747
|
+
return (obj.__aiT ? obj.__aiT("air_type_expand", "Expand") : "Expand");
|
|
1203
1748
|
case "comment":
|
|
1204
|
-
return "Comment";
|
|
1749
|
+
return (obj.__aiT ? obj.__aiT("air_type_comment", "Comment") : "Comment");
|
|
1205
1750
|
case "paragraph":
|
|
1206
|
-
return "Paragraph";
|
|
1751
|
+
return (obj.__aiT ? obj.__aiT("air_type_paragraph", "Paragraph") : "Paragraph");
|
|
1207
1752
|
case "chat":
|
|
1208
|
-
return "Chat";
|
|
1753
|
+
return (obj.__aiT ? obj.__aiT("air_type_chat", "Chat") : "Chat");
|
|
1209
1754
|
default:
|
|
1210
|
-
return "Other";
|
|
1755
|
+
return (obj.__aiT ? obj.__aiT("air_type_other", "Other") : "Other");
|
|
1211
1756
|
}
|
|
1212
1757
|
}
|
|
1213
1758
|
|
|
@@ -1358,7 +1903,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1358
1903
|
if (options.redoNextLabel) {
|
|
1359
1904
|
parts.push(options.redoNextLabel);
|
|
1360
1905
|
}
|
|
1361
|
-
return parts
|
|
1906
|
+
return joinSentences(parts);
|
|
1362
1907
|
}
|
|
1363
1908
|
|
|
1364
1909
|
function buildSuggestionSourceLabel(type, snapshot, language, scope) {
|
|
@@ -1383,11 +1928,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1383
1928
|
var languageValue = getTranslateLanguageValue(language);
|
|
1384
1929
|
var languageLabel = getTranslateLanguageLabel(languageValue);
|
|
1385
1930
|
var leadMap = {
|
|
1386
|
-
spanish: "Version en
|
|
1387
|
-
french: "Version
|
|
1931
|
+
spanish: "Version en espa\xf1ol",
|
|
1932
|
+
french: "Version fran\xe7aise",
|
|
1388
1933
|
german: "Deutsche Fassung",
|
|
1389
1934
|
italian: "Versione italiana",
|
|
1390
|
-
portuguese: "
|
|
1935
|
+
portuguese: "Vers\xe3o em portugu\xeas",
|
|
1391
1936
|
japanese: "Japanese draft"
|
|
1392
1937
|
};
|
|
1393
1938
|
var lead = leadMap[languageValue] || (languageLabel + " version");
|
|
@@ -1402,20 +1947,29 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1402
1947
|
}
|
|
1403
1948
|
|
|
1404
1949
|
var words = clean.split(/\s+/);
|
|
1950
|
+
// 2026-10-18 an in-place edit ends like the selection it replaces: one that stops before the end of its
|
|
1951
|
+
// paragraph gains no closing stop, one that starts mid-sentence keeps its first letter. Proofreading "Hello world"
|
|
1952
|
+
// in "Hello world, this is a test." gave "Hello world., this is a test.". tools/ai/demo-proofread-fragment.mjs
|
|
1953
|
+
function finishEdit(text) {
|
|
1954
|
+
var s = normalizeText(text);
|
|
1955
|
+
if (!s) return s;
|
|
1956
|
+
if (!options.midStart) s = s.charAt(0).toUpperCase() + s.substring(1);
|
|
1957
|
+
if (!options.midEnd && !/[.!?]$/.test(s)) s += ".";
|
|
1958
|
+
return s;
|
|
1959
|
+
}
|
|
1405
1960
|
switch (mode) {
|
|
1406
1961
|
case "proofread":
|
|
1407
1962
|
var proofed = clean
|
|
1408
1963
|
.replace(/\s{2,}/g, " ")
|
|
1409
1964
|
.replace(/\s+([,.;:!?])/g, "$1")
|
|
1410
1965
|
.replace(/([,.;:!?])([^\s"'\)\]])/g, "$1 $2")
|
|
1411
|
-
.replace(/\.{2,}(?!\.)/g, ".")
|
|
1412
|
-
|
|
1413
|
-
return ensureSentence(proofed);
|
|
1966
|
+
.replace(/\.{2,}(?!\.)/g, ".");
|
|
1967
|
+
return finishEdit(proofed);
|
|
1414
1968
|
case "rewrite":
|
|
1415
|
-
return
|
|
1416
|
-
.replace(/\bvery\s
|
|
1417
|
-
.replace(/\breally\s
|
|
1418
|
-
.replace(/\bjust\s
|
|
1969
|
+
return finishEdit(clean
|
|
1970
|
+
.replace(/\bvery\b\s*/gi, "")
|
|
1971
|
+
.replace(/\breally\b\s*/gi, "")
|
|
1972
|
+
.replace(/\bjust\b\s*/gi, "")
|
|
1419
1973
|
.replace(/\bin order to\b/gi, "to")
|
|
1420
1974
|
.replace(/\bdue to the fact that\b/gi, "because")
|
|
1421
1975
|
.replace(/\s{2,}/g, " "));
|
|
@@ -1445,7 +1999,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1445
1999
|
if (!clean) {
|
|
1446
2000
|
return "Additional paragraph: This section adds a concise supporting explanation so the reader gets more context without losing momentum.";
|
|
1447
2001
|
}
|
|
1448
|
-
return ensureSentence("Additional paragraph: This section adds a concise supporting explanation that reinforces " + clean.split(/\s+/).slice(0, 10).join(" ") + " for the reader.");
|
|
2002
|
+
return ensureSentence("Additional paragraph: This section adds a concise supporting explanation that reinforces " + clean.split(/\s+/).slice(0, 10).join(" ").replace(/[.!?;:,]+$/, "") + " for the reader.");
|
|
1449
2003
|
}
|
|
1450
2004
|
|
|
1451
2005
|
function truncateText(text, maxLength) {
|
|
@@ -1457,7 +2011,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1457
2011
|
}
|
|
1458
2012
|
|
|
1459
2013
|
function buildChatAnswer(prompt, source) {
|
|
1460
|
-
|
|
2014
|
+
// 2026-10-18 the quoted text loses its own closing stop, or the reply read "this is a test.. Ask me" and
|
|
2015
|
+
// "test., I would". tools/ai/aria-double-period.mjs
|
|
2016
|
+
var focus = String(truncateText(source, 160) || "").replace(/[ \t\r\n.!?;:,]+$/, "") || "the current document";
|
|
1461
2017
|
if (/\b(headings?|titles?)\b/.test(prompt)) {
|
|
1462
2018
|
return "You could tighten the structure by using shorter headings that mirror the main claims in " + focus + ". Ask me to draft heading options and I can turn them into reviewable suggestions.";
|
|
1463
2019
|
}
|
|
@@ -1996,7 +2552,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
1996
2552
|
if (unavailableReason) {
|
|
1997
2553
|
parts.push(unavailableReason);
|
|
1998
2554
|
}
|
|
1999
|
-
button.setAttribute("aria-label", parts
|
|
2555
|
+
button.setAttribute("aria-label", joinSentences(parts));
|
|
2000
2556
|
button.title = detail
|
|
2001
2557
|
? (label || "Action") + " - " + detail + (unavailableReason ? " " + unavailableReason : "")
|
|
2002
2558
|
: (label || "Action") + (unavailableReason ? " - " + unavailableReason : "");
|
|
@@ -2011,13 +2567,34 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2011
2567
|
}, action, request);
|
|
2012
2568
|
}
|
|
2013
2569
|
|
|
2570
|
+
// Whether a selection starts mid-sentence (text before it in its block that does not end a sentence) and ends
|
|
2571
|
+
// before its block does (text after it). Only for a selection; a document-wide request is a whole text.
|
|
2572
|
+
function selectionSentenceContext(request) {
|
|
2573
|
+
var out = { midStart: false, midEnd: false };
|
|
2574
|
+
try {
|
|
2575
|
+
var snap = request && request.snapshot;
|
|
2576
|
+
var r = snap && snap.hasSelection && request.scope !== "document" ? snap.range : null;
|
|
2577
|
+
if (!r) return out;
|
|
2578
|
+
var doc = r.startContainer.ownerDocument;
|
|
2579
|
+
var blockOf = function (n) {
|
|
2580
|
+
var el = n && n.nodeType === 1 ? n : (n && n.parentNode);
|
|
2581
|
+
while (el && el.nodeType === 1 && !/^(P|LI|TD|TH|H[1-6]|DIV|BLOCKQUOTE|PRE|BODY)$/.test(el.nodeName)) el = el.parentNode;
|
|
2582
|
+
return el && el.nodeType === 1 ? el : null;
|
|
2583
|
+
};
|
|
2584
|
+
var b1 = blockOf(r.startContainer), b2 = blockOf(r.endContainer);
|
|
2585
|
+
if (b1) { var pre = doc.createRange(); pre.selectNodeContents(b1); pre.setEnd(r.startContainer, r.startOffset); var before = pre.toString(); out.midStart = /\S/.test(before) && !/[.!?]["'\u201d)\]]*\s*$/.test(before); }
|
|
2586
|
+
if (b2) { var post = doc.createRange(); post.selectNodeContents(b2); post.setStart(r.endContainer, r.endOffset); out.midEnd = /\S/.test(post.toString()); }
|
|
2587
|
+
} catch (e) { /* a whole text */ }
|
|
2588
|
+
return out;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2014
2591
|
function defaultResolveAction(request) {
|
|
2015
2592
|
switch (request.mode) {
|
|
2016
2593
|
case "proofread":
|
|
2017
2594
|
var proofReason = "Normalizes spacing, punctuation, and capitalization while keeping the original meaning intact.";
|
|
2018
2595
|
return {
|
|
2019
2596
|
operations: [
|
|
2020
|
-
buildLegacyOperation(request.action && request.action.target ? request.action.target : "selection-preview", buildDemoResult("proofread", request.source), { reason: proofReason }, request, request.action)
|
|
2597
|
+
buildLegacyOperation(request.action && request.action.target ? request.action.target : "selection-preview", buildDemoResult("proofread", request.source, selectionSentenceContext(request)), { reason: proofReason }, request, request.action)
|
|
2021
2598
|
],
|
|
2022
2599
|
reason: proofReason
|
|
2023
2600
|
};
|
|
@@ -2025,7 +2602,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2025
2602
|
var rewriteReason = "Tightens the wording by removing filler phrases so the point comes through faster.";
|
|
2026
2603
|
return {
|
|
2027
2604
|
operations: [
|
|
2028
|
-
buildLegacyOperation(request.action && request.action.target ? request.action.target : "selection-preview", buildDemoResult("rewrite", request.source), { reason: rewriteReason }, request, request.action)
|
|
2605
|
+
buildLegacyOperation(request.action && request.action.target ? request.action.target : "selection-preview", buildDemoResult("rewrite", request.source, selectionSentenceContext(request)), { reason: rewriteReason }, request, request.action)
|
|
2029
2606
|
],
|
|
2030
2607
|
reason: rewriteReason
|
|
2031
2608
|
};
|
|
@@ -2048,7 +2625,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2048
2625
|
operations: [
|
|
2049
2626
|
{
|
|
2050
2627
|
type: "preview-suggestion",
|
|
2051
|
-
text: buildDemoResult("rewrite", request.source),
|
|
2628
|
+
text: buildDemoResult("rewrite", request.source, selectionSentenceContext(request)),
|
|
2052
2629
|
reason: "This rewrite reduces filler language, clarifies the main point, and makes the sentence easier to review before accepting."
|
|
2053
2630
|
}
|
|
2054
2631
|
],
|
|
@@ -2534,6 +3111,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2534
3111
|
scope: suggestion.snapshot && suggestion.snapshot.hasSelection ? "selection" : "document",
|
|
2535
3112
|
text: suggestion.text || "",
|
|
2536
3113
|
blockPath: suggestion.blockPath || "",
|
|
3114
|
+
mentions: cleanLedgerMentions(suggestion.mentions),
|
|
3115
|
+
reactions: cleanLedgerReactions(suggestion.reactions),
|
|
2537
3116
|
replies: Array.isArray(suggestion.replies) ? suggestion.replies : []
|
|
2538
3117
|
};
|
|
2539
3118
|
}
|
|
@@ -2560,6 +3139,33 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2560
3139
|
}
|
|
2561
3140
|
return doc.body.innerHTML;
|
|
2562
3141
|
}
|
|
3142
|
+
// 2026-10-12 comment mentions: the { id, name } pairs a comment names, kept with the comment in the shared store.
|
|
3143
|
+
function cleanLedgerMentions(list) {
|
|
3144
|
+
var out = [];
|
|
3145
|
+
if (!Array.isArray(list)) return out;
|
|
3146
|
+
for (var i = 0; i < list.length; i++) {
|
|
3147
|
+
var m = list[i];
|
|
3148
|
+
if (m && m.id != null && m.name) out.push({ id: String(m.id), name: String(m.name) });
|
|
3149
|
+
}
|
|
3150
|
+
return out;
|
|
3151
|
+
}
|
|
3152
|
+
// 2026-10-15 comment reactions: [{ emoji, users: [id] }], kept with the comment like mentions.
|
|
3153
|
+
function cleanLedgerReactions(list) {
|
|
3154
|
+
var out = [];
|
|
3155
|
+
if (!Array.isArray(list)) return out;
|
|
3156
|
+
for (var i = 0; i < list.length && out.length < 20; i++) {
|
|
3157
|
+
var r = list[i];
|
|
3158
|
+
if (!r || typeof r.emoji !== "string" || !r.emoji || r.emoji.length > 8 || !Array.isArray(r.users)) continue;
|
|
3159
|
+
var users = [];
|
|
3160
|
+
for (var j = 0; j < r.users.length; j++) {
|
|
3161
|
+
var u = r.users[j] == null ? "" : String(r.users[j]);
|
|
3162
|
+
if (u && users.indexOf(u) < 0) users.push(u);
|
|
3163
|
+
}
|
|
3164
|
+
if (users.length) out.push({ emoji: r.emoji, users: users });
|
|
3165
|
+
}
|
|
3166
|
+
return out;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
2563
3169
|
|
|
2564
3170
|
function normalizeLedgerEntry(raw) {
|
|
2565
3171
|
if (!raw || !raw.id) return null;
|
|
@@ -2592,6 +3198,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2592
3198
|
sourceLabel: raw.sourceLabel || "",
|
|
2593
3199
|
snapshot: raw.snapshot || { hasSelection: raw.scope === "selection" },
|
|
2594
3200
|
text: raw.text || "",
|
|
3201
|
+
mentions: cleanLedgerMentions(raw.mentions),
|
|
3202
|
+
reactions: cleanLedgerReactions(raw.reactions),
|
|
2595
3203
|
blockPath: raw.blockPath || "",
|
|
2596
3204
|
replies: Array.isArray(raw.replies) ? raw.replies : []
|
|
2597
3205
|
};
|
|
@@ -2634,6 +3242,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2634
3242
|
hasSelection: raw.scope === "selection"
|
|
2635
3243
|
},
|
|
2636
3244
|
text: raw.text || "",
|
|
3245
|
+
mentions: cleanLedgerMentions(raw.mentions),
|
|
3246
|
+
reactions: cleanLedgerReactions(raw.reactions),
|
|
2637
3247
|
blockPath: raw.blockPath || "",
|
|
2638
3248
|
replies: Array.isArray(raw.replies) ? raw.replies : []
|
|
2639
3249
|
};
|
|
@@ -2689,6 +3299,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
2689
3299
|
createdAt: suggestion.createdAt || 0,
|
|
2690
3300
|
decidedAt: suggestion.decidedAt || 0,
|
|
2691
3301
|
sourceLabel: suggestion.sourceLabel || "",
|
|
3302
|
+
mentions: cleanLedgerMentions(suggestion.mentions),
|
|
3303
|
+
reactions: cleanLedgerReactions(suggestion.reactions),
|
|
2692
3304
|
snapshot: {
|
|
2693
3305
|
hasSelection: !!(suggestion.snapshot && suggestion.snapshot.hasSelection)
|
|
2694
3306
|
}
|
|
@@ -4942,13 +5554,13 @@ function RTE_Plugin_AIToolkit() {
|
|
|
4942
5554
|
var bodyNode = ownerDocument.createElement("span");
|
|
4943
5555
|
bodyNode.className = "rte-ai-inline-preview-shortcuts-body";
|
|
4944
5556
|
shortcutsChip.appendChild(bodyNode);
|
|
4945
|
-
var pieces = String(text || "").split(/(\s
|
|
5557
|
+
var pieces = String(text || "").split(/(\s+\xb7\s+|\s+-\s+)/);
|
|
4946
5558
|
for (var pieceIndex = 0; pieceIndex < pieces.length; pieceIndex++) {
|
|
4947
5559
|
var piece = pieces[pieceIndex];
|
|
4948
5560
|
if (!piece) {
|
|
4949
5561
|
continue;
|
|
4950
5562
|
}
|
|
4951
|
-
if (/^\s+[
|
|
5563
|
+
if (/^\s+[\xb7-]\s+$/.test(piece)) {
|
|
4952
5564
|
var separator = ownerDocument.createElement("span");
|
|
4953
5565
|
separator.className = "rte-ai-inline-preview-shortcuts-separator";
|
|
4954
5566
|
separator.innerText = piece.replace(/^\s+|\s+$/g, "");
|
|
@@ -5141,13 +5753,13 @@ function RTE_Plugin_AIToolkit() {
|
|
|
5141
5753
|
function getSuggestionStatusLabel(status) {
|
|
5142
5754
|
switch (status) {
|
|
5143
5755
|
case "accepted":
|
|
5144
|
-
return "Accepted";
|
|
5756
|
+
return (obj.__aiT ? obj.__aiT("air_status_accepted", "Accepted") : "Accepted");
|
|
5145
5757
|
case "rejected":
|
|
5146
|
-
return "Rejected";
|
|
5758
|
+
return (obj.__aiT ? obj.__aiT("air_status_rejected", "Rejected") : "Rejected");
|
|
5147
5759
|
case "stale":
|
|
5148
|
-
return "Needs refresh";
|
|
5760
|
+
return (obj.__aiT ? obj.__aiT("air_status_stale", "Needs refresh") : "Needs refresh");
|
|
5149
5761
|
default:
|
|
5150
|
-
return "Pending";
|
|
5762
|
+
return (obj.__aiT ? obj.__aiT("air_status_pending", "Pending") : "Pending");
|
|
5151
5763
|
}
|
|
5152
5764
|
}
|
|
5153
5765
|
|
|
@@ -5407,7 +6019,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
5407
6019
|
if (summaryData.detail) {
|
|
5408
6020
|
parts.push(summaryData.detail);
|
|
5409
6021
|
}
|
|
5410
|
-
return parts
|
|
6022
|
+
return joinSentences(parts);
|
|
5411
6023
|
}
|
|
5412
6024
|
|
|
5413
6025
|
function createInlinePreviewActionSummaryHtml(previewState) {
|
|
@@ -5566,10 +6178,10 @@ function RTE_Plugin_AIToolkit() {
|
|
|
5566
6178
|
}
|
|
5567
6179
|
var ariaLabel = label ? (label + " actions") : "AI suggestion actions";
|
|
5568
6180
|
if (detailText) {
|
|
5569
|
-
ariaLabel
|
|
6181
|
+
ariaLabel = joinSentences([ariaLabel, detailText]);
|
|
5570
6182
|
}
|
|
5571
6183
|
if (isCurrentGroup && badgeText) {
|
|
5572
|
-
ariaLabel
|
|
6184
|
+
ariaLabel = joinSentences([ariaLabel, "Current lane", badgeText]);
|
|
5573
6185
|
}
|
|
5574
6186
|
groupNode.setAttribute("aria-label", ariaLabel);
|
|
5575
6187
|
}
|
|
@@ -5618,7 +6230,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
5618
6230
|
button.setAttribute("aria-keyshortcuts", shortcutKey ? ("Enter Space " + shortcutKey) : "Enter Space");
|
|
5619
6231
|
button.setAttribute("data-rte-ai-enter-target-label", "Enter");
|
|
5620
6232
|
if (ariaLabel && !/current enter target/i.test(ariaLabel)) {
|
|
5621
|
-
button.setAttribute("aria-label", ariaLabel
|
|
6233
|
+
button.setAttribute("aria-label", joinSentences([ariaLabel, "Current Enter target."]));
|
|
5622
6234
|
}
|
|
5623
6235
|
if (title && !/enter or space activates this action/i.test(title)) {
|
|
5624
6236
|
title += ". Enter or Space activates this action";
|
|
@@ -5627,7 +6239,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
5627
6239
|
}
|
|
5628
6240
|
}
|
|
5629
6241
|
if (title && !/current enter target/i.test(title)) {
|
|
5630
|
-
button.title = title
|
|
6242
|
+
button.title = joinSentences([title, "Current Enter target."]);
|
|
5631
6243
|
}
|
|
5632
6244
|
}
|
|
5633
6245
|
else {
|
|
@@ -6161,7 +6773,32 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6161
6773
|
+ "</" + resolvedTag + ">";
|
|
6162
6774
|
}
|
|
6163
6775
|
|
|
6776
|
+
// 2026-10-18 The card is inserted into the editing document - an iframe by default - but the toolkit's stylesheet
|
|
6777
|
+
// was linked into the host page only, so every card rendered as unstyled run-on text. The host page's copy is
|
|
6778
|
+
// linked into the editing document once (never into the saved HTML). tools/ai/inline-card-styled.mjs
|
|
6779
|
+
function ensureCardStylesheet() {
|
|
6780
|
+
try {
|
|
6781
|
+
var editable = editor.getEditable ? editor.getEditable() : null;
|
|
6782
|
+
var doc = editable && editable.ownerDocument;
|
|
6783
|
+
if (!doc || doc === document || doc.querySelector("link[data-rte-ai-card-css]")) return;
|
|
6784
|
+
var src = document.querySelector("link[data-rte-auto-injected=aitoolkit]");
|
|
6785
|
+
if (!src) {
|
|
6786
|
+
var all = document.querySelectorAll("link[rel=stylesheet]");
|
|
6787
|
+
for (var i = 0; i < all.length; i++) {
|
|
6788
|
+
if (/(?:^|\/)aitoolkit(?:\.min)?\.css(?:[?#].*)?$/i.test(all[i].getAttribute("href") || "")) { src = all[i]; break; }
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
if (!src || !src.href) return;
|
|
6792
|
+
var link = doc.createElement("link");
|
|
6793
|
+
link.rel = "stylesheet";
|
|
6794
|
+
link.href = src.href;
|
|
6795
|
+
link.setAttribute("data-rte-ai-card-css", "1");
|
|
6796
|
+
(doc.head || doc.documentElement).appendChild(link);
|
|
6797
|
+
} catch (e) { /* the card stays unstyled, as before */ }
|
|
6798
|
+
}
|
|
6799
|
+
|
|
6164
6800
|
function createInlinePreviewHtml(previewState) {
|
|
6801
|
+
ensureCardStylesheet();
|
|
6165
6802
|
var isBlock = !!previewState.isBlock;
|
|
6166
6803
|
var wrapperTag = isBlock ? "div" : "span";
|
|
6167
6804
|
var partTag = isBlock ? "div" : "span";
|
|
@@ -6239,6 +6876,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6239
6876
|
editor.__aiBindInlinePreviewEventHandlers();
|
|
6240
6877
|
}
|
|
6241
6878
|
editor.insertHTML(createInlinePreviewHtml(previewState));
|
|
6879
|
+
aiqWatchCards();
|
|
6242
6880
|
persistSuggestionStore();
|
|
6243
6881
|
emitReviewLogEvent("suggestion-created", previewState);
|
|
6244
6882
|
if (editor.__aiReviewPanel && editor.__aiReviewPanel.isConnected) {
|
|
@@ -6326,8 +6964,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6326
6964
|
var searchInput = panel.ownerDocument.createElement("input");
|
|
6327
6965
|
searchInput.type = "text";
|
|
6328
6966
|
searchInput.className = "rte-ai-menu-search-input";
|
|
6329
|
-
searchInput.placeholder = "Filter actions
|
|
6330
|
-
searchInput.setAttribute("aria-label", "Filter AI actions");
|
|
6967
|
+
searchInput.placeholder = (obj.__aiT ? obj.__aiT("aia_filter", "Filter actions\u2026") : "Filter actions\u2026");
|
|
6968
|
+
searchInput.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aia_filteraria", "Filter AI actions") : "Filter AI actions"));
|
|
6331
6969
|
searchInput.setAttribute("autocomplete", "off");
|
|
6332
6970
|
searchInput.setAttribute("spellcheck", "false");
|
|
6333
6971
|
searchRow.appendChild(searchInput);
|
|
@@ -6387,7 +7025,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6387
7025
|
// 26px row floor and 2-col grid measure identically.
|
|
6388
7026
|
if (action.description) {
|
|
6389
7027
|
button.title = action.description;
|
|
6390
|
-
button.setAttribute("aria-label", (action.title || action.id) + "
|
|
7028
|
+
button.setAttribute("aria-label", (action.title || action.id) + " \u2014 " + action.description);
|
|
6391
7029
|
}
|
|
6392
7030
|
var icon = append(button, "span", "", "rte-ai-menu-item-icon");
|
|
6393
7031
|
icon.innerHTML = getActionIconSvg(action);
|
|
@@ -6455,7 +7093,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6455
7093
|
if (!searchRow.__aiEmptyHint) {
|
|
6456
7094
|
var hint = panel.ownerDocument.createElement("div");
|
|
6457
7095
|
hint.className = "rte-ai-menu-empty-hint";
|
|
6458
|
-
hint.textContent = "No actions match.";
|
|
7096
|
+
hint.textContent = (obj.__aiT ? obj.__aiT("aia_nomatch", "No actions match.") : "No actions match.");
|
|
6459
7097
|
panel.appendChild(hint);
|
|
6460
7098
|
searchRow.__aiEmptyHint = hint;
|
|
6461
7099
|
}
|
|
@@ -6644,6 +7282,19 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6644
7282
|
});
|
|
6645
7283
|
}
|
|
6646
7284
|
|
|
7285
|
+
// 2026-10-17 a locked document stays locked: in read-only mode (Ask AI opened through the API) and in comments-only
|
|
7286
|
+
// mode (its button stays enabled there) "Replace document", applyResult() and executeResolvedAction() rewrote the
|
|
7287
|
+
// document. Text-changing operations are refused and the refusal is announced; opening a panel and adding a
|
|
7288
|
+
// comment still work. tools/ai/ai-locked.mjs
|
|
7289
|
+
function documentLocked() {
|
|
7290
|
+
try { return !!((editor.getReadOnly && editor.getReadOnly()) || (editor.getCommentsOnly && editor.getCommentsOnly())); } catch (e) { return false; }
|
|
7291
|
+
}
|
|
7292
|
+
|
|
7293
|
+
function refuseLocked() {
|
|
7294
|
+
announceReviewStatus(config.text_aitoolkitlocked || "This document is read-only, so the AI result was not applied.");
|
|
7295
|
+
return false;
|
|
7296
|
+
}
|
|
7297
|
+
|
|
6647
7298
|
function executeOperations(operations, options) {
|
|
6648
7299
|
options = options || {};
|
|
6649
7300
|
var snapshot = options.snapshot || captureSelectionSnapshot();
|
|
@@ -6680,6 +7331,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6680
7331
|
});
|
|
6681
7332
|
}
|
|
6682
7333
|
|
|
7334
|
+
if (documentLocked() && !/^open-|comment/i.test(String(operation.type))) return refuseLocked();
|
|
6683
7335
|
switch (operation.type) {
|
|
6684
7336
|
case "open-chat-panel":
|
|
6685
7337
|
openChatPanel({ focusComposer: true });
|
|
@@ -6891,6 +7543,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6891
7543
|
}
|
|
6892
7544
|
|
|
6893
7545
|
function applyResult(result, options) {
|
|
7546
|
+
if (documentLocked()) return refuseLocked();
|
|
6894
7547
|
var clean = normalizeText(result);
|
|
6895
7548
|
if (!clean) {
|
|
6896
7549
|
return false;
|
|
@@ -6903,7 +7556,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
6903
7556
|
var snapshot = options.snapshot || captureSelectionSnapshot();
|
|
6904
7557
|
|
|
6905
7558
|
if (mode === "document") {
|
|
6906
|
-
|
|
7559
|
+
// 2026-10-03 undoable: an AI rewrite of the whole document is one Ctrl+Z back to exactly
|
|
7560
|
+
// what was there, edits made just before included (tools/import/replace-undo-plugins.mjs)
|
|
7561
|
+
editor.setHTMLCode(textToHtml(clean), { undoable: true });
|
|
6907
7562
|
syncSuggestionStates();
|
|
6908
7563
|
persistSuggestionStore();
|
|
6909
7564
|
if (editor.__aiReviewPanel && editor.__aiReviewPanel.isConnected) {
|
|
@@ -7116,7 +7771,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
7116
7771
|
if (options.redoNextLabel) {
|
|
7117
7772
|
parts.push(options.redoNextLabel);
|
|
7118
7773
|
}
|
|
7119
|
-
return parts
|
|
7774
|
+
return joinSentences(parts);
|
|
7120
7775
|
}
|
|
7121
7776
|
|
|
7122
7777
|
function buildReviewQueuePositionData(visibleSuggestions, activeSuggestionId, suggestion) {
|
|
@@ -7897,7 +8552,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
7897
8552
|
parts.push(options.queueTransitionData.followupDetail);
|
|
7898
8553
|
}
|
|
7899
8554
|
}
|
|
7900
|
-
return parts
|
|
8555
|
+
return joinSentences(parts);
|
|
7901
8556
|
}
|
|
7902
8557
|
|
|
7903
8558
|
function updateReviewCondensedPreviewAria(item, suggestion, panel) {
|
|
@@ -8222,7 +8877,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
8222
8877
|
focusActionLabel: getReviewFocusActionDisplayLabel(findSuggestionById(nextOverallPendingId), preferredQueueFocusAction)
|
|
8223
8878
|
} : null,
|
|
8224
8879
|
undoAction,
|
|
8225
|
-
{ id: "all-types", label: "All types" },
|
|
8880
|
+
{ id: "all-types", label: (obj.__aiT ? obj.__aiT("air_alltypes", "All types") : "All types") },
|
|
8226
8881
|
hasResolved && !showResolved ? { id: "show-resolved", label: "Show resolved" } : null,
|
|
8227
8882
|
{ id: "open-chat", label: "AI Chat" }
|
|
8228
8883
|
])
|
|
@@ -8250,7 +8905,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
8250
8905
|
announceMessage: "",
|
|
8251
8906
|
actions: [
|
|
8252
8907
|
undoAction,
|
|
8253
|
-
typeFilter !== "all" ? { id: "all-types", label: "All types" } : null,
|
|
8908
|
+
typeFilter !== "all" ? { id: "all-types", label: (obj.__aiT ? obj.__aiT("air_alltypes", "All types") : "All types") } : null,
|
|
8254
8909
|
hasResolved && !showResolved ? { id: "show-resolved", label: "Show resolved" } : null,
|
|
8255
8910
|
{ id: "open-chat", label: "AI Chat" }
|
|
8256
8911
|
]
|
|
@@ -9416,7 +10071,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
9416
10071
|
if (contextCardData.copy) {
|
|
9417
10072
|
parts.push(contextCardData.copy);
|
|
9418
10073
|
}
|
|
9419
|
-
return parts
|
|
10074
|
+
return joinSentences(parts);
|
|
9420
10075
|
}
|
|
9421
10076
|
|
|
9422
10077
|
function getChatMessageStateTitle(message) {
|
|
@@ -9629,7 +10284,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
9629
10284
|
if (isRecommended) {
|
|
9630
10285
|
parts.push("Recommended next step.");
|
|
9631
10286
|
}
|
|
9632
|
-
button.setAttribute("aria-label", parts
|
|
10287
|
+
button.setAttribute("aria-label", joinSentences(parts));
|
|
9633
10288
|
button.title = detail
|
|
9634
10289
|
? label + " - " + detail + (isRecommended ? " Recommended next step." : "")
|
|
9635
10290
|
: label + (isRecommended ? " - Recommended next step." : "");
|
|
@@ -9864,7 +10519,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
9864
10519
|
if (actionLabels.length) {
|
|
9865
10520
|
parts.push("Actions: " + actionLabels.join(", "));
|
|
9866
10521
|
}
|
|
9867
|
-
return parts
|
|
10522
|
+
return joinSentences(parts);
|
|
9868
10523
|
}
|
|
9869
10524
|
|
|
9870
10525
|
function syncChatActionAccessibility(actionsNode, recommendedActionId, message, currentSnapshot) {
|
|
@@ -9874,8 +10529,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
9874
10529
|
var recommendedLabel = getChatRecommendedActionLabel(recommendedActionId);
|
|
9875
10530
|
actionsNode.setAttribute("role", "group");
|
|
9876
10531
|
actionsNode.setAttribute("aria-label", recommendedLabel
|
|
9877
|
-
? "AI chat actions. Recommended next step: " + recommendedLabel + "."
|
|
9878
|
-
: "AI chat actions");
|
|
10532
|
+
? (obj.__aiT ? obj.__aiT("aic_actionsrec", "AI chat actions. Recommended next step: {0}.", recommendedLabel) : "AI chat actions. Recommended next step: " + recommendedLabel + ".")
|
|
10533
|
+
: (obj.__aiT ? obj.__aiT("aic_actions", "AI chat actions") : "AI chat actions"));
|
|
9879
10534
|
var groupNodes = actionsNode.querySelectorAll(".rte-ai-chat-message-action-group");
|
|
9880
10535
|
for (var groupIndex = 0; groupIndex < groupNodes.length; groupIndex++) {
|
|
9881
10536
|
var groupNode = groupNodes[groupIndex];
|
|
@@ -10001,7 +10656,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10001
10656
|
if (planItem.detail) {
|
|
10002
10657
|
parts.push(planItem.detail);
|
|
10003
10658
|
}
|
|
10004
|
-
return parts
|
|
10659
|
+
return joinSentences(parts);
|
|
10005
10660
|
}
|
|
10006
10661
|
|
|
10007
10662
|
function buildChatPreviewPlanItems(message, recommendedActionId, currentSnapshot) {
|
|
@@ -10128,7 +10783,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10128
10783
|
parts.push("+" + resolvedPreview.hiddenPlanCount + " more");
|
|
10129
10784
|
}
|
|
10130
10785
|
}
|
|
10131
|
-
return parts
|
|
10786
|
+
return joinSentences(parts);
|
|
10132
10787
|
}
|
|
10133
10788
|
|
|
10134
10789
|
function summarizeChatPromptCopy(text) {
|
|
@@ -10227,7 +10882,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10227
10882
|
if (message.text) {
|
|
10228
10883
|
parts.push(message.text);
|
|
10229
10884
|
}
|
|
10230
|
-
return parts
|
|
10885
|
+
return joinSentences(parts);
|
|
10231
10886
|
}
|
|
10232
10887
|
|
|
10233
10888
|
function buildChatAssistantRequestCardAriaLabel(requestCardData, exchangeLabel) {
|
|
@@ -10250,7 +10905,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10250
10905
|
if (requestCardData.promptText) {
|
|
10251
10906
|
parts.push(requestCardData.promptText);
|
|
10252
10907
|
}
|
|
10253
|
-
return parts
|
|
10908
|
+
return joinSentences(parts);
|
|
10254
10909
|
}
|
|
10255
10910
|
|
|
10256
10911
|
function buildChatMessageAriaLabel(messages, messageIndex, currentSnapshot) {
|
|
@@ -10322,7 +10977,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10322
10977
|
parts.push(actionSummary.title);
|
|
10323
10978
|
}
|
|
10324
10979
|
}
|
|
10325
|
-
return parts
|
|
10980
|
+
return joinSentences(parts);
|
|
10326
10981
|
}
|
|
10327
10982
|
|
|
10328
10983
|
function buildChatMessageStateCardAriaLabel(message) {
|
|
@@ -10342,7 +10997,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10342
10997
|
if (stateDetail) {
|
|
10343
10998
|
parts.push(stateDetail);
|
|
10344
10999
|
}
|
|
10345
|
-
return parts
|
|
11000
|
+
return joinSentences(parts);
|
|
10346
11001
|
}
|
|
10347
11002
|
|
|
10348
11003
|
function getChatMessageThreadRole(messages, messageIndex) {
|
|
@@ -10496,7 +11151,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10496
11151
|
if (bannerData.detail) {
|
|
10497
11152
|
parts.push(bannerData.detail);
|
|
10498
11153
|
}
|
|
10499
|
-
return parts
|
|
11154
|
+
return joinSentences(parts);
|
|
10500
11155
|
}
|
|
10501
11156
|
|
|
10502
11157
|
function buildChatExchangeGroupAriaLabel(messages, requestIndex, currentSnapshot) {
|
|
@@ -10552,7 +11207,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10552
11207
|
if (bannerData && bannerData.isCurrent) {
|
|
10553
11208
|
parts.push("Current exchange");
|
|
10554
11209
|
}
|
|
10555
|
-
return parts
|
|
11210
|
+
return joinSentences(parts);
|
|
10556
11211
|
}
|
|
10557
11212
|
|
|
10558
11213
|
function buildChatExchangeOutcomeData(messages, messageIndex, currentSnapshot) {
|
|
@@ -10636,7 +11291,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10636
11291
|
if (outcomeData.detail) {
|
|
10637
11292
|
parts.push(outcomeData.detail);
|
|
10638
11293
|
}
|
|
10639
|
-
return parts
|
|
11294
|
+
return joinSentences(parts);
|
|
10640
11295
|
}
|
|
10641
11296
|
|
|
10642
11297
|
function getChatContextWordCount(text) {
|
|
@@ -10779,46 +11434,51 @@ function RTE_Plugin_AIToolkit() {
|
|
|
10779
11434
|
}
|
|
10780
11435
|
}
|
|
10781
11436
|
}
|
|
10782
|
-
return parts
|
|
11437
|
+
return joinSentences(parts);
|
|
10783
11438
|
}
|
|
10784
11439
|
|
|
10785
11440
|
function buildChatSendActionData(state, context, activePrompt) {
|
|
10786
11441
|
var scopeLabel = context && context.scope === "selection" ? "selection" : "document";
|
|
11442
|
+
// 2026-10-18 every sentence reads a config.text_ key per scope (word order and gender differ by language);
|
|
11443
|
+
// the English fallbacks are the sentences this built before. tools/i18n/ai-chat-language.mjs
|
|
11444
|
+
var sc = scopeLabel === "selection" ? "sel" : "doc";
|
|
11445
|
+
var L = function (key, en, arg) { var s = (obj.__aiT ? obj.__aiT(key, en) : en); return arg === undefined ? s : s.split("{0}").join(String(arg)); };
|
|
11446
|
+
var LS = function (key, enSel, enDoc, arg) { return L(key + "_" + sc, sc === "sel" ? enSel : enDoc, arg); };
|
|
10787
11447
|
var hasCustomDraft = !!normalizeText(state && state.draft ? state.draft : "");
|
|
10788
11448
|
if (state && state.busy) {
|
|
10789
11449
|
return {
|
|
10790
|
-
text: "Thinking...",
|
|
11450
|
+
text: L("aic_thinking", "Thinking..."),
|
|
10791
11451
|
disabled: true,
|
|
10792
|
-
ariaLabel: "Send disabled while AI Chat is thinking about the current "
|
|
10793
|
-
title: "AI Chat is thinking about the current "
|
|
11452
|
+
ariaLabel: LS("aic_busyaria", "Send disabled while AI Chat is thinking about the current selection.", "Send disabled while AI Chat is thinking about the current document."),
|
|
11453
|
+
title: LS("aic_busytitle", "AI Chat is thinking about the current selection.", "AI Chat is thinking about the current document.")
|
|
10794
11454
|
};
|
|
10795
11455
|
}
|
|
10796
11456
|
if (activePrompt && activePrompt.label) {
|
|
10797
11457
|
return {
|
|
10798
|
-
text: "Send starter",
|
|
11458
|
+
text: L("aic_sendstarter", "Send starter"),
|
|
10799
11459
|
disabled: false,
|
|
10800
|
-
ariaLabel: "Send starter. Send the
|
|
10801
|
-
title: "Send the
|
|
11460
|
+
ariaLabel: LS("aic_starteraria", "Send starter. Send the {0} starter for the current selection.", "Send starter. Send the {0} starter for the current document.", activePrompt.label),
|
|
11461
|
+
title: LS("aic_startertitle", "Send the {0} starter for the current selection.", "Send the {0} starter for the current document.", activePrompt.label)
|
|
10802
11462
|
};
|
|
10803
11463
|
}
|
|
10804
11464
|
if (hasCustomDraft) {
|
|
10805
11465
|
return {
|
|
10806
|
-
text: "Send prompt",
|
|
11466
|
+
text: L("aic_sendprompt", "Send prompt"),
|
|
10807
11467
|
disabled: false,
|
|
10808
|
-
ariaLabel: "Send prompt. Send the custom prompt for the current "
|
|
10809
|
-
title: "Send the custom prompt for the current "
|
|
11468
|
+
ariaLabel: LS("aic_promptaria", "Send prompt. Send the custom prompt for the current selection.", "Send prompt. Send the custom prompt for the current document."),
|
|
11469
|
+
title: LS("aic_prompttitle", "Send the custom prompt for the current selection.", "Send the custom prompt for the current document.")
|
|
10810
11470
|
};
|
|
10811
11471
|
}
|
|
10812
11472
|
return {
|
|
10813
|
-
text: "Send",
|
|
11473
|
+
text: L("aic_send", "Send"),
|
|
10814
11474
|
disabled: true,
|
|
10815
|
-
ariaLabel: "Send disabled. Type a prompt or load a starter for the current "
|
|
10816
|
-
title: "Type a prompt or load a starter for the current "
|
|
11475
|
+
ariaLabel: LS("aic_emptyaria", "Send disabled. Type a prompt or load a starter for the current selection first.", "Send disabled. Type a prompt or load a starter for the current document first."),
|
|
11476
|
+
title: LS("aic_emptytitle", "Type a prompt or load a starter for the current selection first.", "Type a prompt or load a starter for the current document first.")
|
|
10817
11477
|
};
|
|
10818
11478
|
}
|
|
10819
11479
|
|
|
10820
11480
|
function buildChatComposerActionsAriaLabel(sendActionData) {
|
|
10821
|
-
return "Compose actions. Open Ask AI. " + (sendActionData && sendActionData.ariaLabel ? sendActionData.ariaLabel : "Send.");
|
|
11481
|
+
return (obj.__aiT ? obj.__aiT("aic_composeactions", "Compose actions. Open Ask AI.") : "Compose actions. Open Ask AI.") + " " + (sendActionData && sendActionData.ariaLabel ? sendActionData.ariaLabel : (obj.__aiT ? obj.__aiT("aic_sendp", "Send.") : "Send."));
|
|
10822
11482
|
}
|
|
10823
11483
|
|
|
10824
11484
|
function getLatestChatMessage(state) {
|
|
@@ -11032,7 +11692,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11032
11692
|
parts.push(summaryData.changeGlance.detail);
|
|
11033
11693
|
}
|
|
11034
11694
|
}
|
|
11035
|
-
return parts
|
|
11695
|
+
return joinSentences(parts);
|
|
11036
11696
|
}
|
|
11037
11697
|
|
|
11038
11698
|
function buildChatFeedIntroData(state, context, activePrompt) {
|
|
@@ -11104,7 +11764,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11104
11764
|
if (feedIntroData.detail) {
|
|
11105
11765
|
parts.push(feedIntroData.detail);
|
|
11106
11766
|
}
|
|
11107
|
-
return parts
|
|
11767
|
+
return joinSentences(parts);
|
|
11108
11768
|
}
|
|
11109
11769
|
|
|
11110
11770
|
function buildChatFeedSectionAriaLabel(state, context, activePrompt) {
|
|
@@ -11133,7 +11793,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11133
11793
|
if (summaryData && summaryData.detail) {
|
|
11134
11794
|
parts.push(summaryData.detail);
|
|
11135
11795
|
}
|
|
11136
|
-
return parts
|
|
11796
|
+
return joinSentences(parts);
|
|
11137
11797
|
}
|
|
11138
11798
|
var feedIntroData = buildChatFeedIntroData(state, context, activePrompt);
|
|
11139
11799
|
if (feedIntroData.title) {
|
|
@@ -11149,7 +11809,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11149
11809
|
if (feedIntroData.detail) {
|
|
11150
11810
|
parts.push(feedIntroData.detail);
|
|
11151
11811
|
}
|
|
11152
|
-
return parts
|
|
11812
|
+
return joinSentences(parts);
|
|
11153
11813
|
}
|
|
11154
11814
|
|
|
11155
11815
|
function getChatConversationSummaryTitle(state, context) {
|
|
@@ -11256,7 +11916,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11256
11916
|
parts.push(titleText);
|
|
11257
11917
|
}
|
|
11258
11918
|
if (!headerData) {
|
|
11259
|
-
return parts
|
|
11919
|
+
return joinSentences(parts);
|
|
11260
11920
|
}
|
|
11261
11921
|
if (headerData.statusLabel) {
|
|
11262
11922
|
parts.push(headerData.statusLabel);
|
|
@@ -11274,7 +11934,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11274
11934
|
}
|
|
11275
11935
|
}
|
|
11276
11936
|
}
|
|
11277
|
-
return parts
|
|
11937
|
+
return joinSentences(parts);
|
|
11278
11938
|
}
|
|
11279
11939
|
|
|
11280
11940
|
function buildChatContextData(context) {
|
|
@@ -11354,7 +12014,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11354
12014
|
if (contextData.previewSummary) {
|
|
11355
12015
|
parts.push(contextData.previewSummary);
|
|
11356
12016
|
}
|
|
11357
|
-
return parts
|
|
12017
|
+
return joinSentences(parts);
|
|
11358
12018
|
}
|
|
11359
12019
|
|
|
11360
12020
|
function buildChatContextBarAriaLabel(contextData) {
|
|
@@ -11369,7 +12029,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11369
12029
|
}
|
|
11370
12030
|
}
|
|
11371
12031
|
}
|
|
11372
|
-
return parts
|
|
12032
|
+
return joinSentences(parts);
|
|
11373
12033
|
}
|
|
11374
12034
|
|
|
11375
12035
|
function buildChatContextScopeButtonAriaLabel(scopeId, contextData) {
|
|
@@ -11405,7 +12065,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11405
12065
|
if (contextData.previewSummary) {
|
|
11406
12066
|
parts.push(contextData.previewSummary);
|
|
11407
12067
|
}
|
|
11408
|
-
return parts
|
|
12068
|
+
return joinSentences(parts);
|
|
11409
12069
|
}
|
|
11410
12070
|
|
|
11411
12071
|
function buildChatQuickStartData(state, prompts, activePrompt, context) {
|
|
@@ -11458,7 +12118,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11458
12118
|
if (quickStartData.detail) {
|
|
11459
12119
|
parts.push(quickStartData.detail);
|
|
11460
12120
|
}
|
|
11461
|
-
return parts
|
|
12121
|
+
return joinSentences(parts);
|
|
11462
12122
|
}
|
|
11463
12123
|
|
|
11464
12124
|
function buildChatQuickButtonAriaLabel(prompt, isActivePrompt, context) {
|
|
@@ -11477,7 +12137,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11477
12137
|
parts.push(isActivePrompt
|
|
11478
12138
|
? "Current starter loaded in Compose for the current " + scopeLabel
|
|
11479
12139
|
: "Loads this starter into Compose for the current " + scopeLabel);
|
|
11480
|
-
return parts
|
|
12140
|
+
return joinSentences(parts);
|
|
11481
12141
|
}
|
|
11482
12142
|
|
|
11483
12143
|
function buildChatQuickRowAriaLabel(context) {
|
|
@@ -11512,21 +12172,21 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11512
12172
|
// The capability-cards data is still attached to emptyStateData.aria
|
|
11513
12173
|
// so screen-reader users hear the long form via the aria-label.
|
|
11514
12174
|
return {
|
|
11515
|
-
title: "Start with AI Chat",
|
|
11516
|
-
detail: context.scope === "selection"
|
|
11517
|
-
? "Ask anything about the current selection
|
|
11518
|
-
: "Ask anything about the current document.",
|
|
12175
|
+
title: (obj.__aiT ? obj.__aiT("aic_starttitle", "Start with AI Chat") : "Start with AI Chat"),
|
|
12176
|
+
detail: (context.scope === "selection"
|
|
12177
|
+
? (obj.__aiT ? obj.__aiT("aic_empty_sel", "Ask anything about the current selection") : "Ask anything about the current selection")
|
|
12178
|
+
: (obj.__aiT ? obj.__aiT("aic_empty_doc", "Ask anything about the current document") : "Ask anything about the current document")) + ".",
|
|
11519
12179
|
metaPills: [
|
|
11520
12180
|
{
|
|
11521
|
-
text: context.scope === "selection" ? "Selection context" : "Document context",
|
|
12181
|
+
text: context.scope === "selection" ? (obj.__aiT ? obj.__aiT("aic_pill_sel", "Selection context") : "Selection context") : (obj.__aiT ? obj.__aiT("aic_pill_doc", "Document context") : "Document context"),
|
|
11522
12182
|
className: "is-scope"
|
|
11523
12183
|
},
|
|
11524
12184
|
{
|
|
11525
|
-
text: (prompts && prompts.length ? prompts.length : 0) + " starters",
|
|
12185
|
+
text: (obj.__aiT ? obj.__aiT("aic_pill_starters", "{0} starters", prompts && prompts.length ? prompts.length : 0) : (prompts && prompts.length ? prompts.length : 0) + " starters"),
|
|
11526
12186
|
className: "is-prompts"
|
|
11527
12187
|
},
|
|
11528
12188
|
{
|
|
11529
|
-
text: getChatContextWordCount(context.source) + " words in scope",
|
|
12189
|
+
text: (obj.__aiT ? obj.__aiT("aic_pill_words", "{0} words in scope", getChatContextWordCount(context.source)) : getChatContextWordCount(context.source) + " words in scope"),
|
|
11530
12190
|
className: "is-size"
|
|
11531
12191
|
}
|
|
11532
12192
|
],
|
|
@@ -11545,7 +12205,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11545
12205
|
if (capabilityCard.copy) {
|
|
11546
12206
|
parts.push(capabilityCard.copy);
|
|
11547
12207
|
}
|
|
11548
|
-
return parts
|
|
12208
|
+
return joinSentences(parts);
|
|
11549
12209
|
}
|
|
11550
12210
|
|
|
11551
12211
|
function buildChatEmptyStateAriaLabel(emptyStateData) {
|
|
@@ -11571,7 +12231,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11571
12231
|
parts.push(buildChatEmptyCapabilityAriaLabel(emptyStateData.capabilityCards[cardIndex]));
|
|
11572
12232
|
}
|
|
11573
12233
|
}
|
|
11574
|
-
return parts
|
|
12234
|
+
return joinSentences(parts);
|
|
11575
12235
|
}
|
|
11576
12236
|
|
|
11577
12237
|
function clearChatNode(node) {
|
|
@@ -11930,6 +12590,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11930
12590
|
panel.setAttribute("aria-label", config.text_aichat || "AI Chat");
|
|
11931
12591
|
panel.tabIndex = -1;
|
|
11932
12592
|
editor.__aiChatPanel = panel;
|
|
12593
|
+
aiqWatch(panel);
|
|
12594
|
+
appendDemoNotice(panel);
|
|
11933
12595
|
panel.onkeydown = function (e) {
|
|
11934
12596
|
if (e.key === "Escape") {
|
|
11935
12597
|
e.preventDefault();
|
|
@@ -11971,8 +12633,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11971
12633
|
var clearButton = append(headerActions, "button", "", "secondary rte-ai-chat-header-button is-icon-only");
|
|
11972
12634
|
clearButton.type = "button";
|
|
11973
12635
|
clearButton.setAttribute("data-rte-ai-chat-header-action", "new-chat");
|
|
11974
|
-
clearButton.title = "Start a fresh chat";
|
|
11975
|
-
clearButton.setAttribute("aria-label", "Start a fresh chat");
|
|
12636
|
+
clearButton.title = (obj.__aiT ? obj.__aiT("aic_newchat", "Start a fresh chat") : "Start a fresh chat");
|
|
12637
|
+
clearButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aic_newchat", "Start a fresh chat") : "Start a fresh chat"));
|
|
11976
12638
|
setReviewV2ButtonContent(clearButton, "refresh", "");
|
|
11977
12639
|
clearButton.onclick = function () {
|
|
11978
12640
|
resetChatConversationState(state, "Started a fresh AI chat.");
|
|
@@ -11981,8 +12643,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
11981
12643
|
var closeButton = append(headerActions, "button", "", "rte-ai-panel-close-button");
|
|
11982
12644
|
closeButton.type = "button";
|
|
11983
12645
|
closeButton.setAttribute("data-rte-ai-chat-header-action", "close");
|
|
11984
|
-
closeButton.setAttribute("aria-label", "Close AI chat");
|
|
11985
|
-
closeButton.title = "Close";
|
|
12646
|
+
closeButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aic_closechat", "Close AI chat") : "Close AI chat"));
|
|
12647
|
+
closeButton.title = (obj.__aiT ? obj.__aiT("aic_close", "Close") : "Close");
|
|
11986
12648
|
closeButton.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 6l12 12"/><path d="M18 6L6 18"/></svg>';
|
|
11987
12649
|
closeButton.onclick = function () {
|
|
11988
12650
|
closeChatPanel({ restoreFocus: true });
|
|
@@ -12026,7 +12688,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12026
12688
|
selectionButton.disabled = !context.snapshot.hasSelection;
|
|
12027
12689
|
selectionButton.setAttribute("data-rte-ai-chat-scope-target", "selection");
|
|
12028
12690
|
selectionButton.setAttribute("aria-label", buildChatContextScopeButtonAriaLabel("selection", contextData));
|
|
12029
|
-
selectionButton.title = "Use selection";
|
|
12691
|
+
selectionButton.title = (obj.__aiT ? obj.__aiT("aic_usesel", "Use selection") : "Use selection");
|
|
12030
12692
|
setReviewV2ButtonContent(selectionButton, "cursorText", "");
|
|
12031
12693
|
selectionButton.onclick = function () {
|
|
12032
12694
|
var currentSnapshot = captureSelectionSnapshot();
|
|
@@ -12038,7 +12700,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12038
12700
|
documentButton.type = "button";
|
|
12039
12701
|
documentButton.setAttribute("data-rte-ai-chat-scope-target", "document");
|
|
12040
12702
|
documentButton.setAttribute("aria-label", buildChatContextScopeButtonAriaLabel("document", contextData));
|
|
12041
|
-
documentButton.title = "Use document";
|
|
12703
|
+
documentButton.title = (obj.__aiT ? obj.__aiT("aic_usedoc", "Use document") : "Use document");
|
|
12042
12704
|
setReviewV2ButtonContent(documentButton, "document", "");
|
|
12043
12705
|
documentButton.onclick = function () {
|
|
12044
12706
|
state.scope = "document";
|
|
@@ -12093,7 +12755,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12093
12755
|
if (recentSessions.length > 1) {
|
|
12094
12756
|
var historySection = append(panel, "div", "", "rte-ai-chat-stack rte-ai-chat-history-shell is-minimal");
|
|
12095
12757
|
historySection.setAttribute("role", "group");
|
|
12096
|
-
historySection.setAttribute("aria-label", "Recent AI chats");
|
|
12758
|
+
historySection.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aic_recent", "Recent AI chats") : "Recent AI chats"));
|
|
12097
12759
|
var historyRow = append(historySection, "div", "", "rte-ai-chat-quick-row rte-ai-chat-history-row");
|
|
12098
12760
|
for (var s = 0; s < recentSessions.length; s++) {
|
|
12099
12761
|
(function (session, index) {
|
|
@@ -12160,10 +12822,10 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12160
12822
|
// reference drops with the chips.
|
|
12161
12823
|
var hasQuickStarts = !!(prompts && prompts.length);
|
|
12162
12824
|
var emptyHintBase = context.scope === "selection"
|
|
12163
|
-
? "Ask anything about the current selection"
|
|
12164
|
-
: "Ask anything about the current document";
|
|
12825
|
+
? (obj.__aiT ? obj.__aiT("aic_empty_sel", "Ask anything about the current selection") : "Ask anything about the current selection")
|
|
12826
|
+
: (obj.__aiT ? obj.__aiT("aic_empty_doc", "Ask anything about the current document") : "Ask anything about the current document");
|
|
12165
12827
|
append(emptyState, "div", "", "rte-ai-chat-empty-detail",
|
|
12166
|
-
hasQuickStarts ? (emptyHintBase + "
|
|
12828
|
+
hasQuickStarts ? (emptyHintBase + (obj.__aiT ? obj.__aiT("aic_orquick", " \u2014 or tap a quick start above.") : " \u2014 or tap a quick start above.")) : (emptyHintBase + "."));
|
|
12167
12829
|
}
|
|
12168
12830
|
var activeExchangeGroup = null;
|
|
12169
12831
|
var activeExchangeIndex = 0;
|
|
@@ -12315,8 +12977,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12315
12977
|
var msgActions = append(item, "div", "", "rte-ai-chat-message-actions-row");
|
|
12316
12978
|
var msgCopyBtn = append(msgActions, "button", "", "rte-ai-chat-message-action is-copy");
|
|
12317
12979
|
msgCopyBtn.type = "button";
|
|
12318
|
-
msgCopyBtn.title = "Copy message text";
|
|
12319
|
-
setReviewV2ButtonContent(msgCopyBtn, "checkAll", "Copy");
|
|
12980
|
+
msgCopyBtn.title = (obj.__aiT ? obj.__aiT("aic_copymsg", "Copy message text") : "Copy message text");
|
|
12981
|
+
setReviewV2ButtonContent(msgCopyBtn, "checkAll", (obj.__aiT ? obj.__aiT("aic_copy", "Copy") : "Copy"));
|
|
12320
12982
|
msgCopyBtn.onclick = (function (text, btn) {
|
|
12321
12983
|
return function (e) {
|
|
12322
12984
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
@@ -12341,10 +13003,10 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12341
13003
|
}
|
|
12342
13004
|
if (copied) {
|
|
12343
13005
|
btn.classList.add("is-just-copied");
|
|
12344
|
-
setReviewV2ButtonContent(btn, "check", "Copied");
|
|
13006
|
+
setReviewV2ButtonContent(btn, "check", (obj.__aiT ? obj.__aiT("aic_copied", "Copied") : "Copied"));
|
|
12345
13007
|
setTimeout(function () {
|
|
12346
13008
|
btn.classList.remove("is-just-copied");
|
|
12347
|
-
setReviewV2ButtonContent(btn, "checkAll", "Copy");
|
|
13009
|
+
setReviewV2ButtonContent(btn, "checkAll", (obj.__aiT ? obj.__aiT("aic_copy", "Copy") : "Copy"));
|
|
12348
13010
|
}, 1400);
|
|
12349
13011
|
}
|
|
12350
13012
|
};
|
|
@@ -12362,8 +13024,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12362
13024
|
if (precedingUserPrompt) {
|
|
12363
13025
|
var msgRegenBtn = append(msgActions, "button", "", "rte-ai-chat-message-action is-regen");
|
|
12364
13026
|
msgRegenBtn.type = "button";
|
|
12365
|
-
msgRegenBtn.title = "Regenerate response";
|
|
12366
|
-
setReviewV2ButtonContent(msgRegenBtn, "refresh", "Regenerate");
|
|
13027
|
+
msgRegenBtn.title = (obj.__aiT ? obj.__aiT("aic_regen", "Regenerate response") : "Regenerate response");
|
|
13028
|
+
setReviewV2ButtonContent(msgRegenBtn, "refresh", (obj.__aiT ? obj.__aiT("aid_regenerate", "Regenerate") : "Regenerate"));
|
|
12367
13029
|
msgRegenBtn.onclick = (function (prompt) {
|
|
12368
13030
|
return function (e) {
|
|
12369
13031
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
@@ -12622,7 +13284,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12622
13284
|
moreToggle.type = "button";
|
|
12623
13285
|
moreToggle.setAttribute("data-rte-ai-chat-action", "more");
|
|
12624
13286
|
moreToggle.setAttribute("aria-expanded", "false");
|
|
12625
|
-
moreToggle.title = "More apply options";
|
|
13287
|
+
moreToggle.title = (obj.__aiT ? obj.__aiT("ai2_moreopts", "More apply options") : "More apply options");
|
|
12626
13288
|
setReviewV2ButtonContent(moreToggle, "more", "More");
|
|
12627
13289
|
moreToggle.onclick = (function (host) {
|
|
12628
13290
|
return function (e) {
|
|
@@ -12711,8 +13373,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12711
13373
|
// 51 chars → 14 chars. Same shape Claude composer ("Ask
|
|
12712
13374
|
// anything") / ChatGPT composer ("Message ChatGPT") / Notion AI
|
|
12713
13375
|
// ("Tell AI what to do…") ship.
|
|
12714
|
-
composerArea.placeholder = "Ask anything
|
|
12715
|
-
composerArea.setAttribute("aria-label", "Compose next AI request for the current "
|
|
13376
|
+
composerArea.placeholder = (obj.__aiT ? obj.__aiT("aic_askanything", "Ask anything\u2026") : "Ask anything\u2026");
|
|
13377
|
+
composerArea.setAttribute("aria-label", context.scope === "selection" ? (obj.__aiT ? obj.__aiT("aic_compose_sel", "Compose next AI request for the current selection") : "Compose next AI request for the current selection") : (obj.__aiT ? obj.__aiT("aic_compose_doc", "Compose next AI request for the current document") : "Compose next AI request for the current document"));
|
|
12716
13378
|
composerArea.value = state.draft || "";
|
|
12717
13379
|
composerArea.disabled = !!state.busy;
|
|
12718
13380
|
composerArea.oninput = function () {
|
|
@@ -12839,7 +13501,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
12839
13501
|
}
|
|
12840
13502
|
|
|
12841
13503
|
function getReviewTypeOptions(suggestions) {
|
|
12842
|
-
var map = { all: "All types" };
|
|
13504
|
+
var map = { all: (obj.__aiT ? obj.__aiT("air_alltypes", "All types") : "All types") };
|
|
12843
13505
|
for (var i = 0; i < suggestions.length; i++) {
|
|
12844
13506
|
var typeValue = getSuggestionTypeValue(suggestions[i] && suggestions[i].suggestionType || "");
|
|
12845
13507
|
if (!map[typeValue]) {
|
|
@@ -13782,7 +14444,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
13782
14444
|
if (summaryData.detail) {
|
|
13783
14445
|
parts.push(summaryData.detail);
|
|
13784
14446
|
}
|
|
13785
|
-
return parts
|
|
14447
|
+
return joinSentences(parts);
|
|
13786
14448
|
}
|
|
13787
14449
|
|
|
13788
14450
|
function syncActiveReviewActionSummary(preferredAction) {
|
|
@@ -14005,10 +14667,10 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14005
14667
|
if (isTarget) {
|
|
14006
14668
|
button.setAttribute("data-rte-ai-review-enter-target-label", "Enter");
|
|
14007
14669
|
if (ariaLabel && !/current enter target/i.test(ariaLabel)) {
|
|
14008
|
-
button.setAttribute("aria-label", ariaLabel
|
|
14670
|
+
button.setAttribute("aria-label", joinSentences([ariaLabel, "Current Enter target."]));
|
|
14009
14671
|
}
|
|
14010
14672
|
if (title && !/current enter target/i.test(title)) {
|
|
14011
|
-
button.title = title
|
|
14673
|
+
button.title = joinSentences([title, "Current Enter target."]);
|
|
14012
14674
|
}
|
|
14013
14675
|
}
|
|
14014
14676
|
else {
|
|
@@ -14793,8 +15455,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14793
15455
|
// header pill would have duplicated that signal.
|
|
14794
15456
|
var closeBtn = append(header, "button", "", "rte-ai-review-v2-close");
|
|
14795
15457
|
closeBtn.type = "button";
|
|
14796
|
-
closeBtn.setAttribute("aria-label", "Close AI review");
|
|
14797
|
-
closeBtn.title = "Close";
|
|
15458
|
+
closeBtn.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("air_closearia", "Close AI review") : "Close AI review"));
|
|
15459
|
+
closeBtn.title = (obj.__aiT ? obj.__aiT("air_close", "Close") : "Close");
|
|
14798
15460
|
closeBtn.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 6l12 12"/><path d="M18 6L6 18"/></svg>';
|
|
14799
15461
|
closeBtn.onclick = function () {
|
|
14800
15462
|
closeReviewPanel();
|
|
@@ -14840,9 +15502,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14840
15502
|
return tab;
|
|
14841
15503
|
}
|
|
14842
15504
|
|
|
14843
|
-
makeTab("pending", "inbox", "To review", pendingCountForTabs);
|
|
14844
|
-
makeTab("resolved", "history", "History", resolvedCountForTabs);
|
|
14845
|
-
makeTab("all", "filter", "All", counts.total);
|
|
15505
|
+
makeTab("pending", "inbox", (obj.__aiT ? obj.__aiT("air_tab_pending", "To review") : "To review"), pendingCountForTabs);
|
|
15506
|
+
makeTab("resolved", "history", (obj.__aiT ? obj.__aiT("air_tab_history", "History") : "History"), resolvedCountForTabs);
|
|
15507
|
+
makeTab("all", "filter", (obj.__aiT ? obj.__aiT("air_tab_all", "All") : "All"), counts.total);
|
|
14846
15508
|
|
|
14847
15509
|
// Toolbar: type filter + (on Pending tab only) Accept all / Reject all
|
|
14848
15510
|
var toolbar = append(panel, "div", "", "rte-ai-review-v2-toolbar");
|
|
@@ -14872,14 +15534,14 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14872
15534
|
var acceptAllBtn = append(toolbar, "button", "", "rte-ai-review-v2-batch is-accept");
|
|
14873
15535
|
acceptAllBtn.type = "button";
|
|
14874
15536
|
acceptAllBtn.disabled = !pendingForBatch;
|
|
14875
|
-
setReviewV2ButtonContent(acceptAllBtn, "checkAll", "Accept all");
|
|
15537
|
+
setReviewV2ButtonContent(acceptAllBtn, "checkAll", (obj.__aiT ? obj.__aiT("air_acceptall", "Accept all") : "Accept all"));
|
|
14876
15538
|
acceptAllBtn.onclick = function () {
|
|
14877
15539
|
if (acceptPendingSuggestionsByType(typeFilter)) renderReviewPanelV2(true);
|
|
14878
15540
|
};
|
|
14879
15541
|
var rejectAllBtn = append(toolbar, "button", "", "rte-ai-review-v2-batch is-reject");
|
|
14880
15542
|
rejectAllBtn.type = "button";
|
|
14881
15543
|
rejectAllBtn.disabled = !pendingForBatch;
|
|
14882
|
-
setReviewV2ButtonContent(rejectAllBtn, "x", "Reject all");
|
|
15544
|
+
setReviewV2ButtonContent(rejectAllBtn, "x", (obj.__aiT ? obj.__aiT("air_rejectall", "Reject all") : "Reject all"));
|
|
14883
15545
|
rejectAllBtn.onclick = function () {
|
|
14884
15546
|
if (rejectPendingSuggestionsByType(typeFilter)) renderReviewPanelV2(true);
|
|
14885
15547
|
};
|
|
@@ -14923,11 +15585,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14923
15585
|
}
|
|
14924
15586
|
var activeV2Suggestion = findSuggestionById(activeId);
|
|
14925
15587
|
var resolvedProgress = counts.total ? Math.max(0, Math.min(100, Math.round((resolvedCountForTabs / counts.total) * 100))) : 0;
|
|
14926
|
-
var queueScopeLabel = typeFilter === "all" ? "All types" : getSuggestionTypeLabel(typeFilter);
|
|
15588
|
+
var queueScopeLabel = typeFilter === "all" ? (obj.__aiT ? obj.__aiT("air_alltypes", "All types") : "All types") : getSuggestionTypeLabel(typeFilter);
|
|
14927
15589
|
var v2Center = append(panel, "div", "", "rte-ai-review-v2-center");
|
|
14928
15590
|
panel.insertBefore(v2Center, body);
|
|
14929
15591
|
v2Center.setAttribute("role", "group");
|
|
14930
|
-
v2Center.setAttribute("aria-label", "AI review summary");
|
|
15592
|
+
v2Center.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("air_summary", "AI review summary") : "AI review summary"));
|
|
14931
15593
|
function appendReviewV2CenterTile(className, label, value, detail) {
|
|
14932
15594
|
var tile = append(v2Center, "div", "", "rte-ai-review-v2-center-tile " + className);
|
|
14933
15595
|
append(tile, "span", "", "rte-ai-review-v2-center-label", label);
|
|
@@ -14935,19 +15597,19 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14935
15597
|
append(tile, "span", "", "rte-ai-review-v2-center-detail", detail);
|
|
14936
15598
|
return tile;
|
|
14937
15599
|
}
|
|
14938
|
-
appendReviewV2CenterTile("is-queue", "Queue",
|
|
14939
|
-
appendReviewV2CenterTile("is-progress", "Progress", resolvedProgress + "% done", resolvedCountForTabs + " resolved of " + counts.total);
|
|
15600
|
+
appendReviewV2CenterTile("is-queue", (obj.__aiT ? obj.__aiT("air_queue", "Queue") : "Queue"), (obj.__aiT ? obj.__aiT("air_npending", "{0} pending", pendingForBatch) : pendingForBatch + " pending"), activeTab === "pending" ? (obj.__aiT ? obj.__aiT("air_toreview", "{0} to review", queueScopeLabel) : queueScopeLabel + " to review") : (obj.__aiT ? obj.__aiT("air_filter", "{0} filter", queueScopeLabel) : queueScopeLabel + " filter"));
|
|
15601
|
+
appendReviewV2CenterTile("is-progress", (obj.__aiT ? obj.__aiT("air_progress", "Progress") : "Progress"), (obj.__aiT ? obj.__aiT("air_pctdone", "{0}% done", resolvedProgress) : resolvedProgress + "% done"), (obj.__aiT ? obj.__aiT("air_resolvedof", "{0} resolved of {1}").split("{0}").join(String(resolvedCountForTabs)).split("{1}").join(String(counts.total)) : resolvedCountForTabs + " resolved of " + counts.total));
|
|
14940
15602
|
var nextV2Value = activeV2Suggestion
|
|
14941
|
-
? ((activeV2Suggestion.status === "pending" || activeV2Suggestion.status === "stale") ? "Review current" : "Inspect item")
|
|
14942
|
-
: (pendingForBatch ? "Pick an item" : (counts.total ? "All caught up" : "Start with Ask AI"));
|
|
15603
|
+
? ((activeV2Suggestion.status === "pending" || activeV2Suggestion.status === "stale") ? (obj.__aiT ? obj.__aiT("air_reviewcurrent", "Review current") : "Review current") : (obj.__aiT ? obj.__aiT("air_inspect", "Inspect item") : "Inspect item"))
|
|
15604
|
+
: (pendingForBatch ? (obj.__aiT ? obj.__aiT("air_pick", "Pick an item") : "Pick an item") : (counts.total ? (obj.__aiT ? obj.__aiT("air_caughtup", "All caught up") : "All caught up") : (obj.__aiT ? obj.__aiT("air_startask", "Start with Ask AI") : "Start with Ask AI")));
|
|
14943
15605
|
var nextV2Detail = activeV2Suggestion
|
|
14944
15606
|
? summarizeSuggestionText(activeV2Suggestion.originalText || activeV2Suggestion.resultText || "AI suggestion", 72)
|
|
14945
|
-
: (pendingForBatch ? "Select a card below to compare the change." : (counts.total ? "History keeps accepted and rejected changes." : "AI suggestions will appear here."));
|
|
14946
|
-
var nextV2Tile = appendReviewV2CenterTile("is-next", "Next step", nextV2Value, nextV2Detail);
|
|
15607
|
+
: (pendingForBatch ? (obj.__aiT ? obj.__aiT("air_d_pick", "Select a card below to compare the change.") : "Select a card below to compare the change.") : (counts.total ? (obj.__aiT ? obj.__aiT("air_d_history", "History keeps accepted and rejected changes.") : "History keeps accepted and rejected changes.") : (obj.__aiT ? obj.__aiT("air_d_empty", "AI suggestions will appear here.") : "AI suggestions will appear here.")));
|
|
15608
|
+
var nextV2Tile = appendReviewV2CenterTile("is-next", (obj.__aiT ? obj.__aiT("air_next", "Next step") : "Next step"), nextV2Value, nextV2Detail);
|
|
14947
15609
|
if (activeV2Suggestion) {
|
|
14948
|
-
var focusV2Button = append(nextV2Tile, "button", "", "rte-ai-review-v2-center-action", "Focus");
|
|
15610
|
+
var focusV2Button = append(nextV2Tile, "button", "", "rte-ai-review-v2-center-action", (obj.__aiT ? obj.__aiT("air_focus", "Focus") : "Focus"));
|
|
14949
15611
|
focusV2Button.type = "button";
|
|
14950
|
-
focusV2Button.setAttribute("aria-label", "Focus the current AI review item");
|
|
15612
|
+
focusV2Button.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("air_focusaria", "Focus the current AI review item") : "Focus the current AI review item"));
|
|
14951
15613
|
focusV2Button.onclick = function () {
|
|
14952
15614
|
setActiveSuggestionId(activeId);
|
|
14953
15615
|
renderReviewPanelV2(true);
|
|
@@ -14959,19 +15621,19 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14959
15621
|
if (counts.total === 0) {
|
|
14960
15622
|
// Truly empty store — guide the user to AI Chat / Ask AI.
|
|
14961
15623
|
append(empty, "div", "", "rte-ai-review-v2-empty-art is-prompt");
|
|
14962
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-title", "No AI suggestions yet");
|
|
14963
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-detail", "Ask AI or AI Chat will drop suggestions here for you to accept or reject.");
|
|
15624
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-title", (obj.__aiT ? obj.__aiT("air_e_none_t", "No AI suggestions yet") : "No AI suggestions yet"));
|
|
15625
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-detail", (obj.__aiT ? obj.__aiT("air_e_none_d", "Ask AI or AI Chat will drop suggestions here for you to accept or reject.") : "Ask AI or AI Chat will drop suggestions here for you to accept or reject."));
|
|
14964
15626
|
} else if (activeTab === "pending") {
|
|
14965
15627
|
// History exists but Pending is empty — celebrate.
|
|
14966
15628
|
append(empty, "div", "", "rte-ai-review-v2-empty-art is-clear");
|
|
14967
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-title", "Nothing to review");
|
|
14968
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-detail", "You're all caught up. Switch to History to see past decisions.");
|
|
15629
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-title", (obj.__aiT ? obj.__aiT("air_e_done_t", "Nothing to review") : "Nothing to review"));
|
|
15630
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-detail", (obj.__aiT ? obj.__aiT("air_e_done_d", "You're all caught up. Switch to History to see past decisions.") : "You're all caught up. Switch to History to see past decisions."));
|
|
14969
15631
|
} else if (activeTab === "resolved") {
|
|
14970
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-title", "No history yet");
|
|
14971
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-detail", "Decisions you make on the To review tab will show up here.");
|
|
15632
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-title", (obj.__aiT ? obj.__aiT("air_e_nohist_t", "No history yet") : "No history yet"));
|
|
15633
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-detail", (obj.__aiT ? obj.__aiT("air_e_nohist_d", "Decisions you make on the To review tab will show up here.") : "Decisions you make on the To review tab will show up here."));
|
|
14972
15634
|
} else {
|
|
14973
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-title", "No suggestions match this filter");
|
|
14974
|
-
append(empty, "div", "", "rte-ai-review-v2-empty-detail", "Try a different type, or pick a different tab.");
|
|
15635
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-title", (obj.__aiT ? obj.__aiT("air_e_nomatch_t", "No suggestions match this filter") : "No suggestions match this filter"));
|
|
15636
|
+
append(empty, "div", "", "rte-ai-review-v2-empty-detail", (obj.__aiT ? obj.__aiT("air_e_nomatch_d", "Try a different type, or pick a different tab.") : "Try a different type, or pick a different tab."));
|
|
14975
15637
|
}
|
|
14976
15638
|
} else {
|
|
14977
15639
|
for (var vi = 0; vi < visible.length; vi++) {
|
|
@@ -14986,9 +15648,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14986
15648
|
append(head, "span", "", "rte-ai-review-v2-item-type", getSuggestionTypeLabel(sug.suggestionType));
|
|
14987
15649
|
}
|
|
14988
15650
|
if (sug.snapshot && sug.snapshot.hasSelection) {
|
|
14989
|
-
append(head, "span", "", "rte-ai-review-v2-item-scope", "Selection");
|
|
15651
|
+
append(head, "span", "", "rte-ai-review-v2-item-scope", (obj.__aiT ? obj.__aiT("air_scope_sel", "Selection") : "Selection"));
|
|
14990
15652
|
} else {
|
|
14991
|
-
append(head, "span", "", "rte-ai-review-v2-item-scope", "Document");
|
|
15653
|
+
append(head, "span", "", "rte-ai-review-v2-item-scope", (obj.__aiT ? obj.__aiT("air_scope_doc", "Document") : "Document"));
|
|
14992
15654
|
}
|
|
14993
15655
|
|
|
14994
15656
|
var titleText = summarizeSuggestionText(sug.originalText || sug.resultText || "AI suggestion", 120);
|
|
@@ -14998,7 +15660,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
14998
15660
|
if (sug.originalText && sug.resultText && normalizeText(sug.originalText) !== normalizeText(sug.resultText)) {
|
|
14999
15661
|
var compare = append(item, "div", "", "rte-ai-review-v2-compare");
|
|
15000
15662
|
var before = append(compare, "div", "", "rte-ai-review-v2-before");
|
|
15001
|
-
append(before, "span", "", "rte-ai-review-v2-compare-label", "Current");
|
|
15663
|
+
append(before, "span", "", "rte-ai-review-v2-compare-label", (obj.__aiT ? obj.__aiT("air_current", "Current") : "Current"));
|
|
15002
15664
|
append(before, "div", "", "rte-ai-review-v2-compare-text", normalizeText(sug.originalText));
|
|
15003
15665
|
// Decorative arrow on the centerline of the
|
|
15004
15666
|
// grid pointing from Current -> Suggested.
|
|
@@ -15006,7 +15668,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
15006
15668
|
compareArrow.setAttribute("aria-hidden", "true");
|
|
15007
15669
|
compareArrow.innerHTML = REVIEW_V2_ICONS.arrowRight;
|
|
15008
15670
|
var after = append(compare, "div", "", "rte-ai-review-v2-after");
|
|
15009
|
-
append(after, "span", "", "rte-ai-review-v2-compare-label", "Suggested");
|
|
15671
|
+
append(after, "span", "", "rte-ai-review-v2-compare-label", (obj.__aiT ? obj.__aiT("air_suggested", "Suggested") : "Suggested"));
|
|
15010
15672
|
append(after, "div", "", "rte-ai-review-v2-compare-text", normalizeText(sug.resultText));
|
|
15011
15673
|
}
|
|
15012
15674
|
if (sug.reason) {
|
|
@@ -15018,14 +15680,14 @@ function RTE_Plugin_AIToolkit() {
|
|
|
15018
15680
|
bulbWrap.innerHTML = REVIEW_V2_ICONS.bulb;
|
|
15019
15681
|
reasonLabel.appendChild(bulbWrap);
|
|
15020
15682
|
var reasonLabelText = reason.ownerDocument.createElement("span");
|
|
15021
|
-
reasonLabelText.textContent = "Why";
|
|
15683
|
+
reasonLabelText.textContent = (obj.__aiT ? obj.__aiT("air_why", "Why") : "Why");
|
|
15022
15684
|
reasonLabel.appendChild(reasonLabelText);
|
|
15023
15685
|
append(reason, "span", "", "rte-ai-review-v2-reason-text", sug.reason);
|
|
15024
15686
|
}
|
|
15025
15687
|
var actions = append(item, "div", "", "rte-ai-review-v2-actions");
|
|
15026
15688
|
var locateBtn = append(actions, "button", "", "rte-ai-review-v2-action is-locate");
|
|
15027
15689
|
locateBtn.type = "button";
|
|
15028
|
-
setReviewV2ButtonContent(locateBtn, "target", "Locate");
|
|
15690
|
+
setReviewV2ButtonContent(locateBtn, "target", (obj.__aiT ? obj.__aiT("air_locate", "Locate") : "Locate"));
|
|
15029
15691
|
locateBtn.onclick = function (e) {
|
|
15030
15692
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
15031
15693
|
locateSuggestion(sug.id);
|
|
@@ -15033,14 +15695,14 @@ function RTE_Plugin_AIToolkit() {
|
|
|
15033
15695
|
if (sug.status === "pending" || sug.status === "stale") {
|
|
15034
15696
|
var acceptBtn = append(actions, "button", "", "rte-ai-review-v2-action is-accept");
|
|
15035
15697
|
acceptBtn.type = "button";
|
|
15036
|
-
setReviewV2ButtonContent(acceptBtn, "check", "Accept");
|
|
15698
|
+
setReviewV2ButtonContent(acceptBtn, "check", (obj.__aiT ? obj.__aiT("air_accept", "Accept") : "Accept"));
|
|
15037
15699
|
acceptBtn.onclick = function (e) {
|
|
15038
15700
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
15039
15701
|
applyReviewDecision(sug.id, "accept", { focusAction: "accept" });
|
|
15040
15702
|
};
|
|
15041
15703
|
var rejectBtn = append(actions, "button", "", "rte-ai-review-v2-action is-reject");
|
|
15042
15704
|
rejectBtn.type = "button";
|
|
15043
|
-
setReviewV2ButtonContent(rejectBtn, "x", "Reject");
|
|
15705
|
+
setReviewV2ButtonContent(rejectBtn, "x", (obj.__aiT ? obj.__aiT("air_reject", "Reject") : "Reject"));
|
|
15044
15706
|
rejectBtn.onclick = function (e) {
|
|
15045
15707
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
15046
15708
|
applyReviewDecision(sug.id, "reject", { focusAction: "reject" });
|
|
@@ -15903,7 +16565,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
15903
16565
|
if (previewItem.reasonText) {
|
|
15904
16566
|
previewAriaParts.push("Why this next: " + previewItem.reasonText);
|
|
15905
16567
|
}
|
|
15906
|
-
previewRow.setAttribute("aria-label", previewAriaParts
|
|
16568
|
+
previewRow.setAttribute("aria-label", joinSentences(previewAriaParts));
|
|
15907
16569
|
previewRow.setAttribute("data-rte-ai-primary-label", previewItem.actionLabel || previewItem.label || "Open next queue");
|
|
15908
16570
|
if (previewItem.focusActionLabel) {
|
|
15909
16571
|
previewRow.setAttribute("data-rte-ai-empty-focus-label", previewItem.focusActionLabel);
|
|
@@ -16433,6 +17095,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16433
17095
|
modeChip.className = "rte-ai-dialog-mode-chip is-detached";
|
|
16434
17096
|
modeChip.setAttribute("aria-hidden", "true");
|
|
16435
17097
|
|
|
17098
|
+
appendDemoNotice(dialoginner);
|
|
16436
17099
|
var grid = append(dialoginner, "div", "", "demo-ai-dialog-grid");
|
|
16437
17100
|
var dialogIdSuffix = String(Date.now()) + "-" + String(Math.floor(Math.random() * 1000000));
|
|
16438
17101
|
var modeHelpId = "rte-ai-mode-help-" + dialogIdSuffix;
|
|
@@ -16441,11 +17104,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16441
17104
|
var guidanceId = "rte-ai-apply-guidance-" + dialogIdSuffix;
|
|
16442
17105
|
var statusId = "rte-ai-dialog-status-" + dialogIdSuffix;
|
|
16443
17106
|
grid.setAttribute("role", "form");
|
|
16444
|
-
grid.setAttribute("aria-label", "Ask AI controls");
|
|
17107
|
+
grid.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_controls", "Ask AI controls") : "Ask AI controls"));
|
|
16445
17108
|
|
|
16446
17109
|
var compactControls = append(grid, "div", "", "demo-ai-compact-controls");
|
|
16447
17110
|
compactControls.setAttribute("role", "group");
|
|
16448
|
-
compactControls.setAttribute("aria-label", "Ask AI setup");
|
|
17111
|
+
compactControls.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_setup", "Ask AI setup") : "Ask AI setup"));
|
|
16449
17112
|
|
|
16450
17113
|
// 2026-05-08 compact pass: dropped the per-field uppercase mini-labels
|
|
16451
17114
|
// ("Action" / "Language" / "Use" / blank-spacer) and the duplicate
|
|
@@ -16454,7 +17117,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16454
17117
|
// select's title attribute (still surfaced on hover).
|
|
16455
17118
|
var actionField = append(compactControls, "div", "", "demo-ai-field demo-ai-action-field");
|
|
16456
17119
|
var modeSelect = append(actionField, "select");
|
|
16457
|
-
modeSelect.setAttribute("aria-label", "AI action");
|
|
17120
|
+
modeSelect.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_action", "AI action") : "AI action"));
|
|
16458
17121
|
// 2026-05-17 (v20260517a): the mode help line is back on-screen,
|
|
16459
17122
|
// attached directly to the dialog grid (full-width row under the
|
|
16460
17123
|
// controls strip). The prior hover-only `title` attribute meant
|
|
@@ -16481,7 +17144,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16481
17144
|
|
|
16482
17145
|
var languageField = append(compactControls, "div", "", "demo-ai-field demo-ai-language-field");
|
|
16483
17146
|
var languageSelect = append(languageField, "select");
|
|
16484
|
-
languageSelect.setAttribute("aria-label", "Translation target language");
|
|
17147
|
+
languageSelect.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_targetlang", "Translation target language") : "Translation target language"));
|
|
16485
17148
|
populateDialogModes();
|
|
16486
17149
|
if (options.presetMode) {
|
|
16487
17150
|
// 2026-05-19 (v20260519a): only honor presetMode when it
|
|
@@ -16532,11 +17195,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16532
17195
|
// can override with `.demo-ai-scope-select { display: none }` +
|
|
16533
17196
|
// `.demo-ai-scope-row.is-detached { display: flex !important }`.
|
|
16534
17197
|
var scopeSelect = append(scopeField, "select", "", "demo-ai-scope-select");
|
|
16535
|
-
scopeSelect.setAttribute("aria-label", "Source for Ask AI");
|
|
17198
|
+
scopeSelect.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_source", "Source for Ask AI") : "Source for Ask AI"));
|
|
16536
17199
|
scopeSelect.setAttribute("data-rte-ai-dialog-action", "scope");
|
|
16537
|
-
var scopeOptionSelection = append(scopeSelect, "option", "", "", "Selection");
|
|
17200
|
+
var scopeOptionSelection = append(scopeSelect, "option", "", "", (obj.__aiT ? obj.__aiT("aid_selection", "Selection") : "Selection"));
|
|
16538
17201
|
scopeOptionSelection.value = "selection";
|
|
16539
|
-
var scopeOptionDocument = append(scopeSelect, "option", "", "", "Document");
|
|
17202
|
+
var scopeOptionDocument = append(scopeSelect, "option", "", "", (obj.__aiT ? obj.__aiT("aid_document", "Document") : "Document"));
|
|
16540
17203
|
scopeOptionDocument.value = "document";
|
|
16541
17204
|
|
|
16542
17205
|
// 2026-06-01 (v20260601a): scope caption surfaced inline when only
|
|
@@ -16561,21 +17224,21 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16561
17224
|
var loadRow = scopeField.ownerDocument.createElement("div");
|
|
16562
17225
|
loadRow.className = "demo-actions-row demo-ai-scope-row is-detached";
|
|
16563
17226
|
loadRow.setAttribute("role", "group");
|
|
16564
|
-
loadRow.setAttribute("aria-label", "Source scope");
|
|
17227
|
+
loadRow.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_scope", "Source scope") : "Source scope"));
|
|
16565
17228
|
var loadSelectionButton = scopeField.ownerDocument.createElement("button");
|
|
16566
17229
|
loadSelectionButton.type = "button";
|
|
16567
17230
|
loadSelectionButton.className = "is-icon-only";
|
|
16568
17231
|
loadSelectionButton.setAttribute("data-rte-ai-dialog-action", "load-selection");
|
|
16569
|
-
loadSelectionButton.title = "Use selection";
|
|
16570
|
-
loadSelectionButton.setAttribute("aria-label", "Use selection");
|
|
17232
|
+
loadSelectionButton.title = (obj.__aiT ? obj.__aiT("aid_usesel", "Use selection") : "Use selection");
|
|
17233
|
+
loadSelectionButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_usesel", "Use selection") : "Use selection"));
|
|
16571
17234
|
setReviewV2ButtonContent(loadSelectionButton, "cursorText", "");
|
|
16572
17235
|
loadRow.appendChild(loadSelectionButton);
|
|
16573
17236
|
var loadDocumentButton = scopeField.ownerDocument.createElement("button");
|
|
16574
17237
|
loadDocumentButton.type = "button";
|
|
16575
17238
|
loadDocumentButton.className = "secondary is-icon-only";
|
|
16576
17239
|
loadDocumentButton.setAttribute("data-rte-ai-dialog-action", "load-document");
|
|
16577
|
-
loadDocumentButton.title = "Use document";
|
|
16578
|
-
loadDocumentButton.setAttribute("aria-label", "Use document");
|
|
17240
|
+
loadDocumentButton.title = (obj.__aiT ? obj.__aiT("aid_usedoc", "Use document") : "Use document");
|
|
17241
|
+
loadDocumentButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_usedoc", "Use document") : "Use document"));
|
|
16579
17242
|
setReviewV2ButtonContent(loadDocumentButton, "document", "");
|
|
16580
17243
|
loadRow.appendChild(loadDocumentButton);
|
|
16581
17244
|
// 2026-05-17 (v20260523a): append the legacy scope row to
|
|
@@ -16589,20 +17252,20 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16589
17252
|
var runField = append(compactControls, "div", "", "demo-ai-field demo-ai-run-field");
|
|
16590
17253
|
var runRow = append(runField, "div", "", "demo-actions-row demo-ai-run-row");
|
|
16591
17254
|
runRow.setAttribute("role", "group");
|
|
16592
|
-
runRow.setAttribute("aria-label", "Ask AI run actions");
|
|
17255
|
+
runRow.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_run", "Ask AI run actions") : "Ask AI run actions"));
|
|
16593
17256
|
var runButton = append(runRow, "button", "", "is-primary");
|
|
16594
17257
|
runButton.type = "button";
|
|
16595
17258
|
runButton.setAttribute("data-rte-ai-dialog-action", "run");
|
|
16596
17259
|
// Sparkle glyph + "Generate" — same icon vocabulary as AI Chat
|
|
16597
17260
|
// composer's Send and AI Review V2's Accept buttons.
|
|
16598
|
-
setReviewV2ButtonContent(runButton, "sparkles", "Generate");
|
|
17261
|
+
setReviewV2ButtonContent(runButton, "sparkles", (obj.__aiT ? obj.__aiT("aid_generate", "Generate") : "Generate"));
|
|
16599
17262
|
// 2026-05-09 (v20260509j): surface the Ctrl/Cmd+Enter shortcut on
|
|
16600
17263
|
// hover and to screen readers. Same idiom every modern AI surface
|
|
16601
17264
|
// uses (ChatGPT Send: "Send (⌘↵)", Notion AI Generate: "Generate
|
|
16602
17265
|
// (Ctrl+Enter)"). aria-keyshortcuts is the WAI-ARIA standard way
|
|
16603
17266
|
// to advertise a keyboard shortcut so screen readers announce
|
|
16604
17267
|
// "Generate, Control+Enter" alongside the button name.
|
|
16605
|
-
runButton.title = "Generate AI suggestion (Ctrl+Enter)";
|
|
17268
|
+
runButton.title = (obj.__aiT ? obj.__aiT("ai2_gentitle", "Generate AI suggestion (Ctrl+Enter)") : "Generate AI suggestion (Ctrl+Enter)");
|
|
16606
17269
|
runButton.setAttribute("aria-keyshortcuts", "Control+Enter");
|
|
16607
17270
|
// 2026-05-17 (v20260517a): visible Ctrl+↵ / ⌘↵ chip baked into
|
|
16608
17271
|
// the Generate button. Pre-v20260517a the shortcut only lived in
|
|
@@ -16614,7 +17277,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16614
17277
|
// from navigator.platform so the hint matches the actual key
|
|
16615
17278
|
// the user would press. */
|
|
16616
17279
|
var isMacOs = /Mac|iPad|iPhone|iPod/i.test((navigator.platform || "") + " " + (navigator.userAgent || ""));
|
|
16617
|
-
var runKbdLabel = isMacOs ? "
|
|
17280
|
+
var runKbdLabel = isMacOs ? "\u2318\u23ce" : "Ctrl \u23ce";
|
|
16618
17281
|
function refreshRunKbdHint() {
|
|
16619
17282
|
if (!runButton) return;
|
|
16620
17283
|
var existing = runButton.querySelector(".demo-ai-kbd-hint");
|
|
@@ -16664,7 +17327,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16664
17327
|
|
|
16665
17328
|
var textGrid = append(grid, "div", "", "demo-ai-text-grid");
|
|
16666
17329
|
textGrid.setAttribute("role", "group");
|
|
16667
|
-
textGrid.setAttribute("aria-label", "Ask AI source and result");
|
|
17330
|
+
textGrid.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_srcresult", "Ask AI source and result") : "Ask AI source and result"));
|
|
16668
17331
|
|
|
16669
17332
|
// 2026-05-09 (v20260509j): dropped the "Source" / "Result"
|
|
16670
17333
|
// text-tag spans. They had no CSS rule (the class was
|
|
@@ -16682,7 +17345,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16682
17345
|
// Pre-result it was already hidden (CSS .is-pre-result cascade); the
|
|
16683
17346
|
// remaining showing case was post-result + source-expanded, but the
|
|
16684
17347
|
// user only reaches that flow by clicking the "Edit" toggle pill —
|
|
16685
|
-
// which already names the field's role ("Edit the prompt and retry").
|
|
17348
|
+
// which already names the field's role ((obj.__aiT ? obj.__aiT("ai2_editretry", "Edit the prompt and retry") : "Edit the prompt and retry")).
|
|
16686
17349
|
// Restating "Source" 4px above the textarea was a third copy of the
|
|
16687
17350
|
// same fact (toggle label + textarea placeholder + this eyebrow).
|
|
16688
17351
|
// Saves ~16px vertical when the user expands source for retry/refine.
|
|
@@ -16692,7 +17355,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16692
17355
|
var sourceHelp = append(sourceField, "span", "", "demo-ai-field-help is-detached", "Edit the source text or prompt that Ask AI will use.");
|
|
16693
17356
|
sourceHelp.id = sourceHelpId;
|
|
16694
17357
|
var sourceArea = append(sourceField, "textarea");
|
|
16695
|
-
sourceArea.setAttribute("aria-label", "Source text for Ask AI");
|
|
17358
|
+
sourceArea.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_srctext", "Source text for Ask AI") : "Source text for Ask AI"));
|
|
16696
17359
|
sourceArea.setAttribute("aria-describedby", sourceHelpId + " " + modeHelpId);
|
|
16697
17360
|
// 2026-05-26 (v20260526a): reparent the run-row (Generate + Copy
|
|
16698
17361
|
// buttons) from the compact-controls strip INTO the source field,
|
|
@@ -16743,7 +17406,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16743
17406
|
// verb phrase, no inline shortcut hint.
|
|
16744
17407
|
// Earlier baseline:
|
|
16745
17408
|
// v20260509j: 95 → 47 chars after the scope toggle landed.
|
|
16746
|
-
sourceArea.placeholder = "Type or paste text
|
|
17409
|
+
sourceArea.placeholder = (obj.__aiT ? obj.__aiT("aid_typeorpaste", "Type or paste text\u2026") : "Type or paste text\u2026");
|
|
16747
17410
|
|
|
16748
17411
|
// 2026-06-03 (v20260603a): live character counter pinned to the
|
|
16749
17412
|
// bottom-LEFT corner of the source textarea. Pre-pass users
|
|
@@ -16777,8 +17440,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16777
17440
|
return;
|
|
16778
17441
|
}
|
|
16779
17442
|
var label = len > 999
|
|
16780
|
-
? (Math.round(len / 100) / 10) + "K chars"
|
|
16781
|
-
:
|
|
17443
|
+
? (obj.__aiT ? obj.__aiT("aid_kchars", "{0}K chars", Math.round(len / 100) / 10) : (Math.round(len / 100) / 10) + "K chars")
|
|
17444
|
+
: (len === 1 ? (obj.__aiT ? obj.__aiT("aid_char", "1 char") : "1 char") : (obj.__aiT ? obj.__aiT("aid_chars", "{0} chars", len) : len + " chars"));
|
|
16782
17445
|
sourceCounter.textContent = label;
|
|
16783
17446
|
sourceCounter.classList.add("is-visible");
|
|
16784
17447
|
}
|
|
@@ -16832,9 +17495,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16832
17495
|
var copyClipboardButton = append(resultHead, "button", "", "demo-ai-clipboard-toggle");
|
|
16833
17496
|
copyClipboardButton.type = "button";
|
|
16834
17497
|
copyClipboardButton.setAttribute("data-rte-ai-dialog-action", "copy-clipboard");
|
|
16835
|
-
copyClipboardButton.title = "Copy AI output to clipboard";
|
|
16836
|
-
copyClipboardButton.setAttribute("aria-label", "Copy AI output to clipboard");
|
|
16837
|
-
setReviewV2ButtonContent(copyClipboardButton, "clipboard", "Copy");
|
|
17498
|
+
copyClipboardButton.title = (obj.__aiT ? obj.__aiT("ai2_copyclip", "Copy AI output to clipboard") : "Copy AI output to clipboard");
|
|
17499
|
+
copyClipboardButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_copyclip", "Copy AI output to clipboard") : "Copy AI output to clipboard"));
|
|
17500
|
+
setReviewV2ButtonContent(copyClipboardButton, "clipboard", (obj.__aiT ? obj.__aiT("ai2_copy", "Copy") : "Copy"));
|
|
16838
17501
|
var copyClipboardResetTimer = null;
|
|
16839
17502
|
copyClipboardButton.onclick = function (e) {
|
|
16840
17503
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
@@ -16846,13 +17509,13 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16846
17509
|
return;
|
|
16847
17510
|
}
|
|
16848
17511
|
copyClipboardButton.classList.add("is-copied");
|
|
16849
|
-
setReviewV2ButtonContent(copyClipboardButton, "check", "Copied");
|
|
17512
|
+
setReviewV2ButtonContent(copyClipboardButton, "check", (obj.__aiT ? obj.__aiT("aic_copied", "Copied") : "Copied"));
|
|
16850
17513
|
if (copyClipboardResetTimer) {
|
|
16851
17514
|
clearTimeout(copyClipboardResetTimer);
|
|
16852
17515
|
}
|
|
16853
17516
|
copyClipboardResetTimer = setTimeout(function () {
|
|
16854
17517
|
copyClipboardButton.classList.remove("is-copied");
|
|
16855
|
-
setReviewV2ButtonContent(copyClipboardButton, "clipboard", "Copy");
|
|
17518
|
+
setReviewV2ButtonContent(copyClipboardButton, "clipboard", (obj.__aiT ? obj.__aiT("ai2_copy", "Copy") : "Copy"));
|
|
16856
17519
|
copyClipboardResetTimer = null;
|
|
16857
17520
|
}, 1400);
|
|
16858
17521
|
});
|
|
@@ -16870,8 +17533,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16870
17533
|
var whyButton = append(resultHead, "button", "", "demo-ai-clipboard-toggle demo-ai-why-toggle");
|
|
16871
17534
|
whyButton.type = "button";
|
|
16872
17535
|
whyButton.setAttribute("data-rte-ai-dialog-action", "why");
|
|
16873
|
-
whyButton.title = "Why this AI suggestion?";
|
|
16874
|
-
whyButton.setAttribute("aria-label", "Show why this AI suggestion was generated");
|
|
17536
|
+
whyButton.title = (obj.__aiT ? obj.__aiT("ai2_whytitle", "Why this AI suggestion?") : "Why this AI suggestion?");
|
|
17537
|
+
whyButton.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_whyaria", "Show why this AI suggestion was generated") : "Show why this AI suggestion was generated"));
|
|
16875
17538
|
whyButton.setAttribute("aria-expanded", "false");
|
|
16876
17539
|
setReviewV2ButtonContent(whyButton, "info", "");
|
|
16877
17540
|
|
|
@@ -16890,12 +17553,12 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16890
17553
|
// saying the same thing twice (open the prompt + edit the prompt).
|
|
16891
17554
|
// "Edit" preserves the verb-action shape Pass v20260513b adopted,
|
|
16892
17555
|
// saves ~30px horizontal real estate in the result-head pill row,
|
|
16893
|
-
// and the title attribute ("Edit the prompt and retry") still
|
|
17556
|
+
// and the title attribute ((obj.__aiT ? obj.__aiT("ai2_editretry", "Edit the prompt and retry") : "Edit the prompt and retry")) still
|
|
16894
17557
|
// surfaces the long form on hover + to screen readers via
|
|
16895
17558
|
// aria-label.
|
|
16896
|
-
sourceToggle.title = "Edit the prompt and retry";
|
|
16897
|
-
sourceToggle.setAttribute("aria-label", "Edit the prompt and retry");
|
|
16898
|
-
sourceToggle.innerText = "Edit";
|
|
17559
|
+
sourceToggle.title = (obj.__aiT ? obj.__aiT("ai2_editretry", "Edit the prompt and retry") : "Edit the prompt and retry");
|
|
17560
|
+
sourceToggle.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_editretry", "Edit the prompt and retry") : "Edit the prompt and retry"));
|
|
17561
|
+
sourceToggle.innerText = (obj.__aiT ? obj.__aiT("ai2_edit", "Edit") : "Edit");
|
|
16899
17562
|
sourceField.id = sourceToggle.getAttribute("aria-controls");
|
|
16900
17563
|
sourceToggle.onclick = function (e) {
|
|
16901
17564
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
@@ -16905,7 +17568,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16905
17568
|
};
|
|
16906
17569
|
var resultArea = append(resultField, "textarea");
|
|
16907
17570
|
resultArea.readOnly = true;
|
|
16908
|
-
resultArea.setAttribute("aria-label", "AI suggestion result");
|
|
17571
|
+
resultArea.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_result", "AI suggestion result") : "AI suggestion result"));
|
|
16909
17572
|
resultArea.setAttribute("aria-describedby", resultHelpId + " " + statusId);
|
|
16910
17573
|
// 2026-05-17 (v20260520b): rows=1 lets the CSS min-height: 52px
|
|
16911
17574
|
// floor (since v20260513a) actually bind. Browser default rows=2
|
|
@@ -16921,7 +17584,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16921
17584
|
// here." (20 chars). Same idiom Notion AI's inline composer
|
|
16922
17585
|
// uses ("AI response..."). At 20 chars it never line-wraps
|
|
16923
17586
|
// inside the textarea even at 320px viewport.
|
|
16924
|
-
resultArea.placeholder = "Output appears here.";
|
|
17587
|
+
resultArea.placeholder = (obj.__aiT ? obj.__aiT("ai2_outputph", "Output appears here.") : "Output appears here.");
|
|
16925
17588
|
|
|
16926
17589
|
var oldPreview = document.createElement("pre");
|
|
16927
17590
|
var newPreview = document.createElement("pre");
|
|
@@ -16970,19 +17633,19 @@ function RTE_Plugin_AIToolkit() {
|
|
|
16970
17633
|
// setBusyState / focus-traversal walker keeps resolving.
|
|
16971
17634
|
var refineRetryChip = append(refineRow, "button", "", "secondary demo-ai-refine-chip");
|
|
16972
17635
|
refineRetryChip.type = "button";
|
|
16973
|
-
refineRetryChip.title = "Re-run with the same prompt";
|
|
17636
|
+
refineRetryChip.title = (obj.__aiT ? obj.__aiT("ai2_retrytitle", "Re-run with the same prompt") : "Re-run with the same prompt");
|
|
16974
17637
|
refineRetryChip.setAttribute("data-rte-ai-dialog-action", "refine-retry");
|
|
16975
|
-
setReviewV2ButtonContent(refineRetryChip, "refresh", "Try again");
|
|
17638
|
+
setReviewV2ButtonContent(refineRetryChip, "refresh", (obj.__aiT ? obj.__aiT("ai2_tryagain", "Try again") : "Try again"));
|
|
16976
17639
|
var refineShorterChip = append(refineRow, "button", "", "secondary demo-ai-refine-chip");
|
|
16977
17640
|
refineShorterChip.type = "button";
|
|
16978
|
-
refineShorterChip.title = "Make the suggestion shorter";
|
|
17641
|
+
refineShorterChip.title = (obj.__aiT ? obj.__aiT("ai2_shortertitle", "Make the suggestion shorter") : "Make the suggestion shorter");
|
|
16979
17642
|
refineShorterChip.setAttribute("data-rte-ai-dialog-action", "refine-shorter");
|
|
16980
|
-
setReviewV2ButtonContent(refineShorterChip, "shrink", "Shorter");
|
|
17643
|
+
setReviewV2ButtonContent(refineShorterChip, "shrink", (obj.__aiT ? obj.__aiT("ai2_shorter", "Shorter") : "Shorter"));
|
|
16981
17644
|
var refineLongerChip = append(refineRow, "button", "", "secondary demo-ai-refine-chip");
|
|
16982
17645
|
refineLongerChip.type = "button";
|
|
16983
|
-
refineLongerChip.title = "Expand the suggestion with more detail";
|
|
17646
|
+
refineLongerChip.title = (obj.__aiT ? obj.__aiT("ai2_longertitle", "Expand the suggestion with more detail") : "Expand the suggestion with more detail");
|
|
16984
17647
|
refineLongerChip.setAttribute("data-rte-ai-dialog-action", "refine-longer");
|
|
16985
|
-
setReviewV2ButtonContent(refineLongerChip, "expand", "Longer");
|
|
17648
|
+
setReviewV2ButtonContent(refineLongerChip, "expand", (obj.__aiT ? obj.__aiT("ai2_longer", "Longer") : "Longer"));
|
|
16986
17649
|
// 2026-05-29 (v20260529a): "Why" disclosure folded into the refine
|
|
16987
17650
|
// chip row as a 4th ghost chip — the standalone Details toggle row
|
|
16988
17651
|
// that previously sat on its own line between the chip strip and
|
|
@@ -17029,7 +17692,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17029
17692
|
refineWhyChip.type = "button";
|
|
17030
17693
|
refineWhyChip.className = "secondary demo-ai-refine-chip demo-ai-refine-why-chip is-detached";
|
|
17031
17694
|
refineWhyChip.innerText = "Why";
|
|
17032
|
-
refineWhyChip.title = "Show why this AI suggestion was generated";
|
|
17695
|
+
refineWhyChip.title = (obj.__aiT ? obj.__aiT("ai2_whyaria", "Show why this AI suggestion was generated") : "Show why this AI suggestion was generated");
|
|
17033
17696
|
refineWhyChip.setAttribute("data-rte-ai-dialog-action", "refine-why");
|
|
17034
17697
|
refineWhyChip.setAttribute("aria-expanded", "false");
|
|
17035
17698
|
|
|
@@ -17102,7 +17765,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17102
17765
|
|
|
17103
17766
|
var applyRow = append(grid, "div", "", "demo-actions-row demo-ai-apply-row");
|
|
17104
17767
|
applyRow.setAttribute("role", "group");
|
|
17105
|
-
applyRow.setAttribute("aria-label", "Ask AI apply actions");
|
|
17768
|
+
applyRow.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_applyrow", "Ask AI apply actions") : "Ask AI apply actions"));
|
|
17106
17769
|
var applyPlanButton = append(applyRow, "button", "", "is-primary");
|
|
17107
17770
|
applyPlanButton.type = "button";
|
|
17108
17771
|
applyPlanButton.disabled = true;
|
|
@@ -17114,34 +17777,34 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17114
17777
|
// dialog-level keydown handler (added below) fires this button
|
|
17115
17778
|
// when the shortcut hits from anywhere in the dialog outside the
|
|
17116
17779
|
// source textarea.
|
|
17117
|
-
applyPlanButton.title = "Apply AI suggestion (Ctrl+Enter)";
|
|
17780
|
+
applyPlanButton.title = (obj.__aiT ? obj.__aiT("ai2_applytitle", "Apply AI suggestion (Ctrl+Enter)") : "Apply AI suggestion (Ctrl+Enter)");
|
|
17118
17781
|
applyPlanButton.setAttribute("aria-keyshortcuts", "Control+Enter");
|
|
17119
|
-
setReviewV2ButtonContent(applyPlanButton, "check", "Apply");
|
|
17782
|
+
setReviewV2ButtonContent(applyPlanButton, "check", (obj.__aiT ? obj.__aiT("ai2_apply", "Apply") : "Apply"));
|
|
17120
17783
|
var acceptSelectionButton = append(applyRow, "button", "", "");
|
|
17121
17784
|
acceptSelectionButton.type = "button";
|
|
17122
17785
|
acceptSelectionButton.disabled = !snapshot.hasSelection;
|
|
17123
17786
|
acceptSelectionButton.setAttribute("data-rte-ai-dialog-action", "selection");
|
|
17124
|
-
setReviewV2ButtonContent(acceptSelectionButton, "check", "Replace selection");
|
|
17787
|
+
setReviewV2ButtonContent(acceptSelectionButton, "check", (obj.__aiT ? obj.__aiT("ai2_replsel", "Replace selection") : "Replace selection"));
|
|
17125
17788
|
var previewSelectionButton = append(applyRow, "button", "", "secondary");
|
|
17126
17789
|
previewSelectionButton.type = "button";
|
|
17127
17790
|
previewSelectionButton.disabled = !snapshot.hasSelection;
|
|
17128
17791
|
previewSelectionButton.setAttribute("data-rte-ai-dialog-action", "preview");
|
|
17129
|
-
setReviewV2ButtonContent(previewSelectionButton, "eyeOn", "Preview");
|
|
17792
|
+
setReviewV2ButtonContent(previewSelectionButton, "eyeOn", (obj.__aiT ? obj.__aiT("ai2_preview", "Preview") : "Preview"));
|
|
17130
17793
|
var acceptBelowButton = append(applyRow, "button", "", "secondary");
|
|
17131
17794
|
acceptBelowButton.type = "button";
|
|
17132
17795
|
acceptBelowButton.setAttribute("data-rte-ai-dialog-action", "insert");
|
|
17133
|
-
setReviewV2ButtonContent(acceptBelowButton, "arrowRight", "Insert below");
|
|
17796
|
+
setReviewV2ButtonContent(acceptBelowButton, "arrowRight", (obj.__aiT ? obj.__aiT("ai2_insbelow", "Insert below") : "Insert below"));
|
|
17134
17797
|
var acceptDocumentButton = append(applyRow, "button", "", "secondary");
|
|
17135
17798
|
acceptDocumentButton.type = "button";
|
|
17136
17799
|
acceptDocumentButton.setAttribute("data-rte-ai-dialog-action", "document");
|
|
17137
|
-
// 2026-05-20 (v20260520h): "Replace doc" → "Replace document". The
|
|
17800
|
+
// 2026-05-20 (v20260520h): "Replace doc" → (obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document"). The
|
|
17138
17801
|
// abbreviation read as developer shorthand; the full word costs ~28px
|
|
17139
17802
|
// of horizontal real estate at the apply row, but this button only
|
|
17140
17803
|
// appears behind the ⋯ More toggle so the row's spare space is
|
|
17141
17804
|
// available. Vocabulary now matches getOperationPlanButtonLabel's
|
|
17142
|
-
// primary path ("Replace document") and the chat panel's secondary
|
|
17143
|
-
// button ("Replace document").
|
|
17144
|
-
setReviewV2ButtonContent(acceptDocumentButton, "checkAll", "Replace document");
|
|
17805
|
+
// primary path ((obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document")) and the chat panel's secondary
|
|
17806
|
+
// button ((obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document")).
|
|
17807
|
+
setReviewV2ButtonContent(acceptDocumentButton, "checkAll", (obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document"));
|
|
17145
17808
|
// 2026-05-08 (v20260508t): "More" toggle pinned just before Discard.
|
|
17146
17809
|
// Hides Replace selection / Preview / Insert below / Replace doc by
|
|
17147
17810
|
// default so the default surface only shows Apply (smart primary) +
|
|
@@ -17165,8 +17828,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17165
17828
|
applyRowMoreToggle.type = "button";
|
|
17166
17829
|
applyRowMoreToggle.setAttribute("data-rte-ai-dialog-action", "more");
|
|
17167
17830
|
applyRowMoreToggle.setAttribute("aria-expanded", "false");
|
|
17168
|
-
applyRowMoreToggle.title = "More apply options";
|
|
17169
|
-
applyRowMoreToggle.setAttribute("aria-label", "More apply options");
|
|
17831
|
+
applyRowMoreToggle.title = (obj.__aiT ? obj.__aiT("ai2_moreopts", "More apply options") : "More apply options");
|
|
17832
|
+
applyRowMoreToggle.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("ai2_moreopts", "More apply options") : "More apply options"));
|
|
17170
17833
|
setReviewV2ButtonContent(applyRowMoreToggle, "more", "");
|
|
17171
17834
|
applyRowMoreToggle.onclick = function (e) {
|
|
17172
17835
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
@@ -17225,7 +17888,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17225
17888
|
applyGuidance.className = "demo-ai-apply-guidance";
|
|
17226
17889
|
applyGuidance.style.display = "none";
|
|
17227
17890
|
applyGuidance.setAttribute("role", "group");
|
|
17228
|
-
applyGuidance.setAttribute("aria-label", "Best next step");
|
|
17891
|
+
applyGuidance.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_bestnext", "Best next step") : "Best next step"));
|
|
17229
17892
|
var applyGuidanceHeader = append(applyGuidance, "div", "", "demo-ai-apply-guidance-header");
|
|
17230
17893
|
var applyGuidanceTitle = append(applyGuidanceHeader, "span", "", "demo-ai-apply-guidance-title", "Next");
|
|
17231
17894
|
var applyGuidanceBadge = append(applyGuidanceHeader, "span", "", "demo-ai-apply-guidance-badge");
|
|
@@ -17405,7 +18068,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17405
18068
|
var languages = getTranslateLanguages();
|
|
17406
18069
|
languageSelect.innerHTML = "";
|
|
17407
18070
|
for (var index = 0; index < languages.length; index++) {
|
|
17408
|
-
var option = append(languageSelect, "option", "", "", languages[index]
|
|
18071
|
+
var option = append(languageSelect, "option", "", "", getTranslateLanguageDisplay(languages[index]));
|
|
17409
18072
|
option.value = languages[index].value || languages[index].label;
|
|
17410
18073
|
}
|
|
17411
18074
|
if (options.presetLanguage) {
|
|
@@ -17481,7 +18144,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17481
18144
|
else {
|
|
17482
18145
|
scopeLabel = "Document";
|
|
17483
18146
|
}
|
|
17484
|
-
return scopeLabel + "
|
|
18147
|
+
return scopeLabel + " \xb7 " + countLabel + " chars";
|
|
17485
18148
|
}
|
|
17486
18149
|
|
|
17487
18150
|
// 2026-05-19 (v20260519a): mirror the active AI mode into the
|
|
@@ -17588,7 +18251,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17588
18251
|
}
|
|
17589
18252
|
titleSpan.innerText = resolvedLabel || base;
|
|
17590
18253
|
if (dialoginner.setAttribute) {
|
|
17591
|
-
dialoginner.setAttribute("aria-label", resolvedLabel ? (base + "
|
|
18254
|
+
dialoginner.setAttribute("aria-label", resolvedLabel ? (base + " \xb7 " + resolvedLabel) : base);
|
|
17592
18255
|
}
|
|
17593
18256
|
}
|
|
17594
18257
|
|
|
@@ -17674,16 +18337,16 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17674
18337
|
if (sourceArea && !sourceArea.value) {
|
|
17675
18338
|
var modeId = modeSelect.value || "";
|
|
17676
18339
|
var placeholders = {
|
|
17677
|
-
proofread: "Paste text to proofread
|
|
17678
|
-
rewrite: "Paste text to rewrite
|
|
17679
|
-
shorten: "Paste text to shorten
|
|
17680
|
-
expand: "Paste text to expand
|
|
17681
|
-
summarize: "Paste text to summarize
|
|
17682
|
-
translate: "Paste text to translate
|
|
17683
|
-
justify: "Paste text to rewrite + explain
|
|
17684
|
-
explain: "Paste text to explain
|
|
18340
|
+
proofread: "Paste text to proofread\u2026",
|
|
18341
|
+
rewrite: "Paste text to rewrite\u2026",
|
|
18342
|
+
shorten: "Paste text to shorten\u2026",
|
|
18343
|
+
expand: "Paste text to expand\u2026",
|
|
18344
|
+
summarize: "Paste text to summarize\u2026",
|
|
18345
|
+
translate: "Paste text to translate\u2026",
|
|
18346
|
+
justify: "Paste text to rewrite + explain\u2026",
|
|
18347
|
+
explain: "Paste text to explain\u2026"
|
|
17685
18348
|
};
|
|
17686
|
-
sourceArea.placeholder = placeholders[modeId] || "Type or paste text
|
|
18349
|
+
sourceArea.placeholder = placeholders[modeId] || "Type or paste text\u2026";
|
|
17687
18350
|
}
|
|
17688
18351
|
// 2026-05-08 (v20260508s): refresh the header mode-name chip.
|
|
17689
18352
|
// For Translate the chip carries the target language so users
|
|
@@ -17691,7 +18354,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17691
18354
|
if (modeChip) {
|
|
17692
18355
|
var chipLabel = modeLabel || "";
|
|
17693
18356
|
if (modeLabel && modeSelect.value === "translate") {
|
|
17694
|
-
chipLabel = modeLabel + "
|
|
18357
|
+
chipLabel = modeLabel + " \xb7 " + getTranslateLanguageLabel(languageSelect.value || "spanish");
|
|
17695
18358
|
}
|
|
17696
18359
|
modeChip.innerText = chipLabel;
|
|
17697
18360
|
modeChip.style.display = chipLabel ? "" : "none";
|
|
@@ -17860,9 +18523,9 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17860
18523
|
var docChars = snap ? (snap.wholeText || "").length : 0;
|
|
17861
18524
|
scopeOptionSelection.disabled = !hasSelection;
|
|
17862
18525
|
scopeOptionSelection.text = hasSelection
|
|
17863
|
-
? "Selection"
|
|
17864
|
-
: "Selection (none)";
|
|
17865
|
-
scopeOptionDocument.text = "Document";
|
|
18526
|
+
? (obj.__aiT ? obj.__aiT("aid_selection", "Selection") : "Selection")
|
|
18527
|
+
: (obj.__aiT ? obj.__aiT("aid_selectionnone", "Selection (none)") : "Selection (none)");
|
|
18528
|
+
scopeOptionDocument.text = (obj.__aiT ? obj.__aiT("aid_document", "Document") : "Document");
|
|
17866
18529
|
scopeSelect.value = activeScope === "selection" ? "selection" : "document";
|
|
17867
18530
|
scopeSelect.setAttribute("title", buildDialogScopeHint(snap, scope));
|
|
17868
18531
|
// 2026-06-01 (v20260601a): swap the <select> for a quiet inline
|
|
@@ -17877,7 +18540,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17877
18540
|
// 2026-05-20 (v20260520i) — Pass 61: caption mirrors the option
|
|
17878
18541
|
// label — scope name only, no char-count suffix.
|
|
17879
18542
|
if (scopeCaption) {
|
|
17880
|
-
scopeCaption.innerText = "Document";
|
|
18543
|
+
scopeCaption.innerText = (obj.__aiT ? obj.__aiT("aid_document", "Document") : "Document");
|
|
17881
18544
|
}
|
|
17882
18545
|
if (compactControls && compactControls.classList) {
|
|
17883
18546
|
if (hasSelection) {
|
|
@@ -17892,8 +18555,8 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17892
18555
|
|
|
17893
18556
|
function formatDialogScopeCount(n) {
|
|
17894
18557
|
if (!n) return "0";
|
|
17895
|
-
if (n > 999) return (Math.round(n / 100) / 10) + "K chars";
|
|
17896
|
-
return n + " chars";
|
|
18558
|
+
if (n > 999) return (obj.__aiT ? obj.__aiT("aid_kchars", "{0}K chars", Math.round(n / 100) / 10) : (Math.round(n / 100) / 10) + "K chars");
|
|
18559
|
+
return obj.__aiT ? obj.__aiT("aid_chars", "{0} chars", n) : n + " chars";
|
|
17897
18560
|
}
|
|
17898
18561
|
|
|
17899
18562
|
function markDialogSourceDetached() {
|
|
@@ -17940,11 +18603,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
17940
18603
|
}
|
|
17941
18604
|
|
|
17942
18605
|
function getDialogLanguageChangeMessage(language) {
|
|
17943
|
-
return "Language
|
|
18606
|
+
return "Language \u2192 " + getTranslateLanguageLabel(language || "spanish") + ". Re-run to refresh.";
|
|
17944
18607
|
}
|
|
17945
18608
|
|
|
17946
18609
|
function getDialogScopeChangeMessage(scope) {
|
|
17947
|
-
return "Scope
|
|
18610
|
+
return "Scope \u2192 " + (scope === "selection" ? "selection" : "document") + ". Re-run to refresh.";
|
|
17948
18611
|
}
|
|
17949
18612
|
|
|
17950
18613
|
function getDialogEditorReloadMessage(scope) {
|
|
@@ -18029,70 +18692,70 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18029
18692
|
: "Select text in the editor, then reload it into Ask AI.";
|
|
18030
18693
|
}
|
|
18031
18694
|
if (actionId === "load-document") {
|
|
18032
|
-
return "Reload the current editor document into Ask AI.";
|
|
18695
|
+
return (obj.__aiT ? obj.__aiT("aid_reloaddesc", "Reload the current editor document into Ask AI.") : "Reload the current editor document into Ask AI.");
|
|
18033
18696
|
}
|
|
18034
18697
|
if (actionId === "run") {
|
|
18035
18698
|
return actionState.hasSource
|
|
18036
|
-
? "Generate a new AI suggestion from the current source text using the selected mode."
|
|
18037
|
-
: "Load or type source text, then run Ask AI to generate a suggestion.";
|
|
18699
|
+
? (obj.__aiT ? obj.__aiT("aid_gendesc", "Generate a new AI suggestion from the current source text using the selected mode.") : "Generate a new AI suggestion from the current source text using the selected mode.")
|
|
18700
|
+
: (obj.__aiT ? obj.__aiT("aid_loaddesc", "Load or type source text, then run Ask AI to generate a suggestion.") : "Load or type source text, then run Ask AI to generate a suggestion.");
|
|
18038
18701
|
}
|
|
18039
18702
|
if (actionId === "rerun") {
|
|
18040
18703
|
return actionState.staleAction === "source"
|
|
18041
|
-
? "Refresh this suggestion from the current dialog source before applying it."
|
|
18042
|
-
: "Refresh this suggestion from the current editor state before applying it.";
|
|
18704
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_refreshsrc", "Refresh this suggestion from the current dialog source before applying it.") : "Refresh this suggestion from the current dialog source before applying it.")
|
|
18705
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_refreshed", "Refresh this suggestion from the current editor state before applying it.") : "Refresh this suggestion from the current editor state before applying it.");
|
|
18043
18706
|
}
|
|
18044
18707
|
if (actionId === "copy-to-source") {
|
|
18045
18708
|
return actionState.hasResult
|
|
18046
|
-
? "Copy the current AI result back into the source field before rerunning it."
|
|
18047
|
-
: "Run Ask AI first, then copy the generated result back into the source field.";
|
|
18709
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_copyback", "Copy the current AI result back into the source field before rerunning it.") : "Copy the current AI result back into the source field before rerunning it.")
|
|
18710
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_copyfirst", "Run Ask AI first, then copy the generated result back into the source field.") : "Run Ask AI first, then copy the generated result back into the source field.");
|
|
18048
18711
|
}
|
|
18049
18712
|
if (actionId === "apply") {
|
|
18050
18713
|
if (actionState.pendingIndexes.length > 1) {
|
|
18051
18714
|
return actionState.pendingSelectionDependent && !actionState.hasSelection
|
|
18052
|
-
? "Select text before applying this selection-based AI plan."
|
|
18053
|
-
: "Run the remaining " + actionState.pendingIndexes.length + "-step AI plan directly in the editor.";
|
|
18715
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_selectplan", "Select text before applying this selection-based AI plan.") : "Select text before applying this selection-based AI plan.")
|
|
18716
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_steps", "Run the remaining {0}-step AI plan directly in the editor.", actionState.pendingIndexes.length) : "Run the remaining " + actionState.pendingIndexes.length + "-step AI plan directly in the editor.");
|
|
18054
18717
|
}
|
|
18055
18718
|
switch (actionState.pendingOperationType) {
|
|
18056
18719
|
case "preview-suggestion":
|
|
18057
18720
|
return actionState.hasSelection
|
|
18058
|
-
? "Show the prepared inline diff in the editor before deciding it."
|
|
18059
|
-
: "Select text before previewing this prepared inline diff in the editor.";
|
|
18721
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_showdiff", "Show the prepared inline diff in the editor before deciding it.") : "Show the prepared inline diff in the editor before deciding it.")
|
|
18722
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_selectdiff", "Select text before previewing this prepared inline diff in the editor.") : "Select text before previewing this prepared inline diff in the editor.");
|
|
18060
18723
|
case "replace-selection":
|
|
18061
18724
|
return actionState.hasSelection
|
|
18062
|
-
? "Replace the current selection with this prepared draft."
|
|
18063
|
-
: "Select text before replacing it with this prepared draft.";
|
|
18725
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_replsel", "Replace the current selection with this prepared draft.") : "Replace the current selection with this prepared draft.")
|
|
18726
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_selectrepl", "Select text before replacing it with this prepared draft.") : "Select text before replacing it with this prepared draft.");
|
|
18064
18727
|
case "replace-document":
|
|
18065
|
-
return "Replace the full document with this prepared draft.";
|
|
18728
|
+
return (obj.__aiT ? obj.__aiT("ai2_d_repldoc", "Replace the full document with this prepared draft.") : "Replace the full document with this prepared draft.");
|
|
18066
18729
|
case "insert-below":
|
|
18067
18730
|
return actionState.hasSelection
|
|
18068
|
-
? "Insert this prepared draft below the current selection."
|
|
18069
|
-
: "Insert this prepared draft below the current content.";
|
|
18731
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_insbelowsel", "Insert this prepared draft below the current selection.") : "Insert this prepared draft below the current selection.")
|
|
18732
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_insbelow", "Insert this prepared draft below the current content.") : "Insert this prepared draft below the current content.");
|
|
18070
18733
|
case "add-comment":
|
|
18071
|
-
return "Add this prepared AI comment without rewriting the document body.";
|
|
18734
|
+
return (obj.__aiT ? obj.__aiT("ai2_d_comment", "Add this prepared AI comment without rewriting the document body.") : "Add this prepared AI comment without rewriting the document body.");
|
|
18072
18735
|
default:
|
|
18073
|
-
return "Run the prepared AI plan directly in the editor.";
|
|
18736
|
+
return (obj.__aiT ? obj.__aiT("ai2_d_runplan", "Run the prepared AI plan directly in the editor.") : "Run the prepared AI plan directly in the editor.");
|
|
18074
18737
|
}
|
|
18075
18738
|
}
|
|
18076
18739
|
if (actionId === "selection") {
|
|
18077
18740
|
return actionState.hasSelection
|
|
18078
|
-
? "Replace the current selection with this prepared draft."
|
|
18079
|
-
: "Select text before replacing it with this prepared draft.";
|
|
18741
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_replsel", "Replace the current selection with this prepared draft.") : "Replace the current selection with this prepared draft.")
|
|
18742
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_selectrepl", "Select text before replacing it with this prepared draft.") : "Select text before replacing it with this prepared draft.");
|
|
18080
18743
|
}
|
|
18081
18744
|
if (actionId === "preview") {
|
|
18082
18745
|
return actionState.hasSelection
|
|
18083
|
-
? "Show this change inline in the editor before deciding it."
|
|
18084
|
-
: "Select text to preview this change inline in the editor.";
|
|
18746
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_previewsel", "Show this change inline in the editor before deciding it.") : "Show this change inline in the editor before deciding it.")
|
|
18747
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_previewselect", "Select text to preview this change inline in the editor.") : "Select text to preview this change inline in the editor.");
|
|
18085
18748
|
}
|
|
18086
18749
|
if (actionId === "insert") {
|
|
18087
18750
|
return actionState.hasSelection
|
|
18088
|
-
? "Insert this prepared draft below the current selection."
|
|
18089
|
-
: "Insert this prepared draft below the current content.";
|
|
18751
|
+
? (obj.__aiT ? obj.__aiT("ai2_d_insbelowsel", "Insert this prepared draft below the current selection.") : "Insert this prepared draft below the current selection.")
|
|
18752
|
+
: (obj.__aiT ? obj.__aiT("ai2_d_insbelow", "Insert this prepared draft below the current content.") : "Insert this prepared draft below the current content.");
|
|
18090
18753
|
}
|
|
18091
18754
|
if (actionId === "document") {
|
|
18092
|
-
return "Replace the full document with this prepared draft.";
|
|
18755
|
+
return (obj.__aiT ? obj.__aiT("ai2_d_repldoc", "Replace the full document with this prepared draft.") : "Replace the full document with this prepared draft.");
|
|
18093
18756
|
}
|
|
18094
18757
|
if (actionId === "reject") {
|
|
18095
|
-
return "Dismiss this prepared suggestion without changing the document.";
|
|
18758
|
+
return (obj.__aiT ? obj.__aiT("ai2_d_dismiss", "Dismiss this prepared suggestion without changing the document.") : "Dismiss this prepared suggestion without changing the document.");
|
|
18096
18759
|
}
|
|
18097
18760
|
return "";
|
|
18098
18761
|
}
|
|
@@ -18180,22 +18843,22 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18180
18843
|
var pendingOperationType = actionState.pendingOperationType || "";
|
|
18181
18844
|
if (actionState.actionScope === "selection") {
|
|
18182
18845
|
if (actionState.hasSelection && pendingOperationType !== "preview-suggestion") {
|
|
18183
|
-
addDialogReadyAlternative(alternatives, covered, "preview", "preview inline");
|
|
18846
|
+
addDialogReadyAlternative(alternatives, covered, "preview", (obj.__aiT ? obj.__aiT("ai2_alt_preview", "preview inline") : "preview inline"));
|
|
18184
18847
|
}
|
|
18185
18848
|
if (actionState.hasSelection && pendingOperationType !== "replace-selection") {
|
|
18186
|
-
addDialogReadyAlternative(alternatives, covered, "selection", "replace the selection");
|
|
18849
|
+
addDialogReadyAlternative(alternatives, covered, "selection", (obj.__aiT ? obj.__aiT("ai2_alt_selection", "replace the selection") : "replace the selection"));
|
|
18187
18850
|
}
|
|
18188
18851
|
if (actionState.canInsert && pendingOperationType !== "insert-below") {
|
|
18189
|
-
addDialogReadyAlternative(alternatives, covered, "insert", "insert below");
|
|
18852
|
+
addDialogReadyAlternative(alternatives, covered, "insert", (obj.__aiT ? obj.__aiT("ai2_alt_insert", "insert below") : "insert below"));
|
|
18190
18853
|
}
|
|
18191
18854
|
return alternatives;
|
|
18192
18855
|
}
|
|
18193
18856
|
if (actionState.actionScope === "document") {
|
|
18194
18857
|
if (actionState.canReplaceDocument && pendingOperationType !== "replace-document") {
|
|
18195
|
-
addDialogReadyAlternative(alternatives, covered, "document", "replace the document");
|
|
18858
|
+
addDialogReadyAlternative(alternatives, covered, "document", (obj.__aiT ? obj.__aiT("ai2_alt_document", "replace the document") : "replace the document"));
|
|
18196
18859
|
}
|
|
18197
18860
|
if (actionState.canInsert && pendingOperationType !== "insert-below") {
|
|
18198
|
-
addDialogReadyAlternative(alternatives, covered, "insert", "insert below");
|
|
18861
|
+
addDialogReadyAlternative(alternatives, covered, "insert", (obj.__aiT ? obj.__aiT("ai2_alt_insert", "insert below") : "insert below"));
|
|
18199
18862
|
}
|
|
18200
18863
|
}
|
|
18201
18864
|
return alternatives;
|
|
@@ -18213,10 +18876,10 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18213
18876
|
? (recommended.label || "").replace(/\s+/g, " ").trim()
|
|
18214
18877
|
: "";
|
|
18215
18878
|
if (recommendedLabel) {
|
|
18216
|
-
return "Ready
|
|
18879
|
+
return "Ready \xb7 " + recommendedLabel;
|
|
18217
18880
|
}
|
|
18218
18881
|
if (actionState.actionScope === "selection" && !actionState.hasSelection) {
|
|
18219
|
-
return "Ready
|
|
18882
|
+
return "Ready \xb7 Insert below";
|
|
18220
18883
|
}
|
|
18221
18884
|
return "Ready";
|
|
18222
18885
|
}
|
|
@@ -18294,25 +18957,25 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18294
18957
|
parts.push(detail);
|
|
18295
18958
|
}
|
|
18296
18959
|
if (alternativeText) {
|
|
18297
|
-
parts.push("Other available actions: " + alternativeText + ".");
|
|
18960
|
+
parts.push(obj.__aiT ? obj.__aiT("aid_otheractions", "Other available actions: {0}.", alternativeText) : "Other available actions: " + alternativeText + ".");
|
|
18298
18961
|
}
|
|
18299
18962
|
if (isRecommended) {
|
|
18300
|
-
parts.push("Recommended next step.");
|
|
18963
|
+
parts.push((obj.__aiT ? obj.__aiT("aid_recommended", "Recommended next step.") : "Recommended next step."));
|
|
18301
18964
|
button.setAttribute("data-rte-ai-dialog-recommended", "true");
|
|
18302
18965
|
}
|
|
18303
18966
|
else {
|
|
18304
18967
|
button.removeAttribute("data-rte-ai-dialog-recommended");
|
|
18305
18968
|
}
|
|
18306
|
-
button.setAttribute("aria-label", parts
|
|
18969
|
+
button.setAttribute("aria-label", joinSentences(parts));
|
|
18307
18970
|
var titleParts = [label];
|
|
18308
18971
|
if (detail) {
|
|
18309
18972
|
titleParts.push(detail);
|
|
18310
18973
|
}
|
|
18311
18974
|
if (alternativeText) {
|
|
18312
|
-
titleParts.push("Other available actions: " + alternativeText + ".");
|
|
18975
|
+
titleParts.push(obj.__aiT ? obj.__aiT("aid_otheractions", "Other available actions: {0}.", alternativeText) : "Other available actions: " + alternativeText + ".");
|
|
18313
18976
|
}
|
|
18314
18977
|
if (isRecommended) {
|
|
18315
|
-
titleParts.push("Recommended next step.");
|
|
18978
|
+
titleParts.push((obj.__aiT ? obj.__aiT("aid_recommended", "Recommended next step.") : "Recommended next step."));
|
|
18316
18979
|
}
|
|
18317
18980
|
// 2026-05-22 (v20260522a): preserve any pre-set keyboard shortcut
|
|
18318
18981
|
// hint on the button by reading aria-keyshortcuts (the WAI-ARIA
|
|
@@ -18322,7 +18985,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18322
18985
|
// shortcut would only be discoverable via screen-reader
|
|
18323
18986
|
// announcement of aria-keyshortcuts and never via mouse hover.
|
|
18324
18987
|
// Formatting matches the Generate button's pre-existing title
|
|
18325
|
-
// suffix ("Generate AI suggestion (Ctrl+Enter)").
|
|
18988
|
+
// suffix ((obj.__aiT ? obj.__aiT("ai2_gentitle", "Generate AI suggestion (Ctrl+Enter)") : "Generate AI suggestion (Ctrl+Enter)")).
|
|
18326
18989
|
var keyHint = button.getAttribute && button.getAttribute("aria-keyshortcuts");
|
|
18327
18990
|
if (keyHint) {
|
|
18328
18991
|
var keyHintLabel = keyHint
|
|
@@ -18379,7 +19042,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18379
19042
|
return {
|
|
18380
19043
|
actionId: "run",
|
|
18381
19044
|
button: runButton,
|
|
18382
|
-
label: "Ask AI",
|
|
19045
|
+
label: (obj.__aiT ? obj.__aiT("aiassist", "Ask AI") : "Ask AI"),
|
|
18383
19046
|
detail: getDialogActionDetail("run", actionState)
|
|
18384
19047
|
};
|
|
18385
19048
|
}
|
|
@@ -18411,7 +19074,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18411
19074
|
return {
|
|
18412
19075
|
actionId: "document",
|
|
18413
19076
|
button: acceptDocumentButton,
|
|
18414
|
-
label: (acceptDocumentButton.innerText || acceptDocumentButton.textContent || "").replace(/\s+/g, " ").trim() || "Replace document",
|
|
19077
|
+
label: (acceptDocumentButton.innerText || acceptDocumentButton.textContent || "").replace(/\s+/g, " ").trim() || (obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document"),
|
|
18415
19078
|
detail: getDialogActionDetail("document", actionState)
|
|
18416
19079
|
};
|
|
18417
19080
|
}
|
|
@@ -18464,11 +19127,11 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18464
19127
|
return;
|
|
18465
19128
|
}
|
|
18466
19129
|
applyGuidance.style.display = "";
|
|
18467
|
-
applyGuidanceTitle.innerText = "Best next step";
|
|
19130
|
+
applyGuidanceTitle.innerText = (obj.__aiT ? obj.__aiT("aid_bestnext", "Best next step") : "Best next step");
|
|
18468
19131
|
applyGuidanceBadge.innerText = recommended.label || "";
|
|
18469
19132
|
var summaryDetail = getDialogRecommendedSummaryDetail(actionState, recommended);
|
|
18470
19133
|
applyGuidanceDetail.innerText = summaryDetail || "";
|
|
18471
|
-
applyGuidance.setAttribute("aria-label", "Best next step: " + (recommended.label || "Action") + "." + (summaryDetail ? " " + summaryDetail : ""));
|
|
19134
|
+
applyGuidance.setAttribute("aria-label", (obj.__aiT ? obj.__aiT("aid_bestnextaria", "Best next step: {0}.", recommended.label || "Action") : "Best next step: " + (recommended.label || "Action") + ".") + (summaryDetail ? " " + summaryDetail : ""));
|
|
18472
19135
|
}
|
|
18473
19136
|
|
|
18474
19137
|
function rerunDialogPlanFromEditor() {
|
|
@@ -18672,7 +19335,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18672
19335
|
// setBusyState(false) is called.
|
|
18673
19336
|
if (isBusy) {
|
|
18674
19337
|
runButton.classList.add("is-busy");
|
|
18675
|
-
setReviewV2ButtonContent(runButton, "spinner", "Thinking...");
|
|
19338
|
+
setReviewV2ButtonContent(runButton, "spinner", (obj.__aiT ? obj.__aiT("aic_thinking", "Thinking...") : "Thinking..."));
|
|
18676
19339
|
} else {
|
|
18677
19340
|
runButton.classList.remove("is-busy");
|
|
18678
19341
|
}
|
|
@@ -18792,7 +19455,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18792
19455
|
// hidden apply options on the kebab's title + aria-label so
|
|
18793
19456
|
// users discover *what's behind the dots* on hover or with a
|
|
18794
19457
|
// screen reader, instead of clicking blind. Pre-pass the
|
|
18795
|
-
// tooltip read as "More apply options" — accurate but content-
|
|
19458
|
+
// tooltip read as (obj.__aiT ? obj.__aiT("ai2_moreopts", "More apply options") : "More apply options") — accurate but content-
|
|
18796
19459
|
// free; now it reads "More apply options (3): Replace
|
|
18797
19460
|
// selection, Insert below, Replace doc" so the count + the
|
|
18798
19461
|
// names live where the affordance is. Same shape Notion AI's
|
|
@@ -18804,7 +19467,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18804
19467
|
if (showsSelectionAction) moreOptionNames.push("Replace selection");
|
|
18805
19468
|
if (showsPreviewAction) moreOptionNames.push("Preview");
|
|
18806
19469
|
if (showsInsertAction) moreOptionNames.push("Insert below");
|
|
18807
|
-
if (showsDocumentAction) moreOptionNames.push("Replace document");
|
|
19470
|
+
if (showsDocumentAction) moreOptionNames.push((obj.__aiT ? obj.__aiT("ai2_repldoc", "Replace document") : "Replace document"));
|
|
18808
19471
|
var moreCount = moreOptionNames.length;
|
|
18809
19472
|
var anyMoreAvailable = moreCount > 0;
|
|
18810
19473
|
applyRowMoreToggle.style.display = anyMoreAvailable ? "" : "none";
|
|
@@ -18829,7 +19492,7 @@ function RTE_Plugin_AIToolkit() {
|
|
|
18829
19492
|
// 2026-05-08 (v20260508s): clarify the run button — when a
|
|
18830
19493
|
// result already exists the button re-runs the same source, so
|
|
18831
19494
|
// call it Regenerate. Also flips when the plan goes stale.
|
|
18832
|
-
var runLabel = (hasResult || isStale) ? "Regenerate" : "Generate";
|
|
19495
|
+
var runLabel = (hasResult || isStale) ? (obj.__aiT ? obj.__aiT("aid_regenerate", "Regenerate") : "Regenerate") : (obj.__aiT ? obj.__aiT("aid_generate", "Generate") : "Generate");
|
|
18833
19496
|
setReviewV2ButtonContent(runButton, "sparkles", runLabel);
|
|
18834
19497
|
// 2026-05-17 (v20260517a): re-attach the keyboard hint chip,
|
|
18835
19498
|
// since setReviewV2ButtonContent clears textContent and loses
|