@qarakash/blockwriteai 1.0.7 → 1.0.9

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 CHANGED
@@ -15,8 +15,8 @@ All public assets, package entry points, and browser globals use the `BlockWrite
15
15
  - Multi-image upload, drag and drop, resize, crop, rotate, and gallery layout.
16
16
  - Attachment blocks for documents and videos.
17
17
  - Code block with formatting, syntax help, language detection, and suggestions.
18
- - Mermaid-style drawing designer with shapes, arrows, images, text formatting, ruler, workspace resizing, and preview/export support.
19
- - Charts, LaTeX, audio, drawing, layout columns, signature, signature flow, AI writing, and optional history plugins.
18
+ - Charts, LaTeX, audio, layout columns, code assistance, and optional history plugins in the free plugin set.
19
+ - Premium plugins for Drawing, Mermaid, Signature, Signature Flow, and AI writing, delivered after server-side license verification.
20
20
  - Undo, redo, autosave, block actions, drag reordering, and optional JSON save/export buttons.
21
21
  - JSON-powered preview mounting for public document pages.
22
22
  - JSON save API examples for future database storage.
@@ -65,49 +65,51 @@ BlockWriteAI can be consumed as a CDN/script-link package, an npm package, a Pyt
65
65
 
66
66
  ### Script Link
67
67
 
68
- Use the GitHub CDN link directly from the current main build:
69
-
70
- ```html
71
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/blockwriteai.css">
72
-
73
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/blockwriteai.js"></script>
74
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-code-assist.js"></script>
75
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-advanced-blocks.js"></script>
76
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-ai.js"></script>
77
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-signature.js"></script>
78
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-signature-flow.js"></script>
68
+ Use the npm CDN for a locked release build:
69
+
70
+ ```html
71
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.css">
72
+
73
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.js"></script>
74
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/plugins/blockwriteai-code-assist.js"></script>
75
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/plugins/blockwriteai-advanced-blocks.js"></script>
79
76
  ```
80
77
 
81
- `blockwriteai-advanced-blocks.js` is kept as the compatibility bundle. New
82
- licensed delivery can load Drawing and Mermaid separately:
78
+ `blockwriteai-advanced-blocks.js` is the free advanced bundle. It includes Chart,
79
+ LaTeX, Audio, and Columns/Layout. Drawing, Mermaid, Signature, Signature Flow,
80
+ and AI are premium plugins and should not be loaded directly in production.
81
+
82
+ Load History only when a project needs the history dropdown:
83
83
 
84
84
  ```html
85
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-drawing.js"></script>
86
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-mermaid.js"></script>
85
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/plugins/blockwriteai-history.js"></script>
87
86
  ```
88
87
 
89
- Load History only when a project needs the history dropdown:
90
-
91
- ```html
92
- <script src="https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/plugins/blockwriteai-history.js"></script>
93
- ```
88
+ For development against the current GitHub branch, replace the npm URLs with the
89
+ GitHub CDN form:
94
90
 
95
- For locked production builds, replace `@main` with a release tag after publishing.
91
+ ```html
92
+ https://cdn.jsdelivr.net/gh/qarAkash/BlockWriteAI@main/dist/...
93
+ ```
96
94
 
97
95
  ### Premium License Gating
98
96
 
99
97
  Drawing, Mermaid drawing, Signature, Signature Flow, and AI can be licensed through
100
98
  the BlockWriteAI Platform. Normal editor blocks continue to work without a license.
101
- Premium tools are shown as locked until the license endpoint returns an active trial
102
- or subscription.
99
+ Premium plugin scripts are delivered only after the license endpoint returns an
100
+ active trial or subscription. Without a valid license, those tools are not
101
+ registered and do not appear in the insert-block menu.
103
102
 
104
103
  ```js
105
104
  const editor = new BlockWriteAI({
106
105
  holder: "#editor",
107
- licenseKey: "bwai_live_xxxxx",
108
106
  premium: {
107
+ licenseKey: "bwai_live_xxxxx",
109
108
  verifyEndpoint: "http://localhost/blockwriteai-platform/api/license_verify.php",
110
- usageEndpoint: "http://localhost/blockwriteai-platform/api/ai_usage.php"
109
+ pluginEndpoint: "http://localhost/blockwriteai-platform/api/premium_plugins.php",
110
+ usageEndpoint: "http://localhost/blockwriteai-platform/api/ai_usage.php",
111
+ autoLoadPlugins: true,
112
+ features: ["drawing", "mermaid", "signature", "signature_flow", "ai"]
111
113
  },
112
114
  ai: {
113
115
  endpoint: "/api/ai.php"
@@ -127,19 +129,20 @@ The verify endpoint must return a `features` object with these keys when enabled
127
129
  }
128
130
  ```
