@malaya_jeeva/rich-text-editor 1.0.3 → 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 +713 -215
  2. package/dist/index.mjs +713 -215
  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,24 +749,44 @@ function RichTextEditor({ value, onChange }) {
386
749
  setSelCells([]);
387
750
  dragStartCell.current = null;
388
751
  }, []);
389
- const exec = useCallback((cmd, val = null) => {
752
+ const selectImage = useCallback((img) => {
390
753
  var _a2;
391
- (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
392
- document.execCommand(cmd, false, val != null ? val : void 0);
393
- refresh();
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
+ }, []);
775
+ const fixListNesting = useCallback(() => {
776
+ var _a2;
777
+ (_a2 = editorRef.current) == null ? void 0 : _a2.querySelectorAll("ol > ol, ol > ul, ul > ol, ul > ul").forEach((orphan) => {
778
+ const prev = orphan.previousElementSibling;
779
+ if ((prev == null ? void 0 : prev.tagName) === "LI") prev.appendChild(orphan);
780
+ });
394
781
  }, []);
395
782
  const refresh = useCallback(() => {
396
783
  var _a2, _b, _c, _d;
397
784
  const raw = document.queryCommandValue("formatBlock").toLowerCase().replace(/[<>]/g, "");
398
785
  const block = ["h1", "h2", "h3"].includes(raw) ? raw : "p";
399
- const c = document.queryCommandValue("foreColor");
400
- if (c && c !== "false") {
401
- const m = c.match(/\d+/g);
402
- if (m) setColor("#" + m.slice(0, 3).map((n) => parseInt(n).toString(16).padStart(2, "0")).join(""));
403
- }
404
- const cell = getCurrentCell();
405
- const inTable = !!cell;
786
+ setColor(getColorAtCursor(editorRef.current));
787
+ const cell = getCurrentCell(), inTable = !!cell;
406
788
  const cellMerged = cell ? cell.colSpan > 1 || cell.rowSpan > 1 : false;
789
+ const anchor = getCurrentLink(), inLink = !!anchor;
407
790
  setFmt({
408
791
  bold: document.queryCommandState("bold"),
409
792
  italic: document.queryCommandState("italic"),
@@ -416,15 +799,39 @@ function RichTextEditor({ value, onChange }) {
416
799
  aJ: document.queryCommandState("justifyFull"),
417
800
  block,
418
801
  inTable,
419
- cellMerged
802
+ cellMerged,
803
+ inLink,
804
+ linkHref: anchor == null ? void 0 : anchor.href
420
805
  });
421
806
  if (cell && editorAreaRef.current) setTableFP(calcFloat(cell, 370));
422
807
  else setTableFP(null);
808
+ if (anchor && editorAreaRef.current && !linkBar) setLinkInfoFP(calcFloat(anchor, 290));
809
+ else setLinkInfoFP(null);
423
810
  const txt = (_b = (_a2 = editorRef.current) == null ? void 0 : _a2.innerText) != null ? _b : "";
424
811
  setWords(txt.trim() ? txt.trim().split(/\s+/).length : 0);
425
812
  onChange == null ? void 0 : onChange((_d = (_c = editorRef.current) == null ? void 0 : _c.innerHTML) != null ? _d : "");
426
- }, [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]);
427
828
  const handleEditorMouseDown = useCallback((e) => {
829
+ if (e.target.tagName === "IMG") {
830
+ e.preventDefault();
831
+ selectImage(e.target);
832
+ return;
833
+ }
834
+ clearImageSelection();
428
835
  const target = e.target.closest("td, th");
429
836
  if (!target) {
430
837
  clearSelection();
@@ -441,7 +848,7 @@ function RichTextEditor({ value, onChange }) {
441
848
  dragStartCell.current = target;
442
849
  isDragging.current = true;
443
850
  applySelection([target]);
444
- }, [applySelection, clearSelection]);
851
+ }, [applySelection, clearSelection, selectImage, clearImageSelection]);
445
852
  const handleEditorMouseMove = useCallback((e) => {
446
853
  if (!isDragging.current || !dragStartCell.current) return;
447
854
  const target = e.target.closest("td, th");
@@ -454,12 +861,10 @@ function RichTextEditor({ value, onChange }) {
454
861
  applySelection(cells);
455
862
  }
456
863
  }, [applySelection]);
457
- const handleEditorMouseUp = useCallback(() => {
458
- isDragging.current = false;
459
- }, []);
460
864
  const handleKeyDown = useCallback((e) => {
461
865
  var _a2, _b;
462
866
  clearSelection();
867
+ clearImageSelection();
463
868
  if (e.key === "Tab") {
464
869
  e.preventDefault();
465
870
  const cell = getCurrentCell();
@@ -491,14 +896,14 @@ function RichTextEditor({ value, onChange }) {
491
896
  exec("underline");
492
897
  }
493
898
  }
494
- }, [exec, clearSelection]);
899
+ }, [exec, clearSelection, clearImageSelection]);
495
900
  const insertTable = (rows, cols) => {
496
901
  var _a2;
497
902
  (_a2 = editorRef.current) == null ? void 0 : _a2.focus();
498
- let html = `<table>`;
903
+ let html = "<table>";
499
904
  for (let r = 0; r < rows; r++) {
500
905
  html += "<tr>";
501
- 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>";
502
907
  html += "</tr>";
503
908
  }
504
909
  html += "</table><p><br></p>";
@@ -593,11 +998,7 @@ function RichTextEditor({ value, onChange }) {
593
998
  savedRangeRef.current = (sel == null ? void 0 : sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
594
999
  if ((sel == null ? void 0 : sel.rangeCount) && editorAreaRef.current) {
595
1000
  const rect = sel.getRangeAt(0).getBoundingClientRect();
596
- if (rect.width > 0 || rect.height > 0) setLinkFP(calcFloat({ getBoundingClientRect: () => rect }, 360));
597
- else {
598
- const area = editorAreaRef.current.getBoundingClientRect();
599
- setLinkFP({ top: 60, left: 20, arrowLeft: 20 });
600
- }
1001
+ setLinkFP(rect.width > 0 || rect.height > 0 ? calcFloat({ getBoundingClientRect: () => rect }, 360) : { top: 60, left: 20, arrowLeft: 20 });
601
1002
  }
602
1003
  setLinkUrl("https://");
603
1004
  setLinkBar(true);
@@ -701,7 +1102,7 @@ function RichTextEditor({ value, onChange }) {
701
1102
  };
702
1103
  const copyHtml = () => {
703
1104
  var _a2, _b;
704
- 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(() => {
705
1106
  });
706
1107
  };
707
1108
  useEffect(() => {
@@ -710,14 +1111,8 @@ function RichTextEditor({ value, onChange }) {
710
1111
  refresh();
711
1112
  }
712
1113
  }, []);
713
- useEffect(() => {
714
- if (!showTable) return;
715
- const h = () => setShowTable(false);
716
- document.addEventListener("mousedown", h);
717
- return () => document.removeEventListener("mousedown", h);
718
- }, [showTable]);
719
- const canMerge = selCells.length >= 2;
720
- const canSplit = fmt.cellMerged;
1114
+ const canMerge = selCells.length >= 2, canSplit = fmt.cellMerged;
1115
+ const backdrop = { position: "fixed", inset: 0, zIndex: 199 };
721
1116
  return /* @__PURE__ */ jsxs("div", { style: { padding: "1rem 0" }, children: [
722
1117
  /* @__PURE__ */ jsx("style", { children: CSS }),
723
1118
  /* @__PURE__ */ jsxs("div", { style: { border: "1px solid #d8d8d8", borderRadius: 4, boxShadow: "0 1px 3px rgba(0,0,0,0.06)" }, children: [
@@ -726,26 +1121,29 @@ function RichTextEditor({ value, onChange }) {
726
1121
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("bold"), title: "Bold (Ctrl+B)", active: fmt.bold, style: { fontWeight: 800, fontFamily: "Georgia,serif" }, children: "B" }),
727
1122
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("italic"), title: "Italic (Ctrl+I)", active: fmt.italic, style: { fontStyle: "italic", fontFamily: "Georgia,serif" }, children: "I" }),
728
1123
  /* @__PURE__ */ jsx(Btn, { onClick: () => exec("underline"), title: "Underline (Ctrl+U)", active: fmt.underline, style: { textDecoration: "underline" }, children: "U" }),
729
- /* @__PURE__ */ jsxs(Btn, { onClick: () => {
730
- var _a2;
731
- return (_a2 = colorRef.current) == null ? void 0 : _a2.click();
732
- }, title: "Text color", style: { flexDirection: "column", gap: 1, padding: "3px 6px" }, children: [
733
- /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 800, fontFamily: "Georgia,serif", lineHeight: 1, color: "#222" }, children: "A" }),
734
- /* @__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
+ ) })
735
1146
  ] }),
736
- /* @__PURE__ */ jsx(
737
- "input",
738
- {
739
- ref: colorRef,
740
- type: "color",
741
- value: color,
742
- onChange: (e) => {
743
- setColor(e.target.value);
744
- exec("foreColor", e.target.value);
745
- },
746
- style: { position: "absolute", opacity: 0, width: 0, height: 0, pointerEvents: "none" }
747
- }
748
- ),
749
1147
  /* @__PURE__ */ jsx(Sep, {}),
750
1148
  /* @__PURE__ */ jsxs(
751
1149
  "select",
@@ -783,12 +1181,21 @@ function RichTextEditor({ value, onChange }) {
783
1181
  /* @__PURE__ */ jsx(Sep, {}),
784
1182
  /* @__PURE__ */ jsx(Btn, { onClick: openLinkBar, title: "Insert link", active: linkBar, children: /* @__PURE__ */ jsx(IcoLink, {}) }),
785
1183
  /* @__PURE__ */ jsx(Btn, { onClick: insertCodeBlock, title: "Code block", children: /* @__PURE__ */ jsx(IcoCode, {}) }),
786
- /* @__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) }),
787
1194
  /* @__PURE__ */ jsx(Btn, { onClick: () => setShowTable((v) => !v), title: "Insert table", active: showTable || !!fmt.inTable, children: /* @__PURE__ */ jsx(IcoTable, {}) }),
788
- 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) => {
789
1196
  insertTable(r, c);
790
1197
  setShowTable(false);
791
- }, onClose: () => setShowTable(false) })
1198
+ }, onClose: () => setShowTable(false) }) })
792
1199
  ] }),
