@malaya_jeeva/rich-text-editor 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,146 +20,295 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- RichTextEditor: () => RichTextEditor
23
+ default: () => RichTextEditor
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
27
  // src/RichTextEditor.tsx
28
- var import_react = require("react");
29
- var import_jsx_runtime = require("react/jsx-runtime");
30
- var CSS = `
31
- * { box-sizing: border-box; }
32
- .rte-toolbar {
33
- display: flex; flex-wrap: wrap; align-items: center; gap: 1px;
34
- padding: 4px 8px; background: #f8f8f8; border-bottom: 1px solid #e0e0e0;
35
- }
36
- @media (prefers-color-scheme: dark) { .rte-toolbar { background: #1e1e1e; border-color: #333; } }
37
-
38
- .rte-btn {
39
- background: transparent; border: none; border-radius: 3px; cursor: pointer;
40
- height: 30px; min-width: 30px; padding: 0 6px; color: #444; font-size: 13px;
41
- font-weight: 500; font-family: var(--font-sans); display: inline-flex;
42
- align-items: center; justify-content: center; user-select: none;
43
- white-space: nowrap; transition: background 0.1s; flex-shrink: 0;
44
- }
45
- .rte-btn:hover { background: #e8e8e8; }
46
- .rte-btn.active { background: #d0e4ff; color: #1a5fb4; }
47
- .rte-btn.danger { color: #c0392b; }
48
- .rte-btn.danger:hover { background: #fdecea; }
49
- @media (prefers-color-scheme: dark) {
50
- .rte-btn { color: #ccc; } .rte-btn:hover { background: #2e2e2e; }
51
- .rte-btn.active { background: #1a3a5c; color: #90c4ff; }
52
- .rte-btn.danger { color: #ff6b6b; } .rte-btn.danger:hover { background: #3a1a1a; }
53
- }
54
- .rte-sep { width: 1px; height: 20px; background: #d8d8d8; margin: 0 4px; flex-shrink: 0; }
55
- @media (prefers-color-scheme: dark) { .rte-sep { background: #3a3a3a; } }
28
+ var import_react5 = require("react");
56
29
 
57
- .rte-select {
58
- height: 28px; border: 1px solid #d8d8d8; border-radius: 3px;
59
- background: #fff; color: #333; font-size: 12px; padding: 0 22px 0 7px;
60
- cursor: pointer; outline: none; font-family: var(--font-sans);
61
- appearance: none; -webkit-appearance: none;
62
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E");
63
- background-repeat: no-repeat; background-position: right 6px center;
64
- }
65
- @media (prefers-color-scheme: dark) { .rte-select { background-color: #2a2a2a; border-color: #444; color: #ccc; } }
66
- .rte-swatch { width: 14px; height: 3px; border-radius: 1px; margin-top: 2px; }
67
-
68
- /* Table picker */
69
- .rte-tp-wrap { position: relative; display: inline-flex; }
70
- .rte-tp {
71
- position: absolute; top: 34px; left: 0; z-index: 100;
72
- background: #fff; border: 1px solid #d8d8d8; border-radius: 6px;
73
- box-shadow: 0 4px 16px rgba(0,0,0,0.12); padding: 10px;
74
- }
75
- @media (prefers-color-scheme: dark) { .rte-tp { background: #222; border-color: #444; } }
76
- .rte-tp-lbl { font-size: 11px; color: #888; text-align: center; margin-bottom: 8px; font-family: var(--font-sans); }
77
- .rte-tp-grid { display: grid; grid-template-columns: repeat(8, 18px); gap: 2px; }
78
- .rte-tp-cell { width: 18px; height: 18px; border-radius: 2px; border: 1px solid #ddd; cursor: pointer; background: #fff; transition: background 0.08s; }
79
- .rte-tp-cell.on { background: #d0e4ff; border-color: #1a5fb4; }
80
- @media (prefers-color-scheme: dark) {
81
- .rte-tp-cell { background: #2a2a2a; border-color: #444; }
82
- .rte-tp-cell.on { background: #1a3a5c; border-color: #90c4ff; }
83
- }
30
+ // src/rte/constants.ts
31
+ var PALETTE = [
32
+ "#000000",
33
+ "#434343",
34
+ "#666666",
35
+ "#999999",
36
+ "#b7b7b7",
37
+ "#cccccc",
38
+ "#d9d9d9",
39
+ "#ffffff",
40
+ "#ff0000",
41
+ "#ff4500",
42
+ "#ff9900",
43
+ "#ffff00",
44
+ "#00ff00",
45
+ "#00ffff",
46
+ "#4a86e8",
47
+ "#0000ff",
48
+ "#9900ff",
49
+ "#ff00ff",
50
+ "#e06666",
51
+ "#f6b26b",
52
+ "#ffd966",
53
+ "#93c47d",
54
+ "#76a5af",
55
+ "#6fa8dc",
56
+ "#8e7cc3",
57
+ "#c27ba0",
58
+ "#a61c00",
59
+ "#783f04",
60
+ "#7f6000",
61
+ "#274e13",
62
+ "#0c343d",
63
+ "#073763",
64
+ "#20124d",
65
+ "#4c1130"
66
+ ];
67
+ var CSS = `
68
+ *{box-sizing:border-box;}
69
+ .rte-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:1px;padding:4px 8px;background:#f8f8f8;border-bottom:1px solid #e0e0e0;}
70
+ @media(prefers-color-scheme:dark){.rte-toolbar{background:#1e1e1e;border-color:#333;}}
71
+ .rte-btn{background:transparent;border:none;border-radius:3px;cursor:pointer;height:30px;min-width:30px;padding:0 6px;color:#444;font-size:13px;font-weight:500;font-family:var(--font-sans);display:inline-flex;align-items:center;justify-content:center;user-select:none;white-space:nowrap;transition:background 0.1s;flex-shrink:0;}
72
+ .rte-btn:hover{background:#e8e8e8;}.rte-btn.active{background:#d0e4ff;color:#1a5fb4;}
73
+ .rte-btn.danger{color:#c0392b;}.rte-btn.danger:hover{background:#fdecea;}
74
+ @media(prefers-color-scheme:dark){.rte-btn{color:#ccc;}.rte-btn:hover{background:#2e2e2e;}.rte-btn.active{background:#1a3a5c;color:#90c4ff;}.rte-btn.danger{color:#ff6b6b;}.rte-btn.danger:hover{background:#3a1a1a;}}
75
+ .rte-sep{width:1px;height:20px;background:#d8d8d8;margin:0 4px;flex-shrink:0;}
76
+ @media(prefers-color-scheme:dark){.rte-sep{background:#3a3a3a;}}
77
+ .rte-select{height:28px;border:1px solid #d8d8d8;border-radius:3px;background:#fff;color:#333;font-size:12px;padding:0 22px 0 7px;cursor:pointer;outline:none;font-family:var(--font-sans);appearance:none;-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 6px center;}
78
+ @media(prefers-color-scheme:dark){.rte-select{background-color:#2a2a2a;border-color:#444;color:#ccc;}}
79
+ .rte-swatch{width:14px;height:3px;border-radius:1px;margin-top:2px;}
80
+ .rte-cpick{background:#fff;border:1px solid #ccc;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,0.15);padding:8px;min-width:190px;}
81
+ @media(prefers-color-scheme:dark){.rte-cpick{background:#222;border-color:#444;}}
82
+ .rte-cgrid{display:grid;grid-template-columns:repeat(8,20px);gap:2px;margin-bottom:6px;}
83
+ .rte-ccell{width:20px;height:20px;border-radius:3px;cursor:pointer;border:1px solid rgba(0,0,0,0.12);transition:transform 0.08s,box-shadow 0.08s;}
84
+ .rte-ccell:hover{transform:scale(1.2);box-shadow:0 1px 4px rgba(0,0,0,0.3);z-index:1;position:relative;}
85
+ .rte-ccell.sel{outline:2px solid #1a5fb4;outline-offset:1px;}
86
+ .rte-cactions{display:flex;align-items:center;gap:2px;border-top:1px solid #eee;padding-top:6px;margin-top:2px;}
87
+ @media(prefers-color-scheme:dark){.rte-cactions{border-color:#333;}}
88
+ .rte-caction{flex:1;height:28px;display:flex;align-items:center;justify-content:center;border-radius:4px;cursor:pointer;border:1px solid transparent;transition:background 0.1s;}
89
+ .rte-caction:hover{background:#f0f0f0;border-color:#ddd;}
90
+ @media(prefers-color-scheme:dark){.rte-caction:hover{background:#2a2a2a;border-color:#444;}}
91
+ .rte-tpick{background:#fff;border:1px solid #d8d8d8;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,0.12);padding:10px;}
92
+ @media(prefers-color-scheme:dark){.rte-tpick{background:#222;border-color:#444;}}
93
+ .rte-tplbl{font-size:11px;color:#888;text-align:center;margin-bottom:8px;font-family:var(--font-sans);}
94
+ .rte-tpgrid{display:grid;grid-template-columns:repeat(8,18px);gap:2px;}
95
+ .rte-tpcell{width:18px;height:18px;border-radius:2px;border:1px solid #ddd;cursor:pointer;background:#fff;transition:background 0.08s;}
96
+ .rte-tpcell.on{background:#d0e4ff;border-color:#1a5fb4;}
97
+ @media(prefers-color-scheme:dark){.rte-tpcell{background:#2a2a2a;border-color:#444;}.rte-tpcell.on{background:#1a3a5c;border-color:#90c4ff;}}
98
+ .rte-ipick{background:#fff;border:1px solid #ccc;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,0.15);width:300px;overflow:hidden;}
99
+ @media(prefers-color-scheme:dark){.rte-ipick{background:#222;border-color:#444;}}
100
+ .rte-itabs{display:flex;border-bottom:1px solid #eee;}
101
+ @media(prefers-color-scheme:dark){.rte-itabs{border-color:#333;}}
102
+ .rte-itab{flex:1;padding:8px 4px;font-size:12px;font-weight:500;background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;color:#888;font-family:var(--font-sans);}
103
+ .rte-itab:hover{color:#444;background:#f8f8f8;}.rte-itab.active{color:#1a5fb4;border-bottom-color:#1a5fb4;}
104
+ @media(prefers-color-scheme:dark){.rte-itab:hover{background:#2a2a2a;}}
105
+ .rte-ipbody{padding:12px;display:flex;flex-direction:column;gap:10px;}
106
+ .rte-idrop{border:2px dashed #ccc;border-radius:6px;padding:24px 12px;text-align:center;cursor:pointer;transition:border-color 0.15s,background 0.15s;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-height:110px;}
107
+ .rte-idrop:hover,.rte-idrop.drag{border-color:#1a5fb4;background:#f0f4ff;}
108
+ @media(prefers-color-scheme:dark){.rte-idrop{border-color:#555;}.rte-idrop:hover,.rte-idrop.drag{background:#1a3a5c;border-color:#90c4ff;}}
109
+ .rte-iinsert{width:100%;height:30px;background:#1a6fc4;color:#fff;border:none;border-radius:4px;font-size:12px;font-weight:600;cursor:pointer;}
110
+ .rte-iinsert:disabled{opacity:0.4;cursor:not-allowed;}
111
+ .rte-float{position:absolute;z-index:50;background:#fff;border:1px solid #d0d0d0;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,0.13);padding:4px 6px;display:flex;align-items:center;gap:1px;flex-wrap:wrap;}
112
+ @media(prefers-color-scheme:dark){.rte-float{background:#222;border-color:#444;}}
113
+ .rte-float-arrow{position:absolute;top:-7px;width:14px;height:7px;}
114
+ .rte-float-arrow::before,.rte-float-arrow::after{content:'';position:absolute;left:0;border-left:7px solid transparent;border-right:7px solid transparent;}
115
+ .rte-float-arrow::before{top:0;border-bottom:7px solid #d0d0d0;}
116
+ .rte-float-arrow::after{top:1px;border-bottom:6px solid #fff;}
117
+ @media(prefers-color-scheme:dark){.rte-float-arrow::before{border-bottom-color:#444;}.rte-float-arrow::after{border-bottom-color:#222;}}
118
+ .rte-linkbar{min-width:340px;}
119
+ .rte-linkbar input{flex:1;height:26px;border:1px solid #ccc;border-radius:3px;padding:0 8px;font-size:13px;outline:none;font-family:var(--font-sans);background:#fff;color:#222;min-width:0;}
120
+ @media(prefers-color-scheme:dark){.rte-linkbar input{background:#1a1a1a;border-color:#555;color:#ddd;}}
121
+ .rte-area{position:relative;background:#fff;}
122
+ @media(prefers-color-scheme:dark){.rte-area{background:#141414;}}
123
+ .rte-body{min-height:280px;outline:none;font-size:15px;line-height:1.75;color:#222;caret-color:#222;padding:18px 20px;}
124
+ @media(prefers-color-scheme:dark){.rte-body{color:#ddd;caret-color:#ddd;}}
125
+ .rte-body h1{font-size:26px;font-weight:600;margin:0.6em 0 0.2em;}
126
+ .rte-body h2{font-size:20px;font-weight:600;margin:0.6em 0 0.2em;}
127
+ .rte-body h3{font-size:16px;font-weight:600;margin:0.6em 0 0.2em;}
128
+ .rte-body p{margin:0.2em 0;}
129
+ .rte-body ol{list-style-type:decimal;padding-left:1.6em;margin:0.3em 0;}
130
+ .rte-body ol ol{list-style-type:lower-alpha;padding-left:1.6em;}
131
+ .rte-body ol ol ol{list-style-type:lower-roman;padding-left:1.6em;}
132
+ .rte-body ol ol ol ol{list-style-type:decimal;padding-left:1.6em;}
133
+ .rte-body ul{list-style-type:disc;padding-left:1.6em;margin:0.3em 0;}
134
+ .rte-body ul ul{list-style-type:circle;padding-left:1.6em;}
135
+ .rte-body ul ul ul{list-style-type:square;padding-left:1.6em;}
136
+ .rte-body li{margin:0.1em 0;}
137
+ .rte-body a{color:#1a6fc4;text-decoration:underline;cursor:pointer;}
138
+ .rte-body pre{background:#f4f4f4;border:1px solid #e0e0e0;border-radius:4px;padding:12px 14px;margin:0.6em 0;overflow-x:auto;}
139
+ .rte-body code{font-family:var(--font-mono);font-size:13px;}
140
+ .rte-body:empty:before{content:attr(data-ph);color:#aaa;pointer-events:none;}
141
+ .rte-body table{border-collapse:collapse;width:100%;margin:0.8em 0;}
142
+ .rte-body td,.rte-body th{border:1px solid #ccc;padding:7px 10px;min-width:60px;text-align:left;vertical-align:top;}
143
+ .rte-body th{background:#f5f5f5;font-weight:600;}
144
+ @media(prefers-color-scheme:dark){.rte-body td,.rte-body th{border-color:#444;}.rte-body th{background:#2a2a2a;}}
145
+ .rte-body td.rte-sel,.rte-body th.rte-sel{background:rgba(26,95,180,0.15)!important;outline:2px solid #1a5fb4;outline-offset:-2px;}
146
+ .rte-body img{max-width:100%;height:auto;display:block;margin:4px 0;cursor:pointer;}
147
+ .rte-body figure{margin:0.8em 0;display:block;overflow:hidden;}
148
+ .rte-body figcaption{font-size:13px;color:#666;text-align:center;padding:4px 0;outline:none;clear:both;display:block;}
149
+ .rte-code{min-height:280px;width:100%;background:#1e1e2e;color:#cdd6f4;font-family:var(--font-mono);font-size:13px;line-height:1.6;padding:18px 20px;border:none;outline:none;resize:vertical;}
150
+ .rte-footer{font-size:11px;color:#999;padding:4px 20px 5px;border-top:1px solid #e8e8e8;background:#fafafa;}
151
+ @media(prefers-color-scheme:dark){.rte-footer{border-color:#2a2a2a;background:#1a1a1a;color:#555;}}
152
+ .rte-ie-panel{position:absolute;z-index:52;background:#fff;border:1px solid #d0d0d0;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,0.15);width:210px;overflow:hidden;}
153
+ @media(prefers-color-scheme:dark){.rte-ie-panel{background:#222;border-color:#444;}}
154
+ .rte-ie-tabs{display:flex;border-bottom:1px solid #eee;}
155
+ @media(prefers-color-scheme:dark){.rte-ie-tabs{border-color:#333;}}
156
+ .rte-ie-tab{flex:1;padding:7px 4px;font-size:11px;font-weight:500;background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;color:#888;font-family:var(--font-sans);}
157
+ .rte-ie-tab.active{color:#1a5fb4;border-bottom-color:#1a5fb4;}
158
+ .rte-ie-tab:hover{background:#f8f8f8;}
159
+ @media(prefers-color-scheme:dark){.rte-ie-tab:hover{background:#2a2a2a;}}
160
+ .rte-ie-body{padding:10px;display:flex;flex-direction:column;gap:8px;}
161
+ .rte-ie-label{font-size:10px;color:#888;margin-bottom:3px;font-family:var(--font-sans);}
162
+ .rte-ie-input{width:100%;height:26px;border:1px solid #ccc;border-radius:4px;padding:0 8px;font-size:11px;outline:none;font-family:var(--font-sans);background:#fff;color:#222;}
163
+ @media(prefers-color-scheme:dark){.rte-ie-input{background:#1a1a1a;border-color:#444;color:#ddd;}}
164
+ .rte-ie-row{display:flex;gap:4px;}
165
+ .rte-ie-seg-btn{flex:1;height:26px;font-size:11px;border:1px solid #ccc;border-radius:4px;background:transparent;color:#444;cursor:pointer;font-family:var(--font-sans);transition:all 0.1s;}
166
+ .rte-ie-seg-btn.active{background:#d0e4ff;border-color:#1a5fb4;color:#1a5fb4;}
167
+ .rte-ie-preset{flex:1;height:22px;font-size:10px;border:1px solid #ccc;border-radius:3px;background:transparent;cursor:pointer;font-family:var(--font-sans);}
168
+ .rte-ie-preset:hover{background:#e8e8e8;}
169
+ .rte-ie-delete{width:100%;height:28px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;font-family:var(--font-sans);}
170
+ .rte-ie-apply{flex:1;height:26px;background:#1a6fc4;color:#fff;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer;}
171
+ .rte-ie-remove{flex:1;height:26px;background:#fdecea;color:#c0392b;border:1px solid #f5c6c2;border-radius:4px;font-size:11px;cursor:pointer;}
172
+ .rte-handle{position:absolute;width:10px;height:10px;background:#fff;border:2px solid #1a5fb4;border-radius:2px;pointer-events:all;z-index:53;}
173
+ `;
84
174
 
85
- /* \u2500\u2500 Floating toolbar \u2500\u2500 */
86
- .rte-float {
87
- position: absolute; z-index: 50;
88
- background: #fff; border: 1px solid #d0d0d0;
89
- border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.13);
90
- padding: 4px 6px;
91
- display: flex; align-items: center; gap: 1px; flex-wrap: wrap;
92
- }
93
- @media (prefers-color-scheme: dark) { .rte-float { background: #222; border-color: #444; } }
94
- .rte-float-arrow {
95
- position: absolute; top: -7px; width: 14px; height: 7px;
96
- }
97
- .rte-float-arrow::before,
98
- .rte-float-arrow::after {
99
- content: ''; position: absolute; left: 0;
100
- border-left: 7px solid transparent; border-right: 7px solid transparent;
101
- }
102
- .rte-float-arrow::before { top: 0; border-bottom: 7px solid #d0d0d0; }
103
- .rte-float-arrow::after { top: 1px; border-bottom: 6px solid #fff; }
104
- @media (prefers-color-scheme: dark) {
105
- .rte-float-arrow::before { border-bottom-color: #444; }
106
- .rte-float-arrow::after { border-bottom-color: #222; }
175
+ // src/rte/utils.ts
176
+ function hsv2rgb(h, s, v) {
177
+ const f = (n) => {
178
+ const k = (n + h / 60) % 6;
179
+ return v - v * s * Math.max(0, Math.min(k, 4 - k, 1));
180
+ };
181
+ return [Math.round(f(5) * 255), Math.round(f(3) * 255), Math.round(f(1) * 255)];
182
+ }
183
+ function rgb2hex(r, g, b) {
184
+ return "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("");
185
+ }
186
+ function hex2hsv(hex) {
187
+ if (hex.length < 7) return [0, 1, 1];
188
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
189
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
190
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
191
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min;
192
+ let h = 0;
193
+ const s = max === 0 ? 0 : d / max, v = max;
194
+ if (d !== 0) {
195
+ if (max === r) h = (g - b) / d % 6;
196
+ else if (max === g) h = (b - r) / d + 2;
197
+ else h = (r - g) / d + 4;
198
+ h = Math.round(h * 60);
199
+ if (h < 0) h += 360;
107
200
  }
108
-
109
- /* Link bar inside float */
110
- .rte-link-float { min-width: 340px; }
111
- .rte-link-float input {
112
- flex: 1; height: 26px; border: 1px solid #ccc; border-radius: 3px;
113
- padding: 0 8px; font-size: 13px; outline: none; font-family: var(--font-sans);
114
- background: #fff; color: #222;
115
- min-width: 0;
201
+ return [h, s, v];
202
+ }
203
+ function getColorAtCursor(editor) {
204
+ var _a, _b;
205
+ if (!editor) return "#000000";
206
+ const sel = window.getSelection();
207
+ if (!(sel == null ? void 0 : sel.rangeCount)) return "#000000";
208
+ let el = ((_a = sel.anchorNode) == null ? void 0 : _a.nodeType) === 3 ? sel.anchorNode.parentElement : sel.anchorNode;
209
+ while (el && el !== editor) {
210
+ const c = (_b = el.style) == null ? void 0 : _b.color;
211
+ if (c) {
212
+ const m = c.match(/\d+/g);
213
+ if (m && m.length >= 3)
214
+ return "#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
215
+ if (c.startsWith("#")) return c;
216
+ }
217
+ el = el.parentElement;
116
218
  }
117
- @media (prefers-color-scheme: dark) { .rte-link-float input { background: #1a1a1a; border-color: #555; color: #ddd; } }
118
- .rte-link-apply {
119
- height: 26px; padding: 0 10px; background: #1a6fc4; color: #fff;
120
- border: none; border-radius: 3px; font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
219
+ return "#000000";
220
+ }
221
+ function prettifyHtml(html) {
222
+ const INLINE = /* @__PURE__ */ new Set(["a", "b", "i", "u", "em", "strong", "span", "code", "br", "small", "sub", "sup"]);
223
+ let indent = 0;
224
+ const pad = () => " ".repeat(indent);
225
+ const tokens = html.replace(/>\s+</g, "><").replace(/(<\/?[^>]+>)/g, "\0$1\0").split("\0").filter(Boolean);
226
+ const lines = [];
227
+ let inline = "";
228
+ const flush = () => {
229
+ if (inline.trim()) {
230
+ lines.push(pad() + inline.trim());
231
+ inline = "";
232
+ }
233
+ };
234
+ for (const tok of tokens) {
235
+ const ot = tok.match(/^<([a-zA-Z][a-zA-Z0-9]*)[^>]*>$/);
236
+ const ct = tok.match(/^<\/([a-zA-Z][a-zA-Z0-9]*)>$/);
237
+ if (tok.match(/^<[^>]+\/>$/)) {
238
+ flush();
239
+ lines.push(pad() + tok);
240
+ continue;
241
+ }
242
+ if (ot) {
243
+ const tag = ot[1].toLowerCase();
244
+ if (INLINE.has(tag)) {
245
+ inline += tok;
246
+ continue;
247
+ }
248
+ flush();
249
+ lines.push(pad() + tok);
250
+ indent++;
251
+ continue;
252
+ }
253
+ if (ct) {
254
+ const tag = ct[1].toLowerCase();
255
+ if (INLINE.has(tag)) {
256
+ inline += tok;
257
+ continue;
258
+ }
259
+ flush();
260
+ indent = Math.max(0, indent - 1);
261
+ const prev = lines[lines.length - 1];
262
+ prev && prev.trim().startsWith("<") && !prev.includes("</") ? lines[lines.length - 1] = prev + tok : lines.push(pad() + tok);
263
+ continue;
264
+ }
265
+ inline += tok;
121
266
  }
122
- .rte-link-cancel {
123
- height: 26px; padding: 0 8px; background: transparent; color: #666;
124
- border: 1px solid #ccc; border-radius: 3px; font-size: 12px; cursor: pointer;
267
+ flush();
268
+ return lines.join("\n");
269
+ }
270
+ function getCurrentCell() {
271
+ var _a;
272
+ const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
273
+ const el = (node == null ? void 0 : node.nodeType) === 1 ? node : node == null ? void 0 : node.parentElement;
274
+ return el == null ? void 0 : el.closest("td, th");
275
+ }
276
+ function getCurrentTable() {
277
+ var _a;
278
+ return (_a = getCurrentCell()) == null ? void 0 : _a.closest("table");
279
+ }
280
+ function getCurrentLink() {
281
+ var _a;
282
+ const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
283
+ const el = (node == null ? void 0 : node.nodeType) === 1 ? node : node == null ? void 0 : node.parentElement;
284
+ return el == null ? void 0 : el.closest("a");
285
+ }
286
+ function getCellCoords(table, cell) {
287
+ for (let r = 0; r < table.rows.length; r++) {
288
+ const cells = Array.from(table.rows[r].cells);
289
+ for (let c = 0; c < cells.length; c++) {
290
+ if (cells[c] === cell) return [r, c];
291
+ }
125
292
  }
126
- @media (prefers-color-scheme: dark) { .rte-link-cancel { color: #aaa; border-color: #555; } }
293
+ return null;
294
+ }
295
+ function getCellsInRange(table, a, b) {
296
+ const ca = getCellCoords(table, a), cb = getCellCoords(table, b);
297
+ if (!ca || !cb) return [a];
298
+ const [r1, c1] = [Math.min(ca[0], cb[0]), Math.min(ca[1], cb[1])];
299
+ const [r2, c2] = [Math.max(ca[0], cb[0]), Math.max(ca[1], cb[1])];
300
+ const result = [];
301
+ Array.from(table.rows).forEach((row, ri) => {
302
+ if (ri >= r1 && ri <= r2)
303
+ Array.from(row.cells).forEach((cell, ci) => {
304
+ if (ci >= c1 && ci <= c2) result.push(cell);
305
+ });
306
+ });
307
+ return result;
308
+ }
127
309
 
128
- /* Editor */
129
- .rte-area { position: relative; background: #fff; }
130
- @media (prefers-color-scheme: dark) { .rte-area { background: #141414; } }
131
- .rte-body {
132
- min-height: 280px; outline: none; font-size: 15px; line-height: 1.75;
133
- color: #222; caret-color: #222; padding: 18px 20px;
134
- }
135
- @media (prefers-color-scheme: dark) { .rte-body { color: #ddd; caret-color: #ddd; } }
136
- .rte-body h1 { font-size: 26px; font-weight: 600; margin: 0.6em 0 0.2em; }
137
- .rte-body h2 { font-size: 20px; font-weight: 600; margin: 0.6em 0 0.2em; }
138
- .rte-body h3 { font-size: 16px; font-weight: 600; margin: 0.6em 0 0.2em; }
139
- .rte-body p { margin: 0.2em 0; }
140
- .rte-body ol { list-style-type: decimal; padding-left: 1.6em; margin: 0.3em 0; }
141
- .rte-body ol ol { list-style-type: lower-alpha; padding-left: 1.6em; }
142
- .rte-body ol ol ol { list-style-type: lower-roman; padding-left: 1.6em; }
143
- .rte-body ul { list-style-type: disc; padding-left: 1.6em; margin: 0.3em 0; }
144
- .rte-body ul ul { list-style-type: circle; padding-left: 1.6em; }
145
- .rte-body ul ul ul { list-style-type: square; padding-left: 1.6em; }
146
- .rte-body li { margin: 0.1em 0; }
147
- .rte-body a { color: #1a6fc4; text-decoration: underline; cursor: pointer; }
148
- .rte-body pre { background: #f4f4f4; border: 1px solid #e0e0e0; border-radius: 4px; padding: 12px 14px; margin: 0.6em 0; overflow-x: auto; }
149
- .rte-body code { font-family: var(--font-mono); font-size: 13px; }
150
- .rte-body:empty:before { content: attr(data-ph); color: #aaa; pointer-events: none; }
151
- .rte-body table { border-collapse: collapse; width: 100%; margin: 0.8em 0; }
152
- .rte-body td, .rte-body th { border: 1px solid #ccc; padding: 7px 10px; min-width: 60px; text-align: left; vertical-align: top; }
153
- .rte-body th { background: #f5f5f5; font-weight: 600; }
154
- @media (prefers-color-scheme: dark) {
155
- .rte-body td, .rte-body th { border-color: #444; }
156
- .rte-body th { background: #2a2a2a; }
157
- }
158
- .rte-body td.rte-sel, .rte-body th.rte-sel { background: rgba(26,95,180,0.15) !important; outline: 2px solid #1a5fb4; outline-offset: -2px; }
159
- .rte-code { min-height: 280px; width: 100%; background: #1e1e2e; color: #cdd6f4; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; padding: 18px 20px; border: none; outline: none; resize: vertical; }
160
- .rte-footer { font-size: 11px; color: #999; padding: 4px 20px 5px; border-top: 1px solid #e8e8e8; background: #fafafa; }
161
- @media (prefers-color-scheme: dark) { .rte-footer { border-color: #2a2a2a; background: #1a1a1a; color: #555; } }
162
- `;
310
+ // src/rte/Btn.tsx
311
+ var import_jsx_runtime = require("react/jsx-runtime");
163
312
  function Btn({ onClick, title, active, danger, children, style }) {
164
313
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
165
314
  "button",
@@ -178,264 +327,858 @@ function Btn({ onClick, title, active, danger, children, style }) {
178
327
  function Sep() {
179
328
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rte-sep" });
180
329
  }
181
- function TablePicker({ onInsert, onClose }) {
182
- const [hover, setHover] = (0, import_react.useState)([0, 0]);
183
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-tp", children: [
184
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rte-tp-lbl", children: hover[0] > 0 ? `${hover[0]} \xD7 ${hover[1]}` : "Select table size" }),
185
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rte-tp-grid", children: Array.from({ length: 64 }, (_, i) => {
186
- const r = Math.floor(i / 8) + 1, c = i % 8 + 1;
187
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
188
- "div",
189
- {
190
- className: `rte-tp-cell${r <= hover[0] && c <= hover[1] ? " on" : ""}`,
191
- onMouseEnter: () => setHover([r, c]),
192
- onMouseDown: (e) => {
193
- e.preventDefault();
194
- if (hover[0] > 0) {
195
- onInsert(hover[0], hover[1]);
196
- onClose();
197
- }
198
- }
199
- },
200
- i
201
- );
202
- }) })
203
- ] });
204
- }
330
+
331
+ // src/rte/Icons.tsx
332
+ var import_jsx_runtime2 = require("react/jsx-runtime");
205
333
  function IcoUL() {
206
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
207
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2", cy: "4", r: "1.3", fill: "currentColor" }),
208
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2", cy: "8", r: "1.3", fill: "currentColor" }),
209
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2", cy: "12", r: "1.3", fill: "currentColor" }),
210
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
211
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
212
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
334
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
335
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2", cy: "4", r: "1.3", fill: "currentColor" }),
336
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2", cy: "8", r: "1.3", fill: "currentColor" }),
337
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2", cy: "12", r: "1.3", fill: "currentColor" }),
338
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
340
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
213
341
  ] });
214
342
  }
215
343
  function IcoOL() {
216
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "0", y: "5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "1." }),
218
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "0", y: "9.5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "2." }),
219
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "0", y: "14", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "3." }),
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
222
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
344
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: "0", y: "5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "1." }),
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: "0", y: "9.5", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "2." }),
347
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: "0", y: "14", fontSize: "5.5", fill: "currentColor", fontFamily: "monospace", children: "3." }),
348
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "3.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "7.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "5.5", y: "11.2", width: "8.5", height: "1.6", rx: ".8", fill: "currentColor" })
223
351
  ] });
224
352
  }
225
353
  function IcoIndent() {
226
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
227
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
228
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
229
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
230
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1 6.5L3.5 8.25L1 10V6.5Z", fill: "currentColor" })
354
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
355
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
357
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
358
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1 6.5L3.5 8.25L1 10V6.5Z", fill: "currentColor" })
231
359
  ] });
232
360
  }
233
361
  function IcoOutdent() {
234
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
235
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
236
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.5 6.5L1 8.25L3.5 10V6.5Z", fill: "currentColor" })
362
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "2", width: "13", height: "1.6", rx: ".8", fill: "currentColor" }),
364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4.5", y: "6", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4.5", y: "10", width: "9.5", height: "1.6", rx: ".8", fill: "currentColor" }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 6.5L1 8.25L3.5 10V6.5Z", fill: "currentColor" })
239
367
  ] });
240
368
  }
241
369
  function IcoLink() {
242
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
243
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9C6.4 9.6 7.1 10 7.8 10H9.8C10.6 10 11.4 9.6 11.9 9C12.4 8.4 12.6 7.6 12.6 6.8C12.6 6 12.4 5.3 11.9 4.7C11.4 4.1 10.6 3.8 9.8 3.8H8.6", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }),
244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 6C8.6 5.4 7.9 5 7.2 5H5.2C4.4 5 3.6 5.4 3.1 6C2.6 6.6 2.4 7.4 2.4 8.2C2.4 9 2.6 9.7 3.1 10.3C3.6 10.9 4.4 11.2 5.2 11.2H6.4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
370
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
371
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 9C6.4 9.6 7.1 10 7.8 10H9.8C10.6 10 11.4 9.6 11.9 9C12.4 8.4 12.6 7.6 12.6 6.8C12.6 6 12.4 5.3 11.9 4.7C11.4 4.1 10.6 3.8 9.8 3.8H8.6", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }),
372
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 6C8.6 5.4 7.9 5 7.2 5H5.2C4.4 5 3.6 5.4 3.1 6C2.6 6.6 2.4 7.4 2.4 8.2C2.4 9 2.6 9.7 3.1 10.3C3.6 10.9 4.4 11.2 5.2 11.2H6.4", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
245
373
  ] });
246
374
  }
247
375
  function IcoCode() {
248
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
249
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 4.5L1.5 7.5L5 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
250
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 4.5L13.5 7.5L10 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8.5 2L6.5 13", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
376
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 4.5L1.5 7.5L5 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
378
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 4.5L13.5 7.5L10 10.5", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.5 2L6.5 13", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
252
380
  ] });
253
381
  }
254
382
  function IcoCopy() {
255
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
256
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "3", width: "8.5", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
257
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 1H12.5C13.1 1 13.5 1.4 13.5 2V10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
383
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: [
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "3", width: "8.5", height: "10", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 1H12.5C13.1 1 13.5 1.4 13.5 2V10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
258
386
  ] });
259
387
  }
260
388
  function IcoTable() {
261
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
262
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "13", height: "13", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "5.5", x2: "14", y2: "5.5", stroke: "currentColor", strokeWidth: "1.1" }),
264
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "10", x2: "14", y2: "10", stroke: "currentColor", strokeWidth: "1.1" }),
265
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "5.5", y1: "5.5", x2: "5.5", y2: "14", stroke: "currentColor", strokeWidth: "1.1" }),
266
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "10", y1: "5.5", x2: "10", y2: "14", stroke: "currentColor", strokeWidth: "1.1" })
389
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "13", height: "13", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "5.5", x2: "14", y2: "5.5", stroke: "currentColor", strokeWidth: "1.1" }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "10", x2: "14", y2: "10", stroke: "currentColor", strokeWidth: "1.1" }),
393
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5.5", y1: "5.5", x2: "5.5", y2: "14", stroke: "currentColor", strokeWidth: "1.1" }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "5.5", x2: "10", y2: "14", stroke: "currentColor", strokeWidth: "1.1" })
395
+ ] });
396
+ }
397
+ function IcoImage() {
398
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "2", width: "13", height: "11", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "5.5", r: "1.2", fill: "currentColor" }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1 10L4.5 7L7 9.5L9.5 7L14 11", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
267
402
  ] });
268
403
  }
269
404
  function IcoRowAbove() {
270
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
271
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "7", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
272
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "7", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
273
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 5V1M6 3L8 1L10 3", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
405
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
406
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "7", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "7", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 5V1M6 3L8 1L10 3", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
274
409
  ] });
275
410
  }
276
411
  function IcoRowBelow() {
277
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
278
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
279
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "9", stroke: "currentColor", strokeWidth: "1" }),
280
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 11V15M6 13L8 15L10 13", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
412
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "9", stroke: "currentColor", strokeWidth: "1" }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 11V15M6 13L8 15L10 13", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
281
416
  ] });
282
417
  }
283
418
  function IcoDelRow() {
284
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
285
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "4", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
286
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
287
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 6L10.5 10M10.5 6L5.5 10", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
419
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
420
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "4", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
422
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.5 6L10.5 10M10.5 6L5.5 10", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
288
423
  ] });
289
424
  }
290
425
  function IcoColLeft() {
291
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
292
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "7", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "7", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
294
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 8H1M3 6L1 8L3 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
426
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
427
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "7", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
428
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "7", y1: "8", x2: "15", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
429
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 8H1M3 6L1 8L3 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
295
430
  ] });
296
431
  }
297
432
  function IcoColRight() {
298
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
300
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "8", x2: "9", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
301
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8H15M13 6L15 8L13 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
433
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "8", x2: "9", y2: "8", stroke: "currentColor", strokeWidth: "1" }),
436
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M11 8H15M13 6L15 8L13 10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
302
437
  ] });
303
438
  }
304
439
  function IcoDelCol() {
305
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
306
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
307
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
308
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 5.5L10 10.5M10 5.5L6 10.5", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
440
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
441
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "1", width: "8", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
442
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1" }),
443
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 5.5L10 10.5M10 5.5L6 10.5", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" })
309
444
  ] });
310
445
  }
311
446
  function IcoMerge() {
312
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
313
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
314
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
315
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M7 8H9M7.5 6.5L9 8L7.5 9.5M8.5 6.5L7 8L8.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
447
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
448
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
449
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "9", y: "1", width: "6", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
450
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M7 8H9M7.5 6.5L9 8L7.5 9.5M8.5 6.5L7 8L8.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
316
451
  ] });
317
452
  }
318
453
  function IcoSplit() {
319
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
320
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1.2" }),
322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 8H3M12.5 8H10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
323
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4.5 6.5L3 8L4.5 9.5M11.5 6.5L13 8L11.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
454
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
455
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
456
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "1", x2: "8", y2: "15", stroke: "currentColor", strokeWidth: "1.2" }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.5 8H3M12.5 8H10", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
458
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4.5 6.5L3 8L4.5 9.5M11.5 6.5L13 8L11.5 9.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
324
459
  ] });
325
460
  }
326
461
  function IcoDelTable() {
327
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
328
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1.5", stroke: "currentColor", strokeWidth: "1.1" }),
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5L11 11M11 5L5 11", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
462
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
463
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "1", width: "14", height: "14", rx: "1.5", stroke: "currentColor", strokeWidth: "1.1" }),
464
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 5L11 11M11 5L5 11", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" })
330
465
  ] });
331
466
  }
332
467
  function AlignIco({ t }) {
333
468
  const w = { left: [13, 9, 11], center: [9, 7, 11], right: [13, 9, 11], justify: [13, 13, 13] };
334
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: t === "right" ? 15 - w[t][i] - 1 : 1, y: [2, 6.5, 11][i], width: w[t][i], height: "1.6", rx: ".8", fill: "currentColor" }, i)) });
469
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
470
+ "rect",
471
+ {
472
+ x: t === "right" ? 15 - w[t][i] - 1 : 1,
473
+ y: [2, 6.5, 11][i],
474
+ width: w[t][i],
475
+ height: "1.6",
476
+ rx: ".8",
477
+ fill: "currentColor"
478
+ },
479
+ i
480
+ )) });
335
481
  }
