@underverse-ui/underverse 1.0.203 → 1.0.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +8 -2
  2. package/api-reference.json +1 -1
  3. package/dist/EmojiPicker-BAWP2RAS.js +14 -0
  4. package/dist/EmojiPicker-BAWP2RAS.js.map +1 -0
  5. package/dist/chunk-2V6A6PGI.js +14614 -0
  6. package/dist/chunk-2V6A6PGI.js.map +1 -0
  7. package/dist/chunk-4TYW5K4J.js +769 -0
  8. package/dist/chunk-4TYW5K4J.js.map +1 -0
  9. package/dist/chunk-7U3Y7R7U.js +199 -0
  10. package/dist/chunk-7U3Y7R7U.js.map +1 -0
  11. package/dist/chunk-IXEFOLUD.js +74 -0
  12. package/dist/chunk-IXEFOLUD.js.map +1 -0
  13. package/dist/chunk-MJV7ETJM.js +4501 -0
  14. package/dist/chunk-MJV7ETJM.js.map +1 -0
  15. package/dist/chunk-NSBPE2FW.js +17 -0
  16. package/dist/chunk-NSBPE2FW.js.map +1 -0
  17. package/dist/chunk-XJR7E6QB.js +2467 -0
  18. package/dist/chunk-XJR7E6QB.js.map +1 -0
  19. package/dist/chunk-Z63YNF3N.js +206 -0
  20. package/dist/chunk-Z63YNF3N.js.map +1 -0
  21. package/dist/emojis-I5AMZ3EE.js +8 -0
  22. package/dist/emojis-I5AMZ3EE.js.map +1 -0
  23. package/dist/index.cjs +36711 -35405
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +3 -134
  26. package/dist/index.d.ts +3 -134
  27. package/dist/index.js +2801 -25581
  28. package/dist/index.js.map +1 -1
  29. package/dist/lowlight-runtime-Y43Y7YTW.js +11 -0
  30. package/dist/lowlight-runtime-Y43Y7YTW.js.map +1 -0
  31. package/dist/menu-bar-NDAP3SOR.js +947 -0
  32. package/dist/menu-bar-NDAP3SOR.js.map +1 -0
  33. package/dist/ueditor.cjs +23631 -0
  34. package/dist/ueditor.cjs.map +1 -0
  35. package/dist/ueditor.d.cts +142 -0
  36. package/dist/ueditor.d.ts +142 -0
  37. package/dist/ueditor.js +20 -0
  38. package/dist/ueditor.js.map +1 -0
  39. package/package.json +54 -46
