@underverse-ui/underverse 1.0.140 → 1.0.142

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -747,6 +747,7 @@ interface DropdownMenuItemProps {
747
747
  active?: boolean;
748
748
  shortcut?: string;
749
749
  className?: string;
750
+ closeOnSelect?: boolean;
750
751
  }
751
752
  declare const DropdownMenuItem: React__default.FC<DropdownMenuItemProps>;
752
753
  declare const DropdownMenuSeparator: React__default.FC<{
package/dist/index.d.ts CHANGED
@@ -747,6 +747,7 @@ interface DropdownMenuItemProps {
747
747
  active?: boolean;
748
748
  shortcut?: string;
749
749
  className?: string;
750
+ closeOnSelect?: boolean;
750
751
  }
751
752
  declare const DropdownMenuItem: React__default.FC<DropdownMenuItemProps>;
752
753
  declare const DropdownMenuSeparator: React__default.FC<{
package/dist/index.js CHANGED
@@ -967,13 +967,13 @@ var vi_default = {
967
967
  alignRight: "C\u0103n ph\u1EA3i"
968
968
  },
969
969
  ValidationInput: {
970
- required: "Tr\u01B0\u1EDDng n\xE0y l\xE0 b\u1EAFt bu\u1ED9c",
971
- typeMismatch: "\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7",
970
+ required: "Tr\u01B0\u1EDDng n\xE0y l\xE0 b\u1EAFt bu\u1ED9c.",
971
+ typeMismatch: "\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7.",
972
972
  pattern: "Sai m\u1EABu",
973
973
  tooShort: "Qu\xE1 ng\u1EAFn",
974
974
  tooLong: "Qu\xE1 d\xE0i",
975
- rangeUnderflow: "Nh\u1ECF h\u01A1n gi\xE1 tr\u1ECB t\u1ED1i thi\u1EC3u",
976
- rangeOverflow: "L\u1EDBn h\u01A1n gi\xE1 tr\u1ECB t\u1ED1i \u0111a",
975
+ rangeUnderflow: "Gi\xE1 tr\u1ECB nh\u1ECF h\u01A1n m\u1EE9c t\u1ED1i thi\u1EC3u.",
976
+ rangeOverflow: "Gi\xE1 tr\u1ECB v\u01B0\u1EE3t qu\xE1 m\u1EE9c t\u1ED1i \u0111a.",
977
977
  stepMismatch: "Sai b\u01B0\u1EDBc",
978
978
  badInput: "Gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7",
979
979
  invalid: "Gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7"
@@ -1008,7 +1008,7 @@ var vi_default = {
1008
1008
  start: "B\u1EAFt \u0111\u1EA7u",
1009
1009
  end: "K\u1EBFt th\xFAc",
1010
1010
  resourcesHeader: "T\xE0i nguy\xEAn",
1011
- expandGroup: "M\u1EDF nh\xF3m",
1011
+ expandGroup: "M\u1EDF r\u1ED9ng nh\xF3m",
1012
1012
  collapseGroup: "Thu g\u1ECDn nh\xF3m",
1013
1013
  more: "+{n} th\xEAm",
1014
1014
  sprints: "{n} sprint",
@@ -1043,9 +1043,9 @@ var vi_default = {
1043
1043
  uploadedFiles: "T\u1EC7p \u0111\xE3 t\u1EA3i l\xEAn",
1044
1044
  clearAll: "X\xF3a t\u1EA5t c\u1EA3",
1045
1045
  fileTooLarge: "v\u01B0\u1EE3t qu\xE1 gi\u1EDBi h\u1EA1n k\xEDch th\u01B0\u1EDBc",
1046
- maxFilesReached: "\u0110\xE3 \u0111\u1EA1t gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EC7p",
1047
- uploadSuccess: "\u0111\xE3 t\u1EA3i l\xEAn th\xE0nh c\xF4ng",
1048
- uploadFailed: "t\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"
1046
+ maxFilesReached: "\u0110\xE3 \u0111\u1EA1t gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EC7p.",
1047
+ uploadSuccess: "\u0110\xE3 t\u1EA3i l\xEAn th\xE0nh c\xF4ng.",
1048
+ uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i."
1049
1049
  },
1050
1050
  UEditor: {
1051
1051
  placeholder: "Nh\u1EADp '/' \u0111\u1EC3 m\u1EDF l\u1EC7nh, ho\u1EB7c b\u1EAFt \u0111\u1EA7u vi\u1EBFt...",
@@ -1246,9 +1246,9 @@ var vi_default = {
1246
1246
  table: "B\u1EA3ng",
1247
1247
  save: "L\u01B0u",
1248
1248
  exportHtml: "Xu\u1EA5t HTML",
1249
- undo: "Hu\u1EF7 thao t\xE1c",
1249
+ undo: "Ho\xE0n t\xE1c",
1250
1250
  undoShortcut: "Ctrl+Z",
1251
- redo: "Ho\xE0n t\xE1c",
1251
+ redo: "L\xE0m l\u1EA1i",
1252
1252
  redoShortcut: "Ctrl+Y",
1253
1253
  cut: "C\u1EAFt",
1254
1254
  cutShortcut: "Ctrl+X",
@@ -1278,7 +1278,7 @@ var vi_default = {
1278
1278
  superscript: "Tr\xEAn d\xF2ng",
1279
1279
  subscript: "D\u01B0\u1EDBi d\xF2ng",
1280
1280
  code: "M\xE3",
1281
- wrap: "Bao",
1281
+ wrap: "Ng\u1EAFt d\xF2ng",
1282
1282
  align: "C\u0103n ch\u1EC9nh",
1283
1283
  clearFormatting: "Xo\xE1 \u0111\u1ECBnh d\u1EA1ng",
1284
1284
  paragraph: "\u0110o\u1EA1n v\u0103n",
@@ -1418,10 +1418,10 @@ var ko_default = {
1418
1418
  supportedFormats: "\uBAA8\uB4E0 \uD30C\uC77C \uD615\uC2DD \uC9C0\uC6D0",
1419
1419
  uploadedFiles: "\uC5C5\uB85C\uB4DC\uB41C \uD30C\uC77C",
1420
1420
  clearAll: "\uBAA8\uB450 \uC9C0\uC6B0\uAE30",
1421
- fileTooLarge: "\uD06C\uAE30 \uC81C\uD55C \uCD08\uACFC",
1422
- maxFilesReached: "\uCD5C\uB300 \uD30C\uC77C \uC218 \uB3C4\uB2EC",
1423
- uploadSuccess: "\uC5C5\uB85C\uB4DC \uC131\uACF5",
1424
- uploadFailed: "\uC5C5\uB85C\uB4DC \uC2E4\uD328"
1421
+ fileTooLarge: "\uD30C\uC77C \uD06C\uAE30\uAC00 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
1422
+ maxFilesReached: "\uCD5C\uB300 \uD30C\uC77C \uC218\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4.",
1423
+ uploadSuccess: "\uC5C5\uB85C\uB4DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
1424
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1425
1425
  },
1426
1426
  UEditor: {
1427
1427
  placeholder: "\uBA85\uB839\uC5B4\uB294 '/'\uB97C \uC785\uB825\uD558\uAC70\uB098 \uC785\uB825\uC744 \uC2DC\uC791\uD558\uC138\uC694...",
@@ -1609,7 +1609,7 @@ var ko_default = {
1609
1609
  fileCard: {
1610
1610
  unnamed: "\uC774\uB984 \uC5C6\uB294 \uD30C\uC77C",
1611
1611
  uploading: "(\uC5C5\uB85C\uB4DC \uC911...)",
1612
- uploadFailed: "\uC5C5\uB85C\uB4DC \uC2E4\uD328"
1612
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1613
1613
  },
1614
1614
  menubar: {
1615
1615
  file: "\uD30C\uC77C",
@@ -1620,7 +1620,7 @@ var ko_default = {
1620
1620
  tools: "\uB3C4\uAD6C",
1621
1621
  table: "\uD45C",
1622
1622
  save: "\uC800\uC7A5",
1623
- exportHtml: "HTML \uB0B4\uBCF4\uB0B4\uAE30",
1623
+ exportHtml: "HTML \uCD94\uCD9C",
1624
1624
  undo: "\uC2E4\uD589 \uCDE8\uC18C",
1625
1625
  undoShortcut: "Ctrl+Z",
1626
1626
  redo: "\uB2E4\uC2DC \uC2E4\uD589",
@@ -1684,7 +1684,7 @@ var ko_default = {
1684
1684
  applySource: "\uC801\uC6A9",
1685
1685
  closeDialog: "\uB2EB\uAE30",
1686
1686
  copyHtml: "HTML \uBCF5\uC0AC",
1687
- exportDefault: "HTML \uB0B4\uBCF4\uB0B4\uAE30",
1687
+ exportDefault: "HTML \uCD94\uCD9C",
1688
1688
  previewEmpty: "\uBBF8\uB9AC\uBCFC \uB0B4\uC6A9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
1689
1689
  sourceCodeHint: "HTML\uC744 \uC9C1\uC811 \uD3B8\uC9D1\uD558\uACE0 \uC801\uC6A9\uC744 \uD074\uB9AD\uD558\uC5EC \uC5D0\uB514\uD130\uB97C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694."
1690
1690
  }
@@ -1793,10 +1793,10 @@ var ja_default = {
1793
1793
  supportedFormats: "\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u306B\u5BFE\u5FDC",
1794
1794
  uploadedFiles: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB",
1795
1795
  clearAll: "\u3059\u3079\u3066\u30AF\u30EA\u30A2",
1796
- fileTooLarge: "\u30B5\u30A4\u30BA\u5236\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059",
1797
- maxFilesReached: "\u6700\u5927\u30D5\u30A1\u30A4\u30EB\u6570\u306B\u9054\u3057\u307E\u3057\u305F",
1798
- uploadSuccess: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u6210\u529F",
1799
- uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557"
1796
+ fileTooLarge: "\u30B5\u30A4\u30BA\u5236\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059\u3002",
1797
+ maxFilesReached: "\u6700\u5927\u30D5\u30A1\u30A4\u30EB\u6570\u306B\u9054\u3057\u307E\u3057\u305F\u3002",
1798
+ uploadSuccess: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F\u3002",
1799
+ uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
1800
1800
  },
1801
1801
  UEditor: {
1802
1802
  placeholder: "\u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528\u3059\u308B\u306B\u306F '/' \u3092\u5165\u529B\u3001\u307E\u305F\u306F\u5165\u529B\u3092\u958B\u59CB...",
@@ -1867,9 +1867,9 @@ var ja_default = {
1867
1867
  imageLayoutBlock: "\u753B\u50CF\u30D6\u30ED\u30C3\u30AF",
1868
1868
  imageLayoutLeft: "\u753B\u50CF\u3092\u5DE6\u3001\u6587\u5B57\u3092\u53F3\u306B\u56DE\u308A\u8FBC\u307F",
1869
1869
  imageLayoutRight: "\u753B\u50CF\u3092\u53F3\u3001\u6587\u5B57\u3092\u5DE6\u306B\u56DE\u308A\u8FBC\u307F",
1870
- imageWidthSm: "\u753B\u50CF \u5C0F",
1871
- imageWidthMd: "\u753B\u50CF \u4E2D",
1872
- imageWidthLg: "\u753B\u50CF \u5927",
1870
+ imageWidthSm: "\u753B\u50CF\uFF08\u5C0F\uFF09",
1871
+ imageWidthMd: "\u753B\u50CF\uFF08\u4E2D\uFF09",
1872
+ imageWidthLg: "\u753B\u50CF\uFF08\u5927\uFF09",
1873
1873
  imageResetSize: "\u753B\u50CF\u30B5\u30A4\u30BA\u3092\u30EA\u30BB\u30C3\u30C8",
1874
1874
  imageDelete: "\u753B\u50CF\u3092\u524A\u9664"
1875
1875
  },
@@ -6435,6 +6435,7 @@ var Popover = ({
6435
6435
  if (!triggerEl || !popoverEl) return;
6436
6436
  if (triggerEl.contains(target)) return;
6437
6437
  if (popoverEl.contains(target)) return;
6438
+ if (target instanceof Element && target.closest("[data-popover]")) return;
6438
6439
  setIsOpen(false);
6439
6440
  };
6440
6441
  const handleEscape = (event) => {
@@ -7475,6 +7476,11 @@ var VerticalTabs = ({ sidebarWidth = "w-48", className, ...props }) => {
7475
7476
  import React25, { useState as useState17 } from "react";
7476
7477
  import { ChevronRight as ChevronRight2 } from "lucide-react";
7477
7478
  import { jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
7479
+ var DropdownMenuContext = React25.createContext(null);
7480
+ function useDropdownMenuClose() {
7481
+ return React25.useContext(DropdownMenuContext)?.closeMenu ?? (() => {
7482
+ });
7483
+ }
7478
7484
  function useResettingIndex(resetToken) {
7479
7485
  const [state, setState] = React25.useState({ resetToken, index: -1 });
7480
7486
  const activeIndex = Object.is(state.resetToken, resetToken) ? state.index : -1;
@@ -7508,6 +7514,7 @@ var DropdownMenu = ({
7508
7514
  const menuRef = React25.useRef(null);
7509
7515
  const itemsRef = React25.useRef([]);
7510
7516
  const [activeIndex, setActiveIndex] = useResettingIndex(open);
7517
+ const parentMenu = React25.useContext(DropdownMenuContext);
7511
7518
  useShadCNAnimations();
7512
7519
  React25.useEffect(() => {
7513
7520
  if (!open) return;
@@ -7546,13 +7553,24 @@ var DropdownMenu = ({
7546
7553
  document.removeEventListener("keydown", handleKeyNav);
7547
7554
  };
7548
7555
  }, [open, activeIndex, setActiveIndex]);
7556
+ const closeMenu = React25.useCallback(() => {
7557
+ setOpen(false);
7558
+ parentMenu?.closeMenu();
7559
+ }, [parentMenu, setOpen]);
7560
+ const menuContext = React25.useMemo(
7561
+ () => ({
7562
+ closeMenu,
7563
+ closeOnSelect
7564
+ }),
7565
+ [closeMenu, closeOnSelect]
7566
+ );
7549
7567
  const handleItemClick = (itemOnClick) => {
7550
7568
  itemOnClick();
7551
7569
  if (closeOnSelect) {
7552
- setOpen(false);
7570
+ closeMenu();
7553
7571
  }
7554
7572
  };
7555
- const menuBody = /* @__PURE__ */ jsx29("div", { ref: menuRef, "data-dropdown-menu": true, "data-state": open ? "open" : "closed", role: "menu", className: cn("min-w-40", className), children: items ? items.map((item, index) => {
7573
+ const menuBody = /* @__PURE__ */ jsx29(DropdownMenuContext.Provider, { value: menuContext, children: /* @__PURE__ */ jsx29("div", { ref: menuRef, "data-dropdown-menu": true, "data-state": open ? "open" : "closed", role: "menu", className: cn("min-w-40", className), children: items ? items.map((item, index) => {
7556
7574
  const IconComponent = item.icon;
7557
7575
  return /* @__PURE__ */ jsxs21(
7558
7576
  "button",
@@ -7582,7 +7600,7 @@ var DropdownMenu = ({
7582
7600
  },
7583
7601
  index
7584
7602
  );
7585
- }) : children });
7603
+ }) : children }) });
7586
7604
  const triggerProps = trigger.props;
7587
7605
  const childRef = triggerProps.ref;
7588
7606
  const enhancedTrigger = React25.cloneElement(trigger, {
@@ -7639,34 +7657,44 @@ var DropdownMenuItem = ({
7639
7657
  destructive,
7640
7658
  active,
7641
7659
  shortcut,
7642
- className
7643
- }) => /* @__PURE__ */ jsxs21(
7644
- "button",
7645
- {
7646
- onClick,
7647
- disabled,
7648
- onMouseDown: (e) => e.preventDefault(),
7649
- className: cn(
7650
- "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7651
- "hover:bg-accent hover:text-accent-foreground",
7652
- "focus:bg-accent focus:text-accent-foreground focus:outline-none",
7653
- "disabled:opacity-50 disabled:cursor-not-allowed",
7654
- destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
7655
- active && "bg-primary/10 text-primary",
7656
- className
7657
- ),
7658
- children: [
7659
- Icon && /* @__PURE__ */ jsx29(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
7660
- /* @__PURE__ */ jsxs21("div", { className: "flex-1 text-left", children: [
7661
- label && /* @__PURE__ */ jsx29("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
7662
- description && /* @__PURE__ */ jsx29("div", { className: "text-xs text-muted-foreground", children: description }),
7663
- children
7664
- ] }),
7665
- shortcut && /* @__PURE__ */ jsx29("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
7666
- active && /* @__PURE__ */ jsx29("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx29("polyline", { points: "20 6 9 17 4 12" }) })
7667
- ]
7668
- }
7669
- );
7660
+ className,
7661
+ closeOnSelect
7662
+ }) => {
7663
+ const menu = React25.useContext(DropdownMenuContext);
7664
+ const shouldCloseOnSelect = closeOnSelect ?? menu?.closeOnSelect ?? false;
7665
+ return /* @__PURE__ */ jsxs21(
7666
+ "button",
7667
+ {
7668
+ onClick: () => {
7669
+ onClick?.();
7670
+ if (shouldCloseOnSelect) {
7671
+ menu?.closeMenu();
7672
+ }
7673
+ },
7674
+ disabled,
7675
+ onMouseDown: (e) => e.preventDefault(),
7676
+ className: cn(
7677
+ "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7678
+ "hover:bg-accent hover:text-accent-foreground",
7679
+ "focus:bg-accent focus:text-accent-foreground focus:outline-none",
7680
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7681
+ destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
7682
+ active && "bg-primary/10 text-primary",
7683
+ className
7684
+ ),
7685
+ children: [
7686
+ Icon && /* @__PURE__ */ jsx29(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
7687
+ /* @__PURE__ */ jsxs21("div", { className: "flex-1 text-left", children: [
7688
+ label && /* @__PURE__ */ jsx29("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
7689
+ description && /* @__PURE__ */ jsx29("div", { className: "text-xs text-muted-foreground", children: description }),
7690
+ children
7691
+ ] }),
7692
+ shortcut && /* @__PURE__ */ jsx29("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
7693
+ active && /* @__PURE__ */ jsx29("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx29("polyline", { points: "20 6 9 17 4 12" }) })
7694
+ ]
7695
+ }
7696
+ );
7697
+ };
7670
7698
  var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ jsx29("div", { className: cn("h-px bg-border my-1", className) });
7671
7699
  var DropdownMenuSub = ({ label, icon: Icon, disabled, children }) => /* @__PURE__ */ jsx29(
7672
7700
  DropdownMenu,
@@ -25233,6 +25261,7 @@ import Typography from "@tiptap/extension-typography";
25233
25261
  import Subscript from "@tiptap/extension-subscript";
25234
25262
  import Superscript from "@tiptap/extension-superscript";
25235
25263
  import HorizontalRule from "@tiptap/extension-horizontal-rule";
25264
+ import Gapcursor from "@tiptap/extension-gapcursor";
25236
25265
  import { common, createLowlight } from "lowlight";
25237
25266
 
25238
25267
  // src/components/UEditor/slash-command.tsx
@@ -26023,7 +26052,6 @@ import { mergeAttributes as mergeAttributes4 } from "@tiptap/core";
26023
26052
  import { NodeViewWrapper as NodeViewWrapper4, ReactNodeViewRenderer as ReactNodeViewRenderer4 } from "@tiptap/react";
26024
26053
  import { jsx as jsx81, jsxs as jsxs67 } from "react/jsx-runtime";
26025
26054
  var MIN_IMAGE_SIZE_PX = 40;
26026
- var AXIS_LOCK_THRESHOLD_PX = 4;
26027
26055
  var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
26028
26056
  var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
26029
26057
  function toNullableNumber(value) {
@@ -26064,6 +26092,41 @@ function getImageLayoutStyles(layout) {
26064
26092
  }
26065
26093
  return {};
26066
26094
  }
26095
+ function getAspectRatio({
26096
+ img,
26097
+ widthAttr,
26098
+ heightAttr,
26099
+ fallbackWidth,
26100
+ fallbackHeight
26101
+ }) {
26102
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
26103
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) return img.naturalWidth / img.naturalHeight;
26104
+ if (fallbackWidth > 0 && fallbackHeight > 0) return fallbackWidth / fallbackHeight;
26105
+ return 1;
26106
+ }
26107
+ function sizeFromWidth(width, aspect, maxWidth) {
26108
+ let nextW = clamp8(width, MIN_IMAGE_SIZE_PX, maxWidth);
26109
+ let nextH = nextW / aspect;
26110
+ if (nextH < MIN_IMAGE_SIZE_PX) {
26111
+ nextH = MIN_IMAGE_SIZE_PX;
26112
+ nextW = clamp8(nextH * aspect, MIN_IMAGE_SIZE_PX, maxWidth);
26113
+ nextH = nextW / aspect;
26114
+ }
26115
+ return { width: nextW, height: nextH };
26116
+ }
26117
+ function sizeFromHeight(height, aspect, maxWidth) {
26118
+ let nextH = Math.max(MIN_IMAGE_SIZE_PX, height);
26119
+ let nextW = nextH * aspect;
26120
+ if (nextW > maxWidth) {
26121
+ nextW = maxWidth;
26122
+ nextH = nextW / aspect;
26123
+ }
26124
+ if (nextW < MIN_IMAGE_SIZE_PX) {
26125
+ nextW = MIN_IMAGE_SIZE_PX;
26126
+ nextH = nextW / aspect;
26127
+ }
26128
+ return { width: nextW, height: nextH };
26129
+ }
26067
26130
  function ResizableImageNodeView(props) {
26068
26131
  const { node, selected, updateAttributes, editor, getPos } = props;
26069
26132
  const wrapperRef = useRef29(null);
@@ -26074,7 +26137,6 @@ function ResizableImageNodeView(props) {
26074
26137
  const heightAttr = toNullableNumber(node.attrs["height"]);
26075
26138
  const textAlign = String(node.attrs["textAlign"] ?? "");
26076
26139
  const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
26077
- const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
26078
26140
  const dragStateRef = useRef29(null);
26079
26141
  useEffect34(() => {
26080
26142
  const img = imgRef.current;
@@ -26098,7 +26160,13 @@ function ResizableImageNodeView(props) {
26098
26160
  const maxW = editorEl ? editorEl.getBoundingClientRect().width : Number.POSITIVE_INFINITY;
26099
26161
  const startW = Math.max(MIN_IMAGE_SIZE_PX, rect.width);
26100
26162
  const startH = Math.max(MIN_IMAGE_SIZE_PX, rect.height);
26101
- const aspect = startH > 0 ? startW / startH : 1;
26163
+ const aspect = getAspectRatio({
26164
+ img,
26165
+ widthAttr,
26166
+ heightAttr,
26167
+ fallbackWidth: startW,
26168
+ fallbackHeight: startH
26169
+ });
26102
26170
  dragStateRef.current = {
26103
26171
  pointerId: event.pointerId,
26104
26172
  startX: event.clientX,
@@ -26107,7 +26175,6 @@ function ResizableImageNodeView(props) {
26107
26175
  startH,
26108
26176
  lastW: startW,
26109
26177
  lastH: startH,
26110
- axis: null,
26111
26178
  aspect,
26112
26179
  maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW)
26113
26180
  };
@@ -26121,24 +26188,9 @@ function ResizableImageNodeView(props) {
26121
26188
  if (event.pointerId !== drag.pointerId) return;
26122
26189
  const dx = event.clientX - drag.startX;
26123
26190
  const dy = event.clientY - drag.startY;
26124
- let nextW = drag.startW;
26125
- let nextH = drag.startH;
26126
- const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
26127
- if (shouldPreserveAspect) {
26128
- if (Math.abs(dx) >= Math.abs(dy)) {
26129
- nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26130
- nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26131
- } else {
26132
- nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26133
- nextW = clamp8(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
26134
- }
26135
- } else {
26136
- if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
26137
- drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
26138
- }
26139
- if (drag.axis === "x") nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26140
- if (drag.axis === "y") nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26141
- }
26191
+ const nextSize = Math.abs(dx) >= Math.abs(dy) ? sizeFromWidth(drag.startW + dx, drag.aspect, drag.maxW) : sizeFromHeight(drag.startH + dy, drag.aspect, drag.maxW);
26192
+ const nextW = nextSize.width;
26193
+ const nextH = nextSize.height;
26142
26194
  drag.lastW = nextW;
26143
26195
  drag.lastH = nextH;
26144
26196
  img.style.width = `${Math.round(nextW)}px`;
@@ -27061,6 +27113,7 @@ function buildUEditorExtensions({
27061
27113
  }
27062
27114
  }),
27063
27115
  HorizontalRule,
27116
+ Gapcursor,
27064
27117
  Link.configure({
27065
27118
  openOnClick: false,
27066
27119
  protocols: ["http", "https", "mailto", "tel"],
@@ -27447,6 +27500,43 @@ function isSelectedImage(editor) {
27447
27500
  const { selection } = editor.state;
27448
27501
  return selection instanceof NodeSelection && selection.node.type.name === "image";
27449
27502
  }
27503
+ function toPositiveNumber(value) {
27504
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
27505
+ if (typeof value === "string") {
27506
+ const parsed = Number.parseInt(value, 10);
27507
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
27508
+ }
27509
+ return null;
27510
+ }
27511
+ function getImageElementAtSelection(editor, pos) {
27512
+ const nodeDom = editor.view.nodeDOM(pos);
27513
+ if (!(nodeDom instanceof HTMLElement)) return null;
27514
+ if (nodeDom.tagName === "IMG") return nodeDom;
27515
+ return nodeDom.querySelector("img");
27516
+ }
27517
+ function getImageAspectRatio(editor, attrs, pos) {
27518
+ const widthAttr = toPositiveNumber(attrs.width);
27519
+ const heightAttr = toPositiveNumber(attrs.height);
27520
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
27521
+ const imageElement = typeof pos === "number" ? getImageElementAtSelection(editor, pos) : null;
27522
+ if (!imageElement) return null;
27523
+ if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
27524
+ return imageElement.naturalWidth / imageElement.naturalHeight;
27525
+ }
27526
+ const rect = imageElement.getBoundingClientRect();
27527
+ if (rect.width > 0 && rect.height > 0) return rect.width / rect.height;
27528
+ const width = toPositiveNumber(imageElement.getAttribute("width")) ?? toPositiveNumber(imageElement.style.width);
27529
+ const height = toPositiveNumber(imageElement.getAttribute("height")) ?? toPositiveNumber(imageElement.style.height);
27530
+ return width && height ? width / height : null;
27531
+ }
27532
+ function getImagePresetAttributes(editor, width, preset, attrs, pos) {
27533
+ const aspect = getImageAspectRatio(editor, attrs, pos);
27534
+ return {
27535
+ width,
27536
+ height: aspect ? Math.round(width / aspect) : toPositiveNumber(attrs.height),
27537
+ imageWidthPreset: preset
27538
+ };
27539
+ }
27450
27540
  function applyImageLayout(editor, layout) {
27451
27541
  const { state, view } = editor;
27452
27542
  const { selection, schema } = state;
@@ -27481,15 +27571,15 @@ function applyImageWidthPreset(editor, preset) {
27481
27571
  const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
27482
27572
  const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
27483
27573
  if (!isSelectedImage(editor)) {
27484
- editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
27574
+ editor.chain().focus().updateAttributes("image", getImagePresetAttributes(editor, width, preset, attrs)).run();
27485
27575
  return;
27486
27576
  }
27487
27577
  const { state, view } = editor;
27488
27578
  const selection = state.selection;
27579
+ const nextAttrs = getImagePresetAttributes(editor, width, preset, selection.node.attrs, selection.from);
27489
27580
  const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
27490
27581
  ...selection.node.attrs,
27491
- width,
27492
- imageWidthPreset: preset
27582
+ ...nextAttrs
27493
27583
  });
27494
27584
  view.dispatch(transaction.scrollIntoView());
27495
27585
  view.focus();
@@ -28093,7 +28183,16 @@ var EditorToolbar = ({
28093
28183
  onCancel: () => setShowLinkInput(false)
28094
28184
  }
28095
28185
  ) : /* @__PURE__ */ jsxs71(Fragment28, { children: [
28096
- /* @__PURE__ */ jsx85(DropdownMenuItem, { icon: LinkIcon, label: t("toolbar.link"), onClick: () => setShowLinkInput(true), active: editor.isActive("link") }),
28186
+ /* @__PURE__ */ jsx85(
28187
+ DropdownMenuItem,
28188
+ {
28189
+ icon: LinkIcon,
28190
+ label: t("toolbar.link"),
28191
+ onClick: () => setShowLinkInput(true),
28192
+ active: editor.isActive("link"),
28193
+ closeOnSelect: false
28194
+ }
28195
+ ),
28097
28196
  /* @__PURE__ */ jsx85(
28098
28197
  DropdownMenuItem,
28099
28198
  {
@@ -28317,14 +28416,15 @@ var EditorToolbar = ({
28317
28416
  onCancel: () => setShowImageInput(false)
28318
28417
  }
28319
28418
  ) : /* @__PURE__ */ jsxs71(Fragment28, { children: [
28320
- /* @__PURE__ */ jsx85(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true) }),
28419
+ /* @__PURE__ */ jsx85(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true), closeOnSelect: false }),
28321
28420
  /* @__PURE__ */ jsx85(
28322
28421
  DropdownMenuItem,
28323
28422
  {
28324
28423
  icon: Upload3,
28325
28424
  label: isUploadingImage ? t("imageInput.uploading") : t("imageInput.uploadTab"),
28326
28425
  disabled: isUploadingImage,
28327
- onClick: () => fileInputRef.current?.click()
28426
+ onClick: () => fileInputRef.current?.click(),
28427
+ closeOnSelect: false
28328
28428
  }
28329
28429
  ),
28330
28430
  imageUploadError && /* @__PURE__ */ jsx85(DropdownMenuItem, { label: imageUploadError, disabled: true, destructive: true }),
@@ -29427,10 +29527,22 @@ var CustomBubbleMenu = ({
29427
29527
  setIsVisible(false);
29428
29528
  return;
29429
29529
  }
29430
- const start = view.coordsAtPos(from);
29431
- const end = view.coordsAtPos(to);
29432
- const left = (start.left + end.left) / 2;
29433
- const top = start.top - BUBBLE_MENU_OFFSET;
29530
+ let start;
29531
+ let end;
29532
+ try {
29533
+ start = view.coordsAtPos(from);
29534
+ end = view.coordsAtPos(to);
29535
+ } catch {
29536
+ clearShowTimeout();
29537
+ setIsVisible(false);
29538
+ return;
29539
+ }
29540
+ const viewportPadding = 8;
29541
+ const left = Math.min(
29542
+ window.innerWidth - viewportPadding,
29543
+ Math.max(viewportPadding, (start.left + end.left) / 2)
29544
+ );
29545
+ const top = Math.max(viewportPadding, start.top - BUBBLE_MENU_OFFSET);
29434
29546
  setPosition({ top, left });
29435
29547
  if (keepOpenRef.current) {
29436
29548
  clearShowTimeout();
@@ -29452,11 +29564,17 @@ var CustomBubbleMenu = ({
29452
29564
  editor.on("selectionUpdate", updatePosition);
29453
29565
  editor.on("focus", updatePosition);
29454
29566
  editor.on("blur", handleBlur);
29567
+ editor.on("transaction", updatePosition);
29568
+ editor.on("update", updatePosition);
29569
+ const animationFrameId = requestAnimationFrame(updatePosition);
29455
29570
  return () => {
29571
+ cancelAnimationFrame(animationFrameId);
29456
29572
  clearShowTimeout();
29457
29573
  editor.off("selectionUpdate", updatePosition);
29458
29574
  editor.off("focus", updatePosition);
29459
29575
  editor.off("blur", handleBlur);
29576
+ editor.off("transaction", updatePosition);
29577
+ editor.off("update", updatePosition);
29460
29578
  };
29461
29579
  }, [editor]);
29462
29580
  if (!isVisible) return null;
@@ -29466,7 +29584,7 @@ var CustomBubbleMenu = ({
29466
29584
  {
29467
29585
  ref: menuRef,
29468
29586
  "data-popover": true,
29469
- className: "fixed z-99999 flex rounded-xl border border-border/40 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 zoom-in-95",
29587
+ className: "fixed z-[99999] flex rounded-xl border border-border/40 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 zoom-in-95",
29470
29588
  style: {
29471
29589
  top: `${position.top}px`,
29472
29590
  left: `${position.left}px`,
@@ -35389,6 +35507,17 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
35389
35507
  "[&_.is-editor-empty]:before:float-left",
35390
35508
  "[&_.is-editor-empty]:before:pointer-events-none",
35391
35509
  "[&_.is-editor-empty]:before:h-0",
35510
+ "[&_.ProseMirror-gapcursor]:pointer-events-none",
35511
+ "[&_.ProseMirror-gapcursor]:absolute",
35512
+ "[&_.ProseMirror-gapcursor]:hidden",
35513
+ "[&_.ProseMirror-gapcursor:after]:content-['']",
35514
+ "[&_.ProseMirror-gapcursor:after]:block",
35515
+ "[&_.ProseMirror-gapcursor:after]:absolute",
35516
+ "[&_.ProseMirror-gapcursor:after]:top-[-2px]",
35517
+ "[&_.ProseMirror-gapcursor:after]:w-8",
35518
+ "[&_.ProseMirror-gapcursor:after]:border-t-2",
35519
+ "[&_.ProseMirror-gapcursor:after]:border-primary",
35520
+ "[&.ProseMirror-focused_.ProseMirror-gapcursor]:block",
35392
35521
  "[&_ul[data-type='taskList']]:list-none",
35393
35522
  "[&_ul[data-type='taskList']]:pl-0",
35394
35523
  "[&_ul[data-type='taskList']_li]:flex",
@@ -36003,6 +36132,24 @@ import {
36003
36132
  Upload as Upload4
36004
36133
  } from "lucide-react";
36005
36134
  import { Fragment as Fragment35, jsx as jsx92, jsxs as jsxs77 } from "react/jsx-runtime";
36135
+ function MenuTableInsertGrid({
36136
+ insertLabel,
36137
+ previewTemplate,
36138
+ onInsert
36139
+ }) {
36140
+ const closeMenu = useDropdownMenuClose();
36141
+ return /* @__PURE__ */ jsx92(
36142
+ TableInsertGrid,
36143
+ {
36144
+ insertLabel,
36145
+ previewTemplate,
36146
+ onInsert: (rows, cols) => {
36147
+ onInsert(rows, cols);
36148
+ closeMenu();
36149
+ }
36150
+ }
36151
+ );
36152
+ }
36006
36153
  function renderMenuItems(items) {
36007
36154
  return items.map((item, i) => {
36008
36155
  switch (item.type) {
@@ -36328,7 +36475,7 @@ function buildTableMenuItems(t, editor, onInsertTable) {
36328
36475
  type: "custom",
36329
36476
  key: "table-insert-grid",
36330
36477
  render: () => /* @__PURE__ */ jsx92(
36331
- TableInsertGrid,
36478
+ MenuTableInsertGrid,
36332
36479
  {
36333
36480
  insertLabel: t("tableMenu.insertTable"),
36334
36481
  previewTemplate: t("tableMenu.gridPreview"),
@@ -36520,13 +36667,14 @@ var MenuBar = ({
36520
36667
  type: "custom",
36521
36668
  key: "image",
36522
36669
  render: () => showImageInput ? /* @__PURE__ */ jsx92(ImageInput, { onSubmit: handleImageUrl, onCancel: () => setShowImageInput(false) }) : /* @__PURE__ */ jsxs77(Fragment35, { children: [
36523
- /* @__PURE__ */ jsx92(DropdownMenuItem, { label: t("menubar.image"), icon: ImageIcon3, onClick: () => setShowImageInput(true) }),
36670
+ /* @__PURE__ */ jsx92(DropdownMenuItem, { label: t("menubar.image"), icon: ImageIcon3, onClick: () => setShowImageInput(true), closeOnSelect: false }),
36524
36671
  /* @__PURE__ */ jsx92(
36525
36672
  DropdownMenuItem,
36526
36673
  {
36527
36674
  label: t("menubar.imageUpload"),
36528
36675
  icon: Upload4,
36529
- onClick: () => fileInputRef.current?.click()
36676
+ onClick: () => fileInputRef.current?.click(),
36677
+ closeOnSelect: false
36530
36678
  }
36531
36679
  )
36532
36680
  ] })
@@ -36541,7 +36689,8 @@ var MenuBar = ({
36541
36689
  label: t("menubar.link"),
36542
36690
  icon: LinkIcon3,
36543
36691
  shortcut: t("menubar.linkShortcut"),
36544
- onClick: () => setShowLinkInput(true)
36692
+ onClick: () => setShowLinkInput(true),
36693
+ closeOnSelect: false
36545
36694
  }
36546
36695
  )
36547
36696
  },
@@ -36555,7 +36704,7 @@ var MenuBar = ({
36555
36704
  type: "custom",
36556
36705
  key: "table-grid",
36557
36706
  render: () => /* @__PURE__ */ jsx92(
36558
- TableInsertGrid,
36707
+ MenuTableInsertGrid,
36559
36708
  {
36560
36709
  insertLabel: t("tableMenu.insertTable"),
36561
36710
  previewTemplate: t("tableMenu.gridPreview"),
@@ -36855,7 +37004,7 @@ var UEditor = React82.forwardRef(({
36855
37004
  return /* @__PURE__ */ jsx93(
36856
37005
  "div",
36857
37006
  {
36858
- className: cn("w-full rounded-lg border bg-background flex items-center justify-center text-muted-foreground", className),
37007
+ className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
36859
37008
  style: { minHeight },
36860
37009
  children: t("loading")
36861
37010
  }
@@ -36867,7 +37016,7 @@ var UEditor = React82.forwardRef(({
36867
37016
  className: cn(
36868
37017
  "group relative flex flex-col text-card-foreground",
36869
37018
  editable ? [
36870
- "rounded-2xl md:rounded-3xl border border-border/50 bg-card overflow-hidden",
37019
+ "border border-border/50 bg-card overflow-hidden",
36871
37020
  "transition-[transform,box-shadow,border-color,background-color] duration-300 ease-soft",
36872
37021
  "shadow-sm focus-within:shadow-md focus-within:border-primary/15",
36873
37022
  "backdrop-blur-sm",