793
1200
  /* @__PURE__ */ jsx(Sep, {})
794
1201
  ] }),
@@ -831,29 +1238,120 @@ function RichTextEditor({ value, onChange }) {
831
1238
  "data-ph": "Start typing...",
832
1239
  onMouseDown: handleEditorMouseDown,
833
1240
  onMouseMove: handleEditorMouseMove,
834
- onMouseUp: handleEditorMouseUp,
1241
+ onMouseUp: () => {
1242
+ isDragging.current = false;
1243
+ },
835
1244
  onKeyDown: handleKeyDown,
836
1245
  onKeyUp: refresh,
837
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
+ },
838
1259
  style: { display: isCode ? "none" : "block" }
839
1260
  }
840
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
+ ] }),
841
1303
  !isCode && fmt.inTable && tableFP && /* @__PURE__ */ jsxs("div", { className: "rte-float", style: { top: tableFP.top, left: tableFP.left }, children: [
842
1304
  /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: tableFP.arrowLeft } }),
843
1305
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowAbove"), title: "Add row above", children: /* @__PURE__ */ jsx(IcoRowAbove, {}) }),
844
1306
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addRowBelow"), title: "Add row below", children: /* @__PURE__ */ jsx(IcoRowBelow, {}) }),
845
1307
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteRow"), title: "Delete row", danger: true, children: /* @__PURE__ */ jsx(IcoDelRow, {}) }),
846
1308
  /* @__PURE__ */ jsx(Sep, {}),
