@qarakash/blockwriteai 1.0.8 → 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,2282 +0,0 @@
1
- /*!
2
- * BlockWriteAI Advanced Blocks Plugin
3
- * Adds chart, LaTeX, audio, and columns/layout blocks.
4
- */
5
- (function (global) {
6
- "use strict";
7
-
8
- var BlockWriteAI = global.BlockWriteAI;
9
- if (!BlockWriteAI || BlockWriteAI.__advancedBlocksInstalled) return;
10
- BlockWriteAI.__advancedBlocksInstalled = true;
11
-
12
- var H = BlockWriteAI.helpers || {};
13
- var el = H.el;
14
- var esc = H.escapeHTML || function (value) {
15
- return String(value == null ? "" : value).replace(/[&<>"]/g, function (ch) {
16
- return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[ch];
17
- });
18
- };
19
- var sanitize = H.sanitizeHTML || function (value) { return String(value || ""); };
20
- var inline = H.renderOutputInline || sanitize;
21
- var faIcon = H.faIcon || function (name, label) {
22
- return '<i class="fa-solid fa-' + esc(name) + '" aria-hidden="true"></i><span class="rbe-sr-only">' + esc(label || name) + "</span>";
23
- };
24
-
25
- function injectStyles() {
26
- if (document.querySelector("style[data-blockwriteai-advanced-blocks]")) return;
27
- var style = document.createElement("style");
28
- style.dataset.blockwriteaiAdvancedBlocks = "true";
29
- style.textContent =
30
- ".rbe-plugin-card{border:1px solid var(--rbe-border,#d7dee8);border-radius:8px;background:#fff;padding:12px;display:grid;gap:12px;}" +
31
- ".rbe-plugin-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}" +
32
- ".rbe-plugin-toolbar label{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:750;color:#667085;}" +
33
- ".rbe-plugin-toolbar input,.rbe-plugin-toolbar select,.rbe-plugin-card textarea{border:1px solid var(--rbe-border,#d7dee8);border-radius:7px;background:#fff;color:#172033;font:14px/1.4 Arial,sans-serif;padding:8px 9px;}" +
34
- ".rbe-plugin-toolbar input[type='color']{width:40px;min-width:40px;height:34px;padding:3px;}" +
35
- ".rbe-plugin-card textarea{width:100%;min-height:150px;resize:vertical;font-family:Consolas,'Liberation Mono',monospace;}" +
36
- ".rbe-plugin-preview{border:1px dashed #cbd5e1;border-radius:8px;background:#f8fbff;min-height:120px;padding:12px;overflow:auto;}" +
37
- ".rbe-chart-block{position:relative;}" +
38
- ".rbe-chart-preview-wrap{position:relative;}" +
39
- ".rbe-chart-type-bar{position:absolute;left:50%;top:50%;z-index:4;display:flex;gap:6px;align-items:center;border:1px solid #cbd5e1;border-radius:999px;background:rgba(255,255,255,.96);padding:7px;box-shadow:0 16px 40px rgba(15,23,42,.16);opacity:0;pointer-events:none;transform:translate(-50%,-50%) scale(.96);transition:opacity .16s ease,transform .16s ease;}" +
40
- ".rbe-chart-preview-wrap:hover .rbe-chart-type-bar,.rbe-chart-type-bar:focus-within{opacity:1;pointer-events:auto;transform:translate(-50%,-50%) scale(1);}" +
41
- ".rbe-chart-type-btn{width:34px;height:34px;border:1px solid #d7dee8;border-radius:999px;background:#fff;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
42
- ".rbe-chart-type-btn:hover,.rbe-chart-type-btn.is-active{border-color:#2563eb;background:#eaf1ff;color:#1d4ed8;}" +
43
- ".rbe-chart-slice-colors{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}" +
44
- ".rbe-chart-slice-colors[hidden],.rbe-chart-series-color[hidden]{display:none!important;}" +
45
- ".rbe-chart-slice-label{font-size:12px;font-weight:800;color:#667085;}" +
46
- ".rbe-chart-slice-list{display:flex;flex-wrap:wrap;gap:6px;align-items:center;}" +
47
- ".rbe-chart-slice-chip{display:inline-flex;align-items:center;gap:4px;border:1px solid #d7dee8;border-radius:999px;background:#fff;padding:3px 6px;font-size:11px;font-weight:750;color:#475467;}" +
48
- ".rbe-chart-slice-chip input{width:28px!important;min-width:28px!important;height:24px!important;padding:2px!important;border-radius:999px;}" +
49
- ".rbe-chart-preview svg,.rbe-output-chart svg{width:100%;height:auto;max-height:300px;}" +
50
- ".rbe-output-chart,.rbe-output-mermaid,.rbe-output-latex,.rbe-output-audio,.rbe-output-drawing,.rbe-output-columns{margin:0 0 15px;}" +
51
- ".rbe-output-chart{border:1px solid #d7dee8;border-radius:8px;padding:12px;background:#fff;}" +
52
- ".rbe-output-chart-title{display:block;margin-bottom:8px;font-weight:760;}" +
53
- ".rbe-chart-caption{width:min(560px,100%);min-height:42px;margin:0 auto;border:1px solid var(--rbe-border,#d7dee8);border-radius:8px;background:#fff;padding:10px 12px;text-align:center;outline:none;}" +
54
- ".rbe-chart-caption:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}" +
55
- ".rbe-chart-caption:empty:before{content:attr(data-placeholder);color:#98a2b3;}" +
56
- ".rbe-output-chart-caption{display:block;margin:10px auto 0;color:#475467;font-size:13px;font-weight:650;}" +
57
- ".rbe-latex-preview,.rbe-output-latex{font-family:Georgia,'Times New Roman',serif;font-size:20px;line-height:1.6;text-align:center;font-style:italic;}" +
58
- ".rbe-output-latex{display:block;width:100%;}" +
59
- ".rbe-latex-frac{display:inline-grid;grid-template-rows:auto auto;vertical-align:middle;text-align:center;line-height:1.1;}.rbe-latex-frac span:first-child{border-bottom:1px solid currentColor;padding:0 2px;}" +
60
- ".rbe-output-align-left{text-align:left;}" +
61
- ".rbe-output-align-center{text-align:center;}" +
62
- ".rbe-output-align-right{text-align:right;}" +
63
- ".rbe-output-align-center>.rbe-output-mermaid-diagram,.rbe-output-align-center>.rbe-output-mermaid,.rbe-output-align-center>svg{margin-left:auto;margin-right:auto;}" +
64
- ".rbe-output-align-right>.rbe-output-mermaid-diagram,.rbe-output-align-right>.rbe-output-mermaid,.rbe-output-align-right>svg{margin-left:auto;margin-right:0;}" +
65
- ".rbe-mermaid-block{gap:10px;}" +
66
- ".rbe-mermaid-card{position:relative;min-height:220px;display:grid;gap:10px;align-items:stretch;border:1px solid #d7dee8;border-radius:10px;background:#fbfdff;padding:14px;}" +
67
- ".rbe-mermaid-card-head{display:flex;align-items:center;justify-content:space-between;gap:10px;}" +
68
- ".rbe-mermaid-card-title{font-weight:820;color:#172033;font-size:14px;}" +
69
- ".rbe-mermaid-card-preview{min-height:160px;display:grid;place-items:center;border:1px dashed #cbd5e1;border-radius:9px;background:#fff;padding:12px;overflow:auto;}" +
70
- ".rbe-mermaid-card-preview .rbe-output-mermaid-diagram{max-width:100%;width:100%;}" +
71
- ".rbe-mermaid-card-empty{color:#667085;font-weight:750;font-size:13px;}" +
72
- ".rbe-mermaid-edit-btn{position:absolute;right:12px;top:12px;width:34px;height:34px;border:1px solid #cbd5e1;border-radius:999px;background:#fff;color:#172033;display:grid;place-items:center;cursor:pointer;box-shadow:0 12px 28px rgba(15,23,42,.12);}" +
73
- ".rbe-mermaid-edit-btn:hover,.rbe-mermaid-edit-btn:focus-visible{border-color:#2563eb;color:#1d4ed8;background:#eaf1ff;}" +
74
- ".rbe-mermaid-modal-open{overflow:hidden!important;}" +
75
- ".rbe-mermaid-modal{position:fixed;inset:0;z-index:10000;background:rgba(15,23,42,.46);display:grid;place-items:stretch;padding:22px;}" +
76
- ".rbe-mermaid-dialog{display:grid;grid-template-rows:auto 1fr auto;min-height:0;border-radius:14px;background:#fff;box-shadow:0 28px 80px rgba(15,23,42,.32);overflow:hidden;}" +
77
- ".rbe-mermaid-modal-head,.rbe-mermaid-modal-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid #e2e8f0;}" +
78
- ".rbe-mermaid-modal-foot{border-top:1px solid #e2e8f0;border-bottom:0;justify-content:flex-end;}" +
79
- ".rbe-mermaid-modal-title{margin:0;font-size:17px;font-weight:820;color:#172033;}" +
80
- ".rbe-mermaid-close{width:34px;height:34px;border:1px solid #cbd5e1;border-radius:999px;background:#fff;display:grid;place-items:center;cursor:pointer;}" +
81
- ".rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{border:1px solid #cbd5e1;border-radius:8px;background:#fff;color:#172033;font-weight:800;padding:9px 14px;cursor:pointer;}" +
82
- ".rbe-mermaid-modal-save{border-color:#2563eb;background:#2563eb;color:#fff;}" +
83
- ".rbe-mermaid-modal-body{min-height:0;overflow:hidden;padding:0;background:#f8fafc;}" +
84
- ".rbe-mermaid-modal .rbe-mermaid-block{height:100%;border:0;border-radius:0;background:transparent;padding:0;display:grid;grid-template-rows:auto 1fr;gap:0;}" +
85
- ".rbe-mermaid-toolbar{display:flex;flex-wrap:wrap;gap:7px;align-items:center;padding:8px;border:1px solid #d7dee8;border-radius:12px;background:#eef4fb;}" +
86
- ".rbe-mermaid-modal .rbe-mermaid-toolbar{position:relative;z-index:5;border-width:0 0 1px;border-radius:0;background:#edf2f8;padding:9px 14px;}" +
87
- ".rbe-mermaid-tool,.rbe-mermaid-icon-btn{width:34px;height:34px;border:1px solid transparent;border-radius:8px;background:transparent;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
88
- ".rbe-mermaid-icon-btn{border-color:#d7dee8;background:#fff;}" +
89
- ".rbe-mermaid-tool:hover,.rbe-mermaid-tool.is-active,.rbe-mermaid-icon-btn:hover,.rbe-mermaid-icon-btn.is-active{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8;}" +
90
- ".rbe-mermaid-tool:disabled,.rbe-mermaid-icon-btn:disabled{opacity:.45;cursor:not-allowed;}" +
91
- ".rbe-mermaid-menu{position:relative;}" +
92
- ".rbe-mermaid-menu-btn{width:34px;height:34px;border:1px solid transparent;border-radius:8px;background:transparent;color:#344054;display:inline-grid;place-items:center;cursor:pointer;}" +
93
- ".rbe-mermaid-menu-btn:hover,.rbe-mermaid-menu:focus-within>.rbe-mermaid-menu-btn{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8;}" +
94
- ".rbe-mermaid-popover{position:absolute;left:0;top:calc(100% + 6px);z-index:12;display:none;min-width:166px;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 16px 34px rgba(15,23,42,.18);padding:6px;}" +
95
- ".rbe-mermaid-menu:focus-within>.rbe-mermaid-popover,.rbe-mermaid-menu:hover>.rbe-mermaid-popover{display:block;}" +
96
- ".rbe-mermaid-menu-row{position:relative;display:flex;align-items:center;gap:10px;min-height:34px;padding:7px 8px;border-radius:6px;font-size:14px;color:#344054;white-space:nowrap;}" +
97
- ".rbe-mermaid-menu-row:hover{background:#eef2f7;}" +
98
- ".rbe-mermaid-menu-row:after{content:'›';margin-left:auto;color:#667085;}" +
99
- ".rbe-mermaid-submenu{position:absolute;left:100%;top:-7px;display:none;min-width:270px;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 16px 34px rgba(15,23,42,.18);padding:8px;}" +
100
- ".rbe-mermaid-menu-row:hover>.rbe-mermaid-submenu{display:block;}" +
101
- ".rbe-mermaid-popover:not(:has(.rbe-mermaid-menu-row:hover)) .rbe-mermaid-menu-row:first-child>.rbe-mermaid-submenu{display:block;}" +
102
- ".rbe-mermaid-menu-row-icon{width:18px;display:inline-grid;place-items:center;color:#344054;}" +
103
- ".rbe-mermaid-choice-grid{display:grid;grid-template-columns:repeat(10,22px);gap:7px;align-items:center;}" +
104
- ".rbe-mermaid-choice-grid:has(+ .rbe-mermaid-choice-grid){border-bottom:1px solid #e5e7eb;padding-bottom:8px;margin-bottom:8px;}" +
105
- ".rbe-mermaid-choice{width:22px;height:22px;border:0;background:transparent;color:#111827;display:grid;place-items:center;cursor:pointer;font:18px/1 Arial,sans-serif;border-radius:4px;}" +
106
- ".rbe-mermaid-choice:hover,.rbe-mermaid-choice.is-active{background:#dbeafe;color:#1d4ed8;}" +
107
- ".rbe-mermaid-line-menu .rbe-mermaid-popover{min-width:210px;}" +
108
- ".rbe-mermaid-line-menu .rbe-mermaid-menu-row:after{content:'';}" +
109
- ".rbe-mermaid-line-choice{width:100%;border:0;border-radius:6px;background:transparent;color:#344054;display:flex;align-items:center;gap:10px;padding:8px;text-align:left;cursor:pointer;font:14px/1.2 Arial,sans-serif;}" +
110
- ".rbe-mermaid-line-choice:hover{background:#eef2f7;color:#1d4ed8;}" +
111
- ".rbe-mermaid-menu-select,.rbe-mermaid-action-select,.rbe-mermaid-stylebar select,.rbe-mermaid-workspace input{border:1px solid #d7dee8;border-radius:8px;background:#fff;color:#172033;font:13px/1.2 Arial,sans-serif;padding:7px 9px;max-width:156px;}" +
112
- ".rbe-mermaid-workspace input{width:72px;}" +
113
- ".rbe-mermaid-stylebar,.rbe-mermaid-workspace{display:flex;flex-wrap:wrap;gap:7px;align-items:center;border:1px solid #d7dee8;border-radius:10px;background:#fbfdff;padding:6px;}" +
114
- ".rbe-mermaid-stylebar[hidden]{display:none!important;}" +
115
- ".rbe-mermaid-stylebar label,.rbe-mermaid-workspace label{display:inline-flex;gap:6px;align-items:center;font-size:12px;font-weight:800;color:#667085;}" +
116
- ".rbe-mermaid-stylebar label{height:32px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:3px 6px;color:#344054;}" +
117
- ".rbe-mermaid-stylebar label:hover{border-color:#93c5fd;background:#eef6ff;color:#1d4ed8;}" +
118
- ".rbe-mermaid-stylebar .rbe-mermaid-icon-control,.rbe-mermaid-stylebar .rbe-mermaid-style-btn{position:relative;width:34px;height:32px;box-sizing:border-box;border:1px solid transparent;border-radius:8px;background:#fff;color:#344054;display:inline-grid;place-items:center;padding:0;cursor:pointer;}" +
119
- ".rbe-mermaid-stylebar .rbe-mermaid-icon-control:hover,.rbe-mermaid-stylebar .rbe-mermaid-style-btn:hover,.rbe-mermaid-stylebar .rbe-mermaid-style-btn.is-active{border-color:#93c5fd;background:#dbeafe;color:#1d4ed8;}" +
120
- ".rbe-mermaid-stylebar .rbe-mermaid-icon-control input,.rbe-mermaid-stylebar .rbe-mermaid-icon-control select{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;opacity:0!important;cursor:pointer;}" +
121
- ".rbe-mermaid-control-swatch{position:absolute;left:7px;right:7px;bottom:3px;height:3px;border-radius:999px;background:#2563eb;pointer-events:none;}" +
122
- ".rbe-mermaid-fill-swatch{background:var(--rbe-mermaid-fill,#eef5ff);}" +
123
- ".rbe-mermaid-stroke-swatch{background:var(--rbe-mermaid-stroke,#111827);}" +
124
- ".rbe-mermaid-text-swatch{background:var(--rbe-mermaid-text,#172033);}" +
125
- ".rbe-mermaid-highlight-swatch{background:var(--rbe-mermaid-highlight,transparent);}" +
126
- ".rbe-mermaid-stylebar.is-line-selection .rbe-mermaid-fill-control,.rbe-mermaid-stylebar.is-line-selection .rbe-mermaid-text-control{display:none;}" +
127
- ".rbe-mermaid-stylebar:not(.is-text-capable) .rbe-mermaid-text-control{display:none;}" +
128
- ".rbe-mermaid-stylebar .rbe-mermaid-hidden-control{display:none!important;}" +
129
- ".rbe-mermaid-hint{font-size:12px;font-weight:700;color:#667085;}" +
130
- ".rbe-mermaid-stylebar input[type='color']{width:28px;height:24px;padding:1px;border:1px solid #d7dee8;border-radius:6px;background:#fff;}" +
131
- ".rbe-mermaid-stylebar input[type='range']{width:90px;}" +
132
- ".rbe-mermaid-stylebar select{height:24px;max-width:86px;padding:2px 5px;font-size:12px;}" +
133
- ".rbe-mermaid-stylebar .rbe-mermaid-icon-control.is-active{border-color:#93c5fd;background:#dbeafe;color:#1d4ed8;}" +
134
- ".rbe-mermaid-toolbar-sep{width:1px;height:24px;background:#cbd5e1;margin:0 2px;}" +
135
- ".rbe-mermaid-stage-shell{display:grid;grid-template-columns:28px minmax(0,1fr);grid-template-rows:26px minmax(0,1fr);min-height:0;height:100%;background:#fff;}" +
136
- ".rbe-mermaid-ruler-corner{grid-column:1;grid-row:1;border-right:1px solid #cbd5e1;border-bottom:1px solid #cbd5e1;background:#f8fafc;}" +
137
- ".rbe-mermaid-top-ruler{grid-column:2;grid-row:1;position:relative;border-bottom:1px solid #cbd5e1;background:#f8fafc;background-image:repeating-linear-gradient(90deg,#cbd5e1 0,#cbd5e1 1px,transparent 1px,transparent 10px),repeating-linear-gradient(90deg,#94a3b8 0,#94a3b8 1px,transparent 1px,transparent 100px);}" +
138
- ".rbe-mermaid-side-ruler{grid-column:1;grid-row:2;position:relative;border-right:1px solid #cbd5e1;background:#f8fafc;background-image:repeating-linear-gradient(0deg,#cbd5e1 0,#cbd5e1 1px,transparent 1px,transparent 10px),repeating-linear-gradient(0deg,#94a3b8 0,#94a3b8 1px,transparent 1px,transparent 100px);}" +
139
- ".rbe-mermaid-top-ruler span,.rbe-mermaid-side-ruler span{position:absolute;color:#475467;font:11px/1 Arial,sans-serif;user-select:none;}" +
140
- ".rbe-mermaid-top-ruler span{top:5px;transform:translateX(-50%);}" +
141
- ".rbe-mermaid-side-ruler span{left:8px;transform:translateY(-50%) rotate(-90deg);transform-origin:center;}" +
142
- ".rbe-mermaid-stage{position:relative;border:1px solid #d7dee8;border-radius:8px;background-color:#fff;background-image:linear-gradient(45deg,#f8fafc 25%,transparent 25%),linear-gradient(-45deg,#f8fafc 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#f8fafc 75%),linear-gradient(-45deg,transparent 75%,#f8fafc 75%);background-position:0 0,0 12px,12px -12px,-12px 0;background-size:24px 24px;overflow:auto;resize:vertical;min-height:320px;max-height:560px;}" +
143
- ".rbe-mermaid-modal .rbe-mermaid-stage{grid-column:2;grid-row:2;height:100%;min-height:0;max-height:none;border:0;border-radius:0;resize:none;}" +
144
- ".rbe-mermaid-canvas{display:block;width:auto;height:auto;min-width:100%;touch-action:none;cursor:crosshair;background:rgba(255,255,255,.72);}" +
145
- ".rbe-mermaid-canvas [data-el-id]{cursor:move;}" +
146
- ".rbe-mermaid-canvas .is-selected{outline:none;filter:drop-shadow(0 0 0 #2563eb);}" +
147
- ".rbe-mermaid-selected-stroke{stroke:#2563eb!important;stroke-width:2!important;fill:none!important;pointer-events:none;}" +
148
- ".rbe-mermaid-handle{fill:#fff;stroke:#2563eb;stroke-width:2;cursor:nwse-resize;transition:fill .12s ease;}" +
149
- ".rbe-mermaid-handle:hover{fill:var(--rbe-handle-color,#2563eb);}" +
150
- ".rbe-mermaid-handle[data-handle='ne'],.rbe-mermaid-handle[data-handle='sw']{cursor:nesw-resize;}" +
151
- ".rbe-mermaid-handle[data-handle='n'],.rbe-mermaid-handle[data-handle='s']{cursor:ns-resize;}" +
152
- ".rbe-mermaid-handle[data-handle='e'],.rbe-mermaid-handle[data-handle='w']{cursor:ew-resize;}" +
153
- ".rbe-mermaid-rotate{cursor:grab;}" +
154
- ".rbe-mermaid-rotate circle{fill:#2563eb;stroke:#fff;stroke-width:2;}" +
155
- ".rbe-mermaid-rotate text{fill:#fff;stroke:none;font:800 12px/1 Arial,sans-serif;pointer-events:none;}" +
156
- ".rbe-mermaid-rotate-icon{pointer-events:none;}" +
157
- ".rbe-mermaid-marquee{fill:rgba(37,99,235,.09);stroke:#2563eb;stroke-width:1.5;stroke-dasharray:6 4;pointer-events:none;}" +
158
- ".rbe-mermaid-arrow-origin{fill:#fff;stroke:#2563eb;stroke-width:2;pointer-events:none;}" +
159
- ".rbe-mermaid-hidden-text{visibility:hidden!important;}" +
160
- ".rbe-mermaid-text-editor{position:absolute;z-index:20;box-sizing:border-box;border:0;border-radius:0;background:transparent;outline:none;overflow:hidden;resize:none;color:#172033;box-shadow:none;text-align:center;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;caret-color:#0f172a;font-weight:400;}" +
161
- ".rbe-mermaid-text-editor::placeholder{color:#667085;opacity:.62;}" +
162
- ".rbe-mermaid-options{display:flex;flex-wrap:wrap;gap:8px;align-items:center;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:8px;}" +
163
- ".rbe-mermaid-options[hidden]{display:none;}" +
164
- ".rbe-mermaid-options input,.rbe-mermaid-options select{border:1px solid #d7dee8;border-radius:7px;padding:7px 8px;}" +
165
- ".rbe-mermaid-source{margin-top:0;}" +
166
- ".rbe-mermaid-source summary{cursor:pointer;font-weight:750;color:#475467;}" +
167
- ".rbe-output-mermaid-diagram{display:block;max-width:100%;max-height:560px;overflow:auto;border:1px solid #d7dee8;border-radius:8px;background:#fff;}" +
168
- ".rbe-output-mermaid-diagram svg{display:block;width:auto;max-width:none;height:auto;min-width:100%;border:0;border-radius:0;background:#fff;}" +
169
- ".rbe-output-mermaid-error{display:inline-block;border:1px solid #fecaca;border-radius:8px;background:#fff1f2;color:#991b1b;padding:10px 12px;font:13px/1.45 Consolas,'Liberation Mono',monospace;white-space:pre-wrap;}" +
170
- ".rbe-audio-dropzone{min-height:170px;}" +
171
- ".rbe-audio-error{border:1px solid #fecaca;border-radius:8px;background:#fff1f2;color:#991b1b;padding:8px 10px;font-size:13px;font-weight:700;}" +
172
- ".rbe-audio-card{display:grid;gap:10px;justify-items:center;text-align:center;}" +
173
- ".rbe-audio-card audio,.rbe-output-audio audio{width:min(560px,100%);}" +
174
- ".rbe-output-audio{display:grid;justify-items:center;text-align:center;}" +
175
- ".rbe-output-audio figcaption{font-weight:760;margin-bottom:8px;}" +
176
- ".rbe-drawing-text-input{min-width:220px;flex:1 1 260px;}" +
177
- ".rbe-drawing-text-apply{min-width:38px;}" +
178
- ".rbe-drawing-shell{display:grid;gap:10px;}" +
179
- ".rbe-drawing-canvas{width:100%;height:260px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;touch-action:none;}" +
180
- ".rbe-output-drawing img{max-width:100%;border:1px solid #d7dee8;border-radius:8px;background:#fff;}" +
181
- ".rbe-columns-block{overflow-x:auto;}" +
182
- ".rbe-columns-grid,.rbe-output-columns{display:grid;gap:12px;}" +
183
- ".rbe-column-field{display:grid;gap:8px;}" +
184
- ".rbe-column-header{min-height:38px;border:1px solid #d7dee8;border-radius:8px;padding:9px 10px;font-weight:760;background:#eef5ff!important;outline:none;}" +
185
- ".rbe-column-header:focus,.rbe-column-editor:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,.12);}" +
186
- ".rbe-column-header:empty:before{content:attr(data-placeholder);color:#98a2b3;font-weight:650;}" +
187
- ".rbe-column-editor{min-height:120px;border:1px solid #d7dee8;border-radius:8px;padding:10px;background:#fbfdff;outline:none;}" +
188
- ".rbe-column-editor:empty:before{content:attr(data-placeholder);color:#98a2b3;}" +
189
- ".rbe-output-columns{grid-template-columns:repeat(var(--rbe-columns,2),minmax(180px,1fr));overflow-x:auto;}" +
190
- ".rbe-output-column{border:1px solid #e2e8f0;border-radius:8px;padding:12px;background:#fff;}" +
191
- ".rbe-output-column-header{margin:-12px -12px 10px;padding:10px 12px;border-bottom:1px solid #e2e8f0;border-radius:8px 8px 0 0;background:#eef5ff;font-weight:760;text-align:left;}" +
192
- ".rbe-output-column-body{min-width:0;}" +
193
- ".rbe-image-stage.is-media-menu-open{min-width:176px;}" +
194
- ".rbe-image-transform-panel{position:absolute;left:50%;top:10px;z-index:4;transform:translateX(-50%);display:grid;grid-template-columns:repeat(3,30px);gap:5px;align-items:center;justify-content:center;max-width:calc(100% - 16px);opacity:0;pointer-events:none;transition:opacity .18s ease,transform .18s ease;}" +
195
- ".rbe-image-stage.is-media-menu-open>.rbe-image-transform-panel{opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}" +
196
- ".rbe-image-transform-panel .rbe-media-action{position:static;left:auto;top:auto;width:30px;height:30px;opacity:1;pointer-events:auto;transform:none;box-shadow:0 8px 18px rgba(15,23,42,.14);}" +
197
- ".rbe-image-transform.is-active{background:#2563eb;color:#fff;}" +
198
- "@media (max-width:900px){.rbe-plugin-toolbar input,.rbe-plugin-toolbar select,.rbe-plugin-card textarea{min-width:0;max-width:100%;}.rbe-chart-preview svg,.rbe-output-chart svg{max-height:260px;}.rbe-mermaid-modal{padding:10px;}.rbe-mermaid-dialog{border-radius:10px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{overflow-x:auto;align-content:flex-start;}.rbe-mermaid-popover{max-width:calc(100vw - 28px);max-height:62dvh;overflow:auto;}.rbe-mermaid-submenu{max-width:calc(100vw - 44px);max-height:58dvh;overflow:auto;}.rbe-output-mermaid-diagram{max-height:60dvh;}.rbe-columns-grid{grid-template-columns:1fr!important;}.rbe-output-columns{grid-template-columns:1fr!important;}}" +
199
- "@media (max-width:640px){.rbe-plugin-card{padding:10px;gap:10px;}.rbe-plugin-toolbar{display:grid;grid-template-columns:1fr;align-items:stretch;}.rbe-plugin-toolbar label{width:100%;justify-content:space-between;}.rbe-plugin-toolbar input,.rbe-plugin-toolbar select{width:100%;}.rbe-chart-type-bar{top:auto;bottom:10px;transform:translate(-50%,0) scale(.96);}.rbe-chart-preview-wrap:hover .rbe-chart-type-bar,.rbe-chart-type-bar:focus-within{transform:translate(-50%,0) scale(1);}.rbe-chart-slice-chip{width:100%;justify-content:space-between;}.rbe-latex-preview,.rbe-output-latex{font-size:18px;}.rbe-mermaid-modal{padding:0;}.rbe-mermaid-dialog{border-radius:0;}.rbe-mermaid-modal-head,.rbe-mermaid-modal-foot{padding:10px 12px;}.rbe-mermaid-modal-foot{flex-wrap:wrap;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{flex:1 1 140px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{gap:5px;padding:7px 8px;}.rbe-mermaid-tool,.rbe-mermaid-icon-btn,.rbe-mermaid-menu-btn{width:32px;height:32px;}.rbe-mermaid-stage-shell{grid-template-columns:22px minmax(0,1fr);grid-template-rows:22px minmax(0,1fr);}.rbe-mermaid-choice-grid{grid-template-columns:repeat(6,22px);}.rbe-mermaid-submenu{left:0;top:calc(100% + 6px);}.rbe-mermaid-stage{min-height:280px;}.rbe-mermaid-card-preview{min-height:140px;padding:8px;}.rbe-audio-card audio,.rbe-output-audio audio{width:100%;}.rbe-drawing-canvas{height:220px;}.rbe-image-transform-panel{grid-template-columns:repeat(3,28px);gap:4px;}.rbe-image-transform-panel .rbe-media-action{width:28px;height:28px;}.rbe-image-stage.is-media-menu-open{min-width:min(176px,100%);}}" +
200
- "@media (max-width:900px){.rbe-mermaid-card,.rbe-mermaid-card-preview{min-width:0;max-width:100%;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram{width:100%;max-width:100%;}.rbe-mermaid-modal{overflow:hidden;}.rbe-mermaid-dialog{width:100%;max-width:100%;height:calc(100dvh - 20px);max-height:calc(100dvh - 20px);}.rbe-mermaid-stage-shell{width:100%;min-width:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-stage{width:100%;min-width:0;overflow:auto;}.rbe-mermaid-modal .rbe-mermaid-block{min-width:0;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram svg{max-width:100%;}}" +
201
- "@media (max-width:640px){.rbe-mermaid-card{padding:10px;min-height:180px;}.rbe-mermaid-card-preview{width:100%;max-width:100%;overflow:auto;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram{max-height:340px;overflow:hidden;}.rbe-mermaid-card-preview .rbe-output-mermaid-diagram svg{width:100%!important;min-width:0!important;max-width:100%;height:auto!important;}.rbe-mermaid-dialog{height:100dvh;max-height:100dvh;}.rbe-mermaid-modal-body{min-height:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-toolbar{max-width:100%;overflow-x:auto;overflow-y:hidden;white-space:nowrap;}.rbe-mermaid-stage-shell{width:100%;min-width:0;overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-stage{width:100%;min-width:0;overflow:auto;}.rbe-mermaid-top-ruler{min-width:0;}.rbe-mermaid-canvas{min-width:100%;}.rbe-mermaid-toolbar-sep{display:none;}}" +
202
- ".rbe-mermaid-modal .rbe-mermaid-dialog{min-width:0;}" +
203
- ".rbe-mermaid-modal .rbe-mermaid-modal-body{display:grid;min-height:0;}" +
204
- ".rbe-mermaid-modal .rbe-mermaid-block{min-width:0;min-height:0;overflow:hidden;}" +
205
- ".rbe-mermaid-modal .rbe-mermaid-stage-shell{min-width:0;min-height:0;}" +
206
- ".rbe-mermaid-modal .rbe-mermaid-stage{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;}" +
207
- "@media (max-width:900px){.rbe-mermaid-modal{height:100dvh;max-height:100dvh;padding:8px;overflow:hidden;}.rbe-mermaid-dialog{width:100%;height:100%;max-height:100%;}.rbe-mermaid-modal-body{overflow:hidden;}.rbe-mermaid-modal .rbe-mermaid-block{grid-template-rows:auto minmax(0,1fr);}.rbe-mermaid-modal .rbe-mermaid-toolbar{flex-wrap:nowrap;align-items:center;max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:thin;}.rbe-mermaid-modal .rbe-mermaid-toolbar::-webkit-scrollbar{height:6px;}.rbe-mermaid-modal .rbe-mermaid-toolbar::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:999px;}.rbe-mermaid-modal .rbe-mermaid-stylebar{flex:0 0 auto;flex-wrap:nowrap;min-width:max-content;}.rbe-mermaid-modal .rbe-mermaid-popover{position:fixed;left:12px!important;right:12px;top:98px;width:auto;min-width:0;max-width:none;max-height:calc(100dvh - 180px);overflow:auto;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{height:100%;}.rbe-mermaid-modal .rbe-mermaid-stage{height:100%;min-height:0;max-height:none;}.rbe-mermaid-modal .rbe-mermaid-canvas{max-width:none;max-height:none;}}" +
208
- "@media (max-width:640px){.rbe-mermaid-modal{padding:0;}.rbe-mermaid-dialog{width:100vw;height:100dvh;max-height:100dvh;border-radius:0;}.rbe-mermaid-modal-head{min-height:46px;padding:7px 10px;}.rbe-mermaid-modal-foot{display:flex;justify-content:center;align-items:center;width:100%;max-width:100%;min-height:56px;padding:8px 10px;background:#fff;overflow:hidden;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{flex:0 0 auto;width:auto;min-width:112px;min-height:40px;white-space:nowrap;font-size:13px;}.rbe-mermaid-modal .rbe-mermaid-toolbar{gap:5px;padding:7px 8px;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{grid-template-columns:20px minmax(0,1fr);grid-template-rows:22px minmax(0,1fr);}.rbe-mermaid-modal .rbe-mermaid-top-ruler span,.rbe-mermaid-modal .rbe-mermaid-side-ruler span{font-size:10px;}.rbe-mermaid-modal .rbe-mermaid-side-ruler span{left:5px;}.rbe-mermaid-modal .rbe-mermaid-popover{left:8px!important;right:8px;top:86px;max-height:calc(100dvh - 154px);}.rbe-mermaid-modal .rbe-mermaid-submenu{max-width:calc(100vw - 28px);}}" +
209
- "@media (max-width:520px){.rbe-mermaid-modal .rbe-mermaid-popover{padding:6px;}.rbe-mermaid-modal .rbe-mermaid-menu-row{position:static;flex-wrap:wrap;white-space:normal;}.rbe-mermaid-modal .rbe-mermaid-menu-row:after{content:'';}.rbe-mermaid-modal .rbe-mermaid-menu-row>.rbe-mermaid-submenu{position:static;flex:1 1 100%;min-width:0;max-width:100%;margin-top:6px;border-radius:7px;box-shadow:none;}.rbe-mermaid-modal .rbe-mermaid-choice-grid{grid-template-columns:repeat(auto-fill,minmax(22px,1fr));}.rbe-mermaid-modal .rbe-mermaid-line-choice{font-size:13px;padding:7px;}}" +
210
- "@media (max-width:420px){.rbe-mermaid-modal-title{font-size:15px;}.rbe-mermaid-modal-head{padding:7px 9px;}.rbe-mermaid-choice-grid{grid-template-columns:repeat(auto-fill,minmax(22px,1fr));}.rbe-mermaid-popover{max-width:calc(100vw - 16px);}.rbe-mermaid-submenu{max-width:calc(100vw - 24px);}.rbe-mermaid-modal .rbe-mermaid-tool,.rbe-mermaid-modal .rbe-mermaid-icon-btn,.rbe-mermaid-modal .rbe-mermaid-menu-btn{width:31px;height:31px;}.rbe-mermaid-modal .rbe-mermaid-stage-shell{grid-template-columns:18px minmax(0,1fr);grid-template-rows:20px minmax(0,1fr);}.rbe-mermaid-modal-foot{gap:8px;}.rbe-mermaid-modal-save,.rbe-mermaid-modal-cancel{min-width:104px;padding:8px 10px;}.rbe-mermaid-modal-save{font-size:0;}.rbe-mermaid-modal-save:after{content:'Save';font-size:13px;}}";
211
- document.head.appendChild(style);
212
- }
213
-
214
- function numberList(value) {
215
- return String(value || "")
216
- .split(/[\s,]+/)
217
- .map(function (item) { return Number(item); })
218
- .filter(function (item) { return Number.isFinite(item); });
219
- }
220
-
221
- function labelList(value) {
222
- return String(value || "")
223
- .split(",")
224
- .map(function (item) { return item.trim(); })
225
- .filter(Boolean);
226
- }
227
-
228
- var DEFAULT_CHART_COLORS = ["#2563eb", "#16a34a", "#f59e0b", "#dc2626", "#7c3aed", "#0891b2", "#db2777", "#65a30d"];
229
-
230
- function normalizeChartColor(value, fallback) {
231
- value = String(value || "").trim();
232
- return /^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(value) ? value : fallback;
233
- }
234
-
235
- function chartColorList(value, count) {
236
- var colors = String(value || "")
237
- .split(",")
238
- .map(function (item, index) { return normalizeChartColor(item, DEFAULT_CHART_COLORS[index % DEFAULT_CHART_COLORS.length]); })
239
- .filter(Boolean);
240
- while (colors.length < count) colors.push(DEFAULT_CHART_COLORS[colors.length % DEFAULT_CHART_COLORS.length]);
241
- return colors.slice(0, count);
242
- }
243
-
244
- function chartValueLabel(value) {
245
- if (!Number.isFinite(value)) return "0";
246
- return String(Math.round(value * 100) / 100);
247
- }
248
-
249
- function chartTickValues(max) {
250
- var steps = 4;
251
- var ticks = [];
252
- for (var index = 0; index <= steps; index += 1) {
253
- ticks.push((max / steps) * index);
254
- }
255
- return ticks;
256
- }
257
-
258
- function chartSvg(data) {
259
- var type = data.type === "line" || data.type === "pie" ? data.type : "bar";
260
- var labels = labelList(data.labels || "Q1,Q2,Q3,Q4");
261
- var values = numberList(data.values || "12,19,7,14");
262
- if (!values.length) values = [12, 19, 7, 14];
263
- var count = Math.max(labels.length, values.length);
264
- for (var fillIndex = 0; fillIndex < count; fillIndex += 1) {
265
- if (!labels[fillIndex]) labels[fillIndex] = "Item " + (fillIndex + 1);
266
- if (!Number.isFinite(values[fillIndex])) values[fillIndex] = 0;
267
- }
268
- var color = data.color || "#2563eb";
269
- var max = Math.max.apply(null, values.map(function (value) { return Math.max(0, value); }).concat([1]));
270
- var width = 640;
271
- var height = 300;
272
- if (type === "pie") {
273
- var total = values.reduce(function (sum, item) { return sum + Math.max(0, item); }, 0) || 1;
274
- var start = -Math.PI / 2;
275
- var colors = chartColorList(data.colors, values.length);
276
- var slices = values.map(function (value, index) {
277
- var angle = (Math.max(0, value) / total) * Math.PI * 2;
278
- var end = start + angle;
279
- var mid = start + angle / 2;
280
- var x1 = 320 + Math.cos(start) * 105;
281
- var y1 = 145 + Math.sin(start) * 105;
282
- var x2 = 320 + Math.cos(end) * 105;
283
- var y2 = 145 + Math.sin(end) * 105;
284
- var labelX = 320 + Math.cos(mid) * 64;
285
- var labelY = 145 + Math.sin(mid) * 64;
286
- var large = angle > Math.PI ? 1 : 0;
287
- var path = "M320 145 L" + x1 + " " + y1 + " A105 105 0 " + large + " 1 " + x2 + " " + y2 + " Z";
288
- start = end;
289
- return '<g><path d="' + path + '" fill="' + esc(colors[index % colors.length]) + '"><title>' + esc((labels[index] || "") + ": " + value) + '</title></path>' + (value > 0 ? '<text x="' + labelX + '" y="' + labelY + '" text-anchor="middle" dominant-baseline="central" fill="#fff" stroke="#0f172a" stroke-opacity=".35" stroke-width="3" paint-order="stroke fill" font-size="15" font-weight="800">' + esc(chartValueLabel(value)) + "</text>" : "") + "</g>";
290
- }).join("");
291
- return '<svg viewBox="0 0 640 300" role="img" aria-label="' + esc(data.title || "Pie chart") + '">' + slices + "</svg>";
292
- }
293
- var padLeft = 58;
294
- var padRight = 28;
295
- var padTop = 30;
296
- var padBottom = 48;
297
- var plotWidth = width - padLeft - padRight;
298
- var plotHeight = height - padTop - padBottom;
299
- var axisBottom = height - padBottom;
300
- var step = plotWidth / values.length;
301
- var yFor = function (value) {
302
- return axisBottom - (Math.max(0, value) / max) * plotHeight;
303
- };
304
- var points = values.map(function (value, index) {
305
- var x = padLeft + step * index + step / 2;
306
- var y = yFor(value);
307
- return [x, y];
308
- });
309
- var axis =
310
- '<path d="M' + padLeft + " " + axisBottom + "H" + (width - padRight) + "M" + padLeft + " " + padTop + "V" + axisBottom + '" stroke="#cbd5e1" fill="none"/>' +
311
- chartTickValues(max).map(function (tick) {
312
- var y = yFor(tick);
313
- return '<g><path d="M' + padLeft + " " + y + "H" + (width - padRight) + '" stroke="#e8eef6" fill="none"/><text x="' + (padLeft - 10) + '" y="' + (y + 4) + '" text-anchor="end" font-size="12" fill="#64748b">' + esc(chartValueLabel(tick)) + "</text></g>";
314
- }).join("") +
315
- labels.map(function (label, index) {
316
- var x = padLeft + step * index + step / 2;
317
- return '<text x="' + x + '" y="' + (height - 18) + '" text-anchor="middle" font-size="12" fill="#64748b">' + esc(label) + "</text>";
318
- }).join("");
319
- if (type === "line") {
320
- return '<svg viewBox="0 0 640 300" role="img" aria-label="' + esc(data.title || "Line chart") + '">' + axis + '<polyline points="' + points.map(function (point) { return point.join(","); }).join(" ") + '" fill="none" stroke="' + esc(normalizeChartColor(color, "#2563eb")) + '" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>' + points.map(function (point, index) { return '<g><circle cx="' + point[0] + '" cy="' + point[1] + '" r="5" fill="' + esc(normalizeChartColor(color, "#2563eb")) + '"><title>' + esc((labels[index] || "") + ": " + values[index]) + '</title></circle><text x="' + point[0] + '" y="' + (point[1] - 10) + '" text-anchor="middle" font-size="12" fill="#344054" font-weight="700">' + esc(chartValueLabel(values[index])) + "</text></g>"; }).join("") + "</svg>";
321
- }
322
- return '<svg viewBox="0 0 640 300" role="img" aria-label="' + esc(data.title || "Bar chart") + '">' + axis + values.map(function (value, index) {
323
- var barHeight = (Math.max(0, value) / max) * plotHeight;
324
- var x = padLeft + step * index + step * 0.18;
325
- var y = axisBottom - barHeight;
326
- var barWidth = step * 0.64;
327
- var centerY = y + barHeight / 2 + 5;
328
- return '<g><rect x="' + x + '" y="' + y + '" width="' + barWidth + '" height="' + barHeight + '" rx="5" fill="' + esc(normalizeChartColor(color, "#2563eb")) + '"><title>' + esc((labels[index] || "") + ": " + value) + '</title></rect><text x="' + (x + barWidth / 2) + '" y="' + centerY + '" text-anchor="middle" font-size="13" fill="#fff" stroke="#0f172a" stroke-opacity=".25" stroke-width="2" paint-order="stroke fill" font-weight="800">' + esc(chartValueLabel(value)) + "</text></g>";
329
- }).join("") + "</svg>";
330
- }
331
-
332
- function loadScriptOnce(src, key, callback) {
333
- var existing = document.querySelector("script[data-blockwriteai-lib='" + key + "']");
334
- if (existing) {
335
- if (existing.dataset.loaded === "true") callback();
336
- else existing.addEventListener("load", callback, { once: true });
337
- return;
338
- }
339
- var script = document.createElement("script");
340
- script.src = src;
341
- script.dataset.blockwriteaiLib = key;
342
- script.onload = function () {
343
- script.dataset.loaded = "true";
344
- callback();
345
- };
346
- script.onerror = callback;
347
- document.head.appendChild(script);
348
- }
349
-
350
- function loadStyleOnce(href, key) {
351
- if (document.querySelector("link[data-blockwriteai-lib='" + key + "']")) return;
352
- var link = document.createElement("link");
353
- link.rel = "stylesheet";
354
- link.href = href;
355
- link.dataset.blockwriteaiLib = key;
356
- document.head.appendChild(link);
357
- }
358
-
359
- function renderMermaidPreview(target, code) {
360
- target.innerHTML = '<pre>' + esc(code || "") + "</pre>";
361
- if (!code || !code.trim()) return;
362
- loadScriptOnce("https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js", "mermaid", function () {
363
- if (!global.mermaid) return;
364
- try {
365
- global.mermaid.initialize({ startOnLoad: false, securityLevel: "strict" });
366
- var render = function () {
367
- global.mermaid.render("rbe-mermaid-" + Math.random().toString(36).slice(2), code).then(function (result) {
368
- target.innerHTML = result.svg;
369
- }).catch(function () {
370
- target.innerHTML = '<pre class="rbe-output-mermaid-error">' + esc(code || "") + "</pre>";
371
- });
372
- };
373
- if (global.mermaid.parse) {
374
- Promise.resolve(global.mermaid.parse(code)).then(render).catch(function () {
375
- target.innerHTML = '<pre class="rbe-output-mermaid-error">' + esc(code || "") + "</pre>";
376
- });
377
- } else {
378
- render();
379
- }
380
- } catch (error) {}
381
- });
382
- }
383
-
384
- function latexFallbackHTML(code) {
385
- var html = esc(code || "");
386
- html = html
387
- .replace(/\\int/g, "&int;")
388
- .replace(/\\sum/g, "&sum;")
389
- .replace(/\\sqrt\{([^}]*)\}/g, "&radic;<span style=\"text-decoration:overline\">$1</span>")
390
- .replace(/\\frac\{([^}]*)\}\{([^}]*)\}/g, '<span class="rbe-latex-frac"><span>$1</span><span>$2</span></span>')
391
- .replace(/\^\{([^}]*)\}/g, "<sup>$1</sup>")
392
- .replace(/\^([A-Za-z0-9+\-=]+)/g, "<sup>$1</sup>")
393
- .replace(/_\{([^}]*)\}/g, "<sub>$1</sub>")
394
- .replace(/_([A-Za-z0-9+\-=]+)/g, "<sub>$1</sub>")
395
- .replace(/\\,/g, " ")
396
- .replace(/\\ /g, " ");
397
- return html;
398
- }
399
-
400
- function renderLatexPreview(target, code, displayMode) {
401
- target.innerHTML = latexFallbackHTML(code || "");
402
- if (!code || !code.trim()) return;
403
- loadStyleOnce("https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css", "katex-css");
404
- loadScriptOnce("https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js", "katex", function () {
405
- if (!global.katex) return;
406
- try {
407
- global.katex.render(code, target, { throwOnError: false, displayMode: !!displayMode });
408
- } catch (error) {}
409
- });
410
- }
411
-
412
- var DIAGRAM_LINE_TYPES = /^(line|arrow|leftArrow|rightArrow|upArrow|downArrow|biArrow|elbow|curved|curve|polyline|scribble)$/;
413
- var DIAGRAM_SHAPE_TYPES = /^(rect|roundedRect|circle|oval|triangle|diamond|pentagon|hexagon|parallelogram|trapezoid|octagon|heart|cloud|cylinder|cube|callout|roundCallout|thought|plus|minus|multiply|divide|equal|brackets|text|wordart|image)$/;
414
-
415
- function defaultMermaidWorkspace() {
416
- return { width: 1100, height: 620, zoom: 1 };
417
- }
418
-
419
- var MERMAID_WORKSPACE_MAX = 6000;
420
- var MERMAID_WORKSPACE_GROW_PADDING = 260;
421
- var MERMAID_WORKSPACE_GROW_STEP = 240;
422
-
423
- function defaultMermaidDiagram() {
424
- return [
425
- { id: "shape-start", type: "roundedRect", x: 120, y: 140, w: 170, h: 76, text: "Start", fill: "#eef5ff", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
426
- { id: "shape-blockwriteai", type: "roundedRect", x: 560, y: 140, w: 190, h: 76, text: "BlockWriteAI", fill: "#ecfdf3", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
427
- { id: "line-main", type: "arrow", x1: 290, y1: 178, x2: 560, y2: 178, stroke: "#475467", strokeWidth: 1 }
428
- ];
429
- }
430
-
431
- function simpleMermaidToDiagram(code) {
432
- var match = String(code || "").match(/([A-Za-z0-9_]+)\[([^\]]+)\]\s*-+>\s*([A-Za-z0-9_]+)\[([^\]]+)\]/);
433
- if (!match) return null;
434
- return [
435
- { id: "shape-" + match[1], type: "roundedRect", x: 120, y: 140, w: 170, h: 76, text: match[2], fill: "#eef5ff", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
436
- { id: "shape-" + match[3], type: "roundedRect", x: 560, y: 140, w: 190, h: 76, text: match[4], fill: "#ecfdf3", stroke: "#111827", fontSize: 16, strokeWidth: 1 },
437
- { id: "line-" + match[1] + "-" + match[3], type: "arrow", x1: 290, y1: 178, x2: 560, y2: 178, stroke: "#475467", strokeWidth: 1 }
438
- ];
439
- }
440
-
441
- function isLineType(type) {
442
- return DIAGRAM_LINE_TYPES.test(type || "");
443
- }
444
-
445
- function isTextDiagramType(type) {
446
- return type === "text" || type === "wordart";
447
- }
448
-
449
- function clampNumber(value, min, max, fallback) {
450
- value = Number(value);
451
- if (!Number.isFinite(value)) value = fallback;
452
- return Math.min(max, Math.max(min, value));
453
- }
454
-
455
- function normalizeDiagramWorkspace(data) {
456
- var workspace = data && data.diagram && data.diagram.workspace ? data.diagram.workspace : data && data.workspace ? data.workspace : {};
457
- return {
458
- width: clampNumber(workspace.width, 640, MERMAID_WORKSPACE_MAX, 1100),
459
- height: clampNumber(workspace.height, 360, MERMAID_WORKSPACE_MAX, 620),
460
- zoom: clampNumber(workspace.zoom, 0.35, 2.5, 1)
461
- };
462
- }
463
-
464
- function normalizeDiagramDash(value) {
465
- return value === "dotted" || value === "dashed" || value === "longDash" || value === "dashDot" ? value : "solid";
466
- }
467
-
468
- function dashArray(value) {
469
- value = normalizeDiagramDash(value);
470
- if (value === "dotted") return "2 8";
471
- if (value === "dashed") return "10 8";
472
- if (value === "longDash") return "18 10";
473
- if (value === "dashDot") return "14 7 2 7";
474
- return "";
475
- }
476
-
477
- function normalizeDiagramLineHeight(value) {
478
- value = Number(value);
479
- if (!Number.isFinite(value)) value = 1.25;
480
- return clampNumber(value, 1, 2.5, 1.25);
481
- }
482
-
483
- function normalizeDiagramListType(value) {
484
- return value === "bullet" || value === "number" ? value : "none";
485
- }
486
-
487
- function normalizeDiagramListStyle(type, value) {
488
- if (type === "bullet") {
489
- return value === "circle" || value === "square" || value === "none" ? value : "disc";
490
- }
491
- if (type === "number") {
492
- return value === "lower-alpha" || value === "upper-alpha" || value === "lower-roman" || value === "upper-roman" || value === "none" ? value : "decimal";
493
- }
494
- return "none";
495
- }
496
-
497
- function normalizeDiagramElement(item, index) {
498
- item = item || {};
499
- var type = DIAGRAM_SHAPE_TYPES.test(item.type) || DIAGRAM_LINE_TYPES.test(item.type) ? item.type : "roundedRect";
500
- var isLine = isLineType(type);
501
- var text = item.text || "";
502
- if (!text && type === "wordart") text = "WordArt";
503
- if (!text && /^(plus|minus|multiply|divide|equal|brackets)$/.test(type)) {
504
- text = { plus: "+", minus: "-", multiply: "x", divide: "/", equal: "=", brackets: "[ ]" }[type] || "";
505
- }
506
- var fillValue = String(item.fill || "").trim();
507
- var fillFallback = type === "wordart" ? "#f59e0b" : type === "text" ? "transparent" : "#eef5ff";
508
- return {
509
- id: item.id || "diagram-" + index + "-" + Math.random().toString(36).slice(2),
510
- type: type,
511
- x: clampNumber(item.x, -500, 4000, 80),
512
- y: clampNumber(item.y, -500, 4000, 80),
513
- w: clampNumber(item.w, 20, 2000, isTextDiagramType(type) ? 180 : 150),
514
- h: clampNumber(item.h, 20, 2000, isTextDiagramType(type) ? 70 : 80),
515
- x1: clampNumber(item.x1 == null ? item.x : item.x1, -500, 4000, 80),
516
- y1: clampNumber(item.y1 == null ? item.y : item.y1, -500, 4000, 80),
517
- x2: clampNumber(item.x2 == null ? Number(item.x || 80) + 160 : item.x2, -500, 4000, 240),
518
- y2: clampNumber(item.y2 == null ? item.y : item.y2, -500, 4000, 80),
519
- text: text,
520
- fill: isLine ? "transparent" : fillValue === "transparent" ? "transparent" : normalizeChartColor(fillValue, fillFallback),
521
- stroke: normalizeChartColor(item.stroke, isLine ? "#475467" : "#111827"),
522
- strokeWidth: clampNumber(item.strokeWidth, 1, 16, 1),
523
- dash: normalizeDiagramDash(item.dash),
524
- opacity: clampNumber(item.opacity, 0.1, 1, 1),
525
- fontFamily: item.fontFamily || "Arial",
526
- fontSize: clampNumber(item.fontSize, 8, 96, type === "wordart" ? 36 : 16),
527
- bold: !!item.bold || type === "wordart",
528
- italic: !!item.italic,
529
- underline: !!item.underline,
530
- textColor: normalizeChartColor(item.textColor, "#172033"),
531
- highlightColor: String(item.highlightColor || "").trim() === "transparent" ? "transparent" : normalizeChartColor(item.highlightColor, "transparent"),
532
- textAlign: item.textAlign === "left" || item.textAlign === "right" || item.textAlign === "justify" ? item.textAlign : "center",
533
- lineHeight: normalizeDiagramLineHeight(item.lineHeight),
534
- spacingBefore: clampNumber(item.spacingBefore, 0, 48, 0),
535
- spacingAfter: clampNumber(item.spacingAfter, 0, 48, 0),
536
- indent: clampNumber(item.indent, 0, 8, 0),
537
- listType: normalizeDiagramListType(item.listType),
538
- listStyle: normalizeDiagramListStyle(normalizeDiagramListType(item.listType), item.listStyle),
539
- rotation: clampNumber(item.rotation, -360, 360, 0),
540
- group: item.group || "",
541
- flipX: !!item.flipX,
542
- flipY: !!item.flipY,
543
- image: item.image || "",
544
- name: item.name || ""
545
- };
546
- }
547
-
548
- function diagramElements(data) {
549
- var source = data && data.diagram && Array.isArray(data.diagram.elements) ? data.diagram.elements : Array.isArray(data && data.diagram) ? data.diagram : null;
550
- if (!source && data && data.code) source = simpleMermaidToDiagram(data.code);
551
- return (source ? source : defaultMermaidDiagram()).map(normalizeDiagramElement);
552
- }
553
-
554
- function selectedIdList(selectedIds) {
555
- return String(selectedIds || "").split(",").map(function (id) { return id.trim(); }).filter(Boolean);
556
- }
557
-
558
- function polygonPoints(cx, cy, rx, ry, count, startAngle) {
559
- var points = [];
560
- for (var i = 0; i < count; i += 1) {
561
- var angle = (startAngle || -Math.PI / 2) + (i / count) * Math.PI * 2;
562
- points.push((cx + Math.cos(angle) * rx) + "," + (cy + Math.sin(angle) * ry));
563
- }
564
- return points.join(" ");
565
- }
566
-
567
- function shapeMarkup(elm) {
568
- var x = elm.x;
569
- var y = elm.y;
570
- var w = elm.w;
571
- var h = elm.h;
572
- var cx = x + w / 2;
573
- var cy = y + h / 2;
574
- var fill = esc(elm.fill);
575
- var stroke = esc(elm.stroke);
576
- var sw = esc(elm.strokeWidth);
577
- var dash = dashArray(elm.dash);
578
- var common = ' fill="' + fill + '" stroke="' + stroke + '" stroke-width="' + sw + '"' + (dash ? ' stroke-dasharray="' + dash + '"' : "") + ' opacity="' + esc(elm.opacity) + '"';
579
- if (elm.type === "circle") return '<circle cx="' + cx + '" cy="' + cy + '" r="' + Math.min(w, h) / 2 + '"' + common + "></circle>";
580
- if (elm.type === "oval") return '<ellipse cx="' + cx + '" cy="' + cy + '" rx="' + w / 2 + '" ry="' + h / 2 + '"' + common + "></ellipse>";
581
- if (elm.type === "triangle") return '<polygon points="' + cx + "," + y + " " + (x + w) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
582
- if (elm.type === "diamond") return '<polygon points="' + cx + "," + y + " " + (x + w) + "," + cy + " " + cx + "," + (y + h) + " " + x + "," + cy + '"' + common + "></polygon>";
583
- if (elm.type === "pentagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 5) + '"' + common + "></polygon>";
584
- if (elm.type === "hexagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 6, 0) + '"' + common + "></polygon>";
585
- if (elm.type === "octagon") return '<polygon points="' + polygonPoints(cx, cy, w / 2, h / 2, 8, Math.PI / 8) + '"' + common + "></polygon>";
586
- if (elm.type === "parallelogram") return '<polygon points="' + (x + w * .18) + "," + y + " " + (x + w) + "," + y + " " + (x + w * .82) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
587
- if (elm.type === "trapezoid") return '<polygon points="' + (x + w * .18) + "," + y + " " + (x + w * .82) + "," + y + " " + (x + w) + "," + (y + h) + " " + x + "," + (y + h) + '"' + common + "></polygon>";
588
- if (elm.type === "heart") return '<path d="M' + cx + " " + (y + h * .86) + " C" + (x - w * .1) + " " + (y + h * .35) + " " + (x + w * .12) + " " + y + " " + cx + " " + (y + h * .28) + " C" + (x + w * .88) + " " + y + " " + (x + w * 1.1) + " " + (y + h * .35) + " " + cx + " " + (y + h * .86) + ' Z"' + common + "></path>";
589
- if (elm.type === "cloud") return '<path d="M' + (x + w * .22) + " " + (y + h * .72) + " C" + (x + w * .02) + " " + (y + h * .68) + " " + (x + w * .04) + " " + (y + h * .37) + " " + (x + w * .28) + " " + (y + h * .4) + " C" + (x + w * .34) + " " + (y + h * .12) + " " + (x + w * .68) + " " + (y + h * .1) + " " + (x + w * .74) + " " + (y + h * .38) + " C" + (x + w * 1.02) + " " + (y + h * .36) + " " + (x + w * 1.02) + " " + (y + h * .72) + " " + (x + w * .78) + " " + (y + h * .72) + ' Z"' + common + "></path>";
590
- if (elm.type === "cylinder") return '<path d="M' + x + " " + (y + h * .18) + " C" + x + " " + y + " " + (x + w) + " " + y + " " + (x + w) + " " + (y + h * .18) + " V" + (y + h * .82) + " C" + (x + w) + " " + (y + h) + " " + x + " " + (y + h) + " " + x + " " + (y + h * .82) + ' Z"' + common + "></path>" + '<ellipse cx="' + cx + '" cy="' + (y + h * .18) + '" rx="' + w / 2 + '" ry="' + h * .18 + '"' + common + "></ellipse>";
591
- if (elm.type === "cube") return '<path d="M' + x + " " + (y + h * .2) + " L" + (x + w * .2) + " " + y + " H" + (x + w) + " V" + (y + h * .8) + " L" + (x + w * .8) + " " + (y + h) + " H" + x + ' Z"' + common + "></path>" + '<path d="M' + (x + w * .2) + " " + y + " V" + (y + h * .8) + " H" + (x + w) + '" stroke="' + stroke + '" stroke-width="' + sw + '" fill="none" opacity="' + esc(elm.opacity) + '"></path>';
592
- if (elm.type === "callout") return '<polygon points="' + x + "," + y + " " + (x + w) + "," + y + " " + (x + w) + "," + (y + h * .75) + " " + (x + w * .62) + "," + (y + h * .75) + " " + (x + w * .5) + "," + (y + h) + " " + (x + w * .38) + "," + (y + h * .75) + " " + x + "," + (y + h * .75) + '"' + common + "></polygon>";
593
- if (elm.type === "roundCallout") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + (h * .78) + '" rx="18"' + common + "></rect>" + '<path d="M' + (x + w * .44) + " " + (y + h * .78) + " L" + (x + w * .5) + " " + (y + h) + " L" + (x + w * .58) + " " + (y + h * .78) + ' Z"' + common + "></path>";
594
- if (elm.type === "thought") return '<ellipse cx="' + cx + '" cy="' + (y + h * .42) + '" rx="' + w / 2 + '" ry="' + h * .36 + '"' + common + "></ellipse>" + '<circle cx="' + (x + w * .62) + '" cy="' + (y + h * .82) + '" r="' + Math.min(w, h) * .08 + '"' + common + "></circle>" + '<circle cx="' + (x + w * .72) + '" cy="' + (y + h * .96) + '" r="' + Math.min(w, h) * .05 + '"' + common + "></circle>";
595
- if (elm.type === "text") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="4"' + common + "></rect>";
596
- if (elm.type === "wordart") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" fill="transparent" stroke="transparent"></rect>';
597
- if (elm.type === "image") return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="8" fill="#f8fafc" stroke="#d7dee8"></rect>' + (elm.image ? '<image href="' + esc(elm.image) + '" xlink:href="' + esc(elm.image) + '" x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" preserveAspectRatio="xMidYMid slice"></image>' : diagramText(elm));
598
- return '<rect x="' + x + '" y="' + y + '" width="' + w + '" height="' + h + '" rx="' + (elm.type === "roundedRect" ? 14 : 6) + '"' + common + "></rect>";
599
- }
600
-
601
- function diagramTextContent(elm) {
602
- var lines = String(elm.text || "").split(/\r?\n/);
603
- var highlight = elm.highlightColor && elm.highlightColor !== "transparent" ? 'background-color:' + esc(elm.highlightColor) + ";" : "";
604
- var lineStyle = highlight ? ' style="' + highlight + 'box-decoration-break:clone;-webkit-box-decoration-break:clone;"' : "";
605
- var items = lines.map(function (line) {
606
- return '<span' + lineStyle + ">" + (line ? esc(line) : "&nbsp;") + "</span>";
607
- });
608
- var listStyle = esc(normalizeDiagramListStyle(elm.listType, elm.listStyle));
609
- if (elm.listType === "bullet") {
610
- return '<ul style="margin:0;list-style-type:' + listStyle + ';padding-left:' + (18 + elm.indent * 18) + 'px;">' + items.map(function (item) { return "<li>" + item + "</li>"; }).join("") + "</ul>";
611
- }
612
- if (elm.listType === "number") {
613
- return '<ol style="margin:0;list-style-type:' + listStyle + ';padding-left:' + (22 + elm.indent * 18) + 'px;">' + items.map(function (item) { return "<li>" + item + "</li>"; }).join("") + "</ol>";
614
- }
615
- return items.map(function (item, index) {
616
- var margin = "margin:0;";
617
- if (index === 0 && elm.spacingBefore) margin += "padding-top:" + esc(elm.spacingBefore) + "px;";
618
- if (index === lines.length - 1 && elm.spacingAfter) margin += "padding-bottom:" + esc(elm.spacingAfter) + "px;";
619
- return '<div style="' + margin + '">' + item + "</div>";
620
- }).join("");
621
- }
622
-
623
- function diagramText(elm) {
624
- if (!elm.text) return "";
625
- if (elm.type === "wordart") {
626
- var anchor = elm.textAlign === "left" ? "start" : elm.textAlign === "right" ? "end" : "middle";
627
- var x = elm.textAlign === "left" ? elm.x + 12 : elm.textAlign === "right" ? elm.x + elm.w - 12 : elm.x + elm.w / 2;
628
- var y = elm.y + elm.h / 2;
629
- var decoration = elm.underline ? ' text-decoration="underline"' : "";
630
- var stroke = ' stroke="' + esc(elm.stroke) + '" stroke-width="' + Math.max(1, elm.strokeWidth) + '" paint-order="stroke fill"';
631
- return '<text x="' + x + '" y="' + y + '" text-anchor="' + anchor + '" dominant-baseline="middle" font-family="' + esc(elm.fontFamily) + ', Arial, sans-serif" font-size="' + esc(elm.fontSize) + '" font-weight="' + (elm.bold ? "800" : "400") + '" font-style="' + (elm.italic ? "italic" : "normal") + '"' + decoration + stroke + ' fill="' + esc(elm.textColor) + '">' + esc(elm.text || "") + "</text>";
632
- }
633
- var textBox = { x: elm.x, y: elm.y, w: elm.w, h: elm.h };
634
- if (elm.type === "callout") textBox.h = elm.h * .72;
635
- if (elm.type === "roundCallout") textBox.h = elm.h * .7;
636
- if (elm.type === "thought") textBox.h = elm.h * .72;
637
- var align = elm.textAlign || "center";
638
- var justify = align === "left" ? "flex-start" : align === "right" ? "flex-end" : "center";
639
- var decorationStyle = elm.underline ? "text-decoration:underline;" : "";
640
- var contentStyle = "box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:" + justify + ";overflow:hidden;padding:4px 8px 4px " + (8 + elm.indent * 10) + "px;color:" + esc(elm.textColor) + ";font-family:" + esc(elm.fontFamily) + ",Arial,sans-serif;font-size:" + esc(elm.fontSize) + "px;font-weight:" + (elm.bold ? "800" : "400") + ";font-style:" + (elm.italic ? "italic" : "normal") + ";" + decorationStyle + "text-align:" + esc(align) + ";line-height:" + esc(elm.lineHeight) + ";white-space:normal;overflow-wrap:anywhere;word-break:break-word;";
641
- var innerStyle = "max-width:100%;";
642
- if (align === "justify") innerStyle += "text-align:justify;";
643
- return '<foreignObject class="rbe-mermaid-text-layer" x="' + esc(textBox.x) + '" y="' + esc(textBox.y) + '" width="' + esc(textBox.w) + '" height="' + esc(textBox.h) + '"><div xmlns="http://www.w3.org/1999/xhtml" style="' + contentStyle + '"><div style="' + innerStyle + '">' + diagramTextContent(elm) + "</div></div></foreignObject>";
644
- }
645
-
646
- function linePath(elm) {
647
- if (elm.type === "elbow") return "M" + elm.x1 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y2;
648
- if (elm.type === "curved" || elm.type === "curve") {
649
- var midX = (elm.x1 + elm.x2) / 2;
650
- return "M" + elm.x1 + " " + elm.y1 + " C" + midX + " " + elm.y1 + " " + midX + " " + elm.y2 + " " + elm.x2 + " " + elm.y2;
651
- }
652
- if (elm.type === "polyline") return "M" + elm.x1 + " " + elm.y1 + " L" + ((elm.x1 + elm.x2) / 2) + " " + (elm.y1 - 45) + " L" + elm.x2 + " " + elm.y2;
653
- if (elm.type === "scribble") {
654
- var d = "M" + elm.x1 + " " + elm.y1;
655
- for (var i = 1; i <= 8; i += 1) {
656
- var t = i / 8;
657
- d += " L" + (elm.x1 + (elm.x2 - elm.x1) * t) + " " + (elm.y1 + (elm.y2 - elm.y1) * t + (i % 2 ? 8 : -8));
658
- }
659
- return d;
660
- }
661
- return "M" + elm.x1 + " " + elm.y1 + " L" + elm.x2 + " " + elm.y2;
662
- }
663
-
664
- function elementBounds(elm) {
665
- if (isLineType(elm.type)) {
666
- return { x: Math.min(elm.x1, elm.x2), y: Math.min(elm.y1, elm.y2), w: Math.abs(elm.x2 - elm.x1), h: Math.abs(elm.y2 - elm.y1) };
667
- }
668
- return { x: elm.x, y: elm.y, w: elm.w, h: elm.h };
669
- }
670
-
671
- function rectsIntersect(a, b) {
672
- return a.x <= b.x + b.w && a.x + a.w >= b.x && a.y <= b.y + b.h && a.y + a.h >= b.y;
673
- }
674
-
675
- function selectionMarkup(elm, isSingle) {
676
- var b = elementBounds(elm);
677
- var markup = '<rect class="rbe-mermaid-selected-stroke" x="' + b.x + '" y="' + b.y + '" width="' + b.w + '" height="' + b.h + '" rx="6"></rect>';
678
- var handleStyle = ' style="--rbe-handle-color:#2563eb"';
679
- if (!isSingle || isLineType(elm.type)) {
680
- if (isSingle && isLineType(elm.type)) {
681
- markup += '<circle class="rbe-mermaid-handle" data-handle="start" cx="' + elm.x1 + '" cy="' + elm.y1 + '" r="5"' + handleStyle + '></circle><circle class="rbe-mermaid-handle" data-handle="end" cx="' + elm.x2 + '" cy="' + elm.y2 + '" r="5"' + handleStyle + '></circle>';
682
- }
683
- return markup;
684
- }
685
- [["nw", b.x, b.y], ["ne", b.x + b.w, b.y], ["se", b.x + b.w, b.y + b.h], ["sw", b.x, b.y + b.h]].forEach(function (handle) {
686
- markup += '<circle class="rbe-mermaid-handle" data-handle="' + handle[0] + '" cx="' + handle[1] + '" cy="' + handle[2] + '" r="5"' + handleStyle + '></circle>';
687
- });
688
- [
689
- ["n", b.x + b.w / 2, b.y, 24, 6],
690
- ["e", b.x + b.w, b.y + b.h / 2, 6, 24],
691
- ["s", b.x + b.w / 2, b.y + b.h, 24, 6],
692
- ["w", b.x, b.y + b.h / 2, 6, 24]
693
- ].forEach(function (handle) {
694
- markup += '<rect class="rbe-mermaid-handle" data-handle="' + handle[0] + '" x="' + (handle[1] - handle[3] / 2) + '" y="' + (handle[2] - handle[4] / 2) + '" width="' + handle[3] + '" height="' + handle[4] + '" rx="3"' + handleStyle + '></rect>';
695
- });
696
- var rotateX = b.x + b.w / 2;
697
- var rotateY = b.y - 28;
698
- markup += '<g class="rbe-mermaid-rotate" data-handle="rotate"><circle cx="' + rotateX + '" cy="' + rotateY + '" r="9"></circle><path d="M' + (rotateX + 2) + " " + (rotateY - 3) + " A4 4 0 1 0 " + (rotateX + 4) + " " + (rotateY + 2) + '" fill="none" stroke="#fff" stroke-width="1.6" stroke-linecap="round"></path><path d="M' + (rotateX + 3) + " " + (rotateY - 6) + " L" + (rotateX + 7) + " " + (rotateY - 3) + " L" + (rotateX + 3) + " " + rotateY + ' Z" fill="#fff"></path></g>';
699
- return markup;
700
- }
701
-
702
- function diagramSvg(elements, selectedIds, workspace, marquee) {
703
- elements = (elements || []).map(normalizeDiagramElement);
704
- workspace = workspace || defaultMermaidWorkspace();
705
- var selected = selectedIdList(selectedIds);
706
- var body = elements.map(function (elm) {
707
- var isSelected = selected.indexOf(elm.id) !== -1;
708
- var selectMarkup = isSelected ? selectionMarkup(elm, selected.length === 1) : "";
709
- var transform = "";
710
- if (!isLineType(elm.type) && (elm.rotation || elm.flipX || elm.flipY)) {
711
- var tx = elm.x + elm.w / 2;
712
- var ty = elm.y + elm.h / 2;
713
- transform = ' transform="' + (elm.rotation ? "rotate(" + esc(elm.rotation) + " " + tx + " " + ty + ") " : "") + ((elm.flipX || elm.flipY) ? "translate(" + tx + " " + ty + ") scale(" + (elm.flipX ? -1 : 1) + " " + (elm.flipY ? -1 : 1) + ") translate(" + (-tx) + " " + (-ty) + ")" : "") + '"';
714
- }
715
- if (isLineType(elm.type)) {
716
- var markerStart = elm.type === "leftArrow" || elm.type === "biArrow" ? ' marker-start="url(#rbe-arrow-head-start)"' : "";
717
- var markerEnd = /^(arrow|rightArrow|upArrow|downArrow|biArrow|elbow|curved)$/.test(elm.type) ? ' marker-end="url(#rbe-arrow-head)"' : "";
718
- var dash = dashArray(elm.dash);
719
- var originDot = isSelected && (markerStart || markerEnd) ? '<circle class="rbe-mermaid-arrow-origin" cx="' + esc(elm.x1) + '" cy="' + esc(elm.y1) + '" r="4"></circle>' : "";
720
- return '<g data-el-id="' + esc(elm.id) + '">' + selectMarkup + originDot + '<path d="' + linePath(elm) + '" fill="none" stroke="' + esc(elm.stroke) + '" stroke-width="' + esc(elm.strokeWidth) + '" stroke-linecap="round" stroke-linejoin="round"' + (dash ? ' stroke-dasharray="' + dash + '"' : "") + ' opacity="' + esc(elm.opacity) + '"' + markerStart + markerEnd + "></path></g>";
721
- }
722
- return '<g data-el-id="' + esc(elm.id) + '"' + transform + ">" + selectMarkup + shapeMarkup(elm) + (elm.type !== "image" ? diagramText(elm) : "") + "</g>";
723
- }).join("");
724
- if (marquee) body += '<rect class="rbe-mermaid-marquee" x="' + marquee.x + '" y="' + marquee.y + '" width="' + marquee.w + '" height="' + marquee.h + '"></rect>';
725
- return '<svg class="rbe-mermaid-canvas" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + workspace.width * workspace.zoom + '" height="' + workspace.height * workspace.zoom + '" viewBox="0 0 ' + workspace.width + " " + workspace.height + '" role="img" aria-label="Diagram" tabindex="0"><defs><marker id="rbe-arrow-head" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M0,0 L0,6 L9,3 z" fill="#475467"></path></marker><marker id="rbe-arrow-head-start" markerWidth="10" markerHeight="10" refX="1" refY="3" orient="auto" markerUnits="strokeWidth"><path d="M9,0 L9,6 L0,3 z" fill="#475467"></path></marker></defs>' + body + "</svg>";
726
- }
727
-
728
- function renderDiagramCanvas(wrap) {
729
- var hidden = wrap.querySelector(".rbe-mermaid-diagram-json");
730
- var elements = [];
731
- try {
732
- elements = JSON.parse(hidden.value || "[]");
733
- } catch (error) {
734
- elements = defaultMermaidDiagram();
735
- }
736
- var workspace = readWorkspaceFromBlock(wrap);
737
- var marquee = null;
738
- try {
739
- marquee = wrap.dataset.marquee ? JSON.parse(wrap.dataset.marquee) : null;
740
- } catch (error) {}
741
- wrap.querySelector(".rbe-mermaid-stage").innerHTML = diagramSvg(elements, wrap.dataset.selectedIds || "", workspace, marquee);
742
- }
743
-
744
- function readWorkspaceFromBlock(wrap) {
745
- return {
746
- width: clampNumber((wrap.querySelector(".rbe-mermaid-workspace-w") || {}).value, 640, MERMAID_WORKSPACE_MAX, 1100),
747
- height: clampNumber((wrap.querySelector(".rbe-mermaid-workspace-h") || {}).value, 360, MERMAID_WORKSPACE_MAX, 620),
748
- zoom: clampNumber((wrap.querySelector(".rbe-mermaid-zoom") || {}).value, 0.35, 2.5, 1)
749
- };
750
- }
751
-
752
- function bindMermaidDesigner(editor, wrap) {
753
- var drag = null;
754
- var lastShapePointer = { id: "", time: 0 };
755
-
756
- function elements() {
757
- try {
758
- return JSON.parse(wrap.querySelector(".rbe-mermaid-diagram-json").value || "[]");
759
- } catch (error) {
760
- return defaultMermaidDiagram();
761
- }
762
- }
763
-
764
- function resizeWorkspaceToFit(items, point) {
765
- var workspace = readWorkspaceFromBlock(wrap);
766
- var base = defaultMermaidWorkspace();
767
- var maxX = point ? point.x : 0;
768
- var maxY = point ? point.y : 0;
769
- (items || []).forEach(function (elm) {
770
- var bounds = elementBounds(elm);
771
- maxX = Math.max(maxX, bounds.x + bounds.w);
772
- maxY = Math.max(maxY, bounds.y + bounds.h);
773
- });
774
- var width = Math.min(MERMAID_WORKSPACE_MAX, Math.max(base.width, Math.ceil((maxX + MERMAID_WORKSPACE_GROW_PADDING) / MERMAID_WORKSPACE_GROW_STEP) * MERMAID_WORKSPACE_GROW_STEP));
775
- var height = Math.min(MERMAID_WORKSPACE_MAX, Math.max(base.height, Math.ceil((maxY + MERMAID_WORKSPACE_GROW_PADDING) / MERMAID_WORKSPACE_GROW_STEP) * MERMAID_WORKSPACE_GROW_STEP));
776
- if (width !== workspace.width) wrap.querySelector(".rbe-mermaid-workspace-w").value = width;
777
- if (height !== workspace.height) wrap.querySelector(".rbe-mermaid-workspace-h").value = height;
778
- }
779
-
780
- function setElements(next, skipRender, growPoint) {
781
- var normalized = next.map(normalizeDiagramElement);
782
- resizeWorkspaceToFit(normalized, growPoint || null);
783
- wrap.querySelector(".rbe-mermaid-diagram-json").value = JSON.stringify(normalized);
784
- if (!skipRender) {
785
- renderDiagramCanvas(wrap);
786
- updateOptions();
787
- }
788
- }
789
-
790
- function historyStack(name) {
791
- try {
792
- return JSON.parse(wrap.dataset[name] || "[]");
793
- } catch (error) {
794
- return [];
795
- }
796
- }
797
-
798
- function writeHistory(name, stack) {
799
- wrap.dataset[name] = JSON.stringify(stack.slice(-60));
800
- }
801
-
802
- function snapshot() {
803
- return JSON.stringify({ elements: elements(), workspace: readWorkspaceFromBlock(wrap) });
804
- }
805
-
806
- function restoreSnapshot(value) {
807
- var state = JSON.parse(value);
808
- setElements(state.elements || []);
809
- if (state.workspace) {
810
- wrap.querySelector(".rbe-mermaid-workspace-w").value = state.workspace.width || 1100;
811
- wrap.querySelector(".rbe-mermaid-workspace-h").value = state.workspace.height || 620;
812
- wrap.querySelector(".rbe-mermaid-zoom").value = state.workspace.zoom || 1;
813
- }
814
- renderDiagramCanvas(wrap);
815
- updateOptions();
816
- }
817
-
818
- function pushUndo() {
819
- var undo = historyStack("undoStack");
820
- undo.push(snapshot());
821
- writeHistory("undoStack", undo);
822
- writeHistory("redoStack", []);
823
- refreshHistoryButtons();
824
- }
825
-
826
- function refreshHistoryButtons() {
827
- var undo = wrap.querySelector("[data-diagram-action='undo']");
828
- var redo = wrap.querySelector("[data-diagram-action='redo']");
829
- if (undo) undo.disabled = !historyStack("undoStack").length;
830
- if (redo) redo.disabled = !historyStack("redoStack").length;
831
- }
832
-
833
- function undo() {
834
- var undoStack = historyStack("undoStack");
835
- if (!undoStack.length) return;
836
- var redoStack = historyStack("redoStack");
837
- redoStack.push(snapshot());
838
- restoreSnapshot(undoStack.pop());
839
- writeHistory("undoStack", undoStack);
840
- writeHistory("redoStack", redoStack);
841
- refreshHistoryButtons();
842
- editor.changed(true);
843
- }
844
-
845
- function redo() {
846
- var redoStack = historyStack("redoStack");
847
- if (!redoStack.length) return;
848
- var undoStack = historyStack("undoStack");
849
- undoStack.push(snapshot());
850
- restoreSnapshot(redoStack.pop());
851
- writeHistory("undoStack", undoStack);
852
- writeHistory("redoStack", redoStack);
853
- refreshHistoryButtons();
854
- editor.changed(true);
855
- }
856
-
857
- function selectedIds() {
858
- return selectedIdList(wrap.dataset.selectedIds || "");
859
- }
860
-
861
- function setSelected(ids) {
862
- wrap.dataset.selectedIds = (ids || []).filter(Boolean).join(",");
863
- renderDiagramCanvas(wrap);
864
- updateOptions();
865
- }
866
-
867
- function selectedElements() {
868
- var ids = selectedIds();
869
- return elements().filter(function (elm) { return ids.indexOf(elm.id) !== -1; });
870
- }
871
-
872
- function setTool(tool, sourceSelect) {
873
- wrap.dataset.tool = tool;
874
- Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-tool[data-tool]")).forEach(function (button) {
875
- button.classList.toggle("is-active", button.dataset.tool === tool);
876
- });
877
- Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-menu-select")).forEach(function (select) {
878
- if (select !== sourceSelect) select.value = "";
879
- });
880
- }
881
-
882
- function point(event) {
883
- var svg = wrap.querySelector(".rbe-mermaid-canvas");
884
- var rect = svg.getBoundingClientRect();
885
- var workspace = readWorkspaceFromBlock(wrap);
886
- var raw = { x: ((event.clientX - rect.left) / rect.width) * workspace.width, y: ((event.clientY - rect.top) / rect.height) * workspace.height };
887
- return event.altKey ? raw : { x: Math.round(raw.x / 10) * 10, y: Math.round(raw.y / 10) * 10 };
888
- }
889
-
890
- function autoScrollStage(event) {
891
- var stage = wrap.querySelector(".rbe-mermaid-stage");
892
- if (!stage) return;
893
- var rect = stage.getBoundingClientRect();
894
- var edge = 56;
895
- var step = 30;
896
- var dx = 0;
897
- var dy = 0;
898
- if (event.clientX > rect.right - edge) dx = step;
899
- else if (event.clientX < rect.left + edge) dx = -step;
900
- if (event.clientY > rect.bottom - edge) dy = step;
901
- else if (event.clientY < rect.top + edge) dy = -step;
902
- if (dx || dy) {
903
- stage.scrollLeft += dx;
904
- stage.scrollTop += dy;
905
- }
906
- }
907
-
908
- function constrainLinePoint(start, current) {
909
- var dx = current.x - start.x;
910
- var dy = current.y - start.y;
911
- if (!dx && !dy) return current;
912
- var length = Math.sqrt(dx * dx + dy * dy);
913
- var snappedAngle = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
914
- return {
915
- x: Math.round((start.x + Math.cos(snappedAngle) * length) / 10) * 10,
916
- y: Math.round((start.y + Math.sin(snappedAngle) * length) / 10) * 10
917
- };
918
- }
919
-
920
- function selectedBounds() {
921
- var list = selectedElements();
922
- if (!list.length) return null;
923
- var bounds = list.map(elementBounds);
924
- var minX = Math.min.apply(null, bounds.map(function (b) { return b.x; }));
925
- var minY = Math.min.apply(null, bounds.map(function (b) { return b.y; }));
926
- var maxX = Math.max.apply(null, bounds.map(function (b) { return b.x + b.w; }));
927
- var maxY = Math.max.apply(null, bounds.map(function (b) { return b.y + b.h; }));
928
- return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
929
- }
930
-
931
- function updateOptions() {
932
- var selected = selectedElements();
933
- var panel = wrap.querySelector(".rbe-mermaid-stylebar");
934
- if (!panel || !wrap.querySelector(".rbe-mermaid-option-text")) return;
935
- panel.hidden = !selected.length;
936
- if (!selected.length) return;
937
- var first = selected[0];
938
- var isLineSelection = selected.every(function (elm) { return isLineType(elm.type); });
939
- var isTextCapable = selected.some(function (elm) { return !isLineType(elm.type) && elm.type !== "image"; });
940
- panel.classList.toggle("is-line-selection", isLineSelection);
941
- panel.classList.toggle("is-text-capable", isTextCapable);
942
- wrap.querySelector(".rbe-mermaid-option-text").value = selected.length === 1 ? first.text || "" : selected.length + " objects selected";
943
- wrap.querySelector(".rbe-mermaid-option-text").disabled = selected.length !== 1;
944
- wrap.querySelector(".rbe-mermaid-option-font").value = first.fontFamily || "Arial";
945
- wrap.querySelector(".rbe-mermaid-option-size").value = first.fontSize || 16;
946
- var fillColor = first.fill === "transparent" ? "#ffffff" : normalizeChartColor(first.fill, "#eef5ff");
947
- var strokeColor = normalizeChartColor(first.stroke, "#111827");
948
- var textColor = normalizeChartColor(first.textColor, "#172033");
949
- var highlightColor = first.highlightColor === "transparent" ? "#fff3b0" : normalizeChartColor(first.highlightColor, "#fff3b0");
950
- wrap.querySelector(".rbe-mermaid-option-fill").value = fillColor;
951
- wrap.querySelector(".rbe-mermaid-option-stroke").value = strokeColor;
952
- wrap.querySelector(".rbe-mermaid-option-text-color").value = textColor;
953
- wrap.querySelector(".rbe-mermaid-option-highlight").value = highlightColor;
954
- wrap.querySelector(".rbe-mermaid-option-align").value = first.textAlign || "center";
955
- wrap.querySelector(".rbe-mermaid-option-line-height").value = String(normalizeDiagramLineHeight(first.lineHeight));
956
- wrap.querySelector(".rbe-mermaid-option-bullet").value = first.listType === "bullet" ? normalizeDiagramListStyle("bullet", first.listStyle) : "none";
957
- wrap.querySelector(".rbe-mermaid-option-number").value = first.listType === "number" ? normalizeDiagramListStyle("number", first.listStyle) : "none";
958
- panel.style.setProperty("--rbe-mermaid-fill", fillColor);
959
- panel.style.setProperty("--rbe-mermaid-stroke", strokeColor);
960
- panel.style.setProperty("--rbe-mermaid-text", textColor);
961
- panel.style.setProperty("--rbe-mermaid-highlight", first.highlightColor === "transparent" ? "transparent" : highlightColor);
962
- wrap.querySelector(".rbe-mermaid-option-weight").value = first.strokeWidth || 1;
963
- wrap.querySelector(".rbe-mermaid-option-dash").value = normalizeDiagramDash(first.dash);
964
- wrap.querySelector(".rbe-mermaid-option-opacity").value = first.opacity || 1;
965
- Array.prototype.slice.call(wrap.querySelectorAll("[data-format-toggle]")).forEach(function (button) {
966
- var prop = button.dataset.formatToggle;
967
- button.classList.toggle("is-active", !!first[prop]);
968
- });
969
- var activeControls = [
970
- [".rbe-mermaid-bullet-control", first.listType === "bullet"],
971
- [".rbe-mermaid-number-control", first.listType === "number"],
972
- [".rbe-mermaid-spacing-before", !!first.spacingBefore],
973
- [".rbe-mermaid-spacing-after", !!first.spacingAfter]
974
- ];
975
- activeControls.forEach(function (item) {
976
- var node = wrap.querySelector(item[0]);
977
- if (node) node.classList.toggle("is-active", !!item[1]);
978
- });
979
- }
980
-
981
- function updateSelected(mutator, skipUndo) {
982
- var ids = selectedIds();
983
- if (!ids.length) return;
984
- if (!skipUndo) pushUndo();
985
- setElements(elements().map(function (elm) {
986
- if (ids.indexOf(elm.id) !== -1) mutator(elm);
987
- return elm;
988
- }));
989
- editor.changed(true);
990
- }
991
-
992
- function applyStylebarChange(event) {
993
- if (!event.target.closest(".rbe-mermaid-stylebar")) return false;
994
- var bulletChanged = event.target.classList.contains("rbe-mermaid-option-bullet");
995
- var numberChanged = event.target.classList.contains("rbe-mermaid-option-number");
996
- updateSelected(function (elm) {
997
- if (event.target.classList.contains("rbe-mermaid-option-text")) elm.text = event.target.value;
998
- elm.fontFamily = wrap.querySelector(".rbe-mermaid-option-font").value || "Arial";
999
- elm.fontSize = Number(wrap.querySelector(".rbe-mermaid-option-size").value || 16);
1000
- if (!isLineType(elm.type)) elm.fill = wrap.querySelector(".rbe-mermaid-option-fill").value;
1001
- elm.stroke = wrap.querySelector(".rbe-mermaid-option-stroke").value;
1002
- elm.textColor = wrap.querySelector(".rbe-mermaid-option-text-color").value;
1003
- elm.highlightColor = event.target.classList.contains("rbe-mermaid-option-highlight") ? wrap.querySelector(".rbe-mermaid-option-highlight").value : elm.highlightColor;
1004
- elm.textAlign = wrap.querySelector(".rbe-mermaid-option-align").value || "center";
1005
- elm.lineHeight = normalizeDiagramLineHeight(wrap.querySelector(".rbe-mermaid-option-line-height").value);
1006
- if (bulletChanged) {
1007
- var bulletValue = wrap.querySelector(".rbe-mermaid-option-bullet").value || "none";
1008
- elm.listType = bulletValue === "none" ? "none" : "bullet";
1009
- elm.listStyle = bulletValue === "none" ? "none" : bulletValue;
1010
- wrap.querySelector(".rbe-mermaid-option-number").value = "none";
1011
- }
1012
- if (numberChanged) {
1013
- var numberValue = wrap.querySelector(".rbe-mermaid-option-number").value || "none";
1014
- elm.listType = numberValue === "none" ? "none" : "number";
1015
- elm.listStyle = numberValue === "none" ? "none" : numberValue;
1016
- wrap.querySelector(".rbe-mermaid-option-bullet").value = "none";
1017
- }
1018
- elm.strokeWidth = Number(wrap.querySelector(".rbe-mermaid-option-weight").value || 1);
1019
- elm.dash = wrap.querySelector(".rbe-mermaid-option-dash").value;
1020
- elm.opacity = Number(wrap.querySelector(".rbe-mermaid-option-opacity").value || 1);
1021
- }, true);
1022
- return true;
1023
- }
1024
-
1025
- function createElement(type, p, fileData) {
1026
- var line = isLineType(type);
1027
- var text = "";
1028
- if (type === "wordart") text = "WordArt";
1029
- else if (/^(plus|minus|multiply|divide|equal|brackets)$/.test(type)) text = { plus: "+", minus: "-", multiply: "x", divide: "/", equal: "=", brackets: "[ ]" }[type] || "";
1030
- return normalizeDiagramElement({
1031
- id: "diagram-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 6),
1032
- type: type,
1033
- x: p.x,
1034
- y: p.y,
1035
- w: type === "image" ? 220 : type === "wordart" ? 240 : type === "text" ? 190 : 10,
1036
- h: type === "image" ? 150 : type === "wordart" ? 78 : type === "text" ? 70 : 10,
1037
- x1: p.x,
1038
- y1: p.y,
1039
- x2: p.x,
1040
- y2: p.y,
1041
- text: text,
1042
- image: fileData && fileData.url ? fileData.url : "",
1043
- name: fileData && fileData.name ? fileData.name : "",
1044
- fill: line || type === "text" ? "transparent" : type === "wordart" ? "#f59e0b" : "#eef5ff",
1045
- stroke: line ? "#475467" : "#111827",
1046
- strokeWidth: 1,
1047
- fontSize: type === "wordart" ? 36 : 16
1048
- });
1049
- }
1050
-
1051
- function editableDiagramElement(item) {
1052
- return item && !isLineType(item.type) && item.type !== "image";
1053
- }
1054
-
1055
- function restoreTextEditorTargets() {
1056
- Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-hidden-text")).forEach(function (node) {
1057
- node.classList.remove("rbe-mermaid-hidden-text");
1058
- });
1059
- }
1060
-
1061
- function hideTextForEditor(id) {
1062
- restoreTextEditorTargets();
1063
- var group = Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-canvas [data-el-id]")).filter(function (node) {
1064
- return node.getAttribute("data-el-id") === id;
1065
- })[0];
1066
- if (!group) return;
1067
- Array.prototype.slice.call(group.querySelectorAll("text,.rbe-mermaid-text-layer")).forEach(function (node) {
1068
- node.classList.add("rbe-mermaid-hidden-text");
1069
- });
1070
- }
1071
-
1072
- function placeTextEditorCaretAtEnd(node) {
1073
- if (!node) return;
1074
- node.focus();
1075
- try {
1076
- node.setSelectionRange(node.value.length, node.value.length, "none");
1077
- } catch (error) {}
1078
- }
1079
-
1080
- function scheduleTextEditorCaretAtEnd(node) {
1081
- if (!node) return;
1082
- node.dataset.forceCaretEnd = "true";
1083
- placeTextEditorCaretAtEnd(node);
1084
- if (window.requestAnimationFrame) {
1085
- window.requestAnimationFrame(function () { placeTextEditorCaretAtEnd(node); });
1086
- }
1087
- [0, 40, 120].forEach(function (delay) {
1088
- setTimeout(function () { placeTextEditorCaretAtEnd(node); }, delay);
1089
- });
1090
- }
1091
-
1092
- function closeTextEditor(save) {
1093
- var active = wrap.querySelector(".rbe-mermaid-text-editor");
1094
- if (!active) return;
1095
- var id = active.dataset.editId || "";
1096
- var nextText = active.value || active.textContent || "";
1097
- restoreTextEditorTargets();
1098
- active.remove();
1099
- if (!save || !id) return;
1100
- pushUndo();
1101
- setElements(elements().map(function (elm) {
1102
- if (elm.id === id) elm.text = nextText.trim();
1103
- return elm;
1104
- }));
1105
- setSelected([id]);
1106
- editor.changed(true);
1107
- }
1108
-
1109
- function openTextEditor(id, seedText) {
1110
- var item = elements().filter(function (elm) { return elm.id === id; })[0];
1111
- if (!editableDiagramElement(item)) return;
1112
- closeTextEditor(true);
1113
- var stage = wrap.querySelector(".rbe-mermaid-stage");
1114
- var workspace = readWorkspaceFromBlock(wrap);
1115
- var svg = wrap.querySelector(".rbe-mermaid-canvas");
1116
- var svgRect = svg ? svg.getBoundingClientRect() : null;
1117
- var scaleX = svgRect && svgRect.width ? svgRect.width / workspace.width : workspace.zoom;
1118
- var scaleY = svgRect && svgRect.height ? svgRect.height / workspace.height : workspace.zoom;
1119
- var scale = Math.min(scaleX, scaleY);
1120
- var node = document.createElement("textarea");
1121
- var initial = seedText != null ? seedText : item.text || "";
1122
- node.className = "rbe-mermaid-text-editor";
1123
- node.dataset.editId = id;
1124
- node.spellcheck = false;
1125
- node.wrap = "soft";
1126
- node.setAttribute("role", "textbox");
1127
- node.setAttribute("aria-label", "Shape text");
1128
- if (item.type === "text") node.setAttribute("placeholder", "Text");
1129
- var textBox = { x: item.x, y: item.y, w: item.w, h: item.h };
1130
- if (item.type === "callout") textBox.h = item.h * .72;
1131
- if (item.type === "roundCallout") textBox.h = item.h * .7;
1132
- if (item.type === "thought") textBox.h = item.h * .72;
1133
- var pixelX = textBox.x * scaleX;
1134
- var pixelY = textBox.y * scaleY;
1135
- var pixelW = Math.max(42, textBox.w * scaleX);
1136
- var pixelH = Math.max(32, textBox.h * scaleY);
1137
- node.style.left = pixelX + "px";
1138
- node.style.top = pixelY + "px";
1139
- node.style.width = pixelW + "px";
1140
- node.style.height = pixelH + "px";
1141
- node.style.fontFamily = item.fontFamily || "Arial";
1142
- var fontSize = Math.max(18, (item.fontSize || 16) * scale);
1143
- var lineHeight = Math.round(fontSize * 1.25);
1144
- node.style.fontSize = fontSize + "px";
1145
- lineHeight = Math.round(fontSize * normalizeDiagramLineHeight(item.lineHeight));
1146
- node.style.lineHeight = lineHeight + "px";
1147
- node.style.padding = Math.max(4, Math.floor((pixelH - lineHeight) / 2)) + "px 8px 4px";
1148
- node.style.fontWeight = item.bold ? "800" : "700";
1149
- node.style.fontStyle = item.italic ? "italic" : "normal";
1150
- node.style.color = item.textColor || "#172033";
1151
- node.style.textAlign = item.textAlign || "center";
1152
- node.style.backgroundColor = item.highlightColor && item.highlightColor !== "transparent" ? item.highlightColor : "transparent";
1153
- node.value = initial;
1154
- hideTextForEditor(id);
1155
- stage.appendChild(node);
1156
- scheduleTextEditorCaretAtEnd(node);
1157
- setTimeout(function () {
1158
- node.dataset.allowPointerCaret = "true";
1159
- }, 250);
1160
- node.addEventListener("pointerdown", function () {
1161
- if (node.dataset.allowPointerCaret === "true") node.dataset.forceCaretEnd = "";
1162
- });
1163
- node.addEventListener("keydown", function (event) {
1164
- event.stopPropagation();
1165
- if (event.ctrlKey || event.metaKey || event.altKey) {
1166
- node.dataset.forceCaretEnd = "";
1167
- return;
1168
- }
1169
- if (node.dataset.forceCaretEnd === "true" && (event.key.length === 1 || event.key === "Backspace" || event.key === "Delete")) {
1170
- placeTextEditorCaretAtEnd(node);
1171
- node.dataset.forceCaretEnd = "";
1172
- if (event.key.length === 1 && /^[A-Za-z0-9]$/.test(event.key) && node.value && !/\s$/.test(node.value)) {
1173
- event.preventDefault();
1174
- node.setRangeText(" " + event.key, node.value.length, node.value.length, "end");
1175
- return;
1176
- }
1177
- }
1178
- if (event.key === "Enter" && !event.shiftKey) {
1179
- event.preventDefault();
1180
- closeTextEditor(true);
1181
- }
1182
- if (event.key === "Escape") {
1183
- event.preventDefault();
1184
- closeTextEditor(false);
1185
- }
1186
- });
1187
- node.addEventListener("blur", function () {
1188
- closeTextEditor(true);
1189
- }, { once: true });
1190
- }
1191
-
1192
- function applyAction(action) {
1193
- var list = elements();
1194
- var ids = selectedIds();
1195
- if (action === "image") {
1196
- wrap.querySelector(".rbe-mermaid-image-input").click();
1197
- return;
1198
- }
1199
- if (!ids.length && !/^(paste|downloadSvg|downloadPng|downloadJpeg|downloadPdf|fit|zoomIn|zoomOut)$/.test(action)) return;
1200
- if (action === "undo") return undo();
1201
- if (action === "redo") return redo();
1202
- if (action === "copy" || action === "cut") {
1203
- wrap.dataset.clipboard = JSON.stringify(selectedElements());
1204
- if (action === "cut") {
1205
- pushUndo();
1206
- setElements(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
1207
- setSelected([]);
1208
- editor.changed(true);
1209
- }
1210
- return;
1211
- }
1212
- if (action === "paste") {
1213
- var clip = [];
1214
- try { clip = JSON.parse(wrap.dataset.clipboard || "[]"); } catch (error) {}
1215
- if (!clip.length) return;
1216
- pushUndo();
1217
- var pasted = clip.map(function (elm) {
1218
- elm = normalizeDiagramElement(elm);
1219
- elm.id = "diagram-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 6);
1220
- if (isLineType(elm.type)) {
1221
- elm.x1 += 30; elm.x2 += 30; elm.y1 += 30; elm.y2 += 30;
1222
- } else {
1223
- elm.x += 30; elm.y += 30;
1224
- }
1225
- return elm;
1226
- });
1227
- setElements(list.concat(pasted));
1228
- setSelected(pasted.map(function (elm) { return elm.id; }));
1229
- editor.changed(true);
1230
- return;
1231
- }
1232
- if (action === "duplicate") {
1233
- wrap.dataset.clipboard = JSON.stringify(selectedElements());
1234
- return applyAction("paste");
1235
- }
1236
- if (action === "delete") {
1237
- pushUndo();
1238
- setElements(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
1239
- setSelected([]);
1240
- editor.changed(true);
1241
- return;
1242
- }
1243
- if (/^download/.test(action)) return downloadDiagram(wrap, action);
1244
- if (action === "zoomIn" || action === "zoomOut" || action === "fit") {
1245
- var zoom = wrap.querySelector(".rbe-mermaid-zoom");
1246
- zoom.value = action === "fit" ? 1 : clampNumber(Number(zoom.value || 1) + (action === "zoomIn" ? .1 : -.1), .35, 2.5, 1);
1247
- renderDiagramCanvas(wrap);
1248
- editor.changed(true);
1249
- return;
1250
- }
1251
- pushUndo();
1252
- if (action === "bringFront") list = list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }).concat(list.filter(function (elm) { return ids.indexOf(elm.id) !== -1; }));
1253
- else if (action === "sendBack") list = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1; }).concat(list.filter(function (elm) { return ids.indexOf(elm.id) === -1; }));
1254
- else if (action === "bringForward" || action === "sendBackward") {
1255
- var step = action === "bringForward" ? 1 : -1;
1256
- ids.forEach(function (id) {
1257
- var index = list.findIndex(function (elm) { return elm.id === id; });
1258
- var nextIndex = index + step;
1259
- if (index < 0 || nextIndex < 0 || nextIndex >= list.length) return;
1260
- var item = list[index];
1261
- list.splice(index, 1);
1262
- list.splice(nextIndex, 0, item);
1263
- });
1264
- }
1265
- else {
1266
- var bounds = selectedBounds();
1267
- if (action === "group") {
1268
- var groupId = "group-" + Date.now().toString(36);
1269
- list.forEach(function (elm) { if (ids.indexOf(elm.id) !== -1) elm.group = groupId; });
1270
- }
1271
- if (action === "ungroup") {
1272
- list.forEach(function (elm) { if (ids.indexOf(elm.id) !== -1) elm.group = ""; });
1273
- }
1274
- if (action === "distributeH" && ids.length > 2) {
1275
- var hItems = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1 && !isLineType(elm.type); }).sort(function (a, b) { return a.x - b.x; });
1276
- var firstX = hItems[0].x;
1277
- var lastX = hItems[hItems.length - 1].x;
1278
- var gapX = (lastX - firstX) / (hItems.length - 1);
1279
- hItems.forEach(function (elm, index) { elm.x = firstX + gapX * index; });
1280
- }
1281
- if (action === "distributeV" && ids.length > 2) {
1282
- var vItems = list.filter(function (elm) { return ids.indexOf(elm.id) !== -1 && !isLineType(elm.type); }).sort(function (a, b) { return a.y - b.y; });
1283
- var firstY = vItems[0].y;
1284
- var lastY = vItems[vItems.length - 1].y;
1285
- var gapY = (lastY - firstY) / (vItems.length - 1);
1286
- vItems.forEach(function (elm, index) { elm.y = firstY + gapY * index; });
1287
- }
1288
- list.forEach(function (elm) {
1289
- if (ids.indexOf(elm.id) === -1) return;
1290
- if (action === "rotateClockwise") elm.rotation = (elm.rotation || 0) + 15;
1291
- if (action === "rotateCounter") elm.rotation = (elm.rotation || 0) - 15;
1292
- if (action === "flipH" && !isLineType(elm.type)) elm.flipX = !elm.flipX;
1293
- if (action === "flipV" && !isLineType(elm.type)) elm.flipY = !elm.flipY;
1294
- if (!bounds || isLineType(elm.type)) return;
1295
- if (action === "alignLeft") elm.x = bounds.x;
1296
- if (action === "alignCenter") elm.x = bounds.x + bounds.w / 2 - elm.w / 2;
1297
- if (action === "alignRight") elm.x = bounds.x + bounds.w - elm.w;
1298
- if (action === "alignTop") elm.y = bounds.y;
1299
- if (action === "alignMiddle") elm.y = bounds.y + bounds.h / 2 - elm.h / 2;
1300
- if (action === "alignBottom") elm.y = bounds.y + bounds.h - elm.h;
1301
- });
1302
- }
1303
- setElements(list);
1304
- editor.changed(true);
1305
- }
1306
-
1307
- function downloadDiagram(block, action) {
1308
- var svg = diagramSvg(elements(), "", readWorkspaceFromBlock(block), null);
1309
- var mime = "image/svg+xml";
1310
- var ext = "svg";
1311
- if (action === "downloadSvg") {
1312
- triggerDownload("blockwriteai-diagram.svg", "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg));
1313
- return;
1314
- }
1315
- if (action === "downloadPdf") {
1316
- var popup = window.open("", "_blank");
1317
- if (popup) {
1318
- popup.document.write("<!doctype html><title>BlockWriteAI Diagram</title><style>body{margin:24px;font-family:Arial,sans-serif}svg{max-width:100%;height:auto}</style>" + svg + "<script>setTimeout(function(){window.print();},250);<\/script>");
1319
- popup.document.close();
1320
- }
1321
- return;
1322
- }
1323
- var img = new Image();
1324
- var url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
1325
- img.onload = function () {
1326
- var workspace = readWorkspaceFromBlock(block);
1327
- var canvas = document.createElement("canvas");
1328
- canvas.width = workspace.width;
1329
- canvas.height = workspace.height;
1330
- var ctx = canvas.getContext("2d");
1331
- ctx.fillStyle = "#ffffff";
1332
- ctx.fillRect(0, 0, canvas.width, canvas.height);
1333
- ctx.drawImage(img, 0, 0);
1334
- mime = action === "downloadJpeg" ? "image/jpeg" : "image/png";
1335
- ext = action === "downloadJpeg" ? "jpg" : "png";
1336
- triggerDownload("blockwriteai-diagram." + ext, canvas.toDataURL(mime, .92));
1337
- };
1338
- img.src = url;
1339
- }
1340
-
1341
- function triggerDownload(name, href) {
1342
- var link = document.createElement("a");
1343
- link.href = href;
1344
- link.download = name;
1345
- document.body.appendChild(link);
1346
- link.click();
1347
- link.remove();
1348
- }
1349
-
1350
- wrap.addEventListener("click", function (event) {
1351
- var choiceButton = event.target.closest(".rbe-mermaid-choice[data-tool],.rbe-mermaid-line-choice[data-tool]");
1352
- if (choiceButton) {
1353
- event.preventDefault();
1354
- setTool(choiceButton.dataset.tool || "select");
1355
- return;
1356
- }
1357
- var toolButton = event.target.closest(".rbe-mermaid-tool[data-tool]");
1358
- if (toolButton) {
1359
- event.preventDefault();
1360
- setTool(toolButton.dataset.tool || "select");
1361
- return;
1362
- }
1363
- var textActionButton = event.target.closest("[data-text-action]");
1364
- if (textActionButton) {
1365
- event.preventDefault();
1366
- var textAction = textActionButton.dataset.textAction || "";
1367
- updateSelected(function (elm) {
1368
- if (isLineType(elm.type) || elm.type === "image") return;
1369
- if (textAction === "indent") elm.indent = clampNumber(Number(elm.indent || 0) + 1, 0, 8, 0);
1370
- if (textAction === "outdent") elm.indent = clampNumber(Number(elm.indent || 0) - 1, 0, 8, 0);
1371
- if (textAction === "spacing-before") elm.spacingBefore = elm.spacingBefore ? 0 : 10;
1372
- if (textAction === "spacing-after") elm.spacingAfter = elm.spacingAfter ? 0 : 10;
1373
- if (textAction === "reset-highlight") {
1374
- elm.highlightColor = "transparent";
1375
- }
1376
- });
1377
- updateOptions();
1378
- return;
1379
- }
1380
- var alignButton = event.target.closest(".rbe-mermaid-align");
1381
- if (alignButton) {
1382
- event.preventDefault();
1383
- wrap.querySelector(".rbe-mermaid-alignment").value = alignButton.dataset.align || "center";
1384
- Array.prototype.slice.call(wrap.querySelectorAll(".rbe-mermaid-align")).forEach(function (button) {
1385
- button.classList.toggle("is-active", button === alignButton);
1386
- });
1387
- editor.changed(true);
1388
- return;
1389
- }
1390
- var styleButton = event.target.closest("[data-style-action]");
1391
- if (styleButton) {
1392
- event.preventDefault();
1393
- var sizeInput = wrap.querySelector(".rbe-mermaid-option-size");
1394
- var nextSize = Number(sizeInput.value || 16) + (styleButton.dataset.styleAction === "font-size-up" ? 2 : -2);
1395
- sizeInput.value = clampNumber(nextSize, 8, 96, 16);
1396
- applyStylebarChange({ target: sizeInput });
1397
- updateOptions();
1398
- return;
1399
- }
1400
- var actionButton = event.target.closest("[data-diagram-action]");
1401
- if (actionButton) {
1402
- event.preventDefault();
1403
- applyAction(actionButton.dataset.diagramAction);
1404
- }
1405
- });
1406
-
1407
- wrap.addEventListener("change", function (event) {
1408
- if (applyStylebarChange(event)) return;
1409
- var picker = event.target.closest(".rbe-mermaid-menu-select");
1410
- if (picker && picker.value) {
1411
- setTool(picker.value, picker);
1412
- return;
1413
- }
1414
- var action = event.target.closest(".rbe-mermaid-action-select");
1415
- if (action && action.value) {
1416
- applyAction(action.value);
1417
- action.value = "";
1418
- return;
1419
- }
1420
- if (event.target.closest(".rbe-mermaid-workspace")) {
1421
- pushUndo();
1422
- renderDiagramCanvas(wrap);
1423
- editor.changed(true);
1424
- }
1425
- });
1426
-
1427
- wrap.addEventListener("input", function (event) {
1428
- applyStylebarChange(event);
1429
- });
1430
-
1431
- wrap.addEventListener("click", function (event) {
1432
- var formatButton = event.target.closest("[data-format-toggle]");
1433
- if (!formatButton) return;
1434
- event.preventDefault();
1435
- var prop = formatButton.dataset.formatToggle;
1436
- updateSelected(function (elm) { elm[prop] = !elm[prop]; });
1437
- });
1438
-
1439
- wrap.addEventListener("dblclick", function (event) {
1440
- var target = event.target.closest("[data-el-id]");
1441
- if (!target) return;
1442
- var item = elements().filter(function (elm) { return elm.id === target.dataset.elId; })[0];
1443
- if (!editableDiagramElement(item)) return;
1444
- event.preventDefault();
1445
- setSelected([item.id]);
1446
- openTextEditor(item.id);
1447
- });
1448
-
1449
- wrap.addEventListener("pointerdown", function (event) {
1450
- var svg = event.target.closest(".rbe-mermaid-canvas");
1451
- if (!svg) return;
1452
- if (wrap.querySelector(".rbe-mermaid-text-editor") && !event.target.closest(".rbe-mermaid-text-editor")) {
1453
- closeTextEditor(true);
1454
- svg = wrap.querySelector(".rbe-mermaid-canvas");
1455
- if (!svg) return;
1456
- }
1457
- wrap.focus();
1458
- event.preventDefault();
1459
- var p = point(event);
1460
- var handle = event.target.closest("[data-handle]");
1461
- var target = event.target.closest("[data-el-id]");
1462
- var tool = wrap.dataset.tool || "select";
1463
- if (target && tool === "select" && !handle) {
1464
- var targetId = target.dataset.elId;
1465
- var now = Date.now();
1466
- var isFastRepeat = lastShapePointer.id === targetId && now - lastShapePointer.time < 430;
1467
- lastShapePointer = { id: targetId, time: now };
1468
- if (Number(event.detail || 0) >= 2 || isFastRepeat) {
1469
- var doubleClicked = elements().filter(function (elm) { return elm.id === targetId; })[0];
1470
- if (editableDiagramElement(doubleClicked)) {
1471
- drag = null;
1472
- setSelected([doubleClicked.id]);
1473
- openTextEditor(doubleClicked.id);
1474
- return;
1475
- }
1476
- }
1477
- } else if (!target) {
1478
- lastShapePointer = { id: "", time: 0 };
1479
- }
1480
- if (handle && selectedIds().length === 1) {
1481
- pushUndo();
1482
- drag = { mode: handle.dataset.handle === "rotate" ? "rotate" : "resize", handle: handle.dataset.handle, start: p, ids: selectedIds(), snapshot: elements() };
1483
- svg.setPointerCapture(event.pointerId);
1484
- return;
1485
- }
1486
- if (target && tool === "select") {
1487
- var id = target.dataset.elId;
1488
- var clicked = elements().filter(function (elm) { return elm.id === id; })[0];
1489
- var groupIds = clicked && clicked.group ? elements().filter(function (elm) { return elm.group === clicked.group; }).map(function (elm) { return elm.id; }) : [id];
1490
- var current = selectedIds();
1491
- if (event.shiftKey || event.ctrlKey) {
1492
- groupIds.forEach(function (groupId) {
1493
- if (current.indexOf(groupId) === -1) current.push(groupId);
1494
- else current = current.filter(function (item) { return item !== groupId; });
1495
- });
1496
- setSelected(current);
1497
- } else if (current.indexOf(id) === -1) {
1498
- setSelected(groupIds);
1499
- }
1500
- pushUndo();
1501
- drag = { mode: "move", ids: selectedIds(), start: p, snapshot: elements() };
1502
- svg.setPointerCapture(event.pointerId);
1503
- return;
1504
- }
1505
- if (tool === "select") {
1506
- setSelected([]);
1507
- drag = { mode: "marquee", start: p };
1508
- svg.setPointerCapture(event.pointerId);
1509
- return;
1510
- }
1511
- var type = tool;
1512
- if (!DIAGRAM_SHAPE_TYPES.test(type) && !DIAGRAM_LINE_TYPES.test(type)) return;
1513
- pushUndo();
1514
- var created = createElement(type, p);
1515
- setElements(elements().concat([created]));
1516
- setSelected([created.id]);
1517
- drag = { mode: "draw", id: created.id, start: p };
1518
- svg.setPointerCapture(event.pointerId);
1519
- });
1520
-
1521
- wrap.addEventListener("pointermove", function (event) {
1522
- if (!drag) return;
1523
- autoScrollStage(event);
1524
- var p = point(event);
1525
- resizeWorkspaceToFit(elements(), p);
1526
- if (drag.mode === "marquee") {
1527
- var marquee = { x: Math.min(drag.start.x, p.x), y: Math.min(drag.start.y, p.y), w: Math.abs(p.x - drag.start.x), h: Math.abs(p.y - drag.start.y) };
1528
- wrap.dataset.marquee = JSON.stringify(marquee);
1529
- renderDiagramCanvas(wrap);
1530
- return;
1531
- }
1532
- var next = elements().map(function (elm) {
1533
- if (drag.mode === "move" && drag.ids.indexOf(elm.id) !== -1) {
1534
- var original = drag.snapshot.filter(function (item) { return item.id === elm.id; })[0] || elm;
1535
- var dx = p.x - drag.start.x;
1536
- var dy = p.y - drag.start.y;
1537
- if (isLineType(elm.type)) {
1538
- elm.x1 = original.x1 + dx;
1539
- elm.y1 = original.y1 + dy;
1540
- elm.x2 = original.x2 + dx;
1541
- elm.y2 = original.y2 + dy;
1542
- } else {
1543
- elm.x = original.x + dx;
1544
- elm.y = original.y + dy;
1545
- }
1546
- } else if (drag.mode === "draw" && elm.id === drag.id) {
1547
- if (isLineType(elm.type)) {
1548
- var linePoint = event.shiftKey ? constrainLinePoint(drag.start, p) : p;
1549
- elm.x2 = linePoint.x;
1550
- elm.y2 = linePoint.y;
1551
- } else {
1552
- elm.x = Math.min(drag.start.x, p.x);
1553
- elm.y = Math.min(drag.start.y, p.y);
1554
- elm.w = Math.max(40, Math.abs(p.x - drag.start.x));
1555
- elm.h = Math.max(32, Math.abs(p.y - drag.start.y));
1556
- }
1557
- } else if ((drag.mode === "resize" || drag.mode === "rotate") && drag.ids.indexOf(elm.id) !== -1) {
1558
- var base = drag.snapshot.filter(function (item) { return item.id === elm.id; })[0] || elm;
1559
- if (isLineType(elm.type)) {
1560
- if (drag.handle === "start") { elm.x1 = p.x; elm.y1 = p.y; }
1561
- if (drag.handle === "end") { elm.x2 = p.x; elm.y2 = p.y; }
1562
- } else if (drag.mode === "rotate") {
1563
- var cx = base.x + base.w / 2;
1564
- var cy = base.y + base.h / 2;
1565
- elm.rotation = Math.round(Math.atan2(p.y - cy, p.x - cx) * 180 / Math.PI + 90);
1566
- } else {
1567
- var x2 = base.x + base.w;
1568
- var y2 = base.y + base.h;
1569
- if (/w/.test(drag.handle)) elm.x = p.x;
1570
- if (/n/.test(drag.handle)) elm.y = p.y;
1571
- if (/e/.test(drag.handle)) x2 = p.x;
1572
- if (/s/.test(drag.handle)) y2 = p.y;
1573
- elm.w = Math.max(24, x2 - elm.x);
1574
- elm.h = Math.max(24, y2 - elm.y);
1575
- }
1576
- }
1577
- return elm;
1578
- });
1579
- setElements(next, false, p);
1580
- });
1581
-
1582
- wrap.addEventListener("pointerup", function () {
1583
- if (!drag) return;
1584
- var completedDrawId = drag.mode === "draw" ? drag.id : "";
1585
- if (drag.mode === "marquee") {
1586
- var marquee = JSON.parse(wrap.dataset.marquee || "{}");
1587
- var ids = elements().filter(function (elm) { return rectsIntersect(elementBounds(elm), marquee); }).map(function (elm) { return elm.id; });
1588
- wrap.dataset.marquee = "";
1589
- setSelected(ids);
1590
- }
1591
- if (drag.mode === "draw") setTool("select");
1592
- drag = null;
1593
- refreshHistoryButtons();
1594
- editor.changed(true);
1595
- if (completedDrawId) {
1596
- var item = elements().filter(function (elm) { return elm.id === completedDrawId; })[0];
1597
- if (editableDiagramElement(item)) setTimeout(function () { openTextEditor(completedDrawId); }, 0);
1598
- }
1599
- });
1600
-
1601
- wrap.addEventListener("keydown", function (event) {
1602
- if (event.target.closest("input,textarea,select,[contenteditable='true']")) return;
1603
- if (!event.ctrlKey && !event.metaKey && !event.altKey && event.key && event.key.length === 1) {
1604
- var selected = selectedElements();
1605
- if (selected.length === 1 && editableDiagramElement(selected[0])) {
1606
- event.preventDefault();
1607
- var existingText = String(selected[0].text || "").trim();
1608
- var typedText = existingText ? existingText + (event.key === " " ? "" : " ") + event.key : event.key;
1609
- openTextEditor(selected[0].id, typedText);
1610
- return;
1611
- }
1612
- }
1613
- if (event.key === "Delete" || event.key === "Backspace") {
1614
- event.preventDefault();
1615
- applyAction("delete");
1616
- }
1617
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
1618
- event.preventDefault();
1619
- if (event.shiftKey) redo();
1620
- else undo();
1621
- }
1622
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "y") {
1623
- event.preventDefault();
1624
- redo();
1625
- }
1626
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "c") {
1627
- event.preventDefault();
1628
- applyAction("copy");
1629
- }
1630
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "x") {
1631
- event.preventDefault();
1632
- applyAction("cut");
1633
- }
1634
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "v") {
1635
- event.preventDefault();
1636
- applyAction("paste");
1637
- }
1638
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "d") {
1639
- event.preventDefault();
1640
- applyAction("duplicate");
1641
- }
1642
- });
1643
-
1644
- var imageInput = wrap.querySelector(".rbe-mermaid-image-input");
1645
- function readMermaidImageAsDataURL(file) {
1646
- return new Promise(function (resolve, reject) {
1647
- var reader = new FileReader();
1648
- reader.onload = function () { resolve({ url: reader.result, name: file.name }); };
1649
- reader.onerror = function () { reject(reader.error || new Error("Image read failed")); };
1650
- reader.readAsDataURL(file);
1651
- });
1652
- }
1653
-
1654
- function uploadMermaidImage(file) {
1655
- if (editor && typeof editor.uploadFile === "function") {
1656
- return editor.uploadFile(file, "image").catch(function () {
1657
- return readMermaidImageAsDataURL(file);
1658
- });
1659
- }
1660
- return readMermaidImageAsDataURL(file);
1661
- }
1662
-
1663
- imageInput.addEventListener("change", function (event) {
1664
- var file = event.target.files && event.target.files[0];
1665
- if (!file || !/^image\//.test(file.type || "")) return;
1666
- uploadMermaidImage(file).then(function (uploaded) {
1667
- if (!uploaded || !uploaded.url) return;
1668
- pushUndo();
1669
- var workspace = readWorkspaceFromBlock(wrap);
1670
- var created = createElement("image", { x: workspace.width / 2 - 140, y: 80 }, { url: uploaded.url, name: uploaded.name || file.name });
1671
- created.w = 280;
1672
- created.h = 190;
1673
- setElements(elements().concat([created]));
1674
- setSelected([created.id]);
1675
- setTool("select");
1676
- editor.changed(true);
1677
- }).catch(function (error) {
1678
- window.console && console.warn && console.warn("Mermaid image upload failed", error);
1679
- });
1680
- event.target.value = "";
1681
- });
1682
-
1683
- setTool("select");
1684
- refreshHistoryButtons();
1685
- updateOptions();
1686
- }
1687
-
1688
- function mermaidChoice(tool, icon, label) {
1689
- return '<button type="button" class="rbe-mermaid-choice" data-tool="' + esc(tool) + '" title="' + esc(label) + '"><span aria-hidden="true">' + icon + '</span><span class="rbe-sr-only">' + esc(label) + "</span></button>";
1690
- }
1691
-
1692
- function mermaidChoiceGrid(items) {
1693
- return '<div class="rbe-mermaid-choice-grid">' + items.map(function (item) {
1694
- return mermaidChoice(item[0], item[1], item[2]);
1695
- }).join("") + "</div>";
1696
- }
1697
-
1698
- function mermaidShapeMenuRow(label, icon, grid) {
1699
- return '<div class="rbe-mermaid-menu-row"><span class="rbe-mermaid-menu-row-icon">' + faIcon(icon, label) + '</span><span>' + esc(label) + '</span><div class="rbe-mermaid-submenu">' + grid + "</div></div>";
1700
- }
1701
-
1702
- function mermaidShapeMenu() {
1703
- var basics = mermaidChoiceGrid([
1704
- ["rect", "□", "Rectangle"], ["roundedRect", "▢", "Rounded rectangle"], ["circle", "○", "Circle"], ["oval", "◯", "Oval"], ["triangle", "△", "Triangle"],
1705
- ["diamond", "◇", "Diamond"], ["pentagon", "⬟", "Pentagon"], ["hexagon", "⬡", "Hexagon"], ["parallelogram", "▱", "Parallelogram"], ["trapezoid", "⌂", "Trapezoid"],
1706
- ["octagon", "⬢", "Octagon"], ["heart", "♡", "Heart"], ["cloud", "☁", "Cloud"], ["cylinder", "◫", "Cylinder"], ["cube", "▣", "Cube"]
1707
- ]);
1708
- var arrows = mermaidChoiceGrid([
1709
- ["arrow", "→", "Right arrow"], ["leftArrow", "←", "Left arrow"], ["upArrow", "↑", "Up arrow"], ["downArrow", "↓", "Down arrow"], ["biArrow", "↔", "Bidirectional arrow"]
1710
- ]);
1711
- var callouts = mermaidChoiceGrid([
1712
- ["callout", "▱", "Callout"], ["roundCallout", "▢", "Rounded callout"], ["thought", "☁", "Thought bubble"]
1713
- ]);
1714
- var equations = mermaidChoiceGrid([
1715
- ["plus", "+", "Plus"], ["minus", "−", "Minus"], ["multiply", "×", "Multiply"], ["divide", "÷", "Divide"], ["equal", "=", "Equal"], ["brackets", "[ ]", "Brackets"]
1716
- ]);
1717
- return '<div class="rbe-mermaid-menu"><button type="button" class="rbe-mermaid-menu-btn" title="Shapes">' + faIcon("shapes", "Shapes") + '</button><div class="rbe-mermaid-popover">' + mermaidShapeMenuRow("Shapes", "square", basics) + mermaidShapeMenuRow("Arrows", "arrow-right", arrows) + mermaidShapeMenuRow("Callouts", "comment", callouts) + mermaidShapeMenuRow("Equation", "plus", equations) + "</div></div>";
1718
- }
1719
-
1720
- function mermaidLineChoice(tool, icon, label) {
1721
- return '<button type="button" class="rbe-mermaid-line-choice" data-tool="' + esc(tool) + '"><span class="rbe-mermaid-menu-row-icon">' + faIcon(icon, label) + '</span><span>' + esc(label) + "</span></button>";
1722
- }
1723
-
1724
- function mermaidLineMenu() {
1725
- return '<div class="rbe-mermaid-menu rbe-mermaid-line-menu"><button type="button" class="rbe-mermaid-menu-btn" title="Line options">' + faIcon("caret-down", "Line options") + '</button><div class="rbe-mermaid-popover">' +
1726
- mermaidLineChoice("line", "minus", "Line") +
1727
- mermaidLineChoice("arrow", "arrow-right-long", "Arrow") +
1728
- mermaidLineChoice("elbow", "share-nodes", "Elbow Connector") +
1729
- mermaidLineChoice("curved", "code-branch", "Curved Connector") +
1730
- mermaidLineChoice("curve", "bezier-curve", "Curve") +
1731
- mermaidLineChoice("polyline", "draw-polygon", "Polyline") +
1732
- mermaidLineChoice("scribble", "signature", "Scribble") +
1733
- "</div></div>";
1734
- }
1735
-
1736
- function mermaidShapeStylebar() {
1737
- return '<div class="rbe-mermaid-stylebar" hidden aria-label="Shape style controls">' +
1738
- '<input class="rbe-mermaid-option-text rbe-mermaid-hidden-control" type="text" aria-hidden="true" tabindex="-1">' +
1739
- '<input class="rbe-mermaid-option-opacity rbe-mermaid-hidden-control" type="hidden" value="1">' +
1740
- '<label class="rbe-mermaid-icon-control rbe-mermaid-fill-control" title="Fill color">' + faIcon("fill-drip", "Fill color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-fill-swatch"></span><input class="rbe-mermaid-option-fill" type="color" value="#eef5ff" aria-label="Fill color"></label>' +
1741
- '<label class="rbe-mermaid-icon-control" title="Border color">' + faIcon("pen-nib", "Border color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-stroke-swatch"></span><input class="rbe-mermaid-option-stroke" type="color" value="#111827" aria-label="Border color"></label>' +
1742
- '<label class="rbe-mermaid-icon-control" title="Border weight">' + faIcon("grip-lines", "Border weight") + '<select class="rbe-mermaid-option-weight" aria-label="Border weight"><option value="1">1px</option><option value="2">2px</option><option value="3">3px</option><option value="4">4px</option><option value="8">8px</option><option value="12">12px</option><option value="16">16px</option><option value="24">24px</option></select></label>' +
1743
- '<label class="rbe-mermaid-icon-control" title="Border dash">' + faIcon("grip-lines-vertical", "Border dash") + '<select class="rbe-mermaid-option-dash" aria-label="Border dash"><option value="solid">Solid</option><option value="dotted">Dotted</option><option value="dashed">Dashed</option><option value="longDash">Long dash</option><option value="dashDot">Dash dot</option></select></label>' +
1744
- '<span class="rbe-mermaid-toolbar-sep rbe-mermaid-text-control"></span>' +
1745
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Font family">' + faIcon("font", "Font family") + '<select class="rbe-mermaid-option-font" aria-label="Font family"><option value="Arial">Arial</option><option value="Verdana">Verdana</option><option value="Tahoma">Tahoma</option><option value="Georgia">Georgia</option><option value="Times New Roman">Times New Roman</option><option value="Courier New">Courier New</option></select></label>' +
1746
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-style-action="font-size-down" title="Decrease font size">' + faIcon("minus", "Decrease font size") + '</button>' +
1747
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Font size">' + faIcon("text-height", "Font size") + '<select class="rbe-mermaid-option-size" aria-label="Font size"><option value="10">10</option><option value="12">12</option><option value="14">14</option><option value="16">16</option><option value="18">18</option><option value="20">20</option><option value="24">24</option><option value="28">28</option><option value="32">32</option><option value="40">40</option><option value="48">48</option><option value="64">64</option><option value="72">72</option></select></label>' +
1748
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-style-action="font-size-up" title="Increase font size">' + faIcon("plus", "Increase font size") + '</button>' +
1749
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="bold" title="Bold">' + faIcon("bold", "Bold") + '</button>' +
1750
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="italic" title="Italic">' + faIcon("italic", "Italic") + '</button>' +
1751
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-format-toggle="underline" title="Underline">' + faIcon("underline", "Underline") + '</button>' +
1752
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Text color">' + faIcon("font", "Text color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-text-swatch"></span><input class="rbe-mermaid-option-text-color" type="color" value="#172033" aria-label="Text color"></label>' +
1753
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Highlight color">' + faIcon("highlighter", "Highlight color") + '<span class="rbe-mermaid-control-swatch rbe-mermaid-highlight-swatch"></span><input class="rbe-mermaid-option-highlight" type="color" value="#fff3b0" aria-label="Highlight color"></label>' +
1754
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="reset-highlight" title="Reset highlight">' + faIcon("rotate-left", "Reset highlight") + '</button>' +
1755
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Text alignment">' + faIcon("align-center", "Text alignment") + '<select class="rbe-mermaid-option-align" aria-label="Text alignment"><option value="left">Left</option><option value="center">Center</option><option value="right">Right</option><option value="justify">Justify</option></select></label>' +
1756
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control" title="Line spacing">' + faIcon("arrows-up-down", "Line spacing") + '<select class="rbe-mermaid-option-line-height" aria-label="Line spacing"><option value="1">Single</option><option value="1.15">1.15</option><option value="1.5">1.5</option><option value="2">Double</option></select></label>' +
1757
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control rbe-mermaid-bullet-control" title="Bulleted list">' + faIcon("list-ul", "Bulleted list") + '<select class="rbe-mermaid-option-bullet" aria-label="Bulleted list"><option value="none">None</option><option value="disc">Disc</option><option value="circle">Circle</option><option value="square">Square</option></select></label>' +
1758
- '<label class="rbe-mermaid-icon-control rbe-mermaid-text-control rbe-mermaid-number-control" title="Numbered list">' + faIcon("list-ol", "Numbered list") + '<select class="rbe-mermaid-option-number" aria-label="Numbered list"><option value="none">None</option><option value="decimal">1, 2, 3</option><option value="lower-alpha">a, b, c</option><option value="upper-alpha">A, B, C</option><option value="lower-roman">i, ii, iii</option><option value="upper-roman">I, II, III</option></select></label>' +
1759
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="outdent" title="Decrease indent">' + faIcon("outdent", "Decrease indent") + '</button>' +
1760
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control" data-text-action="indent" title="Increase indent">' + faIcon("indent", "Increase indent") + '</button>' +
1761
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control rbe-mermaid-spacing-before" data-text-action="spacing-before" title="Add space before paragraph">' + faIcon("arrow-up", "Add space before paragraph") + '</button>' +
1762
- '<button type="button" class="rbe-mermaid-style-btn rbe-mermaid-text-control rbe-mermaid-spacing-after" data-text-action="spacing-after" title="Add space after paragraph">' + faIcon("arrow-down", "Add space after paragraph") + '</button>' +
1763
- "</div>";
1764
- }
1765
-
1766
- function mermaidDesignerToolbar() {
1767
- return '<div class="rbe-mermaid-toolbar" role="toolbar" aria-label="Drawing tools">' +
1768
- '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="undo" title="Undo">' + faIcon("rotate-left", "Undo") + '</button>' +
1769
- '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="redo" title="Redo">' + faIcon("rotate-right", "Redo") + '</button>' +
1770
- '<span class="rbe-mermaid-toolbar-sep"></span>' +
1771
- '<button type="button" class="rbe-mermaid-tool" data-tool="select" title="Select">' + faIcon("arrow-pointer", "Select") + '</button>' +
1772
- mermaidShapeMenu() +
1773
- '<button type="button" class="rbe-mermaid-tool" data-tool="arrow" title="Arrow">' + faIcon("arrow-right-long", "Arrow") + '</button>' +
1774
- mermaidLineMenu() +
1775
- '<button type="button" class="rbe-mermaid-tool" data-tool="text" title="Text box">' + faIcon("font", "Text box") + '</button>' +
1776
- '<button type="button" class="rbe-mermaid-icon-btn" data-diagram-action="image" title="Insert image">' + faIcon("image", "Insert image") + '</button>' +
1777
- mermaidShapeStylebar() +
1778
- "</div>";
1779
- }
1780
-
1781
- function refreshMermaidCardPreview(card) {
1782
- var target = card.querySelector(".rbe-mermaid-card-preview");
1783
- if (!target) return;
1784
- var elements = [];
1785
- try {
1786
- elements = JSON.parse((card.querySelector(".rbe-mermaid-diagram-json") || {}).value || "[]").map(normalizeDiagramElement);
1787
- } catch (error) {}
1788
- if (!elements.length) {
1789
- target.innerHTML = '<span class="rbe-mermaid-card-empty">Open the designer to draw a diagram.</span>';
1790
- return;
1791
- }
1792
- target.innerHTML = '<div class="rbe-output-mermaid-diagram">' + diagramSvg(elements, "", readWorkspaceFromBlock(card), null) + "</div>";
1793
- }
1794
-
1795
- function mermaidRulerMarkup() {
1796
- var top = "";
1797
- var side = "";
1798
- for (var i = 1; i <= 10; i += 1) top += '<span style="left:' + (i * 100) + 'px">' + i + "</span>";
1799
- for (var j = 1; j <= 7; j += 1) side += '<span style="top:' + (j * 100) + 'px">' + j + "</span>";
1800
- return '<div class="rbe-mermaid-stage-shell"><div class="rbe-mermaid-ruler-corner"></div><div class="rbe-mermaid-top-ruler">' + top + '</div><div class="rbe-mermaid-side-ruler">' + side + '</div><div class="rbe-mermaid-stage"></div></div>';
1801
- }
1802
-
1803
- function openMermaidDesignerModal(editor, card) {
1804
- var existing = document.querySelector(".rbe-mermaid-modal");
1805
- if (existing) {
1806
- existing.remove();
1807
- document.body.classList.remove("rbe-mermaid-modal-open");
1808
- }
1809
- var modal = document.createElement("div");
1810
- modal.className = "rbe-mermaid-modal";
1811
- modal.innerHTML =
1812
- '<div class="rbe-mermaid-dialog" role="dialog" aria-modal="true" aria-label="Drawing">' +
1813
- '<div class="rbe-mermaid-modal-head"><h2 class="rbe-mermaid-modal-title">Drawing</h2><button type="button" class="rbe-mermaid-close" data-modal-action="close" title="Close">' + faIcon("xmark", "Close") + '</button></div>' +
1814
- '<div class="rbe-mermaid-modal-body"><div class="rbe-mermaid-block" tabindex="0">' +
1815
- '<input type="hidden" class="rbe-mermaid-alignment"><input type="hidden" class="rbe-mermaid-diagram-json"><input class="rbe-mermaid-image-input" type="file" accept="image/*" hidden><textarea class="rbe-mermaid-code" hidden></textarea>' +
1816
- mermaidDesignerToolbar() +
1817
- '<input class="rbe-mermaid-workspace-w" type="hidden"><input class="rbe-mermaid-workspace-h" type="hidden"><input class="rbe-mermaid-zoom" type="hidden">' + mermaidRulerMarkup() +
1818
- '</div></div>' +
1819
- '<div class="rbe-mermaid-modal-foot"><button type="button" class="rbe-mermaid-modal-cancel" data-modal-action="close">Cancel</button><button type="button" class="rbe-mermaid-modal-save" data-modal-action="save">Save drawing</button></div>' +
1820
- "</div>";
1821
- function closeModal() {
1822
- document.body.classList.remove("rbe-mermaid-modal-open");
1823
- modal.remove();
1824
- }
1825
- document.body.classList.add("rbe-mermaid-modal-open");
1826
- document.body.appendChild(modal);
1827
- var designer = modal.querySelector(".rbe-mermaid-block");
1828
- [".rbe-mermaid-alignment", ".rbe-mermaid-diagram-json", ".rbe-mermaid-code", ".rbe-mermaid-workspace-w", ".rbe-mermaid-workspace-h", ".rbe-mermaid-zoom"].forEach(function (selector) {
1829
- var source = card.querySelector(selector);
1830
- var target = designer.querySelector(selector);
1831
- if (source && target) target.value = source.value || "";
1832
- });
1833
- renderDiagramCanvas(designer);
1834
- bindMermaidDesigner(editor, designer);
1835
- designer.focus();
1836
- modal.addEventListener("click", function (event) {
1837
- var actionButton = event.target.closest("[data-modal-action]");
1838
- var action = actionButton ? actionButton.dataset.modalAction : "";
1839
- if (!action) return;
1840
- event.preventDefault();
1841
- if (action === "save") {
1842
- var textEditor = designer.querySelector(".rbe-mermaid-text-editor");
1843
- if (textEditor && textEditor.dataset.editId) {
1844
- var editId = textEditor.dataset.editId;
1845
- var editedText = textEditor.value || textEditor.textContent || "";
1846
- var current = [];
1847
- try { current = JSON.parse((designer.querySelector(".rbe-mermaid-diagram-json") || {}).value || "[]"); } catch (error) {}
1848
- designer.querySelector(".rbe-mermaid-diagram-json").value = JSON.stringify(current.map(function (elm) {
1849
- elm = normalizeDiagramElement(elm);
1850
- if (elm.id === editId) elm.text = editedText.trim();
1851
- return elm;
1852
- }));
1853
- textEditor.remove();
1854
- }
1855
- [".rbe-mermaid-alignment", ".rbe-mermaid-diagram-json", ".rbe-mermaid-code", ".rbe-mermaid-workspace-w", ".rbe-mermaid-workspace-h", ".rbe-mermaid-zoom"].forEach(function (selector) {
1856
- var source = designer.querySelector(selector);
1857
- var target = card.querySelector(selector);
1858
- if (source && target) target.value = source.value || "";
1859
- });
1860
- refreshMermaidCardPreview(card);
1861
- editor.changed(true);
1862
- }
1863
- closeModal();
1864
- });
1865
- }
1866
-
1867
- function hydrateAdvancedOutput(root) {
1868
- root = root || document;
1869
- Array.prototype.slice.call(root.querySelectorAll(".rbe-output-latex")).forEach(function (node) {
1870
- var code = node.dataset.latex || node.textContent || "";
1871
- var display = node.dataset.display !== "false";
1872
- if (node.dataset.renderedLatex === "true" && node.dataset.lastLatex === code && node.dataset.lastDisplay === String(display)) return;
1873
- node.dataset.renderedLatex = "true";
1874
- node.dataset.lastLatex = code;
1875
- node.dataset.lastDisplay = String(display);
1876
- renderLatexPreview(node, code, display);
1877
- });
1878
- Array.prototype.slice.call(root.querySelectorAll(".rbe-output-mermaid")).forEach(function (node) {
1879
- var code = node.dataset.mermaid || node.textContent || "";
1880
- if (node.dataset.renderedMermaid === "true" && node.dataset.lastMermaid === code) return;
1881
- node.dataset.renderedMermaid = "true";
1882
- node.dataset.lastMermaid = code;
1883
- renderMermaidPreview(node, code);
1884
- });
1885
- }
1886
-
1887
- function bootOutputHydration() {
1888
- var run = function () { hydrateAdvancedOutput(document); };
1889
- if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", run, { once: true });
1890
- else setTimeout(run, 0);
1891
- if (!document.body) return;
1892
- var timer = 0;
1893
- new MutationObserver(function () {
1894
- clearTimeout(timer);
1895
- timer = setTimeout(run, 60);
1896
- }).observe(document.body, { childList: true, subtree: true });
1897
- }
1898
-
1899
- function bindChange(editor, block, update) {
1900
- block.addEventListener("input", function () {
1901
- update();
1902
- editor.changed();
1903
- });
1904
- block.addEventListener("change", function () {
1905
- update();
1906
- editor.changed();
1907
- });
1908
- }
1909
-
1910
- function field(className, value) {
1911
- return "." + className + " => " + value;
1912
- }
1913
-
1914
- function chartCaptionAlignment(value) {
1915
- value = String(value || "").toLowerCase();
1916
- return value === "left" || value === "right" ? value : "center";
1917
- }
1918
-
1919
- function plainTextFromHTML(value) {
1920
- var template = document.createElement("template");
1921
- template.innerHTML = sanitize(value || "");
1922
- return template.content.textContent || "";
1923
- }
1924
-
1925
- BlockWriteAI.registerTool("chart", {
1926
- label: "Chart",
1927
- hint: "Bar, line, or pie chart",
1928
- icon: "chart-column",
1929
- defaultData: { type: "bar", title: "Chart", caption: "", labels: "Q1,Q2,Q3,Q4", values: "12,19,7,14", color: "#2563eb", colors: "#2563eb,#16a34a,#f59e0b,#dc2626" },
1930
- render: function (body, data) {
1931
- injectStyles();
1932
- var editor = this;
1933
- var wrap = el("div", "rbe-plugin-card rbe-chart-block");
1934
- wrap.innerHTML =
1935
- '<input class="rbe-chart-type" type="hidden">' +
1936
- '<div class="rbe-plugin-toolbar"><input class="rbe-chart-title" placeholder="Chart title"><label class="rbe-chart-series-color">Color <input class="rbe-chart-color" type="color"></label><div class="rbe-chart-slice-colors" hidden><span class="rbe-chart-slice-label">Slice colors</span><div class="rbe-chart-slice-list"></div><input class="rbe-chart-colors" type="hidden"></div></div>' +
1937
- '<div class="rbe-plugin-toolbar"><input class="rbe-chart-labels" placeholder="Labels: Q1,Q2,Q3"><input class="rbe-chart-values" placeholder="Values: 10,20,30"></div>' +
1938
- '<div class="rbe-chart-preview-wrap"><div class="rbe-chart-type-bar" role="toolbar" aria-label="Chart type"><button type="button" class="rbe-chart-type-btn" data-chart-type="bar" title="Bar chart">' + faIcon("chart-column", "Bar chart") + '</button><button type="button" class="rbe-chart-type-btn" data-chart-type="line" title="Line chart">' + faIcon("chart-line", "Line chart") + '</button><button type="button" class="rbe-chart-type-btn" data-chart-type="pie" title="Pie chart">' + faIcon("chart-pie", "Pie chart") + '</button></div><div class="rbe-plugin-preview rbe-chart-preview"></div></div>' +
1939
- '<div class="rbe-chart-caption rbe-editable" contenteditable="' + (!editor.readOnly) + '" data-placeholder="Chart caption"></div>';
1940
- body.appendChild(wrap);
1941
- wrap.querySelector(".rbe-chart-type").value = data.type === "line" || data.type === "pie" ? data.type : "bar";
1942
- wrap.querySelector(".rbe-chart-title").value = data.title || "";
1943
- wrap.querySelector(".rbe-chart-caption").innerHTML = sanitize(data.caption || "");
1944
- wrap.querySelector(".rbe-chart-caption").style.textAlign = chartCaptionAlignment(data.captionAlignment || "center");
1945
- wrap.querySelector(".rbe-chart-color").value = data.color || "#2563eb";
1946
- wrap.querySelector(".rbe-chart-colors").value = data.colors || "#2563eb,#16a34a,#f59e0b,#dc2626";
1947
- wrap.querySelector(".rbe-chart-labels").value = data.labels || "";
1948
- wrap.querySelector(".rbe-chart-values").value = data.values || "";
1949
- function syncSliceInputs() {
1950
- var labels = labelList(wrap.querySelector(".rbe-chart-labels").value);
1951
- var values = numberList(wrap.querySelector(".rbe-chart-values").value);
1952
- var count = Math.max(1, labels.length, values.length);
1953
- var hidden = wrap.querySelector(".rbe-chart-colors");
1954
- var colors = chartColorList(hidden.value, count);
1955
- var list = wrap.querySelector(".rbe-chart-slice-list");
1956
- list.innerHTML = colors.map(function (item, index) {
1957
- return '<label class="rbe-chart-slice-chip"><span>' + esc(labels[index] || "Q" + (index + 1)) + '</span><input type="color" value="' + esc(item) + '" data-slice-index="' + index + '"></label>';
1958
- }).join("");
1959
- hidden.value = colors.join(",");
1960
- }
1961
- function refreshTypeState() {
1962
- var type = wrap.querySelector(".rbe-chart-type").value || "bar";
1963
- Array.prototype.slice.call(wrap.querySelectorAll(".rbe-chart-type-btn")).forEach(function (button) {
1964
- button.classList.toggle("is-active", button.dataset.chartType === type);
1965
- });
1966
- wrap.querySelector(".rbe-chart-series-color").hidden = type === "pie";
1967
- wrap.querySelector(".rbe-chart-slice-colors").hidden = type !== "pie";
1968
- if (type === "pie") syncSliceInputs();
1969
- }
1970
- var update = function () {
1971
- refreshTypeState();
1972
- wrap.querySelector(".rbe-chart-preview").innerHTML = chartSvg({
1973
- type: wrap.querySelector(".rbe-chart-type").value,
1974
- title: wrap.querySelector(".rbe-chart-title").value,
1975
- color: wrap.querySelector(".rbe-chart-color").value,
1976
- colors: wrap.querySelector(".rbe-chart-colors").value,
1977
- labels: wrap.querySelector(".rbe-chart-labels").value,
1978
- values: wrap.querySelector(".rbe-chart-values").value
1979
- });
1980
- };
1981
- wrap.addEventListener("click", function (event) {
1982
- var button = event.target.closest(".rbe-chart-type-btn");
1983
- if (!button) return;
1984
- event.preventDefault();
1985
- wrap.querySelector(".rbe-chart-type").value = button.dataset.chartType || "bar";
1986
- update();
1987
- editor.changed(true);
1988
- });
1989
- wrap.addEventListener("input", function (event) {
1990
- var color = event.target.closest(".rbe-chart-slice-chip input");
1991
- if (!color) return;
1992
- var inputs = Array.prototype.slice.call(wrap.querySelectorAll(".rbe-chart-slice-chip input"));
1993
- wrap.querySelector(".rbe-chart-colors").value = inputs.map(function (input) { return input.value; }).join(",");
1994
- });
1995
- bindChange(editor, wrap, update);
1996
- update();
1997
- },
1998
- serialize: function (block) {
1999
- var caption = block.querySelector(".rbe-chart-caption");
2000
- return {
2001
- type: (block.querySelector(".rbe-chart-type") || {}).value || "bar",
2002
- title: (block.querySelector(".rbe-chart-title") || {}).value || "",
2003
- caption: caption ? sanitize(caption.innerHTML || "") : "",
2004
- captionAlignment: chartCaptionAlignment(caption && caption.style ? caption.style.textAlign : "center"),
2005
- color: (block.querySelector(".rbe-chart-color") || {}).value || "#2563eb",
2006
- colors: (block.querySelector(".rbe-chart-colors") || {}).value || "",
2007
- labels: (block.querySelector(".rbe-chart-labels") || {}).value || "",
2008
- values: (block.querySelector(".rbe-chart-values") || {}).value || ""
2009
- };
2010
- },
2011
- toHTML: function (data) {
2012
- var captionAlignment = chartCaptionAlignment(data.captionAlignment || "center");
2013
- return '<figure class="rbe-output-chart">' + (data.title ? '<figcaption class="rbe-output-chart-title">' + esc(data.title) + "</figcaption>" : "") + chartSvg(data) + (data.caption ? '<figcaption class="rbe-output-chart-caption" style="text-align:' + captionAlignment + '">' + inline(data.caption) + "</figcaption>" : "") + "</figure>";
2014
- },
2015
- toMarkdown: function (data) {
2016
- return "Chart: " + (data.title || "") + (data.caption ? "\n" + plainTextFromHTML(data.caption) : "") + "\nLabels: " + (data.labels || "") + "\nValues: " + (data.values || "");
2017
- }
2018
- });
2019
-
2020
-
2021
- BlockWriteAI.registerTool("latex", {
2022
- label: "LaTeX",
2023
- hint: "Math formula block",
2024
- icon: "square-root-variable",
2025
- defaultData: { code: "\\\\int_a^b f(x) \\, dx", display: true },
2026
- render: function (body, data) {
2027
- injectStyles();
2028
- var editor = this;
2029
- var wrap = el("div", "rbe-plugin-card rbe-latex-block");
2030
- wrap.innerHTML = '<textarea class="rbe-latex-code" spellcheck="false"></textarea><div class="rbe-plugin-preview rbe-latex-preview"></div>';
2031
- body.appendChild(wrap);
2032
- wrap.querySelector(".rbe-latex-code").value = data.code || "";
2033
- var update = function () { renderLatexPreview(wrap.querySelector(".rbe-latex-preview"), wrap.querySelector(".rbe-latex-code").value, true); };
2034
- bindChange(editor, wrap, update);
2035
- update();
2036
- },
2037
- serialize: function (block) {
2038
- return { code: (block.querySelector(".rbe-latex-code") || {}).value || "", display: true };
2039
- },
2040
- toHTML: function (data) {
2041
- var code = data.code || "";
2042
- return '<div class="rbe-output-latex" data-display="true" data-latex="' + esc(code) + '">' + latexFallbackHTML(code) + "</div>";
2043
- },
2044
- toMarkdown: function (data) {
2045
- return "$$" + (data.code || "") + "$$";
2046
- }
2047
- });
2048
-
2049
- BlockWriteAI.registerTool("audio", {
2050
- label: "Audio",
2051
- hint: "Upload or link audio",
2052
- icon: "volume-high",
2053
- defaultData: { url: "", title: "" },
2054
- render: function (body, data) {
2055
- injectStyles();
2056
- var editor = this;
2057
- var wrap = el("div", "rbe-plugin-card rbe-audio-block");
2058
- wrap.innerHTML =
2059
- '<input class="rbe-audio-title" type="hidden"><input class="rbe-audio-url" type="hidden">' +
2060
- '<label class="rbe-upload-dropzone rbe-audio-dropzone"><input class="rbe-audio-file rbe-upload-input" type="file" accept="audio/*" ' + (editor.readOnly ? "disabled" : "") + '><span class="rbe-upload-icon">' + faIcon("cloud-arrow-up", "Upload audio") + '</span><strong>Drag and drop or click here</strong><span class="rbe-upload-main">to upload audio</span><small>MP3, WAV, OGG, M4A, AAC, and audio files only</small></label>' +
2061
- '<div class="rbe-audio-preview"></div>';
2062
- body.appendChild(wrap);
2063
- wrap.querySelector(".rbe-audio-title").value = data.title || "";
2064
- wrap.querySelector(".rbe-audio-url").value = data.url || "";
2065
- var update = function () {
2066
- var url = wrap.querySelector(".rbe-audio-url").value;
2067
- var title = wrap.querySelector(".rbe-audio-title").value || "Audio";
2068
- wrap.querySelector(".rbe-audio-preview").innerHTML = url ? '<div class="rbe-audio-card"><strong>' + esc(title) + '</strong><audio controls src="' + esc(url) + '"></audio><small>Drop or click above to replace audio</small></div>' : "";
2069
- };
2070
- function useFile(file) {
2071
- var isAudio = !!file && (/^audio\//.test(file.type || "") || /\.(mp3|wav|ogg|m4a|aac|flac|webm)$/i.test(file.name || ""));
2072
- if (!isAudio) {
2073
- wrap.querySelector(".rbe-audio-preview").innerHTML = '<div class="rbe-audio-error">Please upload an audio file.</div>';
2074
- return;
2075
- }
2076
- editor.uploadFile(file, "file").then(function (result) {
2077
- wrap.querySelector(".rbe-audio-url").value = result.url || "";
2078
- wrap.querySelector(".rbe-audio-title").value = result.name || file.name || "Audio";
2079
- update();
2080
- editor.changed(true);
2081
- }).catch(function (error) {
2082
- wrap.querySelector(".rbe-audio-preview").innerHTML = '<div class="rbe-audio-error">' + esc(error && error.message ? error.message : "Audio upload failed") + "</div>";
2083
- });
2084
- }
2085
- wrap.querySelector(".rbe-audio-file").addEventListener("change", function (event) {
2086
- var file = event.target.files && event.target.files[0];
2087
- useFile(file);
2088
- });
2089
- wrap.querySelector(".rbe-audio-dropzone").addEventListener("dragover", function (event) {
2090
- event.preventDefault();
2091
- });
2092
- wrap.querySelector(".rbe-audio-dropzone").addEventListener("drop", function (event) {
2093
- event.preventDefault();
2094
- useFile(event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]);
2095
- });
2096
- bindChange(editor, wrap, update);
2097
- update();
2098
- },
2099
- serialize: function (block) {
2100
- return { title: (block.querySelector(".rbe-audio-title") || {}).value || "", url: (block.querySelector(".rbe-audio-url") || {}).value || "" };
2101
- },
2102
- toHTML: function (data) {
2103
- return '<figure class="rbe-output-audio">' + (data.url ? '<audio controls src="' + esc(data.url) + '"></audio>' : "") + "</figure>";
2104
- },
2105
- toMarkdown: function (data) {
2106
- return "[" + (data.title || "Audio") + "](" + (data.url || "#") + ")";
2107
- }
2108
- });
2109
-
2110
-
2111
- BlockWriteAI.registerTool("columns", {
2112
- label: "Columns",
2113
- hint: "Multi-column layout",
2114
- icon: "table-columns",
2115
- defaultData: { count: 2, headers: ["Column 1", "Column 2"], columns: ["", ""] },
2116
- render: function (body, data) {
2117
- injectStyles();
2118
- var editor = this;
2119
- var maxColumns = 8;
2120
- var count = Math.min(maxColumns, Math.max(2, Number(data.count || 2)));
2121
- var headers = Array.isArray(data.headers) ? data.headers : [];
2122
- var columns = Array.isArray(data.columns) ? data.columns : [];
2123
- var headerAlignments = Array.isArray(data.headerAlignments) ? data.headerAlignments : [];
2124
- var columnAlignments = Array.isArray(data.columnAlignments) ? data.columnAlignments : [];
2125
- var wrap = el("div", "rbe-plugin-card rbe-columns-block");
2126
- wrap.innerHTML = '<div class="rbe-plugin-toolbar"><label>Columns <select class="rbe-columns-count"></select></label></div><div class="rbe-columns-grid"></div>';
2127
- body.appendChild(wrap);
2128
- var select = wrap.querySelector(".rbe-columns-count");
2129
- var grid = wrap.querySelector(".rbe-columns-grid");
2130
- for (var optionCount = 2; optionCount <= maxColumns; optionCount += 1) {
2131
- select.appendChild(el("option", "", { value: String(optionCount), text: String(optionCount) }));
2132
- }
2133
- select.value = String(count);
2134
- function cleanAlign(value) {
2135
- value = String(value || "").toLowerCase();
2136
- return value === "center" || value === "right" ? value : "left";
2137
- }
2138
- function renderColumns() {
2139
- var nextCount = Number(select.value || 2);
2140
- var currentHeaders = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-header")).map(function (item) { return item.innerHTML; });
2141
- var current = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-editor")).map(function (item) { return item.innerHTML; });
2142
- var currentHeaderAlignments = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-header")).map(function (item) { return cleanAlign(item.style.textAlign); });
2143
- var currentColumnAlignments = Array.prototype.slice.call(grid.querySelectorAll(".rbe-column-editor")).map(function (item) { return cleanAlign(item.style.textAlign); });
2144
- grid.innerHTML = "";
2145
- grid.style.gridTemplateColumns = "repeat(" + nextCount + ", minmax(220px, 1fr))";
2146
- for (var i = 0; i < nextCount; i += 1) {
2147
- var column = el("div", "rbe-column-field");
2148
- var header = el("div", "rbe-column-header rbe-editable", {
2149
- contentEditable: !editor.readOnly,
2150
- html: sanitize(currentHeaders[i] || headers[i] || "Column " + (i + 1)),
2151
- dataset: { placeholder: "Column " + (i + 1) + " header" }
2152
- });
2153
- header.style.textAlign = cleanAlign(currentHeaderAlignments[i] || headerAlignments[i]);
2154
- column.appendChild(header);
2155
- var field = el("div", "rbe-column-editor rbe-editable", {
2156
- contentEditable: !editor.readOnly,
2157
- html: sanitize(current[i] || columns[i] || ""),
2158
- dataset: { placeholder: "Column " + (i + 1) }
2159
- });
2160
- field.style.textAlign = cleanAlign(currentColumnAlignments[i] || columnAlignments[i]);
2161
- column.appendChild(field);
2162
- grid.appendChild(column);
2163
- }
2164
- }
2165
- select.addEventListener("change", function () {
2166
- renderColumns();
2167
- editor.changed(true);
2168
- });
2169
- grid.addEventListener("input", function () { editor.changed(); });
2170
- renderColumns();
2171
- },
2172
- serialize: function (block) {
2173
- function cleanAlign(value) {
2174
- value = String(value || "").toLowerCase();
2175
- return value === "center" || value === "right" ? value : "left";
2176
- }
2177
- return {
2178
- count: Number((block.querySelector(".rbe-columns-count") || {}).value || 2),
2179
- headers: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-header")).map(function (item) { return sanitize(item.innerHTML || ""); }),
2180
- columns: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-editor")).map(function (item) { return sanitize(item.innerHTML || ""); }),
2181
- headerAlignments: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-header")).map(function (item) { return cleanAlign(item.style.textAlign); }),
2182
- columnAlignments: Array.prototype.slice.call(block.querySelectorAll(".rbe-column-editor")).map(function (item) { return cleanAlign(item.style.textAlign); })
2183
- };
2184
- },
2185
- toHTML: function (data) {
2186
- function cleanAlign(value) {
2187
- value = String(value || "").toLowerCase();
2188
- return value === "center" || value === "right" ? value : "left";
2189
- }
2190
- var count = Math.min(8, Math.max(2, Number(data.count || 2)));
2191
- var headers = Array.isArray(data.headers) ? data.headers : [];
2192
- var columns = Array.isArray(data.columns) ? data.columns.slice(0, count) : [];
2193
- var headerAlignments = Array.isArray(data.headerAlignments) ? data.headerAlignments : [];
2194
- var columnAlignments = Array.isArray(data.columnAlignments) ? data.columnAlignments : [];
2195
- while (columns.length < count) columns.push("");
2196
- return '<div class="rbe-output-columns" style="--rbe-columns:' + count + '">' + columns.map(function (column, index) {
2197
- var header = headers[index] || "Column " + (index + 1);
2198
- var headerAlign = cleanAlign(headerAlignments[index]);
2199
- var columnAlign = cleanAlign(columnAlignments[index]);
2200
- return '<div class="rbe-output-column"><div class="rbe-output-column-header" style="text-align:' + headerAlign + '">' + inline(header) + '</div><div class="rbe-output-column-body" style="text-align:' + columnAlign + '">' + inline(column || "") + "</div></div>";
2201
- }).join("") + "</div>";
2202
- },
2203
- toMarkdown: function (data, helpers) {
2204
- return (data.columns || []).map(function (column, index) {
2205
- var headers = Array.isArray(data.headers) ? data.headers : [];
2206
- return (headers[index] || "Column " + (index + 1)) + "\n" + (helpers.text ? helpers.text(column) : column);
2207
- }).join("\n\n");
2208
- }
2209
- });
2210
-
2211
- function applyImageTransform(stage, editor) {
2212
- if (!stage) return;
2213
- var image = stage.querySelector(".rbe-image-preview");
2214
- var rotation = Number(stage.dataset.rotation || 0);
2215
- stage.dataset.rotation = String(rotation);
2216
- if (editor && typeof editor.applyImageStageState === "function") editor.applyImageStageState(stage);
2217
- else if (image) image.style.transform = "rotate(" + rotation + "deg)";
2218
- }
2219
-
2220
- function enhanceImageStages(editor, body, data) {
2221
- var items = Array.isArray(data && data.items) ? data.items : data && data.url ? [data] : [];
2222
- Array.prototype.slice.call(body.querySelectorAll(".rbe-image-stage")).forEach(function (stage, index) {
2223
- var item = items[index] || {};
2224
- stage.dataset.rotation = item.rotation || stage.dataset.rotation || "0";
2225
- stage.dataset.crop = item.crop === "custom" ? "custom" : stage.dataset.crop || "free";
2226
- applyImageTransform(stage, editor);
2227
- var menu = stage.querySelector(".rbe-media-gear-menu");
2228
- if (!menu || stage.querySelector(":scope > .rbe-image-transform-panel")) return;
2229
- var panel = document.createElement("div");
2230
- panel.className = "rbe-image-transform-panel";
2231
- panel.innerHTML =
2232
- '<button type="button" class="rbe-media-action rbe-image-transform" data-transform="rotate-left" title="Rotate left">' + faIcon("rotate-left", "Rotate left") + '</button>' +
2233
- '<button type="button" class="rbe-media-action rbe-image-transform" data-transform="rotate-right" title="Rotate right">' + faIcon("rotate-right", "Rotate right") + '</button>';
2234
- stage.appendChild(panel);
2235
- });
2236
- }
2237
-
2238
- var originalRenderImage = BlockWriteAI.prototype.renderImage;
2239
- BlockWriteAI.prototype.renderImage = function (body, data) {
2240
- originalRenderImage.call(this, body, data || {});
2241
- enhanceImageStages(this, body, data || {});
2242
- };
2243
-
2244
- var originalSerializeBlock = BlockWriteAI.prototype.serializeBlock;
2245
- BlockWriteAI.prototype.serializeBlock = function (block) {
2246
- var result = originalSerializeBlock.call(this, block);
2247
- if (result && result.type === "image") {
2248
- var stages = Array.prototype.slice.call(block.querySelectorAll(".rbe-image-stage"));
2249
- result.data.items = (result.data.items || []).map(function (item, index) {
2250
- var stage = stages[index];
2251
- if (!stage) return item;
2252
- item.rotation = Number(stage.dataset.rotation || 0);
2253
- item.crop = stage.dataset.crop === "custom" ? "custom" : "free";
2254
- return item;
2255
- });
2256
- }
2257
- return result;
2258
- };
2259
-
2260
- document.addEventListener("click", function (event) {
2261
- var button = event.target.closest(".rbe-image-transform");
2262
- if (!button) return;
2263
- var stage = button.closest(".rbe-image-stage");
2264
- var block = button.closest(".rbe-block");
2265
- if (!stage || !block) return;
2266
- event.preventDefault();
2267
- var action = button.dataset.transform || "";
2268
- if (action === "rotate-left") stage.dataset.rotation = String((Number(stage.dataset.rotation || 0) - 90) % 360);
2269
- if (action === "rotate-right") stage.dataset.rotation = String((Number(stage.dataset.rotation || 0) + 90) % 360);
2270
- var root = block.closest(".rbe");
2271
- if (root && root.__blockwriteaiInstance) {
2272
- applyImageTransform(stage, root.__blockwriteaiInstance);
2273
- root.__blockwriteaiInstance.updateMediaActionAvailability(stage.closest(".rbe-image-gallery") || block);
2274
- root.__blockwriteaiInstance.changed(true);
2275
- } else {
2276
- applyImageTransform(stage);
2277
- }
2278
- });
2279
-
2280
- injectStyles();
2281
- bootOutputHydration();
2282
- })(typeof window !== "undefined" ? window : this);