@malaya_jeeva/rich-text-editor 1.0.4 → 1.0.5

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.
Files changed (3) hide show
  1. package/dist/index.js +711 -224
  2. package/dist/index.mjs +711 -224
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -6,138 +6,171 @@ import {
6
6
  useEffect
7
7
  } from "react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- var CSS = `
10
- * { box-sizing: border-box; }
11
- .rte-toolbar {
12
- display: flex; flex-wrap: wrap; align-items: center; gap: 1px;
13
- padding: 4px 8px; background: #f8f8f8; border-bottom: 1px solid #e0e0e0;
14
- }
15
- @media (prefers-color-scheme: dark) { .rte-toolbar { background: #1e1e1e; border-color: #333; } }
16
-
17
- .rte-btn {
18
- background: transparent; border: none; border-radius: 3px; cursor: pointer;
19
- height: 30px; min-width: 30px; padding: 0 6px; color: #444; font-size: 13px;
20
- font-weight: 500; font-family: var(--font-sans); display: inline-flex;
21
- align-items: center; justify-content: center; user-select: none;
22
- white-space: nowrap; transition: background 0.1s; flex-shrink: 0;
23
- }
24
- .rte-btn:hover { background: #e8e8e8; }
25
- .rte-btn.active { background: #d0e4ff; color: #1a5fb4; }
26
- .rte-btn.danger { color: #c0392b; }
27
- .rte-btn.danger:hover { background: #fdecea; }
28
- @media (prefers-color-scheme: dark) {
29
- .rte-btn { color: #ccc; } .rte-btn:hover { background: #2e2e2e; }
30
- .rte-btn.active { background: #1a3a5c; color: #90c4ff; }
31
- .rte-btn.danger { color: #ff6b6b; } .rte-btn.danger:hover { background: #3a1a1a; }
32
- }
33
- .rte-sep { width: 1px; height: 20px; background: #d8d8d8; margin: 0 4px; flex-shrink: 0; }
34
- @media (prefers-color-scheme: dark) { .rte-sep { background: #3a3a3a; } }
35
-
36
- .rte-select {
37
- height: 28px; border: 1px solid #d8d8d8; border-radius: 3px;
38
- background: #fff; color: #333; font-size: 12px; padding: 0 22px 0 7px;
39
- cursor: pointer; outline: none; font-family: var(--font-sans);
40
- appearance: none; -webkit-appearance: none;
41
- 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");
42
- background-repeat: no-repeat; background-position: right 6px center;
43
- }
44
- @media (prefers-color-scheme: dark) { .rte-select { background-color: #2a2a2a; border-color: #444; color: #ccc; } }
45
- .rte-swatch { width: 14px; height: 3px; border-radius: 1px; margin-top: 2px; }
46
-
47
- /* Table picker */
48
- .rte-tp-wrap { position: relative; display: inline-flex; }
49
- .rte-tp {
50
- position: absolute; top: 34px; left: 0; z-index: 100;
51
- background: #fff; border: 1px solid #d8d8d8; border-radius: 6px;
52
- box-shadow: 0 4px 16px rgba(0,0,0,0.12); padding: 10px;
53
- }
54
- @media (prefers-color-scheme: dark) { .rte-tp { background: #222; border-color: #444; } }
55
- .rte-tp-lbl { font-size: 11px; color: #888; text-align: center; margin-bottom: 8px; font-family: var(--font-sans); }
56
- .rte-tp-grid { display: grid; grid-template-columns: repeat(8, 18px); gap: 2px; }
57
- .rte-tp-cell { width: 18px; height: 18px; border-radius: 2px; border: 1px solid #ddd; cursor: pointer; background: #fff; transition: background 0.08s; }
58
- .rte-tp-cell.on { background: #d0e4ff; border-color: #1a5fb4; }
59
- @media (prefers-color-scheme: dark) {
60
- .rte-tp-cell { background: #2a2a2a; border-color: #444; }
61
- .rte-tp-cell.on { background: #1a3a5c; border-color: #90c4ff; }
62
- }
63
-
64
- /* \u2500\u2500 Floating toolbar \u2500\u2500 */
65
- .rte-float {
66
- position: absolute; z-index: 50;
67
- background: #fff; border: 1px solid #d0d0d0;
68
- border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.13);
69
- padding: 4px 6px;
70
- display: flex; align-items: center; gap: 1px; flex-wrap: wrap;
71
- }
72
- @media (prefers-color-scheme: dark) { .rte-float { background: #222; border-color: #444; } }
73
- .rte-float-arrow {
74
- position: absolute; top: -7px; width: 14px; height: 7px;
75
- }
76
- .rte-float-arrow::before,
77
- .rte-float-arrow::after {
78
- content: ''; position: absolute; left: 0;
79
- border-left: 7px solid transparent; border-right: 7px solid transparent;
80
- }
81
- .rte-float-arrow::before { top: 0; border-bottom: 7px solid #d0d0d0; }
82
- .rte-float-arrow::after { top: 1px; border-bottom: 6px solid #fff; }
83
- @media (prefers-color-scheme: dark) {
84
- .rte-float-arrow::before { border-bottom-color: #444; }
85
- .rte-float-arrow::after { border-bottom-color: #222; }
86
- }
87
-
88
- /* Link bar inside float */
89
- .rte-link-float { min-width: 340px; }
90
- .rte-link-float input {
91
- flex: 1; height: 26px; border: 1px solid #ccc; border-radius: 3px;
92
- padding: 0 8px; font-size: 13px; outline: none; font-family: var(--font-sans);
93
- background: #fff; color: #222;
94
- min-width: 0;
95
- }
96
- @media (prefers-color-scheme: dark) { .rte-link-float input { background: #1a1a1a; border-color: #555; color: #ddd; } }
97
- .rte-link-apply {
98
- height: 26px; padding: 0 10px; background: #1a6fc4; color: #fff;
99
- border: none; border-radius: 3px; font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
100
- }
101
- .rte-link-cancel {
102
- height: 26px; padding: 0 8px; background: transparent; color: #666;
103
- border: 1px solid #ccc; border-radius: 3px; font-size: 12px; cursor: pointer;
104
- }
105
- @media (prefers-color-scheme: dark) { .rte-link-cancel { color: #aaa; border-color: #555; } }
106
-
107
- /* Editor */
108
- .rte-area { position: relative; background: #fff; }
109
- @media (prefers-color-scheme: dark) { .rte-area { background: #141414; } }
110
- .rte-body {
111
- min-height: 280px; outline: none; font-size: 15px; line-height: 1.75;
112
- color: #222; caret-color: #222; padding: 18px 20px;
9
+ function hsv2rgb(h, s, v) {
10
+ const f = (n) => {
11
+ const k = (n + h / 60) % 6;
12
+ return v - v * s * Math.max(0, Math.min(k, 4 - k, 1));
13
+ };
14
+ return [Math.round(f(5) * 255), Math.round(f(3) * 255), Math.round(f(1) * 255)];
15
+ }
16
+ function rgb2hex(r, g, b) {
17
+ return "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("");
18
+ }
19
+ function hex2hsv(hex) {
20
+ if (hex.length < 7) return [0, 1, 1];
21
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
22
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
23
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
24
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min;
25
+ let h = 0;
26
+ const s = max === 0 ? 0 : d / max, v = max;
27
+ if (d !== 0) {
28
+ if (max === r) h = (g - b) / d % 6;
29
+ else if (max === g) h = (b - r) / d + 2;
30
+ else h = (r - g) / d + 4;
31
+ h = Math.round(h * 60);
32
+ if (h < 0) h += 360;
113
33
  }
114
- @media (prefers-color-scheme: dark) { .rte-body { color: #ddd; caret-color: #ddd; } }
115
- .rte-body h1 { font-size: 26px; font-weight: 600; margin: 0.6em 0 0.2em; }
116
- .rte-body h2 { font-size: 20px; font-weight: 600; margin: 0.6em 0 0.2em; }
117
- .rte-body h3 { font-size: 16px; font-weight: 600; margin: 0.6em 0 0.2em; }
118
- .rte-body p { margin: 0.2em 0; }
119
- .rte-body ol { list-style-type: decimal; padding-left: 1.6em; margin: 0.3em 0; }
120
- .rte-body ol ol { list-style-type: lower-alpha; padding-left: 1.6em; }
121
- .rte-body ol ol ol { list-style-type: lower-roman; padding-left: 1.6em; }
122
- .rte-body ul { list-style-type: disc; padding-left: 1.6em; margin: 0.3em 0; }
123
- .rte-body ul ul { list-style-type: circle; padding-left: 1.6em; }
124
- .rte-body ul ul ul { list-style-type: square; padding-left: 1.6em; }
125
- .rte-body li { margin: 0.1em 0; }
126
- .rte-body a { color: #1a6fc4; text-decoration: underline; cursor: pointer; }
127
- .rte-body pre { background: #f4f4f4; border: 1px solid #e0e0e0; border-radius: 4px; padding: 12px 14px; margin: 0.6em 0; overflow-x: auto; }
128
- .rte-body code { font-family: var(--font-mono); font-size: 13px; }
129
- .rte-body:empty:before { content: attr(data-ph); color: #aaa; pointer-events: none; }
130
- .rte-body table { border-collapse: collapse; width: 100%; margin: 0.8em 0; }
131
- .rte-body td, .rte-body th { border: 1px solid #ccc; padding: 7px 10px; min-width: 60px; text-align: left; vertical-align: top; }
132
- .rte-body th { background: #f5f5f5; font-weight: 600; }
133
- @media (prefers-color-scheme: dark) {
134
- .rte-body td, .rte-body th { border-color: #444; }
135
- .rte-body th { background: #2a2a2a; }
34
+ return [h, s, v];
35
+ }
36
+ function getColorAtCursor(editor) {
37
+ var _a, _b;
38
+ if (!editor) return "#000000";
39
+ const sel = window.getSelection();
40
+ if (!(sel == null ? void 0 : sel.rangeCount)) return "#000000";
41
+ let el = ((_a = sel.anchorNode) == null ? void 0 : _a.nodeType) === 3 ? sel.anchorNode.parentElement : sel.anchorNode;
42
+ while (el && el !== editor) {
43
+ const c = (_b = el.style) == null ? void 0 : _b.color;
44
+ if (c) {
45
+ const m = c.match(/\d+/g);
46
+ if (m && m.length >= 3) return "#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
47
+ if (c.startsWith("#")) return c;
48
+ }
49
+ el = el.parentElement;
136
50
  }
137
- .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; }
138
- .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; }
139
- .rte-footer { font-size: 11px; color: #999; padding: 4px 20px 5px; border-top: 1px solid #e8e8e8; background: #fafafa; }
140
- @media (prefers-color-scheme: dark) { .rte-footer { border-color: #2a2a2a; background: #1a1a1a; color: #555; } }
51
+ return "#000000";
52
+ }
53
+ var PALETTE = [
54
+ "#000000",
55
+ "#434343",
56
+ "#666666",
57
+ "#999999",
58
+ "#b7b7b7",
59
+ "#cccccc",
60
+ "#d9d9d9",
61
+ "#ffffff",
62
+ "#ff0000",
63
+ "#ff4500",
64
+ "#ff9900",
65
+ "#ffff00",
66
+ "#00ff00",
67
+ "#00ffff",
68
+ "#4a86e8",
69
+ "#0000ff",
70
+ "#9900ff",
71
+ "#ff00ff",
72
+ "#e06666",
73
+ "#f6b26b",
74
+ "#ffd966",
75
+ "#93c47d",
76
+ "#76a5af",
77
+ "#6fa8dc",
78
+ "#8e7cc3",
79
+ "#c27ba0",
80
+ "#a61c00",
81
+ "#783f04",
82
+ "#7f6000",
83
+ "#274e13",
84
+ "#0c343d",
85
+ "#073763",
86
+ "#20124d",
87
+ "#4c1130"
88
+ ];
89
+ var CSS = `
90
+ * { box-sizing: border-box; }
91
+ .rte-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:1px; padding:4px 8px; background:#f8f8f8; border-bottom:1px solid #e0e0e0; }
92
+ @media(prefers-color-scheme:dark){ .rte-toolbar{background:#1e1e1e;border-color:#333;} }
93
+ .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; }
94
+ .rte-btn:hover { background:#e8e8e8; }
95
+ .rte-btn.active { background:#d0e4ff; color:#1a5fb4; }
96
+ .rte-btn.danger { color:#c0392b; } .rte-btn.danger:hover { background:#fdecea; }
97
+ @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;} }
98
+ .rte-sep { width:1px; height:20px; background:#d8d8d8; margin:0 4px; flex-shrink:0; }
99
+ @media(prefers-color-scheme:dark){ .rte-sep{background:#3a3a3a;} }
100
+ .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; }
101
+ @media(prefers-color-scheme:dark){ .rte-select{background-color:#2a2a2a;border-color:#444;color:#ccc;} }
102
+ .rte-swatch { width:14px; height:3px; border-radius:1px; margin-top:2px; }
103
+ .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; }
104
+ @media(prefers-color-scheme:dark){ .rte-cpick{background:#222;border-color:#444;} }
105
+ .rte-cgrid { display:grid; grid-template-columns:repeat(8,20px); gap:2px; margin-bottom:6px; }
106
+ .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; }
107
+ .rte-ccell:hover { transform:scale(1.2); box-shadow:0 1px 4px rgba(0,0,0,0.3); z-index:1; position:relative; }
108
+ .rte-ccell.sel { outline:2px solid #1a5fb4; outline-offset:1px; }
109
+ .rte-cactions { display:flex; align-items:center; gap:2px; border-top:1px solid #eee; padding-top:6px; margin-top:2px; }
110
+ @media(prefers-color-scheme:dark){ .rte-cactions{border-color:#333;} }
111
+ .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; }
112
+ .rte-caction:hover { background:#f0f0f0; border-color:#ddd; }
113
+ @media(prefers-color-scheme:dark){ .rte-caction:hover{background:#2a2a2a;border-color:#444;} }
114
+ .rte-tpick { background:#fff; border:1px solid #d8d8d8; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,0.12); padding:10px; }
115
+ @media(prefers-color-scheme:dark){ .rte-tpick{background:#222;border-color:#444;} }
116
+ .rte-tplbl { font-size:11px; color:#888; text-align:center; margin-bottom:8px; font-family:var(--font-sans); }
117
+ .rte-tpgrid { display:grid; grid-template-columns:repeat(8,18px); gap:2px; }
118
+ .rte-tpcell { width:18px; height:18px; border-radius:2px; border:1px solid #ddd; cursor:pointer; background:#fff; transition:background 0.08s; }
119
+ .rte-tpcell.on { background:#d0e4ff; border-color:#1a5fb4; }
120
+ @media(prefers-color-scheme:dark){ .rte-tpcell{background:#2a2a2a;border-color:#444;} .rte-tpcell.on{background:#1a3a5c;border-color:#90c4ff;} }
121
+ .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; }
122
+ @media(prefers-color-scheme:dark){ .rte-ipick{background:#222;border-color:#444;} }
123
+ .rte-itabs { display:flex; border-bottom:1px solid #eee; }
124
+ @media(prefers-color-scheme:dark){ .rte-itabs{border-color:#333;} }
125
+ .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); }
126
+ .rte-itab:hover { color:#444; background:#f8f8f8; }
127
+ .rte-itab.active { color:#1a5fb4; border-bottom-color:#1a5fb4; }
128
+ @media(prefers-color-scheme:dark){ .rte-itab:hover{background:#2a2a2a;} }
129
+ .rte-ipbody { padding:12px; display:flex; flex-direction:column; gap:10px; }
130
+ .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; }
131
+ .rte-idrop:hover,.rte-idrop.drag { border-color:#1a5fb4; background:#f0f4ff; }
132
+ @media(prefers-color-scheme:dark){ .rte-idrop{border-color:#555;} .rte-idrop:hover,.rte-idrop.drag{background:#1a3a5c;border-color:#90c4ff;} }
133
+ .rte-iinsert { width:100%; height:30px; background:#1a6fc4; color:#fff; border:none; border-radius:4px; font-size:12px; font-weight:600; cursor:pointer; }
134
+ .rte-iinsert:disabled { opacity:0.4; cursor:not-allowed; }
135
+ .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; }
136
+ @media(prefers-color-scheme:dark){ .rte-float{background:#222;border-color:#444;} }
137
+ .rte-float-arrow { position:absolute; top:-7px; width:14px; height:7px; }
138
+ .rte-float-arrow::before,.rte-float-arrow::after { content:''; position:absolute; left:0; border-left:7px solid transparent; border-right:7px solid transparent; }
139
+ .rte-float-arrow::before { top:0; border-bottom:7px solid #d0d0d0; }
140
+ .rte-float-arrow::after { top:1px; border-bottom:6px solid #fff; }
141
+ @media(prefers-color-scheme:dark){ .rte-float-arrow::before{border-bottom-color:#444;} .rte-float-arrow::after{border-bottom-color:#222;} }
142
+ .rte-linkbar { min-width:340px; }
143
+ .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; }
144
+ @media(prefers-color-scheme:dark){ .rte-linkbar input{background:#1a1a1a;border-color:#555;color:#ddd;} }
145
+ .rte-area { position:relative; background:#fff; }
146
+ @media(prefers-color-scheme:dark){ .rte-area{background:#141414;} }
147
+ .rte-body { min-height:280px; outline:none; font-size:15px; line-height:1.75; color:#222; caret-color:#222; padding:18px 20px; }
148
+ @media(prefers-color-scheme:dark){ .rte-body{color:#ddd;caret-color:#ddd;} }
149
+ .rte-body h1 { font-size:26px; font-weight:600; margin:0.6em 0 0.2em; }
150
+ .rte-body h2 { font-size:20px; font-weight:600; margin:0.6em 0 0.2em; }
151
+ .rte-body h3 { font-size:16px; font-weight:600; margin:0.6em 0 0.2em; }
152
+ .rte-body p { margin:0.2em 0; }
153
+ .rte-body ol { list-style-type:decimal; padding-left:1.6em; margin:0.3em 0; }
154
+ .rte-body ol ol { list-style-type:lower-alpha; padding-left:1.6em; }
155
+ .rte-body ol ol ol { list-style-type:lower-roman; padding-left:1.6em; }
156
+ .rte-body ul { list-style-type:disc; padding-left:1.6em; margin:0.3em 0; }
157
+ .rte-body ul ul { list-style-type:circle; padding-left:1.6em; }
158
+ .rte-body ul ul ul { list-style-type:square; padding-left:1.6em; }
159
+ .rte-body li { margin:0.1em 0; }
160
+ .rte-body a { color:#1a6fc4; text-decoration:underline; cursor:pointer; }
161
+ .rte-body pre { background:#f4f4f4; border:1px solid #e0e0e0; border-radius:4px; padding:12px 14px; margin:0.6em 0; overflow-x:auto; }
162
+ .rte-body code { font-family:var(--font-mono); font-size:13px; }
163
+ .rte-body:empty:before { content:attr(data-ph); color:#aaa; pointer-events:none; }
164
+ .rte-body table { border-collapse:collapse; width:100%; margin:0.8em 0; }
165
+ .rte-body td,.rte-body th { border:1px solid #ccc; padding:7px 10px; min-width:60px; text-align:left; vertical-align:top; }
166
+ .rte-body th { background:#f5f5f5; font-weight:600; }
167
+ @media(prefers-color-scheme:dark){ .rte-body td,.rte-body th{border-color:#444;} .rte-body th{background:#2a2a2a;} }
168
+ .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; }
169
+ .rte-body img { max-width:100%; height:auto; display:block; margin:4px 0; cursor:pointer; }
170
+ .rte-body img.rte-img-sel { outline:2px solid #1a5fb4; outline-offset:2px; }
171
+ .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; }
172
+ .rte-footer { font-size:11px; color:#999; padding:4px 20px 5px; border-top:1px solid #e8e8e8; background:#fafafa; }
173
+ @media(prefers-color-scheme:dark){ .rte-footer{border-color:#2a2a2a;background:#1a1a1a;color:#555;} }
141
174
  `;
142
175
  function Btn({ onClick, title, active, danger, children, style }) {
143
176
  return /* @__PURE__ */ jsx(
@@ -157,16 +190,213 @@ function Btn({ onClick, title, active, danger, children, style }) {
157
190
  function Sep() {
158
191
  return /* @__PURE__ */ jsx("div", { className: "rte-sep" });
159
192
  }
193
+ function CustomColorPicker({ initialColor, onApply, onBack }) {
194
+ const [hsv, setHsv] = useState(() => hex2hsv(initialColor || "#ff0000"));
195
+ const [hexInput, setHexInput] = useState(initialColor || "#ff0000");
196
+ const svRef = useRef(null);
197
+ const hueRef = useRef(null);
198
+ const svDrag = useRef(false);
199
+ const hueDrag = useRef(false);
200
+ const [h, s, v] = hsv;
201
+ const currentHex = rgb2hex(...hsv2rgb(h, s, v));
202
+ useEffect(() => {
203
+ const c = svRef.current;
204
+ if (!c) return;
205
+ const ctx = c.getContext("2d");
206
+ const W = c.width, H = c.height;
207
+ const [r, g, b] = hsv2rgb(h, 1, 1);
208
+ ctx.fillStyle = `rgb(${r},${g},${b})`;
209
+ ctx.fillRect(0, 0, W, H);
210
+ const wg = ctx.createLinearGradient(0, 0, W, 0);
211
+ wg.addColorStop(0, "rgba(255,255,255,1)");
212
+ wg.addColorStop(1, "rgba(255,255,255,0)");
213
+ ctx.fillStyle = wg;
214
+ ctx.fillRect(0, 0, W, H);
215
+ const bg = ctx.createLinearGradient(0, 0, 0, H);
216
+ bg.addColorStop(0, "rgba(0,0,0,0)");
217
+ bg.addColorStop(1, "rgba(0,0,0,1)");
218
+ ctx.fillStyle = bg;
219
+ ctx.fillRect(0, 0, W, H);
220
+ const cx = s * W, cy = (1 - v) * H;
221
+ ctx.beginPath();
222
+ ctx.arc(cx, cy, 7, 0, Math.PI * 2);
223
+ ctx.strokeStyle = "rgba(0,0,0,0.25)";
224
+ ctx.lineWidth = 2;
225
+ ctx.stroke();
226
+ ctx.beginPath();
227
+ ctx.arc(cx, cy, 6, 0, Math.PI * 2);
228
+ ctx.strokeStyle = "#fff";
229
+ ctx.lineWidth = 2;
230
+ ctx.stroke();
231
+ }, [h, s, v]);
232
+ useEffect(() => {
233
+ const c = hueRef.current;
234
+ if (!c) return;
235
+ const ctx = c.getContext("2d");
236
+ const W = c.width, H = c.height;
237
+ const grad = ctx.createLinearGradient(0, 0, W, 0);
238
+ for (let i = 0; i <= 360; i += 30) {
239
+ const [r, g, b] = hsv2rgb(i, 1, 1);
240
+ grad.addColorStop(i / 360, `rgb(${r},${g},${b})`);
241
+ }
242
+ ctx.fillStyle = grad;
243
+ ctx.fillRect(0, 0, W, H);
244
+ const tx = h / 360 * W;
245
+ ctx.beginPath();
246
+ ctx.roundRect(tx - 4, -1, 8, H + 2, 3);
247
+ ctx.fillStyle = "#fff";
248
+ ctx.fill();
249
+ ctx.strokeStyle = "rgba(0,0,0,0.3)";
250
+ ctx.lineWidth = 1;
251
+ ctx.stroke();
252
+ }, [h]);
253
+ const updateSV = useCallback((e) => {
254
+ const c = svRef.current;
255
+ if (!c) return;
256
+ const rect = c.getBoundingClientRect();
257
+ const ns = Math.max(0, Math.min(1, (e.clientX - rect.left) / c.width));
258
+ const nv = Math.max(0, Math.min(1, 1 - (e.clientY - rect.top) / c.height));
259
+ const next = [h, ns, nv];
260
+ setHsv(next);
261
+ setHexInput(rgb2hex(...hsv2rgb(...next)));
262
+ }, [h]);
263
+ const updateHue = useCallback((e) => {
264
+ const c = hueRef.current;
265
+ if (!c) return;
266
+ const rect = c.getBoundingClientRect();
267
+ const nh = Math.max(0, Math.min(360, (e.clientX - rect.left) / c.width * 360));
268
+ const next = [nh, s, v];
269
+ setHsv(next);
270
+ setHexInput(rgb2hex(...hsv2rgb(...next)));
271
+ }, [s, v]);
272
+ useEffect(() => {
273
+ const mm = (e) => {
274
+ if (svDrag.current) updateSV(e);
275
+ if (hueDrag.current) updateHue(e);
276
+ };
277
+ const mu = () => {
278
+ svDrag.current = false;
279
+ hueDrag.current = false;
280
+ };
281
+ window.addEventListener("mousemove", mm);
282
+ window.addEventListener("mouseup", mu);
283
+ return () => {
284
+ window.removeEventListener("mousemove", mm);
285
+ window.removeEventListener("mouseup", mu);
286
+ };
287
+ }, [updateSV, updateHue]);
288
+ return /* @__PURE__ */ jsxs("div", { children: [
289
+ /* @__PURE__ */ jsxs(
290
+ "button",
291
+ {
292
+ onMouseDown: onBack,
293
+ style: { background: "none", border: "none", cursor: "pointer", color: "#666", fontSize: 12, display: "flex", alignItems: "center", gap: 4, padding: "2px 4px", borderRadius: 3, marginBottom: 8 },
294
+ children: [
295
+ /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M8 2L4 6L8 10", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" }) }),
296
+ "Back"
297
+ ]
298
+ }
299
+ ),
300
+ /* @__PURE__ */ jsx(
301
+ "canvas",
302
+ {
303
+ ref: svRef,
304
+ width: 180,
305
+ height: 150,
306
+ style: { display: "block", borderRadius: 4, cursor: "crosshair", marginBottom: 8 },
307
+ onMouseDown: (e) => {
308
+ svDrag.current = true;
309
+ updateSV(e);
310
+ }
311
+ }
312
+ ),
313
+ /* @__PURE__ */ jsx(
314
+ "canvas",
315
+ {
316
+ ref: hueRef,
317
+ width: 180,
318
+ height: 14,
319
+ style: { display: "block", borderRadius: 4, cursor: "ew-resize", marginBottom: 10 },
320
+ onMouseDown: (e) => {
321
+ hueDrag.current = true;
322
+ updateHue(e);
323
+ }
324
+ }
325
+ ),
326
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }, children: [
327
+ /* @__PURE__ */ jsx("div", { style: { width: 28, height: 28, borderRadius: 4, background: currentHex, border: "1px solid rgba(0,0,0,0.15)", flexShrink: 0 } }),
328
+ /* @__PURE__ */ jsx(
329
+ "input",
330
+ {
331
+ value: hexInput,
332
+ onChange: (e) => {
333
+ setHexInput(e.target.value);
334
+ if (/^#[0-9a-fA-F]{6}$/.test(e.target.value)) setHsv(hex2hsv(e.target.value));
335
+ },
336
+ style: { flex: 1, height: 28, border: "1px solid #ccc", borderRadius: 4, padding: "0 8px", fontSize: 12, fontFamily: "var(--font-mono)", outline: "none" },
337
+ spellCheck: false,
338
+ placeholder: "#000000"
339
+ }
340
+ )
341
+ ] }),
342
+ /* @__PURE__ */ jsx(
343
+ "button",
344
+ {
345
+ onMouseDown: () => onApply(currentHex),
346
+ style: { width: "100%", height: 28, background: "#1a6fc4", color: "#fff", border: "none", borderRadius: 4, fontSize: 12, fontWeight: 600, cursor: "pointer" },
347
+ children: "Apply"
348
+ }
349
+ )
350
+ ] });
351
+ }
352
+ function ColorPicker({ color, onColor, onRemove, onClose }) {
353
+ const [custom, setCustom] = useState(false);
354
+ const pick = (c) => {
355
+ onColor(c);
356
+ onClose();
357
+ };
358
+ return /* @__PURE__ */ jsx("div", { className: "rte-cpick", children: custom ? /* @__PURE__ */ jsx(CustomColorPicker, { initialColor: color, onApply: pick, onBack: () => setCustom(false) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
359
+ /* @__PURE__ */ jsx("div", { className: "rte-cgrid", children: PALETTE.map((hex) => /* @__PURE__ */ jsx(
360
+ "div",
361
+ {
362
+ className: `rte-ccell${color.toLowerCase() === hex.toLowerCase() ? " sel" : ""}`,
363
+ style: { background: hex },
364
+ title: hex,
365
+ onMouseDown: () => pick(hex)
366
+ },
367
+ hex
368
+ )) }),
369
+ /* @__PURE__ */ jsxs("div", { className: "rte-cactions", children: [
370
+ /* @__PURE__ */ jsx("div", { className: "rte-caction", title: "Black", onMouseDown: () => pick("#000000"), children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
371
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", fill: "#000" }),
372
+ /* @__PURE__ */ jsx("path", { d: "M5 8L7 10L11 6", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
373
+ ] }) }),
374
+ /* @__PURE__ */ jsx("div", { className: "rte-caction", title: "Remove color", onMouseDown: () => {
375
+ onRemove();
376
+ onClose();
377
+ }, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
378
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "7.5", width: "12", height: "1.2", rx: ".6", fill: "#e74c3c", transform: "rotate(-30 8 8)" }),
379
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "11", width: "14", height: "2", rx: "1", fill: "#e74c3c" })
380
+ ] }) }),
381
+ /* @__PURE__ */ jsx("div", { className: "rte-caction", title: "Custom color", onMouseDown: () => setCustom(true), children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
382
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6", stroke: "#888", strokeWidth: "1.2", fill: "none" }),
383
+ /* @__PURE__ */ jsx("circle", { cx: "5.5", cy: "6", r: "1.5", fill: "#e74c3c" }),
384
+ /* @__PURE__ */ jsx("circle", { cx: "10.5", cy: "6", r: "1.5", fill: "#3498db" }),
385
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "10.5", r: "1.5", fill: "#2ecc71" })
386
+ ] }) })
387
+ ] })
388
+ ] }) });
389
+ }
160
390
  function TablePicker({ onInsert, onClose }) {
161
391
  const [hover, setHover] = useState([0, 0]);
162
- return /* @__PURE__ */ jsxs("div", { className: "rte-tp", children: [
163
- /* @__PURE__ */ jsx("div", { className: "rte-tp-lbl", children: hover[0] > 0 ? `${hover[0]} \xD7 ${hover[1]}` : "Select table size" }),
164
- /* @__PURE__ */ jsx("div", { className: "rte-tp-grid", children: Array.from({ length: 64 }, (_, i) => {
392
+ return /* @__PURE__ */ jsxs("div", { className: "rte-tpick", children: [
393
+ /* @__PURE__ */ jsx("div", { className: "rte-tplbl", children: hover[0] > 0 ? `${hover[0]} \xD7 ${hover[1]}` : "Select table size" }),
394
+ /* @__PURE__ */ jsx("div", { className: "rte-tpgrid", children: Array.from({ length: 64 }, (_, i) => {
165
395
  const r = Math.floor(i / 8) + 1, c = i % 8 + 1;
166
396
  return /* @__PURE__ */ jsx(
167
397
  "div",
168
398
  {
169
- className: `rte-tp-cell${r <= hover[0] && c <= hover[1] ? " on" : ""}`,
399
+ className: `rte-tpcell${r <= hover[0] && c <= hover[1] ? " on" : ""}`,
170
400
  onMouseEnter: () => setHover([r, c]),
171
401
  onMouseDown: (e) => {
172
402
  e.preventDefault();
@@ -181,6 +411,124 @@ function TablePicker({ onInsert, onClose }) {
181
411
  }) })
182
412
  ] });
183
413
  }
414
+ function ImagePicker({ onInsert, onClose }) {
415
+ const [tab, setTab] = useState("upload");
416
+ const [url, setUrl] = useState("");
417
+ const [preview, setPreview] = useState(null);
418
+ const [dragging, setDragging] = useState(false);
419
+ const [urlError, setUrlError] = useState(false);
420
+ const fileRef = useRef(null);
421
+ const readFile = (file) => {
422
+ if (!file.type.startsWith("image/")) return;
423
+ const reader = new FileReader();
424
+ reader.onload = () => setPreview(reader.result);
425
+ reader.readAsDataURL(file);
426
+ };
427
+ const canInsert = tab === "upload" ? !!preview : !!url.trim() && !urlError;
428
+ return /* @__PURE__ */ jsxs("div", { className: "rte-ipick", children: [
429
+ /* @__PURE__ */ jsx("div", { className: "rte-itabs", children: ["upload", "url"].map((t) => /* @__PURE__ */ jsx("button", { className: `rte-itab${tab === t ? " active" : ""}`, onMouseDown: () => setTab(t), children: t === "upload" ? "Upload / Drop" : "Image URL" }, t)) }),
430
+ /* @__PURE__ */ jsxs("div", { className: "rte-ipbody", children: [
431
+ tab === "upload" && /* @__PURE__ */ jsxs(Fragment, { children: [
432
+ /* @__PURE__ */ jsx(
433
+ "div",
434
+ {
435
+ className: `rte-idrop${dragging ? " drag" : ""}`,
436
+ onDragOver: (e) => {
437
+ e.preventDefault();
438
+ setDragging(true);
439
+ },
440
+ onDragLeave: () => setDragging(false),
441
+ onDrop: (e) => {
442
+ e.preventDefault();
443
+ setDragging(false);
444
+ const f = e.dataTransfer.files[0];
445
+ if (f) readFile(f);
446
+ },
447
+ onMouseDown: () => {
448
+ var _a;
449
+ return (_a = fileRef.current) == null ? void 0 : _a.click();
450
+ },
451
+ children: preview ? /* @__PURE__ */ jsx("img", { src: preview, alt: "preview", style: { maxWidth: "100%", maxHeight: 130, objectFit: "contain", borderRadius: 4, border: "1px solid #eee" } }) : /* @__PURE__ */ jsxs(Fragment, { children: [
452
+ /* @__PURE__ */ jsxs("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", children: [
453
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "4", width: "26", height: "20", rx: "3", stroke: "#aaa", strokeWidth: "1.5" }),
454
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "10", r: "2.5", stroke: "#aaa", strokeWidth: "1.5" }),
455
+ /* @__PURE__ */ jsx("path", { d: "M1 20L9 13L14 18L19 13.5L27 21", stroke: "#aaa", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
456
+ ] }),
457
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: "#888" }, children: [
458
+ "Drop image or ",
459
+ /* @__PURE__ */ jsx("span", { style: { color: "#1a5fb4", textDecoration: "underline" }, children: "browse" })
460
+ ] }),
461
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "#bbb" }, children: "PNG, JPG, GIF, WEBP" })
462
+ ] })
463
+ }
464
+ ),
465
+ /* @__PURE__ */ jsx(
466
+ "input",
467
+ {
468
+ ref: fileRef,
469
+ type: "file",
470
+ accept: "image/*",
471
+ style: { display: "none" },
472
+ onChange: (e) => {
473
+ var _a;
474
+ const f = (_a = e.target.files) == null ? void 0 : _a[0];
475
+ if (f) readFile(f);
476
+ e.target.value = "";
477
+ }
478
+ }
479
+ ),
480
+ preview && /* @__PURE__ */ jsx(
481
+ "button",
482
+ {
483
+ onMouseDown: () => setPreview(null),
484
+ style: { fontSize: 11, color: "#e74c3c", background: "none", border: "none", cursor: "pointer", padding: 0, alignSelf: "flex-start" },
485
+ children: "\u2715 Remove"
486
+ }
487
+ )
488
+ ] }),
489
+ tab === "url" && /* @__PURE__ */ jsxs(Fragment, { children: [
490
+ /* @__PURE__ */ jsx(
491
+ "input",
492
+ {
493
+ type: "text",
494
+ value: url,
495
+ onChange: (e) => {
496
+ setUrl(e.target.value);
497
+ setUrlError(false);
498
+ },
499
+ placeholder: "https://example.com/image.png",
500
+ style: { width: "100%", height: 30, border: `1px solid ${urlError ? "#e74c3c" : "#ccc"}`, borderRadius: 4, padding: "0 8px", fontSize: 12, outline: "none", fontFamily: "var(--font-sans)" }
501
+ }
502
+ ),
503
+ url && /* @__PURE__ */ jsx(
504
+ "img",
505
+ {
506
+ src: url,
507
+ alt: "preview",
508
+ style: { maxWidth: "100%", maxHeight: 130, objectFit: "contain", borderRadius: 4, border: "1px solid #eee", display: "block" },
509
+ onError: () => setUrlError(true)
510
+ }
511
+ ),
512
+ urlError && /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "#e74c3c" }, children: "Could not load image from this URL." })
513
+ ] }),
514
+ /* @__PURE__ */ jsx(
515
+ "button",
516
+ {
517
+ className: "rte-iinsert",
518
+ disabled: !canInsert,
519
+ onMouseDown: () => {
520
+ const src = tab === "url" ? url.trim() : preview;
521
+ if (src) {
522
+ onInsert(src);
523
+ onClose();
524
+ }
525
+ },
526
+ children: "Insert Image"
527
+ }
528
+ )
529
+ ] })
530
+ ] });
531
+ }
184
532
  function IcoUL() {
185
533
  return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
186
534
  /* @__PURE__ */ jsx("circle", { cx: "2", cy: "4", r: "1.3", fill: "currentColor" }),
@@ -245,6 +593,13 @@ function IcoTable() {
245
593
  /* @__PURE__ */ jsx("line", { x1: "10", y1: "5.5", x2: "10", y2: "14", stroke: "currentColor", strokeWidth: "1.1" })
246
594
  ] });
247
595
  }
596
+ function IcoImage() {
597
+ return /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 15 15", fill: "none", children: [
598
+ /* @__PURE__ */ jsx("rect", { x: "1", y: "2", width: "13", height: "11", rx: "1.5", stroke: "currentColor", strokeWidth: "1.2" }),
599
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "5.5", r: "1.2", fill: "currentColor" }),
600
+ /* @__PURE__ */ jsx("path", { d: "M1 10L4.5 7L7 9.5L9.5 7L14 11", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
601
+ ] });
602
+ }
248
603
  function IcoRowAbove() {
249
604
  return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
250
605
  /* @__PURE__ */ jsx("rect", { x: "1", y: "7", width: "14", height: "8", rx: "1", stroke: "currentColor", strokeWidth: "1.1" }),
@@ -322,10 +677,15 @@ function getCurrentTable() {
322
677
  var _a;
323
678
  return (_a = getCurrentCell()) == null ? void 0 : _a.closest("table");
324
679
  }
680
+ function getCurrentLink() {
681
+ var _a;
682
+ const node = (_a = window.getSelection()) == null ? void 0 : _a.anchorNode;
683
+ const el = (node == null ? void 0 : node.nodeType) === 1 ? node : node == null ? void 0 : node.parentElement;
684
+ return el == null ? void 0 : el.closest("a");
685
+ }
325
686
  function getCellCoords(table, cell) {
326
- const rows = Array.from(table.rows);
327
- for (let r = 0; r < rows.length; r++) {
328
- const cells = Array.from(rows[r].cells);
687
+ for (let r = 0; r < table.rows.length; r++) {
688
+ const cells = Array.from(table.rows[r].cells);
329
689
  for (let c = 0; c < cells.length; c++) {
330
690
  if (cells[c] === cell) return [r, c];
331
691
  }
@@ -339,9 +699,10 @@ function getCellsInRange(table, a, b) {
339
699
  const [r2, c2] = [Math.max(ca[0], cb[0]), Math.max(ca[1], cb[1])];
340
700
  const result = [];
341
701
  Array.from(table.rows).forEach((row, ri) => {
342
- if (ri >= r1 && ri <= r2) Array.from(row.cells).forEach((cell, ci) => {
343
- if (ci >= c1 && ci <= c2) result.push(cell);
344
- });
702
+ if (ri >= r1 && ri <= r2)
703
+ Array.from(row.cells).forEach((cell, ci) => {
704
+ if (ci >= c1 && ci <= c2) result.push(cell);
705
+ });
345
706
  });
346
707
  return result;
347
708
  }
@@ -349,30 +710,32 @@ function RichTextEditor({ value, onChange }) {
349
710
  var _a;
350
711
  const editorRef = useRef(null);
351
712
  const editorAreaRef = useRef(null);
352
- const colorRef = useRef(null);
353
713
  const savedRangeRef = useRef(null);
354
714
  const dragStartCell = useRef(null);
355
715
  const isDragging = useRef(false);
356
716
  const [isCode, setIsCode] = useState(false);
357
717
  const [codeVal, setCodeVal] = useState("");
358
718
  const [fmt, setFmt] = useState({ block: "p" });
359
- const [color, setColor] = useState("#e74c3c");
719
+ const [color, setColor] = useState("#000000");
360
720
  const [words, setWords] = useState(0);
361
721
  const [linkBar, setLinkBar] = useState(false);
362
722
  const [linkUrl, setLinkUrl] = useState("https://");
363
723
  const [showTable, setShowTable] = useState(false);
724
+ const [showColor, setShowColor] = useState(false);
725
+ const [showImagePicker, setShowImagePicker] = useState(false);
364
726
  const [selCells, setSelCells] = useState([]);
365
727
  const [tableFP, setTableFP] = useState(null);
366
728
  const [linkFP, setLinkFP] = useState(null);
367
- const calcFloat = useCallback((el, toolbarW) => {
729
+ const [linkInfoFP, setLinkInfoFP] = useState(null);
730
+ const [selectedImg, setSelectedImg] = useState(null);
731
+ const [imgFP, setImgFP] = useState(null);
732
+ const calcFloat = useCallback((el, w) => {
368
733
  const area = editorAreaRef.current;
369
- const er = el.getBoundingClientRect();
370
- const ar = area.getBoundingClientRect();
371
- const arrowCenter = er.left + er.width / 2 - ar.left;
372
- const rawLeft = arrowCenter - toolbarW / 2;
373
- const left = Math.max(4, Math.min(rawLeft, ar.width - toolbarW - 4));
374
- const arrowLeft = Math.max(8, Math.min(arrowCenter - left - 7, toolbarW - 22));
375
- return { top: er.bottom - ar.top + 10, left, arrowLeft };
734
+ const er = el.getBoundingClientRect(), ar = area.getBoundingClientRect();
735
+ const ac = er.left + er.width / 2 - ar.left;
736
+ const rawL = ac - w / 2;
737
+ const left = Math.max(4, Math.min(rawL, ar.width - w - 4));
738
+ return { top: er.bottom - ar.top + 10, left, arrowLeft: Math.max(8, Math.min(ac - left - 7, w - 22)) };
376
739
  }, []);
377
740
  const applySelection = useCallback((cells) => {
378
741
  var _a2;
@@ -386,35 +749,44 @@ function RichTextEditor({ value, onChange }) {
386
749
  setSelCells([]);
387
750
  dragStartCell.current = null;
388
751
  }, []);
752
+ const selectImage = useCallback((img) => {
753
+ var _a2;
754
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("img.rte-img-sel").forEach((i) => i.classList.remove("rte-img-sel"));
755
+ img.classList.add("rte-img-sel");
756
+ setSelectedImg(img);
757
+ if (editorAreaRef.current) setImgFP(calcFloat(img, 260));
758
+ }, [calcFloat]);
759
+ const clearImageSelection = useCallback(() => {
760
+ var _a2;
761
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("img.rte-img-sel").forEach((i) => i.classList.remove("rte-img-sel"));
762
+ setSelectedImg(null);
763
+ setImgFP(null);
764
+ }, []);
765
+ const fixFontTags = useCallback(() => {
766
+ var _a2;
767
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("font[color]").forEach((font) => {
768
+ var _a3;
769
+ const span = document.createElement("span");
770
+ span.style.color = (_a3 = font.getAttribute("color")) != null ? _a3 : "";
771
+ while (font.firstChild) span.appendChild(font.firstChild);
772
+ font.replaceWith(span);
773
+ });
774
+ }, []);
389
775
  const fixListNesting = useCallback(() => {
390
- const editor = editorRef.current;
391
- if (!editor) return;
392
- editor.querySelectorAll("ol > ol, ol > ul, ul > ol, ul > ul").forEach((orphan) => {
776
+ var _a2;
777
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("ol > ol, ol > ul, ul > ol, ul > ul").forEach((orphan) => {
393
778
  const prev = orphan.previousElementSibling;
394
- if (prev && prev.tagName === "LI") {
395
- prev.appendChild(orphan);
396
- }
779
+ if ((prev == null ? void 0 : prev.tagName) === "LI") prev.appendChild(orphan);
397
780
  });
398
781
  }, []);
399
- const exec = useCallback((cmd, val = null) => {
400
- var _a2;
401
- (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
402
- document.execCommand(cmd, false, val != null ? val : void 0);
403
- fixListNesting();
404
- refresh();
405
- }, [fixListNesting]);
406
782
  const refresh = useCallback(() => {
407
783
  var _a2, _b, _c, _d;
408
784
  const raw = document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g, "");
409
785
  const block = ["h1", "h2", "h3"].includes(raw) ? raw : "p";
410
- const c = document.queryCommandValue("foreColor");
411
- if (c && c !== "false") {
412
- const m = c.match(/\d+/g);
413
- if (m) setColor("#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join(""));
414
- }
415
- const cell = getCurrentCell();
416
- const inTable = !!cell;
786
+ setColor(getColorAtCursor(editorRef.current));
787
+ const cell = getCurrentCell(), inTable = !!cell;
417
788
  const cellMerged = cell ? cell.colSpan > 1 || cell.rowSpan > 1 : false;
789
+ const anchor = getCurrentLink(), inLink = !!anchor;
418
790
  setFmt({
419
791
  bold: document.queryCommandState("bold"),
420
792
  italic: document.queryCommandState("italic"),
@@ -427,15 +799,39 @@ function RichTextEditor({ value, onChange }) {
427
799
  aJ: document.queryCommandState("justifyFull"),
428
800
  block,
429
801
  inTable,
430
- cellMerged
802
+ cellMerged,
803
+ inLink,
804
+ linkHref: anchor == null ? void 0 : anchor.href
431
805
  });
432
806
  if (cell && editorAreaRef.current) setTableFP(calcFloat(cell, 370));
433
807
  else setTableFP(null);
808
+ if (anchor && editorAreaRef.current && !linkBar) setLinkInfoFP(calcFloat(anchor, 290));
809
+ else setLinkInfoFP(null);
434
810
  const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
435
811
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
436
812
  onChange == null ? void 0 : onChange((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : "");
437
- }, [onChange, calcFloat]);
813
+ }, [onChange, calcFloat, linkBar]);
814
+ const exec = useCallback((cmd, val = null) => {
815
+ var _a2;
816
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
817
+ document.execCommand(cmd, false, val != null ? val : void 0);
818
+ fixFontTags();
819
+ fixListNesting();
820
+ refresh();
821
+ }, [fixFontTags, fixListNesting, refresh]);
822
+ const insertImage = useCallback((src) => {
823
+ var _a2;
824
+ (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
825
+ document.execCommand("insertHTML", false, `<img src="${src}" alt="" style="max-width:100%;height:auto;" />`);
826
+ refresh();
827
+ }, [refresh]);
438
828
  const handleEditorMouseDown = useCallback((e) => {
829
+ if (e.target.tagName === "IMG") {
830
+ e.preventDefault();
831
+ selectImage(e.target);
832
+ return;
833
+ }
834
+ clearImageSelection();
439
835
  const target = e.target.closest("td, th");
440
836
  if (!target) {
441
837
  clearSelection();
@@ -452,7 +848,7 @@ function RichTextEditor({ value, onChange }) {
452
848
  dragStartCell.current = target;
453
849
  isDragging.current = true;
454
850
  applySelection([target]);
455
- }, [applySelection, clearSelection]);
851
+ }, [applySelection, clearSelection, selectImage, clearImageSelection]);
456
852
  const handleEditorMouseMove = useCallback((e) => {
457
853
  if (!isDragging.current || !dragStartCell.current) return;
458
854
  const target = e.target.closest("td, th");
@@ -465,12 +861,10 @@ function RichTextEditor({ value, onChange }) {
465
861
  applySelection(cells);
466
862
  }
467
863
  }, [applySelection]);
468
- const handleEditorMouseUp = useCallback(() => {
469
- isDragging.current = false;
470
- }, []);
471
864
  const handleKeyDown = useCallback((e) => {
472
865
  var _a2, _b;
473
866
  clearSelection();
867
+ clearImageSelection();
474
868
  if (e.key === "Tab") {
475
869
  e.preventDefault();
476
870
  const cell = getCurrentCell();
@@ -502,14 +896,14 @@ function RichTextEditor({ value, onChange }) {
502
896
  exec("underline");
503
897
  }
504
898
  }
505
- }, [exec, clearSelection]);
899
+ }, [exec, clearSelection, clearImageSelection]);
506
900
  const insertTable = (rows, cols) => {
507
901
  var _a2;
508
902
  (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
509
- let html = `<table>`;
903
+ let html = "<table>";
510
904
  for (let r = 0; r < rows; r++) {
511
905
  html += "<tr>";
512
- for (let c = 0; c < cols; c++) html += r === 0 ? `<th><br></th>` : `<td><br></td>`;
906
+ for (let c = 0; c < cols; c++) html += r === 0 ? "<th><br></th>" : "<td><br></td>";
513
907
  html += "</tr>";
514
908
  }
515
909
  html += "</table><p><br></p>";
@@ -604,11 +998,7 @@ function RichTextEditor({ value, onChange }) {
604
998
  savedRangeRef.current = (sel == null ? void 0 : sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
605
999
  if ((sel == null ? void 0 : sel.rangeCount) && editorAreaRef.current) {
606
1000
  const rect = sel.getRangeAt(0).getBoundingClientRect();
607
- if (rect.width > 0 || rect.height > 0) setLinkFP(calcFloat({ getBoundingClientRect: () => rect }, 360));
608
- else {
609
- const area = editorAreaRef.current.getBoundingClientRect();
610
- setLinkFP({ top: 60, left: 20, arrowLeft: 20 });
611
- }
1001
+ setLinkFP(rect.width > 0 || rect.height > 0 ? calcFloat({ getBoundingClientRect: () => rect }, 360) : { top: 60, left: 20, arrowLeft: 20 });
612
1002
  }
613
1003
  setLinkUrl("https://");
614
1004
  setLinkBar(true);
@@ -712,7 +1102,7 @@ function RichTextEditor({ value, onChange }) {
712
1102
  };
713
1103
  const copyHtml = () => {
714
1104
  var _a2, _b;
715
- navigator.clipboard.writeText(isCode ? codeVal : (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : "").catch(() => {
1105
+ return navigator.clipboard.writeText(isCode ? codeVal : (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b : "").catch(() => {
716
1106
  });
717
1107
  };
718
1108
  useEffect(() => {
@@ -721,14 +1111,8 @@ function RichTextEditor({ value, onChange }) {
721
1111
  refresh();
722
1112
  }
723
1113
  }, []);
724
- useEffect(() => {
725
- if (!showTable) return;
726
- const h = () => setShowTable(false);
727
- document.addEventListener("mousedown", h);
728
- return () => document.removeEventListener("mousedown", h);
729
- }, [showTable]);
730
- const canMerge = selCells.length >= 2;
731
- const canSplit = fmt.cellMerged;
1114
+ const canMerge = selCells.length >= 2, canSplit = fmt.cellMerged;
1115
+ const backdrop = { position: "fixed", inset: 0, zIndex: 199 };
732
1116
  return /* @__PURE__ */ jsxs("div", { style: { padding: "1rem 0" }, children: [
733
1117
  /* @__PURE__ */ jsx("style", { children: CSS }),
734
1118
  /* @__PURE__ */ jsxs("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
@@ -737,26 +1121,29 @@ function RichTextEditor({ value, onChange }) {
737
1121
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
738
1122
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
739
1123
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
740
- /* @__PURE__ */ jsxs(Btn, { onClick: () => {
741
- var _a2;
742
- return (_a2 = colorRef.current) == null ? void 0 : _a2.click();
743
- }, title: "Text color", style: { flexDirection: "column", gap: 1, padding: "3px 6px" }, children: [
744
- /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
745
- /* @__PURE__ */ jsx("span", { className: "rte-swatch", style: { background: color } })
1124
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", display: "inline-flex" }, children: [
1125
+ showColor && /* @__PURE__ */ jsx("div", { style: backdrop, onMouseDown: () => setShowColor(false) }),
1126
+ /* @__PURE__ */ jsxs(Btn, { onClick: () => setShowColor((v) => !v), title: "Text color", active: showColor, style: { flexDirection: "column", gap: 1, padding: "3px 6px" }, children: [
1127
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
1128
+ /* @__PURE__ */ jsx("span", { className: "rte-swatch", style: { background: color } })
1129
+ ] }),
1130
+ showColor && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 34, left: -4, zIndex: 200 }, children: /* @__PURE__ */ jsx(
1131
+ ColorPicker,
1132
+ {
1133
+ color,
1134
+ onColor: (c) => {
1135
+ setColor(c);
1136
+ exec("foreColor", c);
1137
+ setShowColor(false);
1138
+ },
1139
+ onRemove: () => {
1140
+ exec("removeFormat");
1141
+ setShowColor(false);
1142
+ },
1143
+ onClose: () => setShowColor(false)
1144
+ }
1145
+ ) })
746
1146
  ] }),
747
- /* @__PURE__ */ jsx(
748
- "input",
749
- {
750
- ref: colorRef,
751
- type: "color",
752
- value: color,
753
- onChange: (e) => {
754
- setColor(e.target.value);
755
- exec("foreColor", e.target.value);
756
- },
757
- style: { position: "absolute", opacity: 0, width: 0, height: 0, pointerEvents: "none" }
758
- }
759
- ),
760
1147
  /* @__PURE__ */ jsx(Sep, {}),
761
1148
  /* @__PURE__ */ jsxs(
762
1149
  "select",
@@ -794,12 +1181,21 @@ function RichTextEditor({ value, onChange }) {
794
1181
  /* @__PURE__ */ jsx(Sep, {}),
795
1182
  /* @__PURE__ */ jsx(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ jsx(IcoLink, {}) }),
796
1183
  /* @__PURE__ */ jsx(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ jsx(IcoCode, {}) }),
797
- /* @__PURE__ */ jsxs("div", { className: "rte-tp-wrap", onMouseDown: (e) => e.stopPropagation(), children: [
1184
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", display: "inline-flex" }, children: [
1185
+ showImagePicker && /* @__PURE__ */ jsx("div", { style: backdrop, onMouseDown: () => setShowImagePicker(false) }),
1186
+ /* @__PURE__ */ jsx(Btn, { onClick: () => setShowImagePicker((v) => !v), title: "Insert image", active: showImagePicker, children: /* @__PURE__ */ jsx(IcoImage, {}) }),
1187
+ showImagePicker && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ jsx(ImagePicker, { onInsert: (src) => {
1188
+ insertImage(src);
1189
+ setShowImagePicker(false);
1190
+ }, onClose: () => setShowImagePicker(false) }) })
1191
+ ] }),
1192
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", display: "inline-flex" }, children: [
1193
+ showTable && /* @__PURE__ */ jsx("div", { style: backdrop, onMouseDown: () => setShowTable(false) }),
798
1194
  /* @__PURE__ */ jsx(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ jsx(IcoTable, {}) }),
799
- showTable && /* @__PURE__ */ jsx(TablePicker, { onInsert: (r, c) => {
1195
+ showTable && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 34, left: 0, zIndex: 200 }, children: /* @__PURE__ */ jsx(TablePicker, { onInsert: (r, c) => {
800
1196
  insertTable(r, c);
801
1197
  setShowTable(false);
802
- }, onClose: () => setShowTable(false) })
1198
+ }, onClose: () => setShowTable(false) }) })
803
1199
  ] }),
804
1200
  /* @__PURE__ */ jsx(Sep, {})
805
1201
  ] }),
@@ -842,29 +1238,120 @@ function RichTextEditor({ value, onChange }) {
842
1238
  "data-ph": "Start typing...",
843
1239
  onMouseDown: handleEditorMouseDown,
844
1240
  onMouseMove: handleEditorMouseMove,
845
- onMouseUp: handleEditorMouseUp,
1241
+ onMouseUp: () => {
1242
+ isDragging.current = false;
1243
+ },
846
1244
  onKeyDown: handleKeyDown,
847
1245
  onKeyUp: refresh,
848
1246
  onSelect: refresh,
1247
+ onDrop: (e) => {
1248
+ var _a2;
1249
+ const file = (_a2 = e.dataTransfer.files) == null ? void 0 : _a2[0];
1250
+ if (!(file == null ? void 0 : file.type.startsWith("image/"))) return;
1251
+ e.preventDefault();
1252
+ const reader = new FileReader();
1253
+ reader.onload = () => insertImage(reader.result);
1254
+ reader.readAsDataURL(file);
1255
+ },
1256
+ onDragOver: (e) => {
1257
+ if (e.dataTransfer.types.includes("Files")) e.preventDefault();
1258
+ },
849
1259
  style: { display: isCode ? "none" : "block" }
850
1260
  }
851
1261
  ),
1262
+ !isCode && selectedImg && imgFP && /* @__PURE__ */ jsxs("div", { className: "rte-float", style: { top: imgFP.top, left: imgFP.left, gap: 2 }, children: [
1263
+ /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: imgFP.arrowLeft } }),
1264
+ /* @__PURE__ */ jsx(Btn, { title: "Align left", onClick: () => {
1265
+ selectedImg.style.marginLeft = "0";
1266
+ selectedImg.style.marginRight = "auto";
1267
+ selectImage(selectedImg);
1268
+ }, children: /* @__PURE__ */ jsx(AlignIco, { t: "left" }) }),
1269
+ /* @__PURE__ */ jsx(Btn, { title: "Align center", onClick: () => {
1270
+ selectedImg.style.marginLeft = "auto";
1271
+ selectedImg.style.marginRight = "auto";
1272
+ selectImage(selectedImg);
1273
+ }, children: /* @__PURE__ */ jsx(AlignIco, { t: "center" }) }),
1274
+ /* @__PURE__ */ jsx(Btn, { title: "Align right", onClick: () => {
1275
+ selectedImg.style.marginLeft = "auto";
1276
+ selectedImg.style.marginRight = "0";
1277
+ selectImage(selectedImg);
1278
+ }, children: /* @__PURE__ */ jsx(AlignIco, { t: "right" }) }),
1279
+ /* @__PURE__ */ jsx(Sep, {}),
1280
+ [25, 50, 75, 100].map((pct) => /* @__PURE__ */ jsxs(
1281
+ Btn,
1282
+ {
1283
+ title: `Width ${pct}%`,
1284
+ onClick: () => {
1285
+ selectedImg.style.width = `${pct}%`;
1286
+ selectImage(selectedImg);
1287
+ },
1288
+ style: { fontSize: 10, minWidth: 28, padding: "0 4px" },
1289
+ children: [
1290
+ pct,
1291
+ "%"
1292
+ ]
1293
+ },
1294
+ pct
1295
+ )),
1296
+ /* @__PURE__ */ jsx(Sep, {}),
1297
+ /* @__PURE__ */ jsx(Btn, { title: "Delete image", danger: true, onClick: () => {
1298
+ selectedImg.remove();
1299
+ clearImageSelection();
1300
+ refresh();
1301
+ }, children: /* @__PURE__ */ jsx("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 2L11 11M11 2L2 11", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round" }) }) })
1302
+ ] }),
852
1303
  !isCode && fmt.inTable && tableFP && /* @__PURE__ */ jsxs("div", { className: "rte-float", style: { top: tableFP.top, left: tableFP.left }, children: [
853
1304
  /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: tableFP.arrowLeft } }),
854
1305
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowAbove"), title: "Add row above", children: /* @__PURE__ */ jsx(IcoRowAbove, {}) }),
855
1306
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowBelow"), title: "Add row below", children: /* @__PURE__ */ jsx(IcoRowBelow, {}) }),
856
1307
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteRow"), title: "Delete row", danger: true, children: /* @__PURE__ */ jsx(IcoDelRow, {}) }),
857
1308
  /* @__PURE__ */ jsx(Sep, {}),
858
- /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColLeft"), title: "Add column left", children: /* @__PURE__ */ jsx(IcoColLeft, {}) }),
859
- /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColRight"), title: "Add column right", children: /* @__PURE__ */ jsx(IcoColRight, {}) }),
860
- /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteCol"), title: "Delete column", danger: true, children: /* @__PURE__ */ jsx(IcoDelCol, {}) }),
1309
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColLeft"), title: "Add col left", children: /* @__PURE__ */ jsx(IcoColLeft, {}) }),
1310
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColRight"), title: "Add col right", children: /* @__PURE__ */ jsx(IcoColRight, {}) }),
1311
+ /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteCol"), title: "Delete col", danger: true, children: /* @__PURE__ */ jsx(IcoDelCol, {}) }),
861
1312
  /* @__PURE__ */ jsx(Sep, {}),
862
1313
  canMerge && /* @__PURE__ */ jsx(Btn, { onClick: () => doMerge(selCells), title: `Merge ${selCells.length} cells`, children: /* @__PURE__ */ jsx(IcoMerge, {}) }),
863
1314
  canSplit && /* @__PURE__ */ jsx(Btn, { onClick: doSplit, title: "Split merged cell", children: /* @__PURE__ */ jsx(IcoSplit, {}) }),
864
1315
  (canMerge || canSplit) && /* @__PURE__ */ jsx(Sep, {}),
865
1316
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteTable"), title: "Delete table", danger: true, children: /* @__PURE__ */ jsx(IcoDelTable, {}) })
866
1317
  ] }),
867
- !isCode && linkBar && linkFP && /* @__PURE__ */ jsxs("div", { className: "rte-float rte-link-float", style: { top: linkFP.top, left: linkFP.left }, children: [
1318
+ !isCode && fmt.inLink && linkInfoFP && !linkBar && /* @__PURE__ */ jsxs("div", { className: "rte-float", style: { top: linkInfoFP.top, left: linkInfoFP.left, gap: 4 }, children: [
1319
+ /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: linkInfoFP.arrowLeft } }),
1320
+ /* @__PURE__ */ jsx(IcoLink, {}),
1321
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "#1a6fc4", maxWidth: 160, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, title: fmt.linkHref, children: fmt.linkHref }),
1322
+ /* @__PURE__ */ jsx(Sep, {}),
1323
+ /* @__PURE__ */ jsx(Btn, { title: "Edit link", onClick: () => {
1324
+ var _a2;
1325
+ const anchor = getCurrentLink();
1326
+ if (!anchor) return;
1327
+ const sel = window.getSelection();
1328
+ const range = document.createRange();
1329
+ range.selectNodeContents(anchor);
1330
+ sel == null ? void 0 : sel.removeAllRanges();
1331
+ sel == null ? void 0 : sel.addRange(range);
1332
+ savedRangeRef.current = range.cloneRange();
1333
+ if (editorAreaRef.current) setLinkFP(calcFloat(anchor, 360));
1334
+ setLinkUrl((_a2 = anchor.getAttribute("href")) != null ? _a2 : "https://");
1335
+ setLinkBar(true);
1336
+ setLinkInfoFP(null);
1337
+ }, children: /* @__PURE__ */ jsx("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M9 2L11 4L4.5 10.5H2.5V8.5L9 2Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }) }),
1338
+ /* @__PURE__ */ jsx(Btn, { title: "Remove link", danger: true, onClick: () => {
1339
+ const anchor = getCurrentLink();
1340
+ if (!anchor) return;
1341
+ const sel = window.getSelection();
1342
+ const range = document.createRange();
1343
+ range.selectNodeContents(anchor);
1344
+ sel == null ? void 0 : sel.removeAllRanges();
1345
+ sel == null ? void 0 : sel.addRange(range);
1346
+ document.execCommand("unlink", false);
1347
+ refresh();
1348
+ }, children: /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", children: [
1349
+ /* @__PURE__ */ 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" }),
1350
+ /* @__PURE__ */ 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" }),
1351
+ /* @__PURE__ */ jsx("path", { d: "M2 2L11 11", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })
1352
+ ] }) })
1353
+ ] }),
1354
+ !isCode && linkBar && linkFP && /* @__PURE__ */ jsxs("div", { className: "rte-float rte-linkbar", style: { top: linkFP.top, left: linkFP.left }, children: [
868
1355
  /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: linkFP.arrowLeft } }),
869
1356
  /* @__PURE__ */ jsx(IcoLink, {}),
870
1357
  /* @__PURE__ */ jsx(
@@ -884,11 +1371,11 @@ function RichTextEditor({ value, onChange }) {
884
1371
  placeholder: "https://example.com"
885
1372
  }
886
1373
  ),
887
- /* @__PURE__ */ jsx("button", { className: "rte-link-apply", onClick: applyLink, children: "Apply" }),
888
- /* @__PURE__ */ jsx("button", { className: "rte-link-cancel", onClick: () => {
1374
+ /* @__PURE__ */ jsx("button", { onClick: applyLink, style: { height: 26, padding: "0 10px", background: "#1a6fc4", color: "#fff", border: "none", borderRadius: 3, fontSize: 12, fontWeight: 600, cursor: "pointer" }, children: "Apply" }),
1375
+ /* @__PURE__ */ jsx("button", { onClick: () => {
889
1376
  setLinkBar(false);
890
1377
  setLinkFP(null);
891
- }, children: "\u2715" })
1378
+ }, style: { height: 26, padding: "0 8px", background: "transparent", color: "#666", border: "1px solid #ccc", borderRadius: 3, fontSize: 12, cursor: "pointer" }, children: "\u2715" })
892
1379
  ] })
893
1380
  ] }),
894
1381
  !isCode && /* @__PURE__ */ jsxs("div", { className: "rte-footer", children: [
@@ -898,7 +1385,7 @@ function RichTextEditor({ value, onChange }) {
898
1385
  /* @__PURE__ */ jsx("strong", { children: "Shift+click" }),
899
1386
  " to select cells \xA0\xB7\xA0 ",
900
1387
  /* @__PURE__ */ jsx("strong", { children: "Tab" }),
901
- " moves between cells"
1388
+ " moves between cells \xA0\xB7\xA0 Drop an image file to insert"
902
1389
  ] })
903
1390
  ] })
904
1391
  ] });