@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.140",
3
+ "version": "1.0.142",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 232,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -1153,13 +1153,13 @@ var vi_default = {
1153
1153
  alignRight: "C\u0103n ph\u1EA3i"
1154
1154
  },
1155
1155
  ValidationInput: {
1156
- required: "Tr\u01B0\u1EDDng n\xE0y l\xE0 b\u1EAFt bu\u1ED9c",
1157
- typeMismatch: "\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7",
1156
+ required: "Tr\u01B0\u1EDDng n\xE0y l\xE0 b\u1EAFt bu\u1ED9c.",
1157
+ typeMismatch: "\u0110\u1ECBnh d\u1EA1ng kh\xF4ng h\u1EE3p l\u1EC7.",
1158
1158
  pattern: "Sai m\u1EABu",
1159
1159
  tooShort: "Qu\xE1 ng\u1EAFn",
1160
1160
  tooLong: "Qu\xE1 d\xE0i",
1161
- rangeUnderflow: "Nh\u1ECF h\u01A1n gi\xE1 tr\u1ECB t\u1ED1i thi\u1EC3u",
1162
- rangeOverflow: "L\u1EDBn h\u01A1n gi\xE1 tr\u1ECB t\u1ED1i \u0111a",
1161
+ rangeUnderflow: "Gi\xE1 tr\u1ECB nh\u1ECF h\u01A1n m\u1EE9c t\u1ED1i thi\u1EC3u.",
1162
+ rangeOverflow: "Gi\xE1 tr\u1ECB v\u01B0\u1EE3t qu\xE1 m\u1EE9c t\u1ED1i \u0111a.",
1163
1163
  stepMismatch: "Sai b\u01B0\u1EDBc",
1164
1164
  badInput: "Gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7",
1165
1165
  invalid: "Gi\xE1 tr\u1ECB kh\xF4ng h\u1EE3p l\u1EC7"
@@ -1194,7 +1194,7 @@ var vi_default = {
1194
1194
  start: "B\u1EAFt \u0111\u1EA7u",
1195
1195
  end: "K\u1EBFt th\xFAc",
1196
1196
  resourcesHeader: "T\xE0i nguy\xEAn",
1197
- expandGroup: "M\u1EDF nh\xF3m",
1197
+ expandGroup: "M\u1EDF r\u1ED9ng nh\xF3m",
1198
1198
  collapseGroup: "Thu g\u1ECDn nh\xF3m",
1199
1199
  more: "+{n} th\xEAm",
1200
1200
  sprints: "{n} sprint",
@@ -1229,9 +1229,9 @@ var vi_default = {
1229
1229
  uploadedFiles: "T\u1EC7p \u0111\xE3 t\u1EA3i l\xEAn",
1230
1230
  clearAll: "X\xF3a t\u1EA5t c\u1EA3",
1231
1231
  fileTooLarge: "v\u01B0\u1EE3t qu\xE1 gi\u1EDBi h\u1EA1n k\xEDch th\u01B0\u1EDBc",
1232
- maxFilesReached: "\u0110\xE3 \u0111\u1EA1t gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EC7p",
1233
- uploadSuccess: "\u0111\xE3 t\u1EA3i l\xEAn th\xE0nh c\xF4ng",
1234
- uploadFailed: "t\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"
1232
+ maxFilesReached: "\u0110\xE3 \u0111\u1EA1t gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EC7p.",
1233
+ uploadSuccess: "\u0110\xE3 t\u1EA3i l\xEAn th\xE0nh c\xF4ng.",
1234
+ uploadFailed: "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i."
1235
1235
  },
1236
1236
  UEditor: {
1237
1237
  placeholder: "Nh\u1EADp '/' \u0111\u1EC3 m\u1EDF l\u1EC7nh, ho\u1EB7c b\u1EAFt \u0111\u1EA7u vi\u1EBFt...",
@@ -1432,9 +1432,9 @@ var vi_default = {
1432
1432
  table: "B\u1EA3ng",
1433
1433
  save: "L\u01B0u",
1434
1434
  exportHtml: "Xu\u1EA5t HTML",
1435
- undo: "Hu\u1EF7 thao t\xE1c",
1435
+ undo: "Ho\xE0n t\xE1c",
1436
1436
  undoShortcut: "Ctrl+Z",
1437
- redo: "Ho\xE0n t\xE1c",
1437
+ redo: "L\xE0m l\u1EA1i",
1438
1438
  redoShortcut: "Ctrl+Y",
1439
1439
  cut: "C\u1EAFt",
1440
1440
  cutShortcut: "Ctrl+X",
@@ -1464,7 +1464,7 @@ var vi_default = {
1464
1464
  superscript: "Tr\xEAn d\xF2ng",
1465
1465
  subscript: "D\u01B0\u1EDBi d\xF2ng",
1466
1466
  code: "M\xE3",
1467
- wrap: "Bao",
1467
+ wrap: "Ng\u1EAFt d\xF2ng",
1468
1468
  align: "C\u0103n ch\u1EC9nh",
1469
1469
  clearFormatting: "Xo\xE1 \u0111\u1ECBnh d\u1EA1ng",
1470
1470
  paragraph: "\u0110o\u1EA1n v\u0103n",
@@ -1604,10 +1604,10 @@ var ko_default = {
1604
1604
  supportedFormats: "\uBAA8\uB4E0 \uD30C\uC77C \uD615\uC2DD \uC9C0\uC6D0",
1605
1605
  uploadedFiles: "\uC5C5\uB85C\uB4DC\uB41C \uD30C\uC77C",
1606
1606
  clearAll: "\uBAA8\uB450 \uC9C0\uC6B0\uAE30",
1607
- fileTooLarge: "\uD06C\uAE30 \uC81C\uD55C \uCD08\uACFC",
1608
- maxFilesReached: "\uCD5C\uB300 \uD30C\uC77C \uC218 \uB3C4\uB2EC",
1609
- uploadSuccess: "\uC5C5\uB85C\uB4DC \uC131\uACF5",
1610
- uploadFailed: "\uC5C5\uB85C\uB4DC \uC2E4\uD328"
1607
+ fileTooLarge: "\uD30C\uC77C \uD06C\uAE30\uAC00 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.",
1608
+ maxFilesReached: "\uCD5C\uB300 \uD30C\uC77C \uC218\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4.",
1609
+ uploadSuccess: "\uC5C5\uB85C\uB4DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
1610
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1611
1611
  },
1612
1612
  UEditor: {
1613
1613
  placeholder: "\uBA85\uB839\uC5B4\uB294 '/'\uB97C \uC785\uB825\uD558\uAC70\uB098 \uC785\uB825\uC744 \uC2DC\uC791\uD558\uC138\uC694...",
@@ -1795,7 +1795,7 @@ var ko_default = {
1795
1795
  fileCard: {
1796
1796
  unnamed: "\uC774\uB984 \uC5C6\uB294 \uD30C\uC77C",
1797
1797
  uploading: "(\uC5C5\uB85C\uB4DC \uC911...)",
1798
- uploadFailed: "\uC5C5\uB85C\uB4DC \uC2E4\uD328"
1798
+ uploadFailed: "\uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."
1799
1799
  },
1800
1800
  menubar: {
1801
1801
  file: "\uD30C\uC77C",
@@ -1806,7 +1806,7 @@ var ko_default = {
1806
1806
  tools: "\uB3C4\uAD6C",
1807
1807
  table: "\uD45C",
1808
1808
  save: "\uC800\uC7A5",
1809
- exportHtml: "HTML \uB0B4\uBCF4\uB0B4\uAE30",
1809
+ exportHtml: "HTML \uCD94\uCD9C",
1810
1810
  undo: "\uC2E4\uD589 \uCDE8\uC18C",
1811
1811
  undoShortcut: "Ctrl+Z",
1812
1812
  redo: "\uB2E4\uC2DC \uC2E4\uD589",
@@ -1870,7 +1870,7 @@ var ko_default = {
1870
1870
  applySource: "\uC801\uC6A9",
1871
1871
  closeDialog: "\uB2EB\uAE30",
1872
1872
  copyHtml: "HTML \uBCF5\uC0AC",
1873
- exportDefault: "HTML \uB0B4\uBCF4\uB0B4\uAE30",
1873
+ exportDefault: "HTML \uCD94\uCD9C",
1874
1874
  previewEmpty: "\uBBF8\uB9AC\uBCFC \uB0B4\uC6A9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
1875
1875
  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."
1876
1876
  }
@@ -1979,10 +1979,10 @@ var ja_default = {
1979
1979
  supportedFormats: "\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u306B\u5BFE\u5FDC",
1980
1980
  uploadedFiles: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB",
1981
1981
  clearAll: "\u3059\u3079\u3066\u30AF\u30EA\u30A2",
1982
- fileTooLarge: "\u30B5\u30A4\u30BA\u5236\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059",
1983
- maxFilesReached: "\u6700\u5927\u30D5\u30A1\u30A4\u30EB\u6570\u306B\u9054\u3057\u307E\u3057\u305F",
1984
- uploadSuccess: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u6210\u529F",
1985
- uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5931\u6557"
1982
+ fileTooLarge: "\u30B5\u30A4\u30BA\u5236\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059\u3002",
1983
+ maxFilesReached: "\u6700\u5927\u30D5\u30A1\u30A4\u30EB\u6570\u306B\u9054\u3057\u307E\u3057\u305F\u3002",
1984
+ uploadSuccess: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F\u3002",
1985
+ uploadFailed: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
1986
1986
  },
1987
1987
  UEditor: {
1988
1988
  placeholder: "\u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528\u3059\u308B\u306B\u306F '/' \u3092\u5165\u529B\u3001\u307E\u305F\u306F\u5165\u529B\u3092\u958B\u59CB...",
@@ -2053,9 +2053,9 @@ var ja_default = {
2053
2053
  imageLayoutBlock: "\u753B\u50CF\u30D6\u30ED\u30C3\u30AF",
2054
2054
  imageLayoutLeft: "\u753B\u50CF\u3092\u5DE6\u3001\u6587\u5B57\u3092\u53F3\u306B\u56DE\u308A\u8FBC\u307F",
2055
2055
  imageLayoutRight: "\u753B\u50CF\u3092\u53F3\u3001\u6587\u5B57\u3092\u5DE6\u306B\u56DE\u308A\u8FBC\u307F",
2056
- imageWidthSm: "\u753B\u50CF \u5C0F",
2057
- imageWidthMd: "\u753B\u50CF \u4E2D",
2058
- imageWidthLg: "\u753B\u50CF \u5927",
2056
+ imageWidthSm: "\u753B\u50CF\uFF08\u5C0F\uFF09",
2057
+ imageWidthMd: "\u753B\u50CF\uFF08\u4E2D\uFF09",
2058
+ imageWidthLg: "\u753B\u50CF\uFF08\u5927\uFF09",
2059
2059
  imageResetSize: "\u753B\u50CF\u30B5\u30A4\u30BA\u3092\u30EA\u30BB\u30C3\u30C8",
2060
2060
  imageDelete: "\u753B\u50CF\u3092\u524A\u9664"
2061
2061
  },
@@ -6621,6 +6621,7 @@ var Popover = ({
6621
6621
  if (!triggerEl || !popoverEl) return;
6622
6622
  if (triggerEl.contains(target)) return;
6623
6623
  if (popoverEl.contains(target)) return;
6624
+ if (target instanceof Element && target.closest("[data-popover]")) return;
6624
6625
  setIsOpen(false);
6625
6626
  };
6626
6627
  const handleEscape = (event) => {
@@ -7661,6 +7662,11 @@ var VerticalTabs = ({ sidebarWidth = "w-48", className, ...props }) => {
7661
7662
  var import_react14 = __toESM(require("react"), 1);
7662
7663
  var import_lucide_react13 = require("lucide-react");
7663
7664
  var import_jsx_runtime29 = require("react/jsx-runtime");
7665
+ var DropdownMenuContext = import_react14.default.createContext(null);
7666
+ function useDropdownMenuClose() {
7667
+ return import_react14.default.useContext(DropdownMenuContext)?.closeMenu ?? (() => {
7668
+ });
7669
+ }
7664
7670
  function useResettingIndex(resetToken) {
7665
7671
  const [state, setState] = import_react14.default.useState({ resetToken, index: -1 });
7666
7672
  const activeIndex = Object.is(state.resetToken, resetToken) ? state.index : -1;
@@ -7694,6 +7700,7 @@ var DropdownMenu = ({
7694
7700
  const menuRef = import_react14.default.useRef(null);
7695
7701
  const itemsRef = import_react14.default.useRef([]);
7696
7702
  const [activeIndex, setActiveIndex] = useResettingIndex(open);
7703
+ const parentMenu = import_react14.default.useContext(DropdownMenuContext);
7697
7704
  useShadCNAnimations();
7698
7705
  import_react14.default.useEffect(() => {
7699
7706
  if (!open) return;
@@ -7732,13 +7739,24 @@ var DropdownMenu = ({
7732
7739
  document.removeEventListener("keydown", handleKeyNav);
7733
7740
  };
7734
7741
  }, [open, activeIndex, setActiveIndex]);
7742
+ const closeMenu = import_react14.default.useCallback(() => {
7743
+ setOpen(false);
7744
+ parentMenu?.closeMenu();
7745
+ }, [parentMenu, setOpen]);
7746
+ const menuContext = import_react14.default.useMemo(
7747
+ () => ({
7748
+ closeMenu,
7749
+ closeOnSelect
7750
+ }),
7751
+ [closeMenu, closeOnSelect]
7752
+ );
7735
7753
  const handleItemClick = (itemOnClick) => {
7736
7754
  itemOnClick();
7737
7755
  if (closeOnSelect) {
7738
- setOpen(false);
7756
+ closeMenu();
7739
7757
  }
7740
7758
  };
7741
- const menuBody = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("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) => {
7759
+ const menuBody = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(DropdownMenuContext.Provider, { value: menuContext, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("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) => {
7742
7760
  const IconComponent = item.icon;
7743
7761
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
7744
7762
  "button",
@@ -7768,7 +7786,7 @@ var DropdownMenu = ({
7768
7786
  },
7769
7787
  index
7770
7788
  );
7771
- }) : children });
7789
+ }) : children }) });
7772
7790
  const triggerProps = trigger.props;
7773
7791
  const childRef = triggerProps.ref;
7774
7792
  const enhancedTrigger = import_react14.default.cloneElement(trigger, {
@@ -7825,34 +7843,44 @@ var DropdownMenuItem = ({
7825
7843
  destructive,
7826
7844
  active,
7827
7845
  shortcut,
7828
- className
7829
- }) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
7830
- "button",
7831
- {
7832
- onClick,
7833
- disabled,
7834
- onMouseDown: (e) => e.preventDefault(),
7835
- className: cn(
7836
- "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7837
- "hover:bg-accent hover:text-accent-foreground",
7838
- "focus:bg-accent focus:text-accent-foreground focus:outline-none",
7839
- "disabled:opacity-50 disabled:cursor-not-allowed",
7840
- destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
7841
- active && "bg-primary/10 text-primary",
7842
- className
7843
- ),
7844
- children: [
7845
- Icon && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
7846
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 text-left", children: [
7847
- label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
7848
- description && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs text-muted-foreground", children: description }),
7849
- children
7850
- ] }),
7851
- shortcut && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
7852
- active && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("polyline", { points: "20 6 9 17 4 12" }) })
7853
- ]
7854
- }
7855
- );
7846
+ className,
7847
+ closeOnSelect
7848
+ }) => {
7849
+ const menu = import_react14.default.useContext(DropdownMenuContext);
7850
+ const shouldCloseOnSelect = closeOnSelect ?? menu?.closeOnSelect ?? false;
7851
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
7852
+ "button",
7853
+ {
7854
+ onClick: () => {
7855
+ onClick?.();
7856
+ if (shouldCloseOnSelect) {
7857
+ menu?.closeMenu();
7858
+ }
7859
+ },
7860
+ disabled,
7861
+ onMouseDown: (e) => e.preventDefault(),
7862
+ className: cn(
7863
+ "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7864
+ "hover:bg-accent hover:text-accent-foreground",
7865
+ "focus:bg-accent focus:text-accent-foreground focus:outline-none",
7866
+ "disabled:opacity-50 disabled:cursor-not-allowed",
7867
+ destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10",
7868
+ active && "bg-primary/10 text-primary",
7869
+ className
7870
+ ),
7871
+ children: [
7872
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
7873
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex-1 text-left", children: [
7874
+ label && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
7875
+ description && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "text-xs text-muted-foreground", children: description }),
7876
+ children
7877
+ ] }),
7878
+ shortcut && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
7879
+ active && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("polyline", { points: "20 6 9 17 4 12" }) })
7880
+ ]
7881
+ }
7882
+ );
7883
+ };
7856
7884
  var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: cn("h-px bg-border my-1", className) });
