@refactico/pages 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2210 @@
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useRef, useState, useCallback, useEffect, memo, createContext } from 'react';
3
+
4
+ const generateId = () => {
5
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
6
+ return `block_${crypto.randomUUID()}`;
7
+ }
8
+ const timestamp = Date.now().toString(36);
9
+ const randomPart = Math.random().toString(36).substring(2, 11);
10
+ const counter = generateId._counter ?? 0;
11
+ generateId._counter = counter + 1;
12
+ return `block_${timestamp}_${randomPart}_${counter.toString(36)}`;
13
+ };
14
+ const createEmptyEditorData = () => ({
15
+ version: "1.0.0",
16
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
17
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
18
+ blocks: []
19
+ });
20
+ const createTextBlock = (content = "") => ({
21
+ type: "text",
22
+ id: generateId(),
23
+ content,
24
+ fontSize: "base",
25
+ alignment: "left"
26
+ });
27
+ const createHeadingBlock = (level = 2, content = "") => ({
28
+ type: "heading",
29
+ id: generateId(),
30
+ content,
31
+ level,
32
+ alignment: "left"
33
+ });
34
+ const createImageBlock = (src = "", alt = "") => ({
35
+ type: "image",
36
+ id: generateId(),
37
+ src,
38
+ alt,
39
+ caption: "",
40
+ alignment: "center"
41
+ });
42
+ const createCodeBlock = (code = "", language = "javascript") => ({
43
+ type: "code",
44
+ id: generateId(),
45
+ code,
46
+ language,
47
+ showLineNumbers: true
48
+ });
49
+ const createTableBlock = (rows = 3, cols = 3) => ({
50
+ type: "table",
51
+ id: generateId(),
52
+ rows: Array(rows).fill(null).map(
53
+ (_, rowIndex) => Array(cols).fill(null).map(() => ({
54
+ content: "",
55
+ header: rowIndex === 0
56
+ }))
57
+ ),
58
+ hasHeader: true
59
+ });
60
+ const createDividerBlock = () => ({
61
+ type: "divider",
62
+ id: generateId(),
63
+ style: "solid"
64
+ });
65
+ const createQuoteBlock = (content = "") => ({
66
+ type: "quote",
67
+ id: generateId(),
68
+ content,
69
+ style: "default"
70
+ });
71
+ const createListBlock = (listType = "bullet") => ({
72
+ type: "list",
73
+ id: generateId(),
74
+ items: [""],
75
+ listType,
76
+ checkedItems: listType === "checklist" ? [false] : void 0
77
+ });
78
+ const createCalloutBlock = (variant = "info") => ({
79
+ type: "callout",
80
+ id: generateId(),
81
+ content: "",
82
+ variant
83
+ });
84
+ const fileToBase64 = (file) => {
85
+ return new Promise((resolve, reject) => {
86
+ const reader = new FileReader();
87
+ reader.readAsDataURL(file);
88
+ reader.onload = () => resolve(reader.result);
89
+ reader.onerror = (error) => reject(error);
90
+ });
91
+ };
92
+ const validateEditorData = (data) => {
93
+ if (!data || typeof data !== "object") return false;
94
+ const d = data;
95
+ return typeof d.version === "string" && typeof d.createdAt === "string" && typeof d.updatedAt === "string" && Array.isArray(d.blocks);
96
+ };
97
+ const cloneBlock = (block) => {
98
+ return {
99
+ ...JSON.parse(JSON.stringify(block)),
100
+ id: generateId()
101
+ };
102
+ };
103
+ const moveArrayItem = (arr, fromIndex, toIndex) => {
104
+ const newArr = [...arr];
105
+ const [item] = newArr.splice(fromIndex, 1);
106
+ if (item !== void 0) {
107
+ newArr.splice(toIndex, 0, item);
108
+ }
109
+ return newArr;
110
+ };
111
+
112
+ const PlusIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
113
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
114
+ /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
115
+ ] });
116
+ const TextIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
117
+ /* @__PURE__ */ jsx("polyline", { points: "4 7 4 4 20 4 20 7" }),
118
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "20", x2: "15", y2: "20" }),
119
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "4", x2: "12", y2: "20" })
120
+ ] });
121
+ const HeadingIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
122
+ /* @__PURE__ */ jsx("path", { d: "M6 4v16" }),
123
+ /* @__PURE__ */ jsx("path", { d: "M18 4v16" }),
124
+ /* @__PURE__ */ jsx("path", { d: "M6 12h12" })
125
+ ] });
126
+ const ImageIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
127
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
128
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
129
+ /* @__PURE__ */ jsx("polyline", { points: "21 15 16 10 5 21" })
130
+ ] });
131
+ const CodeIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
132
+ /* @__PURE__ */ jsx("polyline", { points: "16 18 22 12 16 6" }),
133
+ /* @__PURE__ */ jsx("polyline", { points: "8 6 2 12 8 18" })
134
+ ] });
135
+ const TableIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
136
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
137
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
138
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }),
139
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
140
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" })
141
+ ] });
142
+ const DividerIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsx("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }) });
143
+ const QuoteIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
144
+ /* @__PURE__ */ jsx("path", { d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V21c0 1 0 1 1 1z" }),
145
+ /* @__PURE__ */ jsx("path", { d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" })
146
+ ] });
147
+ const ListIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
148
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }),
149
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }),
150
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }),
151
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }),
152
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }),
153
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })
154
+ ] });
155
+ const CalloutIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
156
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
157
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
158
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
159
+ ] });
160
+ const TrashIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
161
+ /* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }),
162
+ /* @__PURE__ */ jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
163
+ ] });
164
+ const DragIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
165
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "5", r: "1" }),
166
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "12", r: "1" }),
167
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "19", r: "1" }),
168
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "5", r: "1" }),
169
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "12", r: "1" }),
170
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "19", r: "1" })
171
+ ] });
172
+ const CopyIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
173
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
174
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
175
+ ] });
176
+ const ChevronUpIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsx("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "18 15 12 9 6 15" }) });
177
+ const ChevronDownIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsx("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" }) });
178
+ const BoldIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
179
+ /* @__PURE__ */ jsx("path", { d: "M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" }),
180
+ /* @__PURE__ */ jsx("path", { d: "M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" })
181
+ ] });
182
+ const ItalicIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
183
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "4", x2: "10", y2: "4" }),
184
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "20", x2: "5", y2: "20" }),
185
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" })
186
+ ] });
187
+ const UnderlineIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
188
+ /* @__PURE__ */ jsx("path", { d: "M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3" }),
189
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "21", x2: "20", y2: "21" })
190
+ ] });
191
+ const AlignLeftIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
192
+ /* @__PURE__ */ jsx("line", { x1: "17", y1: "10", x2: "3", y2: "10" }),
193
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "6", x2: "3", y2: "6" }),
194
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "14", x2: "3", y2: "14" }),
195
+ /* @__PURE__ */ jsx("line", { x1: "17", y1: "18", x2: "3", y2: "18" })
196
+ ] });
197
+ const AlignCenterIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
198
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "10", x2: "6", y2: "10" }),
199
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "6", x2: "3", y2: "6" }),
200
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "14", x2: "3", y2: "14" }),
201
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "18", x2: "6", y2: "18" })
202
+ ] });
203
+ const AlignRightIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
204
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "10", x2: "7", y2: "10" }),
205
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "6", x2: "3", y2: "6" }),
206
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "14", x2: "3", y2: "14" }),
207
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "18", x2: "7", y2: "18" })
208
+ ] });
209
+ const CheckIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsx("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) });
210
+ const UploadIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ jsxs("svg", { className, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
211
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
212
+ /* @__PURE__ */ jsx("polyline", { points: "17 8 12 3 7 8" }),
213
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
214
+ ] });
215
+
216
+ const blockOptions = [
217
+ { type: "text", label: "Text", description: "Plain text paragraph", icon: TextIcon },
218
+ { type: "heading", label: "Heading", description: "Section heading", icon: HeadingIcon },
219
+ { type: "image", label: "Image", description: "Upload or embed image", icon: ImageIcon },
220
+ { type: "code", label: "Code", description: "Code block with syntax highlighting", icon: CodeIcon },
221
+ { type: "table", label: "Table", description: "Insert a table", icon: TableIcon },
222
+ { type: "list", label: "List", description: "Bullet, numbered, or checklist", icon: ListIcon },
223
+ { type: "quote", label: "Quote", description: "Blockquote", icon: QuoteIcon },
224
+ { type: "callout", label: "Callout", description: "Info, warning, or tip box", icon: CalloutIcon },
225
+ { type: "divider", label: "Divider", description: "Horizontal line separator", icon: DividerIcon }
226
+ ];
227
+ const AddBlockMenu = ({ onAdd, onClose, theme = "light" }) => {
228
+ const menuRef = useRef(null);
229
+ const [focusedIndex, setFocusedIndex] = useState(0);
230
+ const isDark = theme === "dark";
231
+ const handleKeyDown = useCallback((e) => {
232
+ switch (e.key) {
233
+ case "ArrowDown":
234
+ e.preventDefault();
235
+ setFocusedIndex((prev) => (prev + 1) % blockOptions.length);
236
+ break;
237
+ case "ArrowUp":
238
+ e.preventDefault();
239
+ setFocusedIndex((prev) => (prev - 1 + blockOptions.length) % blockOptions.length);
240
+ break;
241
+ case "Enter":
242
+ case " ": {
243
+ e.preventDefault();
244
+ const option = blockOptions[focusedIndex];
245
+ if (option) {
246
+ onAdd(option.type);
247
+ onClose();
248
+ }
249
+ break;
250
+ }
251
+ case "Escape":
252
+ e.preventDefault();
253
+ onClose();
254
+ break;
255
+ case "Tab":
256
+ onClose();
257
+ break;
258
+ }
259
+ }, [focusedIndex, onAdd, onClose]);
260
+ useEffect(() => {
261
+ const handleClickOutside = (event) => {
262
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
263
+ onClose();
264
+ }
265
+ };
266
+ const handleGlobalEscape = (event) => {
267
+ if (event.key === "Escape") {
268
+ onClose();
269
+ }
270
+ };
271
+ document.addEventListener("mousedown", handleClickOutside);
272
+ document.addEventListener("keydown", handleGlobalEscape);
273
+ menuRef.current?.focus();
274
+ return () => {
275
+ document.removeEventListener("mousedown", handleClickOutside);
276
+ document.removeEventListener("keydown", handleGlobalEscape);
277
+ };
278
+ }, [onClose]);
279
+ return /* @__PURE__ */ jsx(
280
+ "div",
281
+ {
282
+ ref: menuRef,
283
+ role: "menu",
284
+ "aria-label": "Add block menu",
285
+ tabIndex: 0,
286
+ onKeyDown: handleKeyDown,
287
+ className: `absolute z-50 mt-2 w-72 max-h-80 overflow-y-auto rounded-xl shadow-xl border animate-in fade-in slide-in-from-top-2 duration-200 focus:outline-none ${isDark ? "bg-slate-800 border-slate-700" : "bg-white border-slate-200"}`,
288
+ children: /* @__PURE__ */ jsxs("div", { className: "p-2", children: [
289
+ /* @__PURE__ */ jsx(
290
+ "p",
291
+ {
292
+ className: `px-3 py-2 text-xs font-semibold uppercase tracking-wider ${isDark ? "text-slate-500" : "text-slate-400"}`,
293
+ id: "add-block-menu-label",
294
+ children: "Add Block"
295
+ }
296
+ ),
297
+ /* @__PURE__ */ jsx("div", { className: "space-y-1", role: "group", "aria-labelledby": "add-block-menu-label", children: blockOptions.map((option, index) => {
298
+ const Icon = option.icon;
299
+ const isFocused = index === focusedIndex;
300
+ return /* @__PURE__ */ jsxs(
301
+ "button",
302
+ {
303
+ role: "menuitem",
304
+ type: "button",
305
+ tabIndex: -1,
306
+ onClick: () => {
307
+ onAdd(option.type);
308
+ onClose();
309
+ },
310
+ onMouseEnter: () => setFocusedIndex(index),
311
+ "aria-label": `Add ${option.label} block: ${option.description}`,
312
+ className: `w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors text-left group ${isFocused ? isDark ? "bg-slate-700" : "bg-slate-100" : isDark ? "hover:bg-slate-700" : "hover:bg-slate-50"}`,
313
+ children: [
314
+ /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 w-10 h-10 flex items-center justify-center rounded-lg transition-colors ${isDark ? "bg-slate-700 group-hover:bg-slate-600" : "bg-slate-100 group-hover:bg-slate-200"}`, children: /* @__PURE__ */ jsx(Icon, { className: isDark ? "text-slate-300" : "text-slate-600", size: 20, "aria-hidden": "true" }) }),
315
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
316
+ /* @__PURE__ */ jsx("p", { className: `text-sm font-medium ${isDark ? "text-white" : "text-slate-900"}`, children: option.label }),
317
+ /* @__PURE__ */ jsx("p", { className: `text-xs truncate ${isDark ? "text-slate-400" : "text-slate-500"}`, children: option.description })
318
+ ] })
319
+ ]
320
+ },
321
+ option.type
322
+ );
323
+ }) })
324
+ ] })
325
+ }
326
+ );
327
+ };
328
+
329
+ const fontSizeMap = {
330
+ sm: "text-sm leading-relaxed",
331
+ base: "text-base leading-relaxed",
332
+ lg: "text-lg leading-relaxed",
333
+ xl: "text-xl leading-relaxed"
334
+ };
335
+ const alignmentMap$2 = {
336
+ left: "text-left",
337
+ center: "text-center",
338
+ right: "text-right",
339
+ justify: "text-justify"
340
+ };
341
+ const TextBlockComponent = ({ block, onUpdate, readOnly, theme = "light" }) => {
342
+ const [showToolbar, setShowToolbar] = useState(false);
343
+ const textareaRef = useRef(null);
344
+ const isDark = theme === "dark";
345
+ useEffect(() => {
346
+ if (textareaRef.current) {
347
+ textareaRef.current.style.height = "auto";
348
+ textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
349
+ }
350
+ }, [block.content]);
351
+ const handleChange = useCallback((e) => {
352
+ onUpdate({ ...block, content: e.target.value });
353
+ }, [block, onUpdate]);
354
+ const toggleStyle = useCallback((style) => {
355
+ onUpdate({
356
+ ...block,
357
+ style: {
358
+ ...block.style,
359
+ [style]: !block.style?.[style]
360
+ }
361
+ });
362
+ }, [block, onUpdate]);
363
+ const setAlignment = useCallback((alignment) => {
364
+ onUpdate({ ...block, alignment });
365
+ }, [block, onUpdate]);
366
+ const setFontSize = useCallback((fontSize) => {
367
+ onUpdate({ ...block, fontSize });
368
+ }, [block, onUpdate]);
369
+ const handleFocus = useCallback(() => setShowToolbar(true), []);
370
+ const handleBlur = useCallback((e) => {
371
+ if (!e.currentTarget.contains(e.relatedTarget)) {
372
+ setShowToolbar(false);
373
+ }
374
+ }, []);
375
+ const textStyle = {
376
+ fontWeight: block.style?.bold ? "bold" : "normal",
377
+ fontStyle: block.style?.italic ? "italic" : "normal",
378
+ textDecoration: block.style?.underline ? "underline" : block.style?.strikethrough ? "line-through" : "none",
379
+ color: block.style?.color || void 0,
380
+ backgroundColor: block.style?.backgroundColor || "transparent"
381
+ };
382
+ const toolbarBtnClass = (isActive) => `p-2 rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-600"}`;
383
+ return /* @__PURE__ */ jsxs(
384
+ "div",
385
+ {
386
+ className: "group relative",
387
+ onFocus: handleFocus,
388
+ onBlur: handleBlur,
389
+ children: [
390
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
391
+ /* @__PURE__ */ jsx(
392
+ "button",
393
+ {
394
+ onClick: () => toggleStyle("bold"),
395
+ className: toolbarBtnClass(block.style?.bold),
396
+ title: "Bold",
397
+ children: /* @__PURE__ */ jsx(BoldIcon, { size: 16 })
398
+ }
399
+ ),
400
+ /* @__PURE__ */ jsx(
401
+ "button",
402
+ {
403
+ onClick: () => toggleStyle("italic"),
404
+ className: toolbarBtnClass(block.style?.italic),
405
+ title: "Italic",
406
+ children: /* @__PURE__ */ jsx(ItalicIcon, { size: 16 })
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsx(
410
+ "button",
411
+ {
412
+ onClick: () => toggleStyle("underline"),
413
+ className: toolbarBtnClass(block.style?.underline),
414
+ title: "Underline",
415
+ children: /* @__PURE__ */ jsx(UnderlineIcon, { size: 16 })
416
+ }
417
+ ),
418
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
419
+ /* @__PURE__ */ jsx(
420
+ "button",
421
+ {
422
+ onClick: () => setAlignment("left"),
423
+ className: toolbarBtnClass(block.alignment === "left"),
424
+ title: "Align Left",
425
+ children: /* @__PURE__ */ jsx(AlignLeftIcon, { size: 16 })
426
+ }
427
+ ),
428
+ /* @__PURE__ */ jsx(
429
+ "button",
430
+ {
431
+ onClick: () => setAlignment("center"),
432
+ className: toolbarBtnClass(block.alignment === "center"),
433
+ title: "Align Center",
434
+ children: /* @__PURE__ */ jsx(AlignCenterIcon, { size: 16 })
435
+ }
436
+ ),
437
+ /* @__PURE__ */ jsx(
438
+ "button",
439
+ {
440
+ onClick: () => setAlignment("right"),
441
+ className: toolbarBtnClass(block.alignment === "right"),
442
+ title: "Align Right",
443
+ children: /* @__PURE__ */ jsx(AlignRightIcon, { size: 16 })
444
+ }
445
+ ),
446
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
447
+ /* @__PURE__ */ jsxs(
448
+ "select",
449
+ {
450
+ value: block.fontSize || "base",
451
+ onChange: (e) => setFontSize(e.target.value),
452
+ className: `px-2 py-1.5 text-sm rounded-lg border ${isDark ? "bg-slate-700 border-slate-600 text-slate-200" : "bg-white border-slate-200 text-slate-700"}`,
453
+ children: [
454
+ /* @__PURE__ */ jsx("option", { value: "sm", children: "Small" }),
455
+ /* @__PURE__ */ jsx("option", { value: "base", children: "Normal" }),
456
+ /* @__PURE__ */ jsx("option", { value: "lg", children: "Large" }),
457
+ /* @__PURE__ */ jsx("option", { value: "xl", children: "X-Large" })
458
+ ]
459
+ }
460
+ )
461
+ ] }),
462
+ readOnly ? /* @__PURE__ */ jsx(
463
+ "div",
464
+ {
465
+ style: textStyle,
466
+ className: `w-full whitespace-pre-wrap break-words ${fontSizeMap[block.fontSize || "base"]} ${alignmentMap$2[block.alignment || "left"]} ${isDark ? "text-slate-200" : "text-slate-800"}`,
467
+ children: block.content || /* @__PURE__ */ jsx("span", { className: isDark ? "text-slate-500" : "text-slate-400", children: "Empty text block" })
468
+ }
469
+ ) : /* @__PURE__ */ jsx(
470
+ "textarea",
471
+ {
472
+ ref: textareaRef,
473
+ value: block.content,
474
+ onChange: handleChange,
475
+ placeholder: "Start typing...",
476
+ style: textStyle,
477
+ className: `w-full min-h-[2.5rem] p-2 resize-none outline-none bg-transparent ${fontSizeMap[block.fontSize || "base"]} ${alignmentMap$2[block.alignment || "left"]} ${isDark ? "text-slate-200 placeholder:text-slate-500" : "text-slate-800 placeholder:text-slate-400"}`,
478
+ rows: 1
479
+ }
480
+ )
481
+ ]
482
+ }
483
+ );
484
+ };
485
+ const TextBlock = memo(TextBlockComponent);
486
+
487
+ const headingSizeMap = {
488
+ 1: "text-4xl font-bold tracking-tight",
489
+ 2: "text-3xl font-bold tracking-tight",
490
+ 3: "text-2xl font-semibold",
491
+ 4: "text-xl font-semibold",
492
+ 5: "text-lg font-medium",
493
+ 6: "text-base font-medium"
494
+ };
495
+ const alignmentMap$1 = {
496
+ left: "text-left",
497
+ center: "text-center",
498
+ right: "text-right"
499
+ };
500
+ const HeadingBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
501
+ const [showToolbar, setShowToolbar] = useState(false);
502
+ const inputRef = useRef(null);
503
+ const isDark = theme === "dark";
504
+ const handleChange = (e) => {
505
+ onUpdate({ ...block, content: e.target.value });
506
+ };
507
+ const setLevel = (level) => {
508
+ onUpdate({ ...block, level });
509
+ };
510
+ const setAlignment = (alignment) => {
511
+ onUpdate({ ...block, alignment });
512
+ };
513
+ const toolbarBtnClass = (isActive) => `p-2 rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-600"}`;
514
+ return /* @__PURE__ */ jsxs(
515
+ "div",
516
+ {
517
+ className: "group relative",
518
+ onFocus: () => setShowToolbar(true),
519
+ onBlur: (e) => {
520
+ if (!e.currentTarget.contains(e.relatedTarget)) {
521
+ setShowToolbar(false);
522
+ }
523
+ },
524
+ children: [
525
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
526
+ /* @__PURE__ */ jsxs(
527
+ "select",
528
+ {
529
+ value: block.level,
530
+ onChange: (e) => setLevel(parseInt(e.target.value)),
531
+ className: `px-2 py-1.5 text-sm rounded-lg border ${isDark ? "bg-slate-700 border-slate-600 text-slate-200" : "bg-white border-slate-200 text-slate-700"}`,
532
+ children: [
533
+ /* @__PURE__ */ jsx("option", { value: 1, children: "Heading 1" }),
534
+ /* @__PURE__ */ jsx("option", { value: 2, children: "Heading 2" }),
535
+ /* @__PURE__ */ jsx("option", { value: 3, children: "Heading 3" }),
536
+ /* @__PURE__ */ jsx("option", { value: 4, children: "Heading 4" }),
537
+ /* @__PURE__ */ jsx("option", { value: 5, children: "Heading 5" }),
538
+ /* @__PURE__ */ jsx("option", { value: 6, children: "Heading 6" })
539
+ ]
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
543
+ /* @__PURE__ */ jsx(
544
+ "button",
545
+ {
546
+ onClick: () => setAlignment("left"),
547
+ className: toolbarBtnClass(block.alignment === "left"),
548
+ title: "Align Left",
549
+ children: /* @__PURE__ */ jsx(AlignLeftIcon, { size: 16 })
550
+ }
551
+ ),
552
+ /* @__PURE__ */ jsx(
553
+ "button",
554
+ {
555
+ onClick: () => setAlignment("center"),
556
+ className: toolbarBtnClass(block.alignment === "center"),
557
+ title: "Align Center",
558
+ children: /* @__PURE__ */ jsx(AlignCenterIcon, { size: 16 })
559
+ }
560
+ ),
561
+ /* @__PURE__ */ jsx(
562
+ "button",
563
+ {
564
+ onClick: () => setAlignment("right"),
565
+ className: toolbarBtnClass(block.alignment === "right"),
566
+ title: "Align Right",
567
+ children: /* @__PURE__ */ jsx(AlignRightIcon, { size: 16 })
568
+ }
569
+ )
570
+ ] }),
571
+ readOnly ? /* @__PURE__ */ jsx(
572
+ "div",
573
+ {
574
+ className: `w-full p-2 ${headingSizeMap[block.level]} ${alignmentMap$1[block.alignment || "left"]} ${isDark ? "text-white" : "text-slate-900"}`,
575
+ children: block.content || /* @__PURE__ */ jsx("span", { className: isDark ? "text-slate-500" : "text-slate-400", children: "Empty heading" })
576
+ }
577
+ ) : /* @__PURE__ */ jsx(
578
+ "input",
579
+ {
580
+ ref: inputRef,
581
+ type: "text",
582
+ value: block.content,
583
+ onChange: handleChange,
584
+ placeholder: `Heading ${block.level}`,
585
+ className: `w-full p-2 outline-none bg-transparent ${headingSizeMap[block.level]} ${alignmentMap$1[block.alignment || "left"]} ${isDark ? "text-white placeholder:text-slate-500" : "text-slate-900 placeholder:text-slate-400"}`
586
+ }
587
+ )
588
+ ]
589
+ }
590
+ );
591
+ };
592
+
593
+ const alignmentMap = {
594
+ left: "mr-auto",
595
+ center: "mx-auto",
596
+ right: "ml-auto"
597
+ };
598
+ const ImageBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
599
+ const [showToolbar, setShowToolbar] = useState(false);
600
+ const [isDragging, setIsDragging] = useState(false);
601
+ const fileInputRef = useRef(null);
602
+ const isDark = theme === "dark";
603
+ const handleFileChange = async (file) => {
604
+ if (file && file.type.startsWith("image/")) {
605
+ try {
606
+ const base64 = await fileToBase64(file);
607
+ onUpdate({ ...block, src: base64, alt: file.name });
608
+ } catch (error) {
609
+ console.error("Error converting image to base64:", error);
610
+ }
611
+ }
612
+ };
613
+ const handleInputChange = (e) => {
614
+ const file = e.target.files?.[0];
615
+ if (file) {
616
+ handleFileChange(file);
617
+ }
618
+ };
619
+ const handleDrop = (e) => {
620
+ e.preventDefault();
621
+ setIsDragging(false);
622
+ const file = e.dataTransfer.files[0];
623
+ if (file) {
624
+ handleFileChange(file);
625
+ }
626
+ };
627
+ const handleDragOver = (e) => {
628
+ e.preventDefault();
629
+ setIsDragging(true);
630
+ };
631
+ const handleDragLeave = () => {
632
+ setIsDragging(false);
633
+ };
634
+ const setAlignment = (alignment) => {
635
+ onUpdate({ ...block, alignment });
636
+ };
637
+ const handleCaptionChange = (e) => {
638
+ onUpdate({ ...block, caption: e.target.value });
639
+ };
640
+ const handleAltChange = (e) => {
641
+ onUpdate({ ...block, alt: e.target.value });
642
+ };
643
+ const handleWidthChange = (e) => {
644
+ const width = parseInt(e.target.value) || void 0;
645
+ onUpdate({ ...block, width });
646
+ };
647
+ const clearImage = () => {
648
+ onUpdate({ ...block, src: "", alt: "", caption: "" });
649
+ };
650
+ const toolbarBtnClass = (isActive) => `p-2 rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-600"}`;
651
+ if (!block.src) {
652
+ return /* @__PURE__ */ jsxs(
653
+ "div",
654
+ {
655
+ className: `relative border-2 border-dashed rounded-xl p-8 transition-colors ${isDragging ? isDark ? "border-indigo-500 bg-indigo-950/30" : "border-indigo-500 bg-indigo-50" : isDark ? "border-slate-700 hover:border-slate-500" : "border-slate-300 hover:border-slate-400"}`,
656
+ onDrop: handleDrop,
657
+ onDragOver: handleDragOver,
658
+ onDragLeave: handleDragLeave,
659
+ children: [
660
+ /* @__PURE__ */ jsx(
661
+ "input",
662
+ {
663
+ ref: fileInputRef,
664
+ type: "file",
665
+ accept: "image/*",
666
+ onChange: handleInputChange,
667
+ className: "hidden"
668
+ }
669
+ ),
670
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-center", children: [
671
+ /* @__PURE__ */ jsx(UploadIcon, { className: `w-12 h-12 mb-4 ${isDark ? "text-slate-500" : "text-slate-400"}` }),
672
+ /* @__PURE__ */ jsx("p", { className: `mb-2 ${isDark ? "text-slate-400" : "text-slate-600"}`, children: "Drag and drop an image here, or" }),
673
+ /* @__PURE__ */ jsx(
674
+ "button",
675
+ {
676
+ onClick: () => fileInputRef.current?.click(),
677
+ className: "px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-medium",
678
+ disabled: readOnly,
679
+ children: "Browse Files"
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx("p", { className: `text-xs mt-2 ${isDark ? "text-slate-500" : "text-slate-500"}`, children: "PNG, JPG, GIF up to 10MB" })
683
+ ] })
684
+ ]
685
+ }
686
+ );
687
+ }
688
+ return /* @__PURE__ */ jsxs(
689
+ "div",
690
+ {
691
+ className: "group relative",
692
+ onFocus: () => setShowToolbar(true),
693
+ onBlur: (e) => {
694
+ if (!e.currentTarget.contains(e.relatedTarget)) {
695
+ setShowToolbar(false);
696
+ }
697
+ },
698
+ children: [
699
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-1/2 -translate-x-1/2 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
700
+ /* @__PURE__ */ jsx(
701
+ "button",
702
+ {
703
+ onClick: () => setAlignment("left"),
704
+ className: toolbarBtnClass(block.alignment === "left"),
705
+ title: "Align Left",
706
+ children: /* @__PURE__ */ jsx(AlignLeftIcon, { size: 16 })
707
+ }
708
+ ),
709
+ /* @__PURE__ */ jsx(
710
+ "button",
711
+ {
712
+ onClick: () => setAlignment("center"),
713
+ className: toolbarBtnClass(block.alignment === "center"),
714
+ title: "Align Center",
715
+ children: /* @__PURE__ */ jsx(AlignCenterIcon, { size: 16 })
716
+ }
717
+ ),
718
+ /* @__PURE__ */ jsx(
719
+ "button",
720
+ {
721
+ onClick: () => setAlignment("right"),
722
+ className: toolbarBtnClass(block.alignment === "right"),
723
+ title: "Align Right",
724
+ children: /* @__PURE__ */ jsx(AlignRightIcon, { size: 16 })
725
+ }
726
+ ),
727
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
728
+ /* @__PURE__ */ jsx(
729
+ "input",
730
+ {
731
+ type: "number",
732
+ value: block.width || "",
733
+ onChange: handleWidthChange,
734
+ placeholder: "Width",
735
+ className: `w-20 px-2 py-1.5 text-sm rounded-lg border ${isDark ? "bg-slate-700 border-slate-600 text-slate-200" : "bg-white border-slate-200 text-slate-700"}`
736
+ }
737
+ ),
738
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
739
+ /* @__PURE__ */ jsx(
740
+ "button",
741
+ {
742
+ onClick: clearImage,
743
+ className: `p-2 rounded-lg hover:text-red-500 ${isDark ? "hover:bg-red-950/30 text-slate-400" : "hover:bg-red-50 text-slate-400"}`,
744
+ title: "Remove Image",
745
+ children: /* @__PURE__ */ jsx(TrashIcon, { size: 16 })
746
+ }
747
+ )
748
+ ] }),
749
+ /* @__PURE__ */ jsxs("figure", { className: `${alignmentMap[block.alignment || "center"]}`, style: { maxWidth: block.width ? `${block.width}px` : "100%" }, children: [
750
+ /* @__PURE__ */ jsx(
751
+ "img",
752
+ {
753
+ src: block.src,
754
+ alt: block.alt || "",
755
+ className: "max-w-full h-auto rounded-xl shadow-sm"
756
+ }
757
+ ),
758
+ !readOnly ? /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-2", children: [
759
+ /* @__PURE__ */ jsx(
760
+ "input",
761
+ {
762
+ type: "text",
763
+ value: block.caption || "",
764
+ onChange: handleCaptionChange,
765
+ placeholder: "Add a caption...",
766
+ className: `w-full text-center text-sm bg-transparent outline-none ${isDark ? "text-slate-400 placeholder:text-slate-500" : "text-slate-600 placeholder:text-slate-400"}`
767
+ }
768
+ ),
769
+ /* @__PURE__ */ jsx(
770
+ "input",
771
+ {
772
+ type: "text",
773
+ value: block.alt || "",
774
+ onChange: handleAltChange,
775
+ placeholder: "Alt text for accessibility...",
776
+ className: `w-full text-center text-xs bg-transparent outline-none ${isDark ? "text-slate-500 placeholder:text-slate-600" : "text-slate-500 placeholder:text-slate-400"}`
777
+ }
778
+ )
779
+ ] }) : block.caption ? /* @__PURE__ */ jsx("figcaption", { className: `mt-3 text-center text-sm ${isDark ? "text-slate-400" : "text-slate-600"}`, children: block.caption }) : null
780
+ ] })
781
+ ]
782
+ }
783
+ );
784
+ };
785
+
786
+ const SUPPORTED_LANGUAGES = [
787
+ "javascript",
788
+ "typescript",
789
+ "python",
790
+ "java",
791
+ "csharp",
792
+ "cpp",
793
+ "c",
794
+ "go",
795
+ "rust",
796
+ "ruby",
797
+ "php",
798
+ "swift",
799
+ "kotlin",
800
+ "scala",
801
+ "html",
802
+ "css",
803
+ "scss",
804
+ "json",
805
+ "yaml",
806
+ "xml",
807
+ "markdown",
808
+ "sql",
809
+ "bash",
810
+ "shell",
811
+ "powershell",
812
+ "dockerfile",
813
+ "plaintext"
814
+ ];
815
+
816
+ const CodeBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
817
+ const [copied, setCopied] = useState(false);
818
+ const textareaRef = useRef(null);
819
+ const isDark = theme === "dark";
820
+ const handleCodeChange = (e) => {
821
+ onUpdate({ ...block, code: e.target.value });
822
+ if (textareaRef.current) {
823
+ textareaRef.current.style.height = "auto";
824
+ textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
825
+ }
826
+ };
827
+ const handleLanguageChange = (e) => {
828
+ onUpdate({ ...block, language: e.target.value });
829
+ };
830
+ const handleFilenameChange = (e) => {
831
+ onUpdate({ ...block, filename: e.target.value });
832
+ };
833
+ const toggleLineNumbers = () => {
834
+ onUpdate({ ...block, showLineNumbers: !block.showLineNumbers });
835
+ };
836
+ const copyToClipboard = async () => {
837
+ try {
838
+ await navigator.clipboard.writeText(block.code);
839
+ setCopied(true);
840
+ setTimeout(() => setCopied(false), 2e3);
841
+ } catch (error) {
842
+ console.error("Failed to copy:", error);
843
+ }
844
+ };
845
+ const lines = block.code.split("\n");
846
+ const lineNumbers = lines.map((_, i) => i + 1);
847
+ return /* @__PURE__ */ jsxs(
848
+ "div",
849
+ {
850
+ className: `group relative rounded-xl overflow-hidden ${isDark ? "bg-slate-950" : "bg-slate-900"}`,
851
+ children: [
852
+ /* @__PURE__ */ jsxs("div", { className: `flex items-center justify-between px-4 py-2.5 border-b ${isDark ? "bg-slate-900 border-slate-800" : "bg-slate-800 border-slate-700"}`, children: [
853
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
854
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
855
+ /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full bg-red-500/80" }),
856
+ /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full bg-yellow-500/80" }),
857
+ /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full bg-green-500/80" })
858
+ ] }),
859
+ !readOnly ? /* @__PURE__ */ jsx(
860
+ "input",
861
+ {
862
+ type: "text",
863
+ value: block.filename || "",
864
+ onChange: handleFilenameChange,
865
+ placeholder: "filename.js",
866
+ className: "text-sm text-slate-400 bg-transparent outline-none placeholder:text-slate-600 w-32"
867
+ }
868
+ ) : block.filename ? /* @__PURE__ */ jsx("span", { className: "text-sm text-slate-400", children: block.filename }) : null
869
+ ] }),
870
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
871
+ !readOnly && /* @__PURE__ */ jsxs(Fragment, { children: [
872
+ /* @__PURE__ */ jsx(
873
+ "select",
874
+ {
875
+ value: block.language,
876
+ onChange: handleLanguageChange,
877
+ className: "text-xs px-2 py-1.5 bg-slate-700 text-slate-300 rounded-lg border border-slate-600 outline-none",
878
+ children: SUPPORTED_LANGUAGES.map((lang) => /* @__PURE__ */ jsx("option", { value: lang, children: lang }, lang))
879
+ }
880
+ ),
881
+ /* @__PURE__ */ jsx(
882
+ "button",
883
+ {
884
+ onClick: toggleLineNumbers,
885
+ className: `text-xs px-2 py-1.5 rounded-lg transition-colors ${block.showLineNumbers ? "bg-indigo-600 text-white" : "bg-slate-700 text-slate-300 hover:bg-slate-600"}`,
886
+ children: "#"
887
+ }
888
+ )
889
+ ] }),
890
+ /* @__PURE__ */ jsx(
891
+ "button",
892
+ {
893
+ onClick: copyToClipboard,
894
+ className: "p-1.5 rounded-lg hover:bg-slate-700 text-slate-400 hover:text-white transition-colors",
895
+ title: "Copy code",
896
+ children: copied ? /* @__PURE__ */ jsx(CheckIcon, { size: 16 }) : /* @__PURE__ */ jsx(CopyIcon, { size: 16 })
897
+ }
898
+ )
899
+ ] })
900
+ ] }),
901
+ /* @__PURE__ */ jsxs("div", { className: "relative flex overflow-x-auto", children: [
902
+ block.showLineNumbers && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 py-4 pl-4 pr-3 text-right select-none border-r border-slate-800", children: lineNumbers.map((num) => /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-600 leading-6 font-mono", children: num }, num)) }),
903
+ /* @__PURE__ */ jsx("div", { className: "flex-1 p-4 overflow-x-auto", children: readOnly ? /* @__PURE__ */ jsx("pre", { className: "text-sm text-slate-100 font-mono leading-6 whitespace-pre", children: /* @__PURE__ */ jsx("code", { children: block.code }) }) : /* @__PURE__ */ jsx(
904
+ "textarea",
905
+ {
906
+ ref: textareaRef,
907
+ value: block.code,
908
+ onChange: handleCodeChange,
909
+ placeholder: "// Write your code here...",
910
+ spellCheck: false,
911
+ className: "w-full min-h-[100px] text-sm text-slate-100 font-mono leading-6 bg-transparent outline-none resize-none placeholder:text-slate-600",
912
+ style: { tabSize: 2 },
913
+ onKeyDown: (e) => {
914
+ if (e.key === "Tab") {
915
+ e.preventDefault();
916
+ const start = e.currentTarget.selectionStart;
917
+ const end = e.currentTarget.selectionEnd;
918
+ const newValue = block.code.substring(0, start) + " " + block.code.substring(end);
919
+ onUpdate({ ...block, code: newValue });
920
+ setTimeout(() => {
921
+ if (textareaRef.current) {
922
+ textareaRef.current.selectionStart = textareaRef.current.selectionEnd = start + 2;
923
+ }
924
+ }, 0);
925
+ }
926
+ }
927
+ }
928
+ ) })
929
+ ] }),
930
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-2 right-2", children: /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-500 uppercase font-medium", children: block.language }) })
931
+ ]
932
+ }
933
+ );
934
+ };
935
+
936
+ const TableBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
937
+ const [showToolbar, setShowToolbar] = useState(false);
938
+ const [selectedCell, setSelectedCell] = useState(null);
939
+ const isDark = theme === "dark";
940
+ const updateCell = (rowIndex, colIndex, content) => {
941
+ const currentRow = block.rows[rowIndex];
942
+ if (!currentRow) return;
943
+ const currentCell = currentRow[colIndex];
944
+ if (!currentCell) return;
945
+ const newRows = [...block.rows];
946
+ newRows[rowIndex] = [...currentRow];
947
+ newRows[rowIndex][colIndex] = { ...currentCell, content };
948
+ onUpdate({ ...block, rows: newRows });
949
+ };
950
+ const addRow = () => {
951
+ const colCount = block.rows[0]?.length || 3;
952
+ const newRow = Array(colCount).fill(null).map(() => ({ content: "" }));
953
+ onUpdate({ ...block, rows: [...block.rows, newRow] });
954
+ };
955
+ const addColumn = () => {
956
+ const newRows = block.rows.map((row, rowIndex) => [
957
+ ...row,
958
+ { content: "", header: rowIndex === 0 && block.hasHeader }
959
+ ]);
960
+ onUpdate({ ...block, rows: newRows });
961
+ };
962
+ const deleteRow = (rowIndex) => {
963
+ if (block.rows.length <= 1) return;
964
+ const newRows = block.rows.filter((_, i) => i !== rowIndex);
965
+ onUpdate({ ...block, rows: newRows });
966
+ };
967
+ const deleteColumn = (colIndex) => {
968
+ if ((block.rows[0]?.length ?? 0) <= 1) return;
969
+ const newRows = block.rows.map((row) => row.filter((_, i) => i !== colIndex));
970
+ onUpdate({ ...block, rows: newRows });
971
+ };
972
+ const toggleHeader = () => {
973
+ const newRows = block.rows.map(
974
+ (row, rowIndex) => row.map((cell) => ({
975
+ ...cell,
976
+ header: rowIndex === 0 ? !block.hasHeader : false
977
+ }))
978
+ );
979
+ onUpdate({ ...block, rows: newRows, hasHeader: !block.hasHeader });
980
+ };
981
+ const setCellAlignment = (alignment) => {
982
+ if (!selectedCell) return;
983
+ const currentRow = block.rows[selectedCell.row];
984
+ if (!currentRow) return;
985
+ const currentCell = currentRow[selectedCell.col];
986
+ if (!currentCell) return;
987
+ const newRows = [...block.rows];
988
+ const newRow = [...currentRow];
989
+ newRow[selectedCell.col] = {
990
+ ...currentCell,
991
+ alignment
992
+ };
993
+ newRows[selectedCell.row] = newRow;
994
+ onUpdate({ ...block, rows: newRows });
995
+ };
996
+ const toolbarBtnClass = (isActive) => `px-2 py-1 text-sm rounded-lg transition-colors ${isActive ? isDark ? "bg-indigo-900/50 text-indigo-400" : "bg-indigo-100 text-indigo-600" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-700"}`;
997
+ return /* @__PURE__ */ jsxs(
998
+ "div",
999
+ {
1000
+ className: "group relative",
1001
+ onFocus: () => setShowToolbar(true),
1002
+ onBlur: (e) => {
1003
+ if (!e.currentTarget.contains(e.relatedTarget)) {
1004
+ setShowToolbar(false);
1005
+ }
1006
+ },
1007
+ children: [
1008
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
1009
+ /* @__PURE__ */ jsxs(
1010
+ "button",
1011
+ {
1012
+ onClick: addRow,
1013
+ className: `flex items-center gap-1 ${toolbarBtnClass()}`,
1014
+ title: "Add Row",
1015
+ children: [
1016
+ /* @__PURE__ */ jsx(PlusIcon, { size: 14 }),
1017
+ " Row"
1018
+ ]
1019
+ }
1020
+ ),
1021
+ /* @__PURE__ */ jsxs(
1022
+ "button",
1023
+ {
1024
+ onClick: addColumn,
1025
+ className: `flex items-center gap-1 ${toolbarBtnClass()}`,
1026
+ title: "Add Column",
1027
+ children: [
1028
+ /* @__PURE__ */ jsx(PlusIcon, { size: 14 }),
1029
+ " Col"
1030
+ ]
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
1034
+ /* @__PURE__ */ jsx(
1035
+ "button",
1036
+ {
1037
+ onClick: toggleHeader,
1038
+ className: toolbarBtnClass(block.hasHeader),
1039
+ children: "Header"
1040
+ }
1041
+ ),
1042
+ selectedCell && /* @__PURE__ */ jsxs(Fragment, { children: [
1043
+ /* @__PURE__ */ jsx("div", { className: `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}` }),
1044
+ /* @__PURE__ */ jsx(
1045
+ "button",
1046
+ {
1047
+ onClick: () => setCellAlignment("left"),
1048
+ className: toolbarBtnClass(),
1049
+ children: "Left"
1050
+ }
1051
+ ),
1052
+ /* @__PURE__ */ jsx(
1053
+ "button",
1054
+ {
1055
+ onClick: () => setCellAlignment("center"),
1056
+ className: toolbarBtnClass(),
1057
+ children: "Center"
1058
+ }
1059
+ ),
1060
+ /* @__PURE__ */ jsx(
1061
+ "button",
1062
+ {
1063
+ onClick: () => setCellAlignment("right"),
1064
+ className: toolbarBtnClass(),
1065
+ children: "Right"
1066
+ }
1067
+ )
1068
+ ] })
1069
+ ] }),
1070
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-xl", children: /* @__PURE__ */ jsx("table", { className: `w-full border-collapse border ${isDark ? "border-slate-600" : "border-slate-300"}`, children: /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, rowIndex) => /* @__PURE__ */ jsxs("tr", { className: "group/row", children: [
1071
+ row.map((cell, colIndex) => {
1072
+ const isHeader = block.hasHeader && rowIndex === 0;
1073
+ const CellTag = isHeader ? "th" : "td";
1074
+ const alignClass = cell.alignment === "center" ? "text-center" : cell.alignment === "right" ? "text-right" : "text-left";
1075
+ return /* @__PURE__ */ jsxs(
1076
+ CellTag,
1077
+ {
1078
+ className: `relative border p-0 ${isDark ? "border-slate-600" : "border-slate-300"} ${isHeader ? isDark ? "bg-slate-800 font-semibold" : "bg-slate-100 font-semibold" : isDark ? "bg-slate-900" : "bg-white"} ${alignClass}`,
1079
+ children: [
1080
+ readOnly ? /* @__PURE__ */ jsx("div", { className: `px-3 py-2 min-h-[40px] ${isDark ? "text-slate-200" : "text-slate-700"}`, children: cell.content }) : /* @__PURE__ */ jsx(
1081
+ "input",
1082
+ {
1083
+ type: "text",
1084
+ value: cell.content,
1085
+ onChange: (e) => updateCell(rowIndex, colIndex, e.target.value),
1086
+ onFocus: () => setSelectedCell({ row: rowIndex, col: colIndex }),
1087
+ onBlur: () => setSelectedCell(null),
1088
+ className: `w-full px-3 py-2 min-h-[40px] outline-none bg-transparent ${alignClass} ${isHeader ? "font-semibold" : ""} ${isDark ? "text-slate-200 placeholder:text-slate-500" : "text-slate-700 placeholder:text-slate-400"} focus:ring-2 focus:ring-indigo-500 focus:ring-inset`,
1089
+ placeholder: isHeader ? "Header" : ""
1090
+ }
1091
+ ),
1092
+ !readOnly && rowIndex === 0 && showToolbar && /* @__PURE__ */ jsx(
1093
+ "button",
1094
+ {
1095
+ onClick: () => deleteColumn(colIndex),
1096
+ className: "absolute -top-6 left-1/2 -translate-x-1/2 p-1 rounded-lg bg-red-500 text-white opacity-0 group-hover:opacity-100 transition-opacity",
1097
+ title: "Delete Column",
1098
+ children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 })
1099
+ }
1100
+ )
1101
+ ]
1102
+ },
1103
+ colIndex
1104
+ );
1105
+ }),
1106
+ !readOnly && showToolbar && /* @__PURE__ */ jsx("td", { className: "w-8 border-none bg-transparent p-0", children: /* @__PURE__ */ jsx(
1107
+ "button",
1108
+ {
1109
+ onClick: () => deleteRow(rowIndex),
1110
+ className: "p-1 rounded-lg bg-red-500 text-white opacity-0 group-hover/row:opacity-100 transition-opacity",
1111
+ title: "Delete Row",
1112
+ children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 })
1113
+ }
1114
+ ) })
1115
+ ] }, rowIndex)) }) }) })
1116
+ ]
1117
+ }
1118
+ );
1119
+ };
1120
+
1121
+ const styleMap = {
1122
+ solid: "border-solid",
1123
+ dashed: "border-dashed",
1124
+ dotted: "border-dotted"
1125
+ };
1126
+ const DividerBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
1127
+ const isDark = theme === "dark";
1128
+ const cycleStyle = () => {
1129
+ if (readOnly) return;
1130
+ const styles = ["solid", "dashed", "dotted"];
1131
+ const currentIndex = styles.indexOf(block.style || "solid");
1132
+ const nextIndex = (currentIndex + 1) % styles.length;
1133
+ onUpdate({ ...block, style: styles[nextIndex] });
1134
+ };
1135
+ return /* @__PURE__ */ jsxs("div", { className: "py-4 group", children: [
1136
+ /* @__PURE__ */ jsx(
1137
+ "hr",
1138
+ {
1139
+ onClick: cycleStyle,
1140
+ className: `border-t-2 ${styleMap[block.style || "solid"]} ${isDark ? "border-slate-700" + (!readOnly ? " hover:border-slate-500" : "") : "border-slate-200" + (!readOnly ? " hover:border-slate-400" : "")} ${!readOnly ? "cursor-pointer" : ""}`
1141
+ }
1142
+ ),
1143
+ !readOnly && /* @__PURE__ */ jsxs("p", { className: `text-xs text-center mt-1 opacity-0 group-hover:opacity-100 transition-opacity ${isDark ? "text-slate-500" : "text-slate-400"}`, children: [
1144
+ "Click to change style (",
1145
+ block.style || "solid",
1146
+ ")"
1147
+ ] })
1148
+ ] });
1149
+ };
1150
+
1151
+ const QuoteBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
1152
+ const [showToolbar, setShowToolbar] = useState(false);
1153
+ const textareaRef = useRef(null);
1154
+ const isDark = theme === "dark";
1155
+ const styleClasses = {
1156
+ default: `border-l-4 pl-4 italic ${isDark ? "border-slate-600" : "border-slate-300"}`,
1157
+ bordered: `border-l-4 pl-4 py-3 rounded-r ${isDark ? "border-indigo-500 bg-indigo-950/30" : "border-indigo-500 bg-indigo-50"}`,
1158
+ modern: `relative pl-10 before:content-['"'] before:absolute before:left-0 before:top-0 before:text-5xl before:leading-none ${isDark ? "before:text-slate-600" : "before:text-slate-300"}`
1159
+ };
1160
+ useEffect(() => {
1161
+ if (textareaRef.current) {
1162
+ textareaRef.current.style.height = "auto";
1163
+ textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
1164
+ }
1165
+ }, [block.content]);
1166
+ const handleContentChange = (e) => {
1167
+ onUpdate({ ...block, content: e.target.value });
1168
+ };
1169
+ const handleAuthorChange = (e) => {
1170
+ onUpdate({ ...block, author: e.target.value });
1171
+ };
1172
+ const setStyle = (style) => {
1173
+ onUpdate({ ...block, style });
1174
+ };
1175
+ const toolbarBtnClass = (isActive) => `px-3 py-1.5 text-sm rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-600"}`;
1176
+ return /* @__PURE__ */ jsxs(
1177
+ "div",
1178
+ {
1179
+ className: "group relative",
1180
+ onFocus: () => setShowToolbar(true),
1181
+ onBlur: (e) => {
1182
+ if (!e.currentTarget.contains(e.relatedTarget)) {
1183
+ setShowToolbar(false);
1184
+ }
1185
+ },
1186
+ children: [
1187
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
1188
+ /* @__PURE__ */ jsx(
1189
+ "button",
1190
+ {
1191
+ onClick: () => setStyle("default"),
1192
+ className: toolbarBtnClass(block.style === "default" || !block.style),
1193
+ children: "Simple"
1194
+ }
1195
+ ),
1196
+ /* @__PURE__ */ jsx(
1197
+ "button",
1198
+ {
1199
+ onClick: () => setStyle("bordered"),
1200
+ className: toolbarBtnClass(block.style === "bordered"),
1201
+ children: "Bordered"
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsx(
1205
+ "button",
1206
+ {
1207
+ onClick: () => setStyle("modern"),
1208
+ className: toolbarBtnClass(block.style === "modern"),
1209
+ children: "Modern"
1210
+ }
1211
+ )
1212
+ ] }),
1213
+ /* @__PURE__ */ jsx("blockquote", { className: `${styleClasses[block.style || "default"]} ${isDark ? "text-slate-300" : "text-slate-700"}`, children: readOnly ? /* @__PURE__ */ jsxs(Fragment, { children: [
1214
+ /* @__PURE__ */ jsx("p", { className: "text-lg leading-relaxed whitespace-pre-wrap", children: block.content }),
1215
+ block.author && /* @__PURE__ */ jsxs("cite", { className: `block mt-3 text-sm not-italic ${isDark ? "text-slate-400" : "text-slate-500"}`, children: [
1216
+ "— ",
1217
+ block.author
1218
+ ] })
1219
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1220
+ /* @__PURE__ */ jsx(
1221
+ "textarea",
1222
+ {
1223
+ ref: textareaRef,
1224
+ value: block.content,
1225
+ onChange: handleContentChange,
1226
+ placeholder: "Enter quote...",
1227
+ className: `w-full text-lg leading-relaxed bg-transparent outline-none resize-none ${isDark ? "placeholder:text-slate-500" : "placeholder:text-slate-400"}`,
1228
+ rows: 1
1229
+ }
1230
+ ),
1231
+ /* @__PURE__ */ jsx(
1232
+ "input",
1233
+ {
1234
+ type: "text",
1235
+ value: block.author || "",
1236
+ onChange: handleAuthorChange,
1237
+ placeholder: "— Author name",
1238
+ className: `w-full mt-2 text-sm bg-transparent outline-none ${isDark ? "text-slate-400 placeholder:text-slate-500" : "text-slate-500 placeholder:text-slate-400"}`
1239
+ }
1240
+ )
1241
+ ] }) })
1242
+ ]
1243
+ }
1244
+ );
1245
+ };
1246
+
1247
+ const ListBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
1248
+ const [showToolbar, setShowToolbar] = useState(false);
1249
+ const isDark = theme === "dark";
1250
+ const updateItem = (index, value) => {
1251
+ const newItems = [...block.items];
1252
+ newItems[index] = value;
1253
+ onUpdate({ ...block, items: newItems });
1254
+ };
1255
+ const addItem = (afterIndex) => {
1256
+ const newItems = [...block.items];
1257
+ const insertIndex = afterIndex !== void 0 ? afterIndex + 1 : newItems.length;
1258
+ newItems.splice(insertIndex, 0, "");
1259
+ const newCheckedItems = block.checkedItems ? [...block.checkedItems] : void 0;
1260
+ if (newCheckedItems) {
1261
+ newCheckedItems.splice(insertIndex, 0, false);
1262
+ }
1263
+ onUpdate({ ...block, items: newItems, checkedItems: newCheckedItems });
1264
+ };
1265
+ const removeItem = (index) => {
1266
+ if (block.items.length <= 1) return;
1267
+ const newItems = block.items.filter((_, i) => i !== index);
1268
+ const newCheckedItems = block.checkedItems?.filter((_, i) => i !== index);
1269
+ onUpdate({ ...block, items: newItems, checkedItems: newCheckedItems });
1270
+ };
1271
+ const toggleChecked = (index) => {
1272
+ if (!block.checkedItems) return;
1273
+ const newCheckedItems = [...block.checkedItems];
1274
+ newCheckedItems[index] = !newCheckedItems[index];
1275
+ onUpdate({ ...block, checkedItems: newCheckedItems });
1276
+ };
1277
+ const setListType = (listType) => {
1278
+ const newBlock = {
1279
+ ...block,
1280
+ listType,
1281
+ checkedItems: listType === "checklist" ? block.items.map(() => false) : void 0
1282
+ };
1283
+ onUpdate(newBlock);
1284
+ };
1285
+ const handleKeyDown = (e, index) => {
1286
+ if (e.key === "Enter") {
1287
+ e.preventDefault();
1288
+ addItem(index);
1289
+ setTimeout(() => {
1290
+ const inputs = document.querySelectorAll(`[data-list-input="${block.id}"]`);
1291
+ inputs[index + 1]?.focus();
1292
+ }, 0);
1293
+ } else if (e.key === "Backspace" && block.items[index] === "" && block.items.length > 1) {
1294
+ e.preventDefault();
1295
+ removeItem(index);
1296
+ setTimeout(() => {
1297
+ const inputs = document.querySelectorAll(`[data-list-input="${block.id}"]`);
1298
+ inputs[Math.max(0, index - 1)]?.focus();
1299
+ }, 0);
1300
+ }
1301
+ };
1302
+ const toolbarBtnClass = (isActive) => `px-2 py-1 text-sm rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-700"}`;
1303
+ const ListWrapper = block.listType === "numbered" ? "ol" : "ul";
1304
+ return /* @__PURE__ */ jsxs(
1305
+ "div",
1306
+ {
1307
+ className: "group relative",
1308
+ onFocus: () => setShowToolbar(true),
1309
+ onBlur: (e) => {
1310
+ if (!e.currentTarget.contains(e.relatedTarget)) {
1311
+ setShowToolbar(false);
1312
+ }
1313
+ },
1314
+ children: [
1315
+ showToolbar && !readOnly && /* @__PURE__ */ jsxs("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: [
1316
+ /* @__PURE__ */ jsx(
1317
+ "button",
1318
+ {
1319
+ onClick: () => setListType("bullet"),
1320
+ className: toolbarBtnClass(block.listType === "bullet"),
1321
+ children: "• Bullet"
1322
+ }
1323
+ ),
1324
+ /* @__PURE__ */ jsx(
1325
+ "button",
1326
+ {
1327
+ onClick: () => setListType("numbered"),
1328
+ className: toolbarBtnClass(block.listType === "numbered"),
1329
+ children: "1. Numbered"
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsx(
1333
+ "button",
1334
+ {
1335
+ onClick: () => setListType("checklist"),
1336
+ className: toolbarBtnClass(block.listType === "checklist"),
1337
+ children: "☑ Checklist"
1338
+ }
1339
+ )
1340
+ ] }),
1341
+ /* @__PURE__ */ jsx(ListWrapper, { className: `space-y-1 ${block.listType === "numbered" ? "list-decimal" : block.listType === "bullet" ? "list-disc" : "list-none"} ${block.listType !== "checklist" ? "pl-6" : ""} ${isDark ? "text-slate-200" : "text-slate-700"}`, children: block.items.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "group/item flex items-center gap-2", children: [
1342
+ block.listType === "checklist" && /* @__PURE__ */ jsx(
1343
+ "button",
1344
+ {
1345
+ onClick: () => toggleChecked(index),
1346
+ disabled: readOnly,
1347
+ className: `flex-shrink-0 w-5 h-5 rounded border-2 flex items-center justify-center transition-colors ${block.checkedItems?.[index] ? "bg-indigo-500 border-indigo-500 text-white" : isDark ? "border-slate-500 hover:border-indigo-400" : "border-slate-300 hover:border-indigo-400"}`,
1348
+ children: block.checkedItems?.[index] && /* @__PURE__ */ jsx(CheckIcon, { size: 12 })
1349
+ }
1350
+ ),
1351
+ readOnly ? /* @__PURE__ */ jsx("span", { className: block.checkedItems?.[index] ? isDark ? "line-through text-slate-500" : "line-through text-slate-400" : "", children: item }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1352
+ /* @__PURE__ */ jsx(
1353
+ "input",
1354
+ {
1355
+ type: "text",
1356
+ "data-list-input": block.id,
1357
+ value: item,
1358
+ onChange: (e) => updateItem(index, e.target.value),
1359
+ onKeyDown: (e) => handleKeyDown(e, index),
1360
+ placeholder: "List item...",
1361
+ className: `flex-1 outline-none bg-transparent ${block.checkedItems?.[index] ? isDark ? "line-through text-slate-500" : "line-through text-slate-400" : ""} ${isDark ? "placeholder:text-slate-500" : "placeholder:text-slate-400"}`
1362
+ }
1363
+ ),
1364
+ /* @__PURE__ */ jsx(
1365
+ "button",
1366
+ {
1367
+ onClick: () => removeItem(index),
1368
+ className: `opacity-0 group-hover/item:opacity-100 p-1 transition-all ${isDark ? "text-slate-500 hover:text-red-400" : "text-slate-400 hover:text-red-500"}`,
1369
+ children: /* @__PURE__ */ jsx(TrashIcon, { size: 14 })
1370
+ }
1371
+ )
1372
+ ] })
1373
+ ] }, index)) }),
1374
+ !readOnly && /* @__PURE__ */ jsxs(
1375
+ "button",
1376
+ {
1377
+ onClick: () => addItem(),
1378
+ className: `mt-2 flex items-center gap-1 text-sm transition-colors ${isDark ? "text-slate-500 hover:text-slate-300" : "text-slate-400 hover:text-slate-600"}`,
1379
+ children: [
1380
+ /* @__PURE__ */ jsx(PlusIcon, { size: 14 }),
1381
+ " Add item"
1382
+ ]
1383
+ }
1384
+ )
1385
+ ]
1386
+ }
1387
+ );
1388
+ };
1389
+
1390
+ const getVariantStyles = (isDark) => ({
1391
+ info: {
1392
+ bg: isDark ? "bg-blue-950/40" : "bg-blue-50",
1393
+ border: isDark ? "border-blue-800" : "border-blue-200",
1394
+ icon: "text-blue-500",
1395
+ title: isDark ? "text-blue-300" : "text-blue-800",
1396
+ content: isDark ? "text-blue-200" : "text-blue-700"
1397
+ },
1398
+ warning: {
1399
+ bg: isDark ? "bg-amber-950/40" : "bg-amber-50",
1400
+ border: isDark ? "border-amber-800" : "border-amber-200",
1401
+ icon: "text-amber-500",
1402
+ title: isDark ? "text-amber-300" : "text-amber-800",
1403
+ content: isDark ? "text-amber-200" : "text-amber-700"
1404
+ },
1405
+ success: {
1406
+ bg: isDark ? "bg-emerald-950/40" : "bg-emerald-50",
1407
+ border: isDark ? "border-emerald-800" : "border-emerald-200",
1408
+ icon: "text-emerald-500",
1409
+ title: isDark ? "text-emerald-300" : "text-emerald-800",
1410
+ content: isDark ? "text-emerald-200" : "text-emerald-700"
1411
+ },
1412
+ error: {
1413
+ bg: isDark ? "bg-red-950/40" : "bg-red-50",
1414
+ border: isDark ? "border-red-800" : "border-red-200",
1415
+ icon: "text-red-500",
1416
+ title: isDark ? "text-red-300" : "text-red-800",
1417
+ content: isDark ? "text-red-200" : "text-red-700"
1418
+ },
1419
+ tip: {
1420
+ bg: isDark ? "bg-violet-950/40" : "bg-violet-50",
1421
+ border: isDark ? "border-violet-800" : "border-violet-200",
1422
+ icon: "text-violet-500",
1423
+ title: isDark ? "text-violet-300" : "text-violet-800",
1424
+ content: isDark ? "text-violet-200" : "text-violet-700"
1425
+ }
1426
+ });
1427
+ const variantIcons = {
1428
+ info: "ℹ️",
1429
+ warning: "⚠️",
1430
+ success: "✅",
1431
+ error: "❌",
1432
+ tip: "💡"
1433
+ };
1434
+ const variantLabels = {
1435
+ info: "Info",
1436
+ warning: "Warning",
1437
+ success: "Success",
1438
+ error: "Error",
1439
+ tip: "Tip"
1440
+ };
1441
+ const CalloutBlock = ({ block, onUpdate, readOnly, theme = "light" }) => {
1442
+ const [showToolbar, setShowToolbar] = useState(false);
1443
+ const textareaRef = useRef(null);
1444
+ const isDark = theme === "dark";
1445
+ const handleContentChange = (e) => {
1446
+ onUpdate({ ...block, content: e.target.value });
1447
+ if (textareaRef.current) {
1448
+ textareaRef.current.style.height = "auto";
1449
+ textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
1450
+ }
1451
+ };
1452
+ const handleTitleChange = (e) => {
1453
+ onUpdate({ ...block, title: e.target.value });
1454
+ };
1455
+ const setVariant = (variant) => {
1456
+ onUpdate({ ...block, variant });
1457
+ };
1458
+ const variantStyles = getVariantStyles(isDark);
1459
+ const styles = variantStyles[block.variant];
1460
+ const toolbarBtnClass = (isActive) => `px-2 py-1 text-sm rounded-lg transition-colors ${isActive ? isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900" : isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-700"}`;
1461
+ return /* @__PURE__ */ jsxs(
1462
+ "div",
1463
+ {
1464
+ className: "group relative",
1465
+ onFocus: () => setShowToolbar(true),
1466
+ onBlur: (e) => {
1467
+ if (!e.currentTarget.contains(e.relatedTarget)) {
1468
+ setShowToolbar(false);
1469
+ }
1470
+ },
1471
+ children: [
1472
+ showToolbar && !readOnly && /* @__PURE__ */ jsx("div", { className: `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`, children: Object.keys(variantStyles).map((variant) => /* @__PURE__ */ jsxs(
1473
+ "button",
1474
+ {
1475
+ onClick: () => setVariant(variant),
1476
+ className: toolbarBtnClass(block.variant === variant),
1477
+ children: [
1478
+ variantIcons[variant],
1479
+ " ",
1480
+ variantLabels[variant]
1481
+ ]
1482
+ },
1483
+ variant
1484
+ )) }),
1485
+ /* @__PURE__ */ jsx("div", { className: `rounded-xl border ${styles.bg} ${styles.border} p-4`, children: /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
1486
+ /* @__PURE__ */ jsx("span", { className: `text-xl flex-shrink-0 ${styles.icon}`, children: variantIcons[block.variant] }),
1487
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: readOnly ? /* @__PURE__ */ jsxs(Fragment, { children: [
1488
+ block.title && /* @__PURE__ */ jsx("p", { className: `font-semibold mb-1 ${styles.title}`, children: block.title }),
1489
+ /* @__PURE__ */ jsx("p", { className: `whitespace-pre-wrap break-words ${styles.content}`, children: block.content })
1490
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1491
+ /* @__PURE__ */ jsx(
1492
+ "input",
1493
+ {
1494
+ type: "text",
1495
+ value: block.title || "",
1496
+ onChange: handleTitleChange,
1497
+ placeholder: `${variantLabels[block.variant]} title (optional)`,
1498
+ className: `w-full font-semibold mb-1 bg-transparent outline-none ${styles.title} ${isDark ? "placeholder:text-slate-500" : "placeholder:text-slate-400"}`
1499
+ }
1500
+ ),
1501
+ /* @__PURE__ */ jsx(
1502
+ "textarea",
1503
+ {
1504
+ ref: textareaRef,
1505
+ value: block.content,
1506
+ onChange: handleContentChange,
1507
+ placeholder: "Enter callout content...",
1508
+ className: `w-full bg-transparent outline-none resize-none ${styles.content} ${isDark ? "placeholder:text-slate-500" : "placeholder:text-slate-400"}`,
1509
+ rows: 1,
1510
+ onInput: (e) => {
1511
+ const target = e.target;
1512
+ target.style.height = "auto";
1513
+ target.style.height = target.scrollHeight + "px";
1514
+ }
1515
+ }
1516
+ )
1517
+ ] }) })
1518
+ ] }) })
1519
+ ]
1520
+ }
1521
+ );
1522
+ };
1523
+
1524
+ const BlockWrapper = ({
1525
+ block,
1526
+ index,
1527
+ totalBlocks,
1528
+ onUpdate,
1529
+ onDelete,
1530
+ onDuplicate,
1531
+ onMoveUp,
1532
+ onMoveDown,
1533
+ onAddBlock,
1534
+ readOnly,
1535
+ isDragging,
1536
+ onDragStart,
1537
+ onDragEnd,
1538
+ onDragOver,
1539
+ onDrop,
1540
+ theme = "light"
1541
+ }) => {
1542
+ const [showAddMenu, setShowAddMenu] = useState(false);
1543
+ const [isSelected, setIsSelected] = useState(false);
1544
+ const wrapperRef = useRef(null);
1545
+ const isDark = theme === "dark";
1546
+ useEffect(() => {
1547
+ const handleClickOutside = (event) => {
1548
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
1549
+ setIsSelected(false);
1550
+ setShowAddMenu(false);
1551
+ }
1552
+ };
1553
+ if (isSelected) {
1554
+ document.addEventListener("mousedown", handleClickOutside);
1555
+ }
1556
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1557
+ }, [isSelected]);
1558
+ const handleBlockClick = useCallback((e) => {
1559
+ const target = e.target;
1560
+ const interactiveElements = ["INPUT", "TEXTAREA", "BUTTON", "SELECT"];
1561
+ if (interactiveElements.includes(target.tagName)) {
1562
+ return;
1563
+ }
1564
+ if (!readOnly) {
1565
+ setIsSelected(true);
1566
+ }
1567
+ }, [readOnly]);
1568
+ const renderBlock = useCallback(() => {
1569
+ const commonProps = { readOnly, theme };
1570
+ switch (block.type) {
1571
+ case "text":
1572
+ return /* @__PURE__ */ jsx(TextBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1573
+ case "heading":
1574
+ return /* @__PURE__ */ jsx(HeadingBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1575
+ case "image":
1576
+ return /* @__PURE__ */ jsx(ImageBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1577
+ case "code":
1578
+ return /* @__PURE__ */ jsx(CodeBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1579
+ case "table":
1580
+ return /* @__PURE__ */ jsx(TableBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1581
+ case "divider":
1582
+ return /* @__PURE__ */ jsx(DividerBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1583
+ case "quote":
1584
+ return /* @__PURE__ */ jsx(QuoteBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1585
+ case "list":
1586
+ return /* @__PURE__ */ jsx(ListBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1587
+ case "callout":
1588
+ return /* @__PURE__ */ jsx(CalloutBlock, { block, onUpdate: (b) => onUpdate(b), ...commonProps });
1589
+ default: {
1590
+ const _exhaustiveCheck = block;
1591
+ return /* @__PURE__ */ jsxs("div", { children: [
1592
+ "Unknown block type: ",
1593
+ _exhaustiveCheck.type
1594
+ ] });
1595
+ }
1596
+ }
1597
+ }, [block, onUpdate, readOnly, theme]);
1598
+ const buttonBaseClass = `p-1.5 rounded-lg transition-all ${isDark ? "text-slate-400 hover:text-slate-200 hover:bg-slate-700" : "text-slate-500 hover:text-slate-700 hover:bg-slate-100"}`;
1599
+ const showControls = !readOnly && isSelected;
1600
+ const handleKeyDown = useCallback((e) => {
1601
+ if (readOnly || !isSelected) return;
1602
+ switch (e.key) {
1603
+ case "Escape":
1604
+ setIsSelected(false);
1605
+ setShowAddMenu(false);
1606
+ break;
1607
+ case "Delete":
1608
+ case "Backspace":
1609
+ if (!e.target.matches("input, textarea, [contenteditable]")) {
1610
+ e.preventDefault();
1611
+ onDelete();
1612
+ }
1613
+ break;
1614
+ case "ArrowUp":
1615
+ if (e.altKey) {
1616
+ e.preventDefault();
1617
+ onMoveUp();
1618
+ }
1619
+ break;
1620
+ case "ArrowDown":
1621
+ if (e.altKey) {
1622
+ e.preventDefault();
1623
+ onMoveDown();
1624
+ }
1625
+ break;
1626
+ case "d":
1627
+ if (e.ctrlKey || e.metaKey) {
1628
+ e.preventDefault();
1629
+ onDuplicate();
1630
+ }
1631
+ break;
1632
+ }
1633
+ }, [readOnly, isSelected, onDelete, onMoveUp, onMoveDown, onDuplicate]);
1634
+ const editModeBorderClass = !readOnly ? isSelected ? isDark ? "ring-2 ring-indigo-500 rounded-lg bg-slate-800/30" : "ring-2 ring-indigo-500 rounded-lg bg-indigo-50/30" : isDark ? "border border-slate-700 rounded-lg hover:border-slate-500" : "border border-slate-200 rounded-lg hover:border-slate-400" : "";
1635
+ const blockTypeLabel = block.type.charAt(0).toUpperCase() + block.type.slice(1);
1636
+ return /* @__PURE__ */ jsxs(
1637
+ "div",
1638
+ {
1639
+ ref: wrapperRef,
1640
+ role: "article",
1641
+ "aria-label": `${blockTypeLabel} block ${index + 1} of ${totalBlocks}`,
1642
+ "aria-selected": isSelected,
1643
+ tabIndex: readOnly ? -1 : 0,
1644
+ className: `group relative transition-all ${isDragging ? "opacity-50" : ""} ${editModeBorderClass} ${!readOnly ? "cursor-pointer" : ""} focus:outline-none`,
1645
+ onClick: handleBlockClick,
1646
+ onKeyDown: handleKeyDown,
1647
+ draggable: !readOnly && isSelected,
1648
+ onDragStart,
1649
+ onDragEnd,
1650
+ onDragOver,
1651
+ onDrop,
1652
+ "data-block-index": index,
1653
+ "data-block-type": block.type,
1654
+ children: [
1655
+ showControls && /* @__PURE__ */ jsxs(
1656
+ "div",
1657
+ {
1658
+ role: "toolbar",
1659
+ "aria-label": "Block controls",
1660
+ className: `flex items-center justify-between gap-1 px-2 py-1.5 mb-1 rounded-t-lg border-b ${isDark ? "bg-slate-800 border-slate-700" : "bg-slate-50 border-slate-200"}`,
1661
+ children: [
1662
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Reorder controls", children: [
1663
+ /* @__PURE__ */ jsx(
1664
+ "button",
1665
+ {
1666
+ type: "button",
1667
+ className: `${buttonBaseClass} cursor-grab active:cursor-grabbing`,
1668
+ title: "Drag to reorder",
1669
+ "aria-label": "Drag to reorder block",
1670
+ children: /* @__PURE__ */ jsx(DragIcon, { size: 16 })
1671
+ }
1672
+ ),
1673
+ /* @__PURE__ */ jsx(
1674
+ "button",
1675
+ {
1676
+ type: "button",
1677
+ onClick: (e) => {
1678
+ e.stopPropagation();
1679
+ onMoveUp();
1680
+ },
1681
+ disabled: index === 0,
1682
+ className: `${buttonBaseClass} disabled:opacity-30 disabled:cursor-not-allowed`,
1683
+ title: "Move up (Alt+↑)",
1684
+ "aria-label": "Move block up",
1685
+ "aria-disabled": index === 0,
1686
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { size: 16 })
1687
+ }
1688
+ ),
1689
+ /* @__PURE__ */ jsx(
1690
+ "button",
1691
+ {
1692
+ type: "button",
1693
+ onClick: (e) => {
1694
+ e.stopPropagation();
1695
+ onMoveDown();
1696
+ },
1697
+ disabled: index === totalBlocks - 1,
1698
+ className: `${buttonBaseClass} disabled:opacity-30 disabled:cursor-not-allowed`,
1699
+ title: "Move down (Alt+↓)",
1700
+ "aria-label": "Move block down",
1701
+ "aria-disabled": index === totalBlocks - 1,
1702
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
1703
+ }
1704
+ )
1705
+ ] }),
1706
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Block actions", children: [
1707
+ /* @__PURE__ */ jsx(
1708
+ "button",
1709
+ {
1710
+ type: "button",
1711
+ onClick: (e) => {
1712
+ e.stopPropagation();
1713
+ setShowAddMenu(!showAddMenu);
1714
+ },
1715
+ className: `${buttonBaseClass} hover:!text-indigo-500`,
1716
+ title: "Add block below",
1717
+ "aria-label": "Add new block below",
1718
+ "aria-expanded": showAddMenu,
1719
+ "aria-haspopup": "menu",
1720
+ children: /* @__PURE__ */ jsx(PlusIcon, { size: 16 })
1721
+ }
1722
+ ),
1723
+ /* @__PURE__ */ jsx(
1724
+ "button",
1725
+ {
1726
+ type: "button",
1727
+ onClick: (e) => {
1728
+ e.stopPropagation();
1729
+ onDuplicate();
1730
+ },
1731
+ className: `${buttonBaseClass} hover:!text-indigo-500`,
1732
+ title: "Duplicate (Ctrl+D)",
1733
+ "aria-label": "Duplicate block",
1734
+ children: /* @__PURE__ */ jsx(CopyIcon, { size: 16 })
1735
+ }
1736
+ ),
1737
+ /* @__PURE__ */ jsx(
1738
+ "button",
1739
+ {
1740
+ type: "button",
1741
+ onClick: (e) => {
1742
+ e.stopPropagation();
1743
+ onDelete();
1744
+ },
1745
+ className: `${buttonBaseClass} hover:!text-red-500`,
1746
+ title: "Delete",
1747
+ "aria-label": "Delete block",
1748
+ children: /* @__PURE__ */ jsx(TrashIcon, { size: 16 })
1749
+ }
1750
+ )
1751
+ ] }),
1752
+ showAddMenu && /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 -translate-x-1/2 top-full mt-1 z-50", children: /* @__PURE__ */ jsx(
1753
+ AddBlockMenu,
1754
+ {
1755
+ onAdd: (type) => {
1756
+ onAddBlock(type, index);
1757
+ setShowAddMenu(false);
1758
+ },
1759
+ onClose: () => setShowAddMenu(false),
1760
+ theme
1761
+ }
1762
+ ) })
1763
+ ]
1764
+ }
1765
+ ),
1766
+ /* @__PURE__ */ jsx("div", { className: "relative px-2 py-2", children: renderBlock() })
1767
+ ]
1768
+ }
1769
+ );
1770
+ };
1771
+
1772
+ const ThemeContext = createContext("light");
1773
+ const PagesEditor = ({
1774
+ initialData,
1775
+ onChange,
1776
+ debounceDelay = 300,
1777
+ readOnly = false,
1778
+ placeholder = "Start creating your content...",
1779
+ className = "",
1780
+ theme = "light"
1781
+ }) => {
1782
+ const [data, setData] = useState(() => {
1783
+ if (initialData && validateEditorData(initialData)) {
1784
+ return initialData;
1785
+ }
1786
+ return createEmptyEditorData();
1787
+ });
1788
+ const [showInitialAddMenu, setShowInitialAddMenu] = useState(false);
1789
+ const [draggedIndex, setDraggedIndex] = useState(null);
1790
+ const [dragOverIndex, setDragOverIndex] = useState(null);
1791
+ const debounceTimerRef = useRef(null);
1792
+ const isDark = theme === "dark";
1793
+ const createBlock = useCallback((type) => {
1794
+ switch (type) {
1795
+ case "text":
1796
+ return createTextBlock();
1797
+ case "heading":
1798
+ return createHeadingBlock();
1799
+ case "image":
1800
+ return createImageBlock();
1801
+ case "code":
1802
+ return createCodeBlock();
1803
+ case "table":
1804
+ return createTableBlock();
1805
+ case "divider":
1806
+ return createDividerBlock();
1807
+ case "quote":
1808
+ return createQuoteBlock();
1809
+ case "list":
1810
+ return createListBlock();
1811
+ case "callout":
1812
+ return createCalloutBlock();
1813
+ default:
1814
+ return createTextBlock();
1815
+ }
1816
+ }, []);
1817
+ useEffect(() => {
1818
+ if (initialData && validateEditorData(initialData)) {
1819
+ setData(initialData);
1820
+ }
1821
+ }, [initialData]);
1822
+ useEffect(() => {
1823
+ return () => {
1824
+ if (debounceTimerRef.current) {
1825
+ clearTimeout(debounceTimerRef.current);
1826
+ }
1827
+ };
1828
+ }, []);
1829
+ const updateData = useCallback(
1830
+ (newBlocks) => {
1831
+ const newData = {
1832
+ ...data,
1833
+ blocks: newBlocks,
1834
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1835
+ };
1836
+ setData(newData);
1837
+ if (onChange) {
1838
+ if (debounceTimerRef.current) {
1839
+ clearTimeout(debounceTimerRef.current);
1840
+ }
1841
+ debounceTimerRef.current = setTimeout(() => {
1842
+ onChange(newData);
1843
+ }, debounceDelay);
1844
+ }
1845
+ },
1846
+ [data, onChange, debounceDelay]
1847
+ );
1848
+ const addBlock = useCallback(
1849
+ (type, afterIndex) => {
1850
+ const newBlock = createBlock(type);
1851
+ const newBlocks = [...data.blocks];
1852
+ if (afterIndex !== void 0 && afterIndex >= 0) {
1853
+ newBlocks.splice(afterIndex + 1, 0, newBlock);
1854
+ } else {
1855
+ newBlocks.push(newBlock);
1856
+ }
1857
+ updateData(newBlocks);
1858
+ setShowInitialAddMenu(false);
1859
+ },
1860
+ [data.blocks, updateData, createBlock]
1861
+ );
1862
+ const updateBlock = useCallback(
1863
+ (index, block) => {
1864
+ const newBlocks = [...data.blocks];
1865
+ newBlocks[index] = block;
1866
+ updateData(newBlocks);
1867
+ },
1868
+ [data.blocks, updateData]
1869
+ );
1870
+ const deleteBlock = useCallback(
1871
+ (index) => {
1872
+ const newBlocks = data.blocks.filter((_, i) => i !== index);
1873
+ updateData(newBlocks);
1874
+ },
1875
+ [data.blocks, updateData]
1876
+ );
1877
+ const duplicateBlock = useCallback(
1878
+ (index) => {
1879
+ const block = data.blocks[index];
1880
+ if (!block) return;
1881
+ const newBlock = cloneBlock(block);
1882
+ const newBlocks = [...data.blocks];
1883
+ newBlocks.splice(index + 1, 0, newBlock);
1884
+ updateData(newBlocks);
1885
+ },
1886
+ [data.blocks, updateData]
1887
+ );
1888
+ const moveBlockUp = useCallback(
1889
+ (index) => {
1890
+ if (index === 0) return;
1891
+ const newBlocks = moveArrayItem(data.blocks, index, index - 1);
1892
+ updateData(newBlocks);
1893
+ },
1894
+ [data.blocks, updateData]
1895
+ );
1896
+ const moveBlockDown = useCallback(
1897
+ (index) => {
1898
+ if (index === data.blocks.length - 1) return;
1899
+ const newBlocks = moveArrayItem(data.blocks, index, index + 1);
1900
+ updateData(newBlocks);
1901
+ },
1902
+ [data.blocks, updateData]
1903
+ );
1904
+ const handleDragStart = (e, index) => {
1905
+ setDraggedIndex(index);
1906
+ e.dataTransfer.effectAllowed = "move";
1907
+ e.dataTransfer.setData("text/plain", index.toString());
1908
+ };
1909
+ const handleDragEnd = () => {
1910
+ setDraggedIndex(null);
1911
+ setDragOverIndex(null);
1912
+ };
1913
+ const handleDragOver = (e, index) => {
1914
+ e.preventDefault();
1915
+ e.dataTransfer.dropEffect = "move";
1916
+ setDragOverIndex(index);
1917
+ };
1918
+ const handleDrop = (e, dropIndex) => {
1919
+ e.preventDefault();
1920
+ const dragIndex = parseInt(e.dataTransfer.getData("text/plain"), 10);
1921
+ if (dragIndex !== dropIndex && !isNaN(dragIndex)) {
1922
+ const newBlocks = moveArrayItem(data.blocks, dragIndex, dropIndex);
1923
+ updateData(newBlocks);
1924
+ }
1925
+ setDraggedIndex(null);
1926
+ setDragOverIndex(null);
1927
+ };
1928
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx("div", { className: `pages-editor w-full ${isDark ? "pe-dark" : "pe-light"} ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative py-4", children: [
1929
+ data.blocks.length === 0 ? (
1930
+ // Empty state
1931
+ /* @__PURE__ */ jsxs("div", { className: "text-center py-16", children: [
1932
+ /* @__PURE__ */ jsx("p", { className: `mb-4 ${isDark ? "text-slate-500" : "text-slate-400"}`, children: placeholder }),
1933
+ !readOnly && /* @__PURE__ */ jsxs("div", { className: "relative inline-block", children: [
1934
+ /* @__PURE__ */ jsxs(
1935
+ "button",
1936
+ {
1937
+ onClick: () => setShowInitialAddMenu(true),
1938
+ className: "flex items-center gap-2 px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors mx-auto font-medium shadow-sm",
1939
+ children: [
1940
+ /* @__PURE__ */ jsx(PlusIcon, { size: 20 }),
1941
+ "Add First Block"
1942
+ ]
1943
+ }
1944
+ ),
1945
+ showInitialAddMenu && /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 -translate-x-1/2 top-full mt-2 z-50", children: /* @__PURE__ */ jsx(
1946
+ AddBlockMenu,
1947
+ {
1948
+ onAdd: (type) => addBlock(type),
1949
+ onClose: () => setShowInitialAddMenu(false),
1950
+ theme
1951
+ }
1952
+ ) })
1953
+ ] })
1954
+ ] })
1955
+ ) : (
1956
+ // Blocks list
1957
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: data.blocks.map((block, index) => /* @__PURE__ */ jsx(
1958
+ "div",
1959
+ {
1960
+ className: `transition-all duration-200 ${dragOverIndex === index && draggedIndex !== index ? "border-t-2 border-indigo-500 pt-2" : ""}`,
1961
+ children: /* @__PURE__ */ jsx(
1962
+ BlockWrapper,
1963
+ {
1964
+ block,
1965
+ index,
1966
+ totalBlocks: data.blocks.length,
1967
+ onUpdate: (updatedBlock) => updateBlock(index, updatedBlock),
1968
+ onDelete: () => deleteBlock(index),
1969
+ onDuplicate: () => duplicateBlock(index),
1970
+ onMoveUp: () => moveBlockUp(index),
1971
+ onMoveDown: () => moveBlockDown(index),
1972
+ onAddBlock: (type, afterIdx) => addBlock(type, afterIdx),
1973
+ readOnly,
1974
+ isDragging: draggedIndex === index,
1975
+ onDragStart: (e) => handleDragStart(e, index),
1976
+ onDragEnd: handleDragEnd,
1977
+ onDragOver: (e) => handleDragOver(e, index),
1978
+ onDrop: (e) => handleDrop(e, index),
1979
+ theme
1980
+ }
1981
+ )
1982
+ },
1983
+ block.id
1984
+ )) })
1985
+ ),
1986
+ !readOnly && data.blocks.length > 0 && /* @__PURE__ */ jsx("div", { className: "relative mt-6", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1987
+ /* @__PURE__ */ jsxs(
1988
+ "button",
1989
+ {
1990
+ onClick: () => setShowInitialAddMenu(true),
1991
+ className: `flex items-center gap-2 px-4 py-2 border-2 border-dashed rounded-lg transition-colors ${isDark ? "text-slate-500 hover:text-slate-300 border-slate-700 hover:border-slate-500" : "text-slate-400 hover:text-slate-600 border-slate-300 hover:border-slate-400"}`,
1992
+ children: [
1993
+ /* @__PURE__ */ jsx(PlusIcon, { size: 16 }),
1994
+ "Add Block"
1995
+ ]
1996
+ }
1997
+ ),
1998
+ showInitialAddMenu && /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 -translate-x-1/2 bottom-full mb-2 z-50", children: /* @__PURE__ */ jsx(
1999
+ AddBlockMenu,
2000
+ {
2001
+ onAdd: (type) => addBlock(type),
2002
+ onClose: () => setShowInitialAddMenu(false),
2003
+ theme
2004
+ }
2005
+ ) })
2006
+ ] }) }) })
2007
+ ] }) }) });
2008
+ };
2009
+
2010
+ function useBlockToolbar({ readOnly = false } = {}) {
2011
+ const [showToolbar, setShowToolbar] = useState(false);
2012
+ const handleFocus = useCallback(() => {
2013
+ if (!readOnly) {
2014
+ setShowToolbar(true);
2015
+ }
2016
+ }, [readOnly]);
2017
+ const handleBlur = useCallback((e) => {
2018
+ if (!e.currentTarget.contains(e.relatedTarget)) {
2019
+ setShowToolbar(false);
2020
+ }
2021
+ }, []);
2022
+ return {
2023
+ showToolbar: showToolbar && !readOnly,
2024
+ handleFocus,
2025
+ handleBlur,
2026
+ containerProps: {
2027
+ onFocus: handleFocus,
2028
+ onBlur: handleBlur
2029
+ }
2030
+ };
2031
+ }
2032
+
2033
+ function useAutoResize(content) {
2034
+ const ref = useRef(null);
2035
+ useEffect(() => {
2036
+ const element = ref.current;
2037
+ if (element) {
2038
+ element.style.height = "auto";
2039
+ element.style.height = `${element.scrollHeight}px`;
2040
+ }
2041
+ }, [content]);
2042
+ return ref;
2043
+ }
2044
+
2045
+ function useClickOutside(ref, handler, enabled = true) {
2046
+ useEffect(() => {
2047
+ if (!enabled) return;
2048
+ const handleClickOutside = (event) => {
2049
+ if (ref.current && !ref.current.contains(event.target)) {
2050
+ handler();
2051
+ }
2052
+ };
2053
+ document.addEventListener("mousedown", handleClickOutside);
2054
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2055
+ }, [ref, handler, enabled]);
2056
+ }
2057
+
2058
+ const isDarkTheme = (theme) => theme === "dark";
2059
+ const getToolbarButtonClass = (isActive, isDark) => {
2060
+ const baseClass = "p-2 rounded-lg transition-colors";
2061
+ if (isActive) {
2062
+ return `${baseClass} ${isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900"}`;
2063
+ }
2064
+ return `${baseClass} ${isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-600"}`;
2065
+ };
2066
+ const getSmallToolbarButtonClass = (isActive, isDark) => {
2067
+ const baseClass = "px-2 py-1 text-sm rounded-lg transition-colors";
2068
+ if (isActive) {
2069
+ return `${baseClass} ${isDark ? "bg-slate-600 text-white" : "bg-slate-200 text-slate-900"}`;
2070
+ }
2071
+ return `${baseClass} ${isDark ? "hover:bg-slate-700 text-slate-300" : "hover:bg-slate-100 text-slate-700"}`;
2072
+ };
2073
+ const getToolbarContainerClass = (isDark) => {
2074
+ return `absolute -top-12 left-0 flex items-center gap-1 p-1.5 rounded-xl shadow-lg border z-10 ${isDark ? "bg-slate-800 border-slate-600" : "bg-white border-slate-200"}`;
2075
+ };
2076
+ const getToolbarDividerClass = (isDark) => {
2077
+ return `w-px h-6 mx-1 ${isDark ? "bg-slate-600" : "bg-slate-200"}`;
2078
+ };
2079
+ const getPlaceholderClass = (isDark) => {
2080
+ return isDark ? "placeholder:text-slate-500" : "placeholder:text-slate-400";
2081
+ };
2082
+ const getMutedTextClass = (isDark) => {
2083
+ return isDark ? "text-slate-500" : "text-slate-400";
2084
+ };
2085
+ const getPrimaryTextClass = (isDark) => {
2086
+ return isDark ? "text-slate-200" : "text-slate-800";
2087
+ };
2088
+ const getSecondaryTextClass = (isDark) => {
2089
+ return isDark ? "text-slate-300" : "text-slate-700";
2090
+ };
2091
+ const CALLOUT_VARIANT_STYLES = {
2092
+ info: {
2093
+ light: {
2094
+ bg: "bg-blue-50",
2095
+ border: "border-blue-200",
2096
+ icon: "text-blue-500",
2097
+ title: "text-blue-800",
2098
+ content: "text-blue-700"
2099
+ },
2100
+ dark: {
2101
+ bg: "bg-blue-950/40",
2102
+ border: "border-blue-800",
2103
+ icon: "text-blue-500",
2104
+ title: "text-blue-300",
2105
+ content: "text-blue-200"
2106
+ }
2107
+ },
2108
+ warning: {
2109
+ light: {
2110
+ bg: "bg-amber-50",
2111
+ border: "border-amber-200",
2112
+ icon: "text-amber-500",
2113
+ title: "text-amber-800",
2114
+ content: "text-amber-700"
2115
+ },
2116
+ dark: {
2117
+ bg: "bg-amber-950/40",
2118
+ border: "border-amber-800",
2119
+ icon: "text-amber-500",
2120
+ title: "text-amber-300",
2121
+ content: "text-amber-200"
2122
+ }
2123
+ },
2124
+ success: {
2125
+ light: {
2126
+ bg: "bg-emerald-50",
2127
+ border: "border-emerald-200",
2128
+ icon: "text-emerald-500",
2129
+ title: "text-emerald-800",
2130
+ content: "text-emerald-700"
2131
+ },
2132
+ dark: {
2133
+ bg: "bg-emerald-950/40",
2134
+ border: "border-emerald-800",
2135
+ icon: "text-emerald-500",
2136
+ title: "text-emerald-300",
2137
+ content: "text-emerald-200"
2138
+ }
2139
+ },
2140
+ error: {
2141
+ light: {
2142
+ bg: "bg-red-50",
2143
+ border: "border-red-200",
2144
+ icon: "text-red-500",
2145
+ title: "text-red-800",
2146
+ content: "text-red-700"
2147
+ },
2148
+ dark: {
2149
+ bg: "bg-red-950/40",
2150
+ border: "border-red-800",
2151
+ icon: "text-red-500",
2152
+ title: "text-red-300",
2153
+ content: "text-red-200"
2154
+ }
2155
+ },
2156
+ tip: {
2157
+ light: {
2158
+ bg: "bg-violet-50",
2159
+ border: "border-violet-200",
2160
+ icon: "text-violet-500",
2161
+ title: "text-violet-800",
2162
+ content: "text-violet-700"
2163
+ },
2164
+ dark: {
2165
+ bg: "bg-violet-950/40",
2166
+ border: "border-violet-800",
2167
+ icon: "text-violet-500",
2168
+ title: "text-violet-300",
2169
+ content: "text-violet-200"
2170
+ }
2171
+ }
2172
+ };
2173
+ const CALLOUT_ICONS = {
2174
+ info: "ℹ️",
2175
+ warning: "⚠️",
2176
+ success: "✅",
2177
+ error: "❌",
2178
+ tip: "💡"
2179
+ };
2180
+ const CALLOUT_LABELS = {
2181
+ info: "Info",
2182
+ warning: "Warning",
2183
+ success: "Success",
2184
+ error: "Error",
2185
+ tip: "Tip"
2186
+ };
2187
+ const FONT_SIZE_CLASSES = {
2188
+ sm: "text-sm leading-relaxed",
2189
+ base: "text-base leading-relaxed",
2190
+ lg: "text-lg leading-relaxed",
2191
+ xl: "text-xl leading-relaxed"
2192
+ };
2193
+ const ALIGNMENT_CLASSES = {
2194
+ left: "text-left",
2195
+ center: "text-center",
2196
+ right: "text-right",
2197
+ justify: "text-justify"
2198
+ };
2199
+ const QUOTE_STYLE_CLASSES = {
2200
+ default: (isDark) => `border-l-4 pl-4 italic ${isDark ? "border-slate-600" : "border-slate-300"}`,
2201
+ bordered: (isDark) => `border-l-4 pl-4 py-3 rounded-r ${isDark ? "border-indigo-500 bg-indigo-950/30" : "border-indigo-500 bg-indigo-50"}`,
2202
+ modern: (isDark) => `relative pl-10 before:content-['"'] before:absolute before:left-0 before:top-0 before:text-5xl before:leading-none ${isDark ? "before:text-slate-600" : "before:text-slate-300"}`
2203
+ };
2204
+ const DIVIDER_STYLE_CLASSES = {
2205
+ solid: "border-solid",
2206
+ dashed: "border-dashed",
2207
+ dotted: "border-dotted"
2208
+ };
2209
+
2210
+ export { ALIGNMENT_CLASSES, CALLOUT_ICONS, CALLOUT_LABELS, CALLOUT_VARIANT_STYLES, DIVIDER_STYLE_CLASSES, FONT_SIZE_CLASSES, PagesEditor, QUOTE_STYLE_CLASSES, SUPPORTED_LANGUAGES, cloneBlock, createCalloutBlock, createCodeBlock, createDividerBlock, createEmptyEditorData, createHeadingBlock, createImageBlock, createListBlock, createQuoteBlock, createTableBlock, createTextBlock, fileToBase64, generateId, getMutedTextClass, getPlaceholderClass, getPrimaryTextClass, getSecondaryTextClass, getSmallToolbarButtonClass, getToolbarButtonClass, getToolbarContainerClass, getToolbarDividerClass, isDarkTheme, moveArrayItem, useAutoResize, useBlockToolbar, useClickOutside, validateEditorData };