129
131
 
130
- The AI plugin calls `premium.usageEndpoint` before each AI request so the platform
131
- can enforce the 7-day trial and daily prompt limits.
132
+ The AI plugin calls `premium.usageEndpoint` before each AI request so the
133
+ platform can enforce the 7-day trial and daily prompt limits.
132
134
 
133
- For stronger production security, do not load premium plugin files directly from a
134
- public CDN. Load the free core first, request the premium plugins from your
135
+ For stronger production security, do not load premium plugin files directly from
136
+ a public CDN. Load the free core first, request the premium plugins from your
135
137
  BlockWriteAI Platform, and create the editor only after the server has verified
136
138
  the license. The platform serves Drawing, Mermaid, Signature, Signature Flow,
137
- and AI as separate plugin files after checking the requested feature flags:
139
+ and AI as a verified bundle after checking the requested feature flags:
138
140
 
139
141
  ```html
140
- <link rel="stylesheet" href="/BlockWriteAI/dist/blockwriteai.css">
141
- <script src="/BlockWriteAI/dist/blockwriteai.js"></script>
142
- <script src="/BlockWriteAI/dist/plugins/blockwriteai-code-assist.js"></script>
142
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.css">
143
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.js"></script>
144
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/plugins/blockwriteai-code-assist.js"></script>
145
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/plugins/blockwriteai-advanced-blocks.js"></script>
143
146
  <script>
144
147
  async function boot() {
145
148
  await BlockWriteAI.loadPremiumPlugins({
@@ -150,8 +153,8 @@ and AI as separate plugin files after checking the requested feature flags:
150
153
 
151
154
  window.editor = new BlockWriteAI({
152
155
  holder: "#editor",
153
- licenseKey: "bwai_live_xxxxx",
154
156
  premium: {
157
+ licenseKey: "bwai_live_xxxxx",
155
158
  verifyEndpoint: "http://localhost/blockwriteai-platform/api/license_verify.php",
156
159
  usageEndpoint: "http://localhost/blockwriteai-platform/api/ai_usage.php"
157
160
  }
@@ -182,22 +185,19 @@ npm install @qarakash/blockwriteai
182
185
  Bundler entry points:
183
186
 
184
187
  ```js
185
- import BlockWriteAI from "@qarakash/blockwriteai";
186
- import "@qarakash/blockwriteai/css";
187
- import "@qarakash/blockwriteai/plugins/code-assist";
188
- import "@qarakash/blockwriteai/plugins/advanced";
189
- import "@qarakash/blockwriteai/plugins/ai";
190
- import "@qarakash/blockwriteai/plugins/history";
191
- import "@qarakash/blockwriteai/plugins/signature";
192
- import "@qarakash/blockwriteai/plugins/signature-flow";
193
- ```
194
-
195
- After npm publishing, the same package is also available through npm CDNs:
196
-
197
- ```html
198
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.7/dist/blockwriteai.css">
199
- <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.7/dist/blockwriteai.js"></script>
200
- ```
188
+ import BlockWriteAI from "@qarakash/blockwriteai";
189
+ import "@qarakash/blockwriteai/css";
190
+ import "@qarakash/blockwriteai/plugins/code-assist";
191
+ import "@qarakash/blockwriteai/plugins/advanced";
192
+ import "@qarakash/blockwriteai/plugins/history";
193
+ ```
194
+
195
+ After npm publishing, the same package is also available through npm CDNs:
196
+
197
+ ```html
198
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.css">
199
+ <script src="https://cdn.jsdelivr.net/npm/@qarakash/blockwriteai@1.0.9/dist/blockwriteai.js"></script>
200
+ ```
201
201
 
202
202
  ### Python
203
203
 
@@ -221,16 +221,21 @@ from blockwriteai_editor import copy_static
221
221
  copy_static("static/blockwriteai")