847
- /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColLeft"), title: "Add column left", children: /* @__PURE__ */ jsx(IcoColLeft, {}) }),
848
- /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("addColRight"), title: "Add column right", children: /* @__PURE__ */ jsx(IcoColRight, {}) }),
849
- /* @__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, {}) }),
850
1312
  /* @__PURE__ */ jsx(Sep, {}),
851
1313
  canMerge && /* @__PURE__ */ jsx(Btn, { onClick: () => doMerge(selCells), title: `Merge ${selCells.length} cells`, children: /* @__PURE__ */ jsx(IcoMerge, {}) }),
852
1314
  canSplit && /* @__PURE__ */ jsx(Btn, { onClick: doSplit, title: "Split merged cell", children: /* @__PURE__ */ jsx(IcoSplit, {}) }),
853
1315
  (canMerge || canSplit) && /* @__PURE__ */ jsx(Sep, {}),
854
1316
  /* @__PURE__ */ jsx(Btn, { onClick: () => tableOp("deleteTable"), title: "Delete table", danger: true, children: /* @__PURE__ */ jsx(IcoDelTable, {}) })
855
1317
  ] }),
856
- !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: [
857
1355
  /* @__PURE__ */ jsx("div", { className: "rte-float-arrow", style: { left: linkFP.arrowLeft } }),
858
1356
  /* @__PURE__ */ jsx(IcoLink, {}),
859
1357
  /* @__PURE__ */ jsx(
@@ -873,11 +1371,11 @@ function RichTextEditor({ value, onChange }) {
873
1371
  placeholder: "https://example.com"
874
1372
  }
875
1373
  ),
876
- /* @__PURE__ */ jsx("button", { className: "rte-link-apply", onClick: applyLink, children: "Apply" }),
877
- /* @__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: () => {
878
1376
  setLinkBar(false);
879
1377
  setLinkFP(null);
880
- }, 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" })
881
1379
  ] })
882
1380
  ] }),
883
1381
  !isCode && /* @__PURE__ */ jsxs("div", { className: "rte-footer", children: [
@@ -887,7 +1385,7 @@ function RichTextEditor({ value, onChange }) {
887
1385
  /* @__PURE__ */ jsx("strong", { children: "Shift+click" }),
888
1386
  " to select cells \xA0\xB7\xA0 ",
889
1387
  /* @__PURE__ */ jsx("strong", { children: "Tab" }),
890
- " moves between cells"
1388
+ " moves between cells \xA0\xB7\xA0 Drop an image file to insert"
891
1389
  ] })
892
1390
  ] })
893
1391
  ] });