7857
7885
  var DropdownMenuSub = ({ label, icon: Icon, disabled, children }) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
7858
7886
  DropdownMenu,
@@ -25403,6 +25431,7 @@ var import_extension_typography = __toESM(require("@tiptap/extension-typography"
25403
25431
  var import_extension_subscript = __toESM(require("@tiptap/extension-subscript"), 1);
25404
25432
  var import_extension_superscript = __toESM(require("@tiptap/extension-superscript"), 1);
25405
25433
  var import_extension_horizontal_rule = __toESM(require("@tiptap/extension-horizontal-rule"), 1);
25434
+ var import_extension_gapcursor = __toESM(require("@tiptap/extension-gapcursor"), 1);
25406
25435
  var import_lowlight = require("lowlight");
25407
25436
 
25408
25437
  // src/components/UEditor/slash-command.tsx
@@ -26178,7 +26207,6 @@ var import_core8 = require("@tiptap/core");
26178
26207
  var import_react54 = require("@tiptap/react");
26179
26208
  var import_jsx_runtime81 = require("react/jsx-runtime");
26180
26209
  var MIN_IMAGE_SIZE_PX = 40;
26181
- var AXIS_LOCK_THRESHOLD_PX = 4;
26182
26210
  var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
26183
26211
  var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
26184
26212
  function toNullableNumber(value) {
@@ -26219,6 +26247,41 @@ function getImageLayoutStyles(layout) {
26219
26247
  }
26220
26248
  return {};
26221
26249
  }
26250
+ function getAspectRatio({
26251
+ img,
26252
+ widthAttr,
26253
+ heightAttr,
26254
+ fallbackWidth,
26255
+ fallbackHeight
26256
+ }) {
26257
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
26258
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) return img.naturalWidth / img.naturalHeight;
26259
+ if (fallbackWidth > 0 && fallbackHeight > 0) return fallbackWidth / fallbackHeight;
26260
+ return 1;
26261
+ }
26262
+ function sizeFromWidth(width, aspect, maxWidth) {
26263
+ let nextW = clamp8(width, MIN_IMAGE_SIZE_PX, maxWidth);
26264
+ let nextH = nextW / aspect;
26265
+ if (nextH < MIN_IMAGE_SIZE_PX) {
26266
+ nextH = MIN_IMAGE_SIZE_PX;
26267
+ nextW = clamp8(nextH * aspect, MIN_IMAGE_SIZE_PX, maxWidth);
26268
+ nextH = nextW / aspect;
26269
+ }
26270
+ return { width: nextW, height: nextH };
26271
+ }
26272
+ function sizeFromHeight(height, aspect, maxWidth) {
26273
+ let nextH = Math.max(MIN_IMAGE_SIZE_PX, height);
26274
+ let nextW = nextH * aspect;
26275
+ if (nextW > maxWidth) {
26276
+ nextW = maxWidth;
26277
+ nextH = nextW / aspect;
26278
+ }
26279
+ if (nextW < MIN_IMAGE_SIZE_PX) {
26280
+ nextW = MIN_IMAGE_SIZE_PX;
26281
+ nextH = nextW / aspect;
26282
+ }
26283
+ return { width: nextW, height: nextH };
26284
+ }
26222
26285
  function ResizableImageNodeView(props) {
26223
26286
  const { node, selected, updateAttributes, editor, getPos } = props;
26224
26287
  const wrapperRef = (0, import_react53.useRef)(null);
@@ -26229,7 +26292,6 @@ function ResizableImageNodeView(props) {
26229
26292
  const heightAttr = toNullableNumber(node.attrs["height"]);
26230
26293
  const textAlign = String(node.attrs["textAlign"] ?? "");
26231
26294
  const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
26232
- const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
26233
26295
  const dragStateRef = (0, import_react53.useRef)(null);
26234
26296
  (0, import_react53.useEffect)(() => {
26235
26297
  const img = imgRef.current;
@@ -26253,7 +26315,13 @@ function ResizableImageNodeView(props) {
26253
26315
  const maxW = editorEl ? editorEl.getBoundingClientRect().width : Number.POSITIVE_INFINITY;
26254
26316
  const startW = Math.max(MIN_IMAGE_SIZE_PX, rect.width);
26255
26317
  const startH = Math.max(MIN_IMAGE_SIZE_PX, rect.height);
26256
- const aspect = startH > 0 ? startW / startH : 1;
26318
+ const aspect = getAspectRatio({
26319
+ img,
26320
+ widthAttr,
26321
+ heightAttr,
26322
+ fallbackWidth: startW,
26323
+ fallbackHeight: startH
26324
+ });
26257
26325
  dragStateRef.current = {
26258
26326
  pointerId: event.pointerId,
26259
26327
  startX: event.clientX,
@@ -26262,7 +26330,6 @@ function ResizableImageNodeView(props) {
26262
26330
  startH,
26263
26331
  lastW: startW,
26264
26332
  lastH: startH,
26265
- axis: null,
26266
26333
  aspect,
26267
26334
  maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW)
26268
26335
  };
@@ -26276,24 +26343,9 @@ function ResizableImageNodeView(props) {
26276
26343
  if (event.pointerId !== drag.pointerId) return;
26277
26344
  const dx = event.clientX - drag.startX;
26278
26345
  const dy = event.clientY - drag.startY;
26279
- let nextW = drag.startW;
26280
- let nextH = drag.startH;
26281
- const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
26282
- if (shouldPreserveAspect) {
26283
- if (Math.abs(dx) >= Math.abs(dy)) {
26284
- nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26285
- nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26286
- } else {
26287
- nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26288
- nextW = clamp8(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
26289
- }
26290
- } else {
26291
- if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
26292
- drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
26293
- }
26294
- if (drag.axis === "x") nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26295
- if (drag.axis === "y") nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26296
- }
26346
+ const nextSize = Math.abs(dx) >= Math.abs(dy) ? sizeFromWidth(drag.startW + dx, drag.aspect, drag.maxW) : sizeFromHeight(drag.startH + dy, drag.aspect, drag.maxW);
26347
+ const nextW = nextSize.width;
26348
+ const nextH = nextSize.height;
26297
26349
  drag.lastW = nextW;
26298
26350
  drag.lastH = nextH;
26299
26351
  img.style.width = `${Math.round(nextW)}px`;
@@ -27216,6 +27268,7 @@ function buildUEditorExtensions({
27216
27268
  }
27217
27269
  }),
27218
27270
  import_extension_horizontal_rule.default,
27271
+ import_extension_gapcursor.default,
27219
27272
  import_extension_link.default.configure({
27220
27273
  openOnClick: false,
27221
27274
  protocols: ["http", "https", "mailto", "tel"],
@@ -27565,6 +27618,43 @@ function isSelectedImage(editor) {
27565
27618
  const { selection } = editor.state;
27566
27619
  return selection instanceof import_state6.NodeSelection && selection.node.type.name === "image";
27567
27620
  }
27621
+ function toPositiveNumber(value) {
27622
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
27623
+ if (typeof value === "string") {
27624
+ const parsed = Number.parseInt(value, 10);
27625
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
27626
+ }
27627
+ return null;
27628
+ }
27629
+ function getImageElementAtSelection(editor, pos) {
27630
+ const nodeDom = editor.view.nodeDOM(pos);
27631
+ if (!(nodeDom instanceof HTMLElement)) return null;
27632
+ if (nodeDom.tagName === "IMG") return nodeDom;
27633
+ return nodeDom.querySelector("img");
27634
+ }
27635
+ function getImageAspectRatio(editor, attrs, pos) {
27636
+ const widthAttr = toPositiveNumber(attrs.width);
27637
+ const heightAttr = toPositiveNumber(attrs.height);
27638
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
27639
+ const imageElement = typeof pos === "number" ? getImageElementAtSelection(editor, pos) : null;
27640
+ if (!imageElement) return null;
27641
+ if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
27642
+ return imageElement.naturalWidth / imageElement.naturalHeight;
27643
+ }
27644
+ const rect = imageElement.getBoundingClientRect();
27645
+ if (rect.width > 0 && rect.height > 0) return rect.width / rect.height;
27646
+ const width = toPositiveNumber(imageElement.getAttribute("width")) ?? toPositiveNumber(imageElement.style.width);
27647
+ const height = toPositiveNumber(imageElement.getAttribute("height")) ?? toPositiveNumber(imageElement.style.height);
27648
+ return width && height ? width / height : null;
27649
+ }
27650
+ function getImagePresetAttributes(editor, width, preset, attrs, pos) {
27651
+ const aspect = getImageAspectRatio(editor, attrs, pos);
27652
+ return {
27653
+ width,
27654
+ height: aspect ? Math.round(width / aspect) : toPositiveNumber(attrs.height),
27655
+ imageWidthPreset: preset
27656
+ };
27657
+ }
27568
27658
  function applyImageLayout(editor, layout) {
27569
27659
  const { state, view } = editor;
27570
27660
  const { selection, schema } = state;
@@ -27599,15 +27689,15 @@ function applyImageWidthPreset(editor, preset) {
27599
27689
  const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
27600
27690
  const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
27601
27691
  if (!isSelectedImage(editor)) {
27602
- editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
27692
+ editor.chain().focus().updateAttributes("image", getImagePresetAttributes(editor, width, preset, attrs)).run();
27603
27693
  return;
27604
27694
  }
27605
27695
  const { state, view } = editor;
27606
27696
  const selection = state.selection;
27697
+ const nextAttrs = getImagePresetAttributes(editor, width, preset, selection.node.attrs, selection.from);
27607
27698
  const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
27608
27699
  ...selection.node.attrs,
27609
- width,
27610
- imageWidthPreset: preset
27700
+ ...nextAttrs
27611
27701
  });
27612
27702
  view.dispatch(transaction.scrollIntoView());
27613
27703
  view.focus();
@@ -28211,7 +28301,16 @@ var EditorToolbar = ({
28211
28301
  onCancel: () => setShowLinkInput(false)
28212
28302
  }
28213
28303
  ) : /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(import_jsx_runtime85.Fragment, { children: [
28214
- /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(DropdownMenuItem, { icon: import_lucide_react48.Link, label: t("toolbar.link"), onClick: () => setShowLinkInput(true), active: editor.isActive("link") }),
28304
+ /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(
28305
+ DropdownMenuItem,
28306
+ {
28307
+ icon: import_lucide_react48.Link,
28308
+ label: t("toolbar.link"),
28309
+ onClick: () => setShowLinkInput(true),
28310
+ active: editor.isActive("link"),
28311
+ closeOnSelect: false
28312
+ }
28313
+ ),
28215
28314
  /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(
28216
28315
  DropdownMenuItem,
28217
28316
  {
@@ -28435,14 +28534,15 @@ var EditorToolbar = ({
28435
28534
  onCancel: () => setShowImageInput(false)
28436
28535
  }
28437
28536
  ) : /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(import_jsx_runtime85.Fragment, { children: [
28438
- /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(DropdownMenuItem, { icon: import_lucide_react48.Link, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true) }),
28537
+ /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(DropdownMenuItem, { icon: import_lucide_react48.Link, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true), closeOnSelect: false }),
28439
28538
  /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(
28440
28539
  DropdownMenuItem,
28441
28540
  {
28442
28541
  icon: import_lucide_react48.Upload,
28443
28542
  label: isUploadingImage ? t("imageInput.uploading") : t("imageInput.uploadTab"),
28444
28543
  disabled: isUploadingImage,
28445
- onClick: () => fileInputRef.current?.click()
28544
+ onClick: () => fileInputRef.current?.click(),
28545
+ closeOnSelect: false
28446
28546
  }
28447
28547
  ),
28448
28548
  imageUploadError && /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(DropdownMenuItem, { label: imageUploadError, disabled: true, destructive: true }),
@@ -29524,10 +29624,22 @@ var CustomBubbleMenu = ({
29524
29624
  setIsVisible(false);
29525
29625
  return;
29526
29626
  }
29527
- const start = view.coordsAtPos(from);
29528
- const end = view.coordsAtPos(to);
29529
- const left = (start.left + end.left) / 2;
29530
- const top = start.top - BUBBLE_MENU_OFFSET;
29627
+ let start;
29628
+ let end;
29629
+ try {
29630
+ start = view.coordsAtPos(from);
29631
+ end = view.coordsAtPos(to);
29632
+ } catch {
29633
+ clearShowTimeout();
29634
+ setIsVisible(false);
29635
+ return;
29636
+ }
29637
+ const viewportPadding = 8;
29638
+ const left = Math.min(
29639
+ window.innerWidth - viewportPadding,
29640
+ Math.max(viewportPadding, (start.left + end.left) / 2)
29641
+ );
29642
+ const top = Math.max(viewportPadding, start.top - BUBBLE_MENU_OFFSET);
29531
29643
  setPosition({ top, left });
29532
29644
  if (keepOpenRef.current) {
29533
29645
  clearShowTimeout();
@@ -29549,11 +29661,17 @@ var CustomBubbleMenu = ({
29549
29661
  editor.on("selectionUpdate", updatePosition);
29550
29662
  editor.on("focus", updatePosition);
29551
29663
  editor.on("blur", handleBlur);
29664
+ editor.on("transaction", updatePosition);
29665
+ editor.on("update", updatePosition);
29666
+ const animationFrameId = requestAnimationFrame(updatePosition);
29552
29667
  return () => {
29668
+ cancelAnimationFrame(animationFrameId);
29553
29669
  clearShowTimeout();
29554
29670
  editor.off("selectionUpdate", updatePosition);
29555
29671
  editor.off("focus", updatePosition);
29556
29672
  editor.off("blur", handleBlur);
29673
+ editor.off("transaction", updatePosition);
29674
+ editor.off("update", updatePosition);
29557
29675
  };
29558
29676
  }, [editor]);
29559
29677
  if (!isVisible) return null;
@@ -29563,7 +29681,7 @@ var CustomBubbleMenu = ({
29563
29681
  {
29564
29682
  ref: menuRef,
29565
29683
  "data-popover": true,
29566
- 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",
29684
+ 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",
29567
29685
  style: {
29568
29686
  top: `${position.top}px`,
29569
29687
  left: `${position.left}px`,
@@ -35475,6 +35593,17 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
35475
35593
  "[&_.is-editor-empty]:before:float-left",
35476
35594
  "[&_.is-editor-empty]:before:pointer-events-none",
35477
35595
  "[&_.is-editor-empty]:before:h-0",
35596
+ "[&_.ProseMirror-gapcursor]:pointer-events-none",
35597
+ "[&_.ProseMirror-gapcursor]:absolute",
35598
+ "[&_.ProseMirror-gapcursor]:hidden",
35599
+ "[&_.ProseMirror-gapcursor:after]:content-['']",
35600
+ "[&_.ProseMirror-gapcursor:after]:block",
35601
+ "[&_.ProseMirror-gapcursor:after]:absolute",
35602
+ "[&_.ProseMirror-gapcursor:after]:top-[-2px]",
35603
+ "[&_.ProseMirror-gapcursor:after]:w-8",
35604
+ "[&_.ProseMirror-gapcursor:after]:border-t-2",
35605
+ "[&_.ProseMirror-gapcursor:after]:border-primary",
35606
+ "[&.ProseMirror-focused_.ProseMirror-gapcursor]:block",
35478
35607
  "[&_ul[data-type='taskList']]:list-none",
35479
35608
  "[&_ul[data-type='taskList']]:pl-0",
35480
35609
  "[&_ul[data-type='taskList']_li]:flex",
@@ -36062,6 +36191,24 @@ var import_react67 = __toESM(require("react"), 1);
36062
36191
  var import_react68 = require("@tiptap/react");
36063
36192
  var import_lucide_react53 = require("lucide-react");
36064
36193
  var import_jsx_runtime93 = require("react/jsx-runtime");
36194
+ function MenuTableInsertGrid({
36195
+ insertLabel,
36196
+ previewTemplate,
36197
+ onInsert
36198
+ }) {
36199
+ const closeMenu = useDropdownMenuClose();
36200
+ return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
36201
+ TableInsertGrid,
36202
+ {
36203
+ insertLabel,
36204
+ previewTemplate,
36205
+ onInsert: (rows, cols) => {
36206
+ onInsert(rows, cols);
36207
+ closeMenu();
36208
+ }
36209
+ }
36210
+ );
36211
+ }
36065
36212
  function renderMenuItems(items) {
36066
36213
  return items.map((item, i) => {
36067
36214
  switch (item.type) {
@@ -36387,7 +36534,7 @@ function buildTableMenuItems(t, editor, onInsertTable) {
36387
36534
  type: "custom",
36388
36535
  key: "table-insert-grid",
36389
36536
  render: () => /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
36390
- TableInsertGrid,
36537
+ MenuTableInsertGrid,
36391
36538
  {
36392
36539
  insertLabel: t("tableMenu.insertTable"),
36393
36540
  previewTemplate: t("tableMenu.gridPreview"),
@@ -36579,13 +36726,14 @@ var MenuBar = ({
36579
36726
  type: "custom",
36580
36727
  key: "image",
36581
36728
  render: () => showImageInput ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(ImageInput, { onSubmit: handleImageUrl, onCancel: () => setShowImageInput(false) }) : /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(import_jsx_runtime93.Fragment, { children: [
36582
- /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(DropdownMenuItem, { label: t("menubar.image"), icon: import_lucide_react53.Image, onClick: () => setShowImageInput(true) }),
36729
+ /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(DropdownMenuItem, { label: t("menubar.image"), icon: import_lucide_react53.Image, onClick: () => setShowImageInput(true), closeOnSelect: false }),
36583
36730
  /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
36584
36731
  DropdownMenuItem,
36585
36732
  {
36586
36733
  label: t("menubar.imageUpload"),
36587
36734
  icon: import_lucide_react53.Upload,
36588
- onClick: () => fileInputRef.current?.click()
36735
+ onClick: () => fileInputRef.current?.click(),
36736
+ closeOnSelect: false
36589
36737
  }
36590
36738
  )
36591
36739
  ] })
@@ -36600,7 +36748,8 @@ var MenuBar = ({
36600
36748
  label: t("menubar.link"),
36601
36749
  icon: import_lucide_react53.Link,
36602
36750
  shortcut: t("menubar.linkShortcut"),
36603
- onClick: () => setShowLinkInput(true)
36751
+ onClick: () => setShowLinkInput(true),
36752
+ closeOnSelect: false
36604
36753
  }
36605
36754
  )
36606
36755
  },
@@ -36614,7 +36763,7 @@ var MenuBar = ({
36614
36763
  type: "custom",
36615
36764
  key: "table-grid",
36616
36765
  render: () => /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
36617
- TableInsertGrid,
36766
+ MenuTableInsertGrid,
36618
36767
  {
36619
36768
  insertLabel: t("tableMenu.insertTable"),
36620
36769
  previewTemplate: t("tableMenu.gridPreview"),
@@ -36914,7 +37063,7 @@ var UEditor = import_react69.default.forwardRef(({
36914
37063
  return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
36915
37064
  "div",
36916
37065
  {
36917
- className: cn("w-full rounded-lg border bg-background flex items-center justify-center text-muted-foreground", className),
37066
+ className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
36918
37067
  style: { minHeight },
36919
37068
  children: t("loading")
36920
37069
  }
@@ -36926,7 +37075,7 @@ var UEditor = import_react69.default.forwardRef(({
36926
37075
  className: cn(
36927
37076
  "group relative flex flex-col text-card-foreground",
36928
37077
  editable ? [
36929
- "rounded-2xl md:rounded-3xl border border-border/50 bg-card overflow-hidden",
37078
+ "border border-border/50 bg-card overflow-hidden",
36930
37079
  "transition-[transform,box-shadow,border-color,background-color] duration-300 ease-soft",
36931
37080
  "shadow-sm focus-within:shadow-md focus-within:border-primary/15",
36932
37081
  "backdrop-blur-sm",