222
222
  ```
223
223
 
224
- Python helpers are also available for generating asset tags. Pass `history=True` only on pages where you want the optional History dropdown:
225
-
226
- ```python
227
- from blockwriteai_editor import stylesheet_tag, script_tags
228
-
229
- print(stylesheet_tag("/static/blockwriteai"))
230
- print(script_tags("/static/blockwriteai"))
231
- print(script_tags("/static/blockwriteai", history=True))
232
- print(script_tags("/static/blockwriteai", ai=True))
233
- ```
224
+ Python helpers are also available for generating asset tags. `script_tags()` loads
225
+ only core/free plugins by default. Pass `history=True` only on pages where you want
226
+ the optional History dropdown:
227
+
228
+ ```python
229
+ from blockwriteai_editor import premium_script_tags, stylesheet_tag, script_tags
230
+
231
+ print(stylesheet_tag("/static/blockwriteai"))
232
+ print(script_tags("/static/blockwriteai"))
233
+ print(script_tags("/static/blockwriteai", history=True))
234
+
235
+ # Only for private/licensed deployments. Public apps should prefer the platform
236
+ # premium bundle endpoint shown in the Premium License Gating section.
237
+ print(premium_script_tags("/static/blockwriteai", ["signature", "ai"]))
238
+ ```
234
239
 
235
240
  ### PHP / Composer
236
241
 
@@ -252,15 +257,20 @@ Publish assets into your public folder:
252
257
  cp -R vendor/qarakash/blockwriteai/dist public/blockwriteai
253
258
  ```
254
259
 
255
- PHP helper. Pass `history: true` only on pages where you want the optional History dropdown, and `ai: true` only on pages where you want the AI drawer:
256
-
257
- ```php
258
- use QarAkash\BlockWriteAI\BlockWriteAIAssets;
259
-
260
- echo BlockWriteAIAssets::stylesheetTag('/blockwriteai');
261
- echo BlockWriteAIAssets::scriptTags('/blockwriteai');
262
- echo BlockWriteAIAssets::scriptTags('/blockwriteai', plugins: true, history: true, ai: true);
263
- ```
260
+ PHP helper. `scriptTags()` loads only core/free plugins by default. Pass
261
+ `history: true` only on pages where you want the optional History dropdown:
262
+
263
+ ```php
264
+ use QarAkash\BlockWriteAI\BlockWriteAIAssets;
265
+
266
+ echo BlockWriteAIAssets::stylesheetTag('/blockwriteai');
267
+ echo BlockWriteAIAssets::scriptTags('/blockwriteai');
268
+ echo BlockWriteAIAssets::scriptTags('/blockwriteai', plugins: true, history: true);
269
+
270
+ // Only for private/licensed deployments. Public apps should prefer the platform
271
+ // premium bundle endpoint shown in the Premium License Gating section.
272
+ echo BlockWriteAIAssets::premiumScriptTags('/blockwriteai', ['signature', 'ai']);
273
+ ```
264
274
 
265
275
  ## Local XAMPP Demo
266
276
 
@@ -283,40 +293,38 @@ http://localhost/BlockWriteAI/examples/
283
293
 
284
294
  <div id="editor"></div>
285
295
 
286
- <script src="/BlockWriteAI/dist/blockwriteai.js"></script>
287
- <script src="/BlockWriteAI/dist/plugins/blockwriteai-code-assist.js"></script>
288
- <script src="/BlockWriteAI/dist/plugins/blockwriteai-advanced-blocks.js"></script>
289
- <script src="/BlockWriteAI/dist/plugins/blockwriteai-signature.js"></script>
290
- <script src="/BlockWriteAI/dist/plugins/blockwriteai-signature-flow.js"></script>
291
-
292
- <script>
296
+ <script src="/BlockWriteAI/dist/blockwriteai.js"></script>
297
+ <script src="/BlockWriteAI/dist/plugins/blockwriteai-code-assist.js"></script>
298
+ <script src="/BlockWriteAI/dist/plugins/blockwriteai-advanced-blocks.js"></script>
299
+
300
+ <script>
293
301
  const editor = new BlockWriteAI({
294
302
  holder: "#editor",
295
303
  maxWidth: "960px", // optional; omit for full-width editor shell
296
304
  placeholder: "Write something, or press / for blocks",
297
- data: {
298
- blocks: [
299
- {
300
- type: "paragraph",
301
- data: {
302
- text: "Hello from BlockWriteAI",
303
- alignment: "left"
304
- }
305
- }
306
- ]
307
- },
308
- onChange(data) {
309
- console.log("Changed document", data);
310
- },
311
- async onSave(data) {
312
- await fetch("/save-document.php", {
313
- method: "POST",
314
- headers: {
315
- "Content-Type": "application/json"
316
- },
317
- body: JSON.stringify(data)
318
- });
319
- }
305
+ data: {
306
+ blocks: [
307
+ {
308
+ type: "paragraph",
309
+ data: {
310
+ text: "Hello from BlockWriteAI",
311
+ alignment: "left"
312
+ }
313
+ }
314
+ ]
315
+ },
316
+ onChange(data) {
317
+ console.log("Changed document", data);
318
+ },
319
+ async onSave(data) {
320
+ await fetch("/save-document.php", {
321
+ method: "POST",
322
+ headers: {
323
+ "Content-Type": "application/json"
324
+ },
325
+ body: JSON.stringify(data)
326
+ });
327
+ }
320
328
  });