336
- function getCurrentCell() {
337
- var _a;
338
- const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
339
- const el = (node == null ? void 0 : node.nodeType) === 1 ? node : node == null ? void 0 : node.parentElement;
340
- return el == null ? void 0 : el.closest("td, th");
482
+
483
+ // src/rte/ColorPicker.tsx
484
+ var import_react = require("react");
485
+ var import_jsx_runtime3 = require("react/jsx-runtime");
486
+ function CustomColorPicker({ initialColor, onApply, onBack }) {
487
+ const [hsv, setHsv] = (0, import_react.useState)(() => hex2hsv(initialColor || "#ff0000"));
488
+ const [hexInput, setHexInput] = (0, import_react.useState)(initialColor || "#ff0000");
489
+ const svRef = (0, import_react.useRef)(null);
490
+ const hueRef = (0, import_react.useRef)(null);
491
+ const svDrag = (0, import_react.useRef)(false);
492
+ const hueDrag = (0, import_react.useRef)(false);
493
+ const [h, s, v] = hsv;
494
+ const currentHex = rgb2hex(...hsv2rgb(h, s, v));
495
+ (0, import_react.useEffect)(() => {
496
+ const c = svRef.current;
497
+ if (!c) return;
498
+ const ctx = c.getContext("2d");
499
+ const W = c.width, H = c.height;
500
+ const [r, g, b] = hsv2rgb(h, 1, 1);
501
+ ctx.fillStyle = `rgb(${r},${g},${b})`;
502
+ ctx.fillRect(0, 0, W, H);
503
+ const wg = ctx.createLinearGradient(0, 0, W, 0);
504
+ wg.addColorStop(0, "rgba(255,255,255,1)");
505
+ wg.addColorStop(1, "rgba(255,255,255,0)");
506
+ ctx.fillStyle = wg;
507
+ ctx.fillRect(0, 0, W, H);
508
+ const bg = ctx.createLinearGradient(0, 0, 0, H);
509
+ bg.addColorStop(0, "rgba(0,0,0,0)");
510
+ bg.addColorStop(1, "rgba(0,0,0,1)");
511
+ ctx.fillStyle = bg;
512
+ ctx.fillRect(0, 0, W, H);
513
+ const cx = s * W, cy = (1 - v) * H;
514
+ ctx.beginPath();
515
+ ctx.arc(cx, cy, 7, 0, Math.PI * 2);
516
+ ctx.strokeStyle = "rgba(0,0,0,0.25)";
517
+ ctx.lineWidth = 2;
518
+ ctx.stroke();
519
+ ctx.beginPath();
520
+ ctx.arc(cx, cy, 6, 0, Math.PI * 2);
521
+ ctx.strokeStyle = "#fff";
522
+ ctx.lineWidth = 2;
523
+ ctx.stroke();
524
+ }, [h, s, v]);
525
+ (0, import_react.useEffect)(() => {
526
+ const c = hueRef.current;
527
+ if (!c) return;
528
+ const ctx = c.getContext("2d");
529
+ const W = c.width, H = c.height;
530
+ const grad = ctx.createLinearGradient(0, 0, W, 0);
531
+ for (let i = 0; i <= 360; i += 30) {
532
+ const [r, g, b] = hsv2rgb(i, 1, 1);
533
+ grad.addColorStop(i / 360, `rgb(${r},${g},${b})`);
534
+ }
535
+ ctx.fillStyle = grad;
536
+ ctx.fillRect(0, 0, W, H);
537
+ const tx = h / 360 * W;
538
+ ctx.beginPath();
539
+ ctx.roundRect(tx - 4, -1, 8, H + 2, 3);
540
+ ctx.fillStyle = "#fff";
541
+ ctx.fill();
542
+ ctx.strokeStyle = "rgba(0,0,0,0.3)";
543
+ ctx.lineWidth = 1;
544
+ ctx.stroke();
545
+ }, [h]);
546
+ const updateSV = (0, import_react.useCallback)((e) => {
547
+ const c = svRef.current;
548
+ if (!c) return;
549
+ const rect = c.getBoundingClientRect();
550
+ const ns = Math.max(0, Math.min(1, (e.clientX - rect.left) / c.width));
551
+ const nv = Math.max(0, Math.min(1, 1 - (e.clientY - rect.top) / c.height));
552
+ const next = [h, ns, nv];
553
+ setHsv(next);
554
+ setHexInput(rgb2hex(...hsv2rgb(...next)));
555
+ }, [h]);
556
+ const updateHue = (0, import_react.useCallback)((e) => {
557
+ const c = hueRef.current;
558
+ if (!c) return;
559
+ const rect = c.getBoundingClientRect();
560
+ const nh = Math.max(0, Math.min(360, (e.clientX - rect.left) / c.width * 360));
561
+ const next = [nh, s, v];
562
+ setHsv(next);
563
+ setHexInput(rgb2hex(...hsv2rgb(...next)));
564
+ }, [s, v]);
565
+ (0, import_react.useEffect)(() => {
566
+ const mm = (e) => {
567
+ if (svDrag.current) updateSV(e);
568
+ if (hueDrag.current) updateHue(e);
569
+ };
570
+ const mu = () => {
571
+ svDrag.current = false;
572
+ hueDrag.current = false;
573
+ };
574
+ window.addEventListener("mousemove", mm);
575
+ window.addEventListener("mouseup", mu);
576
+ return () => {
577
+ window.removeEventListener("mousemove", mm);
578
+ window.removeEventListener("mouseup", mu);
579
+ };
580
+ }, [updateSV, updateHue]);
581
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
582
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
583
+ "button",
584
+ {
585
+ onMouseDown: onBack,
586
+ style: { background: "none", border: "none", cursor: "pointer", color: "#666", fontSize: 12, display: "flex", alignItems: "center", gap: 4, padding: "2px 4px", borderRadius: 3, marginBottom: 8 },
587
+ children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M8 2L4 6L8 10", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" }) }),
589
+ "Back"
590
+ ]
591
+ }
592
+ ),
593
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
594
+ "canvas",
595
+ {
596
+ ref: svRef,
597
+ width: 180,
598
+ height: 150,
599
+ style: { display: "block", borderRadius: 4, cursor: "crosshair", marginBottom: 8 },
600
+ onMouseDown: (e) => {
601
+ svDrag.current = true;
602
+ updateSV(e);
603
+ }
604
+ }
605
+ ),
606
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
607
+ "canvas",
608
+ {
609
+ ref: hueRef,
610
+ width: 180,
611
+ height: 14,
612
+ style: { display: "block", borderRadius: 4, cursor: "ew-resize", marginBottom: 10 },
613
+ onMouseDown: (e) => {
614
+ hueDrag.current = true;
615
+ updateHue(e);
616
+ }
617
+ }
618
+ ),
619
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }, children: [
620
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { width: 28, height: 28, borderRadius: 4, background: currentHex, border: "1px solid rgba(0,0,0,0.15)", flexShrink: 0 } }),
621
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
622
+ "input",
623
+ {
624
+ value: hexInput,
625
+ onChange: (e) => {
626
+ setHexInput(e.target.value);
627
+ if (/^#[0-9a-fA-F]{6}$/.test(e.target.value)) setHsv(hex2hsv(e.target.value));
628
+ },
629
+ style: { flex: 1, height: 28, border: "1px solid #ccc", borderRadius: 4, padding: "0 8px", fontSize: 12, fontFamily: "var(--font-mono)", outline: "none" },
630
+ spellCheck: false,
631
+ placeholder: "#000000"
632
+ }
633
+ )
634
+ ] }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
636
+ "button",
637
+ {
638
+ onMouseDown: () => onApply(currentHex),
639
+ style: { width: "100%", height: 28, background: "#1a6fc4", color: "#fff", border: "none", borderRadius: 4, fontSize: 12, fontWeight: 600, cursor: "pointer" },
640
+ children: "Apply"
641
+ }
642
+ )
643
+ ] });
341
644
  }
