@qarakash/blockwriteai 1.0.9 → 1.0.10

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.
@@ -1,671 +0,0 @@
1
- /*!
2
- * BlockWriteAI Plugin
3
- * Adds an optional AI inline toolbar button, drawer, and server-proxied actions.
4
- */
5
- (function (global, factory) {
6
- if (typeof module === "object" && module.exports) {
7
- module.exports = factory(require("../blockwriteai"));
8
- } else {
9
- factory(global.BlockWriteAI);
10
- }
11
- })(typeof window !== "undefined" ? window : this, function (BlockWriteAI) {
12
- "use strict";
13
-
14
- if (!BlockWriteAI || BlockWriteAI.__aiPluginInstalled) return BlockWriteAI || null;
15
- BlockWriteAI.__aiPluginInstalled = true;
16
-
17
- var H = BlockWriteAI.helpers || {};
18
- var el = H.el || function (tag, className, attrs) {
19
- var node = document.createElement(tag);
20
- if (className) node.className = className;
21
- Object.keys(attrs || {}).forEach(function (key) {
22
- if (key === "html") node.innerHTML = attrs[key];
23
- else if (key === "text") node.textContent = attrs[key];
24
- else if (key === "dataset") {
25
- Object.keys(attrs[key] || {}).forEach(function (dataKey) {
26
- node.dataset[dataKey] = attrs[key][dataKey];
27
- });
28
- } else if (attrs[key] !== false && attrs[key] != null) {
29
- node.setAttribute(key, attrs[key] === true ? "" : attrs[key]);
30
- }
31
- });
32
- return node;
33
- };
34
- var esc = H.escapeHTML || function (value) {
35
- return String(value == null ? "" : value).replace(/[&<>"]/g, function (ch) {
36
- return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[ch];
37
- });
38
- };
39
- var faIcon = H.faIcon || function (name, label) {
40
- return '<i class="fa-solid fa-' + esc(name) + '" aria-hidden="true"></i><span class="rbe-sr-only">' + esc(label || name) + "</span>";
41
- };
42
-
43
- var ACTIONS = [
44
- ["improve", "wand-magic-sparkles", "Improve writing", "Make selected writing clearer and smoother."],
45
- ["grammar", "spell-check", "Fix grammar", "Correct grammar, spelling, and punctuation."],
46
- ["summarize", "compress", "Summarize", "Create a shorter version of the selected text."],
47
- ["expand", "up-right-and-down-left-from-center", "Expand", "Add helpful detail while keeping the meaning."],
48
- ["professional", "briefcase", "Make professional", "Rewrite in a polished business tone."]
49
- ];
50
-
51
- function injectStyles() {
52
- if (document.querySelector("style[data-blockwriteai-ai]")) return;
53
- var style = document.createElement("style");
54
- style.dataset.blockwriteaiAi = "true";
55
- style.textContent =
56
- ".rbe-ai-inline-btn{width:34px;height:34px;border:1px solid #cbd5e1;border-radius:8px;background:#eef6ff;color:#155eef;display:inline-grid;place-items:center;cursor:pointer;font-weight:800;}" +
57
- ".rbe-ai-inline-btn:hover,.rbe-ai-inline-btn:focus-visible{border-color:#2563eb;background:#dbeafe;color:#1d4ed8;}" +
58
- ".rbe-ai-inline-btn.is-open{border-color:#16a34a;background:#eafaf1;color:#087443;}" +
59
- ".rbe-ai-toolbar-btn{position:absolute;left:50%;top:0;transform:translateX(-50%);width:38px;min-width:38px;padding-left:0;padding-right:0;justify-content:center;border-color:#bfdbfe;background:#eff6ff;color:#1d4ed8;box-shadow:0 8px 24px rgba(37,99,235,.14);}" +
60
- ".rbe-ai-toolbar-btn:hover:not(:disabled),.rbe-ai-toolbar-btn:focus-visible{border-color:#2563eb;background:#dbeafe;color:#1d4ed8;}" +
61
- ".rbe-ai-toolbar-btn.is-open{border-color:#16a34a;background:#eafaf1;color:#087443;}" +
62
- ".rbe-ai-drawer{position:fixed;right:18px;top:18px;z-index:11000;width:390px;max-width:calc(100vw - 24px);height:calc(100vh - 36px);display:grid;grid-template-rows:auto 1fr auto;border:1px solid #cbd5e1;border-radius:14px;background:#fff;box-shadow:0 28px 80px rgba(15,23,42,.25);overflow:hidden;opacity:0;pointer-events:none;transform:translateX(calc(100% + 34px));transition:transform .28s cubic-bezier(.22,1,.36,1),opacity .2s ease;will-change:transform,opacity;}" +
63
- ".rbe-ai-drawer.is-open{opacity:1;pointer-events:auto;transform:translateX(0);}" +
64
- ".rbe-ai-drawer[hidden]{display:none!important;}" +
65
- ".rbe-ai-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #e2e8f0;background:linear-gradient(135deg,#f8fbff,#eef6ff);}" +
66
- ".rbe-ai-title{display:flex;align-items:center;gap:10px;font-weight:850;color:#0f172a;}" +
67
- ".rbe-ai-title-icon{width:34px;height:34px;border-radius:10px;background:#eafaf2;color:#12a865;display:grid;place-items:center;}" +
68
- ".rbe-ai-close{width:32px;height:32px;border:1px solid #cbd5e1;border-radius:999px;background:#fff;color:#172033;display:grid;place-items:center;cursor:pointer;}" +
69
- ".rbe-ai-body{padding:14px 16px;overflow:auto;display:grid;gap:14px;align-content:start;}" +
70
- ".rbe-ai-section{display:grid;gap:8px;}" +
71
- ".rbe-ai-label{font-size:12px;font-weight:850;text-transform:uppercase;letter-spacing:.03em;color:#667085;}" +
72
- ".rbe-ai-selection,.rbe-ai-result{border:1px solid #d7dee8;border-radius:10px;background:#f8fafc;color:#172033;padding:10px 12px;min-height:54px;max-height:132px;overflow:auto;font:13px/1.45 Arial,sans-serif;white-space:pre-wrap;}" +
73
- ".rbe-ai-selection.is-empty{color:#98a2b3;}" +
74
- ".rbe-ai-action-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;}" +
75
- ".rbe-ai-action{border:1px solid #d7dee8;border-radius:10px;background:#fff;color:#172033;padding:10px;display:flex;gap:9px;align-items:flex-start;text-align:left;cursor:pointer;}" +
76
- ".rbe-ai-action:hover,.rbe-ai-action:focus-visible{border-color:#2563eb;background:#eff6ff;}" +
77
- ".rbe-ai-action i{margin-top:2px;color:#1d4ed8;}" +
78
- ".rbe-ai-action strong{display:block;font-size:13px;line-height:1.2;}" +
79
- ".rbe-ai-action small{display:block;margin-top:3px;color:#667085;font-size:11px;line-height:1.25;}" +
80
- ".rbe-ai-prompt{width:100%;min-height:110px;resize:vertical;border:1px solid #d7dee8;border-radius:10px;padding:10px 12px;color:#172033;font:14px/1.45 Arial,sans-serif;}" +
81
- ".rbe-ai-prompt:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}" +
82
- ".rbe-ai-primary,.rbe-ai-secondary{border:1px solid #cbd5e1;border-radius:9px;background:#fff;color:#172033;font-weight:850;padding:9px 12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px;}" +
83
- ".rbe-ai-primary{border-color:#2563eb;background:#2563eb;color:#fff;}" +
84
- ".rbe-ai-primary:hover{background:#1d4ed8;}" +
85
- ".rbe-ai-secondary:hover{border-color:#2563eb;color:#1d4ed8;background:#eff6ff;}" +
86
- ".rbe-ai-primary[disabled],.rbe-ai-secondary[disabled],.rbe-ai-action[disabled]{opacity:.55;cursor:not-allowed;}" +
87
- ".rbe-ai-foot{display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:12px 16px;border-top:1px solid #e2e8f0;background:#f8fafc;}" +
88
- ".rbe-ai-foot-actions{display:flex;gap:8px;align-items:center;justify-content:flex-end;min-width:0;width:100%;}" +
89
- ".rbe-ai-apply{flex:0 1 112px;min-width:96px;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}" +
90
- ".rbe-toast-stack{position:fixed;right:18px;bottom:18px;z-index:12000;display:grid;gap:10px;width:min(360px,calc(100vw - 24px));pointer-events:none;}" +
91
- ".rbe-toast{display:flex;align-items:flex-start;gap:10px;border:1px solid #d7dee8;border-radius:12px;background:#fff;color:#172033;padding:12px 14px;box-shadow:0 18px 48px rgba(15,23,42,.18);opacity:0;transform:translateY(12px) scale(.98);transition:opacity .24s ease,transform .24s ease;pointer-events:auto;}" +
92
- ".rbe-toast.is-show{opacity:1;transform:translateY(0) scale(1);}" +
93
- ".rbe-toast.is-hiding{opacity:0;transform:translateY(10px) scale(.98);}" +
94
- ".rbe-toast i{margin-top:2px;}" +
95
- ".rbe-toast strong{display:block;font-size:13px;line-height:1.25;}" +
96
- ".rbe-toast span{display:block;margin-top:2px;font-size:12px;line-height:1.4;color:#475467;}" +
97
- ".rbe-toast.is-error{border-color:#fecaca;background:#fff7f7;}" +
98
- ".rbe-toast.is-error i,.rbe-toast.is-error strong{color:#b42318;}" +
99
- ".rbe-toast.is-warning{border-color:#fedf89;background:#fffbeb;}" +
100
- ".rbe-toast.is-warning i,.rbe-toast.is-warning strong{color:#b54708;}" +
101
- "@media (max-width:900px){.rbe-ai-toolbar-btn{position:static;transform:none;order:-1;}}" +
102
- "@media (max-width:700px){.rbe-ai-drawer{inset:10px;width:auto;height:auto;max-height:calc(100dvh - 20px);border-radius:12px;}.rbe-ai-action-grid{grid-template-columns:1fr;}.rbe-ai-head,.rbe-ai-body,.rbe-ai-foot{padding-left:12px;padding-right:12px;}}" +
103
- "@media (max-width:520px){.rbe-ai-foot-actions{justify-content:stretch;width:100%;}.rbe-ai-primary,.rbe-ai-secondary{width:100%;}.rbe-ai-apply{flex:1 1 auto;min-width:0;}.rbe-ai-title{min-width:0;}.rbe-ai-title span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.rbe-toast-stack{right:10px;bottom:10px;width:calc(100vw - 20px);}}";
104
- document.head.appendChild(style);
105
- }
106
-
107
- function closestBlock(node) {
108
- if (!node) return null;
109
- var element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
110
- return element && element.closest ? element.closest(".rbe-block") : null;
111
- }
112
-
113
- function isRangeInsideEditor(editor, range) {
114
- if (!editor || !editor.root || !range) return false;
115
- var node = range.commonAncestorContainer;
116
- if (node && node.nodeType !== Node.ELEMENT_NODE) node = node.parentElement;
117
- return !!(node && editor.root.contains(node));
118
- }
119
-
120
- function captureSelection(editor) {
121
- var selection = window.getSelection();
122
- if (selection && selection.rangeCount && !selection.isCollapsed) {
123
- var range = selection.getRangeAt(0);
124
- if (isRangeInsideEditor(editor, range)) {
125
- editor.savedRange = range.cloneRange();
126
- return editor.savedRange;
127
- }
128
- }
129
- return editor.savedRange && isRangeInsideEditor(editor, editor.savedRange) ? editor.savedRange : null;
130
- }
131
-
132
- function selectedText(editor) {
133
- var range = captureSelection(editor);
134
- return range ? range.toString().trim() : "";
135
- }
136
-
137
- function textToFragment(text) {
138
- var fragment = document.createDocumentFragment();
139
- String(text || "").split(/\n/).forEach(function (line, index) {
140
- if (index) fragment.appendChild(document.createElement("br"));
141
- fragment.appendChild(document.createTextNode(line));
142
- });
143
- return fragment;
144
- }
145
-
146
- function replaceSelection(editor, text, range) {
147
- range = range || editor.__blockwriteaiAI && editor.__blockwriteaiAI.lastRange || editor.savedRange;
148
- if (!range || !isRangeInsideEditor(editor, range)) return false;
149
- var selection = window.getSelection();
150
- selection.removeAllRanges();
151
- selection.addRange(range);
152
- range.deleteContents();
153
- var fragment = textToFragment(text);
154
- var last = fragment.lastChild;
155
- range.insertNode(fragment);
156
- if (last && last.parentNode) {
157
- var next = document.createRange();
158
- next.setStartAfter(last);
159
- next.collapse(true);
160
- selection.removeAllRanges();
161
- selection.addRange(next);
162
- editor.savedRange = next.cloneRange();
163
- }
164
- if (typeof editor.noteHistory === "function") editor.noteHistory("AI", "Text applied");
165
- if (typeof editor.changed === "function") editor.changed(true);
166
- if (typeof editor.updateInlineToolbarState === "function") editor.updateInlineToolbarState();
167
- return true;
168
- }
169
-
170
- function endpointFor(editor) {
171
- var ai = editor && editor.options ? editor.options.ai : null;
172
- if (ai && typeof ai.endpoint === "string" && ai.endpoint) return ai.endpoint;
173
- if (BlockWriteAI.ai && typeof BlockWriteAI.ai.endpoint === "string" && BlockWriteAI.ai.endpoint) return BlockWriteAI.ai.endpoint;
174
- return "api/ai.php";
175
- }
176
-
177
- function aiEnabled(editor) {
178
- return !editor || typeof editor.isPremiumFeatureEnabled !== "function" || editor.isPremiumFeatureEnabled("ai");
179
- }
180
-
181
- function aiLockedMessage(editor) {
182
- if (editor && typeof editor.premiumMessage === "function") return editor.premiumMessage("ai");
183
- return "AI is locked. Please add an active BlockWriteAI premium license.";
184
- }
185
-
186
- function requireAI(editor) {
187
- if (aiEnabled(editor)) return true;
188
- showToast(editor, aiLockedMessage(editor), "warning");
189
- return false;
190
- }
191
-
192
- function consumeAIUsage(editor) {
193
- if (editor && typeof editor.consumePremiumUsage === "function") return editor.consumePremiumUsage("ai");
194
- return Promise.resolve();
195
- }
196
-
197
- function refreshAIAvailability(editor) {
198
- if (!editor || !editor.root) return;
199
- var locked = !aiEnabled(editor);
200
- Array.prototype.slice.call(editor.root.querySelectorAll(".rbe-ai-inline-btn,.rbe-ai-toolbar-btn")).forEach(function (button) {
201
- button.classList.toggle("is-premium-locked", locked);
202
- button.setAttribute("aria-disabled", locked ? "true" : "false");
203
- button.title = locked ? aiLockedMessage(editor) : "BlockWriteAI";
204
- });
205
- if (locked && isDrawerOpen(editor)) closeDrawer(editor);
206
- }
207
-
208
- function callAI(editor, payload) {
209
- var endpoint = endpointFor(editor);
210
- return consumeAIUsage(editor).then(function () {
211
- return fetch(endpoint, {
212
- method: "POST",
213
- headers: {
214
- "Content-Type": "application/json",
215
- "Accept": "application/json"
216
- },
217
- body: JSON.stringify(payload)
218
- }).then(function (response) {
219
- return response.json().catch(function () {
220
- return {};
221
- }).then(function (body) {
222
- if (!response.ok || body.ok === false) {
223
- throw new Error(body.error || "AI request failed");
224
- }
225
- return body;
226
- });
227
- });
228
- });
229
- }
230
-
231
- function ensureState(editor) {
232
- if (!editor.__blockwriteaiAI) {
233
- editor.__blockwriteaiAI = {
234
- drawer: null,
235
- lastRange: null,
236
- lastText: "",
237
- result: "",
238
- loading: false,
239
- closeTimer: null,
240
- progressTimer: null
241
- };
242
- }
243
- return editor.__blockwriteaiAI;
244
- }
245
-
246
- function emitProgress(editor, active, message) {
247
- if (!editor || !editor.root || typeof CustomEvent !== "function") return;
248
- editor.root.dispatchEvent(
249
- new CustomEvent("rbe:ai-progress", {
250
- bubbles: true,
251
- detail: { active: !!active, message: message || "" }
252
- })
253
- );
254
- }
255
-
256
- function setProgress(editor, active, message, delay) {
257
- var state = ensureState(editor);
258
- if (state.progressTimer) {
259
- clearTimeout(state.progressTimer);
260
- state.progressTimer = null;
261
- }
262
- if (active) {
263
- emitProgress(editor, true, message || "Working with BlockWriteAI...");
264
- return;
265
- }
266
- state.progressTimer = setTimeout(function () {
267
- emitProgress(editor, false, "");
268
- state.progressTimer = null;
269
- }, typeof delay === "number" ? delay : 0);
270
- }
271
-
272
- function showToast(editor, message, type) {
273
- if (!message || typeof document === "undefined") return;
274
- injectStyles();
275
- var stack = document.querySelector(".rbe-toast-stack");
276
- if (!stack) {
277
- stack = el("div", "rbe-toast-stack", {
278
- role: "status",
279
- "aria-live": "polite"
280
- });
281
- document.body.appendChild(stack);
282
- }
283
- var toastType = type === "error" ? "error" : "warning";
284
- var toast = el("div", "rbe-toast is-" + toastType, {});
285
- toast.innerHTML =
286
- faIcon(toastType === "error" ? "circle-exclamation" : "triangle-exclamation", toastType) +
287
- "<div><strong>" +
288
- esc(toastType === "error" ? "BlockWriteAI error" : "BlockWriteAI warning") +
289
- "</strong><span>" +
290
- esc(message) +
291
- "</span></div>";
292
- stack.appendChild(toast);
293
- requestAnimationFrame(function () {
294
- toast.classList.add("is-show");
295
- });
296
- setTimeout(function () {
297
- toast.classList.remove("is-show");
298
- toast.classList.add("is-hiding");
299
- setTimeout(function () {
300
- if (toast.parentNode) toast.parentNode.removeChild(toast);
301
- if (stack && !stack.children.length && stack.parentNode) stack.parentNode.removeChild(stack);
302
- }, 260);
303
- }, 5000);
304
- }
305
-
306
- function setStatus(editor, message, error) {
307
- if (message && error) showToast(editor, message, "error");
308
- }
309
-
310
- function setLoading(editor, loading) {
311
- var state = ensureState(editor);
312
- state.loading = !!loading;
313
- if (!state.drawer) return;
314
- Array.prototype.slice.call(state.drawer.querySelectorAll("button")).forEach(function (button) {
315
- if (button.classList.contains("rbe-ai-close")) return;
316
- button.disabled = !!loading || (button.classList.contains("rbe-ai-apply") && !state.result);
317
- });
318
- }
319
-
320
- function updateDrawerSelection(editor) {
321
- var state = ensureState(editor);
322
- if (!state.drawer) return;
323
- var selection = selectedText(editor);
324
- state.lastRange = editor.savedRange && isRangeInsideEditor(editor, editor.savedRange) ? editor.savedRange.cloneRange() : null;
325
- state.lastText = selection;
326
- var box = state.drawer.querySelector(".rbe-ai-selection");
327
- if (box) {
328
- box.textContent = selection || "Select text in the editor, then choose an AI action.";
329
- box.classList.toggle("is-empty", !selection);
330
- }
331
- }
332
-
333
- function ensureDrawer(editor) {
334
- var state = ensureState(editor);
335
- if (state.drawer) return state.drawer;
336
- injectStyles();
337
- var drawer = el("aside", "rbe-ai-drawer", {
338
- hidden: true,
339
- role: "dialog",
340
- "aria-label": "BlockWriteAI"
341
- });
342
- drawer.innerHTML =
343
- '<div class="rbe-ai-head"><div class="rbe-ai-title"><span class="rbe-ai-title-icon">' +
344
- '<i class="fa-solid fa-wand-magic-sparkles" aria-hidden="true"></i>' +
345
- "</span><span>BlockWriteAI</span></div><button type=\"button\" class=\"rbe-ai-close\" title=\"Close AI\">" +
346
- faIcon("xmark", "Close") +
347
- '</button></div><div class="rbe-ai-body">' +
348
- '<section class="rbe-ai-section"><div class="rbe-ai-label">Selection</div><div class="rbe-ai-selection is-empty"></div></section>' +
349
- '<section class="rbe-ai-section"><div class="rbe-ai-label">Actions</div><div class="rbe-ai-action-grid">' +
350
- ACTIONS.map(function (action) {
351
- return (
352
- '<button type="button" class="rbe-ai-action" data-ai-action="' +
353
- esc(action[0]) +
354
- '">' +
355
- faIcon(action[1], action[2]) +
356
- "<span><strong>" +
357
- esc(action[2]) +
358
- "</strong><small>" +
359
- esc(action[3]) +
360
- "</small></span></button>"
361
- );
362
- }).join("") +
363
- '</div></section><section class="rbe-ai-section"><div class="rbe-ai-label">Generate blocks from prompt</div>' +
364
- '<textarea class="rbe-ai-prompt" placeholder="Example: Create a short project proposal with a heading, summary, checklist, and next steps."></textarea>' +
365
- '<button type="button" class="rbe-ai-primary rbe-ai-generate">' +
366
- faIcon("sparkles", "Generate") +
367
- "<span>Generate blocks</span></button></section>" +
368
- '<section class="rbe-ai-section"><div class="rbe-ai-label">Result</div><div class="rbe-ai-result"></div></section>' +
369
- '</div><div class="rbe-ai-foot"><div class="rbe-ai-foot-actions"><button type="button" class="rbe-ai-secondary rbe-ai-apply" disabled>' +
370
- faIcon("check", "Apply") +
371
- "<span>Apply</span></button></div></div>";
372
- editor.root.appendChild(drawer);
373
- state.drawer = drawer;
374
- return drawer;
375
- }
376
-
377
- function setAIButtonsOpen(editor, open) {
378
- if (!editor || !editor.root) return;
379
- Array.prototype.slice.call(editor.root.querySelectorAll(".rbe-ai-inline-btn,.rbe-ai-toolbar-btn")).forEach(function (button) {
380
- button.classList.toggle("is-open", !!open);
381
- });
382
- }
383
-
384
- function isDrawerOpen(editor) {
385
- var state = ensureState(editor);
386
- return !!(state.drawer && !state.drawer.hidden && state.drawer.classList.contains("is-open"));
387
- }
388
-
389
- function openDrawer(editor) {
390
- if (!requireAI(editor)) return;
391
- var state = ensureState(editor);
392
- var drawer = ensureDrawer(editor);
393
- updateDrawerSelection(editor);
394
- if (state.closeTimer) {
395
- clearTimeout(state.closeTimer);
396
- state.closeTimer = null;
397
- }
398
- drawer.hidden = false;
399
- drawer.getBoundingClientRect();
400
- requestAnimationFrame(function () {
401
- drawer.classList.add("is-open");
402
- });
403
- setAIButtonsOpen(editor, true);
404
- setStatus(editor, "");
405
- if (!state.lastText) {
406
- setTimeout(function () {
407
- var prompt = drawer.querySelector(".rbe-ai-prompt");
408
- if (prompt) prompt.focus();
409
- }, 0);
410
- }
411
- }
412
-
413
- function closeDrawer(editor) {
414
- var state = ensureState(editor);
415
- if (!state.drawer) return;
416
- if (state.closeTimer) clearTimeout(state.closeTimer);
417
- state.drawer.classList.remove("is-open");
418
- setAIButtonsOpen(editor, false);
419
- state.closeTimer = setTimeout(function () {
420
- if (!state.drawer) return;
421
- state.drawer.hidden = true;
422
- state.closeTimer = null;
423
- }, 300);
424
- }
425
-
426
- function toggleDrawer(editor) {
427
- if (!requireAI(editor)) return;
428
- if (isDrawerOpen(editor)) closeDrawer(editor);
429
- else openDrawer(editor);
430
- }
431
-
432
- function setResult(editor, text) {
433
- var state = ensureState(editor);
434
- state.result = String(text || "").trim();
435
- if (!state.drawer) return;
436
- var result = state.drawer.querySelector(".rbe-ai-result");
437
- var apply = state.drawer.querySelector(".rbe-ai-apply");
438
- if (result) result.textContent = state.result;
439
- if (apply) apply.disabled = state.loading || !state.result;
440
- }
441
-
442
- function runTextAction(editor, action) {
443
- if (!requireAI(editor)) return;
444
- var state = ensureState(editor);
445
- updateDrawerSelection(editor);
446
- if (!state.lastText) {
447
- showToast(editor, "Select text first, then run an AI action.", "warning");
448
- return;
449
- }
450
- setLoading(editor, true);
451
- setResult(editor, "");
452
- setProgress(editor, true, "Improving selected text with BlockWriteAI...");
453
- callAI(editor, {
454
- action: action,
455
- text: state.lastText,
456
- document: typeof editor.getData === "function" ? editor.getData() : null
457
- })
458
- .then(function (payload) {
459
- setResult(editor, payload.result || "");
460
- })
461
- .catch(function (error) {
462
- showToast(editor, error.message || "AI request failed", "error");
463
- })
464
- .finally(function () {
465
- setLoading(editor, false);
466
- setProgress(editor, false, "", 250);
467
- });
468
- }
469
-
470
- function cleanGeneratedBlock(block) {
471
- if (!block || typeof block !== "object") return null;
472
- var type = String(block.type || "paragraph");
473
- var data = block.data && typeof block.data === "object" ? block.data : {};
474
- return { type: type, data: data };
475
- }
476
-
477
- function isBlankParagraphBlock(block) {
478
- if (!block || !block.dataset || block.dataset.type !== "paragraph") return false;
479
- var editable = block.querySelector(".rbe-paragraph");
480
- var text = editable ? editable.textContent : block.textContent;
481
- return !String(text || "").trim();
482
- }
483
-
484
- function insertGeneratedBlocks(editor, blocks) {
485
- blocks = (blocks || []).map(cleanGeneratedBlock).filter(Boolean);
486
- if (!blocks.length) return 0;
487
- var current = typeof editor.getCurrentBlock === "function" ? editor.getCurrentBlock() : null;
488
- var index = current && typeof editor.indexOfBlock === "function" ? editor.indexOfBlock(current) + 1 : editor.canvas.children.length;
489
- var onlyBlock = editor.canvas && editor.canvas.children.length === 1 ? editor.canvas.children[0] : null;
490
- if (onlyBlock && isBlankParagraphBlock(onlyBlock)) {
491
- index = 0;
492
- onlyBlock.remove();
493
- }
494
- blocks.forEach(function (block) {
495
- if (typeof editor.insertBlock === "function") {
496
- editor.insertBlock(block.type, block.data, index++);
497
- } else if (typeof editor.createBlock === "function") {
498
- editor.canvas.appendChild(editor.createBlock(block.type, block.data));
499
- }
500
- });
501
- if (typeof editor.noteHistory === "function") editor.noteHistory("AI", "Generated " + blocks.length + " blocks");
502
- if (typeof editor.changed === "function") editor.changed(true);
503
- return blocks.length;
504
- }
505
-
506
- function generateBlocks(editor) {
507
- if (!requireAI(editor)) return;
508
- var state = ensureState(editor);
509
- var prompt = state.drawer && state.drawer.querySelector(".rbe-ai-prompt");
510
- var value = prompt ? prompt.value.trim() : "";
511
- if (!value) {
512
- showToast(editor, "Write a prompt before generating blocks.", "warning");
513
- return;
514
- }
515
- setLoading(editor, true);
516
- setResult(editor, "");
517
- setProgress(editor, true, "Generating BlockWriteAI blocks...");
518
- callAI(editor, {
519
- action: "generate_blocks",
520
- prompt: value,
521
- document: typeof editor.getData === "function" ? editor.getData() : null
522
- })
523
- .then(function (payload) {
524
- var count = insertGeneratedBlocks(editor, payload.blocks || []);
525
- if (!count) showToast(editor, "AI returned no blocks.", "warning");
526
- })
527
- .catch(function (error) {
528
- showToast(editor, error.message || "AI generation failed", "error");
529
- })
530
- .finally(function () {
531
- setLoading(editor, false);
532
- setProgress(editor, false, "", 250);
533
- });
534
- }
535
-
536
- function ensureAIRefreshListener(editor) {
537
- if (!editor || !editor.root || editor.__blockwriteaiAIRefreshBound) return;
538
- editor.root.addEventListener("rbe:premium-change", refreshAIAvailability.bind(null, editor));
539
- editor.__blockwriteaiAIRefreshBound = true;
540
- }
541
-
542
- function ensureTopToolbarAIButton(editor, toolbar) {
543
- toolbar = toolbar || (editor && editor.topToolbar);
544
- if (!editor || editor.options.ai === false || !toolbar) return toolbar;
545
- injectStyles();
546
- if (!toolbar.querySelector(".rbe-ai-toolbar-btn")) {
547
- toolbar.appendChild(
548
- el("button", "rbe-history-btn rbe-ai-toolbar-btn", {
549
- type: "button",
550
- title: "BlockWriteAI",
551
- html: faIcon("wand-magic-sparkles", "BlockWriteAI"),
552
- dataset: { aiToolbar: "true", premiumFeature: "ai", unlockedTitle: "BlockWriteAI" }
553
- })
554
- );
555
- }
556
- ensureAIRefreshListener(editor);
557
- refreshAIAvailability(editor);
558
- return toolbar;
559
- }
560
-
561
- function ensureInlineAIButton(editor, toolbar) {
562
- toolbar = toolbar || (editor && editor.inlineToolbar);
563
- if (!editor || editor.options.ai === false || !toolbar) return toolbar;
564
- injectStyles();
565
- if (!toolbar.querySelector(".rbe-ai-inline-btn")) {
566
- toolbar.appendChild(
567
- el("button", "rbe-ai-inline-btn", {
568
- type: "button",
569
- title: "BlockWriteAI",
570
- html: faIcon("wand-magic-sparkles", "AI"),
571
- dataset: { aiInline: "true", premiumFeature: "ai", unlockedTitle: "BlockWriteAI" }
572
- })
573
- );
574
- }
575
- ensureAIRefreshListener(editor);
576
- refreshAIAvailability(editor);
577
- return toolbar;
578
- }
579
-
580
- function hydrateExistingEditors() {
581
- if (typeof document === "undefined") return;
582
- Array.prototype.slice.call(document.querySelectorAll(".rbe")).forEach(function (root) {
583
- var editor = root.__blockwriteaiInstance;
584
- if (!editor) return;
585
- ensureTopToolbarAIButton(editor);
586
- ensureInlineAIButton(editor);
587
- });
588
- }
589
-
590
- var originalCreateTopToolbar = BlockWriteAI.prototype.createTopToolbar;
591
- BlockWriteAI.prototype.createTopToolbar = function () {
592
- var toolbar = originalCreateTopToolbar.call(this);
593
- return ensureTopToolbarAIButton(this, toolbar);
594
- };
595
-
596
- var originalCreateInlineToolbar = BlockWriteAI.prototype.createInlineToolbar;
597
- BlockWriteAI.prototype.createInlineToolbar = function () {
598
- var toolbar = originalCreateInlineToolbar.call(this);
599
- return ensureInlineAIButton(this, toolbar);
600
- };
601
-
602
- BlockWriteAI.prototype.openAI = function () {
603
- if (this.options.ai === false) return null;
604
- if (!requireAI(this)) return null;
605
- openDrawer(this);
606
- return this;
607
- };
608
-
609
- var originalHandleRootClick = BlockWriteAI.prototype.handleRootClick;
610
- BlockWriteAI.prototype.handleRootClick = function (event) {
611
- var target = event.target;
612
- if (target.closest && target.closest(".rbe-ai-inline-btn,.rbe-ai-toolbar-btn")) {
613
- event.preventDefault();
614
- event.stopPropagation();
615
- toggleDrawer(this);
616
- return;
617
- }
618
- if (target.closest && target.closest(".rbe-ai-close")) {
619
- event.preventDefault();
620
- event.stopPropagation();
621
- closeDrawer(this);
622
- return;
623
- }
624
- var action = target.closest && target.closest(".rbe-ai-action");
625
- if (action) {
626
- event.preventDefault();
627
- event.stopPropagation();
628
- runTextAction(this, action.dataset.aiAction);
629
- return;
630
- }
631
- if (target.closest && target.closest(".rbe-ai-generate")) {
632
- event.preventDefault();
633
- event.stopPropagation();
634
- generateBlocks(this);
635
- return;
636
- }
637
- if (target.closest && target.closest(".rbe-ai-apply")) {
638
- event.preventDefault();
639
- event.stopPropagation();
640
- var state = ensureState(this);
641
- if (state.result && replaceSelection(this, state.result, state.lastRange)) {
642
- setProgress(this, true, "Applying BlockWriteAI result...");
643
- setResult(this, "");
644
- updateDrawerSelection(this);
645
- setProgress(this, false, "", 600);
646
- } else {
647
- showToast(this, "There is no AI result ready to apply.", "warning");
648
- }
649
- return;
650
- }
651
- return originalHandleRootClick.call(this, event);
652
- };
653
-
654
- var originalHandleDocumentMouseDown = BlockWriteAI.prototype.handleDocumentMouseDown;
655
- BlockWriteAI.prototype.handleDocumentMouseDown = function (event) {
656
- if (event.target.closest && event.target.closest(".rbe-ai-drawer, .rbe-ai-inline-btn, .rbe-ai-toolbar-btn")) return;
657
- if (isDrawerOpen(this)) closeDrawer(this);
658
- return originalHandleDocumentMouseDown.call(this, event);
659
- };
660
-
661
- BlockWriteAI.ai = BlockWriteAI.ai || {};
662
- BlockWriteAI.enableAI = function (options) {
663
- BlockWriteAI.ai = Object.assign(BlockWriteAI.ai || {}, options || {});
664
- BlockWriteAI.configure({ ai: Object.assign({}, BlockWriteAI.ai) });
665
- return BlockWriteAI;
666
- };
667
-
668
- hydrateExistingEditors();
669
-
670
- return BlockWriteAI;
671
- });