321
329
  </script>
322
330
  ```
@@ -364,23 +372,38 @@ const editor = new BlockWriteAI({
364
372
  });
365
373
  ```
366
374
 
367
- ## AI Plugin
368
-
369
- Load the optional AI plugin only on pages where you want writing assistance. The browser plugin never receives your OpenAI API key; it calls your own server endpoint.
370
-
371
- ```html
372
- <script src="/blockwriteai/blockwriteai.js"></script>
373
- <script src="/blockwriteai/plugins/blockwriteai-ai.js"></script>
374
-
375
- <script>
376
- const editor = new BlockWriteAI({
377
- holder: "#editor",
378
- ai: {
379
- endpoint: "/api/blockwriteai-ai.php"
380
- }
381
- });
382
- </script>
383
- ```
375
+ ## AI Plugin
376
+
377
+ AI is a premium plugin. Load it through the platform premium bundle after license
378
+ verification. The browser plugin never receives your OpenAI API key; it calls
379
+ your own server endpoint.
380
+
381
+ ```html
382
+ <script src="/blockwriteai/blockwriteai.js"></script>
383
+
384
+ <script>
385
+ async function boot() {
386
+ await BlockWriteAI.loadPremiumPlugins({
387
+ licenseKey: "bwai_live_xxxxx",
388
+ endpoint: "/blockwriteai-platform/api/premium_plugins.php",
389
+ features: ["ai"]
390
+ });
391
+
392
+ window.editor = new BlockWriteAI({
393
+ holder: "#editor",
394
+ premium: {
395
+ licenseKey: "bwai_live_xxxxx",
396
+ verifyEndpoint: "/blockwriteai-platform/api/license_verify.php",
397
+ usageEndpoint: "/blockwriteai-platform/api/ai_usage.php"
398
+ },
399
+ ai: {
400
+ endpoint: "/api/blockwriteai-ai.php"
401
+ }
402
+ });
403
+ }
404
+ boot();
405
+ </script>
406
+ ```
384
407
 
385
408
  The included demo endpoint is `examples/api/ai.php`. For local development, copy `examples/api/openai.local.example.php` to `examples/api/openai.local.php` and add a development key, or set `OPENAI_API_KEY` in the server environment. Do not expose the key in browser JavaScript.
386
409
 
@@ -404,14 +427,27 @@ Create any page in your application and add a preview container. BlockWriteAI wi
404
427
 
405
428
  <div class="blockwriteai-preview" data-source="/documents/123.json"></div>
406
429
 
407
- <script src="/blockwriteai/blockwriteai.js"></script>
408
- <script src="/blockwriteai/plugins/blockwriteai-advanced-blocks.js"></script>
409
- <script src="/blockwriteai/plugins/blockwriteai-signature.js"></script>
410
- <script src="/blockwriteai/plugins/blockwriteai-signature-flow.js"></script>
411
- <script>
412
- BlockWriteAI.mountPreviews();
413
- </script>
414
- ```
430
+ <script src="/blockwriteai/blockwriteai.js"></script>
431
+ <script src="/blockwriteai/plugins/blockwriteai-advanced-blocks.js"></script>
432
+ <script>
433
+ BlockWriteAI.mountPreviews();
434
+ </script>
435
+ ```
436
+
437
+ If the preview page must render premium blocks, load the licensed premium bundle
438
+ before `BlockWriteAI.mountPreviews()`:
439
+
440
+ ```js
441
+ async function mountDocumentPreview() {
442
+ await BlockWriteAI.loadPremiumPlugins({
443
+ licenseKey: "bwai_live_xxxxx",
444
+ endpoint: "/blockwriteai-platform/api/premium_plugins.php",
445
+ features: ["drawing", "mermaid", "signature", "signature_flow", "ai"]
446
+ });
447
+ BlockWriteAI.mountPreviews();
448
+ }
449
+ mountDocumentPreview();
450
+ ```
415
451
 