342
- function getCurrentTable() {
343
- var _a;
344
- return (_a = getCurrentCell()) == null ? void 0 : _a.closest("table");
645
+ function ColorPicker({ color, onColor, onRemove, onClose }) {
646
+ const [custom, setCustom] = (0, import_react.useState)(false);
647
+ const pick = (c) => {
648
+ onColor(c);
649
+ onClose();
650
+ };
651
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rte-cpick", children: custom ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CustomColorPicker, { initialColor: color, onApply: pick, onBack: () => setCustom(false) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rte-cgrid", children: PALETTE.map((hex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
653
+ "div",
654
+ {
655
+ className: `rte-ccell${color.toLowerCase() === hex.toLowerCase() ? " sel" : ""}`,
656
+ style: { background: hex },
657
+ title: hex,
658
+ onMouseDown: () => pick(hex)
659
+ },
660
+ hex
661
+ )) }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "rte-cactions", children: [
663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rte-caction", title: "Black", onMouseDown: () => pick("#000000"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", fill: "#000" }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M5 8L7 10L11 6", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
666
+ ] }) }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rte-caction", title: "Remove color", onMouseDown: () => {
668
+ onRemove();
669
+ onClose();
670
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "2", y: "7.5", width: "12", height: "1.2", rx: ".6", fill: "#e74c3c", transform: "rotate(-30 8 8)" }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "1", y: "11", width: "14", height: "2", rx: "1", fill: "#e74c3c" })
673
+ ] }) }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rte-caction", title: "Custom color", onMouseDown: () => setCustom(true), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: "#888", strokeWidth: "1.2", fill: "none" }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "5.5", cy: "6", r: "1.5", fill: "#e74c3c" }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "10.5", cy: "6", r: "1.5", fill: "#3498db" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "10.5", r: "1.5", fill: "#2ecc71" })
679
+ ] }) })
680
+ ] })
681
+ ] }) });
345
682
  }
