framepexls-ui-lib 2.2.8 → 2.2.11
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/AvatarSquare.d.mts +4 -1
- package/dist/AvatarSquare.d.ts +4 -1
- package/dist/AvatarSquare.js +7 -1
- package/dist/AvatarSquare.mjs +7 -1
- package/dist/Checkbox.d.mts +1 -1
- package/dist/Checkbox.d.ts +1 -1
- package/dist/Checkbox.js +2 -2
- package/dist/Checkbox.mjs +2 -2
- package/dist/DateTimeField.js +1 -1
- package/dist/DateTimeField.mjs +1 -1
- package/dist/HeliipLoader.js +77 -5
- package/dist/HeliipLoader.mjs +77 -5
- package/dist/MarkdownEditor.d.mts +71 -3
- package/dist/MarkdownEditor.d.ts +71 -3
- package/dist/MarkdownEditor.js +2370 -65
- package/dist/MarkdownEditor.mjs +2388 -66
- package/dist/MediaSelector.d.mts +4 -1
- package/dist/MediaSelector.d.ts +4 -1
- package/dist/MediaSelector.js +134 -23
- package/dist/MediaSelector.mjs +135 -24
- package/dist/Sidebar.d.mts +2 -1
- package/dist/Sidebar.d.ts +2 -1
- package/dist/Sidebar.js +82 -19
- package/dist/Sidebar.mjs +82 -19
- package/dist/SupportChatWidget.js +88 -42
- package/dist/SupportChatWidget.mjs +89 -42
- package/dist/UploadCard.js +7 -7
- package/dist/UploadCard.mjs +7 -7
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -0
- package/dist/index.mjs +3 -1
- package/dist/theme.css +47 -0
- package/package.json +8 -8
package/dist/MarkdownEditor.js
CHANGED
|
@@ -29,13 +29,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
var MarkdownEditor_exports = {};
|
|
31
31
|
__export(MarkdownEditor_exports, {
|
|
32
|
-
|
|
32
|
+
MarkdownPreview: () => MarkdownPreview,
|
|
33
|
+
default: () => MarkdownEditor,
|
|
34
|
+
renderMarkdown: () => renderMarkdown
|
|
33
35
|
});
|
|
34
36
|
module.exports = __toCommonJS(MarkdownEditor_exports);
|
|
35
37
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
38
|
var import_react = __toESM(require("react"));
|
|
39
|
+
var import_react_dom = require("react-dom");
|
|
40
|
+
var import_ActionIconButton = __toESM(require("./ActionIconButton"));
|
|
37
41
|
var import_Button = __toESM(require("./Button"));
|
|
42
|
+
var import_Dropdown = __toESM(require("./Dropdown"));
|
|
38
43
|
var import_Textarea = __toESM(require("./Textarea"));
|
|
44
|
+
var import_Tooltip = __toESM(require("./Tooltip"));
|
|
45
|
+
var import_iconos = require("./iconos");
|
|
39
46
|
function cx(...a) {
|
|
40
47
|
return a.filter(Boolean).join(" ");
|
|
41
48
|
}
|
|
@@ -60,11 +67,61 @@ function getSelectedLines(value, start, end) {
|
|
|
60
67
|
const lineEnd = lineEndIdx === -1 ? value.length : lineEndIdx;
|
|
61
68
|
return { from, to, lineStart, lineEnd, block: value.slice(lineStart, lineEnd) };
|
|
62
69
|
}
|
|
70
|
+
function getLineDeleteRange(value, start, end) {
|
|
71
|
+
const { lineStart, lineEnd } = getSelectedLines(value, start, end);
|
|
72
|
+
return { start: lineStart, end: lineEnd };
|
|
73
|
+
}
|
|
74
|
+
function getFenceDeleteRange(value, position) {
|
|
75
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
76
|
+
const lines = getMarkdownLines(value);
|
|
77
|
+
const index = lines.findIndex((line) => position >= line.start && position <= line.end);
|
|
78
|
+
if (index < 0) return null;
|
|
79
|
+
let openIndex = -1;
|
|
80
|
+
for (let i = index; i >= 0; i -= 1) {
|
|
81
|
+
if (/^```/.test((_b = (_a = lines[i]) == null ? void 0 : _a.text.trim()) != null ? _b : "")) {
|
|
82
|
+
openIndex = i;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (openIndex < 0) return null;
|
|
87
|
+
for (let i = openIndex + 1; i < lines.length; i += 1) {
|
|
88
|
+
if (/^```/.test((_d = (_c = lines[i]) == null ? void 0 : _c.text.trim()) != null ? _d : "")) {
|
|
89
|
+
return { start: (_f = (_e = lines[openIndex]) == null ? void 0 : _e.start) != null ? _f : 0, end: (_j = (_i = (_g = lines[i]) == null ? void 0 : _g.end) != null ? _i : (_h = lines[openIndex]) == null ? void 0 : _h.end) != null ? _j : 0 };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function getDeleteBlockRange(value, range) {
|
|
95
|
+
if (range.kind === "code" || range.kind === "diagram") {
|
|
96
|
+
const fenced = getFenceDeleteRange(value, range.start);
|
|
97
|
+
if (fenced) return fenced;
|
|
98
|
+
const block = getCurrentBlockRange(value, range.start);
|
|
99
|
+
return { start: block.from, end: block.to };
|
|
100
|
+
}
|
|
101
|
+
if (range.kind === "paragraph") {
|
|
102
|
+
const block = getCurrentBlockRange(value, range.start);
|
|
103
|
+
return { start: block.from, end: block.to };
|
|
104
|
+
}
|
|
105
|
+
return getLineDeleteRange(value, range.start, range.end);
|
|
106
|
+
}
|
|
107
|
+
function removeMarkdownBlock(value, range) {
|
|
108
|
+
if (!value) return { next: value, cursor: 0 };
|
|
109
|
+
let { start, end } = getDeleteBlockRange(value, range);
|
|
110
|
+
start = Math.max(0, Math.min(value.length, start));
|
|
111
|
+
end = Math.max(start, Math.min(value.length, end));
|
|
112
|
+
if (start === end && start < value.length) end += 1;
|
|
113
|
+
let removeFrom = start;
|
|
114
|
+
let removeTo = end;
|
|
115
|
+
if (removeTo < value.length && value[removeTo] === "\n") {
|
|
116
|
+
removeTo += 1;
|
|
117
|
+
} else if (removeFrom > 0 && value[removeFrom - 1] === "\n") {
|
|
118
|
+
removeFrom -= 1;
|
|
119
|
+
}
|
|
120
|
+
const next = (value.slice(0, removeFrom) + value.slice(removeTo)).replace(/\n{4,}/g, "\n\n\n");
|
|
121
|
+
return { next, cursor: Math.max(0, Math.min(next.length, removeFrom)) };
|
|
122
|
+
}
|
|
63
123
|
function wrapSelection(value, start, end, left, right, fallbackText) {
|
|
64
|
-
const
|
|
65
|
-
const b = Math.max(0, Math.min(value.length, end));
|
|
66
|
-
const from = Math.min(a, b);
|
|
67
|
-
const to = Math.max(a, b);
|
|
124
|
+
const { from, to } = normalizeRange(value, start, end);
|
|
68
125
|
const sel = value.slice(from, to);
|
|
69
126
|
const inner = sel || fallbackText;
|
|
70
127
|
const next = value.slice(0, from) + left + inner + right + value.slice(to);
|
|
@@ -72,10 +129,1208 @@ function wrapSelection(value, start, end, left, right, fallbackText) {
|
|
|
72
129
|
const nextTo = from + left.length + inner.length;
|
|
73
130
|
return { next, selectFrom: nextFrom, selectTo: nextTo };
|
|
74
131
|
}
|
|
132
|
+
function normalizeRange(value, start, end) {
|
|
133
|
+
const a = Math.max(0, Math.min(value.length, start));
|
|
134
|
+
const b = Math.max(0, Math.min(value.length, end));
|
|
135
|
+
return { from: Math.min(a, b), to: Math.max(a, b) };
|
|
136
|
+
}
|
|
137
|
+
function trimRange(value, from, to) {
|
|
138
|
+
var _a, _b;
|
|
139
|
+
let nextFrom = from;
|
|
140
|
+
let nextTo = to;
|
|
141
|
+
while (nextFrom < nextTo && /\s/.test((_a = value[nextFrom]) != null ? _a : "")) nextFrom += 1;
|
|
142
|
+
while (nextTo > nextFrom && /\s/.test((_b = value[nextTo - 1]) != null ? _b : "")) nextTo -= 1;
|
|
143
|
+
return { from: nextFrom, to: nextTo };
|
|
144
|
+
}
|
|
145
|
+
function getCurrentBlockRange(value, cursor) {
|
|
146
|
+
const pos = Math.max(0, Math.min(value.length, cursor));
|
|
147
|
+
let start = value.lastIndexOf("\n\n", Math.max(0, pos - 1));
|
|
148
|
+
start = start === -1 ? 0 : start + 2;
|
|
149
|
+
let end = value.indexOf("\n\n", pos);
|
|
150
|
+
end = end === -1 ? value.length : end;
|
|
151
|
+
return trimRange(value, start, end);
|
|
152
|
+
}
|
|
153
|
+
function getSmartTextRange(value, start, end) {
|
|
154
|
+
const selected = normalizeRange(value, start, end);
|
|
155
|
+
if (selected.from !== selected.to) return trimRange(value, selected.from, selected.to);
|
|
156
|
+
const block = getCurrentBlockRange(value, selected.from);
|
|
157
|
+
if (block.from !== block.to) return block;
|
|
158
|
+
return selected;
|
|
159
|
+
}
|
|
160
|
+
function unwrapFullMarker(value, left, right) {
|
|
161
|
+
if (!value.startsWith(left) || !value.endsWith(right)) return value;
|
|
162
|
+
return value.slice(left.length, value.length - right.length);
|
|
163
|
+
}
|
|
164
|
+
function mapRenderedOffsetToMarkdown(raw, renderedOffset) {
|
|
165
|
+
var _a, _b;
|
|
166
|
+
const boundaries = [];
|
|
167
|
+
let visible = 0;
|
|
168
|
+
const addVisible = (rawIndex) => {
|
|
169
|
+
if (boundaries[visible] === void 0) boundaries[visible] = rawIndex;
|
|
170
|
+
visible += 1;
|
|
171
|
+
boundaries[visible] = rawIndex + 1;
|
|
172
|
+
};
|
|
173
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
174
|
+
if (raw.startsWith("**", index)) {
|
|
175
|
+
index += 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (raw[index] === "*" || raw[index] === "`") continue;
|
|
179
|
+
const colorOpen = (_a = raw.slice(index).match(/^\{color:#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?(?:[0-9a-fA-F]{2})?\}/)) == null ? void 0 : _a[0];
|
|
180
|
+
if (colorOpen) {
|
|
181
|
+
index += colorOpen.length - 1;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (raw.startsWith("{/color}", index)) {
|
|
185
|
+
index += "{/color}".length - 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (raw[index] === "[") {
|
|
189
|
+
const labelEnd = raw.indexOf("]", index + 1);
|
|
190
|
+
if (labelEnd > index && raw[labelEnd + 1] === "(") {
|
|
191
|
+
const urlEnd = raw.indexOf(")", labelEnd + 2);
|
|
192
|
+
if (urlEnd > labelEnd) {
|
|
193
|
+
for (let labelIndex = index + 1; labelIndex < labelEnd; labelIndex += 1) {
|
|
194
|
+
addVisible(labelIndex);
|
|
195
|
+
}
|
|
196
|
+
index = urlEnd;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
addVisible(index);
|
|
202
|
+
}
|
|
203
|
+
if (!boundaries.length) return Math.max(0, Math.min(raw.length, renderedOffset));
|
|
204
|
+
const offset = Math.max(0, Math.min(renderedOffset, boundaries.length - 1));
|
|
205
|
+
return (_b = boundaries[offset]) != null ? _b : raw.length;
|
|
206
|
+
}
|
|
207
|
+
function findMarkdownWrapper(value, from, to, left, right) {
|
|
208
|
+
let index = 0;
|
|
209
|
+
while (index < value.length) {
|
|
210
|
+
const open = value.indexOf(left, index);
|
|
211
|
+
if (open < 0) return null;
|
|
212
|
+
const contentStart = open + left.length;
|
|
213
|
+
const close = value.indexOf(right, contentStart);
|
|
214
|
+
if (close < 0) return null;
|
|
215
|
+
const wrapperEnd = close + right.length;
|
|
216
|
+
if (from >= contentStart && to <= close) {
|
|
217
|
+
return { wrapperStart: open, wrapperEnd, contentStart, contentEnd: close };
|
|
218
|
+
}
|
|
219
|
+
index = wrapperEnd;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
function findLinkWrapper(value, from, to) {
|
|
224
|
+
var _a, _b, _c;
|
|
225
|
+
const pattern = /\[([^\]]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+|\/[^\s)]+)\)/g;
|
|
226
|
+
let match;
|
|
227
|
+
while (match = pattern.exec(value)) {
|
|
228
|
+
const wrapperStart = match.index;
|
|
229
|
+
const labelStart = wrapperStart + 1;
|
|
230
|
+
const labelEnd = labelStart + ((_b = (_a = match[1]) == null ? void 0 : _a.length) != null ? _b : 0);
|
|
231
|
+
if (from >= labelStart && to <= labelEnd) {
|
|
232
|
+
return { wrapperStart, wrapperEnd: pattern.lastIndex, contentStart: labelStart, contentEnd: labelEnd, url: (_c = match[2]) != null ? _c : "" };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
function findItalicWrapper(value, from, to) {
|
|
238
|
+
var _a, _b, _c, _d;
|
|
239
|
+
const pattern = /(^|[^*])\*([^*\n]+)\*/g;
|
|
240
|
+
let match;
|
|
241
|
+
while (match = pattern.exec(value)) {
|
|
242
|
+
const prefixLength = (_b = (_a = match[1]) == null ? void 0 : _a.length) != null ? _b : 0;
|
|
243
|
+
const wrapperStart = match.index + prefixLength;
|
|
244
|
+
const contentStart = wrapperStart + 1;
|
|
245
|
+
const contentEnd = contentStart + ((_d = (_c = match[2]) == null ? void 0 : _c.length) != null ? _d : 0);
|
|
246
|
+
const wrapperEnd = contentEnd + 1;
|
|
247
|
+
if (from >= contentStart && to <= contentEnd) {
|
|
248
|
+
return { wrapperStart, wrapperEnd, contentStart, contentEnd };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function findInlineWrapperAt(value, offset, left, right) {
|
|
254
|
+
return left === "*" && right === "*" ? findItalicWrapper(value, offset, offset) : findMarkdownWrapper(value, offset, offset, left, right);
|
|
255
|
+
}
|
|
256
|
+
function expandRangeToTouchedInlineWrappers(value, range, left, right) {
|
|
257
|
+
const startWrapper = findInlineWrapperAt(value, range.from, left, right);
|
|
258
|
+
const endOffset = Math.max(range.from, range.to - 1);
|
|
259
|
+
const endWrapper = findInlineWrapperAt(value, endOffset, left, right);
|
|
260
|
+
return {
|
|
261
|
+
from: startWrapper ? Math.min(range.from, startWrapper.wrapperStart) : range.from,
|
|
262
|
+
to: endWrapper ? Math.max(range.to, endWrapper.wrapperEnd) : range.to
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function stripInlineStyleMarkers(value, left, right) {
|
|
266
|
+
if (left === "**" && right === "**") return value.replace(/\*{2,}/g, "");
|
|
267
|
+
if (left === "*" && right === "*") return value.replace(/(^|[^*])\*(?!\*)/g, "$1");
|
|
268
|
+
if (left === "`" && right === "`") return value.replace(/`+/g, "");
|
|
269
|
+
return unwrapFullMarker(value, left, right);
|
|
270
|
+
}
|
|
271
|
+
function normalizeMarkerPairs(value, marker, selectFrom, selectTo) {
|
|
272
|
+
const positions = [];
|
|
273
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
274
|
+
if (!value.startsWith(marker, index)) continue;
|
|
275
|
+
if (marker === "*" && (value[index - 1] === "*" || value[index + 1] === "*")) continue;
|
|
276
|
+
positions.push(index);
|
|
277
|
+
index += marker.length - 1;
|
|
278
|
+
}
|
|
279
|
+
if (positions.length < 3 || positions.length % 2 === 0) return { next: value, selectFrom, selectTo };
|
|
280
|
+
const keep = /* @__PURE__ */ new Set([positions[0], positions[positions.length - 1]]);
|
|
281
|
+
let next = "";
|
|
282
|
+
let cursor = 0;
|
|
283
|
+
let nextSelectFrom = selectFrom;
|
|
284
|
+
let nextSelectTo = selectTo;
|
|
285
|
+
const adjust = (position, removalStart) => {
|
|
286
|
+
if (position <= removalStart) return position;
|
|
287
|
+
if (position <= removalStart + marker.length) return removalStart;
|
|
288
|
+
return position - marker.length;
|
|
289
|
+
};
|
|
290
|
+
for (const position of positions) {
|
|
291
|
+
next += value.slice(cursor, position);
|
|
292
|
+
if (keep.has(position)) {
|
|
293
|
+
next += marker;
|
|
294
|
+
} else {
|
|
295
|
+
nextSelectFrom = adjust(nextSelectFrom, position);
|
|
296
|
+
nextSelectTo = adjust(nextSelectTo, position);
|
|
297
|
+
}
|
|
298
|
+
cursor = position + marker.length;
|
|
299
|
+
}
|
|
300
|
+
next += value.slice(cursor);
|
|
301
|
+
return {
|
|
302
|
+
next,
|
|
303
|
+
selectFrom: Math.max(0, Math.min(next.length, nextSelectFrom)),
|
|
304
|
+
selectTo: Math.max(0, Math.min(next.length, nextSelectTo))
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function normalizeStyleNoise(value, selectFrom, selectTo) {
|
|
308
|
+
let markerResult = normalizeMarkerPairs(value, "**", selectFrom, selectTo);
|
|
309
|
+
markerResult = normalizeMarkerPairs(markerResult.next, "*", markerResult.selectFrom, markerResult.selectTo);
|
|
310
|
+
markerResult = normalizeMarkerPairs(markerResult.next, "`", markerResult.selectFrom, markerResult.selectTo);
|
|
311
|
+
if (markerResult.next !== value) {
|
|
312
|
+
return normalizeStyleNoise(markerResult.next, markerResult.selectFrom, markerResult.selectTo);
|
|
313
|
+
}
|
|
314
|
+
const removals = [];
|
|
315
|
+
const collect = (pattern) => {
|
|
316
|
+
let match;
|
|
317
|
+
while (match = pattern.exec(value)) {
|
|
318
|
+
removals.push({ start: match.index, end: match.index + match[0].length });
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
collect(/\*{3,}/g);
|
|
322
|
+
collect(/\{color:#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?(?:[0-9a-fA-F]{2})?\}\s*\{\/color\}/g);
|
|
323
|
+
if (!removals.length) return { next: value, selectFrom, selectTo };
|
|
324
|
+
const merged = removals.sort((a, b) => a.start - b.start).reduce((items, item) => {
|
|
325
|
+
const previous = items[items.length - 1];
|
|
326
|
+
if (previous && item.start <= previous.end) {
|
|
327
|
+
previous.end = Math.max(previous.end, item.end);
|
|
328
|
+
} else {
|
|
329
|
+
items.push({ ...item });
|
|
330
|
+
}
|
|
331
|
+
return items;
|
|
332
|
+
}, []);
|
|
333
|
+
let next = "";
|
|
334
|
+
let cursor = 0;
|
|
335
|
+
let nextSelectFrom = selectFrom;
|
|
336
|
+
let nextSelectTo = selectTo;
|
|
337
|
+
const adjust = (position, removal) => {
|
|
338
|
+
const length = removal.end - removal.start;
|
|
339
|
+
if (position <= removal.start) return position;
|
|
340
|
+
if (position >= removal.end) return position - length;
|
|
341
|
+
return removal.start;
|
|
342
|
+
};
|
|
343
|
+
for (const removal of merged) {
|
|
344
|
+
next += value.slice(cursor, removal.start);
|
|
345
|
+
nextSelectFrom = adjust(nextSelectFrom, removal);
|
|
346
|
+
nextSelectTo = adjust(nextSelectTo, removal);
|
|
347
|
+
cursor = removal.end;
|
|
348
|
+
}
|
|
349
|
+
next += value.slice(cursor);
|
|
350
|
+
const result = {
|
|
351
|
+
next,
|
|
352
|
+
selectFrom: Math.max(0, Math.min(next.length, nextSelectFrom)),
|
|
353
|
+
selectTo: Math.max(0, Math.min(next.length, nextSelectTo))
|
|
354
|
+
};
|
|
355
|
+
if (/\{color:#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?(?:[0-9a-fA-F]{2})?\}\s*\{\/color\}/.test(result.next)) {
|
|
356
|
+
return normalizeStyleNoise(result.next, result.selectFrom, result.selectTo);
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
function splitSimpleWrapper(value, wrapper, range, left, right) {
|
|
361
|
+
const before = value.slice(wrapper.contentStart, range.from);
|
|
362
|
+
const selected = value.slice(range.from, range.to);
|
|
363
|
+
const after = value.slice(range.to, wrapper.contentEnd);
|
|
364
|
+
const wrap = (text) => text ? `${left}${text}${right}` : "";
|
|
365
|
+
const replacement = `${wrap(before)}${selected}${wrap(after)}`;
|
|
366
|
+
const selectFrom = wrapper.wrapperStart + wrap(before).length;
|
|
367
|
+
const next = value.slice(0, wrapper.wrapperStart) + replacement + value.slice(wrapper.wrapperEnd);
|
|
368
|
+
return normalizeStyleNoise(next, selectFrom, selectFrom + selected.length);
|
|
369
|
+
}
|
|
370
|
+
function splitLinkWrapper(value, wrapper, range) {
|
|
371
|
+
if (!wrapper) return null;
|
|
372
|
+
const before = value.slice(wrapper.contentStart, range.from);
|
|
373
|
+
const selected = value.slice(range.from, range.to);
|
|
374
|
+
const after = value.slice(range.to, wrapper.contentEnd);
|
|
375
|
+
const wrap = (text) => text ? `[${text}](${wrapper.url})` : "";
|
|
376
|
+
const replacement = `${wrap(before)}${selected}${wrap(after)}`;
|
|
377
|
+
const selectFrom = wrapper.wrapperStart + wrap(before).length;
|
|
378
|
+
const next = value.slice(0, wrapper.wrapperStart) + replacement + value.slice(wrapper.wrapperEnd);
|
|
379
|
+
return { next, selectFrom, selectTo: selectFrom + selected.length };
|
|
380
|
+
}
|
|
381
|
+
function smartWrapSelection(value, start, end, left, right, fallbackText) {
|
|
382
|
+
const range = getSmartTextRange(value, start, end);
|
|
383
|
+
if (range.from === range.to) {
|
|
384
|
+
const result = wrapSelection(value, start, end, left, right, fallbackText);
|
|
385
|
+
return normalizeStyleNoise(result.next, result.selectFrom, result.selectTo);
|
|
386
|
+
}
|
|
387
|
+
const wrapper = left === "*" && right === "*" ? findItalicWrapper(value, range.from, range.to) : findMarkdownWrapper(value, range.from, range.to, left, right);
|
|
388
|
+
if (wrapper) {
|
|
389
|
+
return splitSimpleWrapper(value, wrapper, range, left, right);
|
|
390
|
+
}
|
|
391
|
+
const expanded = expandRangeToTouchedInlineWrappers(value, range, left, right);
|
|
392
|
+
const selected = value.slice(expanded.from, expanded.to);
|
|
393
|
+
const inner = stripInlineStyleMarkers(selected, left, right);
|
|
394
|
+
const next = value.slice(0, expanded.from) + left + inner + right + value.slice(expanded.to);
|
|
395
|
+
const selectFrom = expanded.from + left.length;
|
|
396
|
+
const selectTo = selectFrom + inner.length;
|
|
397
|
+
return normalizeStyleNoise(next, selectFrom, selectTo);
|
|
398
|
+
}
|
|
399
|
+
function smartLinkSelection(value, start, end, url) {
|
|
400
|
+
const range = getSmartTextRange(value, start, end);
|
|
401
|
+
const wrapper = findLinkWrapper(value, range.from, range.to);
|
|
402
|
+
if (wrapper) return splitLinkWrapper(value, wrapper, range);
|
|
403
|
+
const selected = range.from === range.to ? "texto" : value.slice(range.from, range.to);
|
|
404
|
+
const inner = selected.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+|\/[^\s)]+)\)/g, "$1");
|
|
405
|
+
const next = value.slice(0, range.from) + `[${inner}](${url})` + value.slice(range.to);
|
|
406
|
+
const selectFrom = range.from + 1;
|
|
407
|
+
return { next, selectFrom, selectTo: selectFrom + inner.length };
|
|
408
|
+
}
|
|
409
|
+
function readColorOpen(value, index) {
|
|
410
|
+
var _a;
|
|
411
|
+
const match = value.slice(index).match(/^\{color:(#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?(?:[0-9a-fA-F]{2})?)\}/);
|
|
412
|
+
return match ? { color: (_a = match[1]) != null ? _a : null, length: match[0].length } : null;
|
|
413
|
+
}
|
|
414
|
+
function collectColorWrappers(value) {
|
|
415
|
+
const wrappers = [];
|
|
416
|
+
const stack = [];
|
|
417
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
418
|
+
const open = readColorOpen(value, index);
|
|
419
|
+
if (open) {
|
|
420
|
+
stack.push({
|
|
421
|
+
wrapperStart: index,
|
|
422
|
+
contentStart: index + open.length,
|
|
423
|
+
color: open.color
|
|
424
|
+
});
|
|
425
|
+
index += open.length - 1;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (value.startsWith("{/color}", index)) {
|
|
429
|
+
const current = stack.pop();
|
|
430
|
+
if (current) {
|
|
431
|
+
wrappers.push({
|
|
432
|
+
...current,
|
|
433
|
+
contentEnd: index,
|
|
434
|
+
wrapperEnd: index + "{/color}".length,
|
|
435
|
+
inner: value.slice(current.contentStart, index)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
index += "{/color}".length - 1;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return wrappers.sort((a, b) => a.wrapperStart - b.wrapperStart);
|
|
442
|
+
}
|
|
443
|
+
function parseColorUnits(value) {
|
|
444
|
+
var _a, _b;
|
|
445
|
+
const units = [];
|
|
446
|
+
const stack = [];
|
|
447
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
448
|
+
const open = readColorOpen(value, index);
|
|
449
|
+
if (open) {
|
|
450
|
+
stack.push(open.color);
|
|
451
|
+
index += open.length - 1;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (value.startsWith("{/color}", index)) {
|
|
455
|
+
stack.pop();
|
|
456
|
+
index += "{/color}".length - 1;
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
units.push({
|
|
460
|
+
text: (_a = value[index]) != null ? _a : "",
|
|
461
|
+
color: (_b = stack[stack.length - 1]) != null ? _b : null,
|
|
462
|
+
rawStart: index,
|
|
463
|
+
rawEnd: index + 1
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
return units;
|
|
467
|
+
}
|
|
468
|
+
function rawOffsetToColorUnitIndex(units, offset) {
|
|
469
|
+
for (let index = 0; index < units.length; index += 1) {
|
|
470
|
+
const unit = units[index];
|
|
471
|
+
if (!unit) continue;
|
|
472
|
+
if (offset <= unit.rawStart) return index;
|
|
473
|
+
if (offset < unit.rawEnd) return index;
|
|
474
|
+
}
|
|
475
|
+
return units.length;
|
|
476
|
+
}
|
|
477
|
+
function serializeColorUnits(units, selectFromIndex, selectToIndex) {
|
|
478
|
+
var _a;
|
|
479
|
+
let next = "";
|
|
480
|
+
let activeColor = null;
|
|
481
|
+
let selectFrom;
|
|
482
|
+
let selectTo;
|
|
483
|
+
const closeColor = () => {
|
|
484
|
+
if (!activeColor) return;
|
|
485
|
+
next += "{/color}";
|
|
486
|
+
activeColor = null;
|
|
487
|
+
};
|
|
488
|
+
const openColor = (color) => {
|
|
489
|
+
if (!color) return;
|
|
490
|
+
next += `{color:${color}}`;
|
|
491
|
+
activeColor = color;
|
|
492
|
+
};
|
|
493
|
+
for (let index = 0; index < units.length; index += 1) {
|
|
494
|
+
const unit = units[index];
|
|
495
|
+
if (!unit) continue;
|
|
496
|
+
if (unit.color !== activeColor) {
|
|
497
|
+
closeColor();
|
|
498
|
+
openColor(unit.color);
|
|
499
|
+
}
|
|
500
|
+
if (index === selectFromIndex) selectFrom = next.length;
|
|
501
|
+
next += unit.text;
|
|
502
|
+
if (index + 1 === selectToIndex) selectTo = next.length;
|
|
503
|
+
}
|
|
504
|
+
closeColor();
|
|
505
|
+
return {
|
|
506
|
+
next,
|
|
507
|
+
selectFrom: selectFrom != null ? selectFrom : next.length,
|
|
508
|
+
selectTo: (_a = selectTo != null ? selectTo : selectFrom) != null ? _a : next.length
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function findSelectionColor(value, from, to, activeOffset) {
|
|
512
|
+
var _a, _b;
|
|
513
|
+
const wrappers = collectColorWrappers(value);
|
|
514
|
+
if (!wrappers.length) return null;
|
|
515
|
+
const normalizedFrom = Math.max(0, Math.min(value.length, from));
|
|
516
|
+
const normalizedTo = Math.max(normalizedFrom, Math.min(value.length, to));
|
|
517
|
+
const preferredOffsets = [
|
|
518
|
+
typeof activeOffset === "number" ? activeOffset : null,
|
|
519
|
+
normalizedTo > normalizedFrom ? normalizedTo - 1 : normalizedFrom,
|
|
520
|
+
normalizedFrom
|
|
521
|
+
];
|
|
522
|
+
for (const offset of preferredOffsets) {
|
|
523
|
+
if (offset === null) continue;
|
|
524
|
+
const point = Math.max(normalizedFrom, Math.min(normalizedTo, offset));
|
|
525
|
+
const match = wrappers.filter((wrapper) => point >= wrapper.contentStart && point <= wrapper.contentEnd).sort((a, b) => a.contentEnd - a.contentStart - (b.contentEnd - b.contentStart) || b.contentStart - a.contentStart)[0];
|
|
526
|
+
if (match == null ? void 0 : match.color) return match.color;
|
|
527
|
+
}
|
|
528
|
+
const overlapping = wrappers.filter((wrapper) => Math.max(wrapper.contentStart, normalizedFrom) < Math.min(wrapper.contentEnd, normalizedTo)).sort((a, b) => b.contentStart - a.contentStart || b.wrapperStart - a.wrapperStart);
|
|
529
|
+
return (_b = (_a = overlapping[0]) == null ? void 0 : _a.color) != null ? _b : null;
|
|
530
|
+
}
|
|
531
|
+
function applyColorToSelection(value, start, end, color) {
|
|
532
|
+
const range = getSmartTextRange(value, start, end);
|
|
533
|
+
if (range.from === range.to) {
|
|
534
|
+
return wrapSelection(value, start, end, `{color:${color}}`, "{/color}", "texto");
|
|
535
|
+
}
|
|
536
|
+
const units = parseColorUnits(value);
|
|
537
|
+
const fromIndex = rawOffsetToColorUnitIndex(units, range.from);
|
|
538
|
+
const toIndex = rawOffsetToColorUnitIndex(units, range.to);
|
|
539
|
+
const selectFromIndex = Math.min(fromIndex, toIndex);
|
|
540
|
+
const selectToIndex = Math.max(fromIndex, toIndex);
|
|
541
|
+
for (let index = selectFromIndex; index < selectToIndex; index += 1) {
|
|
542
|
+
if (units[index]) units[index].color = color;
|
|
543
|
+
}
|
|
544
|
+
return serializeColorUnits(units, selectFromIndex, selectToIndex);
|
|
545
|
+
}
|
|
546
|
+
function detectSelectionMarks(value, range) {
|
|
547
|
+
const normalized = normalizeRange(value, range.start, range.end);
|
|
548
|
+
const color = findSelectionColor(value, normalized.from, normalized.to, range.activeOffset);
|
|
549
|
+
return {
|
|
550
|
+
bold: Boolean(findMarkdownWrapper(value, normalized.from, normalized.to, "**", "**")),
|
|
551
|
+
italic: Boolean(findItalicWrapper(value, normalized.from, normalized.to)),
|
|
552
|
+
code: Boolean(findMarkdownWrapper(value, normalized.from, normalized.to, "`", "`")),
|
|
553
|
+
link: Boolean(findLinkWrapper(value, normalized.from, normalized.to)),
|
|
554
|
+
color
|
|
555
|
+
};
|
|
556
|
+
}
|
|
75
557
|
function prefixLines(block, prefix) {
|
|
76
558
|
const lines = block.split("\n");
|
|
77
559
|
return lines.map((l) => l.trim().length ? prefix + l : l).join("\n");
|
|
78
560
|
}
|
|
561
|
+
function escapeHtml(value) {
|
|
562
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
563
|
+
}
|
|
564
|
+
function escapeAttr(value) {
|
|
565
|
+
return escapeHtml(value).replace(/`/g, "`");
|
|
566
|
+
}
|
|
567
|
+
function decodeBasicEntities(value) {
|
|
568
|
+
return String(value || "").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
569
|
+
}
|
|
570
|
+
function safeMediaUrl(value, kind) {
|
|
571
|
+
const raw = decodeBasicEntities(value).trim();
|
|
572
|
+
if (!raw) return null;
|
|
573
|
+
const lower = raw.toLowerCase();
|
|
574
|
+
if (lower.startsWith("http://") || lower.startsWith("https://")) return raw;
|
|
575
|
+
if (kind === "image" && /^data:image\/(png|jpe?g|gif|webp);base64,/i.test(raw)) return raw;
|
|
576
|
+
if (kind === "video" && /^data:video\/(mp4|webm|ogg);base64,/i.test(raw)) return raw;
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
function safeColor(value) {
|
|
580
|
+
const color = decodeBasicEntities(value).trim();
|
|
581
|
+
return /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?([0-9a-fA-F]{2})?$/.test(color) ? color : null;
|
|
582
|
+
}
|
|
583
|
+
function videoEmbedUrl(value) {
|
|
584
|
+
var _a, _b, _c;
|
|
585
|
+
try {
|
|
586
|
+
const url = new URL(decodeBasicEntities(value).trim());
|
|
587
|
+
const host = url.hostname.replace(/^www\./, "").replace(/^m\./, "").toLowerCase();
|
|
588
|
+
if (host === "youtu.be") {
|
|
589
|
+
const id = url.pathname.split("/").filter(Boolean)[0];
|
|
590
|
+
return id ? `https://www.youtube-nocookie.com/embed/${encodeURIComponent(id)}` : null;
|
|
591
|
+
}
|
|
592
|
+
if (host === "youtube.com" || host === "music.youtube.com") {
|
|
593
|
+
const embed = (_a = url.pathname.match(/^\/embed\/([^/?#]+)/)) == null ? void 0 : _a[1];
|
|
594
|
+
const shorts = (_b = url.pathname.match(/^\/shorts\/([^/?#]+)/)) == null ? void 0 : _b[1];
|
|
595
|
+
const id = embed || shorts || url.searchParams.get("v");
|
|
596
|
+
return id ? `https://www.youtube-nocookie.com/embed/${encodeURIComponent(id)}` : null;
|
|
597
|
+
}
|
|
598
|
+
if (host === "vimeo.com") {
|
|
599
|
+
const id = url.pathname.split("/").filter(Boolean).find((part) => /^\d+$/.test(part));
|
|
600
|
+
return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;
|
|
601
|
+
}
|
|
602
|
+
if (host === "player.vimeo.com") {
|
|
603
|
+
const id = (_c = url.pathname.match(/\/video\/(\d+)/)) == null ? void 0 : _c[1];
|
|
604
|
+
return id ? `https://player.vimeo.com/video/${encodeURIComponent(id)}` : null;
|
|
605
|
+
}
|
|
606
|
+
} catch {
|
|
607
|
+
}
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
function renderImageMarkdown(rawUrl, alt, attrs = "") {
|
|
611
|
+
const url = safeMediaUrl(rawUrl, "image");
|
|
612
|
+
if (!url) return null;
|
|
613
|
+
return `<figure${attrs}><img src="${escapeAttr(url)}" alt="${escapeAttr(decodeBasicEntities(alt != null ? alt : ""))}" loading="lazy" /></figure>`;
|
|
614
|
+
}
|
|
615
|
+
function renderVideoMarkdown(rawUrl, title, attrs = "") {
|
|
616
|
+
const url = safeMediaUrl(rawUrl, "video");
|
|
617
|
+
if (!url) return null;
|
|
618
|
+
const label = title ? `<figcaption>${escapeHtml(decodeBasicEntities(title))}</figcaption>` : "";
|
|
619
|
+
const embed = videoEmbedUrl(url);
|
|
620
|
+
if (embed) {
|
|
621
|
+
return `<figure${attrs}><iframe src="${escapeAttr(embed)}" title="${escapeAttr(decodeBasicEntities(title || "Video"))}" loading="lazy" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>${label}</figure>`;
|
|
622
|
+
}
|
|
623
|
+
return `<figure${attrs}><video controls preload="metadata" src="${escapeAttr(url)}"></video>${label}</figure>`;
|
|
624
|
+
}
|
|
625
|
+
function parseMarkdownTableRow(value) {
|
|
626
|
+
const trimmed = String(value || "").trim();
|
|
627
|
+
if (!trimmed.includes("|")) return null;
|
|
628
|
+
const normalized = trimmed.replace(/^\|/, "").replace(/\|$/, "");
|
|
629
|
+
const cells = normalized.split("|").map((cell) => cell.trim());
|
|
630
|
+
return cells.length >= 2 ? cells : null;
|
|
631
|
+
}
|
|
632
|
+
function isMarkdownTableSeparator(cells) {
|
|
633
|
+
return Boolean((cells == null ? void 0 : cells.length) && cells.every((cell) => /^:?-{3,}:?$/.test(cell)));
|
|
634
|
+
}
|
|
635
|
+
function getMarkdownLines(value) {
|
|
636
|
+
const normalized = value.replace(/\r\n/g, "\n");
|
|
637
|
+
const parts = normalized.split("\n");
|
|
638
|
+
let cursor = 0;
|
|
639
|
+
return parts.map((text) => {
|
|
640
|
+
const line = { text, start: cursor, end: cursor + text.length };
|
|
641
|
+
cursor = line.end + 1;
|
|
642
|
+
return line;
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
function rangesOverlap(a, start, end) {
|
|
646
|
+
if (!a) return false;
|
|
647
|
+
return a.start < end && a.end > start;
|
|
648
|
+
}
|
|
649
|
+
function rangeAttrs(options, kind, start, end) {
|
|
650
|
+
if (!(options == null ? void 0 : options.includeRanges)) return "";
|
|
651
|
+
const selected = rangesOverlap(options.selectedRange, start, end) ? ' data-md-selected="true"' : "";
|
|
652
|
+
return ` data-md-kind="${escapeAttr(kind)}" data-md-start="${start}" data-md-end="${end}"${selected}`;
|
|
653
|
+
}
|
|
654
|
+
function applyInlineMarks(html, marks) {
|
|
655
|
+
let next = html;
|
|
656
|
+
if (marks.code) next = `<code>${next}</code>`;
|
|
657
|
+
if (marks.italic) next = `<em>${next}</em>`;
|
|
658
|
+
if (marks.bold) next = `<strong>${next}</strong>`;
|
|
659
|
+
if (marks.color) next = `<span style="color:${marks.color}">${next}</span>`;
|
|
660
|
+
if (marks.link) next = `<a href="${escapeAttr(marks.link)}" target="_blank" rel="noopener noreferrer">${next}</a>`;
|
|
661
|
+
return next;
|
|
662
|
+
}
|
|
663
|
+
function renderInlineMarkdown(value) {
|
|
664
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
665
|
+
const source = normalizeStyleNoise(value, 0, value.length).next;
|
|
666
|
+
const html = [];
|
|
667
|
+
const state = {
|
|
668
|
+
bold: false,
|
|
669
|
+
italic: false,
|
|
670
|
+
code: false,
|
|
671
|
+
color: null
|
|
672
|
+
};
|
|
673
|
+
const colorStack = [];
|
|
674
|
+
let buffer = "";
|
|
675
|
+
const flush = () => {
|
|
676
|
+
if (!buffer) return;
|
|
677
|
+
html.push(applyInlineMarks(escapeHtml(buffer), state));
|
|
678
|
+
buffer = "";
|
|
679
|
+
};
|
|
680
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
681
|
+
const rest = source.slice(index);
|
|
682
|
+
const image = /^!\[([^\]]*)\]\(([^)\s]+)\)/.exec(rest);
|
|
683
|
+
if (image) {
|
|
684
|
+
const rendered = renderImageMarkdown((_a = image[2]) != null ? _a : "", (_b = image[1]) != null ? _b : "");
|
|
685
|
+
if (rendered) {
|
|
686
|
+
flush();
|
|
687
|
+
html.push(rendered);
|
|
688
|
+
index += image[0].length - 1;
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const video = /^@\[video(?::([^\]]*))?\]\(([^)\s]+)\)/.exec(rest);
|
|
693
|
+
if (video) {
|
|
694
|
+
const rendered = renderVideoMarkdown((_c = video[2]) != null ? _c : "", (_d = video[1]) != null ? _d : "");
|
|
695
|
+
if (rendered) {
|
|
696
|
+
flush();
|
|
697
|
+
html.push(rendered);
|
|
698
|
+
index += video[0].length - 1;
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const colorOpen = /^\{color:([^}]+)\}/.exec(rest);
|
|
703
|
+
if (colorOpen) {
|
|
704
|
+
const color = safeColor((_e = colorOpen[1]) != null ? _e : "");
|
|
705
|
+
flush();
|
|
706
|
+
colorStack.push(state.color);
|
|
707
|
+
state.color = color != null ? color : state.color;
|
|
708
|
+
index += colorOpen[0].length - 1;
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (rest.startsWith("{/color}")) {
|
|
712
|
+
flush();
|
|
713
|
+
state.color = colorStack.length ? (_f = colorStack.pop()) != null ? _f : null : null;
|
|
714
|
+
index += "{/color}".length - 1;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
const link = /^\[([^\]]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+|\/[^\s)]+)\)/.exec(rest);
|
|
718
|
+
if (link) {
|
|
719
|
+
flush();
|
|
720
|
+
html.push(applyInlineMarks(renderInlineMarkdown((_g = link[1]) != null ? _g : ""), { ...state, link: (_h = link[2]) != null ? _h : "" }));
|
|
721
|
+
index += link[0].length - 1;
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
if (rest.startsWith("**")) {
|
|
725
|
+
flush();
|
|
726
|
+
state.bold = !state.bold;
|
|
727
|
+
index += 1;
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
const current = (_i = source[index]) != null ? _i : "";
|
|
731
|
+
if (current === "`") {
|
|
732
|
+
flush();
|
|
733
|
+
state.code = !state.code;
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (current === "*") {
|
|
737
|
+
flush();
|
|
738
|
+
state.italic = !state.italic;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
buffer += current;
|
|
742
|
+
}
|
|
743
|
+
flush();
|
|
744
|
+
return html.join("");
|
|
745
|
+
}
|
|
746
|
+
function renderMarkdown(value, options) {
|
|
747
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
748
|
+
const lines = getMarkdownLines(value);
|
|
749
|
+
const html = [];
|
|
750
|
+
const paragraph = [];
|
|
751
|
+
const code = [];
|
|
752
|
+
let codeOpen = false;
|
|
753
|
+
let codeLang = "";
|
|
754
|
+
let listType = null;
|
|
755
|
+
let listItem = null;
|
|
756
|
+
const closeListItem = () => {
|
|
757
|
+
if (!listItem) return;
|
|
758
|
+
html.push(`<li${rangeAttrs(options, "list-item", listItem.contentStart, listItem.end)}>${listItem.parts.join("<br />")}</li>`);
|
|
759
|
+
listItem = null;
|
|
760
|
+
};
|
|
761
|
+
const closeList = () => {
|
|
762
|
+
if (!listType) return;
|
|
763
|
+
closeListItem();
|
|
764
|
+
html.push(`</${listType}>`);
|
|
765
|
+
listType = null;
|
|
766
|
+
};
|
|
767
|
+
const flushParagraph = () => {
|
|
768
|
+
var _a2, _b2, _c2, _d2;
|
|
769
|
+
if (!paragraph.length) return;
|
|
770
|
+
closeList();
|
|
771
|
+
const start = (_b2 = (_a2 = paragraph[0]) == null ? void 0 : _a2.start) != null ? _b2 : 0;
|
|
772
|
+
const end = (_d2 = (_c2 = paragraph[paragraph.length - 1]) == null ? void 0 : _c2.end) != null ? _d2 : start;
|
|
773
|
+
const content = (options == null ? void 0 : options.includeRanges) ? paragraph.map((line) => `<span${rangeAttrs(options, "line", line.start, line.end)}>${renderInlineMarkdown(line.text.trimEnd())}</span>`).join("<br />") : paragraph.map((line) => renderInlineMarkdown(line.text.trimEnd())).join("<br />");
|
|
774
|
+
html.push(`<p${rangeAttrs(options, "paragraph", start, end)}>${content}</p>`);
|
|
775
|
+
paragraph.length = 0;
|
|
776
|
+
};
|
|
777
|
+
const renderCodeBlock = () => {
|
|
778
|
+
var _a2, _b2, _c2, _d2;
|
|
779
|
+
const raw = code.map((line) => line.text).join("\n");
|
|
780
|
+
const lang = codeLang.trim().toLowerCase();
|
|
781
|
+
const start = (_b2 = (_a2 = code[0]) == null ? void 0 : _a2.start) != null ? _b2 : 0;
|
|
782
|
+
const end = (_d2 = (_c2 = code[code.length - 1]) == null ? void 0 : _c2.end) != null ? _d2 : start;
|
|
783
|
+
if (lang === "mermaid" || lang === "uml") {
|
|
784
|
+
html.push(`<div class="fp-markdown-diagram"${rangeAttrs(options, "diagram", start, end)} data-diagram="${escapeAttr(raw)}"><pre><code class="language-${escapeAttr(lang)}">${escapeHtml(raw)}</code></pre></div>`);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
html.push(`<pre${rangeAttrs(options, "code", start, end)}><code>${escapeHtml(raw)}</code></pre>`);
|
|
788
|
+
};
|
|
789
|
+
const openList = (nextType) => {
|
|
790
|
+
flushParagraph();
|
|
791
|
+
if (listType === nextType) return;
|
|
792
|
+
closeList();
|
|
793
|
+
html.push(`<${nextType}>`);
|
|
794
|
+
listType = nextType;
|
|
795
|
+
};
|
|
796
|
+
const appendListItem = (line) => {
|
|
797
|
+
if (!listItem) return false;
|
|
798
|
+
listItem.parts.push(renderInlineMarkdown(line.text.trim()));
|
|
799
|
+
listItem.end = line.end;
|
|
800
|
+
return true;
|
|
801
|
+
};
|
|
802
|
+
const appendListItemGap = (line) => {
|
|
803
|
+
if (!listItem) return false;
|
|
804
|
+
listItem.parts.push('<span class="fp-markdown-list-gap" aria-hidden="true"></span>');
|
|
805
|
+
listItem.end = line.end;
|
|
806
|
+
return true;
|
|
807
|
+
};
|
|
808
|
+
const nextNonEmptyLine = (fromIndex) => {
|
|
809
|
+
for (let index = fromIndex; index < lines.length; index += 1) {
|
|
810
|
+
const nextLine = lines[index];
|
|
811
|
+
if (!(nextLine == null ? void 0 : nextLine.text.trim())) continue;
|
|
812
|
+
return nextLine.text.trim();
|
|
813
|
+
}
|
|
814
|
+
return "";
|
|
815
|
+
};
|
|
816
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
817
|
+
const currentLine = (_a = lines[lineIndex]) != null ? _a : { text: "", start: 0, end: 0 };
|
|
818
|
+
const line = currentLine.text;
|
|
819
|
+
const trimmed = line.trim();
|
|
820
|
+
const fence = /^```([A-Za-z0-9_-]+)?/.exec(trimmed);
|
|
821
|
+
if (fence) {
|
|
822
|
+
if (codeOpen) {
|
|
823
|
+
renderCodeBlock();
|
|
824
|
+
code.length = 0;
|
|
825
|
+
codeOpen = false;
|
|
826
|
+
codeLang = "";
|
|
827
|
+
} else {
|
|
828
|
+
flushParagraph();
|
|
829
|
+
closeList();
|
|
830
|
+
codeOpen = true;
|
|
831
|
+
codeLang = (_b = fence[1]) != null ? _b : "";
|
|
832
|
+
}
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (codeOpen) {
|
|
836
|
+
code.push(currentLine);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (!trimmed) {
|
|
840
|
+
flushParagraph();
|
|
841
|
+
if (line.length > 0 && appendListItemGap(currentLine)) {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
const nextTrimmed = nextNonEmptyLine(lineIndex + 1);
|
|
845
|
+
const keepsOrderedList = listType === "ol" && /^\d+\.\s+/.test(nextTrimmed);
|
|
846
|
+
const keepsUnorderedList = listType === "ul" && /^[-*]\s+/.test(nextTrimmed);
|
|
847
|
+
if ((keepsOrderedList || keepsUnorderedList) && appendListItemGap(currentLine)) {
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
closeList();
|
|
851
|
+
html.push(`<div class="fp-markdown-empty-line"${rangeAttrs(options, "empty-line", currentLine.start, currentLine.end)}></div>`);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
if (/^---+$/.test(trimmed)) {
|
|
855
|
+
flushParagraph();
|
|
856
|
+
closeList();
|
|
857
|
+
html.push(`<hr${rangeAttrs(options, "divider", currentLine.start, currentLine.end)} />`);
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
const imageBlock = /^!\[([^\]]*)\]\(([^)\s]+)\)$/.exec(trimmed);
|
|
861
|
+
if (imageBlock) {
|
|
862
|
+
const rendered = renderImageMarkdown(imageBlock[2], imageBlock[1], rangeAttrs(options, "image", currentLine.start, currentLine.end));
|
|
863
|
+
if (rendered) {
|
|
864
|
+
flushParagraph();
|
|
865
|
+
closeList();
|
|
866
|
+
html.push(rendered);
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const videoBlock = /^@\[video(?::([^\]]*))?\]\(([^)\s]+)\)$/.exec(trimmed);
|
|
871
|
+
if (videoBlock) {
|
|
872
|
+
const rendered = renderVideoMarkdown(videoBlock[2], videoBlock[1], rangeAttrs(options, "video", currentLine.start, currentLine.end));
|
|
873
|
+
if (rendered) {
|
|
874
|
+
flushParagraph();
|
|
875
|
+
closeList();
|
|
876
|
+
html.push(rendered);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
const tableHead = parseMarkdownTableRow(trimmed);
|
|
881
|
+
const tableSeparator = parseMarkdownTableRow((_d = (_c = lines[lineIndex + 1]) == null ? void 0 : _c.text) != null ? _d : "");
|
|
882
|
+
if (tableHead && isMarkdownTableSeparator(tableSeparator)) {
|
|
883
|
+
flushParagraph();
|
|
884
|
+
closeList();
|
|
885
|
+
const rows = [];
|
|
886
|
+
let tableEnd = (_f = (_e = lines[lineIndex + 1]) == null ? void 0 : _e.end) != null ? _f : currentLine.end;
|
|
887
|
+
let nextIndex = lineIndex + 2;
|
|
888
|
+
while (nextIndex < lines.length) {
|
|
889
|
+
const row = parseMarkdownTableRow((_h = (_g = lines[nextIndex]) == null ? void 0 : _g.text) != null ? _h : "");
|
|
890
|
+
if (!row || isMarkdownTableSeparator(row)) break;
|
|
891
|
+
rows.push(row);
|
|
892
|
+
tableEnd = (_j = (_i = lines[nextIndex]) == null ? void 0 : _i.end) != null ? _j : tableEnd;
|
|
893
|
+
nextIndex += 1;
|
|
894
|
+
}
|
|
895
|
+
const thead = `<thead><tr>${tableHead.map((cell) => `<th>${renderInlineMarkdown(cell)}</th>`).join("")}</tr></thead>`;
|
|
896
|
+
const tbody = rows.length ? `<tbody>${rows.map((row) => `<tr>${tableHead.map((_, index) => {
|
|
897
|
+
var _a2;
|
|
898
|
+
return `<td>${renderInlineMarkdown((_a2 = row[index]) != null ? _a2 : "")}</td>`;
|
|
899
|
+
}).join("")}</tr>`).join("")}</tbody>` : "";
|
|
900
|
+
html.push(`<table${rangeAttrs(options, "table", currentLine.start, tableEnd)}>${thead}${tbody}</table>`);
|
|
901
|
+
lineIndex = nextIndex - 1;
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
const heading = /^(#{1,4})\s+(.+)$/.exec(trimmed);
|
|
905
|
+
if (heading) {
|
|
906
|
+
flushParagraph();
|
|
907
|
+
closeList();
|
|
908
|
+
const level = heading[1].length;
|
|
909
|
+
const contentStart = currentLine.start + heading[1].length + ((_l = (_k = line.slice(heading[1].length).match(/^\s+/)) == null ? void 0 : _k[0].length) != null ? _l : 0);
|
|
910
|
+
html.push(`<h${level}${rangeAttrs(options, "heading", contentStart, currentLine.end)}>${renderInlineMarkdown(heading[2])}</h${level}>`);
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
const quote = /^>\s?(.+)$/.exec(trimmed);
|
|
914
|
+
if (quote) {
|
|
915
|
+
flushParagraph();
|
|
916
|
+
closeList();
|
|
917
|
+
const markerIndex = line.indexOf(">");
|
|
918
|
+
const contentStart = currentLine.start + markerIndex + 1 + (line.slice(markerIndex + 1).startsWith(" ") ? 1 : 0);
|
|
919
|
+
html.push(`<blockquote${rangeAttrs(options, "quote", contentStart, currentLine.end)}>${renderInlineMarkdown(quote[1])}</blockquote>`);
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
const unordered = /^[-*]\s+(.+)$/.exec(trimmed);
|
|
923
|
+
if (unordered) {
|
|
924
|
+
openList("ul");
|
|
925
|
+
closeListItem();
|
|
926
|
+
const markerIndex = Math.max(line.indexOf("-"), line.indexOf("*"));
|
|
927
|
+
const contentStart = currentLine.start + markerIndex + 2;
|
|
928
|
+
listItem = { start: currentLine.start, end: currentLine.end, contentStart, parts: [renderInlineMarkdown(unordered[1])] };
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
const ordered = /^\d+\.\s+(.+)$/.exec(trimmed);
|
|
932
|
+
if (ordered) {
|
|
933
|
+
openList("ol");
|
|
934
|
+
closeListItem();
|
|
935
|
+
const marker = line.match(/\d+\.\s+/);
|
|
936
|
+
const contentStart = currentLine.start + ((_m = marker == null ? void 0 : marker.index) != null ? _m : 0) + ((_n = marker == null ? void 0 : marker[0].length) != null ? _n : 0);
|
|
937
|
+
listItem = { start: currentLine.start, end: currentLine.end, contentStart, parts: [renderInlineMarkdown(ordered[1])] };
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
if (listType && appendListItem(currentLine)) {
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
paragraph.push(currentLine);
|
|
944
|
+
}
|
|
945
|
+
if (codeOpen) {
|
|
946
|
+
renderCodeBlock();
|
|
947
|
+
}
|
|
948
|
+
flushParagraph();
|
|
949
|
+
closeList();
|
|
950
|
+
return html.join("");
|
|
951
|
+
}
|
|
952
|
+
const COLOR_SWATCHES = [
|
|
953
|
+
"#111827",
|
|
954
|
+
"#6b7280",
|
|
955
|
+
"#dc2626",
|
|
956
|
+
"#ea580c",
|
|
957
|
+
"#ca8a04",
|
|
958
|
+
"#16a34a",
|
|
959
|
+
"#0891b2",
|
|
960
|
+
"#2563eb",
|
|
961
|
+
"#7c3aed",
|
|
962
|
+
"#db2777"
|
|
963
|
+
];
|
|
964
|
+
const MARKDOWN_EDITOR_SAMPLE = [
|
|
965
|
+
"# P\xE1gina de ejemplo",
|
|
966
|
+
"",
|
|
967
|
+
"{color:#2563eb}Este contenido muestra varios bloques que se pueden construir con el editor visual.{/color}",
|
|
968
|
+
"",
|
|
969
|
+
"## Texto enriquecido",
|
|
970
|
+
"",
|
|
971
|
+
"Puedes combinar **negritas**, *cursivas*, `c\xF3digo`, enlaces como [ui-lib](https://example.com) y colores para destacar informaci\xF3n importante.",
|
|
972
|
+
"",
|
|
973
|
+
"- Bloques de texto",
|
|
974
|
+
"- Listas y citas",
|
|
975
|
+
"- Im\xE1genes, videos, tablas y diagramas",
|
|
976
|
+
"",
|
|
977
|
+
"> Este bloque sirve para resaltar una nota, advertencia o resumen dentro del contenido.",
|
|
978
|
+
"",
|
|
979
|
+
"## Imagen",
|
|
980
|
+
"",
|
|
981
|
+
"",
|
|
982
|
+
"",
|
|
983
|
+
"## Video",
|
|
984
|
+
"",
|
|
985
|
+
"@[video:Video de ejemplo](https://www.youtube.com/watch?v=jNQXAC9IVRw)",
|
|
986
|
+
"",
|
|
987
|
+
"## Tabla",
|
|
988
|
+
"",
|
|
989
|
+
"| Bloque | Uso | Resultado |",
|
|
990
|
+
"| --- | --- | --- |",
|
|
991
|
+
"| T\xEDtulo | Separar secciones | Lectura m\xE1s clara |",
|
|
992
|
+
"| Imagen | Mostrar contexto visual | Contenido m\xE1s atractivo |",
|
|
993
|
+
"| Video | Explicar procesos | Mejor comprensi\xF3n |",
|
|
994
|
+
"",
|
|
995
|
+
"## Diagrama UML",
|
|
996
|
+
"",
|
|
997
|
+
"```mermaid",
|
|
998
|
+
"classDiagram",
|
|
999
|
+
" class Pagina {",
|
|
1000
|
+
" +string titulo",
|
|
1001
|
+
" +string contenido",
|
|
1002
|
+
" +publicar()",
|
|
1003
|
+
" }",
|
|
1004
|
+
" class Medio {",
|
|
1005
|
+
" +string tipo",
|
|
1006
|
+
" +string url",
|
|
1007
|
+
" }",
|
|
1008
|
+
" Pagina --> Medio",
|
|
1009
|
+
"```",
|
|
1010
|
+
"",
|
|
1011
|
+
"---",
|
|
1012
|
+
"",
|
|
1013
|
+
"Este ejemplo se puede editar, reorganizar o eliminar desde las acciones de cada bloque."
|
|
1014
|
+
].join("\n");
|
|
1015
|
+
const MARKDOWN_EDITOR_SAMPLE_WITHOUT_DIAGRAMS = [
|
|
1016
|
+
"# P\xE1gina de ejemplo",
|
|
1017
|
+
"",
|
|
1018
|
+
"{color:#2563eb}Este contenido muestra varios bloques que se pueden construir con el editor visual.{/color}",
|
|
1019
|
+
"",
|
|
1020
|
+
"## Texto enriquecido",
|
|
1021
|
+
"",
|
|
1022
|
+
"Puedes combinar **negritas**, *cursivas*, `c\xF3digo`, enlaces como [ui-lib](https://example.com) y colores para destacar informaci\xF3n importante.",
|
|
1023
|
+
"",
|
|
1024
|
+
"- Bloques de texto",
|
|
1025
|
+
"- Listas y citas",
|
|
1026
|
+
"- Im\xE1genes, videos y tablas",
|
|
1027
|
+
"",
|
|
1028
|
+
"> Este bloque sirve para resaltar una nota, advertencia o resumen dentro del contenido.",
|
|
1029
|
+
"",
|
|
1030
|
+
"## Imagen",
|
|
1031
|
+
"",
|
|
1032
|
+
"",
|
|
1033
|
+
"",
|
|
1034
|
+
"## Video",
|
|
1035
|
+
"",
|
|
1036
|
+
"@[video:Video de ejemplo](https://www.youtube.com/watch?v=jNQXAC9IVRw)",
|
|
1037
|
+
"",
|
|
1038
|
+
"## Tabla",
|
|
1039
|
+
"",
|
|
1040
|
+
"| Bloque | Uso | Resultado |",
|
|
1041
|
+
"| --- | --- | --- |",
|
|
1042
|
+
"| T\xEDtulo | Separar secciones | Lectura m\xE1s clara |",
|
|
1043
|
+
"| Imagen | Mostrar contexto visual | Contenido m\xE1s atractivo |",
|
|
1044
|
+
"| Video | Explicar procesos | Mejor comprensi\xF3n |",
|
|
1045
|
+
"",
|
|
1046
|
+
"---",
|
|
1047
|
+
"",
|
|
1048
|
+
"Este ejemplo se puede editar, reorganizar o eliminar desde las acciones de cada bloque."
|
|
1049
|
+
].join("\n");
|
|
1050
|
+
function readFileAsDataUrl(file) {
|
|
1051
|
+
return new Promise((resolve, reject) => {
|
|
1052
|
+
const reader = new FileReader();
|
|
1053
|
+
reader.onload = () => {
|
|
1054
|
+
var _a;
|
|
1055
|
+
return resolve(String((_a = reader.result) != null ? _a : ""));
|
|
1056
|
+
};
|
|
1057
|
+
reader.onerror = () => {
|
|
1058
|
+
var _a;
|
|
1059
|
+
return reject((_a = reader.error) != null ? _a : new Error("No se pudo leer el archivo."));
|
|
1060
|
+
};
|
|
1061
|
+
reader.readAsDataURL(file);
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
const MarkdownPreviewContent = import_react.default.memo(function MarkdownPreviewContent2({
|
|
1065
|
+
html,
|
|
1066
|
+
className,
|
|
1067
|
+
interactive,
|
|
1068
|
+
selectable,
|
|
1069
|
+
showBlockControls,
|
|
1070
|
+
rootRef,
|
|
1071
|
+
onSelectPreviewRange,
|
|
1072
|
+
onUpdateHoverBlock
|
|
1073
|
+
}) {
|
|
1074
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1075
|
+
"div",
|
|
1076
|
+
{
|
|
1077
|
+
ref: rootRef,
|
|
1078
|
+
className: cx(
|
|
1079
|
+
"text-base leading-7 text-[var(--foreground)]",
|
|
1080
|
+
"[&>*]:my-0 [&_.fp-markdown-empty-line]:h-5 [&_.fp-markdown-empty-line]:leading-none [&_.fp-markdown-list-gap]:block [&_.fp-markdown-list-gap]:h-3",
|
|
1081
|
+
"[&_a]:font-medium [&_a]:text-[var(--primary)] [&_blockquote]:m-0 [&_blockquote]:border-l-4 [&_blockquote]:border-[var(--border)] [&_blockquote]:pl-3 [&_blockquote]:text-[var(--muted)]",
|
|
1082
|
+
"[&_code]:rounded-md [&_code]:bg-[color-mix(in_oklab,var(--muted)_14%,transparent)] [&_code]:px-1.5 [&_code]:py-0.5",
|
|
1083
|
+
interactive && "[&_[data-md-kind=line]]:block [&_[data-md-start]]:rounded-md [&_[data-md-start]]:transition [&_[data-md-start]:hover]:bg-[color-mix(in_oklab,var(--primary)_6%,transparent)]",
|
|
1084
|
+
selectable && "[&_[data-md-start]]:cursor-text",
|
|
1085
|
+
"[&_figcaption]:mt-2 [&_figcaption]:text-sm [&_figcaption]:text-[var(--muted)] [&_figure]:m-0",
|
|
1086
|
+
showBlockControls && "[&_iframe]:pointer-events-none",
|
|
1087
|
+
"[&_.fp-markdown-diagram]:overflow-auto [&_.fp-markdown-diagram]:rounded-xl [&_.fp-markdown-diagram]:border [&_.fp-markdown-diagram]:border-[var(--border)] [&_.fp-markdown-diagram]:bg-[color-mix(in_oklab,var(--card)_96%,var(--background))] [&_.fp-markdown-diagram]:p-4 [&_.fp-markdown-diagram-error]:border-[var(--danger)]",
|
|
1088
|
+
"[&_h1]:m-0 [&_h1]:text-3xl [&_h1]:font-semibold [&_h2]:m-0 [&_h2]:text-2xl [&_h2]:font-semibold [&_h3]:m-0 [&_h3]:text-xl [&_h3]:font-semibold [&_h4]:m-0 [&_h4]:text-lg [&_h4]:font-semibold",
|
|
1089
|
+
"[&_hr]:m-0 [&_hr]:border-[var(--border)] [&_iframe]:aspect-video [&_iframe]:w-full [&_iframe]:rounded-xl [&_iframe]:border [&_iframe]:border-[var(--border)] [&_img]:block [&_img]:max-h-[520px] [&_img]:w-auto [&_img]:max-w-full [&_img]:rounded-xl [&_img]:border [&_img]:border-[var(--border)] [&_img]:object-contain",
|
|
1090
|
+
"[&_ol]:m-0 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:m-0 [&_pre]:m-0 [&_pre]:overflow-auto [&_pre]:rounded-xl [&_pre]:border [&_pre]:border-[var(--border)] [&_pre]:bg-[color-mix(in_oklab,var(--foreground)_6%,transparent)] [&_pre]:p-3",
|
|
1091
|
+
"[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_strong]:font-semibold [&_table]:m-0 [&_table]:w-full [&_table]:border-collapse [&_table]:overflow-hidden [&_table]:rounded-xl [&_td]:border [&_td]:border-[var(--border)] [&_td]:px-3 [&_td]:py-2 [&_th]:border [&_th]:border-[var(--border)] [&_th]:bg-[color-mix(in_oklab,var(--muted)_10%,transparent)] [&_th]:px-3 [&_th]:py-2 [&_th]:text-left [&_th]:font-semibold [&_ul]:m-0 [&_ul]:list-disc [&_ul]:pl-5 [&_video]:max-h-[520px] [&_video]:w-full [&_video]:rounded-xl [&_video]:border [&_video]:border-[var(--border)]",
|
|
1092
|
+
className
|
|
1093
|
+
),
|
|
1094
|
+
onClick: interactive ? onSelectPreviewRange : void 0,
|
|
1095
|
+
onMouseUp: interactive ? onSelectPreviewRange : void 0,
|
|
1096
|
+
onMouseMove: showBlockControls ? onUpdateHoverBlock : void 0,
|
|
1097
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
});
|
|
1101
|
+
function MarkdownPreview({
|
|
1102
|
+
value,
|
|
1103
|
+
className,
|
|
1104
|
+
emptyLabel = "Sin contenido.",
|
|
1105
|
+
selectable = false,
|
|
1106
|
+
selectedRange,
|
|
1107
|
+
onSelectRange,
|
|
1108
|
+
onOpenSelectionMenu,
|
|
1109
|
+
showBlockControls = false,
|
|
1110
|
+
onOpenBlockMenu
|
|
1111
|
+
}) {
|
|
1112
|
+
const source = String(value != null ? value : "");
|
|
1113
|
+
const interactive = selectable || showBlockControls || Boolean(onSelectRange) || Boolean(onOpenSelectionMenu) || Boolean(onOpenBlockMenu);
|
|
1114
|
+
const html = import_react.default.useMemo(() => renderMarkdown(source, { includeRanges: interactive, selectedRange }), [interactive, selectedRange, source]);
|
|
1115
|
+
const idPrefix = import_react.default.useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1116
|
+
const shellRef = import_react.default.useRef(null);
|
|
1117
|
+
const rootRef = import_react.default.useRef(null);
|
|
1118
|
+
const hoverBlockRef = import_react.default.useRef(null);
|
|
1119
|
+
const hoverFrameRef = import_react.default.useRef(null);
|
|
1120
|
+
const [hoverBlock, setHoverBlock] = import_react.default.useState(null);
|
|
1121
|
+
const readElementRange = import_react.default.useCallback((element) => {
|
|
1122
|
+
const target = element == null ? void 0 : element.closest("[data-md-start][data-md-end]");
|
|
1123
|
+
if (!target) return null;
|
|
1124
|
+
const start = Number(target.dataset.mdStart);
|
|
1125
|
+
const end = Number(target.dataset.mdEnd);
|
|
1126
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start > end) return null;
|
|
1127
|
+
return { start, end, kind: target.dataset.mdKind };
|
|
1128
|
+
}, []);
|
|
1129
|
+
const readSelectionBoundary = import_react.default.useCallback(
|
|
1130
|
+
(node, offset) => {
|
|
1131
|
+
var _a;
|
|
1132
|
+
const element = node instanceof Element ? node : node.parentElement;
|
|
1133
|
+
const target = element == null ? void 0 : element.closest("[data-md-start][data-md-end]");
|
|
1134
|
+
if (!target || !((_a = rootRef.current) == null ? void 0 : _a.contains(target))) return null;
|
|
1135
|
+
const start = Number(target.dataset.mdStart);
|
|
1136
|
+
const end = Number(target.dataset.mdEnd);
|
|
1137
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start > end) return null;
|
|
1138
|
+
const range = document.createRange();
|
|
1139
|
+
range.selectNodeContents(target);
|
|
1140
|
+
try {
|
|
1141
|
+
range.setEnd(node, offset);
|
|
1142
|
+
} catch {
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
const renderedOffset = range.toString().length;
|
|
1146
|
+
const raw = source.slice(start, end);
|
|
1147
|
+
const markdownOffset = start + mapRenderedOffsetToMarkdown(raw, renderedOffset);
|
|
1148
|
+
return { offset: Math.max(start, Math.min(end, markdownOffset)), kind: target.dataset.mdKind };
|
|
1149
|
+
},
|
|
1150
|
+
[source]
|
|
1151
|
+
);
|
|
1152
|
+
const readExactDomSelectionRange = import_react.default.useCallback(
|
|
1153
|
+
(selection) => {
|
|
1154
|
+
if (!selection.rangeCount) return null;
|
|
1155
|
+
const root = rootRef.current;
|
|
1156
|
+
if (!root) return null;
|
|
1157
|
+
const range = selection.getRangeAt(0);
|
|
1158
|
+
if (!root.contains(range.startContainer) || !root.contains(range.endContainer)) return null;
|
|
1159
|
+
const start = readSelectionBoundary(range.startContainer, range.startOffset);
|
|
1160
|
+
const end = readSelectionBoundary(range.endContainer, range.endOffset);
|
|
1161
|
+
const focus = selection.focusNode ? readSelectionBoundary(selection.focusNode, selection.focusOffset) : null;
|
|
1162
|
+
if (!start || !end) return null;
|
|
1163
|
+
return {
|
|
1164
|
+
start: Math.min(start.offset, end.offset),
|
|
1165
|
+
end: Math.max(start.offset, end.offset),
|
|
1166
|
+
activeOffset: focus == null ? void 0 : focus.offset,
|
|
1167
|
+
kind: start.kind === end.kind ? start.kind : "selection"
|
|
1168
|
+
};
|
|
1169
|
+
},
|
|
1170
|
+
[readSelectionBoundary]
|
|
1171
|
+
);
|
|
1172
|
+
const selectPreviewRange = import_react.default.useCallback(
|
|
1173
|
+
(event) => {
|
|
1174
|
+
var _a;
|
|
1175
|
+
const root = rootRef.current;
|
|
1176
|
+
const selection = (_a = window.getSelection) == null ? void 0 : _a.call(window);
|
|
1177
|
+
if (root && selection && !selection.isCollapsed && root.contains(selection.anchorNode) && root.contains(selection.focusNode)) {
|
|
1178
|
+
const range2 = readExactDomSelectionRange(selection);
|
|
1179
|
+
if (range2 && range2.start !== range2.end) {
|
|
1180
|
+
onSelectRange == null ? void 0 : onSelectRange(range2);
|
|
1181
|
+
onOpenSelectionMenu == null ? void 0 : onOpenSelectionMenu(event, range2);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
const range = readElementRange(event.target);
|
|
1186
|
+
if (!range) return;
|
|
1187
|
+
},
|
|
1188
|
+
[onOpenSelectionMenu, onSelectRange, readElementRange, readExactDomSelectionRange]
|
|
1189
|
+
);
|
|
1190
|
+
const updateHoverBlock = import_react.default.useCallback(
|
|
1191
|
+
(event) => {
|
|
1192
|
+
var _a;
|
|
1193
|
+
if (!showBlockControls) return;
|
|
1194
|
+
const root = rootRef.current;
|
|
1195
|
+
const shell = shellRef.current;
|
|
1196
|
+
if (!root || !shell) return;
|
|
1197
|
+
const eventTarget = event.target;
|
|
1198
|
+
const target = eventTarget == null ? void 0 : eventTarget.closest("[data-md-start][data-md-end]");
|
|
1199
|
+
if (!eventTarget || !target || !root.contains(target)) {
|
|
1200
|
+
if (hoverBlockRef.current) {
|
|
1201
|
+
hoverBlockRef.current = null;
|
|
1202
|
+
if (hoverFrameRef.current !== null) {
|
|
1203
|
+
cancelAnimationFrame(hoverFrameRef.current);
|
|
1204
|
+
hoverFrameRef.current = null;
|
|
1205
|
+
}
|
|
1206
|
+
setHoverBlock((current) => current ? null : current);
|
|
1207
|
+
}
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const start = Number(target.dataset.mdStart);
|
|
1211
|
+
const end = Number(target.dataset.mdEnd);
|
|
1212
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return;
|
|
1213
|
+
const visualTarget = (_a = eventTarget.closest("figure, img, video, iframe, table, pre, blockquote, h1, h2, h3, h4")) != null ? _a : target;
|
|
1214
|
+
const rect = visualTarget.getBoundingClientRect();
|
|
1215
|
+
const shellRect = shell.getBoundingClientRect();
|
|
1216
|
+
const next = {
|
|
1217
|
+
start,
|
|
1218
|
+
end,
|
|
1219
|
+
kind: target.dataset.mdKind,
|
|
1220
|
+
top: Math.round(rect.top - shellRect.top),
|
|
1221
|
+
height: Math.round(Math.max(28, rect.height))
|
|
1222
|
+
};
|
|
1223
|
+
const previous = hoverBlockRef.current;
|
|
1224
|
+
if (previous && previous.start === next.start && previous.end === next.end && previous.kind === next.kind && previous.top === next.top && previous.height === next.height) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
hoverBlockRef.current = next;
|
|
1228
|
+
if (hoverFrameRef.current !== null) {
|
|
1229
|
+
cancelAnimationFrame(hoverFrameRef.current);
|
|
1230
|
+
}
|
|
1231
|
+
hoverFrameRef.current = requestAnimationFrame(() => {
|
|
1232
|
+
hoverFrameRef.current = null;
|
|
1233
|
+
setHoverBlock(next);
|
|
1234
|
+
});
|
|
1235
|
+
},
|
|
1236
|
+
[showBlockControls]
|
|
1237
|
+
);
|
|
1238
|
+
const clearHoverBlock = import_react.default.useCallback(() => {
|
|
1239
|
+
hoverBlockRef.current = null;
|
|
1240
|
+
if (hoverFrameRef.current !== null) {
|
|
1241
|
+
cancelAnimationFrame(hoverFrameRef.current);
|
|
1242
|
+
hoverFrameRef.current = null;
|
|
1243
|
+
}
|
|
1244
|
+
setHoverBlock((current) => current ? null : current);
|
|
1245
|
+
}, []);
|
|
1246
|
+
import_react.default.useEffect(() => {
|
|
1247
|
+
return () => {
|
|
1248
|
+
if (hoverFrameRef.current !== null) {
|
|
1249
|
+
cancelAnimationFrame(hoverFrameRef.current);
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
}, []);
|
|
1253
|
+
import_react.default.useEffect(() => {
|
|
1254
|
+
const root = rootRef.current;
|
|
1255
|
+
if (!root) return;
|
|
1256
|
+
const nodes = Array.from(root.querySelectorAll(".fp-markdown-diagram[data-diagram]"));
|
|
1257
|
+
if (!nodes.length) return;
|
|
1258
|
+
let cancelled = false;
|
|
1259
|
+
void import("mermaid").then((module2) => {
|
|
1260
|
+
const mermaid = module2.default;
|
|
1261
|
+
mermaid.initialize({
|
|
1262
|
+
startOnLoad: false,
|
|
1263
|
+
securityLevel: "strict",
|
|
1264
|
+
theme: document.documentElement.classList.contains("dark") ? "dark" : "default"
|
|
1265
|
+
});
|
|
1266
|
+
nodes.forEach((node, index) => {
|
|
1267
|
+
var _a;
|
|
1268
|
+
const diagram = (_a = node.dataset.diagram) != null ? _a : "";
|
|
1269
|
+
if (!diagram.trim()) return;
|
|
1270
|
+
void mermaid.render(`fpMarkdownDiagram${idPrefix}${index}`, diagram).then(({ svg }) => {
|
|
1271
|
+
if (cancelled) return;
|
|
1272
|
+
node.innerHTML = svg;
|
|
1273
|
+
}).catch(() => {
|
|
1274
|
+
node.classList.add("fp-markdown-diagram-error");
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
}).catch(() => {
|
|
1278
|
+
nodes.forEach((node) => node.classList.add("fp-markdown-diagram-error"));
|
|
1279
|
+
});
|
|
1280
|
+
return () => {
|
|
1281
|
+
cancelled = true;
|
|
1282
|
+
};
|
|
1283
|
+
}, [html, idPrefix]);
|
|
1284
|
+
if (!source.trim()) {
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx("text-sm text-[var(--muted)]", className), children: emptyLabel });
|
|
1286
|
+
}
|
|
1287
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1288
|
+
MarkdownPreviewContent,
|
|
1289
|
+
{
|
|
1290
|
+
html,
|
|
1291
|
+
className,
|
|
1292
|
+
interactive,
|
|
1293
|
+
selectable,
|
|
1294
|
+
showBlockControls,
|
|
1295
|
+
rootRef,
|
|
1296
|
+
onSelectPreviewRange: selectPreviewRange,
|
|
1297
|
+
onUpdateHoverBlock: updateHoverBlock
|
|
1298
|
+
}
|
|
1299
|
+
);
|
|
1300
|
+
if (!showBlockControls) return content;
|
|
1301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: shellRef, className: "relative pl-9", onMouseLeave: clearHoverBlock, children: [
|
|
1302
|
+
content,
|
|
1303
|
+
hoverBlock ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1304
|
+
"div",
|
|
1305
|
+
{
|
|
1306
|
+
className: "pointer-events-none absolute inset-x-0",
|
|
1307
|
+
style: { top: hoverBlock.top, height: hoverBlock.height },
|
|
1308
|
+
children: [
|
|
1309
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute bottom-0 left-9 right-0 top-0 rounded-lg border border-dashed border-[color-mix(in_oklab,var(--primary)_34%,transparent)] bg-[color-mix(in_oklab,var(--primary)_5%,transparent)]" }),
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1311
|
+
"button",
|
|
1312
|
+
{
|
|
1313
|
+
type: "button",
|
|
1314
|
+
title: "Acciones del bloque",
|
|
1315
|
+
"aria-label": "Acciones del bloque",
|
|
1316
|
+
className: "pointer-events-auto absolute left-0 top-1/2 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-lg border border-[var(--border)] bg-[var(--card)] text-[var(--foreground)] shadow-lg transition hover:border-[var(--primary)] hover:text-[var(--primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]",
|
|
1317
|
+
onMouseDown: (event) => {
|
|
1318
|
+
event.preventDefault();
|
|
1319
|
+
event.stopPropagation();
|
|
1320
|
+
},
|
|
1321
|
+
onClick: (event) => {
|
|
1322
|
+
event.stopPropagation();
|
|
1323
|
+
const range = { start: hoverBlock.start, end: hoverBlock.end, kind: hoverBlock.kind };
|
|
1324
|
+
onOpenBlockMenu == null ? void 0 : onOpenBlockMenu(event, range);
|
|
1325
|
+
},
|
|
1326
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MenuPuntosVerticalIcon, { className: "h-4 w-4" })
|
|
1327
|
+
}
|
|
1328
|
+
)
|
|
1329
|
+
]
|
|
1330
|
+
}
|
|
1331
|
+
) : null
|
|
1332
|
+
] });
|
|
1333
|
+
}
|
|
79
1334
|
function MarkdownEditor({
|
|
80
1335
|
value,
|
|
81
1336
|
onChange,
|
|
@@ -84,12 +1339,129 @@ function MarkdownEditor({
|
|
|
84
1339
|
height,
|
|
85
1340
|
className,
|
|
86
1341
|
templates,
|
|
87
|
-
disabled = false
|
|
1342
|
+
disabled = false,
|
|
1343
|
+
fullscreenHeight = "calc(100vh - 196px)",
|
|
1344
|
+
showPreview = true,
|
|
1345
|
+
enablePreview = true,
|
|
1346
|
+
enableFullscreen = true,
|
|
1347
|
+
enableDiagrams = true,
|
|
1348
|
+
labels,
|
|
1349
|
+
maxImageBytes = 6 * 1024 * 1024,
|
|
1350
|
+
maxVideoBytes = 24 * 1024 * 1024
|
|
88
1351
|
}) {
|
|
89
1352
|
const ref = import_react.default.useRef(null);
|
|
1353
|
+
const imageInputRef = import_react.default.useRef(null);
|
|
1354
|
+
const videoInputRef = import_react.default.useRef(null);
|
|
1355
|
+
const uploadInsertPositionRef = import_react.default.useRef(null);
|
|
1356
|
+
const blockMenuRef = import_react.default.useRef(null);
|
|
1357
|
+
const selectionMenuRef = import_react.default.useRef(null);
|
|
1358
|
+
const undoStackRef = import_react.default.useRef([]);
|
|
1359
|
+
const redoStackRef = import_react.default.useRef([]);
|
|
1360
|
+
const selectionRef = import_react.default.useRef({ start: 0, end: 0 });
|
|
1361
|
+
const [blockMenu, setBlockMenu] = import_react.default.useState(null);
|
|
1362
|
+
const [selectionMenu, setSelectionMenu] = import_react.default.useState(null);
|
|
1363
|
+
const [visibleViews, setVisibleViews] = import_react.default.useState(() => ({
|
|
1364
|
+
blocks: showPreview && enablePreview,
|
|
1365
|
+
preview: false,
|
|
1366
|
+
markdown: !(showPreview && enablePreview)
|
|
1367
|
+
}));
|
|
1368
|
+
const [fullscreen, setFullscreen] = import_react.default.useState(false);
|
|
1369
|
+
const [portalTarget, setPortalTarget] = import_react.default.useState(null);
|
|
1370
|
+
const L = {
|
|
1371
|
+
editor: "Editor",
|
|
1372
|
+
preview: "Preview",
|
|
1373
|
+
emptyPreview: "El preview aparecer\xE1 cuando escribas contenido.",
|
|
1374
|
+
headings: "Formato",
|
|
1375
|
+
paragraph: "Texto",
|
|
1376
|
+
h1: "Encabezado 1",
|
|
1377
|
+
h2: "Encabezado 2",
|
|
1378
|
+
h3: "Encabezado 3",
|
|
1379
|
+
quote: "Cita",
|
|
1380
|
+
undo: "Deshacer",
|
|
1381
|
+
redo: "Rehacer",
|
|
1382
|
+
bold: "Negrita",
|
|
1383
|
+
italic: "Cursiva",
|
|
1384
|
+
code: "C\xF3digo",
|
|
1385
|
+
bullets: "Lista",
|
|
1386
|
+
numbers: "Lista numerada",
|
|
1387
|
+
link: "Enlace",
|
|
1388
|
+
color: "Color de texto",
|
|
1389
|
+
insert: "Insertar",
|
|
1390
|
+
imageUrl: "Imagen desde URL",
|
|
1391
|
+
imageUpload: "Subir imagen",
|
|
1392
|
+
videoUrl: "Video desde URL",
|
|
1393
|
+
videoUpload: "Subir video",
|
|
1394
|
+
table: "Tabla",
|
|
1395
|
+
lineBreak: "Salto de l\xEDnea",
|
|
1396
|
+
diagram: "Diagrama UML",
|
|
1397
|
+
umlClass: "Diagrama de clases",
|
|
1398
|
+
umlSequence: "Diagrama de secuencia",
|
|
1399
|
+
divider: "Separador",
|
|
1400
|
+
blocksView: "Construcci\xF3n",
|
|
1401
|
+
markdownView: "Markdown",
|
|
1402
|
+
previewView: "Preview",
|
|
1403
|
+
sampleButton: "Construir ejemplo",
|
|
1404
|
+
showPreview: "Mostrar preview",
|
|
1405
|
+
hidePreview: "Ocultar preview",
|
|
1406
|
+
fullscreen: "Pantalla completa",
|
|
1407
|
+
exitFullscreen: "Salir de pantalla completa",
|
|
1408
|
+
...labels
|
|
1409
|
+
};
|
|
1410
|
+
import_react.default.useEffect(() => {
|
|
1411
|
+
if (typeof document === "undefined") return;
|
|
1412
|
+
setPortalTarget(document.body);
|
|
1413
|
+
}, []);
|
|
1414
|
+
import_react.default.useEffect(() => {
|
|
1415
|
+
setVisibleViews((current) => {
|
|
1416
|
+
if (!enablePreview) {
|
|
1417
|
+
return current.markdown && !current.blocks && !current.preview ? current : { blocks: false, preview: false, markdown: true };
|
|
1418
|
+
}
|
|
1419
|
+
if (current.blocks || current.preview || current.markdown) return current;
|
|
1420
|
+
return { blocks: true, preview: false, markdown: false };
|
|
1421
|
+
});
|
|
1422
|
+
}, [enablePreview]);
|
|
1423
|
+
import_react.default.useEffect(() => {
|
|
1424
|
+
if (!blockMenu && !selectionMenu) return;
|
|
1425
|
+
const close = () => {
|
|
1426
|
+
setBlockMenu(null);
|
|
1427
|
+
setSelectionMenu(null);
|
|
1428
|
+
};
|
|
1429
|
+
const onPointerDown = (event) => {
|
|
1430
|
+
var _a, _b;
|
|
1431
|
+
const target = event.target;
|
|
1432
|
+
if (target && (((_a = blockMenuRef.current) == null ? void 0 : _a.contains(target)) || ((_b = selectionMenuRef.current) == null ? void 0 : _b.contains(target)))) return;
|
|
1433
|
+
close();
|
|
1434
|
+
};
|
|
1435
|
+
const onKey = (event) => {
|
|
1436
|
+
if (event.key === "Escape") close();
|
|
1437
|
+
};
|
|
1438
|
+
window.addEventListener("pointerdown", onPointerDown, true);
|
|
1439
|
+
window.addEventListener("scroll", close, true);
|
|
1440
|
+
window.addEventListener("resize", close);
|
|
1441
|
+
window.addEventListener("keydown", onKey);
|
|
1442
|
+
return () => {
|
|
1443
|
+
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
1444
|
+
window.removeEventListener("scroll", close, true);
|
|
1445
|
+
window.removeEventListener("resize", close);
|
|
1446
|
+
window.removeEventListener("keydown", onKey);
|
|
1447
|
+
};
|
|
1448
|
+
}, [blockMenu, selectionMenu]);
|
|
1449
|
+
const pushUndo = import_react.default.useCallback((snapshot) => {
|
|
1450
|
+
const stack = undoStackRef.current;
|
|
1451
|
+
if (stack[stack.length - 1] === snapshot) return;
|
|
1452
|
+
stack.push(snapshot);
|
|
1453
|
+
if (stack.length > 80) stack.shift();
|
|
1454
|
+
}, []);
|
|
90
1455
|
const apply = import_react.default.useCallback(
|
|
91
|
-
(next, selectFrom, selectTo) => {
|
|
1456
|
+
(next, selectFrom, selectTo, recordHistory = true) => {
|
|
1457
|
+
if (recordHistory && next !== value) {
|
|
1458
|
+
pushUndo(value);
|
|
1459
|
+
redoStackRef.current = [];
|
|
1460
|
+
}
|
|
92
1461
|
onChange(next);
|
|
1462
|
+
if (typeof selectFrom === "number" && typeof selectTo === "number") {
|
|
1463
|
+
selectionRef.current = { start: selectFrom, end: selectTo };
|
|
1464
|
+
}
|
|
93
1465
|
const el = ref.current;
|
|
94
1466
|
if (!el) return;
|
|
95
1467
|
requestAnimationFrame(() => {
|
|
@@ -102,46 +1474,267 @@ function MarkdownEditor({
|
|
|
102
1474
|
}
|
|
103
1475
|
});
|
|
104
1476
|
},
|
|
105
|
-
[onChange]
|
|
1477
|
+
[onChange, pushUndo, value]
|
|
106
1478
|
);
|
|
1479
|
+
const rememberSelection = import_react.default.useCallback(() => {
|
|
1480
|
+
var _a, _b, _c;
|
|
1481
|
+
const el = ref.current;
|
|
1482
|
+
if (!el) return selectionRef.current;
|
|
1483
|
+
selectionRef.current = {
|
|
1484
|
+
start: (_a = el.selectionStart) != null ? _a : 0,
|
|
1485
|
+
end: (_c = (_b = el.selectionEnd) != null ? _b : el.selectionStart) != null ? _c : 0
|
|
1486
|
+
};
|
|
1487
|
+
return selectionRef.current;
|
|
1488
|
+
}, []);
|
|
1489
|
+
const activeSelection = import_react.default.useCallback(() => {
|
|
1490
|
+
var _a, _b;
|
|
1491
|
+
const el = ref.current;
|
|
1492
|
+
if (!el) return selectionRef.current;
|
|
1493
|
+
if (typeof document !== "undefined" && document.activeElement !== el) {
|
|
1494
|
+
return selectionRef.current;
|
|
1495
|
+
}
|
|
1496
|
+
const start = (_a = el.selectionStart) != null ? _a : selectionRef.current.start;
|
|
1497
|
+
const end = (_b = el.selectionEnd) != null ? _b : selectionRef.current.end;
|
|
1498
|
+
selectionRef.current = { start, end };
|
|
1499
|
+
return selectionRef.current;
|
|
1500
|
+
}, []);
|
|
1501
|
+
const selectMarkdownRange = import_react.default.useCallback((range) => {
|
|
1502
|
+
const start = Math.max(0, Math.min(value.length, range.start));
|
|
1503
|
+
const end = Math.max(0, Math.min(value.length, range.end));
|
|
1504
|
+
const next = { start: Math.min(start, end), end: Math.max(start, end) };
|
|
1505
|
+
selectionRef.current = next;
|
|
1506
|
+
requestAnimationFrame(() => {
|
|
1507
|
+
var _a;
|
|
1508
|
+
try {
|
|
1509
|
+
(_a = ref.current) == null ? void 0 : _a.setSelectionRange(next.start, next.end);
|
|
1510
|
+
} catch {
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
}, [value.length]);
|
|
1514
|
+
const handleTextChange = import_react.default.useCallback(
|
|
1515
|
+
(next) => {
|
|
1516
|
+
if (next !== value) {
|
|
1517
|
+
pushUndo(value);
|
|
1518
|
+
redoStackRef.current = [];
|
|
1519
|
+
}
|
|
1520
|
+
onChange(next);
|
|
1521
|
+
},
|
|
1522
|
+
[onChange, pushUndo, value]
|
|
1523
|
+
);
|
|
1524
|
+
const doUndo = import_react.default.useCallback(() => {
|
|
1525
|
+
const previous = undoStackRef.current.pop();
|
|
1526
|
+
if (previous === void 0) return;
|
|
1527
|
+
redoStackRef.current.push(value);
|
|
1528
|
+
apply(previous, previous.length, previous.length, false);
|
|
1529
|
+
}, [apply, value]);
|
|
1530
|
+
const doRedo = import_react.default.useCallback(() => {
|
|
1531
|
+
const next = redoStackRef.current.pop();
|
|
1532
|
+
if (next === void 0) return;
|
|
1533
|
+
pushUndo(value);
|
|
1534
|
+
apply(next, next.length, next.length, false);
|
|
1535
|
+
}, [apply, pushUndo, value]);
|
|
107
1536
|
const actWrap = import_react.default.useCallback(
|
|
108
1537
|
(left, right, fallbackText) => {
|
|
109
1538
|
var _a, _b;
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
const { next, selectFrom, selectTo } =
|
|
1539
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1540
|
+
const selection = activeSelection();
|
|
1541
|
+
const { next, selectFrom, selectTo } = smartWrapSelection(current, selection.start, selection.end, left, right, fallbackText);
|
|
113
1542
|
apply(next, selectFrom, selectTo);
|
|
114
1543
|
},
|
|
115
|
-
[apply]
|
|
1544
|
+
[activeSelection, apply, value]
|
|
116
1545
|
);
|
|
1546
|
+
const actLink = import_react.default.useCallback(() => {
|
|
1547
|
+
var _a, _b;
|
|
1548
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1549
|
+
const selection = activeSelection();
|
|
1550
|
+
const range = getSmartTextRange(current, selection.start, selection.end);
|
|
1551
|
+
const wrapper = findLinkWrapper(current, range.from, range.to);
|
|
1552
|
+
if (wrapper) {
|
|
1553
|
+
const result2 = splitLinkWrapper(current, wrapper, range);
|
|
1554
|
+
if (result2) apply(result2.next, result2.selectFrom, result2.selectTo);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const url = safeWindowPrompt("URL del enlace", "https://");
|
|
1558
|
+
if (!url) return;
|
|
1559
|
+
const result = smartLinkSelection(current, selection.start, selection.end, url);
|
|
1560
|
+
if (result) apply(result.next, result.selectFrom, result.selectTo);
|
|
1561
|
+
}, [activeSelection, apply, value]);
|
|
117
1562
|
const actPrefix = import_react.default.useCallback(
|
|
118
1563
|
(prefix) => {
|
|
119
1564
|
var _a, _b;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const { lineStart, lineEnd, block } = getSelectedLines(
|
|
1565
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1566
|
+
const selection = activeSelection();
|
|
1567
|
+
const { lineStart, lineEnd, block } = getSelectedLines(current, selection.start, selection.end);
|
|
123
1568
|
const replaced = prefixLines(block, prefix);
|
|
124
|
-
const next =
|
|
1569
|
+
const next = current.slice(0, lineStart) + replaced + current.slice(lineEnd);
|
|
125
1570
|
apply(next, lineStart, lineStart + replaced.length);
|
|
126
1571
|
},
|
|
127
|
-
[apply]
|
|
1572
|
+
[activeSelection, apply, value]
|
|
128
1573
|
);
|
|
129
1574
|
const actInsert = import_react.default.useCallback(
|
|
130
1575
|
(text) => {
|
|
131
1576
|
var _a, _b;
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
const start =
|
|
135
|
-
const end =
|
|
136
|
-
const a = Math.max(0, Math.min(
|
|
137
|
-
const b = Math.max(0, Math.min(
|
|
1577
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1578
|
+
const selection = activeSelection();
|
|
1579
|
+
const start = selection.start;
|
|
1580
|
+
const end = selection.end;
|
|
1581
|
+
const a = Math.max(0, Math.min(current.length, start));
|
|
1582
|
+
const b = Math.max(0, Math.min(current.length, end));
|
|
138
1583
|
const from = Math.min(a, b);
|
|
139
1584
|
const to = Math.max(a, b);
|
|
140
|
-
const next =
|
|
1585
|
+
const next = current.slice(0, from) + text + current.slice(to);
|
|
141
1586
|
apply(next, from + text.length, from + text.length);
|
|
142
1587
|
},
|
|
1588
|
+
[activeSelection, apply, value]
|
|
1589
|
+
);
|
|
1590
|
+
const actLineBreak = import_react.default.useCallback(() => {
|
|
1591
|
+
actInsert("\n");
|
|
1592
|
+
}, [actInsert]);
|
|
1593
|
+
const actNewParagraph = import_react.default.useCallback(() => {
|
|
1594
|
+
actInsert("\n\n");
|
|
1595
|
+
}, [actInsert]);
|
|
1596
|
+
const handleMarkdownKeyDown = import_react.default.useCallback(
|
|
1597
|
+
(event) => {
|
|
1598
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1599
|
+
if (event.key !== "Enter" || event.altKey || event.ctrlKey || event.metaKey || event.nativeEvent.isComposing) return;
|
|
1600
|
+
const target = event.currentTarget;
|
|
1601
|
+
const current = target.value;
|
|
1602
|
+
const start = (_a = target.selectionStart) != null ? _a : 0;
|
|
1603
|
+
const end = (_b = target.selectionEnd) != null ? _b : start;
|
|
1604
|
+
if (start !== end) return;
|
|
1605
|
+
const lineStart = current.lastIndexOf("\n", Math.max(0, start - 1)) + 1;
|
|
1606
|
+
const lineEndIndex = current.indexOf("\n", start);
|
|
1607
|
+
const lineEnd = lineEndIndex === -1 ? current.length : lineEndIndex;
|
|
1608
|
+
const line = current.slice(lineStart, lineEnd);
|
|
1609
|
+
const ordered = /^(\s*)(\d+)\.\s*(.*)$/.exec(line);
|
|
1610
|
+
const unordered = /^(\s*)([-*])\s*(.*)$/.exec(line);
|
|
1611
|
+
const isInListContext = () => {
|
|
1612
|
+
var _a2;
|
|
1613
|
+
if (ordered || unordered) return true;
|
|
1614
|
+
const previousLines = current.slice(0, lineStart).split("\n");
|
|
1615
|
+
for (let index = previousLines.length - 1; index >= 0; index -= 1) {
|
|
1616
|
+
const previous = (_a2 = previousLines[index]) != null ? _a2 : "";
|
|
1617
|
+
if (!previous.trim()) continue;
|
|
1618
|
+
if (/^\s*\d+\.\s+/.test(previous) || /^\s*[-*]\s+/.test(previous)) return true;
|
|
1619
|
+
if (/^#{1,4}\s+/.test(previous.trim()) || /^---+$/.test(previous.trim()) || /^>\s?/.test(previous.trim())) return false;
|
|
1620
|
+
}
|
|
1621
|
+
return false;
|
|
1622
|
+
};
|
|
1623
|
+
if (event.shiftKey) {
|
|
1624
|
+
if (!isInListContext()) return;
|
|
1625
|
+
event.preventDefault();
|
|
1626
|
+
const insert = " \n";
|
|
1627
|
+
const next = current.slice(0, start) + insert + current.slice(start);
|
|
1628
|
+
const cursor = start + insert.length;
|
|
1629
|
+
apply(next, cursor, cursor);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (ordered && !((_c = ordered[3]) == null ? void 0 : _c.trim())) {
|
|
1633
|
+
event.preventDefault();
|
|
1634
|
+
const next = current.slice(0, lineStart) + current.slice(lineEnd);
|
|
1635
|
+
apply(next, lineStart, lineStart);
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
if (unordered && !((_d = unordered[3]) == null ? void 0 : _d.trim())) {
|
|
1639
|
+
event.preventDefault();
|
|
1640
|
+
const next = current.slice(0, lineStart) + current.slice(lineEnd);
|
|
1641
|
+
apply(next, lineStart, lineStart);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
if (ordered) {
|
|
1645
|
+
event.preventDefault();
|
|
1646
|
+
const nextNumber = Number((_e = ordered[2]) != null ? _e : "0") + 1;
|
|
1647
|
+
const insert = `
|
|
1648
|
+
${nextNumber}. `;
|
|
1649
|
+
const next = current.slice(0, start) + insert + current.slice(start);
|
|
1650
|
+
const cursor = start + insert.length;
|
|
1651
|
+
apply(next, cursor, cursor);
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
if (unordered) {
|
|
1655
|
+
event.preventDefault();
|
|
1656
|
+
const insert = `
|
|
1657
|
+
${(_f = unordered[2]) != null ? _f : "-"} `;
|
|
1658
|
+
const next = current.slice(0, start) + insert + current.slice(start);
|
|
1659
|
+
const cursor = start + insert.length;
|
|
1660
|
+
apply(next, cursor, cursor);
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
143
1663
|
[apply]
|
|
144
1664
|
);
|
|
1665
|
+
const actInsertAt = import_react.default.useCallback(
|
|
1666
|
+
(position, text) => {
|
|
1667
|
+
const safePosition = Math.max(0, Math.min(value.length, position));
|
|
1668
|
+
const next = value.slice(0, safePosition) + text + value.slice(safePosition);
|
|
1669
|
+
const cursor = safePosition + text.length;
|
|
1670
|
+
apply(next, cursor, cursor);
|
|
1671
|
+
},
|
|
1672
|
+
[apply, value]
|
|
1673
|
+
);
|
|
1674
|
+
const actDeleteBlock = import_react.default.useCallback(
|
|
1675
|
+
(range) => {
|
|
1676
|
+
const { next, cursor } = removeMarkdownBlock(value, range);
|
|
1677
|
+
if (next === value) return;
|
|
1678
|
+
apply(next, cursor, cursor);
|
|
1679
|
+
},
|
|
1680
|
+
[apply, value]
|
|
1681
|
+
);
|
|
1682
|
+
const actInsertBlockAt = import_react.default.useCallback(
|
|
1683
|
+
(position, text) => {
|
|
1684
|
+
const block = text.trim();
|
|
1685
|
+
if (!block) return;
|
|
1686
|
+
const safePosition = Math.max(0, Math.min(value.length, position));
|
|
1687
|
+
const before = value.slice(0, safePosition);
|
|
1688
|
+
const after = value.slice(safePosition);
|
|
1689
|
+
const prefix = before.trim().length && !before.endsWith("\n\n") ? before.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
1690
|
+
const suffix = after.trim().length && !after.startsWith("\n\n") ? after.startsWith("\n") ? "\n" : "\n\n" : "";
|
|
1691
|
+
const next = before + prefix + block + suffix + after;
|
|
1692
|
+
const cursor = before.length + prefix.length + block.length;
|
|
1693
|
+
apply(next, cursor, cursor);
|
|
1694
|
+
},
|
|
1695
|
+
[apply, value]
|
|
1696
|
+
);
|
|
1697
|
+
const openBlockMenu = import_react.default.useCallback((event, range) => {
|
|
1698
|
+
const position = range.end;
|
|
1699
|
+
setSelectionMenu(null);
|
|
1700
|
+
setBlockMenu({ x: event.clientX, y: event.clientY, position, range });
|
|
1701
|
+
}, []);
|
|
1702
|
+
const openSelectionMenu = import_react.default.useCallback((event, range) => {
|
|
1703
|
+
if (range.start === range.end) return;
|
|
1704
|
+
setBlockMenu(null);
|
|
1705
|
+
setSelectionMenu({ x: event.clientX, y: event.clientY, range, marks: detectSelectionMarks(value, range) });
|
|
1706
|
+
}, [value]);
|
|
1707
|
+
const actInsertBlock = import_react.default.useCallback(
|
|
1708
|
+
(text) => {
|
|
1709
|
+
var _a, _b, _c, _d;
|
|
1710
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1711
|
+
const selection = activeSelection();
|
|
1712
|
+
const start = (_c = selection.start) != null ? _c : current.length;
|
|
1713
|
+
const end = (_d = selection.end) != null ? _d : start;
|
|
1714
|
+
const a = Math.max(0, Math.min(current.length, start));
|
|
1715
|
+
const b = Math.max(0, Math.min(current.length, end));
|
|
1716
|
+
const from = Math.min(a, b);
|
|
1717
|
+
const to = Math.max(a, b);
|
|
1718
|
+
const before = current.slice(0, from);
|
|
1719
|
+
const after = current.slice(to);
|
|
1720
|
+
const prefix = before.trim().length && !before.endsWith("\n\n") ? "\n\n" : "";
|
|
1721
|
+
const suffix = after.trim().length && !after.startsWith("\n\n") ? "\n\n" : "";
|
|
1722
|
+
const next = before + prefix + text.trim() + suffix + after;
|
|
1723
|
+
const cursor = before.length + prefix.length + text.trim().length;
|
|
1724
|
+
apply(next, cursor, cursor);
|
|
1725
|
+
},
|
|
1726
|
+
[activeSelection, apply, value]
|
|
1727
|
+
);
|
|
1728
|
+
const actColor = import_react.default.useCallback(
|
|
1729
|
+
(color) => {
|
|
1730
|
+
var _a, _b;
|
|
1731
|
+
const current = (_b = (_a = ref.current) == null ? void 0 : _a.value) != null ? _b : value;
|
|
1732
|
+
const selection = activeSelection();
|
|
1733
|
+
const { next, selectFrom, selectTo } = applyColorToSelection(current, selection.start, selection.end, color);
|
|
1734
|
+
apply(next, selectFrom, selectTo);
|
|
1735
|
+
},
|
|
1736
|
+
[activeSelection, apply, value]
|
|
1737
|
+
);
|
|
145
1738
|
const insertTemplate = import_react.default.useCallback(
|
|
146
1739
|
(t) => {
|
|
147
1740
|
if (!t.content.trim()) return;
|
|
@@ -150,50 +1743,762 @@ function MarkdownEditor({
|
|
|
150
1743
|
},
|
|
151
1744
|
[apply, value]
|
|
152
1745
|
);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
1746
|
+
const insertExample = import_react.default.useCallback(() => {
|
|
1747
|
+
const sep = value.trim().length ? "\n\n---\n\n" : "";
|
|
1748
|
+
const next = value + sep + (enableDiagrams ? MARKDOWN_EDITOR_SAMPLE : MARKDOWN_EDITOR_SAMPLE_WITHOUT_DIAGRAMS) + "\n";
|
|
1749
|
+
apply(next, next.length, next.length);
|
|
1750
|
+
if (enablePreview) {
|
|
1751
|
+
setVisibleViews((current) => ({ ...current, blocks: true, preview: true }));
|
|
1752
|
+
}
|
|
1753
|
+
}, [apply, enableDiagrams, enablePreview, value]);
|
|
1754
|
+
const alertMessage = import_react.default.useCallback((message) => {
|
|
1755
|
+
try {
|
|
1756
|
+
window.alert(message);
|
|
1757
|
+
} catch {
|
|
1758
|
+
}
|
|
1759
|
+
}, []);
|
|
1760
|
+
const renderSelectionMenu = () => {
|
|
1761
|
+
if (!selectionMenu || !portalTarget) return null;
|
|
1762
|
+
const activeButtonClass = "bg-[color-mix(in_oklab,var(--primary)_14%,transparent)] text-[var(--primary)] ring-1 ring-[color-mix(in_oklab,var(--primary)_35%,transparent)]";
|
|
1763
|
+
const menuButtonClass = (active) => cx(
|
|
1764
|
+
"flex h-8 w-8 items-center justify-center rounded-lg hover:bg-[color-mix(in_oklab,var(--primary)_10%,transparent)]",
|
|
1765
|
+
active && activeButtonClass
|
|
1766
|
+
);
|
|
1767
|
+
return (0, import_react_dom.createPortal)(
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1769
|
+
"div",
|
|
1770
|
+
{
|
|
1771
|
+
ref: selectionMenuRef,
|
|
1772
|
+
className: "fixed z-[2147483647] flex max-w-[min(92vw,520px)] flex-wrap items-center gap-1 rounded-xl border border-[var(--border)] bg-[var(--card)] p-1 text-sm text-[var(--foreground)] shadow-2xl",
|
|
1773
|
+
style: { left: Math.min(selectionMenu.x, Math.max(8, window.innerWidth - 540)), top: Math.min(selectionMenu.y + 10, Math.max(8, window.innerHeight - 110)) },
|
|
1774
|
+
children: [
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1776
|
+
"button",
|
|
1777
|
+
{
|
|
1778
|
+
type: "button",
|
|
1779
|
+
className: menuButtonClass(selectionMenu.marks.bold),
|
|
1780
|
+
title: L.bold,
|
|
1781
|
+
onClick: () => {
|
|
1782
|
+
actWrap("**", "**", "texto");
|
|
1783
|
+
setSelectionMenu(null);
|
|
1784
|
+
},
|
|
1785
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextBIcon, { className: "h-4 w-4" })
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
className: menuButtonClass(selectionMenu.marks.italic),
|
|
1793
|
+
title: L.italic,
|
|
1794
|
+
onClick: () => {
|
|
1795
|
+
actWrap("*", "*", "texto");
|
|
1796
|
+
setSelectionMenu(null);
|
|
1797
|
+
},
|
|
1798
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextItalicIcon, { className: "h-4 w-4" })
|
|
1799
|
+
}
|
|
1800
|
+
),
|
|
1801
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1802
|
+
"button",
|
|
1803
|
+
{
|
|
1804
|
+
type: "button",
|
|
1805
|
+
className: menuButtonClass(selectionMenu.marks.code),
|
|
1806
|
+
title: L.code,
|
|
1807
|
+
onClick: () => {
|
|
1808
|
+
actWrap("`", "`", "c\xF3digo");
|
|
1809
|
+
setSelectionMenu(null);
|
|
1810
|
+
},
|
|
1811
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CodeIcon, { className: "h-4 w-4" })
|
|
1812
|
+
}
|
|
1813
|
+
),
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1815
|
+
"button",
|
|
1816
|
+
{
|
|
1817
|
+
type: "button",
|
|
1818
|
+
className: menuButtonClass(selectionMenu.marks.link),
|
|
1819
|
+
title: L.link,
|
|
1820
|
+
onClick: () => {
|
|
1821
|
+
actLink();
|
|
1822
|
+
setSelectionMenu(null);
|
|
1823
|
+
},
|
|
1824
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.LinkIcon, { className: "h-4 w-4" })
|
|
1825
|
+
}
|
|
1826
|
+
),
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-1 h-6 w-px bg-[var(--border)]" }),
|
|
1828
|
+
COLOR_SWATCHES.map((color) => {
|
|
1829
|
+
var _a;
|
|
1830
|
+
const selected = ((_a = selectionMenu.marks.color) == null ? void 0 : _a.toLowerCase()) === color.toLowerCase();
|
|
1831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1832
|
+
"button",
|
|
1833
|
+
{
|
|
1834
|
+
type: "button",
|
|
1835
|
+
title: color,
|
|
1836
|
+
className: cx(
|
|
1837
|
+
"relative h-6 w-6 rounded-full border border-[var(--border)] shadow-sm outline-none transition hover:scale-105 focus-visible:ring-2 focus-visible:ring-[var(--ring)]",
|
|
1838
|
+
selected && "z-10 scale-110 ring-2 ring-[var(--ring)] ring-offset-2 ring-offset-[var(--card)] after:absolute after:inset-1.5 after:rounded-full after:border after:border-white/80"
|
|
1839
|
+
),
|
|
1840
|
+
style: { backgroundColor: color },
|
|
1841
|
+
onClick: () => {
|
|
1842
|
+
actColor(color);
|
|
1843
|
+
setSelectionMenu(null);
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
color
|
|
1847
|
+
);
|
|
1848
|
+
})
|
|
1849
|
+
]
|
|
1850
|
+
}
|
|
1851
|
+
),
|
|
1852
|
+
portalTarget
|
|
1853
|
+
);
|
|
1854
|
+
};
|
|
1855
|
+
const closeBlockMenu = import_react.default.useCallback(() => {
|
|
1856
|
+
setBlockMenu(null);
|
|
1857
|
+
}, []);
|
|
1858
|
+
const renderBlockMenu = () => {
|
|
1859
|
+
if (!blockMenu || !portalTarget) return null;
|
|
1860
|
+
const position = blockMenu.position;
|
|
1861
|
+
const itemClass = "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left hover:bg-[color-mix(in_oklab,var(--primary)_10%,transparent)]";
|
|
1862
|
+
return (0, import_react_dom.createPortal)(
|
|
1863
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1864
|
+
"div",
|
|
167
1865
|
{
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
1866
|
+
ref: blockMenuRef,
|
|
1867
|
+
className: "fixed z-[2147483647] grid max-h-[min(80vh,520px)] min-w-[240px] gap-1 overflow-y-auto overscroll-contain rounded-xl border border-[var(--border)] bg-[var(--card)] p-1 text-sm text-[var(--foreground)] shadow-2xl",
|
|
1868
|
+
style: { left: Math.min(blockMenu.x, Math.max(8, window.innerWidth - 260)), top: Math.min(blockMenu.y, Math.max(8, window.innerHeight - 360)) },
|
|
1869
|
+
children: [
|
|
1870
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-3 py-2 text-xs font-semibold uppercase tracking-wide text-[var(--muted)]", children: "Agregar bloque" }),
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1872
|
+
"button",
|
|
1873
|
+
{
|
|
1874
|
+
type: "button",
|
|
1875
|
+
className: itemClass,
|
|
1876
|
+
onClick: () => {
|
|
1877
|
+
actInsertAt(position, "\n");
|
|
1878
|
+
closeBlockMenu();
|
|
1879
|
+
},
|
|
1880
|
+
children: [
|
|
1881
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MinusIcon, { className: "h-4 w-4 rotate-90" }),
|
|
1882
|
+
"Salto de l\xEDnea"
|
|
1883
|
+
]
|
|
1884
|
+
}
|
|
1885
|
+
),
|
|
1886
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1887
|
+
"button",
|
|
1888
|
+
{
|
|
1889
|
+
type: "button",
|
|
1890
|
+
className: itemClass,
|
|
1891
|
+
onClick: () => {
|
|
1892
|
+
actInsertBlockAt(position, "## Titulo");
|
|
1893
|
+
closeBlockMenu();
|
|
1894
|
+
},
|
|
1895
|
+
children: [
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold", children: "H2" }),
|
|
1897
|
+
"Titulo"
|
|
1898
|
+
]
|
|
1899
|
+
}
|
|
1900
|
+
),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1902
|
+
"button",
|
|
1903
|
+
{
|
|
1904
|
+
type: "button",
|
|
1905
|
+
className: itemClass,
|
|
1906
|
+
onClick: () => {
|
|
1907
|
+
actInsertBlockAt(position, "Nuevo p\xE1rrafo");
|
|
1908
|
+
closeBlockMenu();
|
|
1909
|
+
},
|
|
1910
|
+
children: [
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextTIcon, { className: "h-4 w-4" }),
|
|
1912
|
+
"P\xE1rrafo"
|
|
1913
|
+
]
|
|
1914
|
+
}
|
|
1915
|
+
),
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1917
|
+
"button",
|
|
1918
|
+
{
|
|
1919
|
+
type: "button",
|
|
1920
|
+
className: itemClass,
|
|
1921
|
+
onClick: () => {
|
|
1922
|
+
insertImageUrl(position);
|
|
1923
|
+
closeBlockMenu();
|
|
1924
|
+
},
|
|
1925
|
+
children: [
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ImageIcon, { className: "h-4 w-4" }),
|
|
1927
|
+
"Imagen desde URL"
|
|
1928
|
+
]
|
|
1929
|
+
}
|
|
1930
|
+
),
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1932
|
+
"button",
|
|
1933
|
+
{
|
|
1934
|
+
type: "button",
|
|
1935
|
+
className: itemClass,
|
|
1936
|
+
onClick: () => {
|
|
1937
|
+
var _a;
|
|
1938
|
+
uploadInsertPositionRef.current = position;
|
|
1939
|
+
(_a = imageInputRef.current) == null ? void 0 : _a.click();
|
|
1940
|
+
closeBlockMenu();
|
|
1941
|
+
},
|
|
1942
|
+
children: [
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ImageIcon, { className: "h-4 w-4" }),
|
|
1944
|
+
"Subir imagen"
|
|
1945
|
+
]
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1949
|
+
"button",
|
|
1950
|
+
{
|
|
1951
|
+
type: "button",
|
|
1952
|
+
className: itemClass,
|
|
1953
|
+
onClick: () => {
|
|
1954
|
+
insertVideoUrl(position);
|
|
1955
|
+
closeBlockMenu();
|
|
1956
|
+
},
|
|
1957
|
+
children: [
|
|
1958
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.VideoIcon, { className: "h-4 w-4" }),
|
|
1959
|
+
"Video desde URL"
|
|
1960
|
+
]
|
|
1961
|
+
}
|
|
1962
|
+
),
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1964
|
+
"button",
|
|
1965
|
+
{
|
|
1966
|
+
type: "button",
|
|
1967
|
+
className: itemClass,
|
|
1968
|
+
onClick: () => {
|
|
1969
|
+
var _a;
|
|
1970
|
+
uploadInsertPositionRef.current = position;
|
|
1971
|
+
(_a = videoInputRef.current) == null ? void 0 : _a.click();
|
|
1972
|
+
closeBlockMenu();
|
|
1973
|
+
},
|
|
1974
|
+
children: [
|
|
1975
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.VideoIcon, { className: "h-4 w-4" }),
|
|
1976
|
+
"Subir video"
|
|
1977
|
+
]
|
|
1978
|
+
}
|
|
1979
|
+
),
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1981
|
+
"button",
|
|
1982
|
+
{
|
|
1983
|
+
type: "button",
|
|
1984
|
+
className: itemClass,
|
|
1985
|
+
onClick: () => {
|
|
1986
|
+
insertTable(position);
|
|
1987
|
+
closeBlockMenu();
|
|
1988
|
+
},
|
|
1989
|
+
children: [
|
|
1990
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TableIcon, { className: "h-4 w-4" }),
|
|
1991
|
+
"Tabla"
|
|
1992
|
+
]
|
|
1993
|
+
}
|
|
1994
|
+
),
|
|
1995
|
+
enableDiagrams ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1996
|
+
"button",
|
|
1997
|
+
{
|
|
1998
|
+
type: "button",
|
|
1999
|
+
className: itemClass,
|
|
2000
|
+
onClick: () => {
|
|
2001
|
+
insertUmlClassDiagram(position);
|
|
2002
|
+
closeBlockMenu();
|
|
2003
|
+
},
|
|
2004
|
+
children: [
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CodeIcon, { className: "h-4 w-4" }),
|
|
2006
|
+
"Diagrama UML"
|
|
2007
|
+
]
|
|
2008
|
+
}
|
|
2009
|
+
) : null,
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
2011
|
+
"button",
|
|
2012
|
+
{
|
|
2013
|
+
type: "button",
|
|
2014
|
+
className: itemClass,
|
|
2015
|
+
onClick: () => {
|
|
2016
|
+
actInsertBlockAt(position, "---");
|
|
2017
|
+
closeBlockMenu();
|
|
2018
|
+
},
|
|
2019
|
+
children: [
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MinusIcon, { className: "h-4 w-4" }),
|
|
2021
|
+
"Separador"
|
|
2022
|
+
]
|
|
2023
|
+
}
|
|
2024
|
+
),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
2026
|
+
"button",
|
|
2027
|
+
{
|
|
2028
|
+
type: "button",
|
|
2029
|
+
className: "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-[var(--danger)] hover:bg-[color-mix(in_oklab,var(--danger)_10%,transparent)]",
|
|
2030
|
+
onClick: () => {
|
|
2031
|
+
actDeleteBlock(blockMenu.range);
|
|
2032
|
+
closeBlockMenu();
|
|
2033
|
+
},
|
|
2034
|
+
children: [
|
|
2035
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TrashIcon, { className: "h-4 w-4" }),
|
|
2036
|
+
"Eliminar bloque"
|
|
2037
|
+
]
|
|
2038
|
+
}
|
|
2039
|
+
)
|
|
2040
|
+
]
|
|
177
2041
|
}
|
|
178
2042
|
),
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
2043
|
+
portalTarget
|
|
2044
|
+
);
|
|
2045
|
+
};
|
|
2046
|
+
const insertImageUrl = import_react.default.useCallback((position) => {
|
|
2047
|
+
var _a;
|
|
2048
|
+
const url = safeWindowPrompt("URL de la imagen", "https://");
|
|
2049
|
+
if (!url) return;
|
|
2050
|
+
if (!safeMediaUrl(url, "image")) {
|
|
2051
|
+
alertMessage("La imagen debe usar una URL http(s) v\xE1lida o un archivo de imagen permitido.");
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
const alt = (_a = safeWindowPrompt("Texto alternativo de la imagen", "Imagen")) != null ? _a : "Imagen";
|
|
2055
|
+
const markdown = ``;
|
|
2056
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2057
|
+
else actInsertBlock(markdown);
|
|
2058
|
+
}, [actInsertBlock, actInsertBlockAt, alertMessage]);
|
|
2059
|
+
const insertVideoUrl = import_react.default.useCallback((position) => {
|
|
2060
|
+
var _a;
|
|
2061
|
+
const url = safeWindowPrompt("URL del video", "https://");
|
|
2062
|
+
if (!url) return;
|
|
2063
|
+
if (!safeMediaUrl(url, "video")) {
|
|
2064
|
+
alertMessage("El video debe usar una URL http(s) v\xE1lida o un archivo de video permitido.");
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const title = (_a = safeWindowPrompt("Titulo del video", "Video")) != null ? _a : "Video";
|
|
2068
|
+
const markdown = `@[video:${title}](${url})`;
|
|
2069
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2070
|
+
else actInsertBlock(markdown);
|
|
2071
|
+
}, [actInsertBlock, actInsertBlockAt, alertMessage]);
|
|
2072
|
+
const insertTable = import_react.default.useCallback((position) => {
|
|
2073
|
+
const colsValue = Number(safeWindowPrompt("Columnas de la tabla", "3"));
|
|
2074
|
+
if (!Number.isFinite(colsValue)) return;
|
|
2075
|
+
const rowsValue = Number(safeWindowPrompt("Filas de la tabla", "3"));
|
|
2076
|
+
if (!Number.isFinite(rowsValue)) return;
|
|
2077
|
+
const cols = Math.max(1, Math.min(8, Math.round(colsValue)));
|
|
2078
|
+
const rows2 = Math.max(1, Math.min(12, Math.round(rowsValue)));
|
|
2079
|
+
const header = `| ${Array.from({ length: cols }, (_, index) => `Columna ${index + 1}`).join(" | ")} |`;
|
|
2080
|
+
const separator = `| ${Array.from({ length: cols }, () => "---").join(" | ")} |`;
|
|
2081
|
+
const body = Array.from({ length: rows2 }, () => `| ${Array.from({ length: cols }, () => " ").join(" | ")} |`).join("\n");
|
|
2082
|
+
const markdown = `${header}
|
|
2083
|
+
${separator}
|
|
2084
|
+
${body}`;
|
|
2085
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2086
|
+
else actInsertBlock(markdown);
|
|
2087
|
+
}, [actInsertBlock, actInsertBlockAt]);
|
|
2088
|
+
const insertUmlClassDiagram = import_react.default.useCallback((position) => {
|
|
2089
|
+
const markdown = [
|
|
2090
|
+
"```mermaid",
|
|
2091
|
+
"classDiagram",
|
|
2092
|
+
" class Usuario {",
|
|
2093
|
+
" +string nombre",
|
|
2094
|
+
" +iniciarSesion()",
|
|
2095
|
+
" }",
|
|
2096
|
+
" class Cuenta {",
|
|
2097
|
+
" +string email",
|
|
2098
|
+
" }",
|
|
2099
|
+
" Usuario --> Cuenta",
|
|
2100
|
+
"```"
|
|
2101
|
+
].join("\n");
|
|
2102
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2103
|
+
else actInsertBlock(markdown);
|
|
2104
|
+
}, [actInsertBlock, actInsertBlockAt]);
|
|
2105
|
+
const insertUmlSequenceDiagram = import_react.default.useCallback((position) => {
|
|
2106
|
+
const markdown = [
|
|
2107
|
+
"```mermaid",
|
|
2108
|
+
"sequenceDiagram",
|
|
2109
|
+
" participant Usuario",
|
|
2110
|
+
" participant Sistema",
|
|
2111
|
+
" Usuario->>Sistema: Solicita acceso",
|
|
2112
|
+
" Sistema-->>Usuario: Responde resultado",
|
|
2113
|
+
"```"
|
|
2114
|
+
].join("\n");
|
|
2115
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2116
|
+
else actInsertBlock(markdown);
|
|
2117
|
+
}, [actInsertBlock, actInsertBlockAt]);
|
|
2118
|
+
const insertUploadedFile = import_react.default.useCallback(
|
|
2119
|
+
async (file, kind, position) => {
|
|
2120
|
+
if (!file) return;
|
|
2121
|
+
const isImage = kind === "image";
|
|
2122
|
+
const maxBytes = isImage ? maxImageBytes : maxVideoBytes;
|
|
2123
|
+
if (file.size > maxBytes) {
|
|
2124
|
+
const mb = Math.round(maxBytes / 1024 / 1024);
|
|
2125
|
+
alertMessage(`${isImage ? "La imagen" : "El video"} supera el limite de ${mb} MB.`);
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (isImage && !file.type.startsWith("image/")) {
|
|
2129
|
+
alertMessage("Selecciona un archivo de imagen v\xE1lido.");
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
if (!isImage && !file.type.startsWith("video/")) {
|
|
2133
|
+
alertMessage("Selecciona un archivo de video v\xE1lido.");
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
try {
|
|
2137
|
+
const dataUrl = await readFileAsDataUrl(file);
|
|
2138
|
+
const markdown = isImage ? `` : `@[video:${file.name || "Video"}](${dataUrl})`;
|
|
2139
|
+
if (typeof position === "number") actInsertBlockAt(position, markdown);
|
|
2140
|
+
else actInsertBlock(markdown);
|
|
2141
|
+
} catch {
|
|
2142
|
+
alertMessage("No se pudo leer el archivo seleccionado.");
|
|
2143
|
+
}
|
|
2144
|
+
},
|
|
2145
|
+
[actInsertBlock, actInsertBlockAt, alertMessage, maxImageBytes, maxVideoBytes]
|
|
2146
|
+
);
|
|
2147
|
+
const Tool = ({
|
|
2148
|
+
content,
|
|
2149
|
+
children
|
|
2150
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Tooltip.default, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-0.5", children: content }), placement: "top", delay: 120, className: "max-w-[240px]", children });
|
|
2151
|
+
const ToolSep = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-1 h-6 w-px bg-[var(--border)]" });
|
|
2152
|
+
const toggleView = import_react.default.useCallback(
|
|
2153
|
+
(view) => {
|
|
2154
|
+
if (!enablePreview && view !== "markdown") return;
|
|
2155
|
+
setBlockMenu(null);
|
|
2156
|
+
setSelectionMenu(null);
|
|
2157
|
+
setVisibleViews((current) => {
|
|
2158
|
+
const next = { ...current, [view]: !current[view] };
|
|
2159
|
+
if (!enablePreview) return { blocks: false, preview: false, markdown: true };
|
|
2160
|
+
if (!next.blocks && !next.preview && !next.markdown) return current;
|
|
2161
|
+
return next;
|
|
2162
|
+
});
|
|
2163
|
+
},
|
|
2164
|
+
[enablePreview]
|
|
2165
|
+
);
|
|
2166
|
+
const renderEditor = (isFullscreen) => {
|
|
2167
|
+
const effectiveHeight = isFullscreen ? fullscreenHeight : height;
|
|
2168
|
+
const previewStyle = effectiveHeight ? { height: effectiveHeight } : void 0;
|
|
2169
|
+
const viewCount = Number(Boolean(visibleViews.blocks && enablePreview)) + Number(Boolean(visibleViews.preview && enablePreview)) + Number(Boolean(visibleViews.markdown));
|
|
2170
|
+
const gridColumns = viewCount >= 3 ? "lg:grid-cols-2 2xl:grid-cols-3" : viewCount === 2 ? "lg:grid-cols-2" : "grid-cols-1";
|
|
2171
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
2172
|
+
"div",
|
|
187
2173
|
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
2174
|
+
className: cx(
|
|
2175
|
+
"overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)]",
|
|
2176
|
+
isFullscreen && "flex h-full flex-col rounded-none border-0 bg-[var(--background)]",
|
|
2177
|
+
!isFullscreen && className
|
|
2178
|
+
),
|
|
2179
|
+
children: [
|
|
2180
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2181
|
+
"input",
|
|
2182
|
+
{
|
|
2183
|
+
ref: imageInputRef,
|
|
2184
|
+
type: "file",
|
|
2185
|
+
accept: "image/*",
|
|
2186
|
+
className: "hidden",
|
|
2187
|
+
onChange: (event) => {
|
|
2188
|
+
var _a, _b;
|
|
2189
|
+
const input = event.target;
|
|
2190
|
+
void insertUploadedFile((_b = (_a = input.files) == null ? void 0 : _a[0]) != null ? _b : null, "image", uploadInsertPositionRef.current);
|
|
2191
|
+
uploadInsertPositionRef.current = null;
|
|
2192
|
+
input.value = "";
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
),
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2197
|
+
"input",
|
|
2198
|
+
{
|
|
2199
|
+
ref: videoInputRef,
|
|
2200
|
+
type: "file",
|
|
2201
|
+
accept: "video/mp4,video/webm,video/ogg,video/*",
|
|
2202
|
+
className: "hidden",
|
|
2203
|
+
onChange: (event) => {
|
|
2204
|
+
var _a, _b;
|
|
2205
|
+
const input = event.target;
|
|
2206
|
+
void insertUploadedFile((_b = (_a = input.files) == null ? void 0 : _a[0]) != null ? _b : null, "video", uploadInsertPositionRef.current);
|
|
2207
|
+
uploadInsertPositionRef.current = null;
|
|
2208
|
+
input.value = "";
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
),
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5 border-b border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_92%,transparent)] p-2", children: [
|
|
2213
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [
|
|
2214
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.undo, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.undo, disabled: disabled || undoStackRef.current.length === 0, onClick: doUndo, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ArrowArcLeftIcon, { className: "h-4 w-4" }) }) }),
|
|
2215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.redo, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.redo, disabled: disabled || redoStackRef.current.length === 0, onClick: doRedo, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ArrowArcRightIcon, { className: "h-4 w-4" }) }) }),
|
|
2216
|
+
ToolSep,
|
|
2217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default, { children: [
|
|
2218
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Trigger, { disabled, className: "rounded-full px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Tooltip.default, { content: L.headings, placement: "top", delay: 120, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
2219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm font-semibold text-[var(--foreground)]", children: "H" }),
|
|
2220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ChevronDownIcon, { className: "h-4 w-4 text-[var(--muted)]" })
|
|
2221
|
+
] }) }) }),
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Content, { children: [
|
|
2223
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Label, { children: L.headings }),
|
|
2224
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Item, { onSelect: () => actPrefix(""), children: [
|
|
2225
|
+
"P - ",
|
|
2226
|
+
L.paragraph
|
|
2227
|
+
] }),
|
|
2228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Item, { onSelect: () => actPrefix("# "), children: [
|
|
2229
|
+
"H1 - ",
|
|
2230
|
+
L.h1
|
|
2231
|
+
] }),
|
|
2232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Item, { onSelect: () => actPrefix("## "), children: [
|
|
2233
|
+
"H2 - ",
|
|
2234
|
+
L.h2
|
|
2235
|
+
] }),
|
|
2236
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Item, { onSelect: () => actPrefix("### "), children: [
|
|
2237
|
+
"H3 - ",
|
|
2238
|
+
L.h3
|
|
2239
|
+
] }),
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.QuotesIcon, { className: "h-4 w-4" }), onSelect: () => actPrefix("> "), children: L.quote })
|
|
2241
|
+
] })
|
|
2242
|
+
] }),
|
|
2243
|
+
ToolSep,
|
|
2244
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.bold, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.bold, disabled, onClick: () => actWrap("**", "**", "texto"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextBIcon, { className: "h-4 w-4" }) }) }),
|
|
2245
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.italic, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.italic, disabled, onClick: () => actWrap("*", "*", "texto"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextItalicIcon, { className: "h-4 w-4" }) }) }),
|
|
2246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.code, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.code, disabled, onClick: () => actWrap("`", "`", "c\xF3digo"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CodeIcon, { className: "h-4 w-4" }) }) }),
|
|
2247
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.link, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2248
|
+
import_ActionIconButton.default,
|
|
2249
|
+
{
|
|
2250
|
+
size: "sm",
|
|
2251
|
+
title: L.link,
|
|
2252
|
+
disabled,
|
|
2253
|
+
onClick: actLink,
|
|
2254
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.LinkIcon, { className: "h-4 w-4" })
|
|
2255
|
+
}
|
|
2256
|
+
) }),
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default, { children: [
|
|
2258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Trigger, { disabled, className: "rounded-full px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Tooltip.default, { content: L.color, placement: "top", delay: 120, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
2259
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.PaletteIcon, { className: "h-4 w-4 text-[var(--foreground)]" }),
|
|
2260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ChevronDownIcon, { className: "h-4 w-4 text-[var(--muted)]" })
|
|
2261
|
+
] }) }) }),
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Content, { children: [
|
|
2263
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Label, { children: L.color }),
|
|
2264
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-5 gap-2 p-2", children: COLOR_SWATCHES.map((color) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2265
|
+
"button",
|
|
2266
|
+
{
|
|
2267
|
+
type: "button",
|
|
2268
|
+
title: color,
|
|
2269
|
+
className: "h-8 w-8 rounded-full border border-[var(--border)] shadow-sm outline-none transition hover:scale-105 focus-visible:ring-2 focus-visible:ring-[var(--ring)]",
|
|
2270
|
+
style: { backgroundColor: color },
|
|
2271
|
+
onClick: () => actColor(color)
|
|
2272
|
+
},
|
|
2273
|
+
color
|
|
2274
|
+
)) })
|
|
2275
|
+
] })
|
|
2276
|
+
] }),
|
|
2277
|
+
ToolSep,
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.bullets, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.bullets, disabled, onClick: () => actPrefix("- "), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ListBulletsIcon, { className: "h-4 w-4" }) }) }),
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.numbers, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.numbers, disabled, onClick: () => actPrefix("1. "), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ListNumbersIcon, { className: "h-4 w-4" }) }) }),
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tool, { content: L.quote, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ActionIconButton.default, { size: "sm", title: L.quote, disabled, onClick: () => actPrefix("> "), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.QuotesIcon, { className: "h-4 w-4" }) }) }),
|
|
2281
|
+
ToolSep,
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default, { children: [
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Trigger, { disabled, className: "rounded-full px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Tooltip.default, { content: L.insert, placement: "top", delay: 120, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-2", children: [
|
|
2284
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ImageIcon, { className: "h-4 w-4 text-[var(--foreground)]" }),
|
|
2285
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ChevronDownIcon, { className: "h-4 w-4 text-[var(--muted)]" })
|
|
2286
|
+
] }) }) }),
|
|
2287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Dropdown.default.Content, { children: [
|
|
2288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Label, { children: L.insert }),
|
|
2289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ImageIcon, { className: "h-4 w-4" }), onSelect: () => insertImageUrl(), children: L.imageUrl }),
|
|
2290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2291
|
+
import_Dropdown.default.Item,
|
|
2292
|
+
{
|
|
2293
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ImageIcon, { className: "h-4 w-4" }),
|
|
2294
|
+
onSelect: () => {
|
|
2295
|
+
var _a;
|
|
2296
|
+
uploadInsertPositionRef.current = null;
|
|
2297
|
+
(_a = imageInputRef.current) == null ? void 0 : _a.click();
|
|
2298
|
+
},
|
|
2299
|
+
children: L.imageUpload
|
|
2300
|
+
}
|
|
2301
|
+
),
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.VideoIcon, { className: "h-4 w-4" }), onSelect: () => insertVideoUrl(), children: L.videoUrl }),
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2304
|
+
import_Dropdown.default.Item,
|
|
2305
|
+
{
|
|
2306
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.VideoIcon, { className: "h-4 w-4" }),
|
|
2307
|
+
onSelect: () => {
|
|
2308
|
+
var _a;
|
|
2309
|
+
uploadInsertPositionRef.current = null;
|
|
2310
|
+
(_a = videoInputRef.current) == null ? void 0 : _a.click();
|
|
2311
|
+
},
|
|
2312
|
+
children: L.videoUpload
|
|
2313
|
+
}
|
|
2314
|
+
),
|
|
2315
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TableIcon, { className: "h-4 w-4" }), onSelect: () => insertTable(), children: L.table }),
|
|
2316
|
+
enableDiagrams ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CodeIcon, { className: "h-4 w-4" }), onSelect: () => insertUmlClassDiagram(), children: L.umlClass }),
|
|
2318
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CodeIcon, { className: "h-4 w-4" }), onSelect: () => insertUmlSequenceDiagram(), children: L.umlSequence })
|
|
2319
|
+
] }) : null,
|
|
2320
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Separator, {}),
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MinusIcon, { className: "h-4 w-4 rotate-90" }), onSelect: actLineBreak, children: L.lineBreak }),
|
|
2322
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.TextTIcon, { className: "h-4 w-4" }), onSelect: actNewParagraph, children: "Nuevo p\xE1rrafo" }),
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Dropdown.default.Item, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.MinusIcon, { className: "h-4 w-4" }), onSelect: () => actInsert("\n---\n"), children: L.divider })
|
|
2324
|
+
] })
|
|
2325
|
+
] })
|
|
2326
|
+
] }),
|
|
2327
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2328
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2329
|
+
import_Button.default,
|
|
2330
|
+
{
|
|
2331
|
+
size: "xs",
|
|
2332
|
+
variant: "secondary",
|
|
2333
|
+
disabled,
|
|
2334
|
+
leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.PlusIcon, { className: "h-3.5 w-3.5" }),
|
|
2335
|
+
onClick: insertExample,
|
|
2336
|
+
children: L.sampleButton
|
|
2337
|
+
}
|
|
2338
|
+
),
|
|
2339
|
+
(templates == null ? void 0 : templates.length) ? templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_Button.default, { size: "xs", variant: "ghost", disabled, onClick: () => insertTemplate(t), children: [
|
|
2340
|
+
"+ ",
|
|
2341
|
+
t.label
|
|
2342
|
+
] }, t.label)) : null
|
|
2343
|
+
] }),
|
|
2344
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex overflow-hidden rounded-full border border-[var(--border)] bg-[color-mix(in_oklab,var(--background)_70%,transparent)] p-0.5", children: [
|
|
2346
|
+
enablePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2348
|
+
"button",
|
|
2349
|
+
{
|
|
2350
|
+
type: "button",
|
|
2351
|
+
className: cx(
|
|
2352
|
+
"rounded-full px-3 py-1.5 text-xs font-semibold transition",
|
|
2353
|
+
visibleViews.blocks ? "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm" : "text-[var(--muted)] hover:bg-[color-mix(in_oklab,var(--muted)_10%,transparent)] hover:text-[var(--foreground)]"
|
|
2354
|
+
),
|
|
2355
|
+
"aria-pressed": visibleViews.blocks,
|
|
2356
|
+
onClick: () => toggleView("blocks"),
|
|
2357
|
+
children: L.blocksView
|
|
2358
|
+
}
|
|
2359
|
+
),
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2361
|
+
"button",
|
|
2362
|
+
{
|
|
2363
|
+
type: "button",
|
|
2364
|
+
className: cx(
|
|
2365
|
+
"rounded-full px-3 py-1.5 text-xs font-semibold transition",
|
|
2366
|
+
visibleViews.preview ? "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm" : "text-[var(--muted)] hover:bg-[color-mix(in_oklab,var(--muted)_10%,transparent)] hover:text-[var(--foreground)]"
|
|
2367
|
+
),
|
|
2368
|
+
"aria-pressed": visibleViews.preview,
|
|
2369
|
+
onClick: () => toggleView("preview"),
|
|
2370
|
+
children: L.previewView
|
|
2371
|
+
}
|
|
2372
|
+
)
|
|
2373
|
+
] }) : null,
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2375
|
+
"button",
|
|
2376
|
+
{
|
|
2377
|
+
type: "button",
|
|
2378
|
+
className: cx(
|
|
2379
|
+
"rounded-full px-3 py-1.5 text-xs font-semibold transition",
|
|
2380
|
+
visibleViews.markdown ? "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-sm" : "text-[var(--muted)] hover:bg-[color-mix(in_oklab,var(--muted)_10%,transparent)] hover:text-[var(--foreground)]"
|
|
2381
|
+
),
|
|
2382
|
+
"aria-pressed": visibleViews.markdown,
|
|
2383
|
+
onClick: () => toggleView("markdown"),
|
|
2384
|
+
children: L.markdownView
|
|
2385
|
+
}
|
|
2386
|
+
)
|
|
2387
|
+
] }),
|
|
2388
|
+
enableFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2389
|
+
import_ActionIconButton.default,
|
|
2390
|
+
{
|
|
2391
|
+
size: "sm",
|
|
2392
|
+
title: isFullscreen ? L.exitFullscreen : L.fullscreen,
|
|
2393
|
+
active: isFullscreen,
|
|
2394
|
+
onClick: () => setFullscreen((current) => !current),
|
|
2395
|
+
children: isFullscreen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CornersInIcon, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.CornersOutIcon, { className: "h-4 w-4" })
|
|
2396
|
+
}
|
|
2397
|
+
) : null
|
|
2398
|
+
] })
|
|
2399
|
+
] }),
|
|
2400
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
2401
|
+
"div",
|
|
2402
|
+
{
|
|
2403
|
+
className: cx(
|
|
2404
|
+
"grid min-h-0 divide-y divide-[var(--border)] lg:divide-y-0 lg:divide-x",
|
|
2405
|
+
gridColumns,
|
|
2406
|
+
isFullscreen && "flex-1"
|
|
2407
|
+
),
|
|
2408
|
+
children: [
|
|
2409
|
+
visibleViews.blocks && enablePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2410
|
+
"section",
|
|
2411
|
+
{
|
|
2412
|
+
"aria-label": L.blocksView,
|
|
2413
|
+
className: cx(
|
|
2414
|
+
"min-h-[220px] min-w-0 overflow-auto bg-[color-mix(in_oklab,var(--card)_96%,var(--background))] p-4",
|
|
2415
|
+
isFullscreen && "h-full"
|
|
2416
|
+
),
|
|
2417
|
+
style: previewStyle,
|
|
2418
|
+
children: value.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2419
|
+
MarkdownPreview,
|
|
2420
|
+
{
|
|
2421
|
+
value,
|
|
2422
|
+
className: "text-sm leading-6",
|
|
2423
|
+
emptyLabel: L.emptyPreview,
|
|
2424
|
+
selectable: true,
|
|
2425
|
+
showBlockControls: true,
|
|
2426
|
+
onSelectRange: selectMarkdownRange,
|
|
2427
|
+
onOpenSelectionMenu: openSelectionMenu,
|
|
2428
|
+
onOpenBlockMenu: openBlockMenu
|
|
2429
|
+
}
|
|
2430
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full min-h-[180px] flex-col items-center justify-center gap-3 rounded-xl border border-dashed border-[var(--border)] px-4 text-center text-sm text-[var(--muted)]", children: [
|
|
2431
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: L.emptyPreview }),
|
|
2432
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2433
|
+
import_Button.default,
|
|
2434
|
+
{
|
|
2435
|
+
size: "sm",
|
|
2436
|
+
variant: "secondary",
|
|
2437
|
+
leftIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.PlusIcon, { className: "h-4 w-4" }),
|
|
2438
|
+
disabled,
|
|
2439
|
+
onClick: (event) => openBlockMenu(event, { start: 0, end: 0 }),
|
|
2440
|
+
children: "Agregar bloque"
|
|
2441
|
+
}
|
|
2442
|
+
)
|
|
2443
|
+
] })
|
|
2444
|
+
}
|
|
2445
|
+
) : null,
|
|
2446
|
+
visibleViews.preview && enablePreview ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2447
|
+
"section",
|
|
2448
|
+
{
|
|
2449
|
+
"aria-label": L.preview,
|
|
2450
|
+
className: cx(
|
|
2451
|
+
"min-h-[220px] min-w-0 overflow-auto bg-[color-mix(in_oklab,var(--card)_96%,var(--background))] p-5",
|
|
2452
|
+
isFullscreen && "h-full"
|
|
2453
|
+
),
|
|
2454
|
+
style: previewStyle,
|
|
2455
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownPreview, { value, className: "text-sm leading-6", emptyLabel: L.emptyPreview })
|
|
2456
|
+
}
|
|
2457
|
+
) : null,
|
|
2458
|
+
visibleViews.markdown ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
2459
|
+
import_Textarea.default,
|
|
2460
|
+
{
|
|
2461
|
+
ref,
|
|
2462
|
+
value,
|
|
2463
|
+
onChange: (e) => {
|
|
2464
|
+
var _a, _b, _c;
|
|
2465
|
+
const target = e.target;
|
|
2466
|
+
selectionRef.current = { start: (_a = target.selectionStart) != null ? _a : 0, end: (_c = (_b = target.selectionEnd) != null ? _b : target.selectionStart) != null ? _c : 0 };
|
|
2467
|
+
handleTextChange(target.value);
|
|
2468
|
+
},
|
|
2469
|
+
onClick: rememberSelection,
|
|
2470
|
+
onFocus: rememberSelection,
|
|
2471
|
+
onKeyDown: handleMarkdownKeyDown,
|
|
2472
|
+
onKeyUp: rememberSelection,
|
|
2473
|
+
onMouseUp: rememberSelection,
|
|
2474
|
+
onSelect: rememberSelection,
|
|
2475
|
+
placeholder,
|
|
2476
|
+
"aria-label": L.editor,
|
|
2477
|
+
rows: isFullscreen ? Math.max(rows, 20) : rows,
|
|
2478
|
+
disabled,
|
|
2479
|
+
className: "resize-none rounded-none border-0 shadow-none focus:border-transparent focus:ring-0",
|
|
2480
|
+
style: effectiveHeight ? { height: effectiveHeight } : void 0
|
|
2481
|
+
}
|
|
2482
|
+
) }) : null
|
|
2483
|
+
]
|
|
2484
|
+
}
|
|
2485
|
+
)
|
|
2486
|
+
]
|
|
2487
|
+
}
|
|
2488
|
+
);
|
|
2489
|
+
};
|
|
2490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
2491
|
+
!fullscreen ? renderEditor(false) : null,
|
|
2492
|
+
fullscreen && portalTarget ? (0, import_react_dom.createPortal)(
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fixed inset-0 z-[2147483647] bg-[var(--background)] p-3 sm:p-5", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto h-full max-w-[1600px] overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] shadow-2xl", children: renderEditor(true) }) }),
|
|
2494
|
+
portalTarget
|
|
2495
|
+
) : null,
|
|
2496
|
+
renderSelectionMenu(),
|
|
2497
|
+
renderBlockMenu()
|
|
198
2498
|
] });
|
|
199
2499
|
}
|
|
2500
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2501
|
+
0 && (module.exports = {
|
|
2502
|
+
MarkdownPreview,
|
|
2503
|
+
renderMarkdown
|
|
2504
|
+
});
|