@@ -0,0 +1,4501 @@
1
+ import {
2
+ Tooltip,
3
+ chainEventHandlers,
4
+ cn,
5
+ mergeRefs,
6
+ setRefValue,
7
+ useSmartTranslations
8
+ } from "./chunk-XJR7E6QB.js";
9
+
10
+ // src/contexts/UnderverseConfigContext.tsx
11
+ import * as React from "react";
12
+ import { Fragment, jsx } from "react/jsx-runtime";
13
+ var UnderverseUIConfigContext = React.createContext(null);
14
+ function useUnderverseUIConfig() {
15
+ return React.useContext(UnderverseUIConfigContext) ?? {};
16
+ }
17
+ function UnderverseConfigProvider({ children, config }) {
18
+ if (!config) return /* @__PURE__ */ jsx(Fragment, { children });
19
+ return /* @__PURE__ */ jsx(UnderverseUIConfigContext.Provider, { value: config, children });
20
+ }
21
+
22
+ // src/utils/animations.ts
23
+ import { useEffect } from "react";
24
+ var shadcnAnimationStyles = `
25
+ /* ============================================
26
+ * DROPDOWN / POPOVER ANIMATIONS
27
+ * Uses spring-like cubic-bezier for natural feel
28
+ * ============================================ */
29
+
30
+ /* Native-like Combobox Animation - Mimics browser default select */
31
+ [data-state="open"][data-combobox-dropdown] {
32
+ animation: comboboxOpen 150ms cubic-bezier(0.2, 0, 0, 1);
33
+ transform-origin: top center;
34
+ }
35
+
36
+ [data-state="closed"][data-combobox-dropdown] {
37
+ animation: comboboxClose 120ms cubic-bezier(0.4, 0, 1, 1);
38
+ transform-origin: top center;
39
+ }
40
+
41
+ @keyframes comboboxOpen {
42
+ 0% {
43
+ opacity: 0;
44
+ transform: translateY(-4px) scaleY(0.9);
45
+ }
46
+ 100% {
47
+ opacity: 1;
48
+ transform: translateY(0) scaleY(1);
49
+ }
50
+ }
51
+
52
+ @keyframes comboboxClose {
53
+ 0% {
54
+ opacity: 1;
55
+ transform: translateY(0) scaleY(1);
56
+ }
57
+ 100% {
58
+ opacity: 0;
59
+ transform: translateY(-4px) scaleY(0.9);
60
+ }
61
+ }
62
+
63
+ /* Generic dropdown open/close */
64
+ [data-state="open"] {
65
+ animation: slideDownAndFade 220ms cubic-bezier(0.16, 1, 0.3, 1);
66
+ }
67
+
68
+ [data-state="closed"] {
69
+ animation: slideUpAndFade 180ms cubic-bezier(0.4, 0, 0.2, 1);
70
+ }
71
+
72
+ @keyframes slideDownAndFade {
73
+ from {
74
+ opacity: 0;
75
+ transform: translateY(-4px) scale(0.98);
76
+ }
77
+ to {
78
+ opacity: 1;
79
+ transform: translateY(0) scale(1);
80
+ }
81
+ }
82
+
83
+ @keyframes slideUpAndFade {
84
+ from {
85
+ opacity: 1;
86
+ transform: translateY(0) scale(1);
87
+ }
88
+ to {
89
+ opacity: 0;
90
+ transform: translateY(-4px) scale(0.98);
91
+ }
92
+ }
93
+
94
+ /* ============================================
95
+ * DROPDOWN ITEMS - Native-like instant appearance
96
+ * ============================================ */
97
+
98
+ /* Fast staggered animation for native feel */
99
+ .dropdown-item {
100
+ opacity: 0;
101
+ animation: itemFadeIn 120ms cubic-bezier(0.2, 0, 0, 1) forwards;
102
+ }
103
+
104
+ @keyframes itemFadeIn {
105
+ from {
106
+ opacity: 0;
107
+ transform: translateX(-4px);
108
+ }
109
+ to {
110
+ opacity: 1;
111
+ transform: translateX(0);
112
+ }
113
+ }
114
+
115
+ /* Item stagger delays - minimal for speed */
116
+ .dropdown-item:nth-child(1) { animation-delay: 0ms; }
117
+ .dropdown-item:nth-child(2) { animation-delay: 15ms; }
118
+ .dropdown-item:nth-child(3) { animation-delay: 30ms; }
119
+ .dropdown-item:nth-child(4) { animation-delay: 45ms; }
120
+ .dropdown-item:nth-child(5) { animation-delay: 60ms; }
121
+ .dropdown-item:nth-child(6) { animation-delay: 75ms; }
122
+ .dropdown-item:nth-child(7) { animation-delay: 90ms; }
123
+ .dropdown-item:nth-child(8) { animation-delay: 105ms; }
124
+ .dropdown-item:nth-child(n+9) { animation-delay: 120ms; }
125
+
126
+ /* ============================================
127
+ * DATEPICKER ANIMATIONS
128
+ * ============================================ */
129
+
130
+ .datepicker-day {
131
+ opacity: 0;
132
+ animation: dayFadeIn 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
133
+ }
134
+
135
+ @keyframes dayFadeIn {
136
+ from {
137
+ opacity: 0;
138
+ transform: scale(0.8);
139
+ }
140
+ to {
141
+ opacity: 1;
142
+ transform: scale(1);
143
+ }
144
+ }
145
+
146
+ /* ============================================
147
+ * TOOLTIP ANIMATIONS
148
+ * ============================================ */
149
+
150
+ [data-tooltip] {
151
+ animation: tooltipIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
152
+ }
153
+
154
+ @keyframes tooltipIn {
155
+ from {
156
+ opacity: 0;
157
+ transform: scale(0.95);
158
+ }
159
+ to {
160
+ opacity: 1;
161
+ transform: scale(1);
162
+ }
163
+ }
164
+
165
+ /* ============================================
166
+ * MODAL / DIALOG ANIMATIONS
167
+ * ============================================ */
168
+
169
+ .modal-content {
170
+ animation: scaleIn 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
171
+ }
172
+
173
+ @keyframes scaleIn {
174
+ from {
175
+ opacity: 0;
176
+ transform: scale(0.9);
177
+ }
178
+ to {
179
+ opacity: 1;
180
+ transform: scale(1);
181
+ }
182
+ }
183
+
184
+ /* Smooth backdrop blur transition */
185
+ .backdrop-animate {
186
+ transition: backdrop-filter 200ms ease, background-color 200ms ease;
187
+ }
188
+ `;
189
+ function ensureAnimationStylesInjected() {
190
+ if (typeof document !== "undefined") {
191
+ const styleId = "shadcn-animations";
192
+ if (!document.getElementById(styleId)) {
193
+ const styleElement = document.createElement("style");
194
+ styleElement.id = styleId;
195
+ styleElement.textContent = shadcnAnimationStyles;
196
+ document.head.appendChild(styleElement);
197
+ }
198
+ }
199
+ }
200
+ function useShadCNAnimations() {
201
+ useEffect(() => {
202
+ ensureAnimationStylesInjected();
203
+ }, []);
204
+ }
205
+ function injectAnimationStyles() {
206
+ ensureAnimationStylesInjected();
207
+ }
208
+ function getAnimationStyles() {
209
+ return shadcnAnimationStyles;
210
+ }
211
+
212
+ // src/components/Popover.tsx
213
+ import * as React2 from "react";
214
+ import { createPortal } from "react-dom";
215
+
216
+ // src/utils/radius.ts
217
+ var STANDARD_BORDER_MODES = ["none", "sm", "md", "lg", "xl", "2xl", "3xl", "full"];
218
+ var CUSTOM_BORDER_MAP = {
219
+ daewoo: "rounded",
220
+ infiniq: "rounded-full"
221
+ };
222
+ var BORDER_MODE_DOCS_TYPE = `"${STANDARD_BORDER_MODES.join('" | "')}" | "${Object.keys(CUSTOM_BORDER_MAP).join('" | "')}"`;
223
+ function getBorderRadiusClass(borderMode = "full") {
224
+ if (!borderMode) return "rounded-full";
225
+ if (borderMode in CUSTOM_BORDER_MAP) {
226
+ return CUSTOM_BORDER_MAP[borderMode];
227
+ }
228
+ if (STANDARD_BORDER_MODES.includes(borderMode)) {
229
+ return `rounded-${borderMode}`;
230
+ }
231
+ return borderMode;
232
+ }
233
+ function getPanelBorderRadiusClass(borderMode = "lg") {
234
+ const resolved = borderMode ?? "lg";
235
+ if (resolved === "full" || resolved === "infiniq") {
236
+ return "rounded-2xl";
237
+ }
238
+ return getBorderRadiusClass(resolved);
239
+ }
240
+
241
+ // src/components/Popover.tsx
242
+ import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
243
+ function getTransformOrigin(side, align) {
244
+ if (side === "top") return `${align === "end" ? "right" : "left"} bottom`;
245
+ if (side === "bottom") return `${align === "end" ? "right" : "left"} top`;
246
+ if (side === "left") return "right top";
247
+ return "left top";
248
+ }
249
+ function normalizePlacement(placement) {
250
+ switch (placement) {
251
+ case "top":
252
+ return { side: "top", align: "start" };
253
+ case "bottom":
254
+ return { side: "bottom", align: "start" };
255
+ case "left":
256
+ return { side: "left", align: "start" };
257
+ case "right":
258
+ return { side: "right", align: "start" };
259
+ default: {
260
+ const [side, align] = placement.split("-");
261
+ return { side, align };
262
+ }
263
+ }
264
+ }
265
+ var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
266
+ function overflowAmount(left, width, viewportWidth, padding) {
267
+ const min = padding;
268
+ const max = viewportWidth - padding;
269
+ const overflowLeft = Math.max(0, min - left);
270
+ const overflowRight = Math.max(0, left + width - max);
271
+ return overflowLeft + overflowRight;
272
+ }
273
+ function computePopoverPosition(args) {
274
+ const { triggerRect, contentSize, placement, offset, padding, viewport } = args;
275
+ let { side, align } = normalizePlacement(placement);
276
+ if (side === "bottom") {
277
+ const bottomTop = triggerRect.bottom + offset;
278
+ const overflowsBottom = bottomTop + contentSize.height > viewport.height - padding;
279
+ const topTop = triggerRect.top - offset - contentSize.height;
280
+ const fitsTop = topTop >= padding;
281
+ if (overflowsBottom && fitsTop) side = "top";
282
+ } else if (side === "top") {
283
+ const topTop = triggerRect.top - offset - contentSize.height;
284
+ const overflowsTop = topTop < padding;
285
+ const bottomTop = triggerRect.bottom + offset;
286
+ const fitsBottom = bottomTop + contentSize.height <= viewport.height - padding;
287
+ if (overflowsTop && fitsBottom) side = "bottom";
288
+ } else if (side === "right") {
289
+ const rightLeft = triggerRect.right + offset;
290
+ const overflowsRight = rightLeft + contentSize.width > viewport.width - padding;
291
+ const leftLeft = triggerRect.left - offset - contentSize.width;
292
+ const fitsLeft = leftLeft >= padding;
293
+ if (overflowsRight && fitsLeft) side = "left";
294
+ } else if (side === "left") {
295
+ const leftLeft = triggerRect.left - offset - contentSize.width;
296
+ const overflowsLeft = leftLeft < padding;
297
+ const rightLeft = triggerRect.right + offset;
298
+ const fitsRight = rightLeft + contentSize.width <= viewport.width - padding;
299
+ if (overflowsLeft && fitsRight) side = "right";
300
+ }
301
+ let top = 0;
302
+ let left = 0;
303
+ if (side === "top" || side === "bottom") {
304
+ const leftStart = triggerRect.left;
305
+ const leftEnd = triggerRect.right - contentSize.width;
306
+ const startOverflow = overflowAmount(leftStart, contentSize.width, viewport.width, padding);
307
+ const endOverflow = overflowAmount(leftEnd, contentSize.width, viewport.width, padding);
308
+ if (align === "start" && startOverflow > 0 && endOverflow < startOverflow) align = "end";
309
+ if (align === "end" && endOverflow > 0 && startOverflow < endOverflow) align = "start";
310
+ left = align === "end" ? leftEnd : leftStart;
311
+ top = side === "top" ? triggerRect.top - offset - contentSize.height : triggerRect.bottom + offset;
312
+ left = clamp(left, padding, viewport.width - contentSize.width - padding);
313
+ top = clamp(top, padding, viewport.height - contentSize.height - padding);
314
+ return { top, left, side, align };
315
+ }
316
+ top = triggerRect.top;
317
+ left = side === "left" ? triggerRect.left - offset - contentSize.width : triggerRect.right + offset;
318
+ left = clamp(left, padding, viewport.width - contentSize.width - padding);
319
+ top = clamp(top, padding, viewport.height - contentSize.height - padding);
320
+ return { top, left, side, align };
321
+ }
322
+ var Popover = ({
323
+ trigger,
324
+ children,
325
+ className,
326
+ contentClassName,
327
+ contentProps,
328
+ contentScrollable = false,
329
+ placement = "bottom",
330
+ modal = false,
331
+ disabled = false,
332
+ open,
333
+ onOpenChange,
334
+ matchTriggerWidth = false,
335
+ contentWidth,
336
+ borderMode
337
+ }) => {
338
+ const isControlled = open !== void 0;
339
+ const [internalOpen, setInternalOpen] = React2.useState(false);
340
+ const triggerRef = React2.useRef(null);
341
+ const positionerRef = React2.useRef(null);
342
+ const panelRef = React2.useRef(null);
343
+ const lastAppliedRef = React2.useRef(null);
344
+ useShadCNAnimations();
345
+ const globalConfig = useUnderverseUIConfig();
346
+ const resolvedBorderMode = borderMode ?? globalConfig.popover?.borderMode ?? globalConfig.borderMode;
347
+ const isOpen = isControlled ? open : internalOpen;
348
+ const setIsOpen = React2.useCallback(
349
+ (next) => {
350
+ if (!isControlled) setInternalOpen(next);
351
+ onOpenChange?.(next);
352
+ },
353
+ [isControlled, onOpenChange]
354
+ );
355
+ const offset = 4;
356
+ const padding = 8;
357
+ const triggerSelector = React2.useId();
358
+ const initialPlacement = React2.useMemo(() => normalizePlacement(placement), [placement]);
359
+ React2.useLayoutEffect(() => {
360
+ if (typeof document === "undefined") return;
361
+ const triggerEl = document.querySelector(`[data-underverse-popover-trigger="${triggerSelector}"]`);
362
+ if (triggerEl) {
363
+ triggerRef.current = triggerEl;
364
+ }
365
+ }, [triggerSelector, trigger]);
366
+ const updatePosition = React2.useCallback(() => {
367
+ const triggerEl = triggerRef.current;
368
+ const positionerEl = positionerRef.current;
369
+ const panelEl = panelRef.current;
370
+ if (!triggerEl || !positionerEl || !panelEl) return;
371
+ const triggerRect = triggerEl.getBoundingClientRect();
372
+ const widthWanted = matchTriggerWidth ? triggerRect.width : contentWidth;
373
+ const widthPx = widthWanted == null ? void 0 : Math.max(0, Math.round(widthWanted));
374
+ if (widthPx == null) {
375
+ if (positionerEl.style.width) positionerEl.style.width = "";
376
+ } else if (positionerEl.style.width !== `${widthPx}px`) {
377
+ positionerEl.style.width = `${widthPx}px`;
378
+ }
379
+ const prevApplied = lastAppliedRef.current;
380
+ const measuredWidth = positionerEl.offsetWidth;
381
+ const measuredHeight = positionerEl.offsetHeight;
382
+ const contentRect = positionerEl.getBoundingClientRect();
383
+ const contentBoxWidth = measuredWidth || contentRect.width;
384
+ const contentBoxHeight = measuredHeight || contentRect.height;
385
+ const next = computePopoverPosition({
386
+ triggerRect,
387
+ contentSize: { width: contentBoxWidth, height: contentBoxHeight },
388
+ placement,
389
+ offset,
390
+ padding,
391
+ viewport: { width: window.innerWidth, height: window.innerHeight }
392
+ });
393
+ const top = Math.round(next.top);
394
+ const left = Math.round(next.left);
395
+ const applied = prevApplied && Math.abs(prevApplied.top - top) < 0.5 && Math.abs(prevApplied.left - left) < 0.5 && prevApplied.side === next.side && prevApplied.align === next.align && prevApplied.width === widthPx;
396
+ if (applied) return;
397
+ lastAppliedRef.current = { top, left, side: next.side, align: next.align, width: widthPx };
398
+ positionerEl.style.transform = `translate3d(${left}px, ${top}px, 0)`;
399
+ panelEl.style.transformOrigin = getTransformOrigin(next.side, next.align);
400
+ if (positionerEl.style.visibility !== "visible") positionerEl.style.visibility = "visible";
401
+ if (positionerEl.style.pointerEvents !== "auto") positionerEl.style.pointerEvents = "auto";
402
+ }, [placement, matchTriggerWidth, contentWidth]);
403
+ React2.useLayoutEffect(() => {
404
+ if (!isOpen) return;
405
+ updatePosition();
406
+ let raf1 = 0;
407
+ let raf2 = 0;
408
+ raf1 = window.requestAnimationFrame(() => {
409
+ updatePosition();
410
+ raf2 = window.requestAnimationFrame(() => updatePosition());
411
+ });
412
+ return () => {
413
+ cancelAnimationFrame(raf1);
414
+ cancelAnimationFrame(raf2);
415
+ };
416
+ }, [isOpen, updatePosition]);
417
+ React2.useEffect(() => {
418
+ if (!isOpen) return;
419
+ let raf = 0;
420
+ const tick = () => {
421
+ updatePosition();
422
+ raf = window.requestAnimationFrame(tick);
423
+ };
424
+ raf = window.requestAnimationFrame(tick);
425
+ return () => window.cancelAnimationFrame(raf);
426
+ }, [isOpen, updatePosition]);
427
+ React2.useEffect(() => {
428
+ if (!isOpen) return;
429
+ let raf = 0;
430
+ const handler = () => {
431
+ cancelAnimationFrame(raf);
432
+ raf = window.requestAnimationFrame(() => updatePosition());
433
+ };
434
+ handler();
435
+ window.addEventListener("resize", handler);
436
+ window.addEventListener("scroll", handler, true);
437
+ document.addEventListener("scroll", handler, true);
438
+ return () => {
439
+ cancelAnimationFrame(raf);
440
+ window.removeEventListener("resize", handler);
441
+ window.removeEventListener("scroll", handler, true);
442
+ document.removeEventListener("scroll", handler, true);
443
+ };
444
+ }, [isOpen, updatePosition]);
445
+ React2.useEffect(() => {
446
+ if (!isOpen) return;
447
+ if (typeof ResizeObserver === "undefined") return;
448
+ const ro = new ResizeObserver(() => updatePosition());
449
+ if (positionerRef.current) ro.observe(positionerRef.current);
450
+ if (triggerRef.current) ro.observe(triggerRef.current);
451
+ return () => ro.disconnect();
452
+ }, [isOpen, updatePosition]);
453
+ React2.useLayoutEffect(() => {
454
+ if (!isOpen) {
455
+ lastAppliedRef.current = null;
456
+ return;
457
+ }
458
+ }, [isOpen]);
459
+ React2.useEffect(() => {
460
+ if (!isOpen) return;
461
+ const handleClickOutside = (event) => {
462
+ const target = event.target;
463
+ const triggerEl = triggerRef.current;
464
+ const popoverEl = positionerRef.current;
465
+ if (!triggerEl || !popoverEl) return;
466
+ if (triggerEl.contains(target)) return;
467
+ if (popoverEl.contains(target)) return;
468
+ if (target instanceof Element && target.closest("[data-popover]")) return;
469
+ setIsOpen(false);
470
+ };
471
+ const handleEscape = (event) => {
472
+ if (event.key === "Escape") {
473
+ setIsOpen(false);
474
+ }
475
+ };
476
+ if (typeof document !== "undefined") {
477
+ document.addEventListener("mousedown", handleClickOutside);
478
+ document.addEventListener("keydown", handleEscape);
479
+ return () => {
480
+ document.removeEventListener("mousedown", handleClickOutside);
481
+ document.removeEventListener("keydown", handleEscape);
482
+ };
483
+ }
484
+ }, [isOpen, setIsOpen]);
485
+ const handleTriggerClick = () => {
486
+ if (!disabled) {
487
+ setIsOpen(!isOpen);
488
+ }
489
+ };
490
+ const popoverContent = isOpen && typeof window !== "undefined" ? createPortal(
491
+ /* @__PURE__ */ jsx2(
492
+ "div",
493
+ {
494
+ ref: positionerRef,
495
+ "data-popover": true,
496
+ style: {
497
+ position: "fixed",
498
+ top: 0,
499
+ left: 0,
500
+ transform: "translate3d(0, 0, 0)",
501
+ zIndex: 99999,
502
+ visibility: "hidden",
503
+ pointerEvents: "none",
504
+ willChange: "transform"
505
+ },
506
+ className: "z-[99999]",
507
+ children: /* @__PURE__ */ jsx2(
508
+ "div",
509
+ {
510
+ ref: panelRef,
511
+ "data-state": "open",
512
+ role: modal ? "dialog" : void 0,
513
+ "aria-modal": modal || void 0,
514
+ style: {
515
+ transformOrigin: getTransformOrigin(initialPlacement.side, initialPlacement.align)
516
+ },
517
+ className: cn(
518
+ // shadcn-like enter animation
519
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
520
+ className
521
+ ),
522
+ children: /* @__PURE__ */ jsx2(
523
+ "div",
524
+ {
525
+ ...contentProps,
526
+ className: cn(
527
+ resolvedBorderMode ? getPanelBorderRadiusClass(resolvedBorderMode) : "rounded-2xl md:rounded-3xl",
528
+ "border bg-popover text-popover-foreground shadow-md",
529
+ "backdrop-blur-sm bg-popover/95 border-border/60 p-4",
530
+ contentProps?.className,
531
+ contentClassName
532
+ ),
533
+ tabIndex: contentProps?.tabIndex ?? -1,
534
+ children
535
+ }
536
+ )
537
+ }
538
+ )
539
+ }
540
+ ),
541
+ document.body
542
+ ) : null;
543
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
544
+ (() => {
545
+ const triggerProps = trigger.props;
546
+ const childRef = triggerProps.ref;
547
+ return React2.cloneElement(trigger, {
548
+ ...triggerProps,
549
+ ref: mergeRefs(childRef, (node) => {
550
+ triggerRef.current = node;
551
+ }),
552
+ "data-underverse-popover-trigger": triggerSelector,
553
+ onClick: chainEventHandlers(
554
+ (e) => {
555
+ triggerRef.current = e.currentTarget;
556
+ e.preventDefault();
557
+ e.stopPropagation();
558
+ handleTriggerClick();
559
+ },
560
+ triggerProps.onClick
561
+ ),
562
+ onFocus: chainEventHandlers(
563
+ (e) => {
564
+ triggerRef.current = e.currentTarget;
565
+ },
566
+ triggerProps.onFocus
567
+ ),
568
+ "aria-expanded": isOpen,
569
+ "aria-haspopup": triggerProps["aria-haspopup"] ?? "dialog"
570
+ });
571
+ })(),
572
+ popoverContent
573
+ ] });
574
+ };
575
+
576
+ // src/components/DropdownMenu.tsx
577
+ import React3, { useState as useState2 } from "react";
578
+ import { ChevronRight } from "lucide-react";
579
+
580
+ // src/constants/form-control-size.ts
581
+ var formControlSizeStyles = {
582
+ sm: {
583
+ control: "h-8 px-3 text-sm leading-none md:h-7 md:text-xs",
584
+ compactControl: "h-8 px-2.5 text-sm leading-none md:h-7 md:text-xs",
585
+ input: "h-8 px-3 text-sm leading-none md:h-7 md:text-xs",
586
+ label: "text-xs",
587
+ icon: "h-4 w-4 md:h-3.5 md:w-3.5",
588
+ iconButton: "h-7 w-7 md:h-6 md:w-6",
589
+ tag: "h-5 max-w-24 px-2 text-[10px] leading-none"
590
+ },
591
+ md: {
592
+ control: "h-10 px-3 text-sm leading-none",
593
+ compactControl: "h-10 px-3 text-sm leading-none",
594
+ input: "h-10 px-4 text-sm leading-none",
595
+ label: "text-sm",
596
+ icon: "h-4 w-4",
597
+ iconButton: "h-8 w-8",
598
+ tag: "h-6 max-w-28 px-2 text-xs leading-none"
599
+ },
600
+ lg: {
601
+ control: "h-12 px-4 text-base leading-none",
602
+ compactControl: "h-12 px-4 text-base leading-none",
603
+ input: "h-12 px-5 text-base leading-none",
604
+ label: "text-base",
605
+ icon: "h-5 w-5",
606
+ iconButton: "h-10 w-10",
607
+ tag: "h-7 max-w-32 px-2.5 text-sm leading-none"
608
+ }
609
+ };
610
+ var formControlFixedClass = "min-h-0 overflow-hidden";
611
+ var formControlValueClass = "max-h-full min-w-0 flex-1 truncate whitespace-nowrap";
612
+
613
+ // src/components/DropdownMenu.tsx
614
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
615
+ var DropdownMenuContext = React3.createContext(null);
616
+ function useDropdownMenuClose() {
617
+ return React3.useContext(DropdownMenuContext)?.closeMenu ?? (() => {
618
+ });
619
+ }
620
+ function useResettingIndex(resetToken) {
621
+ const [state, setState] = React3.useState({ resetToken, index: -1 });
622
+ const activeIndex = Object.is(state.resetToken, resetToken) ? state.index : -1;
623
+ const setActiveIndex = React3.useCallback((nextIndex) => {
624
+ setState((prev) => {
625
+ const prevIndex = Object.is(prev.resetToken, resetToken) ? prev.index : -1;
626
+ return {
627
+ resetToken,
628
+ index: typeof nextIndex === "function" ? nextIndex(prevIndex) : nextIndex
629
+ };
630
+ });
631
+ }, [resetToken]);
632
+ return [activeIndex, setActiveIndex];
633
+ }
634
+ var DropdownMenu = ({
635
+ trigger,
636
+ children,
637
+ className,
638
+ contentClassName,
639
+ placement = "bottom-start",
640
+ closeOnSelect = true,
641
+ disabled = false,
642
+ isOpen,
643
+ onOpenChange,
644
+ openOnHover = false,
645
+ hoverCloseDelay = 120,
646
+ items,
647
+ borderMode
648
+ }) => {
649
+ const [internalOpen, setInternalOpen] = useState2(false);
650
+ const open = isOpen !== void 0 ? isOpen : internalOpen;
651
+ const setOpen = React3.useCallback(
652
+ (nextOpen) => {
653
+ if (isOpen === void 0) {
654
+ setInternalOpen(nextOpen);
655
+ }
656
+ onOpenChange?.(nextOpen);
657
+ },
658
+ [isOpen, onOpenChange]
659
+ );
660
+ const triggerRef = React3.useRef(null);
661
+ const menuRef = React3.useRef(null);
662
+ const itemsRef = React3.useRef([]);
663
+ const [activeIndex, setActiveIndex] = useResettingIndex(open);
664
+ const parentMenu = React3.useContext(DropdownMenuContext);
665
+ const hoverCloseTimeoutRef = React3.useRef(null);
666
+ const cancelHoverClose = React3.useCallback(() => {
667
+ if (hoverCloseTimeoutRef.current === null) return;
668
+ clearTimeout(hoverCloseTimeoutRef.current);
669
+ hoverCloseTimeoutRef.current = null;
670
+ }, []);
671
+ const scheduleHoverClose = React3.useCallback(() => {
672
+ if (!openOnHover) return;
673
+ cancelHoverClose();
674
+ hoverCloseTimeoutRef.current = setTimeout(() => {
675
+ hoverCloseTimeoutRef.current = null;
676
+ setOpen(false);
677
+ }, hoverCloseDelay);
678
+ }, [cancelHoverClose, hoverCloseDelay, openOnHover, setOpen]);
679
+ React3.useEffect(() => () => cancelHoverClose(), [cancelHoverClose]);
680
+ const closeMenu = React3.useCallback(() => {
681
+ cancelHoverClose();
682
+ setOpen(false);
683
+ parentMenu?.closeMenu();
684
+ }, [cancelHoverClose, parentMenu, setOpen]);
685
+ const getEnabledMenuItems = React3.useCallback(() => {
686
+ const menuEl = menuRef.current;
687
+ if (!menuEl) return [];
688
+ return Array.from(menuEl.querySelectorAll("[data-dropdown-menu-item]")).filter((el) => !el.disabled);
689
+ }, []);
690
+ const focusMenuItem = React3.useCallback((index) => {
691
+ const enabled = getEnabledMenuItems();
692
+ const item = enabled[index];
693
+ if (!item) return;
694
+ setActiveIndex(index);
695
+ item.focus();
696
+ item.scrollIntoView({ block: "nearest" });
697
+ }, [getEnabledMenuItems, setActiveIndex]);
698
+ useShadCNAnimations();
699
+ const globalConfig = useUnderverseUIConfig();
700
+ const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
701
+ React3.useEffect(() => {
702
+ if (!open) return;
703
+ const handleKeyNav = (e) => {
704
+ const active = document.activeElement;
705
+ const triggerEl = triggerRef.current;
706
+ const menuEl = menuRef.current;
707
+ if (!active || !triggerEl || !menuEl) return;
708
+ const isInMenu = menuEl.contains(active);
709
+ const isOnTrigger = triggerEl.contains(active);
710
+ const enabled = getEnabledMenuItems();
711
+ if (enabled.length === 0) return;
712
+ const currentIndex = enabled.findIndex((el) => el === active);
713
+ const baseIndex = currentIndex >= 0 ? currentIndex : activeIndex;
714
+ if (e.key === "ArrowDown") {
715
+ e.preventDefault();
716
+ const next = (baseIndex + 1 + enabled.length) % enabled.length;
717
+ focusMenuItem(next);
718
+ } else if (e.key === "ArrowUp") {
719
+ e.preventDefault();
720
+ const prev = (baseIndex - 1 + enabled.length) % enabled.length;
721
+ focusMenuItem(prev);
722
+ } else if (e.key === "Home") {
723
+ e.preventDefault();
724
+ focusMenuItem(0);
725
+ } else if (e.key === "End") {
726
+ e.preventDefault();
727
+ focusMenuItem(enabled.length - 1);
728
+ } else if (e.key === "Escape" && (isInMenu || isOnTrigger)) {
729
+ e.preventDefault();
730
+ closeMenu();
731
+ }
732
+ };
733
+ document.addEventListener("keydown", handleKeyNav, true);
734
+ return () => {
735
+ document.removeEventListener("keydown", handleKeyNav, true);
736
+ };
737
+ }, [open, activeIndex, closeMenu, focusMenuItem, getEnabledMenuItems]);
738
+ const menuContext = React3.useMemo(
739
+ () => ({
740
+ closeMenu,
741
+ closeOnSelect,
742
+ cancelHoverClose,
743
+ scheduleHoverClose
744
+ }),
745
+ [cancelHoverClose, closeMenu, closeOnSelect, scheduleHoverClose]
746
+ );
747
+ const handleItemClick = (itemOnClick) => {
748
+ itemOnClick();
749
+ if (closeOnSelect) {
750
+ closeMenu();
751
+ }
752
+ };
753
+ const menuBody = /* @__PURE__ */ jsx3(DropdownMenuContext.Provider, { value: menuContext, children: /* @__PURE__ */ jsx3(
754
+ "div",
755
+ {
756
+ ref: menuRef,
757
+ "data-dropdown-menu": true,
758
+ "data-state": open ? "open" : "closed",
759
+ role: "menu",
760
+ className: cn("min-w-40", className),
761
+ onMouseEnter: openOnHover ? () => {
762
+ cancelHoverClose();
763
+ parentMenu?.cancelHoverClose();
764
+ } : void 0,
765
+ onMouseLeave: openOnHover ? () => {
766
+ scheduleHoverClose();
767
+ parentMenu?.scheduleHoverClose();
768
+ } : void 0,
769
+ children: items ? items.map((item, index) => {
770
+ const IconComponent = item.icon;
771
+ return /* @__PURE__ */ jsxs2(
772
+ "button",
773
+ {
774
+ ref: (el) => {
775
+ if (el) itemsRef.current[index] = el;
776
+ },
777
+ onClick: () => handleItemClick(item.onClick),
778
+ disabled: item.disabled,
779
+ role: "menuitem",
780
+ "data-dropdown-menu-item": "",
781
+ tabIndex: -1,
782
+ style: {
783
+ animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
784
+ },
785
+ className: cn(
786
+ "dropdown-item flex w-full items-center gap-2 px-2.5 py-1.5 text-sm",
787
+ resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
788
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
789
+ "hover:bg-accent hover:text-accent-foreground",
790
+ "focus:bg-accent focus:text-accent-foreground",
791
+ "disabled:opacity-50 disabled:cursor-not-allowed",
792
+ item.destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10"
793
+ ),
794
+ children: [
795
+ IconComponent && /* @__PURE__ */ jsx3(IconComponent, { className: "h-4 w-4" }),
796
+ item.label
797
+ ]
798
+ },
799
+ index
800
+ );
801
+ }) : children
802
+ }
803
+ ) });
804
+ const triggerProps = trigger.props;
805
+ const {
806
+ ref: childRef,
807
+ onKeyDown: triggerOnKeyDown,
808
+ onClick: triggerOnClick,
809
+ onMouseEnter: triggerOnMouseEnter,
810
+ onMouseLeave: triggerOnMouseLeave
811
+ } = triggerProps;
812
+ const setTriggerNode = React3.useCallback((node) => {
813
+ setRefValue(childRef, node);
814
+ triggerRef.current = node;
815
+ }, [childRef]);
816
+ const handleTriggerKeyDown = React3.useCallback((event) => {
817
+ if (!disabled) {
818
+ if (event.key === "ArrowDown") {
819
+ event.preventDefault();
820
+ setOpen(true);
821
+ requestAnimationFrame(() => focusMenuItem(0));
822
+ } else if (event.key === "ArrowUp") {
823
+ event.preventDefault();
824
+ setOpen(true);
825
+ requestAnimationFrame(() => {
826
+ const enabled = getEnabledMenuItems();
827
+ focusMenuItem(enabled.length - 1);
828
+ });
829
+ } else if (event.key === "Escape") {
830
+ event.preventDefault();
831
+ setOpen(false);
832
+ }
833
+ }
834
+ triggerOnKeyDown?.(event);
835
+ }, [disabled, focusMenuItem, getEnabledMenuItems, setOpen, triggerOnKeyDown]);
836
+ const handleTriggerClick = React3.useCallback((event) => {
837
+ if (openOnHover && !disabled) {
838
+ cancelHoverClose();
839
+ setOpen(true);
840
+ }
841
+ triggerOnClick?.(event);
842
+ }, [cancelHoverClose, disabled, openOnHover, setOpen, triggerOnClick]);
843
+ const handleTriggerMouseEnter = React3.useCallback((event) => {
844
+ if (openOnHover && !disabled) {
845
+ cancelHoverClose();
846
+ parentMenu?.cancelHoverClose();
847
+ setOpen(true);
848
+ }
849
+ triggerOnMouseEnter?.(event);
850
+ }, [cancelHoverClose, disabled, openOnHover, parentMenu, setOpen, triggerOnMouseEnter]);
851
+ const handleTriggerMouseLeave = React3.useCallback((event) => {
852
+ scheduleHoverClose();
853
+ triggerOnMouseLeave?.(event);
854
+ }, [scheduleHoverClose, triggerOnMouseLeave]);
855
+ const enhancedTrigger = React3.cloneElement(trigger, {
856
+ ...triggerProps,
857
+ ref: setTriggerNode,
858
+ "aria-haspopup": "menu",
859
+ "aria-expanded": open,
860
+ onKeyDown: handleTriggerKeyDown,
861
+ onClick: handleTriggerClick,
862
+ onMouseEnter: handleTriggerMouseEnter,
863
+ onMouseLeave: handleTriggerMouseLeave
864
+ });
865
+ return /* @__PURE__ */ jsx3(
866
+ Popover,
867
+ {
868
+ open,
869
+ onOpenChange: setOpen,
870
+ trigger: enhancedTrigger,
871
+ placement,
872
+ disabled,
873
+ borderMode: resolvedBorderMode,
874
+ contentClassName: cn("p-1", contentClassName),
875
+ children: menuBody
876
+ }
877
+ );
878
+ };
879
+ var DropdownMenuItem = ({
880
+ children,
881
+ label,
882
+ description,
883
+ icon: Icon,
884
+ onClick,
885
+ disabled,
886
+ destructive,
887
+ active,
888
+ shortcut,
889
+ className,
890
+ closeOnSelect,
891
+ borderMode
892
+ }) => {
893
+ const menu = React3.useContext(DropdownMenuContext);
894
+ const shouldCloseOnSelect = closeOnSelect ?? menu?.closeOnSelect ?? false;
895
+ const globalConfig = useUnderverseUIConfig();
896
+ const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
897
+ return /* @__PURE__ */ jsxs2(
898
+ "button",
899
+ {
900
+ onClick: () => {
901
+ onClick?.();
902
+ if (shouldCloseOnSelect) {
903
+ menu?.closeMenu();
904
+ }
905
+ },
906
+ disabled,
907
+ onMouseDown: (e) => e.preventDefault(),
908
+ "data-dropdown-menu-item": "",
909
+ tabIndex: -1,
910
+ className: cn(
911
+ "flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors group cursor-pointer",
912
+ resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
913
+ "hover:bg-accent hover:text-accent-foreground",
914
+ "focus:bg-accent focus:text-accent-foreground focus:outline-none",
915
+ "disabled:opacity-50 disabled:cursor-not-allowed",
916
+ destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
917
+ active && "bg-primary/10 text-primary",
918
+ className
919
+ ),
920
+ children: [
921
+ Icon && /* @__PURE__ */ jsx3(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
922
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 text-left", children: [
923
+ label && /* @__PURE__ */ jsx3("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
924
+ description && /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: description }),
925
+ children
926
+ ] }),
927
+ shortcut && /* @__PURE__ */ jsx3("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
928
+ active && /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx3("polyline", { points: "20 6 9 17 4 12" }) })
929
+ ]
930
+ }
931
+ );
932
+ };
933
+ var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ jsx3("div", { className: cn("h-px bg-border my-1", className) });
934
+ var DropdownMenuSub = ({ label, icon: Icon, disabled, borderMode, children }) => {
935
+ const globalConfig = useUnderverseUIConfig();
936
+ const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
937
+ return /* @__PURE__ */ jsx3(
938
+ DropdownMenu,
939
+ {
940
+ trigger: /* @__PURE__ */ jsxs2(
941
+ "button",
942
+ {
943
+ type: "button",
944
+ disabled,
945
+ onMouseDown: (e) => e.preventDefault(),
946
+ className: cn(
947
+ "flex w-full items-center gap-2 px-3 py-2 text-sm transition-colors cursor-pointer",
948
+ resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-lg",
949
+ "hover:bg-accent hover:text-accent-foreground",
950
+ "focus:bg-accent focus:text-accent-foreground focus:outline-none",
951
+ "disabled:opacity-50 disabled:cursor-not-allowed"
952
+ ),
953
+ children: [
954
+ Icon && /* @__PURE__ */ jsx3(Icon, { className: "w-4 h-4 shrink-0 opacity-60" }),
955
+ /* @__PURE__ */ jsx3("span", { className: "flex-1 text-left", children: label }),
956
+ /* @__PURE__ */ jsx3(ChevronRight, { className: "w-3 h-3 opacity-50" })
957
+ ]
958
+ }
959
+ ),
960
+ placement: "right",
961
+ openOnHover: true,
962
+ children
963
+ }
964
+ );
965
+ };
966
+ var SelectDropdown = ({ options, value, onChange, placeholder = "Select...", className, borderMode, size = "md" }) => {
967
+ const globalConfig = useUnderverseUIConfig();
968
+ const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
969
+ return /* @__PURE__ */ jsx3(
970
+ DropdownMenu,
971
+ {
972
+ trigger: /* @__PURE__ */ jsxs2(
973
+ "button",
974
+ {
975
+ className: cn(
976
+ "inline-flex items-center justify-between gap-2 border bg-background border-border/60",
977
+ resolvedBorderMode ? getBorderRadiusClass(resolvedBorderMode) : "rounded-2xl",
978
+ formControlFixedClass,
979
+ formControlSizeStyles[size].control,
980
+ "hover:bg-accent/50",
981
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
982
+ className
983
+ ),
984
+ children: [
985
+ /* @__PURE__ */ jsx3("span", { className: cn(formControlValueClass, "max-w-64 text-foreground/90"), children: value || placeholder }),
986
+ /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", className: "shrink-0 opacity-70", children: /* @__PURE__ */ jsx3("path", { d: "M6 8l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
987
+ ]
988
+ }
989
+ ),
990
+ items: options.map((option) => ({
991
+ label: option,
992
+ onClick: () => onChange(option)
993
+ })),
994
+ borderMode: resolvedBorderMode
995
+ }
996
+ );
997
+ };
998
+ var DropdownMenu_default = DropdownMenu;
999
+
1000
+ // src/components/UEditor/url-safety.ts
1001
+ var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
1002
+ var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
1003
+ var FILE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "blob:"]);
1004
+ function normalizeUrlInput(raw) {
1005
+ return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
1006
+ }
1007
+ function isProtocolRelativeUrl(value) {
1008
+ return value.startsWith("//");
1009
+ }
1010
+ function isRelativeUrl(value) {
1011
+ return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
1012
+ }
1013
+ function isValidIpv4Hostname(hostname) {
1014
+ const parts = hostname.split(".");
1015
+ return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
1016
+ }
1017
+ function isValidWebHostname(hostname) {
1018
+ const normalized = hostname.toLowerCase();
1019
+ if (normalized === "localhost" || isValidIpv4Hostname(normalized)) return true;
1020
+ if (normalized.startsWith("[") && normalized.endsWith("]") && normalized.includes(":")) return true;
1021
+ const labels = normalized.split(".");
1022
+ if (labels.length < 2) return false;
1023
+ const validLabel = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$/;
1024
+ return labels.every((label) => validLabel.test(label)) && /[a-z]/.test(labels.at(-1) ?? "");
1025
+ }
1026
+ function isValidLinkUrl(parsed) {
1027
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
1028
+ return isValidWebHostname(parsed.hostname);
1029
+ }
1030
+ if (parsed.protocol === "mailto:") {
1031
+ return /^[^@]+@[^@]+\.[^@]+$/.test(decodeURIComponent(parsed.pathname));
1032
+ }
1033
+ if (parsed.protocol === "tel:") {
1034
+ const number = decodeURIComponent(parsed.pathname);
1035
+ return /^\+?[\d().-]+$/.test(number) && (number.match(/\d/g)?.length ?? 0) >= 3;
1036
+ }
1037
+ return false;
1038
+ }
1039
+ function isDataImageUrl(value) {
1040
+ return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
1041
+ }
1042
+ function isDataFileUrl(value) {
1043
+ return /^data:[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*(?:;[a-z0-9!#$&^_.+-]+=[^;,]*)*;base64,[a-z0-9+/]*={0,2}$/i.test(value);
1044
+ }
1045
+ function isSafeUEditorUrl(raw, kind) {
1046
+ const value = normalizeUrlInput(raw);
1047
+ if (!value) return false;
1048
+ if (kind === "image" && isDataImageUrl(value)) return true;
1049
+ if (kind === "file" && isDataFileUrl(value)) return true;
1050
+ if (isProtocolRelativeUrl(value)) return false;
1051
+ if (isRelativeUrl(value)) return true;
1052
+ try {
1053
+ const parsed = new URL(value);
1054
+ if (kind === "image") return IMAGE_PROTOCOLS.has(parsed.protocol);
1055
+ if (kind === "file") return FILE_PROTOCOLS.has(parsed.protocol);
1056
+ return LINK_PROTOCOLS.has(parsed.protocol) && isValidLinkUrl(parsed);
1057
+ } catch {
1058
+ return false;
1059
+ }
1060
+ }
1061
+ function sanitizeUEditorUrl(raw, kind) {
1062
+ const value = raw.trim();
1063
+ if (!value) return "";
1064
+ if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
1065
+ if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
1066
+ const withProtocol = `https://${value}`;
1067
+ return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
1068
+ }
1069
+ return "";
1070
+ }
1071
+
1072
+ // src/components/UEditor/clipboard-images.ts
1073
+ import { Extension } from "@tiptap/core";
1074
+ import { Plugin } from "@tiptap/pm/state";
1075
+
1076
+ // src/components/UEditor/clipboard-tables.ts
1077
+ var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
1078
+ var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
1079
+ function getClipboardData(dataTransfer, type) {
1080
+ try {
1081
+ return dataTransfer.getData(type) ?? "";
1082
+ } catch {
1083
+ return "";
1084
+ }
1085
+ }
1086
+ function extractClipboardHtmlFragment(html) {
1087
+ const startMarker = "<!--StartFragment-->";
1088
+ const endMarker = "<!--EndFragment-->";
1089
+ const start = html.indexOf(startMarker);
1090
+ const end = html.indexOf(endMarker);
1091
+ if (start >= 0 && end > start) {
1092
+ return html.slice(start + startMarker.length, end);
1093
+ }
1094
+ return html;
1095
+ }
1096
+ function normalizeClipboardCellText(value) {
1097
+ return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " ").replace(/[ \t]+\n/g, "\n").replace(/\n[ \t]+/g, "\n").replace(/\n+$/g, "").replace(/^\n+/g, "").trim();
1098
+ }
1099
+ function parseStyleDeclarations(styleText) {
1100
+ const declarations = /* @__PURE__ */ new Map();
1101
+ if (!styleText) return declarations;
1102
+ for (const declaration of styleText.split(";")) {
1103
+ const separatorIndex = declaration.indexOf(":");
1104
+ if (separatorIndex <= 0) continue;
1105
+ const property = declaration.slice(0, separatorIndex).trim().toLowerCase();
1106
+ const value = cleanStyleValue(declaration.slice(separatorIndex + 1));
1107
+ if (!property || !value) continue;
1108
+ declarations.set(property, value);
1109
+ }
1110
+ return declarations;
1111
+ }
1112
+ function mergeStyleDeclarations(...sources) {
1113
+ const declarations = /* @__PURE__ */ new Map();
1114
+ for (const source of sources) {
1115
+ if (!source) continue;
1116
+ for (const [property, value] of source.entries()) {
1117
+ declarations.set(property, value);
1118
+ }
1119
+ }
1120
+ return declarations;
1121
+ }
1122
+ function extractCssClassNames(selectorText) {
1123
+ const classNames = /* @__PURE__ */ new Set();
1124
+ const classNamePattern = /\.([_a-zA-Z-][\w-]*)/g;
1125
+ let match;
1126
+ while ((match = classNamePattern.exec(selectorText)) !== null) {
1127
+ classNames.add(match[1]);
1128
+ }
1129
+ return classNames;
1130
+ }
1131
+ function parseClipboardCssClassStyles(doc) {
1132
+ const styleMap = /* @__PURE__ */ new Map();
1133
+ for (const styleElement of Array.from(doc.querySelectorAll("style"))) {
1134
+ const cssText = (styleElement.textContent ?? "").replace(/<!--|-->/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
1135
+ const rulePattern = /([^{}]+)\{([^{}]*)\}/g;
1136
+ let match;
1137
+ while ((match = rulePattern.exec(cssText)) !== null) {
1138
+ const classNames = extractCssClassNames(match[1]);
1139
+ if (classNames.size === 0) continue;
1140
+ const declarations = parseStyleDeclarations(match[2]);
1141
+ if (declarations.size === 0) continue;
1142
+ for (const className of classNames) {
1143
+ styleMap.set(className, mergeStyleDeclarations(styleMap.get(className), declarations));
1144
+ }
1145
+ }
1146
+ }
1147
+ return styleMap;
1148
+ }
1149
+ function getElementStyleDeclarations(element, styleMap) {
1150
+ const classDeclarations = Array.from(element.classList).map((className) => styleMap.get(className));
1151
+ const inlineDeclarations = parseStyleDeclarations(element.getAttribute("style"));
1152
+ return mergeStyleDeclarations(...classDeclarations, inlineDeclarations);
1153
+ }
1154
+ function cleanStyleValue(value) {
1155
+ const normalized = value?.trim();
1156
+ if (!normalized) return null;
1157
+ if (/[\0<>;{}]/.test(normalized)) return null;
1158
+ if (/\b(?:expression|url|(?:repeating-)?(?:linear|radial|conic)-gradient)\s*\(/i.test(normalized)) return null;
1159
+ return normalized;
1160
+ }
1161
+ function normalizeColorValue(value) {
1162
+ const normalized = cleanStyleValue(value);
1163
+ if (!normalized) return null;
1164
+ if (/^(?:auto|inherit|initial|none|transparent|unset)$/i.test(normalized)) return null;
1165
+ return normalized;
1166
+ }
1167
+ function normalizeTextColorValue(value) {
1168
+ const normalized = normalizeColorValue(value);
1169
+ if (!normalized) return null;
1170
+ if (/^(?:automatic|windowtext|black|#000|#000000|rgb\(\s*0\s*,\s*0\s*,\s*0\s*\))$/i.test(normalized)) {
1171
+ return DEFAULT_HTML_TABLE_TEXT_COLOR;
1172
+ }
1173
+ return normalized;
1174
+ }
1175
+ function isWhiteColor(value) {
1176
+ if (!value) return false;
1177
+ return /^(?:white|#fff|#ffffff|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))$/i.test(value.trim());
1178
+ }
1179
+ function parseCssColorRgb(value) {
1180
+ const normalized = normalizeColorValue(value);
1181
+ if (!normalized) return null;
1182
+ const lowerColor = normalized.toLowerCase();
1183
+ if (lowerColor === "white") return { r: 255, g: 255, b: 255 };
1184
+ if (lowerColor === "black") return { r: 0, g: 0, b: 0 };
1185
+ const hexMatch = lowerColor.match(/^#([\da-f]{3}|[\da-f]{6})$/i);
1186
+ if (hexMatch) {
1187
+ const hex = hexMatch[1];
1188
+ const fullHex = hex.length === 3 ? hex.split("").map((part) => part + part).join("") : hex;
1189
+ return {
1190
+ r: Number.parseInt(fullHex.slice(0, 2), 16),
1191
+ g: Number.parseInt(fullHex.slice(2, 4), 16),
1192
+ b: Number.parseInt(fullHex.slice(4, 6), 16)
1193
+ };
1194
+ }
1195
+ const rgbMatch = lowerColor.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
1196
+ if (rgbMatch) {
1197
+ return {
1198
+ r: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[1]))),
1199
+ g: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[2]))),
1200
+ b: Math.max(0, Math.min(255, Number.parseFloat(rgbMatch[3])))
1201
+ };
1202
+ }
1203
+ return null;
1204
+ }
1205
+ function getRelativeLuminance(value) {
1206
+ const rgb = parseCssColorRgb(value);
1207
+ if (!rgb) return null;
1208
+ const toLinear = (channel) => {
1209
+ const normalized = channel / 255;
1210
+ return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
1211
+ };
1212
+ return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
1213
+ }
1214
+ function isLightTextColor(value) {
1215
+ const luminance = getRelativeLuminance(value);
1216
+ return luminance !== null && luminance >= 0.72;
1217
+ }
1218
+ function isDarkReadableBackground(value) {
1219
+ const luminance = getRelativeLuminance(value);
1220
+ return luminance !== null && luminance <= 0.45;
1221
+ }
1222
+ function splitCssTokens(value) {
1223
+ const tokens = [];
1224
+ let current = "";
1225
+ let depth = 0;
1226
+ for (const char of value) {
1227
+ if (char === "(") depth += 1;
1228
+ if (char === ")") depth = Math.max(0, depth - 1);
1229
+ if (/\s/.test(char) && depth === 0) {
1230
+ if (current) {
1231
+ tokens.push(current);
1232
+ current = "";
1233
+ }
1234
+ continue;
1235
+ }
1236
+ current += char;
1237
+ }
1238
+ if (current) tokens.push(current);
1239
+ return tokens;
1240
+ }
1241
+ function extractColorFromShorthand(value) {
1242
+ const normalized = cleanStyleValue(value);
1243
+ if (!normalized) return null;
1244
+ const explicitColor = normalized.match(/#[\da-f]{3,8}\b|rgba?\([^)]+\)|hsla?\([^)]+\)/i);
1245
+ if (explicitColor) return explicitColor[0];
1246
+ const ignoredKeywords = /* @__PURE__ */ new Set([
1247
+ "border-box",
1248
+ "center",
1249
+ "contain",
1250
+ "content-box",
1251
+ "cover",
1252
+ "fixed",
1253
+ "inherit",
1254
+ "initial",
1255
+ "left",
1256
+ "local",
1257
+ "none",
1258
+ "no-repeat",
1259
+ "padding-box",
1260
+ "repeat",
1261
+ "repeat-x",
1262
+ "repeat-y",
1263
+ "right",
1264
+ "scroll",
1265
+ "top",
1266
+ "transparent",
1267
+ "unset"
1268
+ ]);
1269
+ return splitCssTokens(normalized).find((token) => !ignoredKeywords.has(token.toLowerCase())) ?? null;
1270
+ }
1271
+ function getBackgroundColor(styles) {
1272
+ return normalizeColorValue(styles.get("background-color")) ?? normalizeColorValue(extractColorFromShorthand(styles.get("background")));
1273
+ }
1274
+ var BORDER_STYLES = /* @__PURE__ */ new Set([
1275
+ "dashed",
1276
+ "dotted",
1277
+ "double",
1278
+ "groove",
1279
+ "hidden",
1280
+ "inset",
1281
+ "none",
1282
+ "outset",
1283
+ "ridge",
1284
+ "solid"
1285
+ ]);
1286
+ var BORDER_WIDTH_KEYWORDS = /* @__PURE__ */ new Set(["medium", "thick", "thin"]);
1287
+ function normalizeBorderStyle(value) {
1288
+ const normalized = cleanStyleValue(value);
1289
+ if (!normalized) return null;
1290
+ const styles = splitCssTokens(normalized).filter((token) => BORDER_STYLES.has(token.toLowerCase()));
1291
+ const usefulStyles = styles.filter((style) => !/^(?:hidden|none)$/i.test(style));
1292
+ return usefulStyles.length > 0 ? usefulStyles.join(" ") : null;
1293
+ }
1294
+ function normalizeBorderWidth(value) {
1295
+ const normalized = cleanStyleValue(value);
1296
+ if (!normalized) return null;
1297
+ const widths = splitCssTokens(normalized).filter((token) => {
1298
+ const lowerToken = token.toLowerCase();
1299
+ return BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token);
1300
+ });
1301
+ return widths.length > 0 ? widths.join(" ") : null;
1302
+ }
1303
+ function parseBorderShorthand(value) {
1304
+ const normalized = cleanStyleValue(value);
1305
+ if (!normalized) return null;
1306
+ const tokens = splitCssTokens(normalized);
1307
+ let borderStyle = null;
1308
+ let borderWidth = null;
1309
+ const colorTokens = [];
1310
+ for (const token of tokens) {
1311
+ const lowerToken = token.toLowerCase();
1312
+ if (!borderStyle && BORDER_STYLES.has(lowerToken)) {
1313
+ borderStyle = lowerToken;
1314
+ continue;
1315
+ }
1316
+ if (!borderWidth && (BORDER_WIDTH_KEYWORDS.has(lowerToken) || /^\d*\.?\d+(?:px|pt|pc|in|cm|mm|em|rem)?$/i.test(token))) {
1317
+ borderWidth = token;
1318
+ continue;
1319
+ }
1320
+ colorTokens.push(token);
1321
+ }
1322
+ if (borderStyle && /^(?:hidden|none)$/i.test(borderStyle)) return null;
1323
+ return {
1324
+ borderColor: normalizeColorValue(colorTokens.join(" ")),
1325
+ borderStyle,
1326
+ borderWidth
1327
+ };
1328
+ }
1329
+ function getFirstParsedBorder(styles) {
1330
+ for (const property of ["border", "border-top", "border-right", "border-bottom", "border-left"]) {
1331
+ const border = parseBorderShorthand(styles.get(property));
1332
+ if (border) return border;
1333
+ }
1334
+ return null;
1335
+ }
1336
+ function getBorderAttrs(styles) {
1337
+ const parsedBorder = getFirstParsedBorder(styles);
1338
+ return {
1339
+ borderColor: normalizeColorValue(styles.get("border-color")) ?? parsedBorder?.borderColor ?? void 0,
1340
+ borderStyle: normalizeBorderStyle(styles.get("border-style")) ?? parsedBorder?.borderStyle ?? void 0,
1341
+ borderWidth: normalizeBorderWidth(styles.get("border-width")) ?? parsedBorder?.borderWidth ?? void 0
1342
+ };
1343
+ }
1344
+ function parsePositiveInteger(value, max = 100) {
1345
+ if (!value) return null;
1346
+ const parsed = Number.parseInt(value, 10);
1347
+ if (!Number.isFinite(parsed) || parsed < 1) return null;
1348
+ return Math.min(parsed, max);
1349
+ }
1350
+ function parseCssSize(value) {
1351
+ const normalized = cleanStyleValue(value);
1352
+ if (!normalized) return null;
1353
+ const match = normalized.match(/^(\d+(?:\.\d+)?)(px|pt)?$/i);
1354
+ if (!match) return null;
1355
+ const amount = Number.parseFloat(match[1]);
1356
+ if (!Number.isFinite(amount) || amount <= 0) return null;
1357
+ return Math.round(match[2]?.toLowerCase() === "pt" ? amount * (4 / 3) : amount);
1358
+ }
1359
+ function getCellWidth(cell, styles, colspan) {
1360
+ if (colspan !== 1) return null;
1361
+ const width = parseCssSize(cell.getAttribute("data-colwidth") ?? cell.getAttribute("width") ?? styles.get("width"));
1362
+ return width ? [width] : null;
1363
+ }
1364
+ function getTableRowAttrs(row, styles) {
1365
+ const rowHeight = parseCssSize(
1366
+ row.getAttribute("data-row-height") ?? row.getAttribute("height") ?? styles.get("height")
1367
+ );
1368
+ return rowHeight ? { rowHeight } : void 0;
1369
+ }
1370
+ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
1371
+ const colspan = parsePositiveInteger(cell.getAttribute("colspan")) ?? 1;
1372
+ const rowspan = parsePositiveInteger(cell.getAttribute("rowspan")) ?? 1;
1373
+ const backgroundColor = getBackgroundColor(styles) ?? normalizeColorValue(cell.getAttribute("data-background-color")) ?? normalizeColorValue(cell.getAttribute("bgcolor")) ?? defaultBackgroundColor;
1374
+ const borderAttrs = getBorderAttrs(styles);
1375
+ const colwidth = getCellWidth(cell, styles, colspan);
1376
+ const attrs = {};
1377
+ if (backgroundColor) attrs.backgroundColor = backgroundColor;
1378
+ if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
1379
+ if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
1380
+ if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
1381
+ if (cell.getAttribute("data-cell-id")) attrs.cellId = cell.getAttribute("data-cell-id") ?? void 0;
1382
+ if (cell.getAttribute("data-number-format")) attrs.numberFormat = cell.getAttribute("data-number-format") ?? void 0;
1383
+ if (cell.getAttribute("data-formula")) attrs.formula = cell.getAttribute("data-formula") ?? void 0;
1384
+ if (cell.getAttribute("data-computed-value")) attrs.computedValue = cell.getAttribute("data-computed-value") ?? void 0;
1385
+ if (colspan > 1) attrs.colspan = colspan;
1386
+ if (rowspan > 1) attrs.rowspan = rowspan;
1387
+ if (colwidth) attrs.colwidth = colwidth;
1388
+ return Object.keys(attrs).length > 0 ? attrs : void 0;
1389
+ }
1390
+ function marksEqual(left, right) {
1391
+ return JSON.stringify(left ?? []) === JSON.stringify(right ?? []);
1392
+ }
1393
+ function mergeMarks(base, additions) {
1394
+ const next = [...base ?? []];
1395
+ for (const addition of additions ?? []) {
1396
+ const existingIndex = next.findIndex((mark) => mark.type === addition.type);
1397
+ if (existingIndex >= 0) {
1398
+ const existingMark = next[existingIndex];
1399
+ next[existingIndex] = {
1400
+ ...existingMark,
1401
+ attrs: {
1402
+ ...existingMark.attrs ?? {},
1403
+ ...addition.attrs ?? {}
1404
+ }
1405
+ };
1406
+ continue;
1407
+ }
1408
+ next.push(addition);
1409
+ }
1410
+ return next.length > 0 ? next : void 0;
1411
+ }
1412
+ function getMarkColor(marks, markType) {
1413
+ const mark = marks?.find((candidate) => candidate.type === markType);
1414
+ const color = mark?.attrs?.color;
1415
+ return typeof color === "string" ? color : null;
1416
+ }
1417
+ function replaceTextStyleColor(marks, color) {
1418
+ let replaced = false;
1419
+ const next = (marks ?? []).map((mark) => {
1420
+ if (mark.type !== "textStyle") return mark;
1421
+ replaced = true;
1422
+ return {
1423
+ ...mark,
1424
+ attrs: {
1425
+ ...mark.attrs ?? {},
1426
+ color
1427
+ }
1428
+ };
1429
+ });
1430
+ if (!replaced) {
1431
+ next.unshift({ type: "textStyle", attrs: { color } });
1432
+ }
1433
+ return next;
1434
+ }
1435
+ function ensureReadableSpreadsheetSegments(segments, cellBackgroundColor) {
1436
+ return segments.map((segment) => {
1437
+ const textColor = getMarkColor(segment.marks, "textStyle");
1438
+ if (!isLightTextColor(textColor)) return segment;
1439
+ const inlineBackgroundColor = getMarkColor(segment.marks, "highlight");
1440
+ if (isDarkReadableBackground(inlineBackgroundColor) || isDarkReadableBackground(cellBackgroundColor)) {
1441
+ return segment;
1442
+ }
1443
+ return {
1444
+ ...segment,
1445
+ marks: replaceTextStyleColor(segment.marks, DEFAULT_HTML_TABLE_TEXT_COLOR)
1446
+ };
1447
+ });
1448
+ }
1449
+ function getElementInlineMarks(element, styles) {
1450
+ const marks = [];
1451
+ const tagName = element.tagName;
1452
+ const color = normalizeTextColorValue(styles.get("color") ?? element.getAttribute("color"));
1453
+ const backgroundColor = getBackgroundColor(styles);
1454
+ const fontWeight = styles.get("font-weight")?.toLowerCase();
1455
+ const fontStyle = styles.get("font-style")?.toLowerCase();
1456
+ const textDecoration = styles.get("text-decoration")?.toLowerCase();
1457
+ if (color) {
1458
+ marks.push({ type: "textStyle", attrs: { color } });
1459
+ }
1460
+ if (backgroundColor && !isWhiteColor(backgroundColor)) {
1461
+ marks.push({ type: "highlight", attrs: { color: backgroundColor } });
1462
+ }
1463
+ if (tagName === "B" || tagName === "STRONG" || fontWeight === "bold" || /^\d+$/.test(fontWeight ?? "") && Number(fontWeight) >= 600) {
1464
+ marks.push({ type: "bold" });
1465
+ }
1466
+ if (tagName === "I" || tagName === "EM" || fontStyle === "italic") {
1467
+ marks.push({ type: "italic" });
1468
+ }
1469
+ if (tagName === "U" || textDecoration?.includes("underline")) {
1470
+ marks.push({ type: "underline" });
1471
+ }
1472
+ return marks.length > 0 ? marks : void 0;
1473
+ }
1474
+ function appendTextSegment(segments, segment) {
1475
+ if (!segment.text) return;
1476
+ const lastSegment = segments[segments.length - 1];
1477
+ if (lastSegment && marksEqual(lastSegment.marks, segment.marks)) {
1478
+ lastSegment.text += segment.text;
1479
+ return;
1480
+ }
1481
+ segments.push(segment);
1482
+ }
1483
+ function segmentsEndWithNewline(segments) {
1484
+ return segments.length > 0 && segments[segments.length - 1].text.endsWith("\n");
1485
+ }
1486
+ function normalizeClipboardTextSegments(segments) {
1487
+ const normalizedSegments = [];
1488
+ for (const segment of segments) {
1489
+ appendTextSegment(normalizedSegments, {
1490
+ text: segment.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " "),
1491
+ marks: segment.marks
1492
+ });
1493
+ }
1494
+ while (normalizedSegments.length > 0) {
1495
+ const firstSegment = normalizedSegments[0];
1496
+ firstSegment.text = firstSegment.text.replace(/^\s+/, "");
1497
+ if (firstSegment.text) break;
1498
+ normalizedSegments.shift();
1499
+ }
1500
+ while (normalizedSegments.length > 0) {
1501
+ const lastSegment = normalizedSegments[normalizedSegments.length - 1];
1502
+ lastSegment.text = lastSegment.text.replace(/\s+$/, "");
1503
+ if (lastSegment.text) break;
1504
+ normalizedSegments.pop();
1505
+ }
1506
+ return normalizedSegments;
1507
+ }
1508
+ function getClipboardCellText(node) {
1509
+ if (node.nodeType === Node.TEXT_NODE) {
1510
+ return node.textContent ?? "";
1511
+ }
1512
+ if (!(node instanceof HTMLElement)) {
1513
+ return "";
1514
+ }
1515
+ if (node.tagName === "BR") {
1516
+ return "\n";
1517
+ }
1518
+ const childText = Array.from(node.childNodes).map(getClipboardCellText).join("");
1519
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && childText && !childText.endsWith("\n")) {
1520
+ return `${childText}
1521
+ `;
1522
+ }
1523
+ return childText;
1524
+ }
1525
+ function getClipboardCellSegments(node, styleMap, inheritedMarks) {
1526
+ if (node.nodeType === Node.TEXT_NODE) {
1527
+ return [{ text: node.textContent ?? "", marks: inheritedMarks }];
1528
+ }
1529
+ if (!(node instanceof HTMLElement)) {
1530
+ return [];
1531
+ }
1532
+ if (node.tagName === "BR") {
1533
+ return [{ text: "\n", marks: inheritedMarks }];
1534
+ }
1535
+ const styles = getElementStyleDeclarations(node, styleMap);
1536
+ const marks = mergeMarks(inheritedMarks, getElementInlineMarks(node, styles));
1537
+ const segments = [];
1538
+ for (const childNode of Array.from(node.childNodes)) {
1539
+ for (const segment of getClipboardCellSegments(childNode, styleMap, marks)) {
1540
+ appendTextSegment(segments, segment);
1541
+ }
1542
+ }
1543
+ if ((node.tagName === "P" || node.tagName === "DIV" || node.tagName === "LI") && segments.length > 0 && !segmentsEndWithNewline(segments)) {
1544
+ appendTextSegment(segments, { text: "\n" });
1545
+ }
1546
+ return segments;
1547
+ }
1548
+ function getClipboardCellChildSegments(cell, styleMap, inheritedMarks) {
1549
+ const segments = [];
1550
+ for (const childNode of Array.from(cell.childNodes)) {
1551
+ for (const segment of getClipboardCellSegments(childNode, styleMap, inheritedMarks)) {
1552
+ appendTextSegment(segments, segment);
1553
+ }
1554
+ }
1555
+ return normalizeClipboardTextSegments(segments);
1556
+ }
1557
+ function getHtmlTableRows(table, styleMap) {
1558
+ const rows = Array.from(table.querySelectorAll("tr")).map(
1559
+ (row) => ({
1560
+ attrs: getTableRowAttrs(row, getElementStyleDeclarations(row, styleMap)),
1561
+ cells: Array.from(row.children).filter((cell) => cell instanceof HTMLTableCellElement).map((cell) => {
1562
+ const styles = getElementStyleDeclarations(cell, styleMap);
1563
+ const textColor = normalizeTextColorValue(styles.get("color")) ?? DEFAULT_HTML_TABLE_TEXT_COLOR;
1564
+ const inheritedMarks = [{ type: "textStyle", attrs: { color: textColor } }];
1565
+ const attrs = getTableCellAttrs(cell, styles, DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR);
1566
+ const segments = ensureReadableSpreadsheetSegments(
1567
+ getClipboardCellChildSegments(cell, styleMap, inheritedMarks),
1568
+ attrs?.backgroundColor
1569
+ );
1570
+ return {
1571
+ text: normalizeClipboardCellText(getClipboardCellText(cell)),
1572
+ isHeader: cell.tagName === "TH",
1573
+ attrs,
1574
+ segments: segments.length > 0 ? segments : void 0,
1575
+ textColor
1576
+ };
1577
+ })
1578
+ })
1579
+ );
1580
+ return rows.filter((row) => row.cells.length > 0);
1581
+ }
1582
+ function createTextMarks(cell) {
1583
+ return cell.textColor ? [{ type: "textStyle", attrs: { color: cell.textColor } }] : void 0;
1584
+ }
1585
+ function createParagraphContent(text, marks) {
1586
+ return text ? {
1587
+ type: "paragraph",
1588
+ content: [{ type: "text", text, ...marks ? { marks } : {} }]
1589
+ } : { type: "paragraph" };
1590
+ }
1591
+ function createParagraphContentFromSegments(segments) {
1592
+ const paragraphs = [[]];
1593
+ for (const segment of segments) {
1594
+ const parts = segment.text.split("\n");
1595
+ parts.forEach((part, index) => {
1596
+ if (part) {
1597
+ paragraphs[paragraphs.length - 1].push({ text: part, marks: segment.marks });
1598
+ }
1599
+ if (index < parts.length - 1) {
1600
+ paragraphs.push([]);
1601
+ }
1602
+ });
1603
+ }
1604
+ return paragraphs.map((paragraphSegments) => {
1605
+ const content = paragraphSegments.map((segment) => ({
1606
+ type: "text",
1607
+ text: segment.text,
1608
+ ...segment.marks ? { marks: segment.marks } : {}
1609
+ }));
1610
+ return content.length > 0 ? { type: "paragraph", content } : { type: "paragraph" };
1611
+ });
1612
+ }
1613
+ function createTableCellContent(cell) {
1614
+ const lines = cell.text.split("\n");
1615
+ const marks = createTextMarks(cell);
1616
+ const paragraphs = cell.segments && cell.segments.length > 0 ? createParagraphContentFromSegments(cell.segments) : (lines.length > 0 ? lines : [""]).map((line) => createParagraphContent(line, marks));
1617
+ return {
1618
+ type: cell.isHeader ? "tableHeader" : "tableCell",
1619
+ ...cell.attrs ? { attrs: cell.attrs } : {},
1620
+ content: paragraphs.length > 0 ? paragraphs : [{ type: "paragraph" }]
1621
+ };
1622
+ }
1623
+ function getRowspanLimitedCell(cell, remainingRowCount) {
1624
+ const attrs = cell.attrs;
1625
+ if (!attrs?.rowspan || attrs.rowspan <= remainingRowCount) return cell;
1626
+ if (remainingRowCount <= 1) {
1627
+ const { rowspan: _rowspan, ...nextAttrs } = attrs;
1628
+ return {
1629
+ ...cell,
1630
+ attrs: Object.keys(nextAttrs).length > 0 ? nextAttrs : void 0
1631
+ };
1632
+ }
1633
+ return {
1634
+ ...cell,
1635
+ attrs: {
1636
+ ...attrs,
1637
+ rowspan: remainingRowCount
1638
+ }
1639
+ };
1640
+ }
1641
+ function normalizeTableRows(rows) {
1642
+ const positionedRows = [];
1643
+ let rowspans = [];
1644
+ let columnCount = 0;
1645
+ rows.forEach((row, rowIndex) => {
1646
+ const coveredColumns = rowspans.map((span) => span > 0);
1647
+ const nextRowspans = rowspans.map((span) => Math.max(0, span - 1));
1648
+ const positionedCells = [];
1649
+ let columnIndex = 0;
1650
+ for (const rawCell of row.cells) {
1651
+ while (coveredColumns[columnIndex]) columnIndex += 1;
1652
+ const remainingRowCount = rows.length - rowIndex;
1653
+ const cell = getRowspanLimitedCell(rawCell, remainingRowCount);
1654
+ const colspan = Math.max(1, cell.attrs?.colspan ?? 1);
1655
+ const rowspan = Math.max(1, cell.attrs?.rowspan ?? 1);
1656
+ positionedCells.push({ startColumn: columnIndex, colspan, cell });
1657
+ if (rowspan > 1) {
1658
+ for (let offset = 0; offset < colspan; offset += 1) {
1659
+ const spannedColumn = columnIndex + offset;
1660
+ nextRowspans[spannedColumn] = Math.max(nextRowspans[spannedColumn] ?? 0, rowspan - 1);
1661
+ }
1662
+ }
1663
+ columnIndex += colspan;
1664
+ }
1665
+ const lastCoveredColumn = coveredColumns.reduce((lastIndex, covered, index) => covered ? index : lastIndex, -1);
1666
+ const lastFutureRowspanColumn = nextRowspans.reduce((lastIndex, span, index) => span > 0 ? index : lastIndex, -1);
1667
+ columnCount = Math.max(columnCount, columnIndex, lastCoveredColumn + 1, lastFutureRowspanColumn + 1);
1668
+ positionedRows.push({
1669
+ attrs: row.attrs,
1670
+ cells: positionedCells,
1671
+ coveredColumns
1672
+ });
1673
+ rowspans = nextRowspans;
1674
+ });
1675
+ return { positionedRows, columnCount };
1676
+ }
1677
+ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
1678
+ const content = [];
1679
+ const cellByStartColumn = new Map(row.cells.map((cell) => [cell.startColumn, cell]));
1680
+ let columnIndex = 0;
1681
+ while (columnIndex < columnCount) {
1682
+ if (row.coveredColumns[columnIndex]) {
1683
+ columnIndex += 1;
1684
+ continue;
1685
+ }
1686
+ const positionedCell = cellByStartColumn.get(columnIndex);
1687
+ if (positionedCell) {
1688
+ content.push(createTableCellContent(positionedCell.cell));
1689
+ columnIndex += positionedCell.colspan;
1690
+ continue;
1691
+ }
1692
+ content.push(createTableCellContent({ text: "", isHeader: false, attrs: fillerCellAttrs }));
1693
+ columnIndex += 1;
1694
+ }
1695
+ return content;
1696
+ }
1697
+ function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
1698
+ const tableRows = rows.filter((row) => row.cells.length > 0);
1699
+ if (tableRows.length === 0) return null;
1700
+ const { positionedRows, columnCount } = normalizeTableRows(tableRows);
1701
+ if (columnCount < minColumnCount) return null;
1702
+ return {
1703
+ type: "table",
1704
+ content: positionedRows.map((row) => ({
1705
+ type: "tableRow",
1706
+ ...row.attrs ? { attrs: row.attrs } : {},
1707
+ content: createNormalizedRowContent(row, columnCount, fillerCellAttrs)
1708
+ }))
1709
+ };
1710
+ }
1711
+ function getClipboardTableContent(dataTransfer) {
1712
+ const html = getClipboardData(dataTransfer, "text/html");
1713
+ if (!/<table(?:\s|>)/i.test(html)) return null;
1714
+ if (typeof DOMParser === "undefined") return null;
1715
+ const doc = new DOMParser().parseFromString(html, "text/html");
1716
+ const fragment = extractClipboardHtmlFragment(html);
1717
+ const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
1718
+ const styleMap = parseClipboardCssClassStyles(doc);
1719
+ const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
1720
+ if (!(table instanceof HTMLTableElement)) return null;
1721
+ return createTableContent(getHtmlTableRows(table, styleMap), 1, {
1722
+ backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
1723
+ });
1724
+ }
1725
+ function parseClipboardTsvRows(text) {
1726
+ const rows = [];
1727
+ let row = [];
1728
+ let field = "";
1729
+ let inQuotes = false;
1730
+ const pushField = () => {
1731
+ row.push(field);
1732
+ field = "";
1733
+ };
1734
+ const pushRow = () => {
1735
+ pushField();
1736
+ rows.push(row);
1737
+ row = [];
1738
+ };
1739
+ for (let index = 0; index < text.length; index += 1) {
1740
+ const char = text[index];
1741
+ const nextChar = text[index + 1];
1742
+ if (inQuotes) {
1743
+ if (char === '"' && nextChar === '"') {
1744
+ field += '"';
1745
+ index += 1;
1746
+ continue;
1747
+ }
1748
+ if (char === '"') {
1749
+ inQuotes = false;
1750
+ continue;
1751
+ }
1752
+ field += char;
1753
+ continue;
1754
+ }
1755
+ if (char === '"' && field.length === 0) {
1756
+ inQuotes = true;
1757
+ continue;
1758
+ }
1759
+ if (char === " ") {
1760
+ pushField();
1761
+ continue;
1762
+ }
1763
+ if (char === "\n") {
1764
+ pushRow();
1765
+ continue;
1766
+ }
1767
+ field += char;
1768
+ }
1769
+ pushRow();
1770
+ while (rows.length > 0 && rows[rows.length - 1].every((cell) => cell === "")) {
1771
+ rows.pop();
1772
+ }
1773
+ return rows;
1774
+ }
1775
+ function getClipboardTsvTableContent(dataTransfer) {
1776
+ const text = getClipboardData(dataTransfer, "text/plain").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1777
+ if (!text.includes(" ")) return null;
1778
+ const rows = parseClipboardTsvRows(text);
1779
+ return createTableContent(
1780
+ rows.map((row) => ({
1781
+ cells: row.map((cell) => ({ text: normalizeClipboardCellText(cell), isHeader: false }))
1782
+ })),
1783
+ 2
1784
+ );
1785
+ }
1786
+
1787
+ // src/components/UEditor/clipboard-images.ts
1788
+ var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
1789
+ var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
1790
+ function getImageFiles(dataTransfer) {
1791
+ if (!dataTransfer) return [];
1792
+ const itemFiles = [];
1793
+ const byKey = /* @__PURE__ */ new Map();
1794
+ for (const item of Array.from(dataTransfer.items ?? [])) {
1795
+ if (item.kind !== "file") continue;
1796
+ if (!item.type.startsWith("image/")) continue;
1797
+ const file = item.getAsFile();
1798
+ if (!file) continue;
1799
+ byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
1800
+ }
1801
+ itemFiles.push(...Array.from(byKey.values()));
1802
+ if (itemFiles.length > 0) return itemFiles;
1803
+ for (const file of Array.from(dataTransfer.files ?? [])) {
1804
+ if (!file.type.startsWith("image/")) continue;
1805
+ byKey.set(`${file.name}:${file.size}:${file.lastModified}`, file);
1806
+ }
1807
+ return Array.from(byKey.values());
1808
+ }
1809
+ function fileToDataUrl(file) {
1810
+ return new Promise((resolve, reject) => {
1811
+ const reader = new FileReader();
1812
+ reader.onload = () => resolve(String(reader.result ?? ""));
1813
+ reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
1814
+ reader.readAsDataURL(file);
1815
+ });
1816
+ }
1817
+ async function resolveImageSrc(file, options) {
1818
+ if (options.insertMode === "upload" && options.upload) {
1819
+ try {
1820
+ const result = await options.upload(file);
1821
+ const src = typeof result === "string" ? sanitizeUEditorUrl(result, "image") : "";
1822
+ if (src) return src;
1823
+ } catch (err) {
1824
+ if (!options.fallbackToDataUrl) throw err;
1825
+ }
1826
+ }
1827
+ return fileToDataUrl(file);
1828
+ }
1829
+ var ClipboardImages = Extension.create({
1830
+ name: "clipboardImages",
1831
+ addOptions() {
1832
+ return {
1833
+ maxFileSize: DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
1834
+ allowedMimeTypes: DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
1835
+ upload: void 0,
1836
+ fallbackToDataUrl: true,
1837
+ insertMode: "base64"
1838
+ };
1839
+ },
1840
+ addProseMirrorPlugins() {
1841
+ const editor = this.editor;
1842
+ const options = this.options;
1843
+ const insertFiles = async (files, selectionPos) => {
1844
+ if (selectionPos !== void 0) {
1845
+ editor.commands.setTextSelection(selectionPos);
1846
+ }
1847
+ for (const file of files) {
1848
+ if (file.size > options.maxFileSize) continue;
1849
+ if (options.allowedMimeTypes.length > 0 && !options.allowedMimeTypes.includes(file.type)) continue;
1850
+ try {
1851
+ const src = await resolveImageSrc(file, options);
1852
+ editor.chain().focus().setImage({ src, alt: file.name }).run();
1853
+ editor.commands.createParagraphNear();
1854
+ } catch {
1855
+ }
1856
+ }
1857
+ };
1858
+ return [
1859
+ new Plugin({
1860
+ props: {
1861
+ handlePaste: (_view, event) => {
1862
+ if (!event || !event.clipboardData) return false;
1863
+ const tableContent = getClipboardTableContent(event.clipboardData);
1864
+ if (tableContent) {
1865
+ event.preventDefault();
1866
+ editor.chain().focus().insertContent(tableContent).run();
1867
+ return true;
1868
+ }
1869
+ const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
1870
+ if (tsvTableContent) {
1871
+ event.preventDefault();
1872
+ editor.chain().focus().insertContent(tsvTableContent).run();
1873
+ return true;
1874
+ }
1875
+ const files = getImageFiles(event.clipboardData);
1876
+ if (files.length === 0) return false;
1877
+ event.preventDefault();
1878
+ void insertFiles(files);
1879
+ return true;
1880
+ },
1881
+ handleDrop: (view, event, _slice, moved) => {
1882
+ if (moved) return false;
1883
+ if (!(event instanceof DragEvent)) return false;
1884
+ const files = getImageFiles(event.dataTransfer);
1885
+ if (files.length === 0) return false;
1886
+ const pos = view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos;
1887
+ event.preventDefault();
1888
+ void insertFiles(files, pos);
1889
+ return true;
1890
+ }
1891
+ }
1892
+ })
1893
+ ];
1894
+ }
1895
+ });
1896
+
1897
+ // src/components/UEditor/inputs.tsx
1898
+ import { useEffect as useEffect3, useId as useId2, useRef as useRef2, useState as useState3 } from "react";
1899
+ import { Check, X } from "lucide-react";
1900
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1901
+ function normalizeUrl(raw) {
1902
+ return sanitizeUEditorUrl(raw, "link");
1903
+ }
1904
+ var LinkInput = ({
1905
+ onSubmit,
1906
+ onCancel,
1907
+ initialUrl = ""
1908
+ }) => {
1909
+ const t = useSmartTranslations("UEditor");
1910
+ const [url, setUrl] = useState3(initialUrl);
1911
+ const [error, setError] = useState3("");
1912
+ const inputRef = useRef2(null);
1913
+ const errorId = useId2();
1914
+ useEffect3(() => {
1915
+ inputRef.current?.focus();
1916
+ inputRef.current?.select();
1917
+ }, []);
1918
+ const handleSubmit = (e) => {
1919
+ e.preventDefault();
1920
+ const normalized = normalizeUrl(url);
1921
+ if (!normalized) {
1922
+ setError(t("linkInput.invalid"));
1923
+ return;
1924
+ }
1925
+ setError("");
1926
+ onSubmit(normalized);
1927
+ };
1928
+ return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-2", children: [
1929
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
1930
+ /* @__PURE__ */ jsx4(
1931
+ "input",
1932
+ {
1933
+ ref: inputRef,
1934
+ type: "text",
1935
+ value: url,
1936
+ onChange: (e) => {
1937
+ setUrl(e.target.value);
1938
+ if (error) setError("");
1939
+ },
1940
+ placeholder: t("linkInput.placeholder"),
1941
+ "aria-invalid": Boolean(error),
1942
+ "aria-describedby": error ? errorId : void 0,
1943
+ className: "flex-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 aria-invalid:ring-2 aria-invalid:ring-destructive/40"
1944
+ }
1945
+ ),
1946
+ /* @__PURE__ */ jsx4("button", { type: "submit", className: "p-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", children: /* @__PURE__ */ jsx4(Check, { className: "w-4 h-4" }) }),
1947
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: onCancel, className: "p-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: /* @__PURE__ */ jsx4(X, { className: "w-4 h-4" }) })
1948
+ ] }),
1949
+ error ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", className: "mt-1.5 px-1 text-xs text-destructive", children: error }) : null
1950
+ ] });
1951
+ };
1952
+ var ImageInput = ({ onSubmit, onCancel }) => {
1953
+ const t = useSmartTranslations("UEditor");
1954
+ const [url, setUrl] = useState3("");
1955
+ const [alt, setAlt] = useState3("");
1956
+ const inputRef = useRef2(null);
1957
+ useEffect3(() => {
1958
+ inputRef.current?.focus();
1959
+ }, []);
1960
+ const handleSubmit = (e) => {
1961
+ e.preventDefault();
1962
+ const safeUrl = sanitizeUEditorUrl(url, "image");
1963
+ if (safeUrl) {
1964
+ onSubmit(safeUrl, alt);
1965
+ }
1966
+ };
1967
+ return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-3 space-y-3", children: [
1968
+ /* @__PURE__ */ jsxs3("div", { children: [
1969
+ /* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.urlLabel") }),
1970
+ /* @__PURE__ */ jsx4(
1971
+ "input",
1972
+ {
1973
+ ref: inputRef,
1974
+ type: "text",
1975
+ value: url,
1976
+ onChange: (e) => setUrl(e.target.value),
1977
+ placeholder: t("imageInput.urlPlaceholder"),
1978
+ className: "w-full mt-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
1979
+ }
1980
+ )
1981
+ ] }),
1982
+ /* @__PURE__ */ jsxs3("div", { children: [
1983
+ /* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.altLabel") }),
1984
+ /* @__PURE__ */ jsx4(
1985
+ "input",
1986
+ {
1987
+ type: "text",
1988
+ value: alt,
1989
+ onChange: (e) => setAlt(e.target.value),
1990
+ placeholder: t("imageInput.altPlaceholder"),
1991
+ className: "w-full mt-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
1992
+ }
1993
+ )
1994
+ ] }),
1995
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
1996
+ /* @__PURE__ */ jsx4(
1997
+ "button",
1998
+ {
1999
+ type: "submit",
2000
+ disabled: !url,
2001
+ className: "flex-1 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50",
2002
+ children: t("imageInput.addBtn")
2003
+ }
2004
+ ),
2005
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: onCancel, className: "px-4 py-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: t("imageInput.cancelBtn") })
2006
+ ] })
2007
+ ] });
2008
+ };
2009
+
2010
+ // src/components/UEditor/link-commands.ts
2011
+ import { TextSelection } from "@tiptap/pm/state";
2012
+ function applyEditorLink(editor, href) {
2013
+ const isEditingLink = editor.isActive("link");
2014
+ const hasSelectedText = !editor.state.selection.empty;
2015
+ const chain = editor.chain().focus();
2016
+ if (isEditingLink) {
2017
+ chain.extendMarkRange("link");
2018
+ }
2019
+ chain.setLink({ href });
2020
+ if (!hasSelectedText && !isEditingLink) {
2021
+ chain.insertContent(href);
2022
+ }
2023
+ return chain.command(({ tr }) => {
2024
+ tr.setSelection(TextSelection.create(tr.doc, tr.selection.to));
2025
+ tr.removeStoredMark(editor.schema.marks.link);
2026
+ return true;
2027
+ }).run();
2028
+ }
2029
+
2030
+ // src/components/UEditor/toolbar.tsx
2031
+ import React7, { useRef as useRef4, useState as useState4 } from "react";
2032
+ import { useEditorState } from "@tiptap/react";
2033
+ import {
2034
+ AlignCenter,
2035
+ AlignJustify,
2036
+ AlignLeft,
2037
+ AlignRight,
2038
+ ArrowDown,
2039
+ ArrowLeft,
2040
+ ArrowRight,
2041
+ ArrowUp,
2042
+ CircleCheckBig,
2043
+ FileCode,
2044
+ Heading1 as Heading1Icon,
2045
+ Heading2 as Heading2Icon,
2046
+ Heading3 as Heading3Icon,
2047
+ IndentDecrease,
2048
+ IndentIncrease,
2049
+ Link as LinkIcon,
2050
+ List as ListIcon,
2051
+ ListOrdered as ListOrderedIcon,
2052
+ ListTodo,
2053
+ Quote as QuoteIcon,
2054
+ RotateCcw,
2055
+ SquareCheckBig,
2056
+ TableCellsMerge,
2057
+ Trash2,
2058
+ Type,
2059
+ Upload,
2060
+ AlignStartVertical,
2061
+ AlignCenterVertical,
2062
+ AlignEndVertical
2063
+ } from "lucide-react";
2064
+ import { setCellAttr } from "@tiptap/pm/tables";
2065
+
2066
+ // src/components/UEditor/colors.tsx
2067
+ import { useMemo as useMemo2, useRef as useRef3 } from "react";
2068
+ import { Check as Check2, Palette, Paintbrush, Grid } from "lucide-react";
2069
+
2070
+ // src/components/UEditor/figma-toolbar-icons.tsx
2071
+ import React5 from "react";
2072
+ import { jsx as jsx5 } from "react/jsx-runtime";
2073
+ function createToolbarIcon(displayName, width, height, path) {
2074
+ const Icon = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
2075
+ "svg",
2076
+ {
2077
+ ref,
2078
+ "aria-hidden": "true",
2079
+ focusable: "false",
2080
+ viewBox: `0 0 ${width} ${height}`,
2081
+ className,
2082
+ fill: "currentColor",
2083
+ xmlns: "http://www.w3.org/2000/svg",
2084
+ ...props,
2085
+ children: /* @__PURE__ */ jsx5("path", { d: path })
2086
+ }
2087
+ ));
2088
+ Icon.displayName = displayName;
2089
+ return Icon;
2090
+ }
2091
+ var FigmaChevronDownIcon = createToolbarIcon(
2092
+ "FigmaChevronDownIcon",
2093
+ 512,
2094
+ 512,
2095
+ "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
2096
+ );
2097
+ var FigmaTextStyleIcon = ({ className, ...props }) => /* @__PURE__ */ jsx5("svg", { "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", className, fill: "currentColor", ...props, children: /* @__PURE__ */ jsx5("path", { d: "M4 4.25C4 3.56 4.56 3 5.25 3h13.5C19.44 3 20 3.56 20 4.25v2.5a1 1 0 1 1-2 0V5h-5v14h2a1 1 0 1 1 0 2H9a1 1 0 1 1 0-2h2V5H6v1.75a1 1 0 1 1-2 0v-2.5Z" }) });
2098
+ var FigmaLineHeightIcon = createToolbarIcon(
2099
+ "FigmaLineHeightIcon",
2100
+ 576,
2101
+ 512,
2102
+ "M64 128l0-32 64 0 0 320-32 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l128 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0 0-320 64 0 0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-48c0-26.5-21.5-48-48-48L160 32 48 32C21.5 32 0 53.5 0 80l0 48c0 17.7 14.3 32 32 32s32-14.3 32-32zM502.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-64 64c-9.2 9.2-11.9 22.9-6.9 34.9s16.6 19.8 29.6 19.8l32 0 0 192-32 0c-12.9 0-24.6 7.8-29.6 19.8s-2.2 25.7 6.9 34.9l64 64c12.5 12.5 32.8 12.5 45.3 0l64-64c9.2-9.2 11.9-22.9 6.9-34.9s-16.6-19.8-29.6-19.8l-32 0 0-192 32 0c12.9 0 24.6-7.8 29.6-19.8s2.2-25.7-6.9-34.9l-64-64z"
2103
+ );
2104
+ var FigmaLetterSpacingIcon = createToolbarIcon(
2105
+ "FigmaLetterSpacingIcon",
2106
+ 448,
2107
+ 512,
2108
+ "M64 128l0-32 128 0 0 128-16 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-16 0 0-128 128 0 0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-48c0-26.5-21.5-48-48-48L224 32 48 32C21.5 32 0 53.5 0 80l0 48c0 17.7 14.3 32 32 32s32-14.3 32-32zM9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6l0-32 192 0 0 32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9s-19.8 16.6-19.8 29.6l0 32-192 0 0-32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64z"
2109
+ );
2110
+ var FigmaBoldIcon = createToolbarIcon(
2111
+ "FigmaBoldIcon",
2112
+ 384,
2113
+ 512,
2114
+ "M0 64C0 46.3 14.3 32 32 32l48 0 16 0 128 0c70.7 0 128 57.3 128 128c0 31.3-11.3 60.1-30 82.3c37.1 22.4 62 63.1 62 109.7c0 70.7-57.3 128-128 128L96 480l-16 0-48 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l16 0 0-160L48 96 32 96C14.3 96 0 81.7 0 64zM224 224c35.3 0 64-28.7 64-64s-28.7-64-64-64L112 96l0 128 112 0zM112 288l0 128 144 0c35.3 0 64-28.7 64-64s-28.7-64-64-64l-32 0-112 0z"
2115
+ );
2116
+ var FigmaItalicIcon = createToolbarIcon(
2117
+ "FigmaItalicIcon",
2118
+ 384,
2119
+ 512,
2120
+ "M128 64c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-58.7 0L160 416l64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l58.7 0L224 96l-64 0c-17.7 0-32-14.3-32-32z"
2121
+ );
2122
+ var FigmaUnderlineIcon = createToolbarIcon(
2123
+ "FigmaUnderlineIcon",
2124
+ 448,
2125
+ 512,
2126
+ "M16 64c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-16 0 0 128c0 53 43 96 96 96s96-43 96-96l0-128-16 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-16 0 0 128c0 88.4-71.6 160-160 160s-160-71.6-160-160L64 96 48 96C30.3 96 16 81.7 16 64zM0 448c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32z"
2127
+ );
2128
+ var FigmaStrikeIcon = createToolbarIcon(
2129
+ "FigmaStrikeIcon",
2130
+ 512,
2131
+ 512,
2132
+ "M161.3 144c3.2-17.2 14-30.1 33.7-38.6c21.1-9 51.8-12.3 88.6-6.5c11.9 1.9 48.8 9.1 60.1 12c17.1 4.5 34.6-5.6 39.2-22.7s-5.6-34.6-22.7-39.2c-14.3-3.8-53.6-11.4-66.6-13.4c-44.7-7-88.3-4.2-123.7 10.9c-36.5 15.6-64.4 44.8-71.8 87.3c-.1 .6-.2 1.1-.2 1.7c-2.8 23.9 .5 45.6 10.1 64.6c4.5 9 10.2 16.9 16.7 23.9L32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l448 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-209.9 0-.4-.1-1.1-.3c-36-10.8-65.2-19.6-85.2-33.1c-9.3-6.3-15-12.6-18.2-19.1c-3.1-6.1-5.2-14.6-3.8-27.4zM348.9 337.2c2.7 6.5 4.4 15.8 1.9 30.1c-3 17.6-13.8 30.8-33.9 39.4c-21.1 9-51.7 12.3-88.5 6.5c-18-2.9-49.1-13.5-74.4-22.1c-5.6-1.9-11-3.7-15.9-5.4c-16.8-5.6-34.9 3.5-40.5 20.3s3.5 34.9 20.3 40.5c3.6 1.2 7.9 2.7 12.7 4.3c24.9 8.5 63.6 21.7 87.6 25.6l.2 0c44.7 7 88.3 4.2 123.7-10.9c36.5-15.6 64.4-44.8 71.8-87.3c3.6-21 2.7-40.4-3.1-58.1l-75.7 0c7 5.6 11.4 11.2 13.9 17.2z"
2133
+ );
2134
+ var FigmaCodeIcon = createToolbarIcon(
2135
+ "FigmaCodeIcon",
2136
+ 640,
2137
+ 512,
2138
+ "M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"
2139
+ );
2140
+ var FigmaSubscriptIcon = createToolbarIcon(
2141
+ "FigmaSubscriptIcon",
2142
+ 512,
2143
+ 512,
2144
+ "M32 64C14.3 64 0 78.3 0 96s14.3 32 32 32l15.3 0 89.6 128L47.3 384 32 384c-17.7 0-32 14.3-32 32s14.3 32 32 32l32 0c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6l32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-15.3 0L215.1 256l89.6-128 15.3 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0c-10.4 0-20.2 5.1-26.2 13.6L176 200.2 90.2 77.6C84.2 69.1 74.4 64 64 64L32 64zM480 320c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 361.5 404.3 368 416 368l0 80c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-128z"
2145
+ );
2146
+ var FigmaSuperscriptIcon = createToolbarIcon(
2147
+ "FigmaSuperscriptIcon",
2148
+ 512,
2149
+ 512,
2150
+ "M480 32c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 73.5 404.3 80 416 80l0 80c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-128zM32 64C14.3 64 0 78.3 0 96s14.3 32 32 32l15.3 0 89.6 128L47.3 384 32 384c-17.7 0-32 14.3-32 32s14.3 32 32 32l32 0c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6l32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-15.3 0L215.1 256l89.6-128 15.3 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-32 0c-10.4 0-20.2 5.1-26.2 13.6L176 200.2 90.2 77.6C84.2 69.1 74.4 64 64 64L32 64z"
2151
+ );
2152
+ var FigmaLinkIcon = createToolbarIcon(
2153
+ "FigmaLinkIcon",
2154
+ 640,
2155
+ 512,
2156
+ "M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"
2157
+ );
2158
+ var FigmaSmileIcon = createToolbarIcon(
2159
+ "FigmaSmileIcon",
2160
+ 512,
2161
+ 512,
2162
+ "M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm177.6 62.1C192.8 334.5 218.8 352 256 352s63.2-17.5 78.4-33.9c9-9.7 24.2-10.4 33.9-1.4s10.4 24.2 1.4 33.9c-22 23.8-60 49.4-113.6 49.4s-91.7-25.5-113.6-49.4c-9-9.7-8.4-24.9 1.4-33.9s24.9-8.4 33.9 1.4zM144.4 208a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm192-32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
2163
+ );
2164
+ var FigmaAlignLeftIcon = createToolbarIcon(
2165
+ "FigmaAlignLeftIcon",
2166
+ 448,
2167
+ 512,
2168
+ "M288 64c0 17.7-14.3 32-32 32L32 96C14.3 96 0 81.7 0 64S14.3 32 32 32l224 0c17.7 0 32 14.3 32 32zm0 256c0 17.7-14.3 32-32 32L32 352c-17.7 0-32-14.3-32-32s14.3-32 32-32l224 0c17.7 0 32 14.3 32 32zM0 192c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 224c-17.7 0-32-14.3-32-32zM448 448c0 17.7-14.3 32-32 32L32 480c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"
2169
+ );
2170
+ var FigmaListIcon = createToolbarIcon(
2171
+ "FigmaListIcon",
2172
+ 512,
2173
+ 512,
2174
+ "M64 144a48 48 0 1 0 0-96 48 48 0 1 0 0 96zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 64zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zM64 464a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm48-208a48 48 0 1 0-96 0 48 48 0 1 0 96 0z"
2175
+ );
2176
+ var FigmaQuoteIcon = createToolbarIcon(
2177
+ "FigmaQuoteIcon",
2178
+ 448,
2179
+ 512,
2180
+ "M0 216C0 149.7 53.7 96 120 96l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136zm256 0c0-66.3 53.7-120 120-120l8 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-8 0c-30.9 0-56 25.1-56 56l0 8 64 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64l-64 0c-35.3 0-64-28.7-64-64l0-136z"
2181
+ );
2182
+ var FigmaImageIcon = createToolbarIcon(
2183
+ "FigmaImageIcon",
2184
+ 512,
2185
+ 512,
2186
+ "M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6l336 0c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"
2187
+ );
2188
+ var FigmaTableIcon = createToolbarIcon(
2189
+ "FigmaTableIcon",
2190
+ 512,
2191
+ 512,
2192
+ "M64 256l0-96 160 0 0 96L64 256zm0 64l160 0 0 96L64 416l0-96zm224 96l0-96 160 0 0 96-160 0zM448 256l-160 0 0-96 160 0 0 96zM64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-320c0-35.3-28.7-64-64-64L64 32z"
2193
+ );
2194
+ var FigmaUndoIcon = createToolbarIcon(
2195
+ "FigmaUndoIcon",
2196
+ 512,
2197
+ 512,
2198
+ "M48.5 224L40 224c-13.3 0-24-10.7-24-24L16 72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2l41.6 41.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2S177.7 224 168 224L48.5 224z"
2199
+ );
2200
+ var FigmaRedoIcon = createToolbarIcon(
2201
+ "FigmaRedoIcon",
2202
+ 512,
2203
+ 512,
2204
+ "M463.5 224l8.5 0c13.3 0 24-10.7 24-24l0-128c0-9.7-5.8-18.5-14.8-22.2s-19.3-1.7-26.2 5.2l-41.6 41.6c-87.6-86.5-228.7-86.2-315.8 1c-87.5 87.5-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3c62.2-62.2 162.7-62.5 225.3-1L327 183c-6.9 6.9-8.9 17.2-5.2 26.2S334.3 224 344 224l119.5 0z"
2205
+ );
2206
+ var FigmaHighlighterIcon = createToolbarIcon(
2207
+ "FigmaHighlighterIcon",
2208
+ 576,
2209
+ 512,
2210
+ "M315 315l158.4-215L444.1 70.6 229 229 315 315zm-187 5l0-71.7c0-15.3 7.2-29.6 19.5-38.6L420.6 8.4C428 2.9 437 0 446.2 0c11.4 0 22.4 4.5 30.5 12.6l54.8 54.8c8.1 8.1 12.6 19 12.6 30.5c0 9.2-2.9 18.2-8.4 25.6L334.4 396.5c-9 12.3-23.4 19.5-38.6 19.5L224 416l-25.4 25.4c-12.5 12.5-32.8 12.5-45.3 0l-50.7-50.7c-12.5-12.5-12.5-32.8 0-45.3L128 320zM7 466.3l63-63 70.6 70.6-31 31c-4.5 4.5-10.6 7-17 7L24 512c-13.3 0-24-10.7-24-24l0-4.7c0-6.4 2.5-12.5 7-17z"
2211
+ );
2212
+
2213
+ // src/components/UEditor/colors.tsx
2214
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
2215
+ var TextColorIcon = ({ color }) => {
2216
+ const underlineColor = color && color !== "inherit" ? color : "currentColor";
2217
+ return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
2218
+ /* @__PURE__ */ jsx6("span", { className: "text-[15px] font-semibold leading-none", children: "A" }),
2219
+ /* @__PURE__ */ jsx6(
2220
+ "span",
2221
+ {
2222
+ "aria-hidden": "true",
2223
+ className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
2224
+ style: { backgroundColor: underlineColor }
2225
+ }
2226
+ )
2227
+ ] });
2228
+ };
2229
+ var HighlightColorIcon = ({ color }) => {
2230
+ const underlineColor = color || "currentColor";
2231
+ return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
2232
+ /* @__PURE__ */ jsx6(FigmaHighlighterIcon, { className: "h-4 w-4" }),
2233
+ /* @__PURE__ */ jsx6(
2234
+ "span",
2235
+ {
2236
+ "aria-hidden": "true",
2237
+ className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
2238
+ style: { backgroundColor: underlineColor }
2239
+ }
2240
+ )
2241
+ ] });
2242
+ };
2243
+ var CellBgColorIcon = ({ color }) => {
2244
+ const underlineColor = color && color !== "inherit" ? color : "currentColor";
2245
+ return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
2246
+ /* @__PURE__ */ jsx6(Paintbrush, { className: "h-4 w-4" }),
2247
+ /* @__PURE__ */ jsx6(
2248
+ "span",
2249
+ {
2250
+ "aria-hidden": "true",
2251
+ className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
2252
+ style: { backgroundColor: underlineColor }
2253
+ }
2254
+ )
2255
+ ] });
2256
+ };
2257
+ var CellBorderIcon = ({ color }) => {
2258
+ const underlineColor = color && color !== "inherit" ? color : "currentColor";
2259
+ return /* @__PURE__ */ jsxs4("span", { className: "relative flex h-5 w-5 items-center justify-center leading-none", children: [
2260
+ /* @__PURE__ */ jsx6(Grid, { className: "h-4 w-4" }),
2261
+ /* @__PURE__ */ jsx6(
2262
+ "span",
2263
+ {
2264
+ "aria-hidden": "true",
2265
+ className: "absolute bottom-0 left-1/2 h-0.5 w-4 -translate-x-1/2 rounded-full",
2266
+ style: { backgroundColor: underlineColor }
2267
+ }
2268
+ )
2269
+ ] });
2270
+ };
2271
+ var EDITOR_COLOR_SWATCHES = [
2272
+ "#000000",
2273
+ "#3f3f46",
2274
+ "#713f12",
2275
+ "#14532d",
2276
+ "#164e63",
2277
+ "#1e3a8a",
2278
+ "#3730a3",
2279
+ "#404040",
2280
+ "#b91c1c",
2281
+ "#c2410c",
2282
+ "#a16207",
2283
+ "#15803d",
2284
+ "#0f766e",
2285
+ "#2563eb",
2286
+ "#4f46e5",
2287
+ "#737373",
2288
+ "#ef4444",
2289
+ "#f97316",
2290
+ "#eab308",
2291
+ "#22c55e",
2292
+ "#14b8a6",
2293
+ "#3b82f6",
2294
+ "#7c3aed",
2295
+ "#a3a3a3",
2296
+ "#f43f5e",
2297
+ "#f59e0b",
2298
+ "#facc15",
2299
+ "#00e676",
2300
+ "#22d3ee",
2301
+ "#06b6d4",
2302
+ "#be185d",
2303
+ "#bdbdbd",
2304
+ "#f9a8d4",
2305
+ "#fecaca",
2306
+ "#fde68a",
2307
+ "#bbf7d0",
2308
+ "#a7f3d0",
2309
+ "#bae6fd",
2310
+ "#c4b5fd",
2311
+ "#f5f5f5"
2312
+ ];
2313
+ var HIGHLIGHT_COLOR_SWATCHES = [
2314
+ "#fef08a",
2315
+ "#fde68a",
2316
+ "#fed7aa",
2317
+ "#fecaca",
2318
+ "#fbcfe8",
2319
+ "#e9d5ff",
2320
+ "#c7d2fe",
2321
+ "#bfdbfe",
2322
+ "#bae6fd",
2323
+ "#ccfbf1",
2324
+ "#bbf7d0",
2325
+ "#d9f99d",
2326
+ "#e5e7eb",
2327
+ "#fca5a5",
2328
+ "#fdba74",
2329
+ "#facc15",
2330
+ "#86efac",
2331
+ "#5eead4",
2332
+ "#7dd3fc",
2333
+ "#a5b4fc",
2334
+ "#d8b4fe",
2335
+ "#f0abfc",
2336
+ "#f9a8d4",
2337
+ "#d4d4d4"
2338
+ ];
2339
+ function buildColorOptions(colors, prefix) {
2340
+ return colors.map((color, index) => ({ name: `${prefix} ${index + 1}`, color }));
2341
+ }
2342
+ function getSwatchCheckClass(color) {
2343
+ return /^#(?:fff|ffffff)$/i.test(color) ? "text-foreground" : "text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]";
2344
+ }
2345
+ var useEditorColors = () => {
2346
+ const t = useSmartTranslations("UEditor");
2347
+ const textColors = useMemo2(
2348
+ () => [
2349
+ { name: t("colors.default"), color: "inherit", cssClass: "text-foreground" },
2350
+ { name: t("colors.muted"), color: "var(--muted-foreground)", cssClass: "text-muted-foreground" },
2351
+ { name: t("colors.primary"), color: "var(--primary)", cssClass: "text-primary" },
2352
+ { name: t("colors.secondary"), color: "var(--secondary)", cssClass: "text-secondary" },
2353
+ { name: t("colors.success"), color: "var(--success)", cssClass: "text-success" },
2354
+ { name: t("colors.warning"), color: "var(--warning)", cssClass: "text-warning" },
2355
+ { name: t("colors.destructive"), color: "var(--destructive)", cssClass: "text-destructive" },
2356
+ { name: t("colors.info"), color: "var(--info)", cssClass: "text-info" },
2357
+ ...buildColorOptions(EDITOR_COLOR_SWATCHES, t("colors.color"))
2358
+ ],
2359
+ [t]
2360
+ );
2361
+ const highlightColors = useMemo2(
2362
+ () => [
2363
+ { name: t("colors.default"), color: "", cssClass: "" },
2364
+ { name: t("colors.muted"), color: "var(--muted)", cssClass: "bg-muted" },
2365
+ { name: t("colors.primary"), color: "color-mix(in oklch, var(--primary) 20%, transparent)", cssClass: "bg-primary/20" },
2366
+ { name: t("colors.secondary"), color: "color-mix(in oklch, var(--secondary) 20%, transparent)", cssClass: "bg-secondary/20" },
2367
+ { name: t("colors.success"), color: "color-mix(in oklch, var(--success) 20%, transparent)", cssClass: "bg-success/20" },
2368
+ { name: t("colors.warning"), color: "color-mix(in oklch, var(--warning) 20%, transparent)", cssClass: "bg-warning/20" },
2369
+ { name: t("colors.destructive"), color: "color-mix(in oklch, var(--destructive) 20%, transparent)", cssClass: "bg-destructive/20" },
2370
+ { name: t("colors.info"), color: "color-mix(in oklch, var(--info) 20%, transparent)", cssClass: "bg-info/20" },
2371
+ { name: t("colors.accent"), color: "var(--accent)", cssClass: "bg-accent" },
2372
+ ...buildColorOptions(HIGHLIGHT_COLOR_SWATCHES, t("colors.color"))
2373
+ ],
2374
+ [t]
2375
+ );
2376
+ return { textColors, highlightColors };
2377
+ };
2378
+ var EditorColorPalette = ({
2379
+ colors,
2380
+ currentColor,
2381
+ onSelect,
2382
+ label
2383
+ }) => {
2384
+ const t = useSmartTranslations("UEditor");
2385
+ const colorInputRef = useRef3(null);
2386
+ const automaticColor = colors[0]?.color ?? "";
2387
+ const paletteColors = colors.slice(1);
2388
+ return /* @__PURE__ */ jsxs4("div", { className: "w-56 p-2", children: [
2389
+ /* @__PURE__ */ jsx6("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
2390
+ /* @__PURE__ */ jsxs4(
2391
+ "button",
2392
+ {
2393
+ type: "button",
2394
+ onMouseDown: (e) => e.preventDefault(),
2395
+ onClick: () => onSelect(automaticColor),
2396
+ className: cn(
2397
+ "mt-2 flex h-9 w-full items-center gap-3 rounded-md border px-2 text-sm transition-colors",
2398
+ "bg-muted/50 hover:bg-muted",
2399
+ currentColor === automaticColor ? "border-primary text-primary" : "border-transparent text-foreground"
2400
+ ),
2401
+ children: [
2402
+ /* @__PURE__ */ jsx6("span", { className: "flex h-5 w-5 items-center justify-center rounded border border-border bg-background", children: currentColor === automaticColor && /* @__PURE__ */ jsx6(Check2, { className: "h-3.5 w-3.5" }) }),
2403
+ /* @__PURE__ */ jsx6("span", { className: "flex-1 text-center", children: t("colors.automatic") })
2404
+ ]
2405
+ }
2406
+ ),
2407
+ /* @__PURE__ */ jsx6("div", { className: "mt-2 grid grid-cols-8 gap-1", children: paletteColors.map((c) => /* @__PURE__ */ jsx6(Tooltip, { placement: "top", content: /* @__PURE__ */ jsx6("span", { className: "text-xs font-medium", children: c.name }), children: /* @__PURE__ */ jsx6(
2408
+ "button",
2409
+ {
2410
+ type: "button",
2411
+ "aria-label": c.name,
2412
+ onMouseDown: (e) => e.preventDefault(),
2413
+ onClick: () => onSelect(c.color),
2414
+ className: cn(
2415
+ "relative h-5 w-5 rounded-[3px] border transition-transform hover:scale-110",
2416
+ currentColor === c.color ? "border-primary ring-2 ring-primary/25" : "border-border/70"
2417
+ ),
2418
+ style: { backgroundColor: c.color || "transparent" },
2419
+ children: currentColor === c.color && /* @__PURE__ */ jsx6("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx6(Check2, { className: cn("h-3.5 w-3.5", getSwatchCheckClass(c.color)) }) })
2420
+ }
2421
+ ) }, `${c.name}-${c.color}`)) }),
2422
+ /* @__PURE__ */ jsxs4(
2423
+ "button",
2424
+ {
2425
+ type: "button",
2426
+ onMouseDown: (e) => e.preventDefault(),
2427
+ onClick: () => colorInputRef.current?.click(),
2428
+ className: "mt-3 flex h-9 w-full items-center gap-3 rounded-md px-2 text-sm text-foreground transition-colors hover:bg-muted",
2429
+ children: [
2430
+ /* @__PURE__ */ jsx6(
2431
+ "span",
2432
+ {
2433
+ "aria-hidden": "true",
2434
+ className: "h-5 w-5 rounded border border-border",
2435
+ style: {
2436
+ background: "linear-gradient(135deg, #ff004c 0%, #fffb00 22%, #00ff66 42%, #00d5ff 62%, #2446ff 78%, #ff00d4 100%)"
2437
+ }
2438
+ }
2439
+ ),
2440
+ /* @__PURE__ */ jsx6("span", { className: "flex-1 text-center", children: t("colors.moreColors") }),
2441
+ /* @__PURE__ */ jsx6(Palette, { className: "h-4 w-4 text-muted-foreground" }),
2442
+ /* @__PURE__ */ jsx6(
2443
+ "input",
2444
+ {
2445
+ ref: colorInputRef,
2446
+ type: "color",
2447
+ value: currentColor.startsWith("#") ? currentColor : "#000000",
2448
+ onChange: (event) => onSelect(event.target.value),
2449
+ className: "sr-only",
2450
+ tabIndex: -1
2451
+ }
2452
+ )
2453
+ ]
2454
+ }
2455
+ )
2456
+ ] });
2457
+ };
2458
+
2459
+ // src/components/UEditor/image-commands.ts
2460
+ import { NodeSelection, TextSelection as TextSelection2 } from "@tiptap/pm/state";
2461
+ var IMAGE_WIDTHS_BY_LAYOUT = {
2462
+ block: {
2463
+ sm: 180,
2464
+ md: 280,
2465
+ lg: 380
2466
+ },
2467
+ wrap: {
2468
+ sm: 140,
2469
+ md: 200,
2470
+ lg: 260
2471
+ }
2472
+ };
2473
+ function isSelectedImage(editor) {
2474
+ const { selection } = editor.state;
2475
+ return selection instanceof NodeSelection && selection.node.type.name === "image";
2476
+ }
2477
+ function toPositiveNumber(value) {
2478
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
2479
+ if (typeof value === "string") {
2480
+ const parsed = Number.parseInt(value, 10);
2481
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
2482
+ }
2483
+ return null;
2484
+ }
2485
+ function getImageElementAtSelection(editor, pos) {
2486
+ const nodeDom = editor.view.nodeDOM(pos);
2487
+ if (!(nodeDom instanceof HTMLElement)) return null;
2488
+ if (nodeDom.tagName === "IMG") return nodeDom;
2489
+ return nodeDom.querySelector("img");
2490
+ }
2491
+ function getImageAspectRatio(editor, attrs, pos) {
2492
+ const widthAttr = toPositiveNumber(attrs.width);
2493
+ const heightAttr = toPositiveNumber(attrs.height);
2494
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
2495
+ const imageElement = typeof pos === "number" ? getImageElementAtSelection(editor, pos) : null;
2496
+ if (!imageElement) return null;
2497
+ if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
2498
+ return imageElement.naturalWidth / imageElement.naturalHeight;
2499
+ }
2500
+ const rect = imageElement.getBoundingClientRect();
2501
+ if (rect.width > 0 && rect.height > 0) return rect.width / rect.height;
2502
+ const width = toPositiveNumber(imageElement.getAttribute("width")) ?? toPositiveNumber(imageElement.style.width);
2503
+ const height = toPositiveNumber(imageElement.getAttribute("height")) ?? toPositiveNumber(imageElement.style.height);
2504
+ return width && height ? width / height : null;
2505
+ }
2506
+ function getImagePresetAttributes(editor, width, preset, attrs, pos) {
2507
+ const aspect = getImageAspectRatio(editor, attrs, pos);
2508
+ return {
2509
+ width,
2510
+ height: aspect ? Math.round(width / aspect) : toPositiveNumber(attrs.height),
2511
+ imageWidthPreset: preset
2512
+ };
2513
+ }
2514
+ function applyImageLayout(editor, layout) {
2515
+ const { state, view } = editor;
2516
+ const { selection, schema } = state;
2517
+ if (!(selection instanceof NodeSelection) || selection.node.type.name !== "image") {
2518
+ editor.chain().focus().updateAttributes("image", { imageLayout: layout }).run();
2519
+ return;
2520
+ }
2521
+ let transaction = state.tr.setNodeMarkup(selection.from, void 0, {
2522
+ ...selection.node.attrs,
2523
+ imageLayout: layout
2524
+ });
2525
+ if (layout !== "block") {
2526
+ const nextPos = transaction.mapping.map(selection.to);
2527
+ const nextNode = transaction.doc.nodeAt(nextPos);
2528
+ if (!nextNode || nextNode.type.name !== "paragraph") {
2529
+ const paragraph = schema.nodes.paragraph?.create();
2530
+ if (paragraph) {
2531
+ transaction = transaction.insert(nextPos, paragraph);
2532
+ }
2533
+ }
2534
+ const resolvedPos = transaction.doc.resolve(Math.min(nextPos + 1, transaction.doc.content.size));
2535
+ transaction = transaction.setSelection(TextSelection2.near(resolvedPos));
2536
+ } else {
2537
+ const resolvedPos = transaction.doc.resolve(selection.from);
2538
+ transaction = transaction.setSelection(NodeSelection.create(transaction.doc, resolvedPos.pos));
2539
+ }
2540
+ view.dispatch(transaction.scrollIntoView());
2541
+ view.focus();
2542
+ }
2543
+ function applyImageWidthPreset(editor, preset) {
2544
+ const attrs = editor.getAttributes("image");
2545
+ const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
2546
+ const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
2547
+ if (!isSelectedImage(editor)) {
2548
+ editor.chain().focus().updateAttributes("image", getImagePresetAttributes(editor, width, preset, attrs)).run();
2549
+ return;
2550
+ }
2551
+ const { state, view } = editor;
2552
+ const selection = state.selection;
2553
+ const nextAttrs = getImagePresetAttributes(editor, width, preset, selection.node.attrs, selection.from);
2554
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
2555
+ ...selection.node.attrs,
2556
+ ...nextAttrs
2557
+ });
2558
+ view.dispatch(transaction.scrollIntoView());
2559
+ view.focus();
2560
+ }
2561
+ function resetImageSize(editor) {
2562
+ if (!isSelectedImage(editor)) {
2563
+ editor.chain().focus().updateAttributes("image", {
2564
+ width: null,
2565
+ height: null,
2566
+ imageWidthPreset: null
2567
+ }).run();
2568
+ return;
2569
+ }
2570
+ const { state, view } = editor;
2571
+ const selection = state.selection;
2572
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
2573
+ ...selection.node.attrs,
2574
+ width: null,
2575
+ height: null,
2576
+ imageWidthPreset: null
2577
+ });
2578
+ view.dispatch(transaction.scrollIntoView());
2579
+ view.focus();
2580
+ }
2581
+ function deleteSelectedImage(editor) {
2582
+ if (!isSelectedImage(editor)) return;
2583
+ editor.chain().focus().deleteSelection().run();
2584
+ }
2585
+
2586
+ // src/components/UEditor/table-dom-utils.ts
2587
+ var DEFAULT_TABLE_ROW_HEIGHT = 25;
2588
+ var MIN_TABLE_ROW_HEIGHT = DEFAULT_TABLE_ROW_HEIGHT;
2589
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
2590
+ var ROW_RESIZE_LINE_THICKNESS = 2;
2591
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
2592
+ var TABLE_RESIZE_HIT_ZONE = 10;
2593
+ function findTableRowNodeInfo(view, rowElement) {
2594
+ const firstCell = rowElement.querySelector("th,td");
2595
+ if (!firstCell) return null;
2596
+ const cellPos = view.posAtDOM(firstCell, 0);
2597
+ const $pos = view.state.doc.resolve(cellPos);
2598
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
2599
+ const node = $pos.node(depth);
2600
+ if (node.type.name === "tableRow") {
2601
+ return {
2602
+ pos: $pos.before(depth),
2603
+ node
2604
+ };
2605
+ }
2606
+ }
2607
+ return null;
2608
+ }
2609
+ function resolveEventElement(target) {
2610
+ if (target instanceof Element) return target;
2611
+ if (target instanceof Node) return target.parentElement;
2612
+ return null;
2613
+ }
2614
+ function isPointOverRenderedText(root, clientX, clientY) {
2615
+ const view = root.ownerDocument.defaultView;
2616
+ if (!view) return false;
2617
+ const walker = root.ownerDocument.createTreeWalker(root, view.NodeFilter.SHOW_TEXT);
2618
+ let textNode = walker.nextNode();
2619
+ while (textNode) {
2620
+ if (textNode.textContent?.length) {
2621
+ const range = root.ownerDocument.createRange();
2622
+ range.selectNodeContents(textNode);
2623
+ const textRects = Array.from(range.getClientRects());
2624
+ range.detach?.();
2625
+ if (textRects.some((rect) => clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom)) {
2626
+ return true;
2627
+ }
2628
+ }
2629
+ textNode = walker.nextNode();
2630
+ }
2631
+ return false;
2632
+ }
2633
+ function getSelectionTableCell(view) {
2634
+ const browserSelection = window.getSelection();
2635
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
2636
+ const anchorCell = anchorElement?.closest?.("th,td");
2637
+ if (anchorCell instanceof HTMLElement) {
2638
+ return anchorCell;
2639
+ }
2640
+ const { from } = view.state.selection;
2641
+ const domAtPos = view.domAtPos(from);
2642
+ const element = resolveEventElement(domAtPos.node);
2643
+ const cell = element?.closest?.("th,td");
2644
+ return cell instanceof HTMLElement ? cell : null;
2645
+ }
2646
+ function isRowResizeHotspot(cell, clientX, clientY) {
2647
+ const rect = cell.getBoundingClientRect();
2648
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
2649
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
2650
+ return nearBottom && !nearRight;
2651
+ }
2652
+ function isColumnResizeHotspot(cell, clientX, clientY) {
2653
+ const rect = cell.getBoundingClientRect();
2654
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
2655
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
2656
+ return nearRight && !nearBottom;
2657
+ }
2658
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
2659
+ const surfaceRect = surface.getBoundingClientRect();
2660
+ const tableRect = table.getBoundingClientRect();
2661
+ const rowRect = row.getBoundingClientRect();
2662
+ const cellRect = cell.getBoundingClientRect();
2663
+ return {
2664
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
2665
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
2666
+ width: tableRect.width,
2667
+ height: tableRect.height,
2668
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
2669
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
2670
+ };
2671
+ }
2672
+ function getRelativeCellMetrics(surface, cell) {
2673
+ const surfaceRect = surface.getBoundingClientRect();
2674
+ const cellRect = cell.getBoundingClientRect();
2675
+ return {
2676
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
2677
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
2678
+ width: cellRect.width,
2679
+ height: cellRect.height
2680
+ };
2681
+ }
2682
+ function getRelativeSelectedCellsMetrics(surface) {
2683
+ const selectedCells = Array.from(
2684
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
2685
+ );
2686
+ if (selectedCells.length === 0) {
2687
+ return null;
2688
+ }
2689
+ const surfaceRect = surface.getBoundingClientRect();
2690
+ let left = Number.POSITIVE_INFINITY;
2691
+ let top = Number.POSITIVE_INFINITY;
2692
+ let right = Number.NEGATIVE_INFINITY;
2693
+ let bottom = Number.NEGATIVE_INFINITY;
2694
+ selectedCells.forEach((cell) => {
2695
+ const rect = cell.getBoundingClientRect();
2696
+ left = Math.min(left, rect.left);
2697
+ top = Math.min(top, rect.top);
2698
+ right = Math.max(right, rect.right);
2699
+ bottom = Math.max(bottom, rect.bottom);
2700
+ });
2701
+ return {
2702
+ left: left - surfaceRect.left + surface.scrollLeft,
2703
+ top: top - surfaceRect.top + surface.scrollTop,
2704
+ width: right - left,
2705
+ height: bottom - top
2706
+ };
2707
+ }
2708
+
2709
+ // src/components/UEditor/table-align-utils.ts
2710
+ function findTableNodeInfoAtResolvedPos($pos) {
2711
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
2712
+ const node = $pos.node(depth);
2713
+ if (node.type.name === "table") {
2714
+ return {
2715
+ depth,
2716
+ pos: $pos.before(depth),
2717
+ node
2718
+ };
2719
+ }
2720
+ }
2721
+ return null;
2722
+ }
2723
+ function findTableNodeInfoFromState(state, anchorPos) {
2724
+ if (typeof anchorPos === "number" && Number.isFinite(anchorPos)) {
2725
+ const safePos = Math.max(0, Math.min(anchorPos, state.doc.content.size));
2726
+ return findTableNodeInfoAtResolvedPos(state.doc.resolve(safePos));
2727
+ }
2728
+ return findTableNodeInfoAtResolvedPos(state.selection.$from);
2729
+ }
2730
+ function applyTableAlignment(editor, tableAlign, anchorPos) {
2731
+ const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
2732
+ if (!tableInfo) return false;
2733
+ editor.view.dispatch(
2734
+ editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
2735
+ ...tableInfo.node.attrs,
2736
+ textAlign: tableAlign
2737
+ })
2738
+ );
2739
+ const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
2740
+ const domAtTableElement = resolveEventElement(domAtTable);
2741
+ const tableDom = editor.view.nodeDOM(tableInfo.pos);
2742
+ const tableElement = domAtTableElement?.closest?.("table") ?? (tableDom instanceof HTMLTableElement ? tableDom : tableDom instanceof HTMLElement ? tableDom.querySelector("table") : null) ?? (editor.view.dom.querySelectorAll("table").length === 1 ? editor.view.dom.querySelector("table") : null);
2743
+ if (tableElement instanceof HTMLTableElement) {
2744
+ if (tableAlign) {
2745
+ tableElement.setAttribute("data-table-align", tableAlign);
2746
+ tableElement.style.width = "max-content";
2747
+ tableElement.style.maxWidth = "100%";
2748
+ tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
2749
+ tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
2750
+ } else {
2751
+ tableElement.removeAttribute("data-table-align");
2752
+ tableElement.style.removeProperty("width");
2753
+ tableElement.style.removeProperty("max-width");
2754
+ tableElement.style.removeProperty("margin-left");
2755
+ tableElement.style.removeProperty("margin-right");
2756
+ }
2757
+ }
2758
+ return true;
2759
+ }
2760
+
2761
+ // src/components/UEditor/table-cell-commands.ts
2762
+ import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
2763
+ import { selectedRect, TableMap } from "@tiptap/pm/tables";
2764
+ function getCellSelectionPositions(selection) {
2765
+ const value = selection;
2766
+ const anchor = value.$anchorCell?.pos;
2767
+ const head = value.$headCell?.pos;
2768
+ return typeof anchor === "number" && typeof head === "number" ? { anchor, head } : null;
2769
+ }
2770
+ function findTableInfoFromCellPos(editor, cellPos) {
2771
+ const $pos = editor.state.doc.resolve(cellPos);
2772
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
2773
+ const node = $pos.node(depth);
2774
+ if (node.type.name === "table") {
2775
+ return {
2776
+ table: node,
2777
+ tablePos: $pos.before(depth),
2778
+ tableStart: $pos.start(depth)
2779
+ };
2780
+ }
2781
+ }
2782
+ return null;
2783
+ }
2784
+ function getFocusableCellPos(editor, cellPos) {
2785
+ const cellNode = editor.state.doc.nodeAt(cellPos);
2786
+ if (!cellNode) return cellPos + 1;
2787
+ let offset = cellPos + 1;
2788
+ let node = cellNode.firstChild ?? null;
2789
+ while (node && !node.isTextblock) {
2790
+ offset += 1;
2791
+ node = node.firstChild ?? null;
2792
+ }
2793
+ return node?.isTextblock ? offset + 1 : cellPos + 1;
2794
+ }
2795
+ function focusCell(editor, cellPos) {
2796
+ const selection = TextSelection3.near(editor.state.doc.resolve(getFocusableCellPos(editor, cellPos)));
2797
+ editor.view.dispatch(editor.state.tr.setSelection(selection));
2798
+ editor.view.focus();
2799
+ }
2800
+ function collectChildren(node) {
2801
+ const children = [];
2802
+ node.forEach((child) => children.push(child));
2803
+ return children;
2804
+ }
2805
+ function createEmptyCellNode(cellNode) {
2806
+ return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
2807
+ }
2808
+ function createCellCopyForColumnDuplicate(cellNode) {
2809
+ return cellNode.type.create(cellNode.attrs, cellNode.content);
2810
+ }
2811
+ function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
2812
+ const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
2813
+ let nextColwidth = null;
2814
+ if (Array.isArray(cellNode.attrs.colwidth)) {
2815
+ nextColwidth = [...cellNode.attrs.colwidth];
2816
+ const duplicateWidth = nextColwidth[widthIndex];
2817
+ nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
2818
+ }
2819
+ return cellNode.type.create({
2820
+ ...cellNode.attrs,
2821
+ colspan: colspan + 1,
2822
+ ...nextColwidth ? { colwidth: nextColwidth } : null
2823
+ }, cellNode.content);
2824
+ }
2825
+ function getTableRows(tableNode) {
2826
+ const rows = [];
2827
+ tableNode.forEach((rowNode, rowOffset) => {
2828
+ const cells = [];
2829
+ rowNode.forEach((cellNode, cellOffset, index) => {
2830
+ cells.push({
2831
+ index,
2832
+ node: cellNode,
2833
+ relativePos: rowOffset + 1 + cellOffset
2834
+ });
2835
+ });
2836
+ rows.push({
2837
+ node: rowNode,
2838
+ cells
2839
+ });
2840
+ });
2841
+ return rows;
2842
+ }
2843
+ function safeFindCell(map, relativePos) {
2844
+ try {
2845
+ return map.findCell(relativePos);
2846
+ } catch {
2847
+ return null;
2848
+ }
2849
+ }
2850
+ function getSelectedTableRect(editor) {
2851
+ const cellSelection = getCellSelectionPositions(editor.state.selection);
2852
+ if (cellSelection) {
2853
+ const tableInfo = findTableInfoFromCellPos(editor, cellSelection.anchor);
2854
+ if (tableInfo) {
2855
+ const map = TableMap.get(tableInfo.table);
2856
+ const rect = map.rectBetween(
2857
+ cellSelection.anchor - tableInfo.tableStart,
2858
+ cellSelection.head - tableInfo.tableStart
2859
+ );
2860
+ return {
2861
+ ...rect,
2862
+ map,
2863
+ table: tableInfo.table,
2864
+ tableStart: tableInfo.tableStart
2865
+ };
2866
+ }
2867
+ }
2868
+ return selectedRect(editor.state);
2869
+ }
2870
+ function parsePixelWidth(value) {
2871
+ if (!value) return null;
2872
+ const parsed = Number.parseFloat(value);
2873
+ return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
2874
+ }
2875
+ function getDomColumnWidths(editor, rect) {
2876
+ const tableDom = editor.view.nodeDOM(rect.tableStart - 1);
2877
+ if (!(tableDom instanceof HTMLTableElement)) return null;
2878
+ const cols = Array.from(tableDom.querySelectorAll("colgroup > col"));
2879
+ if (cols.length === 0) return null;
2880
+ const widths = [];
2881
+ for (let col = rect.left; col < rect.right; col += 1) {
2882
+ const colElement = cols[col];
2883
+ if (!colElement) return null;
2884
+ const width = parsePixelWidth(colElement.style.width) ?? parsePixelWidth(colElement.getAttribute("width")) ?? Math.round(colElement.getBoundingClientRect().width);
2885
+ if (!Number.isFinite(width) || width <= 0) return null;
2886
+ widths.push(width);
2887
+ }
2888
+ return widths.length > 0 ? widths : null;
2889
+ }
2890
+ function getNodeColumnWidths(rect) {
2891
+ const widths = [];
2892
+ for (let col = rect.left; col < rect.right; col += 1) {
2893
+ let width = null;
2894
+ const seen = /* @__PURE__ */ new Set();
2895
+ for (let row = 0; row < rect.map.height && width == null; row += 1) {
2896
+ const cellPos = rect.map.map[row * rect.map.width + col];
2897
+ if (seen.has(cellPos)) continue;
2898
+ seen.add(cellPos);
2899
+ const cell = rect.table.nodeAt(cellPos);
2900
+ const colwidth = cell?.attrs.colwidth;
2901
+ if (!Array.isArray(colwidth)) continue;
2902
+ const cellLeft = rect.map.colCount(cellPos);
2903
+ const widthIndex = col - cellLeft;
2904
+ const candidate = colwidth[widthIndex];
2905
+ if (typeof candidate === "number" && candidate > 0) {
2906
+ width = candidate;
2907
+ }
2908
+ }
2909
+ if (width == null) return null;
2910
+ widths.push(width);
2911
+ }
2912
+ return widths.length > 0 ? widths : null;
2913
+ }
2914
+ function getSelectedColumnWidths(editor, rect) {
2915
+ return getDomColumnWidths(editor, rect) ?? getNodeColumnWidths(rect);
2916
+ }
2917
+ function dispatchTableLayoutChange(editor) {
2918
+ editor.view.dom.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, { bubbles: true }));
2919
+ }
2920
+ function mergeTableCellsPreservingColumnWidths(editor) {
2921
+ const rect = getSelectedTableRect(editor);
2922
+ const widths = getSelectedColumnWidths(editor, rect);
2923
+ const merged = editor.chain().focus().mergeCells().run();
2924
+ if (!merged) return merged;
2925
+ if (!widths) {
2926
+ dispatchTableLayoutChange(editor);
2927
+ return merged;
2928
+ }
2929
+ const nextRect = getSelectedTableRect(editor);
2930
+ const cellPos = nextRect.map.map[nextRect.top * nextRect.map.width + nextRect.left];
2931
+ const absolutePos = nextRect.tableStart + cellPos;
2932
+ const node = editor.state.doc.nodeAt(absolutePos);
2933
+ if (!node) return merged;
2934
+ editor.view.dispatch(
2935
+ editor.state.tr.setNodeMarkup(absolutePos, node.type, {
2936
+ ...node.attrs,
2937
+ colwidth: widths
2938
+ })
2939
+ );
2940
+ dispatchTableLayoutChange(editor);
2941
+ return true;
2942
+ }
2943
+ function runTableCommandAtCellPos(editor, cellPos, command) {
2944
+ if (cellPos == null) return false;
2945
+ focusCell(editor, cellPos);
2946
+ return command(editor.chain().focus(null, { scrollIntoView: false })).run();
2947
+ }
2948
+ function getTableCornerCellPos(editor, activePos) {
2949
+ const tableInfo = findTableInfoFromCellPos(editor, activePos);
2950
+ if (!tableInfo) return null;
2951
+ const map = TableMap.get(tableInfo.table);
2952
+ return tableInfo.tableStart + map.positionAt(map.height - 1, map.width - 1, tableInfo.table);
2953
+ }
2954
+ function replaceTableAtCellPos(editor, cellPos, updateTable) {
2955
+ if (cellPos == null) return false;
2956
+ const tableInfo = findTableInfoFromCellPos(editor, cellPos);
2957
+ if (!tableInfo) return false;
2958
+ const nextTable = updateTable(tableInfo.table);
2959
+ if (!nextTable) return false;
2960
+ editor.view.dispatch(editor.state.tr.replaceWith(tableInfo.tablePos, tableInfo.tablePos + tableInfo.table.nodeSize, nextTable));
2961
+ dispatchTableLayoutChange(editor);
2962
+ return true;
2963
+ }
2964
+ function duplicateTableRowAt(editor, rowIndex, cellPos) {
2965
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
2966
+ const rows = collectChildren(tableNode);
2967
+ const rowNode = rows[rowIndex];
2968
+ if (!rowNode) return null;
2969
+ rows.splice(rowIndex + 1, 0, rowNode.copy(rowNode.content));
2970
+ return tableNode.type.create(tableNode.attrs, rows);
2971
+ });
2972
+ }
2973
+ function clearTableRowAt(editor, rowIndex, cellPos) {
2974
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
2975
+ const map = TableMap.get(tableNode);
2976
+ if (rowIndex < 0 || rowIndex >= map.height) return null;
2977
+ const rows = getTableRows(tableNode).map((rowInfo) => {
2978
+ const cells = collectChildren(rowInfo.node);
2979
+ for (const entry of rowInfo.cells) {
2980
+ const rect = safeFindCell(map, entry.relativePos);
2981
+ if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
2982
+ cells[entry.index] = createEmptyCellNode(entry.node);
2983
+ }
2984
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
2985
+ });
2986
+ return tableNode.type.create(tableNode.attrs, rows);
2987
+ });
2988
+ }
2989
+ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
2990
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
2991
+ const map = TableMap.get(tableNode);
2992
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
2993
+ const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
2994
+ const cells = collectChildren(rowInfo.node);
2995
+ const sourceCell = rowInfo.cells.find((entry) => {
2996
+ const rect = safeFindCell(map, entry.relativePos);
2997
+ return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
2998
+ });
2999
+ if (!sourceCell) return rowInfo.node;
3000
+ const sourceRect = safeFindCell(map, sourceCell.relativePos);
3001
+ if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
3002
+ cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
3003
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
3004
+ }
3005
+ cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
3006
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
3007
+ });
3008
+ return tableNode.type.create(tableNode.attrs, rows);
3009
+ });
3010
+ }
3011
+ function clearTableColumnAt(editor, columnIndex, cellPos) {
3012
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
3013
+ const map = TableMap.get(tableNode);
3014
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
3015
+ const rows = getTableRows(tableNode).map((rowInfo) => {
3016
+ const cells = collectChildren(rowInfo.node);
3017
+ for (const entry of rowInfo.cells) {
3018
+ const rect = safeFindCell(map, entry.relativePos);
3019
+ if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
3020
+ cells[entry.index] = createEmptyCellNode(entry.node);
3021
+ }
3022
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
3023
+ });
3024
+ return tableNode.type.create(tableNode.attrs, rows);
3025
+ });
3026
+ }
3027
+ function expandTableFromCell(editor, activeCellPos, rows, columns) {
3028
+ let cornerCellPos = getTableCornerCellPos(editor, activeCellPos);
3029
+ if (cornerCellPos == null) return false;
3030
+ for (let index = 0; index < rows; index += 1) {
3031
+ const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addRowAfter());
3032
+ if (!ok) return false;
3033
+ cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
3034
+ if (cornerCellPos == null) return false;
3035
+ }
3036
+ for (let index = 0; index < columns; index += 1) {
3037
+ const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addColumnAfter());
3038
+ if (!ok) return false;
3039
+ cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
3040
+ if (cornerCellPos == null) return false;
3041
+ }
3042
+ dispatchTableLayoutChange(editor);
3043
+ return true;
3044
+ }
3045
+
3046
+ // src/components/UEditor/typography-options.ts
3047
+ function normalizeStyleValue(value) {
3048
+ return typeof value === "string" ? value.trim().replace(/^['"]|['"]$/g, "") : "";
3049
+ }
3050
+ function getDefaultFontFamilies(t) {
3051
+ return [
3052
+ { label: "Inter", value: '"Inter", "Noto Sans", "Noto Sans CJK KR", "Noto Sans CJK JP", "Segoe UI", sans-serif' },
3053
+ { label: "\uAD74\uB9BC", value: '"Gulim", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
3054
+ { label: "\uAD74\uB9BC\uCCB4", value: '"GulimChe", "Gulim", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
3055
+ { label: "\uAD81\uC11C", value: '"Gungsuh", "Nanum Myeongjo", serif' },
3056
+ { label: "\uAD81\uC11C\uCCB4", value: '"GungsuhChe", "Gungsuh", "Nanum Myeongjo", serif' },
3057
+ { label: "\uB3CB\uC6C0", value: '"Dotum", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
3058
+ { label: "\uB3CB\uC6C0\uCCB4", value: '"DotumChe", "Dotum", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
3059
+ { label: "\uBC14\uD0D5", value: '"Batang", "Nanum Myeongjo", serif' },
3060
+ { label: "\uBC14\uD0D5\uCCB4", value: '"BatangChe", "Batang", "Nanum Myeongjo", serif' },
3061
+ { label: "\uB9D1\uC740\uACE0\uB515", value: '"Malgun Gothic", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif' },
3062
+ { label: "\uB098\uB214\uBA85\uC870", value: '"Nanum Myeongjo", "Batang", serif' },
3063
+ { label: "System UI", value: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' },
3064
+ { label: "Roboto", value: '"Roboto", "Noto Sans", "Apple SD Gothic Neo", "Hiragino Kaku Gothic ProN", sans-serif' },
3065
+ { label: "Lexend", value: '"Lexend", "Be Vietnam Pro", "Segoe UI", sans-serif' },
3066
+ { label: "Montserrat", value: '"Montserrat", "Segoe UI", sans-serif' },
3067
+ { label: "Lora", value: '"Lora", "Georgia", "Times New Roman", "Nanum Myeongjo", "BIZ UDPMincho", serif' },
3068
+ { label: "Playfair Display", value: '"Playfair Display", "Times New Roman", "Nanum Myeongjo", serif' },
3069
+ { label: "Georgia", value: 'Georgia, "Nanum Myeongjo", "Batang", "Times New Roman", serif' },
3070
+ { label: "Times New Roman", value: '"Times New Roman", Times, "BIZ UDPMincho", serif' },
3071
+ { label: "Meiryo (JA)", value: '"Meiryo", "Hiragino Sans", "Noto Sans JP", sans-serif' },
3072
+ { label: "Apple SD Gothic Neo (KO)", value: '"Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif' },
3073
+ { label: "JetBrains Mono", value: '"JetBrains Mono", "Fira Code", "SFMono-Regular", Consolas, "Noto Sans Mono CJK KR", "Noto Sans Mono CJK JP", monospace' }
3074
+ ];
3075
+ }
3076
+ function getDefaultFontSizes() {
3077
+ return [
3078
+ { label: "8", value: "8px" },
3079
+ { label: "9", value: "9px" },
3080
+ { label: "10", value: "10px" },
3081
+ { label: "11", value: "11px" },
3082
+ { label: "12", value: "12px" },
3083
+ { label: "13", value: "13px" },
3084
+ { label: "14", value: "14px" },
3085
+ { label: "15", value: "15px" },
3086
+ { label: "16", value: "16px" },
3087
+ { label: "17", value: "17px" },
3088
+ { label: "18", value: "18px" },
3089
+ { label: "19", value: "19px" },
3090
+ { label: "20", value: "20px" },
3091
+ { label: "21", value: "21px" },
3092
+ { label: "22", value: "22px" },
3093
+ { label: "23", value: "23px" },
3094
+ { label: "24", value: "24px" },
3095
+ { label: "25", value: "25px" },
3096
+ { label: "26", value: "26px" },
3097
+ { label: "27", value: "27px" },
3098
+ { label: "28", value: "28px" },
3099
+ { label: "36", value: "36px" },
3100
+ { label: "48", value: "48px" },
3101
+ { label: "72", value: "72px" },
3102
+ { label: "96", value: "96px" }
3103
+ ];
3104
+ }
3105
+ function getDefaultLineHeights() {
3106
+ return [
3107
+ { label: "1.2", value: "1.2" },
3108
+ { label: "1.5", value: "1.5" },
3109
+ { label: "1.75", value: "1.75" },
3110
+ { label: "2", value: "2" }
3111
+ ];
3112
+ }
3113
+ function getDefaultLetterSpacings() {
3114
+ return [
3115
+ { label: "-0.02em", value: "-0.02em" },
3116
+ { label: "0", value: "0" },
3117
+ { label: "0.02em", value: "0.02em" },
3118
+ { label: "0.05em", value: "0.05em" },
3119
+ { label: "0.08em", value: "0.08em" }
3120
+ ];
3121
+ }
3122
+
3123
+ // src/components/UEditor/toolbar.tsx
3124
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3125
+ function getTableAnchorPos(editor) {
3126
+ const tableInfo = findTableNodeInfoFromState(editor.state);
3127
+ if (tableInfo) return editor.state.selection.from;
3128
+ const selectionAnchor = resolveEventElement(window.getSelection()?.anchorNode ?? null);
3129
+ const selectionCell = selectionAnchor?.closest?.("th,td");
3130
+ if (selectionCell instanceof HTMLTableCellElement && editor.view.dom.contains(selectionCell)) {
3131
+ return editor.view.posAtDOM(selectionCell, 0) + 1;
3132
+ }
3133
+ const activeElement = document.activeElement instanceof Element ? document.activeElement : null;
3134
+ const activeCell = activeElement?.closest?.("th,td");
3135
+ if (activeCell instanceof HTMLTableCellElement && editor.view.dom.contains(activeCell)) {
3136
+ return editor.view.posAtDOM(activeCell, 0) + 1;
3137
+ }
3138
+ const tables = editor.view.dom.querySelectorAll("table");
3139
+ if (tables.length !== 1) return null;
3140
+ const firstCell = tables[0]?.querySelector("th,td");
3141
+ return firstCell instanceof HTMLTableCellElement ? editor.view.posAtDOM(firstCell, 0) + 1 : null;
3142
+ }
3143
+ var EDITOR_UI_ACTIVE_MARKS = [
3144
+ "blockquote",
3145
+ "bold",
3146
+ "bulletList",
3147
+ "code",
3148
+ "codeBlock",
3149
+ "formCheckbox",
3150
+ "highlight",
3151
+ "image",
3152
+ "italic",
3153
+ "link",
3154
+ "orderedList",
3155
+ "paragraph",
3156
+ "strike",
3157
+ "subscript",
3158
+ "superscript",
3159
+ "taskList",
3160
+ "underline"
3161
+ ];
3162
+ function computeEditorUiRenderState(editor) {
3163
+ const textStyle = editor.getAttributes("textStyle");
3164
+ const highlight = editor.getAttributes("highlight");
3165
+ const image = editor.getAttributes("image");
3166
+ const link = editor.getAttributes("link");
3167
+ const tableCell = editor.getAttributes("tableCell");
3168
+ const tableHeader = editor.getAttributes("tableHeader");
3169
+ const hasTableContext = findTableNodeInfoFromState(editor.state) !== null;
3170
+ const can = editor.can();
3171
+ return {
3172
+ active: EDITOR_UI_ACTIVE_MARKS.map((name) => editor.isActive(name)),
3173
+ alignment: ["left", "center", "right", "justify"].map((textAlign) => editor.isActive({ textAlign })),
3174
+ heading: [1, 2, 3].map((level) => editor.isActive("heading", { level })),
3175
+ textStyle: {
3176
+ color: textStyle.color ?? null,
3177
+ fontFamily: textStyle.fontFamily ?? null,
3178
+ fontSize: textStyle.fontSize ?? null,
3179
+ letterSpacing: textStyle.letterSpacing ?? null,
3180
+ lineHeight: textStyle.lineHeight ?? null
3181
+ },
3182
+ highlightColor: highlight.color ?? null,
3183
+ image: {
3184
+ imageLayout: image.imageLayout ?? null,
3185
+ imageWidthPreset: image.imageWidthPreset ?? null
3186
+ },
3187
+ linkHref: link.href ?? null,
3188
+ tableCell: {
3189
+ backgroundColor: tableCell.backgroundColor ?? tableHeader.backgroundColor ?? null,
3190
+ borderColor: tableCell.borderColor ?? tableHeader.borderColor ?? null,
3191
+ borderStyle: tableCell.borderStyle ?? tableHeader.borderStyle ?? null,
3192
+ borderWidth: tableCell.borderWidth ?? tableHeader.borderWidth ?? null,
3193
+ formula: tableCell.formula ?? tableHeader.formula ?? null,
3194
+ numberFormat: tableCell.numberFormat ?? tableHeader.numberFormat ?? null,
3195
+ textDirection: tableCell.textDirection ?? tableHeader.textDirection ?? null,
3196
+ verticalAlign: tableCell.verticalAlign ?? tableHeader.verticalAlign ?? null
3197
+ },
3198
+ can: {
3199
+ addColumnAfter: hasTableContext && can.addColumnAfter(),
3200
+ addColumnBefore: hasTableContext && can.addColumnBefore(),
3201
+ addRowAfter: hasTableContext && can.addRowAfter(),
3202
+ addRowBefore: hasTableContext && can.addRowBefore(),
3203
+ decreaseIndent: can.decreaseIndent(),
3204
+ increaseIndent: can.increaseIndent(),
3205
+ mergeCells: hasTableContext && can.mergeCells(),
3206
+ redo: can.redo(),
3207
+ splitCell: hasTableContext && can.splitCell(),
3208
+ undo: can.undo()
3209
+ },
3210
+ hasTableContext,
3211
+ isEmpty: editor.isEmpty
3212
+ };
3213
+ }
3214
+ var editorUiRenderStateCache = /* @__PURE__ */ new WeakMap();
3215
+ function getEditorUiRenderState(editor) {
3216
+ const state = editor.state;
3217
+ const cached = editorUiRenderStateCache.get(editor);
3218
+ if (cached?.state === state) return cached.value;
3219
+ const value = computeEditorUiRenderState(editor);
3220
+ editorUiRenderStateCache.set(editor, { state, value });
3221
+ return value;
3222
+ }
3223
+ var EditorUiRenderStateContext = React7.createContext(null);
3224
+ function EditorUiRenderStateProvider({
3225
+ children,
3226
+ editor
3227
+ }) {
3228
+ const value = useEditorState({
3229
+ editor,
3230
+ selector: ({ editor: currentEditor }) => getEditorUiRenderState(currentEditor)
3231
+ });
3232
+ return /* @__PURE__ */ jsx7(EditorUiRenderStateContext.Provider, { value: { editor, value }, children });
3233
+ }
3234
+ function useSharedEditorUiRenderState(editor) {
3235
+ const context = React7.useContext(EditorUiRenderStateContext);
3236
+ if (!context || context.editor !== editor) {
3237
+ throw new Error("UEditor chrome must be rendered inside EditorUiRenderStateProvider");
3238
+ }
3239
+ return context.value;
3240
+ }
3241
+ function fileToDataUrl2(file) {
3242
+ return new Promise((resolve, reject) => {
3243
+ const reader = new FileReader();
3244
+ reader.onload = () => resolve(String(reader.result ?? ""));
3245
+ reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
3246
+ reader.readAsDataURL(file);
3247
+ });
3248
+ }
3249
+ function formatTableInsertLabel(template, rows, cols) {
3250
+ return template.replace("{rows}", String(rows)).replace("{cols}", String(cols));
3251
+ }
3252
+ var ToolbarButton = React7.forwardRef(({ onClick, onMouseDown, active, disabled, children, title, className }, ref) => {
3253
+ const button = /* @__PURE__ */ jsx7(
3254
+ "button",
3255
+ {
3256
+ ref,
3257
+ type: "button",
3258
+ "aria-label": title,
3259
+ onMouseDown: (e) => {
3260
+ onMouseDown?.(e);
3261
+ e.preventDefault();
3262
+ },
3263
+ onClick,
3264
+ disabled,
3265
+ className: cn(
3266
+ "flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors duration-150",
3267
+ "gap-0.5 [&>svg]:h-3.5 [&>svg]:w-3.5 [&>svg]:shrink-0",
3268
+ "hover:bg-accent",
3269
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20",
3270
+ "disabled:opacity-40 disabled:cursor-not-allowed",
3271
+ active ? "bg-primary/10 text-primary shadow-sm" : "text-[#7B8184] hover:text-foreground dark:text-muted-foreground",
3272
+ className
3273
+ ),
3274
+ children
3275
+ }
3276
+ );
3277
+ if (title) {
3278
+ return /* @__PURE__ */ jsx7(Tooltip, { content: title, placement: "top", delay: { open: 200, close: 0 }, children: button });
3279
+ }
3280
+ return button;
3281
+ });
3282
+ ToolbarButton.displayName = "ToolbarButton";
3283
+ var ToolbarDivider = () => /* @__PURE__ */ jsx7("div", { "aria-hidden": "true", className: "mx-1.5 h-7 w-px shrink-0 bg-[rgba(123,129,132,0.24)]" });
3284
+ var TableInsertGrid = ({
3285
+ insertLabel,
3286
+ previewTemplate,
3287
+ onInsert
3288
+ }) => {
3289
+ const [selection, setSelection] = React7.useState({ rows: 3, cols: 3 });
3290
+ const maxRows = 8;
3291
+ const maxCols = 8;
3292
+ return /* @__PURE__ */ jsxs5("div", { className: "mb-2 rounded-xl border border-border/60 bg-muted/20 p-2", children: [
3293
+ /* @__PURE__ */ jsx7("div", { className: "mb-2 text-sm font-medium text-foreground", children: formatTableInsertLabel(previewTemplate, selection.rows, selection.cols) }),
3294
+ /* @__PURE__ */ jsx7("div", { className: "grid grid-cols-8 gap-1", children: Array.from({ length: maxRows }).map(
3295
+ (_, rowIndex) => Array.from({ length: maxCols }).map((__, colIndex) => {
3296
+ const rows = rowIndex + 1;
3297
+ const cols = colIndex + 1;
3298
+ const active = rows <= selection.rows && cols <= selection.cols;
3299
+ return /* @__PURE__ */ jsx7(
3300
+ "button",
3301
+ {
3302
+ type: "button",
3303
+ "aria-label": formatTableInsertLabel(previewTemplate, rows, cols),
3304
+ onMouseDown: (e) => e.preventDefault(),
3305
+ onMouseEnter: () => setSelection({ rows, cols }),
3306
+ onFocus: () => setSelection({ rows, cols }),
3307
+ onClick: () => onInsert(rows, cols),
3308
+ className: cn(
3309
+ "h-5 w-5 rounded-sm border transition-colors",
3310
+ active ? "border-primary bg-primary/20" : "border-border/70 bg-background hover:border-primary/60 hover:bg-primary/10"
3311
+ )
3312
+ },
3313
+ `${rows}-${cols}`
3314
+ );
3315
+ })
3316
+ ) }),
3317
+ /* @__PURE__ */ jsx7("div", { className: "mt-2 text-xs text-muted-foreground", children: insertLabel })
3318
+ ] });
3319
+ };
3320
+ function applyTableCellAttribute(editor, name, value) {
3321
+ const { state, view } = editor;
3322
+ const applied = setCellAttr(name, value)(state, view.dispatch.bind(view));
3323
+ if (applied) {
3324
+ view.focus();
3325
+ return;
3326
+ }
3327
+ editor.chain().focus().setCellAttribute(name, value).run();
3328
+ }
3329
+ var EditorToolbar = ({
3330
+ editor,
3331
+ variant,
3332
+ uploadImage,
3333
+ imageInsertMode = "base64",
3334
+ maxImageFileSize = DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
3335
+ allowedImageMimeTypes = DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
3336
+ fontFamilies,
3337
+ fontSizes,
3338
+ lineHeights,
3339
+ letterSpacings
3340
+ }) => {
3341
+ const t = useSmartTranslations("UEditor");
3342
+ const editorUiState = useSharedEditorUiRenderState(editor);
3343
+ const { textColors, highlightColors } = useEditorColors();
3344
+ const [showImageInput, setShowImageInput] = useState4(false);
3345
+ const [showLinkInput, setShowLinkInput] = useState4(false);
3346
+ const [isTableMenuOpen, setIsTableMenuOpen] = useState4(false);
3347
+ const fileInputRef = useRef4(null);
3348
+ const [isUploadingImage, setIsUploadingImage] = useState4(false);
3349
+ const [imageUploadError, setImageUploadError] = useState4(null);
3350
+ const isImageSelected = editor.isActive("image");
3351
+ const imageAttrs = editor.getAttributes("image");
3352
+ const tableAnchorPos = getTableAnchorPos(editor);
3353
+ const tableInfo = tableAnchorPos == null ? null : findTableNodeInfoFromState(editor.state, tableAnchorPos);
3354
+ const textStyleAttrs = editor.getAttributes("textStyle");
3355
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
3356
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
3357
+ const isTableSelected = tableInfo !== null;
3358
+ const hasTableContext = isTableSelected;
3359
+ const canMergeCells = hasTableContext && editor.can().mergeCells();
3360
+ const canSplitCell = hasTableContext && editor.can().splitCell();
3361
+ const currentCellVerticalAlign = normalizeStyleValue(editor.getAttributes("tableCell").verticalAlign || editor.getAttributes("tableHeader").verticalAlign) || "";
3362
+ const currentCellTextDirection = normalizeStyleValue(editor.getAttributes("tableCell").textDirection || editor.getAttributes("tableHeader").textDirection) || "horizontal";
3363
+ const currentFontFamily = normalizeStyleValue(textStyleAttrs.fontFamily);
3364
+ const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
3365
+ const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
3366
+ const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
3367
+ const currentLineHeight = normalizeStyleValue(textStyleAttrs.lineHeight);
3368
+ const currentLetterSpacing = normalizeStyleValue(textStyleAttrs.letterSpacing);
3369
+ const availableFontFamilies = React7.useMemo(() => fontFamilies ?? getDefaultFontFamilies(t), [fontFamilies, t]);
3370
+ const availableFontSizes = React7.useMemo(() => fontSizes ?? getDefaultFontSizes(), [fontSizes]);
3371
+ const availableLineHeights = React7.useMemo(() => lineHeights ?? getDefaultLineHeights(), [lineHeights]);
3372
+ const availableLetterSpacings = React7.useMemo(() => letterSpacings ?? getDefaultLetterSpacings(), [letterSpacings]);
3373
+ const currentFontFamilyDisplayValue = currentFontFamily.split(",")[0]?.trim() ?? currentFontFamily;
3374
+ const currentFontFamilyLabel = availableFontFamilies.find((option) => normalizeStyleValue(option.value) === currentFontFamily)?.label ?? (currentFontFamilyDisplayValue || t("toolbar.fontDefault"));
3375
+ const currentFontSizeLabel = availableFontSizes.find((option) => normalizeStyleValue(option.value) === currentFontSize)?.label ?? "13";
3376
+ const currentLineHeightLabel = availableLineHeights.find((option) => normalizeStyleValue(option.value) === currentLineHeight)?.label ?? t("toolbar.lineHeightDefault");
3377
+ const currentLetterSpacingLabel = availableLetterSpacings.find((option) => normalizeStyleValue(option.value) === currentLetterSpacing)?.label ?? t("toolbar.letterSpacingDefault");
3378
+ const defaultFontFamily = availableFontFamilies[0];
3379
+ const defaultFontFamilyValue = defaultFontFamily?.value ?? "";
3380
+ const displayedFontFamilyLabel = currentFontFamily ? currentFontFamilyLabel : defaultFontFamily?.label ?? t("toolbar.fontDefault");
3381
+ const displayedFontFamilyValue = currentFontFamily || defaultFontFamilyValue;
3382
+ const displayedFontSizeLabel = currentFontSize ? currentFontSizeLabel : "13";
3383
+ const activeFontSize = currentFontSize || "13px";
3384
+ const isMedium = variant === "medium";
3385
+ const isMediumFull = variant === "medium-full";
3386
+ const isFull = variant === "default" || variant === "full" || variant === "notion" || !variant;
3387
+ const insertImageFiles = async (files) => {
3388
+ if (files.length === 0) return;
3389
+ setIsUploadingImage(true);
3390
+ setImageUploadError(null);
3391
+ for (const file of files) {
3392
+ if (!file.type.startsWith("image/")) continue;
3393
+ if (file.size > maxImageFileSize) continue;
3394
+ if (allowedImageMimeTypes.length > 0 && !allowedImageMimeTypes.includes(file.type)) continue;
3395
+ try {
3396
+ const src = imageInsertMode === "upload" && uploadImage ? await uploadImage(file) : await fileToDataUrl2(file);
3397
+ const safeSrc = sanitizeUEditorUrl(src, "image");
3398
+ if (!safeSrc) continue;
3399
+ editor.chain().focus().setImage({ src: safeSrc, alt: file.name }).run();
3400
+ editor.commands.createParagraphNear();
3401
+ } catch {
3402
+ setImageUploadError(t("imageInput.uploadError"));
3403
+ }
3404
+ }
3405
+ setIsUploadingImage(false);
3406
+ };
3407
+ if (variant === "minimal") {
3408
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/35 bg-muted/30 p-1.5", children: [
3409
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: t("toolbar.undo"), children: /* @__PURE__ */ jsx7(FigmaUndoIcon, { className: "h-4 w-4" }) }),
3410
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: t("toolbar.redo"), children: /* @__PURE__ */ jsx7(FigmaRedoIcon, { className: "h-4 w-4" }) }),
3411
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
3412
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ jsx7(FigmaBoldIcon, { className: "h-4 w-4" }) }),
3413
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ jsx7(FigmaItalicIcon, { className: "h-4 w-4" }) }),
3414
+ /* @__PURE__ */ jsx7(
3415
+ ToolbarButton,
3416
+ {
3417
+ onClick: () => editor.chain().focus().toggleBulletList().run(),
3418
+ active: editor.isActive("bulletList"),
3419
+ title: t("toolbar.bulletList"),
3420
+ children: /* @__PURE__ */ jsx7(FigmaListIcon, { className: "h-4 w-4" })
3421
+ }
3422
+ ),
3423
+ /* @__PURE__ */ jsx7(
3424
+ ToolbarButton,
3425
+ {
3426
+ onClick: () => editor.chain().focus().decreaseIndent().run(),
3427
+ disabled: !editorUiState.can.decreaseIndent,
3428
+ title: t("toolbar.decreaseIndent"),
3429
+ children: /* @__PURE__ */ jsx7(IndentDecrease, { className: "h-4 w-4" })
3430
+ }
3431
+ ),
3432
+ /* @__PURE__ */ jsx7(
3433
+ ToolbarButton,
3434
+ {
3435
+ onClick: () => editor.chain().focus().increaseIndent().run(),
3436
+ disabled: !editorUiState.can.increaseIndent,
3437
+ title: t("toolbar.increaseIndent"),
3438
+ children: /* @__PURE__ */ jsx7(IndentIncrease, { className: "h-4 w-4" })
3439
+ }
3440
+ ),
3441
+ /* @__PURE__ */ jsxs5(
3442
+ DropdownMenu,
3443
+ {
3444
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3445
+ }, active: editor.isActive("formCheckbox"), title: t("slashCommand.formCheckbox"), children: /* @__PURE__ */ jsx7(SquareCheckBig, { className: "w-4 h-4" }) }),
3446
+ children: [
3447
+ /* @__PURE__ */ jsx7(
3448
+ DropdownMenuItem,
3449
+ {
3450
+ icon: SquareCheckBig,
3451
+ label: t("slashCommand.formCheckbox"),
3452
+ onClick: () => editor.chain().focus().setFormCheckbox().run()
3453
+ }
3454
+ ),
3455
+ /* @__PURE__ */ jsx7(
3456
+ DropdownMenuItem,
3457
+ {
3458
+ icon: CircleCheckBig,
3459
+ label: t("slashCommand.roundCheckbox"),
3460
+ onClick: () => editor.chain().focus().setFormCheckbox({ variant: "circle" }).run()
3461
+ }
3462
+ )
3463
+ ]
3464
+ }
3465
+ ),
3466
+ /* @__PURE__ */ jsx7(
3467
+ DropdownMenu,
3468
+ {
3469
+ contentClassName: "min-w-72",
3470
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => setShowLinkInput(!editor.isActive("link")), active: editor.isActive("link"), title: t("toolbar.link"), children: /* @__PURE__ */ jsx7(FigmaLinkIcon, { className: "h-4 w-4" }) }),
3471
+ children: showLinkInput ? /* @__PURE__ */ jsx7(
3472
+ LinkInput,
3473
+ {
3474
+ initialUrl: String(editor.getAttributes("link").href ?? ""),
3475
+ onSubmit: (url) => {
3476
+ applyEditorLink(editor, url);
3477
+ setShowLinkInput(false);
3478
+ },
3479
+ onCancel: () => setShowLinkInput(false)
3480
+ }
3481
+ ) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
3482
+ /* @__PURE__ */ jsx7(
3483
+ DropdownMenuItem,
3484
+ {
3485
+ icon: LinkIcon,
3486
+ label: t("toolbar.link"),
3487
+ onClick: () => setShowLinkInput(true),
3488
+ active: editor.isActive("link"),
3489
+ closeOnSelect: false
3490
+ }
3491
+ ),
3492
+ /* @__PURE__ */ jsx7(
3493
+ DropdownMenuItem,
3494
+ {
3495
+ icon: Trash2,
3496
+ label: t("toolbar.removeLink"),
3497
+ onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
3498
+ disabled: !editor.isActive("link"),
3499
+ destructive: true
3500
+ }
3501
+ )
3502
+ ] })
3503
+ }
3504
+ )
3505
+ ] });
3506
+ }
3507
+ const VerticalAlignActiveIcon = currentCellVerticalAlign === "middle" ? AlignCenterVertical : currentCellVerticalAlign === "bottom" ? AlignEndVertical : AlignStartVertical;
3508
+ return /* @__PURE__ */ jsxs5(
3509
+ "div",
3510
+ {
3511
+ role: "toolbar",
3512
+ className: "flex min-h-12 flex-nowrap items-center gap-0.5 overflow-x-auto border-b border-[rgba(196,197,213,0.6)] bg-[#F4F4F4] px-2 py-2 dark:bg-muted/60",
3513
+ children: [
3514
+ isFull && /* @__PURE__ */ jsx7(
3515
+ DropdownMenu,
3516
+ {
3517
+ trigger: /* @__PURE__ */ jsxs5(
3518
+ ToolbarButton,
3519
+ {
3520
+ onClick: () => {
3521
+ },
3522
+ title: t("toolbar.fontFamily"),
3523
+ className: "h-8 w-44 max-w-44 justify-between gap-2 border border-[rgba(196,197,213,0.6)] bg-white px-2.5 text-[#404040] shadow-[0_1px_2px_rgba(0,0,0,0.07)] hover:bg-white hover:text-[#404040] dark:bg-background dark:text-foreground",
3524
+ children: [
3525
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1 truncate text-left text-sm font-normal", style: { fontFamily: displayedFontFamilyValue || void 0 }, children: displayedFontFamilyLabel }),
3526
+ /* @__PURE__ */ jsx7(FigmaChevronDownIcon, { className: "h-3 w-3 shrink-0 text-[#7B8184]" })
3527
+ ]
3528
+ }
3529
+ ),
3530
+ contentClassName: "max-h-80 overflow-y-auto min-w-56 p-2",
3531
+ children: availableFontFamilies.map((option) => /* @__PURE__ */ jsx7(
3532
+ DropdownMenuItem,
3533
+ {
3534
+ label: option.label,
3535
+ onClick: () => editor.chain().focus().setFontFamily(option.value).run(),
3536
+ active: normalizeStyleValue(option.value) === (currentFontFamily || normalizeStyleValue(defaultFontFamilyValue)),
3537
+ className: "font-medium"
3538
+ },
3539
+ option.value
3540
+ ))
3541
+ }
3542
+ ),
3543
+ (isMediumFull || isFull) && /* @__PURE__ */ jsx7(
3544
+ DropdownMenu,
3545
+ {
3546
+ trigger: /* @__PURE__ */ jsxs5(
3547
+ ToolbarButton,
3548
+ {
3549
+ onClick: () => {
3550
+ },
3551
+ title: t("toolbar.fontSize"),
3552
+ className: "h-8 w-16 justify-between gap-2 border border-[rgba(196,197,213,0.6)] bg-white px-2.5 text-[#404040] shadow-[0_1px_2px_rgba(0,0,0,0.07)] hover:bg-white hover:text-[#404040] dark:bg-background dark:text-foreground",
3553
+ children: [
3554
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-normal leading-none", children: displayedFontSizeLabel }),
3555
+ /* @__PURE__ */ jsx7(FigmaChevronDownIcon, { className: "h-3 w-3 shrink-0 text-[#7B8184]" })
3556
+ ]
3557
+ }
3558
+ ),
3559
+ contentClassName: "max-h-80 overflow-y-auto min-w-32 p-2",
3560
+ children: availableFontSizes.map((option) => /* @__PURE__ */ jsx7(
3561
+ DropdownMenuItem,
3562
+ {
3563
+ label: option.label,
3564
+ onClick: () => editor.chain().focus().setFontSize(option.value).run(),
3565
+ active: normalizeStyleValue(option.value) === activeFontSize
3566
+ },
3567
+ option.value
3568
+ ))
3569
+ }
3570
+ ),
3571
+ /* @__PURE__ */ jsxs5(
3572
+ DropdownMenu,
3573
+ {
3574
+ contentClassName: "p-1",
3575
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3576
+ }, title: t("toolbar.textStyle"), className: "px-1.5 w-auto gap-0.5", children: /* @__PURE__ */ jsx7(FigmaTextStyleIcon, { className: "h-4 w-4" }) }),
3577
+ children: [
3578
+ /* @__PURE__ */ jsx7(
3579
+ DropdownMenuItem,
3580
+ {
3581
+ icon: Type,
3582
+ label: t("toolbar.normal"),
3583
+ onClick: () => editor.chain().focus().setParagraph().run(),
3584
+ active: editor.isActive("paragraph")
3585
+ }
3586
+ ),
3587
+ /* @__PURE__ */ jsx7(
3588
+ DropdownMenuItem,
3589
+ {
3590
+ icon: Heading1Icon,
3591
+ label: t("toolbar.heading1"),
3592
+ onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
3593
+ active: editor.isActive("heading", { level: 1 }),
3594
+ shortcut: "Ctrl+Alt+1"
3595
+ }
3596
+ ),
3597
+ /* @__PURE__ */ jsx7(
3598
+ DropdownMenuItem,
3599
+ {
3600
+ icon: Heading2Icon,
3601
+ label: t("toolbar.heading2"),
3602
+ onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
3603
+ active: editor.isActive("heading", { level: 2 }),
3604
+ shortcut: "Ctrl+Alt+2"
3605
+ }
3606
+ ),
3607
+ /* @__PURE__ */ jsx7(
3608
+ DropdownMenuItem,
3609
+ {
3610
+ icon: Heading3Icon,
3611
+ label: t("toolbar.heading3"),
3612
+ onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
3613
+ active: editor.isActive("heading", { level: 3 }),
3614
+ shortcut: "Ctrl+Alt+3"
3615
+ }
3616
+ )
3617
+ ]
3618
+ }
3619
+ ),
3620
+ isFull && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3621
+ /* @__PURE__ */ jsxs5(
3622
+ DropdownMenu,
3623
+ {
3624
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3625
+ }, title: t("toolbar.lineHeight"), className: "gap-0.5", children: /* @__PURE__ */ jsx7(FigmaLineHeightIcon, { className: "h-4 w-4" }) }),
3626
+ contentClassName: "max-h-72 overflow-y-auto p-1",
3627
+ children: [
3628
+ /* @__PURE__ */ jsx7(
3629
+ DropdownMenuItem,
3630
+ {
3631
+ icon: Type,
3632
+ label: t("toolbar.lineHeightDefault"),
3633
+ onClick: () => editor.chain().focus().unsetLineHeight().run(),
3634
+ active: !currentLineHeight
3635
+ }
3636
+ ),
3637
+ availableLineHeights.map((option) => /* @__PURE__ */ jsx7(
3638
+ DropdownMenuItem,
3639
+ {
3640
+ label: option.label,
3641
+ onClick: () => editor.chain().focus().setLineHeight(option.value).run(),
3642
+ active: normalizeStyleValue(option.value) === currentLineHeight
3643
+ },
3644
+ option.value
3645
+ ))
3646
+ ]
3647
+ }
3648
+ ),
3649
+ /* @__PURE__ */ jsxs5(
3650
+ DropdownMenu,
3651
+ {
3652
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3653
+ }, title: t("toolbar.letterSpacing"), className: "gap-0.5", children: /* @__PURE__ */ jsx7(FigmaLetterSpacingIcon, { className: "h-4 w-4" }) }),
3654
+ contentClassName: "max-h-72 overflow-y-auto p-1",
3655
+ children: [
3656
+ /* @__PURE__ */ jsx7(
3657
+ DropdownMenuItem,
3658
+ {
3659
+ icon: Type,
3660
+ label: t("toolbar.letterSpacingDefault"),
3661
+ onClick: () => editor.chain().focus().unsetLetterSpacing().run(),
3662
+ active: !currentLetterSpacing
3663
+ }
3664
+ ),
3665
+ availableLetterSpacings.map((option) => /* @__PURE__ */ jsx7(
3666
+ DropdownMenuItem,
3667
+ {
3668
+ label: option.label,
3669
+ onClick: () => editor.chain().focus().setLetterSpacing(option.value).run(),
3670
+ active: normalizeStyleValue(option.value) === currentLetterSpacing
3671
+ },
3672
+ option.value
3673
+ ))
3674
+ ]
3675
+ }
3676
+ )
3677
+ ] }),
3678
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
3679
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ jsx7(FigmaBoldIcon, { className: "h-4 w-4" }) }),
3680
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ jsx7(FigmaItalicIcon, { className: "h-4 w-4" }) }),
3681
+ /* @__PURE__ */ jsx7(
3682
+ ToolbarButton,
3683
+ {
3684
+ onClick: () => editor.chain().focus().toggleUnderline().run(),
3685
+ active: editor.isActive("underline"),
3686
+ title: t("toolbar.underline"),
3687
+ children: /* @__PURE__ */ jsx7(FigmaUnderlineIcon, { className: "h-4 w-4" })
3688
+ }
3689
+ ),
3690
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleStrike().run(), active: editor.isActive("strike"), title: t("toolbar.strike"), children: /* @__PURE__ */ jsx7(FigmaStrikeIcon, { className: "h-4 w-4" }) }),
3691
+ (isMediumFull || isFull) && /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleCode().run(), active: editor.isActive("code"), title: t("toolbar.code"), children: /* @__PURE__ */ jsx7(FigmaCodeIcon, { className: "h-4 w-4" }) }),
3692
+ isFull && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3693
+ /* @__PURE__ */ jsx7(
3694
+ ToolbarButton,
3695
+ {
3696
+ onClick: () => editor.chain().focus().toggleSubscript().run(),
3697
+ active: editor.isActive("subscript"),
3698
+ title: t("toolbar.subscript"),
3699
+ children: /* @__PURE__ */ jsx7(FigmaSubscriptIcon, { className: "h-4 w-4" })
3700
+ }
3701
+ ),
3702
+ /* @__PURE__ */ jsx7(
3703
+ ToolbarButton,
3704
+ {
3705
+ onClick: () => editor.chain().focus().toggleSuperscript().run(),
3706
+ active: editor.isActive("superscript"),
3707
+ title: t("toolbar.superscript"),
3708
+ children: /* @__PURE__ */ jsx7(FigmaSuperscriptIcon, { className: "h-4 w-4" })
3709
+ }
3710
+ )
3711
+ ] }),
3712
+ /* @__PURE__ */ jsx7(
3713
+ DropdownMenu,
3714
+ {
3715
+ contentClassName: "min-w-72",
3716
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => setShowLinkInput(!editor.isActive("link")), active: editor.isActive("link"), title: t("toolbar.link"), children: /* @__PURE__ */ jsx7(FigmaLinkIcon, { className: "h-4 w-4" }) }),
3717
+ children: showLinkInput ? /* @__PURE__ */ jsx7(
3718
+ LinkInput,
3719
+ {
3720
+ initialUrl: String(editor.getAttributes("link").href ?? ""),
3721
+ onSubmit: (url) => {
3722
+ applyEditorLink(editor, url);
3723
+ setShowLinkInput(false);
3724
+ },
3725
+ onCancel: () => setShowLinkInput(false)
3726
+ }
3727
+ ) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
3728
+ /* @__PURE__ */ jsx7(
3729
+ DropdownMenuItem,
3730
+ {
3731
+ icon: LinkIcon,
3732
+ label: t("toolbar.link"),
3733
+ onClick: () => setShowLinkInput(true),
3734
+ active: editor.isActive("link"),
3735
+ closeOnSelect: false
3736
+ }
3737
+ ),
3738
+ /* @__PURE__ */ jsx7(
3739
+ DropdownMenuItem,
3740
+ {
3741
+ icon: Trash2,
3742
+ label: t("toolbar.removeLink"),
3743
+ onClick: () => editor.chain().focus().extendMarkRange("link").unsetLink().run(),
3744
+ disabled: !editor.isActive("link"),
3745
+ destructive: true
3746
+ }
3747
+ )
3748
+ ] })
3749
+ }
3750
+ ),
3751
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
3752
+ /* @__PURE__ */ jsx7(
3753
+ DropdownMenu,
3754
+ {
3755
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3756
+ }, title: t("colors.textColor"), children: /* @__PURE__ */ jsx7(TextColorIcon, { color: currentTextColor }) }),
3757
+ children: /* @__PURE__ */ jsx7(
3758
+ EditorColorPalette,
3759
+ {
3760
+ colors: textColors,
3761
+ currentColor: currentTextColor,
3762
+ onSelect: (color) => {
3763
+ if (color === "inherit") {
3764
+ editor.chain().focus().unsetColor().run();
3765
+ } else {
3766
+ editor.chain().focus().setColor(color).run();
3767
+ }
3768
+ },
3769
+ label: t("colors.textColor")
3770
+ }
3771
+ )
3772
+ }
3773
+ ),
3774
+ /* @__PURE__ */ jsx7(
3775
+ DropdownMenu,
3776
+ {
3777
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3778
+ }, active: editor.isActive("highlight"), title: t("colors.highlight"), children: /* @__PURE__ */ jsx7(HighlightColorIcon, { color: currentHighlightColor }) }),
3779
+ children: /* @__PURE__ */ jsx7(
3780
+ EditorColorPalette,
3781
+ {
3782
+ colors: highlightColors,
3783
+ currentColor: currentHighlightColor,
3784
+ onSelect: (color) => {
3785
+ if (color === "") {
3786
+ editor.chain().focus().unsetHighlight().run();
3787
+ } else {
3788
+ editor.chain().focus().toggleHighlight({ color }).run();
3789
+ }
3790
+ },
3791
+ label: t("colors.highlight")
3792
+ }
3793
+ )
3794
+ }
3795
+ ),
3796
+ (isMediumFull || isFull) && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3797
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
3798
+ /* @__PURE__ */ jsxs5(
3799
+ DropdownMenu,
3800
+ {
3801
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3802
+ }, title: t("toolbar.alignment"), children: /* @__PURE__ */ jsx7(FigmaAlignLeftIcon, { className: "h-4 w-4" }) }),
3803
+ children: [
3804
+ /* @__PURE__ */ jsx7(
3805
+ DropdownMenuItem,
3806
+ {
3807
+ icon: AlignLeft,
3808
+ label: t("toolbar.alignLeft"),
3809
+ onClick: () => editor.chain().focus().setTextAlign("left").run(),
3810
+ active: editor.isActive({ textAlign: "left" })
3811
+ }
3812
+ ),
3813
+ /* @__PURE__ */ jsx7(
3814
+ DropdownMenuItem,
3815
+ {
3816
+ icon: AlignCenter,
3817
+ label: t("toolbar.alignCenter"),
3818
+ onClick: () => editor.chain().focus().setTextAlign("center").run(),
3819
+ active: editor.isActive({ textAlign: "center" })
3820
+ }
3821
+ ),
3822
+ /* @__PURE__ */ jsx7(
3823
+ DropdownMenuItem,
3824
+ {
3825
+ icon: AlignRight,
3826
+ label: t("toolbar.alignRight"),
3827
+ onClick: () => editor.chain().focus().setTextAlign("right").run(),
3828
+ active: editor.isActive({ textAlign: "right" })
3829
+ }
3830
+ ),
3831
+ /* @__PURE__ */ jsx7(
3832
+ DropdownMenuItem,
3833
+ {
3834
+ icon: AlignJustify,
3835
+ label: t("toolbar.justify"),
3836
+ onClick: () => editor.chain().focus().setTextAlign("justify").run(),
3837
+ active: editor.isActive({ textAlign: "justify" })
3838
+ }
3839
+ ),
3840
+ hasTableContext && /* @__PURE__ */ jsxs5(Fragment3, { children: [
3841
+ /* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
3842
+ /* @__PURE__ */ jsx7(
3843
+ DropdownMenuItem,
3844
+ {
3845
+ icon: AlignStartVertical,
3846
+ label: t("tableMenu.alignVerticalTop") || "Align top",
3847
+ onClick: () => applyTableCellAttribute(editor, "verticalAlign", "top"),
3848
+ active: currentCellVerticalAlign === "top"
3849
+ }
3850
+ ),
3851
+ /* @__PURE__ */ jsx7(
3852
+ DropdownMenuItem,
3853
+ {
3854
+ icon: AlignCenterVertical,
3855
+ label: t("tableMenu.alignVerticalMiddle") || "Align middle",
3856
+ onClick: () => applyTableCellAttribute(editor, "verticalAlign", "middle"),
3857
+ active: currentCellVerticalAlign === "middle"
3858
+ }
3859
+ ),
3860
+ /* @__PURE__ */ jsx7(
3861
+ DropdownMenuItem,
3862
+ {
3863
+ icon: AlignEndVertical,
3864
+ label: t("tableMenu.alignVerticalBottom") || "Align bottom",
3865
+ onClick: () => applyTableCellAttribute(editor, "verticalAlign", "bottom"),
3866
+ active: currentCellVerticalAlign === "bottom"
3867
+ }
3868
+ )
3869
+ ] })
3870
+ ]
3871
+ }
3872
+ ),
3873
+ hasTableContext && /* @__PURE__ */ jsxs5(
3874
+ DropdownMenu,
3875
+ {
3876
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3877
+ }, title: t("tableMenu.textDirection"), children: currentCellTextDirection === "vertical" ? /* @__PURE__ */ jsx7(ArrowDown, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx7(ArrowRight, { className: "w-4 h-4" }) }),
3878
+ children: [
3879
+ /* @__PURE__ */ jsx7(
3880
+ DropdownMenuItem,
3881
+ {
3882
+ icon: ArrowRight,
3883
+ label: t("tableMenu.horizontalText"),
3884
+ onClick: () => applyTableCellAttribute(editor, "textDirection", null),
3885
+ active: currentCellTextDirection === "horizontal"
3886
+ }
3887
+ ),
3888
+ /* @__PURE__ */ jsx7(
3889
+ DropdownMenuItem,
3890
+ {
3891
+ icon: ArrowDown,
3892
+ label: t("tableMenu.verticalText"),
3893
+ onClick: () => applyTableCellAttribute(editor, "textDirection", "vertical"),
3894
+ active: currentCellTextDirection === "vertical"
3895
+ }
3896
+ )
3897
+ ]
3898
+ }
3899
+ )
3900
+ ] }),
3901
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
3902
+ /* @__PURE__ */ jsxs5(
3903
+ DropdownMenu,
3904
+ {
3905
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3906
+ }, title: t("toolbar.bulletList"), children: /* @__PURE__ */ jsx7(FigmaListIcon, { className: "h-4 w-4" }) }),
3907
+ children: [
3908
+ /* @__PURE__ */ jsx7(
3909
+ DropdownMenuItem,
3910
+ {
3911
+ icon: ListIcon,
3912
+ label: t("toolbar.bulletList"),
3913
+ onClick: () => editor.chain().focus().toggleBulletList().run(),
3914
+ active: editor.isActive("bulletList"),
3915
+ shortcut: "Ctrl+Shift+8"
3916
+ }
3917
+ ),
3918
+ /* @__PURE__ */ jsx7(
3919
+ DropdownMenuItem,
3920
+ {
3921
+ icon: ListOrderedIcon,
3922
+ label: t("toolbar.orderedList"),
3923
+ onClick: () => editor.chain().focus().toggleOrderedList().run(),
3924
+ active: editor.isActive("orderedList"),
3925
+ shortcut: "Ctrl+Shift+7"
3926
+ }
3927
+ ),
3928
+ /* @__PURE__ */ jsx7(
3929
+ DropdownMenuItem,
3930
+ {
3931
+ icon: ListTodo,
3932
+ label: t("toolbar.taskList"),
3933
+ onClick: () => editor.chain().focus().toggleTaskList().run(),
3934
+ active: editor.isActive("taskList"),
3935
+ shortcut: "Ctrl+Shift+9"
3936
+ }
3937
+ )
3938
+ ]
3939
+ }
3940
+ ),
3941
+ /* @__PURE__ */ jsx7(
3942
+ ToolbarButton,
3943
+ {
3944
+ onClick: () => editor.chain().focus().decreaseIndent().run(),
3945
+ disabled: !editorUiState.can.decreaseIndent,
3946
+ title: t("toolbar.decreaseIndent"),
3947
+ children: /* @__PURE__ */ jsx7(IndentDecrease, { className: "h-4 w-4" })
3948
+ }
3949
+ ),
3950
+ /* @__PURE__ */ jsx7(
3951
+ ToolbarButton,
3952
+ {
3953
+ onClick: () => editor.chain().focus().increaseIndent().run(),
3954
+ disabled: !editorUiState.can.increaseIndent,
3955
+ title: t("toolbar.increaseIndent"),
3956
+ children: /* @__PURE__ */ jsx7(IndentIncrease, { className: "h-4 w-4" })
3957
+ }
3958
+ ),
3959
+ /* @__PURE__ */ jsxs5(
3960
+ DropdownMenu,
3961
+ {
3962
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3963
+ }, active: editor.isActive("formCheckbox"), title: t("slashCommand.formCheckbox"), children: /* @__PURE__ */ jsx7(SquareCheckBig, { className: "w-4 h-4" }) }),
3964
+ children: [
3965
+ /* @__PURE__ */ jsx7(
3966
+ DropdownMenuItem,
3967
+ {
3968
+ icon: SquareCheckBig,
3969
+ label: t("slashCommand.formCheckbox"),
3970
+ onClick: () => editor.chain().focus().setFormCheckbox().run()
3971
+ }
3972
+ ),
3973
+ /* @__PURE__ */ jsx7(
3974
+ DropdownMenuItem,
3975
+ {
3976
+ icon: CircleCheckBig,
3977
+ label: t("slashCommand.roundCheckbox"),
3978
+ onClick: () => editor.chain().focus().setFormCheckbox({ variant: "circle" }).run()
3979
+ }
3980
+ )
3981
+ ]
3982
+ }
3983
+ ),
3984
+ isMediumFull && /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().toggleBlockquote().run(), active: editor.isActive("blockquote"), title: t("toolbar.quote"), children: /* @__PURE__ */ jsx7(FigmaQuoteIcon, { className: "h-4 w-4" }) }),
3985
+ isFull && /* @__PURE__ */ jsxs5(
3986
+ DropdownMenu,
3987
+ {
3988
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
3989
+ }, title: t("toolbar.quote"), children: /* @__PURE__ */ jsx7(FigmaQuoteIcon, { className: "h-4 w-4" }) }),
3990
+ children: [
3991
+ /* @__PURE__ */ jsx7(
3992
+ DropdownMenuItem,
3993
+ {
3994
+ icon: QuoteIcon,
3995
+ label: t("toolbar.quote"),
3996
+ onClick: () => editor.chain().focus().toggleBlockquote().run(),
3997
+ active: editor.isActive("blockquote"),
3998
+ shortcut: "Ctrl+Shift+B"
3999
+ }
4000
+ ),
4001
+ /* @__PURE__ */ jsx7(
4002
+ DropdownMenuItem,
4003
+ {
4004
+ icon: FileCode,
4005
+ label: t("toolbar.codeBlock"),
4006
+ onClick: () => editor.chain().focus().toggleCodeBlock().run(),
4007
+ active: editor.isActive("codeBlock"),
4008
+ shortcut: "Ctrl+Alt+C"
4009
+ }
4010
+ )
4011
+ ]
4012
+ }
4013
+ ),
4014
+ (isMediumFull || isFull) && /* @__PURE__ */ jsx7(
4015
+ DropdownMenu,
4016
+ {
4017
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
4018
+ }, title: t("toolbar.image"), children: /* @__PURE__ */ jsx7(FigmaImageIcon, { className: "h-4 w-4" }) }),
4019
+ children: showImageInput ? /* @__PURE__ */ jsx7(
4020
+ ImageInput,
4021
+ {
4022
+ onSubmit: (url, alt) => {
4023
+ editor.chain().focus().setImage({ src: url, alt }).run();
4024
+ setShowImageInput(false);
4025
+ },
4026
+ onCancel: () => setShowImageInput(false)
4027
+ }
4028
+ ) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
4029
+ /* @__PURE__ */ jsx7(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true), closeOnSelect: false }),
4030
+ /* @__PURE__ */ jsx7(
4031
+ DropdownMenuItem,
4032
+ {
4033
+ icon: Upload,
4034
+ label: isUploadingImage ? t("imageInput.uploading") : t("imageInput.uploadTab"),
4035
+ disabled: isUploadingImage,
4036
+ onClick: () => fileInputRef.current?.click(),
4037
+ closeOnSelect: false
4038
+ }
4039
+ ),
4040
+ imageUploadError && /* @__PURE__ */ jsx7(DropdownMenuItem, { label: imageUploadError, disabled: true, destructive: true }),
4041
+ /* @__PURE__ */ jsx7(
4042
+ "input",
4043
+ {
4044
+ ref: fileInputRef,
4045
+ type: "file",
4046
+ accept: "image/*",
4047
+ multiple: true,
4048
+ className: "hidden",
4049
+ onChange: (e) => {
4050
+ const files = Array.from(e.target.files ?? []);
4051
+ e.target.value = "";
4052
+ void insertImageFiles(files);
4053
+ }
4054
+ }
4055
+ ),
4056
+ /* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
4057
+ /* @__PURE__ */ jsx7(
4058
+ DropdownMenuItem,
4059
+ {
4060
+ icon: AlignCenter,
4061
+ label: t("toolbar.imageLayoutBlock"),
4062
+ onClick: () => applyImageLayout(editor, "block"),
4063
+ active: isImageSelected && imageLayout === "block",
4064
+ disabled: !isImageSelected
4065
+ }
4066
+ ),
4067
+ /* @__PURE__ */ jsx7(
4068
+ DropdownMenuItem,
4069
+ {
4070
+ icon: AlignLeft,
4071
+ label: t("toolbar.imageLayoutLeft"),
4072
+ onClick: () => applyImageLayout(editor, "left"),
4073
+ active: isImageSelected && imageLayout === "left",
4074
+ disabled: !isImageSelected
4075
+ }
4076
+ ),
4077
+ /* @__PURE__ */ jsx7(
4078
+ DropdownMenuItem,
4079
+ {
4080
+ icon: AlignRight,
4081
+ label: t("toolbar.imageLayoutRight"),
4082
+ onClick: () => applyImageLayout(editor, "right"),
4083
+ active: isImageSelected && imageLayout === "right",
4084
+ disabled: !isImageSelected
4085
+ }
4086
+ ),
4087
+ /* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
4088
+ /* @__PURE__ */ jsx7(
4089
+ DropdownMenuItem,
4090
+ {
4091
+ label: t("toolbar.imageWidthSm"),
4092
+ onClick: () => applyImageWidthPreset(editor, "sm"),
4093
+ active: isImageSelected && imageWidthPreset === "sm",
4094
+ disabled: !isImageSelected
4095
+ }
4096
+ ),
4097
+ /* @__PURE__ */ jsx7(
4098
+ DropdownMenuItem,
4099
+ {
4100
+ label: t("toolbar.imageWidthMd"),
4101
+ onClick: () => applyImageWidthPreset(editor, "md"),
4102
+ active: isImageSelected && imageWidthPreset === "md",
4103
+ disabled: !isImageSelected
4104
+ }
4105
+ ),
4106
+ /* @__PURE__ */ jsx7(
4107
+ DropdownMenuItem,
4108
+ {
4109
+ label: t("toolbar.imageWidthLg"),
4110
+ onClick: () => applyImageWidthPreset(editor, "lg"),
4111
+ active: isImageSelected && imageWidthPreset === "lg",
4112
+ disabled: !isImageSelected
4113
+ }
4114
+ ),
4115
+ /* @__PURE__ */ jsx7("div", { className: "my-1 border-t" }),
4116
+ /* @__PURE__ */ jsx7(
4117
+ DropdownMenuItem,
4118
+ {
4119
+ icon: RotateCcw,
4120
+ label: t("toolbar.imageResetSize"),
4121
+ onClick: () => resetImageSize(editor),
4122
+ disabled: !isImageSelected
4123
+ }
4124
+ ),
4125
+ /* @__PURE__ */ jsx7(
4126
+ DropdownMenuItem,
4127
+ {
4128
+ icon: Trash2,
4129
+ label: t("toolbar.imageDelete"),
4130
+ onClick: () => deleteSelectedImage(editor),
4131
+ disabled: !isImageSelected,
4132
+ destructive: true
4133
+ }
4134
+ )
4135
+ ] })
4136
+ }
4137
+ ),
4138
+ (isMediumFull || isFull) && /* @__PURE__ */ jsx7(
4139
+ DropdownMenu,
4140
+ {
4141
+ isOpen: isTableMenuOpen,
4142
+ onOpenChange: setIsTableMenuOpen,
4143
+ trigger: /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => {
4144
+ }, title: t("toolbar.table"), children: /* @__PURE__ */ jsx7(FigmaTableIcon, { className: "h-4 w-4" }) }),
4145
+ contentClassName: "p-2 min-w-56",
4146
+ children: /* @__PURE__ */ jsx7(
4147
+ TableInsertGrid,
4148
+ {
4149
+ insertLabel: t("tableMenu.insertTable"),
4150
+ previewTemplate: t("tableMenu.gridPreview"),
4151
+ onInsert: (rows, cols) => {
4152
+ editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
4153
+ setIsTableMenuOpen(false);
4154
+ }
4155
+ }
4156
+ )
4157
+ }
4158
+ ),
4159
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
4160
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: t("toolbar.undo"), children: /* @__PURE__ */ jsx7(FigmaUndoIcon, { className: "h-4 w-4" }) }),
4161
+ /* @__PURE__ */ jsx7(ToolbarButton, { onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: t("toolbar.redo"), children: /* @__PURE__ */ jsx7(FigmaRedoIcon, { className: "h-4 w-4" }) }),
4162
+ hasTableContext && /* @__PURE__ */ jsxs5(Fragment3, { children: [
4163
+ /* @__PURE__ */ jsx7(ToolbarDivider, {}),
4164
+ /* @__PURE__ */ jsx7(
4165
+ ToolbarButton,
4166
+ {
4167
+ onClick: () => editor.chain().focus().addColumnBefore().run(),
4168
+ disabled: !editor.can().addColumnBefore(),
4169
+ title: t("tableMenu.addColumnBefore"),
4170
+ children: /* @__PURE__ */ jsx7(ArrowLeft, { className: "w-4 h-4" })
4171
+ }
4172
+ ),
4173
+ /* @__PURE__ */ jsx7(
4174
+ ToolbarButton,
4175
+ {
4176
+ onClick: () => editor.chain().focus().addColumnAfter().run(),
4177
+ disabled: !editor.can().addColumnAfter(),
4178
+ title: t("tableMenu.addColumnAfter"),
4179
+ children: /* @__PURE__ */ jsx7(ArrowRight, { className: "w-4 h-4" })
4180
+ }
4181
+ ),
4182
+ /* @__PURE__ */ jsx7(
4183
+ ToolbarButton,
4184
+ {
4185
+ onClick: () => editor.chain().focus().addRowBefore().run(),
4186
+ disabled: !editor.can().addRowBefore(),
4187
+ title: t("tableMenu.addRowBefore"),
4188
+ children: /* @__PURE__ */ jsx7(ArrowUp, { className: "w-4 h-4" })
4189
+ }
4190
+ ),
4191
+ /* @__PURE__ */ jsx7(
4192
+ ToolbarButton,
4193
+ {
4194
+ onClick: () => editor.chain().focus().addRowAfter().run(),
4195
+ disabled: !editor.can().addRowAfter(),
4196
+ title: t("tableMenu.addRowAfter"),
4197
+ children: /* @__PURE__ */ jsx7(ArrowDown, { className: "w-4 h-4" })
4198
+ }
4199
+ ),
4200
+ /* @__PURE__ */ jsx7(
4201
+ ToolbarButton,
4202
+ {
4203
+ onClick: () => {
4204
+ if (canSplitCell) {
4205
+ editor.chain().focus().splitCell().run();
4206
+ return;
4207
+ }
4208
+ mergeTableCellsPreservingColumnWidths(editor);
4209
+ },
4210
+ active: canSplitCell,
4211
+ disabled: !canMergeCells && !canSplitCell,
4212
+ title: canSplitCell ? t("tableMenu.splitCell") : t("tableMenu.mergeCells"),
4213
+ children: /* @__PURE__ */ jsx7(TableCellsMerge, { className: "w-4 h-4" })
4214
+ }
4215
+ )
4216
+ ] })
4217
+ ]
4218
+ }
4219
+ );
4220
+ };
4221
+
4222
+ // src/components/UEditor/editor-styles.ts
4223
+ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
4224
+ "prose prose-sm sm:prose dark:prose-invert max-w-none",
4225
+ "focus:outline-none",
4226
+ "px-4 py-4",
4227
+ "[&_.is-editor-empty]:before:content-[attr(data-placeholder)]",
4228
+ "[&_.is-editor-empty]:before:text-muted-foreground/50",
4229
+ "[&_.is-editor-empty]:before:float-left",
4230
+ "[&_.is-editor-empty]:before:pointer-events-none",
4231
+ "[&_.is-editor-empty]:before:h-0",
4232
+ "[&_.ProseMirror-gapcursor]:pointer-events-none",
4233
+ "[&_.ProseMirror-gapcursor]:absolute",
4234
+ "[&_.ProseMirror-gapcursor]:hidden",
4235
+ "[&_.ProseMirror-gapcursor:after]:content-['']",
4236
+ "[&_.ProseMirror-gapcursor:after]:block",
4237
+ "[&_.ProseMirror-gapcursor:after]:absolute",
4238
+ "[&_.ProseMirror-gapcursor:after]:top-[-2px]",
4239
+ "[&_.ProseMirror-gapcursor:after]:w-8",
4240
+ "[&_.ProseMirror-gapcursor:after]:border-t-2",
4241
+ "[&_.ProseMirror-gapcursor:after]:border-primary",
4242
+ "[&.ProseMirror-focused_.ProseMirror-gapcursor]:block",
4243
+ "[&_ul[data-type='taskList']]:list-none",
4244
+ "[&_ul[data-type='taskList']]:pl-0",
4245
+ "[&_ul[data-type='taskList']_li]:flex",
4246
+ "[&_ul[data-type='taskList']_li]:items-start",
4247
+ "[&_ul[data-type='taskList']_li]:gap-2",
4248
+ "[&_ul[data-type='taskList']_li>label]:mt-0.5",
4249
+ "[&_ul[data-type='taskList']_li>label>input]:w-4",
4250
+ "[&_ul[data-type='taskList']_li>label>input]:h-4",
4251
+ "[&_ul[data-type='taskList']_li>label>input]:rounded",
4252
+ "[&_ul[data-type='taskList']_li>label>input]:border-2",
4253
+ "[&_ul[data-type='taskList']_li>label>input]:border-primary/50",
4254
+ "[&_ul[data-type='taskList']_li>label>input]:accent-primary",
4255
+ "[&_pre]:bg-muted/40!",
4256
+ "[&_pre]:text-foreground!",
4257
+ "[&_pre]:border!",
4258
+ "[&_pre]:border-border/60!",
4259
+ "[&_pre_code]:bg-transparent!",
4260
+ "[&_.tableWrapper]:overflow-x-auto",
4261
+ "[&_.tableWrapper]:pb-1.5",
4262
+ "[&_.tableWrapper]:select-text",
4263
+ "[&_.tableWrapper]:[scrollbar-width:thin]",
4264
+ "[&_.tableWrapper]:[scrollbar-color:hsl(var(--border))_transparent]",
4265
+ "[&_.tableWrapper::-webkit-scrollbar]:h-2",
4266
+ "[&_.tableWrapper::-webkit-scrollbar]:w-2",
4267
+ "[&_.tableWrapper::-webkit-scrollbar-track]:rounded-full",
4268
+ "[&_.tableWrapper::-webkit-scrollbar-track]:bg-transparent",
4269
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:rounded-full",
4270
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border",
4271
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-solid",
4272
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:border-transparent",
4273
+ "[&_.tableWrapper::-webkit-scrollbar-thumb]:bg-border/70",
4274
+ "[&_.tableWrapper::-webkit-scrollbar-thumb:hover]:bg-muted-foreground/45",
4275
+ "[&_table]:w-auto",
4276
+ "[&_table]:table-fixed",
4277
+ "[&_table]:overflow-hidden",
4278
+ "[&_table]:select-text",
4279
+ "[&_table[data-table-align]]:w-max",
4280
+ "[&_table[data-table-align]]:max-w-full",
4281
+ "[&_table[data-table-align='center']]:mx-auto",
4282
+ "[&_table[data-table-align='right']]:ml-auto",
4283
+ "[&_table[data-table-align='right']]:mr-0",
4284
+ "[&_td]:relative",
4285
+ "[&_td]:align-top",
4286
+ "[&_td]:box-border",
4287
+ "[&_td]:select-text",
4288
+ "[&_td]:px-2",
4289
+ "[&_td]:py-0",
4290
+ "[&_td_p]:my-0",
4291
+ "[&_th]:relative",
4292
+ "[&_th]:align-top",
4293
+ "[&_th]:box-border",
4294
+ "[&_th]:select-text",
4295
+ "[&_th]:px-2",
4296
+ "[&_th]:py-0",
4297
+ "[&_th_p]:my-0",
4298
+ "[&_td[data-formula]]:pr-7",
4299
+ "[&_th[data-formula]]:pr-7",
4300
+ "[&_td[data-formula]]:before:pointer-events-none",
4301
+ "[&_th[data-formula]]:before:pointer-events-none",
4302
+ "[&_td[data-formula]]:before:absolute",
4303
+ "[&_th[data-formula]]:before:absolute",
4304
+ "[&_td[data-formula]]:before:right-1",
4305
+ "[&_th[data-formula]]:before:right-1",
4306
+ "[&_td[data-formula]]:before:top-1",
4307
+ "[&_th[data-formula]]:before:top-1",
4308
+ "[&_td[data-formula]]:before:z-[1]",
4309
+ "[&_th[data-formula]]:before:z-[1]",
4310
+ "[&_td[data-formula]]:before:rounded-sm",
4311
+ "[&_th[data-formula]]:before:rounded-sm",
4312
+ "[&_td[data-formula]]:before:bg-primary/10",
4313
+ "[&_th[data-formula]]:before:bg-primary/10",
4314
+ "[&_td[data-formula]]:before:px-1",
4315
+ "[&_th[data-formula]]:before:px-1",
4316
+ "[&_td[data-formula]]:before:font-mono",
4317
+ "[&_th[data-formula]]:before:font-mono",
4318
+ "[&_td[data-formula]]:before:text-[9px]",
4319
+ "[&_th[data-formula]]:before:text-[9px]",
4320
+ "[&_td[data-formula]]:before:font-semibold",
4321
+ "[&_th[data-formula]]:before:font-semibold",
4322
+ "[&_td[data-formula]]:before:leading-4",
4323
+ "[&_th[data-formula]]:before:leading-4",
4324
+ "[&_td[data-formula]]:before:text-primary",
4325
+ "[&_th[data-formula]]:before:text-primary",
4326
+ "[&_td[data-formula]]:before:content-['fx']",
4327
+ "[&_th[data-formula]]:before:content-['fx']",
4328
+ "[&_td[data-formula-state='error']]:bg-destructive/5",
4329
+ "[&_th[data-formula-state='error']]:bg-destructive/5",
4330
+ "[&_td[data-formula-state='error']]:before:bg-destructive/10",
4331
+ "[&_th[data-formula-state='error']]:before:bg-destructive/10",
4332
+ "[&_td[data-formula-state='error']]:before:text-destructive",
4333
+ "[&_th[data-formula-state='error']]:before:text-destructive",
4334
+ "[&_td[colwidth]]:min-w-0",
4335
+ "[&_th[colwidth]]:min-w-0",
4336
+ "[&_td[data-colwidth]]:min-w-0",
4337
+ "[&_th[data-colwidth]]:min-w-0",
4338
+ "[&_.selectedCell]:after:content-['']",
4339
+ "[&_.selectedCell]:after:absolute",
4340
+ "[&_.selectedCell]:after:inset-0",
4341
+ "[&_.selectedCell]:after:z-[2]",
4342
+ "[&_.selectedCell]:after:bg-primary/15",
4343
+ "[&_.selectedCell]:after:pointer-events-none",
4344
+ "[&_.column-resize-handle]:pointer-events-auto",
4345
+ "[&_.column-resize-handle]:cursor-col-resize",
4346
+ "[&_.column-resize-handle]:absolute",
4347
+ "[&_.column-resize-handle]:top-[-1px]",
4348
+ "[&_.column-resize-handle]:bottom-[-1px]",
4349
+ "[&_.column-resize-handle]:right-[-5px]",
4350
+ "[&_.column-resize-handle]:z-30",
4351
+ "[&_.column-resize-handle]:w-2.5",
4352
+ "[&_.column-resize-handle]:bg-transparent",
4353
+ "[&_.column-resize-handle]:rounded-none",
4354
+ "[&_.column-resize-handle]:opacity-0",
4355
+ "[&_.column-resize-handle]:transition-opacity",
4356
+ "[&_.column-resize-handle]:after:absolute",
4357
+ "[&_.column-resize-handle]:after:top-0",
4358
+ "[&_.column-resize-handle]:after:bottom-0",
4359
+ "[&_.column-resize-handle]:after:left-1/2",
4360
+ "[&_.column-resize-handle]:after:w-0.5",
4361
+ "[&_.column-resize-handle]:after:-translate-x-1/2",
4362
+ "[&_.column-resize-handle]:after:rounded-full",
4363
+ "[&_.column-resize-handle]:after:bg-primary/75",
4364
+ "[&_.column-resize-handle]:after:content-['']",
4365
+ "[&.resize-cursor_.column-resize-handle]:opacity-100",
4366
+ "[&.resize-cursor_.column-resize-handle]:after:bg-primary",
4367
+ "[&_.column-resize-dragging]:min-w-0",
4368
+ "[&.resize-cursor]:cursor-col-resize",
4369
+ "[&.resize-row-cursor]:cursor-row-resize",
4370
+ "[&_img.ProseMirror-selectednode]:ring-2",
4371
+ "[&_img.ProseMirror-selectednode]:ring-primary/60",
4372
+ "[&_img.ProseMirror-selectednode]:ring-offset-2",
4373
+ "[&_img.ProseMirror-selectednode]:ring-offset-background",
4374
+ "[&_hr]:border-t-2",
4375
+ "[&_hr]:border-primary/30",
4376
+ "[&_hr]:my-8",
4377
+ "[&_h1]:text-3xl",
4378
+ "[&_h1]:font-bold",
4379
+ "[&_h1]:mt-6",
4380
+ "[&_h1]:mb-4",
4381
+ "[&_h1]:text-foreground",
4382
+ "[&_h2]:text-2xl",
4383
+ "[&_h2]:font-semibold",
4384
+ "[&_h2]:mt-5",
4385
+ "[&_h2]:mb-3",
4386
+ "[&_h2]:text-foreground",
4387
+ "[&_h3]:text-xl",
4388
+ "[&_h3]:font-semibold",
4389
+ "[&_h3]:mt-4",
4390
+ "[&_h3]:mb-2",
4391
+ "[&_h3]:text-foreground",
4392
+ "[&_ul:not([data-type='taskList'])]:list-disc",
4393
+ "[&_ul:not([data-type='taskList'])]:pl-6",
4394
+ "[&_ul:not([data-type='taskList'])]:my-3",
4395
+ "[&_ol]:list-decimal",
4396
+ "[&_ol]:pl-6",
4397
+ "[&_ol]:my-3",
4398
+ "[&_li]:my-1",
4399
+ "[&_li]:pl-1",
4400
+ "[&_li_p]:my-0",
4401
+ "[&_blockquote]:border-l-4",
4402
+ "[&_blockquote]:border-primary",
4403
+ "[&_blockquote]:pl-4",
4404
+ "[&_blockquote]:py-2",
4405
+ "[&_blockquote]:my-4",
4406
+ "[&_blockquote]:bg-muted/30",
4407
+ "[&_blockquote]:rounded-r-lg",
4408
+ "[&_blockquote]:italic",
4409
+ "[&_blockquote]:text-muted-foreground",
4410
+ "[&_blockquote_p]:my-0",
4411
+ "[&_[data-image-layout='left']+p]:mt-1",
4412
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
4413
+ "[&_[data-image-layout='right']+p]:mt-1",
4414
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
4415
+ "max-md:[&_[data-image-layout='left']]:float-none",
4416
+ "max-md:[&_[data-image-layout='left']]:mr-0",
4417
+ "max-md:[&_[data-image-layout='left']]:ml-0",
4418
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
4419
+ "max-md:[&_[data-image-layout='right']]:float-none",
4420
+ "max-md:[&_[data-image-layout='right']]:mr-0",
4421
+ "max-md:[&_[data-image-layout='right']]:ml-0",
4422
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
4423
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
4424
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
4425
+ );
4426
+
4427
+ export {
4428
+ formControlSizeStyles,
4429
+ formControlFixedClass,
4430
+ formControlValueClass,
4431
+ getBorderRadiusClass,
4432
+ getPanelBorderRadiusClass,
4433
+ useUnderverseUIConfig,
4434
+ UnderverseConfigProvider,
4435
+ shadcnAnimationStyles,
4436
+ useShadCNAnimations,
4437
+ injectAnimationStyles,
4438
+ getAnimationStyles,
4439
+ Popover,
4440
+ useDropdownMenuClose,
4441
+ DropdownMenu,
4442
+ DropdownMenuItem,
4443
+ DropdownMenuSeparator,
4444
+ DropdownMenuSub,
4445
+ SelectDropdown,
4446
+ DropdownMenu_default,
4447
+ isSafeUEditorUrl,
4448
+ sanitizeUEditorUrl,
4449
+ DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
4450
+ DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
4451
+ ClipboardImages,
4452
+ DEFAULT_TABLE_ROW_HEIGHT,
4453
+ MIN_TABLE_ROW_HEIGHT,
4454
+ COLUMN_RESIZE_LINE_THICKNESS,
4455
+ ROW_RESIZE_LINE_THICKNESS,
4456
+ UEDITOR_TABLE_LAYOUT_CHANGE_EVENT,
4457
+ findTableRowNodeInfo,
4458
+ resolveEventElement,
4459
+ isPointOverRenderedText,
4460
+ getSelectionTableCell,
4461
+ isRowResizeHotspot,
4462
+ isColumnResizeHotspot,
4463
+ getRelativeBoundaryMetrics,
4464
+ getRelativeCellMetrics,
4465
+ getRelativeSelectedCellsMetrics,
4466
+ findTableNodeInfoFromState,
4467
+ applyTableAlignment,
4468
+ dispatchTableLayoutChange,
4469
+ mergeTableCellsPreservingColumnWidths,
4470
+ runTableCommandAtCellPos,
4471
+ duplicateTableRowAt,
4472
+ clearTableRowAt,
4473
+ duplicateTableColumnAt,
4474
+ clearTableColumnAt,
4475
+ expandTableFromCell,
4476
+ TextColorIcon,
4477
+ HighlightColorIcon,
4478
+ CellBgColorIcon,
4479
+ CellBorderIcon,
4480
+ useEditorColors,
4481
+ EditorColorPalette,
4482
+ applyImageLayout,
4483
+ applyImageWidthPreset,
4484
+ resetImageSize,
4485
+ deleteSelectedImage,
4486
+ LinkInput,
4487
+ ImageInput,
4488
+ applyEditorLink,
4489
+ normalizeStyleValue,
4490
+ getDefaultFontSizes,
4491
+ getDefaultLineHeights,
4492
+ getTableAnchorPos,
4493
+ EditorUiRenderStateProvider,
4494
+ useSharedEditorUiRenderState,
4495
+ fileToDataUrl2 as fileToDataUrl,
4496
+ ToolbarButton,
4497
+ TableInsertGrid,
4498
+ EditorToolbar,
4499
+ UEDITOR_PROSEMIRROR_CLASS_NAME
4500
+ };
4501
+ //# sourceMappingURL=chunk-MJV7ETJM.js.map