346
- function getCellCoords(table, cell) {
347
- const rows = Array.from(table.rows);
348
- for (let r = 0; r < rows.length; r++) {
349
- const cells = Array.from(rows[r].cells);
350
- for (let c = 0; c < cells.length; c++) {
351
- if (cells[c] === cell) return [r, c];
352
- }
353
- }
354
- return null;
683
+
684
+ // src/rte/TablePicker.tsx
685
+ var import_react2 = require("react");
686
+ var import_jsx_runtime4 = require("react/jsx-runtime");
687
+ function TablePicker({ onInsert, onClose }) {
688
+ const [hover, setHover] = (0, import_react2.useState)([0, 0]);
689
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "rte-tpick", children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rte-tplbl", children: hover[0] > 0 ? `${hover[0]} \xD7 ${hover[1]}` : "Select table size" }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rte-tpgrid", children: Array.from({ length: 64 }, (_, i) => {
692
+ const r = Math.floor(i / 8) + 1, c = i % 8 + 1;
693
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
694
+ "div",
695
+ {
696
+ className: `rte-tpcell${r <= hover[0] && c <= hover[1] ? " on" : ""}`,
697
+ onMouseEnter: () => setHover([r, c]),
698
+ onMouseDown: (e) => {
699
+ e.preventDefault();
700
+ if (hover[0] > 0) {
701
+ onInsert(hover[0], hover[1]);
702
+ onClose();
703
+ }
704
+ }
705
+ },
706
+ i
707
+ );
708
+ }) })
709
+ ] });
355
710
  }