416
452
  The JSON endpoint can return either a BlockWriteAI document directly or an API wrapper such as:
417
453
 
@@ -420,7 +456,7 @@ The JSON endpoint can return either a BlockWriteAI document directly or an API w
420
456
  "ok": true,
421
457
  "data": {
422
458
  "time": 1779540000000,
423
- "version": "1.0.7",
459
+ "version": "1.0.9",
424
460
  "blocks": []
425
461
  }
426
462
  }
@@ -524,7 +560,7 @@ BlockWriteAI.mountPreviews(); // Render JSON into .blockwriteai-preview containe
524
560
  ```json
525
561
  {
526
562
  "time": 1779540000000,
527
- "version": "1.0.7",
563
+ "version": "1.0.9",
528
564
  "blocks": [
529
565
  {
530
566
  "id": "block-title",
@@ -614,8 +650,8 @@ After a release is ready, publish each package from the repository root.
614
650
  Create a Git release tag for script-link/CDN users:
615
651
 
616
652
  ```bash
617
- git tag v1.0.7
618
- git push origin v1.0.7
653
+ git tag v1.0.9
654
+ git push origin v1.0.9
619
655
  ```
620
656
 
621
657
  Publish npm:
@@ -13,7 +13,7 @@
13
13
  })(typeof window !== "undefined" ? window : this, function () {
14
14
  "use strict";
15
15
 
16
- var VERSION = "1.0.7";
16
+ var VERSION = "1.0.9";
17
17
  var HISTORY_LIMIT = 80;
18
18
  var BLOCK_TYPES = [
19
19
  "paragraph",
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * BlockWriteAI Advanced Blocks Plugin
3
- * Adds chart, Mermaid, LaTeX, audio, drawing, and columns/layout blocks.
3
+ * Adds chart, LaTeX, audio, and columns/layout blocks.
4
4
  */
5
5
  (function (global) {
6
6
  "use strict";
@@ -2017,63 +2017,6 @@
2017
2017
  }
2018
2018
  });
2019
2019
 
2020
- BlockWriteAI.registerTool("mermaid", {
2021
- label: "Mermaid",
2022
- hint: "Visual diagrams and flowcharts",
2023
- icon: "diagram-project",
2024
- premiumFeature: "mermaid",
2025
- defaultData: { code: "", alignment: "center", openOnInsert: true, diagram: { elements: [], workspace: defaultMermaidWorkspace() } },
2026
- render: function (body, data) {
2027
- injectStyles();
2028
- var editor = this;
2029
- var wrap = el("div", "rbe-plugin-card rbe-mermaid-block rbe-mermaid-card");
2030
- wrap.tabIndex = 0;
2031
- var alignment = data.alignment === "left" || data.alignment === "right" ? data.alignment : "center";
2032
- var workspace = normalizeDiagramWorkspace(data);
2033
- wrap.innerHTML =
2034
- '<input type="hidden" class="rbe-mermaid-alignment" value="' + esc(alignment) + '">' +
2035
- '<input type="hidden" class="rbe-mermaid-diagram-json">' +
2036
- '<textarea class="rbe-mermaid-code" hidden></textarea>' +
2037
- '<input class="rbe-mermaid-workspace-w" type="hidden" value="' + esc(workspace.width) + '">' +
2038
- '<input class="rbe-mermaid-workspace-h" type="hidden" value="' + esc(workspace.height) + '">' +
2039
- '<input class="rbe-mermaid-zoom" type="hidden" value="' + esc(workspace.zoom) + '">' +
2040
- '<div class="rbe-mermaid-card-head"><span class="rbe-mermaid-card-title">Mermaid drawing</span><button type="button" class="rbe-mermaid-edit-btn" title="Edit drawing"' + (editor.readOnly ? " hidden" : "") + '>' + faIcon("pen-to-square", "Edit drawing") + '</button></div>' +
2041
- '<div class="rbe-mermaid-card-preview"></div>';
2042
- body.appendChild(wrap);
2043
- var input = wrap.querySelector(".rbe-mermaid-code");
2044
- input.value = data.code || "";
2045
- wrap.querySelector(".rbe-mermaid-diagram-json").value = JSON.stringify(diagramElements(data));
2046
- refreshMermaidCardPreview(wrap);
2047
- wrap.addEventListener("click", function (event) {
2048
- if (!event.target.closest(".rbe-mermaid-edit-btn")) return;
2049
- event.preventDefault();
2050
- if (!editor.readOnly) openMermaidDesignerModal(editor, wrap);
2051
- });
2052
- if (!editor.readOnly && data.openOnInsert === true) {
2053
- setTimeout(function () { openMermaidDesignerModal(editor, wrap); }, 80);
2054
- }
2055
- },
2056
- serialize: function (block) {
2057
- var diagram = [];
2058
- try {
2059
- diagram = JSON.parse((block.querySelector(".rbe-mermaid-diagram-json") || {}).value || "[]");
2060
- } catch (error) {}
2061
- return {
2062
- code: (block.querySelector(".rbe-mermaid-code") || {}).value || "",
2063
- alignment: (block.querySelector(".rbe-mermaid-alignment") || {}).value || "center",
2064
- openOnInsert: false,
2065
- diagram: { elements: diagram.map(normalizeDiagramElement), workspace: readWorkspaceFromBlock(block) }
2066
- };
2067
- },
2068
- toHTML: function (data) {
2069
- var alignment = data.alignment === "left" || data.alignment === "right" ? data.alignment : "center";
2070
- var elements = diagramElements(data);
2071
- return '<figure class="rbe-output-mermaid-wrap rbe-output-align-' + esc(alignment) + '"><div class="rbe-output-mermaid-diagram">' + diagramSvg(elements, "", normalizeDiagramWorkspace(data), null) + "</div></figure>";
2072
- },
2073
- toMarkdown: function (data) {
2074
- return "```mermaid\n" + (data.code || "") + "\n```";
2075
- }
2076
- });
2077
2020
 
2078
2021
  BlockWriteAI.registerTool("latex", {
2079
2022
  label: "LaTeX",
@@ -2164,129 +2107,6 @@
2164
2107
  }
2165
2108
  });
2166
2109
 
2167
- function drawingTextSvgDataUri(text, color) {
2168
- var safeColor = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(color || "") ? color : "#2f1830";
2169
- var words = String(text || "Signature text").trim().split(/\s+/);
2170
- var lineOne = words.slice(0, Math.ceil(words.length / 2)).join(" ");
2171
- var lineTwo = words.slice(Math.ceil(words.length / 2)).join(" ");
2172
- var hasTwoLines = !!lineTwo;
2173
- var svg = [
2174
- '<svg xmlns="http://www.w3.org/2000/svg" width="900" height="320" viewBox="0 0 900 320">',
2175
- '<rect width="900" height="320" rx="16" fill="#ffffff"/>',
2176
- '<text x="450" y="' + (hasTwoLines ? "138" : "176") + '" text-anchor="middle" font-family="Mabriella, \'Brush Script MT\', \'Segoe Script\', \'Lucida Handwriting\', cursive" font-size="' + (hasTwoLines ? "104" : "118") + '" font-weight="400" fill="' + esc(safeColor) + '">' + esc(lineOne) + "</text>",
2177
- hasTwoLines ? '<text x="450" y="244" text-anchor="middle" font-family="Mabriella, \'Brush Script MT\', \'Segoe Script\', \'Lucida Handwriting\', cursive" font-size="104" font-weight="400" fill="' + esc(safeColor) + '">' + esc(lineTwo) + "</text>" : "",
2178
- "</svg>"
2179
- ].join("");
2180
- return "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(svg);
2181
- }
2182
-
2183
- BlockWriteAI.registerTool("drawing", {
2184
- label: "Drawing",
2185
- hint: "Freehand sketch canvas",
2186
- icon: "pen-nib",
2187
- premiumFeature: "drawing",
2188
- defaultData: { image: "", color: "#172033", size: 4, text: "" },
2189
- render: function (body, data) {
2190
- injectStyles();
2191
- var editor = this;
2192
- var wrap = el("div", "rbe-plugin-card rbe-drawing-block");
2193
- wrap.dataset.initialImage = data.image || data.url || data.src || "";
2194
- if (!wrap.dataset.initialImage && data.text) {
2195
- wrap.dataset.initialImage = drawingTextSvgDataUri(data.text, data.color || "#2f1830");
2196
- }
2197
- wrap.dataset.drawingDirty = "false";
2198
- wrap.innerHTML = '<div class="rbe-plugin-toolbar"><label>Pen <input class="rbe-drawing-color" type="color"></label><label>Size <input class="rbe-drawing-size" type="range" min="1" max="24"></label><input class="rbe-drawing-text-input" type="text" placeholder="Type name or text"><button type="button" class="rbe-small-btn rbe-drawing-text-apply" title="Convert text to Mabriella style" aria-label="Convert text to Mabriella style">' + faIcon("wand-magic-sparkles", "Convert text") + '</button><button type="button" class="rbe-small-btn rbe-drawing-clear">' + faIcon("eraser", "Clear drawing") + '</button></div><div class="rbe-drawing-shell"><canvas class="rbe-drawing-canvas" width="900" height="320"></canvas></div>';
2199
- body.appendChild(wrap);
2200
- var canvas = wrap.querySelector("canvas");
2201
- var ctx = canvas.getContext("2d");
2202
- wrap.querySelector(".rbe-drawing-color").value = data.color || "#172033";
2203
- wrap.querySelector(".rbe-drawing-size").value = data.size || 4;
2204
- wrap.querySelector(".rbe-drawing-text-input").value = data.text || "";
2205
- function loadImage(source) {
2206
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2207
- if (!source) return;
2208
- var image = new Image();
2209
- image.onload = function () { ctx.drawImage(image, 0, 0, canvas.width, canvas.height); };
2210
- image.src = source;
2211
- }
2212
- if (wrap.dataset.initialImage) {
2213
- loadImage(wrap.dataset.initialImage);
2214
- }
2215
- var drawing = false;
2216
- function point(event) {
2217
- var rect = canvas.getBoundingClientRect();
2218
- return { x: ((event.clientX - rect.left) / rect.width) * canvas.width, y: ((event.clientY - rect.top) / rect.height) * canvas.height };
2219
- }
2220
- canvas.addEventListener("pointerdown", function (event) {
2221
- drawing = true;
2222
- wrap.dataset.drawingDirty = "true";
2223
- canvas.setPointerCapture(event.pointerId);
2224
- var p = point(event);
2225
- ctx.beginPath();
2226
- ctx.moveTo(p.x, p.y);
2227
- });
2228
- canvas.addEventListener("pointermove", function (event) {
2229
- if (!drawing) return;
2230
- var p = point(event);
2231
- ctx.strokeStyle = wrap.querySelector(".rbe-drawing-color").value;
2232
- ctx.lineWidth = Number(wrap.querySelector(".rbe-drawing-size").value || 4);
2233
- ctx.lineCap = "round";
2234
- ctx.lineJoin = "round";
2235
- ctx.lineTo(p.x, p.y);
2236
- ctx.stroke();
2237
- });
2238
- canvas.addEventListener("pointerup", function () {
2239
- drawing = false;
2240
- editor.changed(true);
2241
- });
2242
- wrap.querySelector(".rbe-drawing-text-apply").addEventListener("click", function () {
2243
- var input = wrap.querySelector(".rbe-drawing-text-input");
2244
- var text = (input.value || "").trim();
2245
- if (!text) {
2246
- input.focus();
2247
- return;
2248
- }
2249
- wrap.dataset.initialImage = drawingTextSvgDataUri(text, wrap.querySelector(".rbe-drawing-color").value || "#2f1830");
2250
- wrap.dataset.drawingDirty = "false";
2251
- loadImage(wrap.dataset.initialImage);
2252
- editor.changed(true);
2253
- });
2254
- wrap.querySelector(".rbe-drawing-clear").addEventListener("click", function () {
2255
- wrap.dataset.initialImage = "";
2256
- wrap.dataset.drawingDirty = "true";
2257
- wrap.querySelector(".rbe-drawing-text-input").value = "";
2258
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2259
- editor.changed(true);
2260
- });
2261
- },
2262
- serialize: function (block) {
2263
- var canvas = block.querySelector(".rbe-drawing-canvas");
2264
- var drawingBlock = block.querySelector(".rbe-drawing-block") || block;
2265
- var initialImage = drawingBlock.dataset.initialImage || "";
2266
- if (initialImage && drawingBlock.dataset.drawingDirty !== "true") {
2267
- return {
2268
- image: initialImage,
2269
- color: (block.querySelector(".rbe-drawing-color") || {}).value || "#172033",
2270
- size: Number((block.querySelector(".rbe-drawing-size") || {}).value || 4),
2271
- text: (block.querySelector(".rbe-drawing-text-input") || {}).value || ""
2272
- };
2273
- }
2274
- return {
2275
- image: canvas ? canvas.toDataURL("image/png") : "",
2276
- color: (block.querySelector(".rbe-drawing-color") || {}).value || "#172033",
2277
- size: Number((block.querySelector(".rbe-drawing-size") || {}).value || 4),
2278
- text: (block.querySelector(".rbe-drawing-text-input") || {}).value || ""
2279
- };
2280
- },
2281
- toHTML: function (data) {
2282
- var image = data.image || data.url || data.src || (data.diagram && data.diagram.image) || "";
2283
- if (!image && data.text) image = drawingTextSvgDataUri(data.text, data.color || "#2f1830");
2284
- return image ? '<figure class="rbe-output-drawing"><img src="' + esc(image) + '" alt="Drawing"></figure>' : "";
2285
- },
2286
- toMarkdown: function () {
2287
- return "![Drawing](drawing.png)";
2288
- }
2289
- });
2290
2110
 
2291
2111
  BlockWriteAI.registerTool("columns", {
2292
2112
  label: "Columns",
@@ -533,38 +533,70 @@
533
533
  });
534
534
  }
535
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
+
536
590
  var originalCreateTopToolbar = BlockWriteAI.prototype.createTopToolbar;
537
591
  BlockWriteAI.prototype.createTopToolbar = function () {
538
592
  var toolbar = originalCreateTopToolbar.call(this);
539
- if (this.options.ai === false) return toolbar;
540
- injectStyles();
541
- toolbar.appendChild(
542
- el("button", "rbe-history-btn rbe-ai-toolbar-btn", {
543
- type: "button",
544
- title: "BlockWriteAI",
545
- html: faIcon("wand-magic-sparkles", "BlockWriteAI"),
546
- dataset: { aiToolbar: "true", premiumFeature: "ai", unlockedTitle: "BlockWriteAI" }
547
- })
548
- );
549
- refreshAIAvailability(this);
550
- this.root.addEventListener("rbe:premium-change", refreshAIAvailability.bind(null, this));
551
- return toolbar;
593
+ return ensureTopToolbarAIButton(this, toolbar);
552
594
  };
553
595
 
554
596
  var originalCreateInlineToolbar = BlockWriteAI.prototype.createInlineToolbar;
555
597
  BlockWriteAI.prototype.createInlineToolbar = function () {
556
598
  var toolbar = originalCreateInlineToolbar.call(this);
557
- if (this.options.ai === false) return toolbar;
558
- injectStyles();
559
- var button = el("button", "rbe-ai-inline-btn", {
560
- type: "button",
561
- title: "BlockWriteAI",
562
- html: faIcon("wand-magic-sparkles", "AI"),
563
- dataset: { aiInline: "true", premiumFeature: "ai", unlockedTitle: "BlockWriteAI" }
564
- });
565
- toolbar.appendChild(button);
566
- refreshAIAvailability(this);
567
- return toolbar;
599
+ return ensureInlineAIButton(this, toolbar);
568
600
  };
569
601
 
570
602
  BlockWriteAI.prototype.openAI = function () {
@@ -633,5 +665,7 @@
633
665
  return BlockWriteAI;
634
666
  };
635
667
 
668
+ hydrateExistingEditors();
669
+
636
670
  return BlockWriteAI;
637
671
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qarakash/blockwriteai",
3
- "version": "1.0.7",
4
- "description": "BlockWriteAI JSON-first drop-in block writing editor with media, diagrams, signatures, AI writing, optional history, and preview mounting.",
3
+ "version": "1.0.9",
4
+ "description": "BlockWriteAI JSON-first drop-in block writing editor with free core blocks, preview mounting, and license-gated premium plugins.",
5
5
  "keywords": [
6
6
  "blockwriteai",
7
7
  "editor",