356
- function getCellsInRange(table, a, b) {
357
- const ca = getCellCoords(table, a), cb = getCellCoords(table, b);
358
- if (!ca || !cb) return [a];
359
- const [r1, c1] = [Math.min(ca[0], cb[0]), Math.min(ca[1], cb[1])];
360
- const [r2, c2] = [Math.max(ca[0], cb[0]), Math.max(ca[1], cb[1])];
361
- const result = [];
362
- Array.from(table.rows).forEach((row, ri) => {
363
- if (ri >= r1 && ri <= r2) Array.from(row.cells).forEach((cell, ci) => {
364
- if (ci >= c1 && ci <= c2) result.push(cell);
365
- });
711
+
712
+ // src/rte/ImagePicker.tsx
713
+ var import_react3 = require("react");
714
+ var import_jsx_runtime5 = require("react/jsx-runtime");
715
+ function ImagePicker({ onInsert, onClose }) {
716
+ const [tab, setTab] = (0, import_react3.useState)("upload");
717
+ const [url, setUrl] = (0, import_react3.useState)("");
718
+ const [preview, setPreview] = (0, import_react3.useState)(null);
719
+ const [dragging, setDragging] = (0, import_react3.useState)(false);
720
+ const [urlError, setUrlError] = (0, import_react3.useState)(false);
721
+ const fileRef = (0, import_react3.useRef)(null);
722
+ const readFile = (file) => {
723
+ if (!file.type.startsWith("image/")) return;
724
+ const reader = new FileReader();
725
+ reader.onload = () => setPreview(reader.result);
726
+ reader.readAsDataURL(file);
727
+ };
728
+ const canInsert = tab === "upload" ? !!preview : !!url.trim() && !urlError;
729
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rte-ipick", children: [
730
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "rte-itabs", children: ["upload", "url"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: `rte-itab${tab === t ? " active" : ""}`, onMouseDown: () => setTab(t), children: t === "upload" ? "Upload / Drop" : "Image URL" }, t)) }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "rte-ipbody", children: [
732
+ tab === "upload" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
734
+ "div",
735
+ {
736
+ className: `rte-idrop${dragging ? " drag" : ""}`,
737
+ onDragOver: (e) => {
738
+ e.preventDefault();
739
+ setDragging(true);
740
+ },
741
+ onDragLeave: () => setDragging(false),
742
+ onDrop: (e) => {
743
+ e.preventDefault();
744
+ setDragging(false);
745
+ const f = e.dataTransfer.files[0];
746
+ if (f) readFile(f);
747
+ },
748
+ onMouseDown: () => {
749
+ var _a;
750
+ return (_a = fileRef.current) == null ? void 0 : _a.click();
751
+ },
752
+ children: preview ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: preview, alt: "preview", style: { maxWidth: "100%", maxHeight: 130, objectFit: "contain", borderRadius: 4, border: "1px solid #eee" } }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
753
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "1", y: "4", width: "26", height: "20", rx: "3", stroke: "#aaa", strokeWidth: "1.5" }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "9", cy: "10", r: "2.5", stroke: "#aaa", strokeWidth: "1.5" }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M1 20L9 13L14 18L19 13.5L27 21", stroke: "#aaa", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
757
+ ] }),
758
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: 12, color: "#888" }, children: [
759
+ "Drop or ",
760
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: "#1a5fb4", textDecoration: "underline" }, children: "browse" })
761
+ ] }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 11, color: "#bbb" }, children: "PNG, JPG, GIF, WEBP" })
763
+ ] })
764
+ }
765
+ ),
766
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
767
+ "input",
768
+ {
769
+ ref: fileRef,
770
+ type: "file",
771
+ accept: "image/*",
772
+ style: { display: "none" },
773
+ onChange: (e) => {
774
+ var _a;
775
+ const f = (_a = e.target.files) == null ? void 0 : _a[0];
776
+ if (f) readFile(f);
777
+ e.target.value = "";
778
+ }
779
+ }
780
+ ),
781
+ preview && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
782
+ "button",
783
+ {
784
+ onMouseDown: () => setPreview(null),
785
+ style: { fontSize: 11, color: "#e74c3c", background: "none", border: "none", cursor: "pointer", padding: 0, alignSelf: "flex-start" },
786
+ children: "\u2715 Remove"
787
+ }
788
+ )
789
+ ] }),
790
+ tab === "url" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
791
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
792
+ "input",
793
+ {
794
+ type: "text",
795
+ value: url,
796
+ onChange: (e) => {
797
+ setUrl(e.target.value);
798
+ setUrlError(false);
799
+ },
800
+ placeholder: "https://example.com/image.png",
801
+ style: { width: "100%", height: 30, border: `1px solid ${urlError ? "#e74c3c" : "#ccc"}`, borderRadius: 4, padding: "0 8px", fontSize: 12, outline: "none", fontFamily: "var(--font-sans)" }
802
+ }
803
+ ),
804
+ url && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
805
+ "img",
806
+ {
807
+ src: url,
808
+ alt: "preview",
809
+ style: { maxWidth: "100%", maxHeight: 130, objectFit: "contain", borderRadius: 4, border: "1px solid #eee", display: "block" },
810
+ onError: () => setUrlError(true)
811
+ }
812
+ ),
813
+ urlError && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: 11, color: "#e74c3c" }, children: "Could not load image." })
814
+ ] }),
815
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
816
+ "button",
817
+ {
818
+ className: "rte-iinsert",
819
+ disabled: !canInsert,
820
+ onMouseDown: () => {
821
+ const src = tab === "url" ? url.trim() : preview;
822
+ if (src) {
823
+ onInsert(src);
824
+ onClose();
825
+ }
826
+ },
827
+ children: "Insert Image"
828
+ }
829
+ )
830
+ ] })
831
+ ] });
832
+ }
833
+
834
+ // src/rte/ImageEditor.tsx
835
+ var import_react4 = require("react");
836
+ var import_jsx_runtime6 = require("react/jsx-runtime");
837
+ var HANDLE_POS = {
838
+ nw: [0, 0],
839
+ n: [0, 50],
840
+ ne: [0, 100],
841
+ e: [50, 100],
842
+ se: [100, 100],
843
+ s: [100, 50],
844
+ sw: [100, 0],
845
+ w: [50, 0]
846
+ };
847
+ var HANDLE_CURSOR = {
848
+ nw: "nw-resize",
849
+ n: "n-resize",
850
+ ne: "ne-resize",
851
+ e: "e-resize",
852
+ se: "se-resize",
853
+ s: "s-resize",
854
+ sw: "sw-resize",
855
+ w: "w-resize"
856
+ };
857
+ function readAlign(img) {
858
+ if (img.style.float === "right") return "right";
859
+ if (img.style.float === "left") return "left";
860
+ const ml = img.style.marginLeft, mr = img.style.marginRight;
861
+ if (ml === "auto" && (mr === "0px" || mr === "0")) return "right";
862
+ if (ml === "auto" && mr === "auto") return "center";
863
+ return "left";
864
+ }
865
+ function ImageEditor({ img, containerRef, onClose, onDelete, onChange }) {
866
+ var _a;
867
+ const [overlay, setOverlay] = (0, import_react4.useState)({ top: 0, left: 0, width: 0, height: 0 });
868
+ const [activeTab, setActiveTab] = (0, import_react4.useState)("style");
869
+ const [dispMode, setDispMode] = (0, import_react4.useState)(img.style.display === "inline" ? "inline" : "block");
870
+ const [align, setAlign] = (0, import_react4.useState)(() => readAlign(img));
871
+ const [widthVal, setWidthVal] = (0, import_react4.useState)(img.style.width || `${img.offsetWidth}px`);
872
+ const [altVal, setAltVal] = (0, import_react4.useState)(img.alt || "");
873
+ const [captionTxt, setCaptionTxt] = (0, import_react4.useState)(() => {
874
+ var _a2, _b;
875
+ return ((_b = (_a2 = img.closest("figure")) == null ? void 0 : _a2.querySelector("figcaption")) == null ? void 0 : _b.textContent) || "";
366
876
  });
367
- return result;
877
+ const [showCaption, setShowCaption] = (0, import_react4.useState)(() => !!img.closest("figure"));
878
+ const [linkVal, setLinkVal] = (0, import_react4.useState)(() => {
879
+ var _a2;
880
+ return ((_a2 = img.closest("a")) == null ? void 0 : _a2.href) || "";
881
+ });
882
+ const aspectRef = (0, import_react4.useRef)(img.naturalWidth && img.naturalHeight ? img.naturalWidth / img.naturalHeight : img.offsetWidth / Math.max(img.offsetHeight, 1));
883
+ const updateOverlay = (0, import_react4.useCallback)(() => {
884
+ if (!containerRef.current) return;
885
+ const r = img.getBoundingClientRect(), cr = containerRef.current.getBoundingClientRect();
886
+ setOverlay({ top: r.top - cr.top, left: r.left - cr.left, width: r.width, height: r.height });
887
+ }, [img, containerRef]);
888
+ (0, import_react4.useEffect)(() => {
889
+ updateOverlay();
890
+ window.addEventListener("resize", updateOverlay);
891
+ return () => window.removeEventListener("resize", updateOverlay);
892
+ }, [updateOverlay]);
893
+ const startResize = (0, import_react4.useCallback)((e, handle) => {
894
+ e.preventDefault();
895
+ e.stopPropagation();
896
+ const sx = e.clientX, sy = e.clientY, sw = img.offsetWidth, sh = img.offsetHeight;
897
+ const isCorner = handle.length === 2;
898
+ const onMove = (ev) => {
899
+ const dx = ev.clientX - sx, dy = ev.clientY - sy;
900
+ let nw = sw;
901
+ if (handle.includes("e")) nw = Math.max(50, sw + dx);
902
+ if (handle.includes("w")) nw = Math.max(50, sw - dx);
903
+ if (isCorner) {
904
+ img.style.width = `${Math.round(nw)}px`;
905
+ img.style.height = `${Math.round(nw / aspectRef.current)}px`;
906
+ } else {
907
+ if (handle.includes("e") || handle.includes("w")) {
908
+ img.style.width = `${Math.round(nw)}px`;
909
+ img.style.height = "auto";
910
+ } else {
911
+ const nh = handle === "s" ? Math.max(30, sh + dy) : Math.max(30, sh - dy);
912
+ img.style.height = `${Math.round(nh)}px`;
913
+ }
914
+ }
915
+ setWidthVal(`${Math.round(img.offsetWidth)}px`);
916
+ updateOverlay();
917
+ };
918
+ const onUp = () => {
919
+ window.removeEventListener("mousemove", onMove);
920
+ window.removeEventListener("mouseup", onUp);
921
+ onChange();
922
+ };
923
+ window.addEventListener("mousemove", onMove);
924
+ window.addEventListener("mouseup", onUp);
925
+ }, [img, onChange, updateOverlay]);
926
+ const applyWidth = (w) => {
927
+ img.style.width = w;
928
+ img.style.height = "auto";
929
+ setWidthVal(w);
930
+ updateOverlay();
931
+ onChange();
932
+ };
933
+ const applyAlign = (a) => {
934
+ setAlign(a);
935
+ if (dispMode === "block") {
936
+ img.style.float = "none";
937
+ img.style.marginLeft = a === "center" || a === "right" ? "auto" : "0";
938
+ img.style.marginRight = a === "center" || a === "left" ? "auto" : "0";
939
+ } else {
940
+ img.style.float = a === "left" ? "left" : a === "right" ? "right" : "none";
941
+ img.style.marginLeft = "0";
942
+ img.style.marginRight = "0";
943
+ }
944
+ onChange();
945
+ };
946
+ const applyDisplay = (m) => {
947
+ setDispMode(m);
948
+ img.style.display = m;
949
+ img.style.float = "none";
950
+ img.style.marginLeft = "0";
951
+ img.style.marginRight = "auto";
952
+ setAlign("left");
953
+ onChange();
954
+ };
955
+ const applyAlt = (v) => {
956
+ setAltVal(v);
957
+ img.alt = v;
958
+ onChange();
959
+ };
960
+ const toggleCaption = () => {
961
+ var _a2, _b;
962
+ const fig = img.closest("figure");
963
+ if (fig) {
964
+ (_a2 = fig.parentNode) == null ? void 0 : _a2.insertBefore(img, fig);
965
+ fig.remove();
966
+ setShowCaption(false);
967
+ setCaptionTxt("");
968
+ } else {
969
+ const figure = document.createElement("figure");
970
+ figure.style.cssText = "margin:0.8em 0;display:block;overflow:hidden;";
971
+ const figcap = document.createElement("figcaption");
972
+ figcap.contentEditable = "true";
973
+ figcap.style.cssText = "font-size:13px;color:#666;text-align:center;padding:4px 0;outline:none;clear:both;display:block;";
974
+ figcap.textContent = captionTxt || "Caption";
975
+ (_b = img.parentNode) == null ? void 0 : _b.insertBefore(figure, img);
976
+ figure.appendChild(img);
977
+ figure.appendChild(figcap);
978
+ setShowCaption(true);
979
+ }
980
+ updateOverlay();
981
+ onChange();
982
+ };
983
+ const updateCaption = (v) => {
984
+ var _a2;
985
+ setCaptionTxt(v);
986
+ const fc = (_a2 = img.closest("figure")) == null ? void 0 : _a2.querySelector("figcaption");
987
+ if (fc) {
988
+ fc.textContent = v;
989
+ onChange();
990
+ }
991
+ };
992
+ const applyLink = (href) => {
993
+ var _a2, _b;
994
+ const existing = img.closest("a");
995
+ if (!href.trim()) {
996
+ if (existing) {
997
+ (_a2 = existing.parentNode) == null ? void 0 : _a2.insertBefore(img, existing);
998
+ existing.remove();
999
+ }
1000
+ } else if (existing) {
1001
+ existing.href = href;
1002
+ } else {
1003
+ const a = document.createElement("a");
1004
+ a.href = href;
1005
+ a.target = "_blank";
1006
+ a.rel = "noopener";
1007
+ (_b = img.parentNode) == null ? void 0 : _b.insertBefore(a, img);
1008
+ a.appendChild(img);
1009
+ }
1010
+ onChange();
1011
+ };
1012
+ const PANEL_W = 218;
1013
+ const panelRight = overlay.left + overlay.width + 8;
1014
+ const containerW = ((_a = containerRef.current) == null ? void 0 : _a.clientWidth) || 600;
1015
+ const panelLeft = panelRight + PANEL_W < containerW ? panelRight : Math.max(4, overlay.left + overlay.width - PANEL_W);
1016
+ const panelTop = panelRight + PANEL_W < containerW ? overlay.top : overlay.top + overlay.height + 10;
1017
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1018
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { position: "absolute", top: overlay.top, left: overlay.left, width: overlay.width, height: overlay.height, pointerEvents: "none", zIndex: 51 }, children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { position: "absolute", inset: 0, border: "2px solid #1a5fb4", borderRadius: 2, boxSizing: "border-box" } }),
1020
+ Object.keys(HANDLE_POS).map((h) => {
1021
+ const [tp, lp] = HANDLE_POS[h];
1022
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1023
+ "div",
1024
+ {
1025
+ className: "rte-handle",
1026
+ style: { top: `calc(${tp}% - 5px)`, left: `calc(${lp}% - 5px)`, cursor: HANDLE_CURSOR[h] },
1027
+ onMouseDown: (e) => startResize(e, h)
1028
+ },
1029
+ h
1030
+ );
1031
+ })
1032
+ ] }),
1033
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rte-ie-panel", style: { top: panelTop, left: panelLeft }, children: [
1034
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1035
+ "button",
1036
+ {
1037
+ onMouseDown: onClose,
1038
+ style: { position: "absolute", top: 4, right: 6, background: "none", border: "none", cursor: "pointer", color: "#999", fontSize: 13, lineHeight: 1 },
1039
+ children: "\u2715"
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-tabs", children: ["style", "caption", "link"].map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: `rte-ie-tab${activeTab === t ? " active" : ""}`, onMouseDown: () => setActiveTab(t), children: t === "style" ? "Style" : t === "caption" ? "Caption" : "Link" }, t)) }),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rte-ie-body", children: [
1044
+ activeTab === "style" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-label", children: "Display" }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-row", children: ["block", "inline"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: `rte-ie-seg-btn${dispMode === m ? " active" : ""}`, onMouseDown: () => applyDisplay(m), children: m === "block" ? "\u2B1B Block" : "\u25AC Inline" }, m)) })
1048
+ ] }),
1049
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1050
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-label", children: "Alignment" }),
1051
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-row", children: ["left", "center", "right"].map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: `rte-ie-seg-btn${align === a ? " active" : ""}`, onMouseDown: () => applyAlign(a), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AlignIco, { t: a }) }, a)) })
1052
+ ] }),
1053
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1054
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-label", children: "Width" }),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rte-ie-row", style: { marginBottom: 4 }, children: [
1056
+ [25, 50, 75, 100].map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("button", { className: "rte-ie-preset", onMouseDown: () => applyWidth(`${p}%`), children: [
1057
+ p,
1058
+ "%"
1059
+ ] }, p)),
1060
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "rte-ie-preset", title: "Natural size", onMouseDown: () => applyWidth(`${img.naturalWidth || img.offsetWidth}px`), children: "Orig" })
1061
+ ] }),
1062
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1063
+ "input",
1064
+ {
1065
+ className: "rte-ie-input",
1066
+ value: widthVal,
1067
+ onChange: (e) => setWidthVal(e.target.value),
1068
+ onBlur: (e) => applyWidth(e.target.value),
1069
+ onKeyDown: (e) => e.key === "Enter" && applyWidth(widthVal),
1070
+ placeholder: "e.g. 300px or 50%"
1071
+ }
1072
+ )
1073
+ ] }),
1074
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1075
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-label", children: "Alt text" }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { className: "rte-ie-input", value: altVal, onChange: (e) => applyAlt(e.target.value), placeholder: "Describe the image" })
1077
+ ] }),
1078
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "rte-ie-delete", onMouseDown: onDelete, children: "Delete image" })
1079
+ ] }),
1080
+ activeTab === "caption" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1081
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: { fontSize: 11, color: "#777", margin: 0, lineHeight: 1.5, fontFamily: "var(--font-sans)" }, children: [
1082
+ "Wraps image in ",
1083
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "<figure>" }),
1084
+ " with ",
1085
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "<figcaption>" }),
1086
+ "."
1087
+ ] }),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 12, cursor: "pointer", fontFamily: "var(--font-sans)" }, children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: showCaption, onChange: toggleCaption }),
1090
+ " Show caption"
1091
+ ] }),
1092
+ showCaption && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { className: "rte-ie-input", value: captionTxt, onChange: (e) => updateCaption(e.target.value), placeholder: "Caption text" })
1093
+ ] }),
1094
+ activeTab === "link" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1095
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "rte-ie-label", children: "Wrap image in a hyperlink" }),
1096
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { className: "rte-ie-input", value: linkVal, onChange: (e) => setLinkVal(e.target.value), placeholder: "https://example.com" }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "rte-ie-row", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "rte-ie-apply", onMouseDown: () => applyLink(linkVal), children: "Apply" }),
1099
+ img.closest("a") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "rte-ie-remove", onMouseDown: () => {
1100
+ setLinkVal("");
1101
+ applyLink("");
1102
+ }, children: "Remove" })
1103
+ ] })
1104
+ ] })
1105
+ ] })
1106
+ ] })
1107
+ ] });
368
1108
  }
1109
+
1110
+ // src/RichTextEditor.tsx
1111
+ var import_jsx_runtime7 = require("react/jsx-runtime");
369
1112
  function RichTextEditor({ value, onChange }) {
370
1113
  var _a;
371
- const editorRef = (0, import_react.useRef)(null);
372
- const editorAreaRef = (0, import_react.useRef)(null);
373
- const colorRef = (0, import_react.useRef)(null);
374
- const savedRangeRef = (0, import_react.useRef)(null);
375
- const dragStartCell = (0, import_react.useRef)(null);
376
- const isDragging = (0, import_react.useRef)(false);
377
- const [isCode, setIsCode] = (0, import_react.useState)(false);
378
- const [codeVal, setCodeVal] = (0, import_react.useState)("");
379
- const [fmt, setFmt] = (0, import_react.useState)({ block: "p" });
380
- const [color, setColor] = (0, import_react.useState)("#e74c3c");
381
- const [words, setWords] = (0, import_react.useState)(0);
382
- const [linkBar, setLinkBar] = (0, import_react.useState)(false);
383
- const [linkUrl, setLinkUrl] = (0, import_react.useState)("https://");
384
- const [showTable, setShowTable] = (0, import_react.useState)(false);
385
- const [selCells, setSelCells] = (0, import_react.useState)([]);
386
- const [tableFP, setTableFP] = (0, import_react.useState)(null);
387
- const [linkFP, setLinkFP] = (0, import_react.useState)(null);
388
- const calcFloat = (0, import_react.useCallback)((el, toolbarW) => {
1114
+ const editorRef = (0, import_react5.useRef)(null);
1115
+ const editorAreaRef = (0, import_react5.useRef)(null);
1116
+ const savedRangeRef = (0, import_react5.useRef)(null);
1117
+ const dragStartCell = (0, import_react5.useRef)(null);
1118
+ const isDragging = (0, import_react5.useRef)(false);
1119
+ const [isCode, setIsCode] = (0, import_react5.useState)(false);
1120
+ const [codeVal, setCodeVal] = (0, import_react5.useState)("");
1121
+ const [fmt, setFmt] = (0, import_react5.useState)({ block: "p" });
1122
+ const [color, setColor] = (0, import_react5.useState)("#000000");
1123
+ const [words, setWords] = (0, import_react5.useState)(0);
1124
+ const [linkBar, setLinkBar] = (0, import_react5.useState)(false);
1125
+ const [linkUrl, setLinkUrl] = (0, import_react5.useState)("https://");
1126
+ const [showTable, setShowTable] = (0, import_react5.useState)(false);
1127
+ const [showColor, setShowColor] = (0, import_react5.useState)(false);
1128
+ const [showImagePicker, setShowImagePicker] = (0, import_react5.useState)(false);
1129
+ const [selCells, setSelCells] = (0, import_react5.useState)([]);
1130
+ const [tableFP, setTableFP] = (0, import_react5.useState)(null);
1131
+ const [linkFP, setLinkFP] = (0, import_react5.useState)(null);
1132
+ const [linkInfoFP, setLinkInfoFP] = (0, import_react5.useState)(null);
1133
+ const [selectedImg, setSelectedImg] = (0, import_react5.useState)(null);
1134
+ const calcFloat = (0, import_react5.useCallback)((el, w) => {
389
1135
  const area = editorAreaRef.current;
390
- const er = el.getBoundingClientRect();
391
- const ar = area.getBoundingClientRect();
392
- const arrowCenter = er.left + er.width / 2 - ar.left;
393
- const rawLeft = arrowCenter - toolbarW / 2;
394
- const left = Math.max(4, Math.min(rawLeft, ar.width - toolbarW - 4));
395
- const arrowLeft = Math.max(8, Math.min(arrowCenter - left - 7, toolbarW - 22));
396
- return { top: er.bottom - ar.top + 10, left, arrowLeft };
1136
+ const er = el.getBoundingClientRect(), ar = area.getBoundingClientRect();
1137
+ const ac = er.left + er.width / 2 - ar.left;
1138
+ const rawL = ac - w / 2;
1139
+ const left = Math.max(4, Math.min(rawL, ar.width - w - 4));
1140
+ return { top: er.bottom - ar.top + 10, left, arrowLeft: Math.max(8, Math.min(ac - left - 7, w - 22)) };
397
1141
  }, []);
398
- const applySelection = (0, import_react.useCallback)((cells) => {
1142
+ const applySelection = (0, import_react5.useCallback)((cells) => {
399
1143
  var _a2;
400
1144
  (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll(".rte-sel").forEach((c) => c.classList.remove("rte-sel"));
401
1145
  cells.forEach((c) => c.classList.add("rte-sel"));
402
1146
  setSelCells(cells);
403
1147
  }, []);
404
- const clearSelection = (0, import_react.useCallback)(() => {
1148
+ const clearSelection = (0, import_react5.useCallback)(() => {
405
1149
  var _a2;
406
1150
  (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll(".rte-sel").forEach((c) => c.classList.remove("rte-sel"));
407
1151
  setSelCells([]);
408
1152
  dragStartCell.current = null;
409
1153
  }, []);
410
- const fixListNesting = (0, import_react.useCallback)(() => {
411
- const editor = editorRef.current;
412
- if (!editor) return;
413
- editor.querySelectorAll("ol > ol, ol > ul, ul > ol, ul > ul").forEach((orphan) => {
414
- const prev = orphan.previousElementSibling;
415
- if (prev && prev.tagName === "LI") {
416
- prev.appendChild(orphan);
417
- }
1154
+ const clearImageSel = (0, import_react5.useCallback)(() => setSelectedImg(null), []);
1155
+ const fixFontTags = (0, import_react5.useCallback)(() => {
1156
+ var _a2;
1157
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("font[color]").forEach((font) => {
1158
+ var _a3;
1159
+ const span = document.createElement("span");
1160
+ span.style.color = (_a3 = font.getAttribute("color")) != null ? _a3 : "";
1161
+ while (font.firstChild) span.appendChild(font.firstChild);
1162
+ font.replaceWith(span);
418
1163
  });
419
1164
  }, []);
420
- const exec = (0, import_react.useCallback)((cmd, val = null) => {
1165
+ const fixListNesting = (0, import_react5.useCallback)(() => {
421
1166
  var _a2;
422
- (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
423
- document.execCommand(cmd, false, val != null ? val : void 0);
424
- fixListNesting();
425
- refresh();
426
- }, [fixListNesting]);
427
- const refresh = (0, import_react.useCallback)(() => {
1167
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("ol > ol, ol > ul, ul > ol, ul > ul").forEach((orphan) => {
1168
+ const prev = orphan.previousElementSibling;
1169
+ if ((prev == null ? void 0 : prev.tagName) === "LI") prev.appendChild(orphan);
1170
+ });
1171
+ }, []);
1172
+ const refresh = (0, import_react5.useCallback)(() => {
428
1173
  var _a2, _b, _c, _d;
429
1174
  const raw = document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g, "");
430
1175
  const block = ["h1", "h2", "h3"].includes(raw) ? raw : "p";
431
- const c = document.queryCommandValue("foreColor");
432
- if (c && c !== "false") {
433
- const m = c.match(/\d+/g);
434
- if (m) setColor("#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join(""));
435
- }
1176
+ setColor(getColorAtCursor(editorRef.current));
436
1177
  const cell = getCurrentCell();
437
1178
  const inTable = !!cell;
438
1179
  const cellMerged = cell ? cell.colSpan > 1 || cell.rowSpan > 1 : false;
1180
+ const anchor = getCurrentLink();
1181
+ const inLink = !!anchor;
439
1182
  setFmt({
440
1183
  bold: document.queryCommandState("bold"),
441
1184
  italic: document.queryCommandState("italic"),
@@ -448,15 +1191,39 @@ function RichTextEditor({ value, onChange }) {
448
1191
  aJ: document.queryCommandState("justifyFull"),
449
1192
  block,
450
1193
  inTable,
451
- cellMerged
1194
+ cellMerged,
1195
+ inLink,
1196
+ linkHref: anchor == null ? void 0 : anchor.href
452
1197
  });
453
1198
  if (cell && editorAreaRef.current) setTableFP(calcFloat(cell, 370));
454
1199
  else setTableFP(null);
1200
+ if (anchor && editorAreaRef.current && !linkBar) setLinkInfoFP(calcFloat(anchor, 290));
1201
+ else setLinkInfoFP(null);
455
1202
  const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
456
1203
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
457
1204
  onChange == null ? void 0 : onChange((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : "");
458
- }, [onChange, calcFloat]);
459
- const handleEditorMouseDown = (0, import_react.useCallback)((e) => {
1205
+ }, [onChange, calcFloat, linkBar]);
1206
+ const exec = (0, import_react5.useCallback)((cmd, val = null) => {
1207
+ var _a2;
1208
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
1209
+ document.execCommand(cmd, false, val != null ? val : void 0);
1210
+ fixFontTags();
1211
+ fixListNesting();
1212
+ refresh();
1213
+ }, [fixFontTags, fixListNesting, refresh]);
1214
+ const insertImage = (0, import_react5.useCallback)((src) => {
1215
+ var _a2;
1216
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
1217
+ document.execCommand("insertHTML", false, `<img src="${src}" alt="" style="max-width:100%;height:auto;display:block;" />`);
1218
+ refresh();
1219
+ }, [refresh]);
1220
+ const handleEditorMouseDown = (0, import_react5.useCallback)((e) => {
1221
+ if (e.target.tagName === "IMG") {
1222
+ e.preventDefault();
1223
+ setSelectedImg(e.target);
1224
+ return;
1225
+ }
1226
+ clearImageSel();
460
1227
  const target = e.target.closest("td, th");
461
1228
  if (!target) {
462
1229
  clearSelection();
@@ -473,8 +1240,8 @@ function RichTextEditor({ value, onChange }) {
473
1240
  dragStartCell.current = target;
474
1241
  isDragging.current = true;
475
1242
  applySelection([target]);
476
- }, [applySelection, clearSelection]);
477
- const handleEditorMouseMove = (0, import_react.useCallback)((e) => {
1243
+ }, [applySelection, clearSelection, clearImageSel]);
1244
+ const handleEditorMouseMove = (0, import_react5.useCallback)((e) => {
478
1245
  if (!isDragging.current || !dragStartCell.current) return;
479
1246
  const target = e.target.closest("td, th");
480
1247
  if (!target || target === dragStartCell.current) return;
@@ -486,12 +1253,10 @@ function RichTextEditor({ value, onChange }) {
486
1253
  applySelection(cells);
487
1254
  }
488
1255
  }, [applySelection]);
489
- const handleEditorMouseUp = (0, import_react.useCallback)(() => {
490
- isDragging.current = false;
491
- }, []);
492
- const handleKeyDown = (0, import_react.useCallback)((e) => {
1256
+ const handleKeyDown = (0, import_react5.useCallback)((e) => {
493
1257
  var _a2, _b;
494
1258
  clearSelection();
1259
+ clearImageSel();
495
1260
  if (e.key === "Tab") {
496
1261
  e.preventDefault();
497
1262
  const cell = getCurrentCell();
@@ -523,21 +1288,21 @@ function RichTextEditor({ value, onChange }) {
523
1288
  exec("underline");
524
1289
  }
525
1290
  }
526
- }, [exec, clearSelection]);
1291
+ }, [exec, clearSelection, clearImageSel]);
527
1292
  const insertTable = (rows, cols) => {
528
1293
  var _a2;
529
1294
  (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
530
- let html = `<table>`;
1295
+ let html = "<table>";
531
1296
  for (let r = 0; r < rows; r++) {
532
1297
  html += "<tr>";
533
- for (let c = 0; c < cols; c++) html += r === 0 ? `<th><br></th>` : `<td><br></td>`;
1298
+ for (let c = 0; c < cols; c++) html += r === 0 ? "<th><br></th>" : "<td><br></td>";
534
1299
  html += "</tr>";
535
1300
  }
536
1301
  html += "</table><p><br></p>";
537
1302
  document.execCommand("insertHTML", false, html);
538
1303
  refresh();
539
1304
  };
540
- const tableOp = (0, import_react.useCallback)((op) => {
1305
+ const tableOp = (0, import_react5.useCallback)((op) => {
541
1306
  const cell = getCurrentCell(), table = getCurrentTable();
542
1307
  if (!cell || !table) return;
543
1308
  const row = cell.closest("tr");
@@ -575,7 +1340,7 @@ function RichTextEditor({ value, onChange }) {
575
1340
  }
576
1341
  refresh();
577
1342
  }, [clearSelection, refresh]);
578
- const doMerge = (0, import_react.useCallback)((cells) => {
1343
+ const doMerge = (0, import_react5.useCallback)((cells) => {
579
1344
  var _a2;
580
1345
  if (cells.length < 2) return;
581
1346
  const table = cells[0].closest("table");
@@ -593,12 +1358,13 @@ function RichTextEditor({ value, onChange }) {
593
1358
  clearSelection();
594
1359
  refresh();
595
1360
  }, [clearSelection, refresh]);
596
- const doSplit = (0, import_react.useCallback)(() => {
1361
+ const doSplit = (0, import_react5.useCallback)(() => {
597
1362
  const cell = getCurrentCell();
598
1363
  if (!cell || cell.colSpan === 1 && cell.rowSpan === 1) return;
599
1364
  const cs = cell.colSpan, rs = cell.rowSpan;
600
1365
  const table = cell.closest("table");
601
- const rows = Array.from(table.rows), row = cell.closest("tr");
1366
+ const rows = Array.from(table.rows);
1367
+ const row = cell.closest("tr");
602
1368
  const ri = rows.indexOf(row), ci = Array.from(row.cells).indexOf(cell);
603
1369
  cell.colSpan = 1;
604
1370
  cell.rowSpan = 1;
@@ -625,11 +1391,9 @@ function RichTextEditor({ value, onChange }) {
625
1391
  savedRangeRef.current = (sel == null ? void 0 : sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
626
1392
  if ((sel == null ? void 0 : sel.rangeCount) && editorAreaRef.current) {
627
1393
  const rect = sel.getRangeAt(0).getBoundingClientRect();
628
- if (rect.width > 0 || rect.height > 0) setLinkFP(calcFloat({ getBoundingClientRect: () => rect }, 360));
629
- else {
630
- const area = editorAreaRef.current.getBoundingClientRect();
631
- setLinkFP({ top: 60, left: 20, arrowLeft: 20 });
632
- }
1394
+ setLinkFP(
1395
+ rect.width > 0 || rect.height > 0 ? calcFloat({ getBoundingClientRect: () => rect }, 360) : { top: 60, left: 20, arrowLeft: 20 }
1396
+ );
633
1397
  }
634
1398
  setLinkUrl("https://");
635
1399
  setLinkBar(true);
@@ -643,8 +1407,9 @@ function RichTextEditor({ value, onChange }) {
643
1407
  sel.removeAllRanges();
644
1408
  sel.addRange(savedRangeRef.current);
645
1409
  }
646
- if ((_b = savedRangeRef.current) == null ? void 0 : _b.toString()) document.execCommand("createLink", false, linkUrl);
647
- else if (savedRangeRef.current) {
1410
+ if ((_b = savedRangeRef.current) == null ? void 0 : _b.toString()) {
1411
+ document.execCommand("createLink", false, linkUrl);
1412
+ } else if (savedRangeRef.current) {
648
1413
  const a = document.createElement("a");
649
1414
  a.href = linkUrl;
650
1415
  a.textContent = linkUrl;
@@ -655,70 +1420,11 @@ function RichTextEditor({ value, onChange }) {
655
1420
  setLinkFP(null);
656
1421
  refresh();
657
1422
  };
658
- const prettifyHtml = (html) => {
659
- const INLINE = /* @__PURE__ */ new Set(["a", "b", "i", "u", "em", "strong", "span", "code", "br", "small", "sub", "sup"]);
660
- let indent = 0;
661
- const pad = () => " ".repeat(indent);
662
- const tokens = html.replace(/>\s+</g, "><").replace(/(<\/?[^>]+>)/g, "\0$1\0").split("\0").filter(Boolean);
663
- const lines = [];
664
- let inline = "";
665
- const flush = () => {
666
- if (inline.trim()) {
667
- lines.push(pad() + inline.trim());
668
- inline = "";
669
- }
670
- };
671
- for (const tok of tokens) {
672
- const ot = tok.match(/^<([a-zA-Z][a-zA-Z0-9]*)[^>]*>$/), ct = tok.match(/^<\/([a-zA-Z][a-zA-Z0-9]*)>$/);
673
- if (tok.match(/^<[^>]+\/>$/)) {
674
- flush();
675
- lines.push(pad() + tok);
676
- continue;
677
- }
678
- if (ot) {
679
- const tag = ot[1].toLowerCase();
680
- if (INLINE.has(tag)) {
681
- inline += tok;
682
- continue;
683
- }
684
- flush();
685
- lines.push(pad() + tok);
686
- indent++;
687
- continue;
688
- }
689
- if (ct) {
690
- const tag = ct[1].toLowerCase();
691
- if (INLINE.has(tag)) {
692
- inline += tok;
693
- continue;
694
- }
695
- flush();
696
- indent = Math.max(0, indent - 1);
697
- const prev = lines[lines.length - 1];
698
- prev && prev.trim().startsWith("<") && !prev.includes("</") ? lines[lines.length - 1] = prev + tok : lines.push(pad() + tok);
699
- continue;
700
- }
701
- inline += tok;
702
- }
703
- flush();
704
- return lines.join("\n");
705
- };
706
- const toCode = () => {
707
- var _a2, _b;
708
- setCodeVal(prettifyHtml((_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : ""));
709
- setIsCode(true);
710
- };
711
- const toVisual = () => {
712
- if (editorRef.current) editorRef.current.innerHTML = codeVal;
713
- setIsCode(false);
714
- refresh();
715
- };
716
1423
  const insertCodeBlock = () => {
717
1424
  const sel = window.getSelection();
718
1425
  if (!(sel == null ? void 0 : sel.rangeCount)) return;
719
1426
  const range = sel.getRangeAt(0);
720
- const pre = document.createElement("pre");
721
- const code = document.createElement("code");
1427
+ const pre = document.createElement("pre"), code = document.createElement("code");
722
1428
  code.textContent = range.toString() || "// code here";
723
1429
  pre.appendChild(code);
724
1430
  range.deleteContents();
@@ -731,55 +1437,72 @@ function RichTextEditor({ value, onChange }) {
731
1437
  sel.addRange(range);
732
1438
  refresh();
733
1439
  };
1440
+ const toCode = () => {
1441
+ var _a2, _b;
1442
+ setCodeVal(prettifyHtml((_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : ""));
1443
+ setIsCode(true);
1444
+ };
1445
+ const toVisual = () => {
1446
+ if (editorRef.current) editorRef.current.innerHTML = codeVal;
1447
+ setIsCode(false);
1448
+ refresh();
1449
+ };
734
1450
  const copyHtml = () => {
735
1451
  var _a2, _b;
736
- navigator.clipboard.writeText(isCode ? codeVal : (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : "").catch(() => {
1452
+ return navigator.clipboard.writeText(isCode ? codeVal : (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : "").catch(() => {
737
1453
  });
738
1454
  };
739
- (0, import_react.useEffect)(() => {
1455
+ (0, import_react5.useEffect)(() => {
740
1456
  if (editorRef.current) {
741
1457
  editorRef.current.innerHTML = value != null ? value : "<p>Start writing here...</p>";
742
1458
  refresh();
743
1459
  }
744
1460
  }, []);
745
- (0, import_react.useEffect)(() => {
746
- if (!showTable) return;
747
- const h = () => setShowTable(false);
748
- document.addEventListener("mousedown", h);
749
- return () => document.removeEventListener("mousedown", h);
750
- }, [showTable]);
751
1461
  const canMerge = selCells.length >= 2;
752
1462
  const canSplit = fmt.cellMerged;
753
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "1rem 0" }, children: [
754
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: CSS }),
755
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
756
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-toolbar", children: [
757
- !isCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
758
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
759
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
760
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
761
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Btn, { onClick: () => {
762
- var _a2;
763
- return (_a2 = colorRef.current) == null ? void 0 : _a2.click();
764
- }, title: "Text color", style: { flexDirection: "column", gap: 1, padding: "3px 6px" }, children: [
765
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
766
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rte-swatch", style: { background: color } })
1463
+ const backdrop = { position: "fixed", inset: 0, zIndex: 199 };
1464
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { padding: "1rem 0" }, children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: CSS }),
1466
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
1467
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-toolbar", children: [
1468
+ !isCode && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1469
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
1470
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
1472
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
1473
+ showColor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowColor(false) }),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1475
+ Btn,
1476
+ {
1477
+ onClick: () => setShowColor((v) => !v),
1478
+ title: "Text color",
1479
+ active: showColor,
1480
+ style: { flexDirection: "column", gap: 1, padding: "3px 6px" },
1481
+ children: [
1482
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
1483
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "rte-swatch", style: { background: color } })
1484
+ ]
1485
+ }
1486
+ ),
1487
+ showColor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", top: 34, left: -4, zIndex: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1488
+ ColorPicker,
1489
+ {
1490
+ color,
1491
+ onColor: (c) => {
1492
+ setColor(c);
1493
+ exec("foreColor", c);
1494
+ setShowColor(false);
1495
+ },
1496
+ onRemove: () => {
1497
+ exec("removeFormat");
1498
+ setShowColor(false);
1499
+ },
1500
+ onClose: () => setShowColor(false)
1501
+ }
1502
+ ) })
767
1503
  ] }),
768
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
769
- "input",
770
- {
771
- ref: colorRef,
772
- type: "color",
773
- value: color,
774
- onChange: (e) => {
775
- setColor(e.target.value);
776
- exec("foreColor", e.target.value);
777
- },
778
- style: { position: "absolute", opacity: 0, width: 0, height: 0, pointerEvents: "none" }
779
- }
780
- ),
781
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
782
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1505
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
783
1506
  "select",
784
1507
  {
785
1508
  className: "rte-select",
@@ -795,36 +1518,57 @@ function RichTextEditor({ value, onChange }) {
795
1518
  refresh();
796
1519
  },
797
1520
  children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "p", children: "Paragraph" }),
799
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "h1", children: "Heading 1" }),
800
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "h2", children: "Heading 2" }),
801
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "h3", children: "Heading 3" })
1521
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "p", children: "Paragraph" }),
1522
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h1", children: "Heading 1" }),
1523
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h2", children: "Heading 2" }),
1524
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h3", children: "Heading 3" })
802
1525
  ]
803
1526
  }
804
1527
  ),
805
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
806
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlignIco, { t: "left" }) }),
807
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlignIco, { t: "center" }) }),
808
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlignIco, { t: "right" }) }),
809
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlignIco, { t: "justify" }) }),
810
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
811
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoUL, {}) }),
812
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoOL, {}) }),
813
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoIndent, {}) }),
814
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoOutdent, {}) }),
815
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
816
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoLink, {}) }),
817
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoCode, {}) }),
818
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-tp-wrap", onMouseDown: (e) => e.stopPropagation(), children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoTable, {}) }),
820
- showTable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TablePicker, { onInsert: (r, c) => {
821
- insertTable(r, c);
822
- setShowTable(false);
823
- }, onClose: () => setShowTable(false) })
1528
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1529
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyLeft"), title: "Align left", active: fmt.aL, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "left" }) }),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyCenter"), title: "Align center", active: fmt.aC, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "center" }) }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyRight"), title: "Align right", active: fmt.aR, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "right" }) }),
1532
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("justifyFull"), title: "Justify", active: fmt.aJ, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AlignIco, { t: "justify" }) }),
1533
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1534
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertUnorderedList"), title: "Bullet list", active: fmt.ul, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoUL, {}) }),
1535
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("insertOrderedList"), title: "Numbered list", active: fmt.ol, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOL, {}) }),
1536
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("indent"), title: "Indent (Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoIndent, {}) }),
1537
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => exec("outdent"), title: "Outdent (Shift+Tab)", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoOutdent, {}) }),
1538
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoLink, {}) }),
1540
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCode, {}) }),
1541
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
1542
+ showImagePicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowImagePicker(false) }),
1543
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => setShowImagePicker((v) => !v), title: "Insert image", active: showImagePicker, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoImage, {}) }),
1544
+ showImagePicker && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1545
+ ImagePicker,
1546
+ {
1547
+ onInsert: (src) => {
1548
+ insertImage(src);
1549
+ setShowImagePicker(false);
1550
+ },
1551
+ onClose: () => setShowImagePicker(false)
1552
+ }
1553
+ ) })
824
1554
  ] }),
825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {})
1555
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative", display: "inline-flex" }, children: [
1556
+ showTable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: backdrop, onMouseDown: () => setShowTable(false) }),
1557
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoTable, {}) }),
1558
+ showTable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1559
+ TablePicker,
1560
+ {
1561
+ onInsert: (r, c) => {
1562
+ insertTable(r, c);
1563
+ setShowTable(false);
1564
+ },
1565
+ onClose: () => setShowTable(false)
1566
+ }
1567
+ ) })
1568
+ ] }),
1569
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {})
826
1570
  ] }),
827
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1571
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
828
1572
  Btn,
829
1573
  {
830
1574
  onClick: isCode ? toVisual : toCode,
@@ -834,16 +1578,16 @@ function RichTextEditor({ value, onChange }) {
834
1578
  children: isCode ? "Visual" : "HTML"
835
1579
  }
836
1580
  ),
837
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoCopy, {}) }),
838
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1 } }),
839
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { fontSize: 12, color: "#aaa" }, children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: copyHtml, title: "Copy HTML", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoCopy, {}) }),
1582
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { flex: 1 } }),
1583
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 12, color: "#aaa" }, children: [
840
1584
  words,
841
1585
  " ",
842
1586
  words === 1 ? "word" : "words"
843
1587
  ] })
844
1588
  ] }),
845
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-area", ref: editorAreaRef, children: [
846
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1589
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-area", ref: editorAreaRef, children: [
1590
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
847
1591
  "textarea",
848
1592
  {
849
1593
  className: "rte-code",
@@ -853,7 +1597,7 @@ function RichTextEditor({ value, onChange }) {
853
1597
  style: { display: isCode ? "block" : "none" }
854
1598
  }
855
1599
  ),
856
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1600
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
857
1601
  "div",
858
1602
  {
859
1603
  ref: editorRef,
@@ -863,32 +1607,103 @@ function RichTextEditor({ value, onChange }) {
863
1607
  "data-ph": "Start typing...",
864
1608
  onMouseDown: handleEditorMouseDown,
865
1609
  onMouseMove: handleEditorMouseMove,
866
- onMouseUp: handleEditorMouseUp,
1610
+ onMouseUp: () => {
1611
+ isDragging.current = false;
1612
+ },
867
1613
  onKeyDown: handleKeyDown,
868
1614
  onKeyUp: refresh,
869
1615
  onSelect: refresh,
1616
+ onDrop: (e) => {
1617
+ var _a2;
1618
+ const file = (_a2 = e.dataTransfer.files) == null ? void 0 : _a2[0];
1619
+ if (!(file == null ? void 0 : file.type.startsWith("image/"))) return;
1620
+ e.preventDefault();
1621
+ const reader = new FileReader();
1622
+ reader.onload = () => insertImage(reader.result);
1623
+ reader.readAsDataURL(file);
1624
+ },
1625
+ onDragOver: (e) => {
1626
+ if (e.dataTransfer.types.includes("Files")) e.preventDefault();
1627
+ },
870
1628
  style: { display: isCode ? "none" : "block" }
871
1629
  }
872
1630
  ),
873
- !isCode && fmt.inTable && tableFP && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-float", style: { top: tableFP.top, left: tableFP.left }, children: [
874
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rte-float-arrow", style: { left: tableFP.arrowLeft } }),
875
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("addRowAbove"), title: "Add row above", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoRowAbove, {}) }),
876
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("addRowBelow"), title: "Add row below", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoRowBelow, {}) }),
877
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("deleteRow"), title: "Delete row", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoDelRow, {}) }),
878
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
879
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("addColLeft"), title: "Add column left", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoColLeft, {}) }),
880
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("addColRight"), title: "Add column right", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoColRight, {}) }),
881
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("deleteCol"), title: "Delete column", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoDelCol, {}) }),
882
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
883
- canMerge && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => doMerge(selCells), title: `Merge ${selCells.length} cells`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoMerge, {}) }),
884
- canSplit && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: doSplit, title: "Split merged cell", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoSplit, {}) }),
885
- (canMerge || canSplit) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sep, {}),
886
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Btn, { onClick: () => tableOp("deleteTable"), title: "Delete table", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoDelTable, {}) })
1631
+ !isCode && selectedImg && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1632
+ ImageEditor,
1633
+ {
1634
+ img: selectedImg,
1635
+ containerRef: editorAreaRef,
1636
+ onClose: clearImageSel,
1637
+ onDelete: () => {
1638
+ selectedImg.remove();
1639
+ clearImageSel();
1640
+ refresh();
1641
+ },
1642
+ onChange: refresh
1643
+ }
1644
+ ),
1645
+ !isCode && fmt.inTable && tableFP && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-float", style: { top: tableFP.top, left: tableFP.left }, children: [
1646
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rte-float-arrow", style: { left: tableFP.arrowLeft } }),
1647
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("addRowAbove"), title: "Add row above", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoRowAbove, {}) }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("addRowBelow"), title: "Add row below", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoRowBelow, {}) }),
1649
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("deleteRow"), title: "Delete row", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoDelRow, {}) }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1651
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("addColLeft"), title: "Add col left", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoColLeft, {}) }),
1652
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("addColRight"), title: "Add col right", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoColRight, {}) }),
1653
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("deleteCol"), title: "Delete col", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoDelCol, {}) }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1655
+ canMerge && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => doMerge(selCells), title: `Merge ${selCells.length} cells`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoMerge, {}) }),
1656
+ canSplit && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: doSplit, title: "Split merged cell", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoSplit, {}) }),
1657
+ (canMerge || canSplit) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1658
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { onClick: () => tableOp("deleteTable"), title: "Delete table", danger: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoDelTable, {}) })
887
1659
  ] }),
888
- !isCode && linkBar && linkFP && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-float rte-link-float", style: { top: linkFP.top, left: linkFP.left }, children: [
889
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rte-float-arrow", style: { left: linkFP.arrowLeft } }),
890
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IcoLink, {}),
891
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1660
+ !isCode && fmt.inLink && linkInfoFP && !linkBar && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-float", style: { top: linkInfoFP.top, left: linkInfoFP.left, gap: 4 }, children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rte-float-arrow", style: { left: linkInfoFP.arrowLeft } }),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoLink, {}),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1664
+ "span",
1665
+ {
1666
+ style: { fontSize: 12, color: "#1a6fc4", maxWidth: 160, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
1667
+ title: fmt.linkHref,
1668
+ children: fmt.linkHref
1669
+ }
1670
+ ),
1671
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sep, {}),
1672
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { title: "Edit link", onClick: () => {
1673
+ var _a2;
1674
+ const anchor = getCurrentLink();
1675
+ if (!anchor) return;
1676
+ const sel = window.getSelection();
1677
+ const range = document.createRange();
1678
+ range.selectNodeContents(anchor);
1679
+ sel == null ? void 0 : sel.removeAllRanges();
1680
+ sel == null ? void 0 : sel.addRange(range);
1681
+ savedRangeRef.current = range.cloneRange();
1682
+ if (editorAreaRef.current) setLinkFP(calcFloat(anchor, 360));
1683
+ setLinkUrl((_a2 = anchor.getAttribute("href")) != null ? _a2 : "https://");
1684
+ setLinkBar(true);
1685
+ setLinkInfoFP(null);
1686
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M9 2L11 4L4.5 10.5H2.5V8.5L9 2Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }) }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { title: "Remove link", danger: true, onClick: () => {
1688
+ const anchor = getCurrentLink();
1689
+ if (!anchor) return;
1690
+ const sel = window.getSelection();
1691
+ const range = document.createRange();
1692
+ range.selectNodeContents(anchor);
1693
+ sel == null ? void 0 : sel.removeAllRanges();
1694
+ sel == null ? void 0 : sel.addRange(range);
1695
+ document.execCommand("unlink", false);
1696
+ refresh();
1697
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", children: [
1698
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M4.5 7C4.9 7.6 5.5 8 6.1 8H7.9C8.6 8 9.2 7.7 9.6 7.2", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
1699
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M8.5 6C8.1 5.4 7.5 5 6.9 5H5.1C4.4 5 3.8 5.3 3.4 5.8", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }),
1700
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M2 2L11 11", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
1701
+ ] }) })
1702
+ ] }),
1703
+ !isCode && linkBar && linkFP && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-float rte-linkbar", style: { top: linkFP.top, left: linkFP.left }, children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rte-float-arrow", style: { left: linkFP.arrowLeft } }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(IcoLink, {}),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
892
1707
  "input",
893
1708
  {
894
1709
  autoFocus: true,
@@ -905,26 +1720,36 @@ function RichTextEditor({ value, onChange }) {
905
1720
  placeholder: "https://example.com"
906
1721
  }
907
1722
  ),
908
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "rte-link-apply", onClick: applyLink, children: "Apply" }),
909
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "rte-link-cancel", onClick: () => {
910
- setLinkBar(false);
911
- setLinkFP(null);
912
- }, children: "\u2715" })
1723
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1724
+ "button",
1725
+ {
1726
+ onClick: applyLink,
1727
+ style: { height: 26, padding: "0 10px", background: "#1a6fc4", color: "#fff", border: "none", borderRadius: 3, fontSize: 12, fontWeight: 600, cursor: "pointer" },
1728
+ children: "Apply"
1729
+ }
1730
+ ),
1731
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1732
+ "button",
1733
+ {
1734
+ onClick: () => {
1735
+ setLinkBar(false);
1736
+ setLinkFP(null);
1737
+ },
1738
+ style: { height: 26, padding: "0 8px", background: "transparent", color: "#666", border: "1px solid #ccc", borderRadius: 3, fontSize: 12, cursor: "pointer" },
1739
+ children: "\u2715"
1740
+ }
1741
+ )
913
1742
  ] })
914
1743
  ] }),
915
- !isCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rte-footer", children: [
916
- "Table: ",
917
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "drag" }),
1744
+ !isCode && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rte-footer", children: [
1745
+ "Click image to edit \xA0\xB7\xA0 Drag handles to resize \xA0\xB7\xA0 Drop image file to insert \xA0\xB7\xA0 Table: ",
1746
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "drag" }),
918
1747
  " or ",
919
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Shift+click" }),
920
- " to select cells \xA0\xB7\xA0 ",
921
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Tab" }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "Shift+click" }),
1749
+ " to select cells \xA0\xB7\xA0",
1750
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "Tab" }),
922
1751
  " moves between cells"
923
1752
  ] })
924
1753
  ] })
925
1754
  ] });
926
1755
  }
927
- // Annotate the CommonJS export names for ESM import in node:
928
- 0 && (module.exports = {
929
- RichTextEditor
930
- });