@underverse-ui/underverse 1.0.63 → 1.0.64

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.63",
3
+ "version": "1.0.64",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 217,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -890,7 +890,15 @@ var en_default = {
890
890
  heading1: "Heading 1",
891
891
  heading2: "Heading 2",
892
892
  heading3: "Heading 3",
893
- emoji: "Insert Emoji"
893
+ emoji: "Insert Emoji",
894
+ imageLayoutBlock: "Image Block",
895
+ imageLayoutLeft: "Wrap Text Right",
896
+ imageLayoutRight: "Wrap Text Left",
897
+ imageWidthSm: "Image Width Small",
898
+ imageWidthMd: "Image Width Medium",
899
+ imageWidthLg: "Image Width Large",
900
+ imageResetSize: "Reset Image Size",
901
+ imageDelete: "Delete Image"
894
902
  },
895
903
  slashCommand: {
896
904
  basicBlocks: "Basic Blocks",
@@ -1114,7 +1122,15 @@ var vi_default = {
1114
1122
  heading1: "Ti\xEAu \u0111\u1EC1 1",
1115
1123
  heading2: "Ti\xEAu \u0111\u1EC1 2",
1116
1124
  heading3: "Ti\xEAu \u0111\u1EC1 3",
1117
- emoji: "Ch\xE8n Emoji"
1125
+ emoji: "Ch\xE8n Emoji",
1126
+ imageLayoutBlock: "\u1EA2nh D\u1EA1ng Kh\u1ED1i",
1127
+ imageLayoutLeft: "\u1EA2nh Tr\xE1i, Ch\u1EEF Ph\u1EA3i",
1128
+ imageLayoutRight: "\u1EA2nh Ph\u1EA3i, Ch\u1EEF Tr\xE1i",
1129
+ imageWidthSm: "\u1EA2nh Nh\u1ECF",
1130
+ imageWidthMd: "\u1EA2nh V\u1EEBa",
1131
+ imageWidthLg: "\u1EA2nh L\u1EDBn",
1132
+ imageResetSize: "\u0110\u1EB7t L\u1EA1i K\xEDch Th\u01B0\u1EDBc \u1EA2nh",
1133
+ imageDelete: "X\xF3a \u1EA2nh"
1118
1134
  },
1119
1135
  slashCommand: {
1120
1136
  basicBlocks: "Kh\u1ED1i c\u01A1 b\u1EA3n",
@@ -1338,7 +1354,15 @@ var ko_default = {
1338
1354
  heading1: "\uC81C\uBAA9 1",
1339
1355
  heading2: "\uC81C\uBAA9 2",
1340
1356
  heading3: "\uC81C\uBAA9 3",
1341
- emoji: "\uC774\uBAA8\uC9C0 \uC0BD\uC785"
1357
+ emoji: "\uC774\uBAA8\uC9C0 \uC0BD\uC785",
1358
+ imageLayoutBlock: "\uC774\uBBF8\uC9C0 \uBE14\uB85D",
1359
+ imageLayoutLeft: "\uC67C\uCABD \uC774\uBBF8\uC9C0, \uC624\uB978\uCABD \uD14D\uC2A4\uD2B8",
1360
+ imageLayoutRight: "\uC624\uB978\uCABD \uC774\uBBF8\uC9C0, \uC67C\uCABD \uD14D\uC2A4\uD2B8",
1361
+ imageWidthSm: "\uC791\uC740 \uC774\uBBF8\uC9C0",
1362
+ imageWidthMd: "\uC911\uAC04 \uC774\uBBF8\uC9C0",
1363
+ imageWidthLg: "\uD070 \uC774\uBBF8\uC9C0",
1364
+ imageResetSize: "\uC774\uBBF8\uC9C0 \uD06C\uAE30 \uCD08\uAE30\uD654",
1365
+ imageDelete: "\uC774\uBBF8\uC9C0 \uC0AD\uC81C"
1342
1366
  },
1343
1367
  slashCommand: {
1344
1368
  basicBlocks: "\uAE30\uBCF8 \uBE14\uB85D",
@@ -1561,7 +1585,15 @@ var ja_default = {
1561
1585
  heading1: "\u898B\u51FA\u3057 1",
1562
1586
  heading2: "\u898B\u51FA\u3057 2",
1563
1587
  heading3: "\u898B\u51FA\u3057 3",
1564
- emoji: "\u7D75\u6587\u5B57\u3092\u633F\u5165"
1588
+ emoji: "\u7D75\u6587\u5B57\u3092\u633F\u5165",
1589
+ imageLayoutBlock: "\u753B\u50CF\u30D6\u30ED\u30C3\u30AF",
1590
+ imageLayoutLeft: "\u753B\u50CF\u3092\u5DE6\u3001\u6587\u5B57\u3092\u53F3\u306B\u56DE\u308A\u8FBC\u307F",
1591
+ imageLayoutRight: "\u753B\u50CF\u3092\u53F3\u3001\u6587\u5B57\u3092\u5DE6\u306B\u56DE\u308A\u8FBC\u307F",
1592
+ imageWidthSm: "\u753B\u50CF \u5C0F",
1593
+ imageWidthMd: "\u753B\u50CF \u4E2D",
1594
+ imageWidthLg: "\u753B\u50CF \u5927",
1595
+ imageResetSize: "\u753B\u50CF\u30B5\u30A4\u30BA\u3092\u30EA\u30BB\u30C3\u30C8",
1596
+ imageDelete: "\u753B\u50CF\u3092\u524A\u9664"
1565
1597
  },
1566
1598
  slashCommand: {
1567
1599
  basicBlocks: "\u57FA\u672C\u30D6\u30ED\u30C3\u30AF",
@@ -22535,6 +22567,8 @@ var import_react44 = require("@tiptap/react");
22535
22567
  var import_jsx_runtime73 = require("react/jsx-runtime");
22536
22568
  var MIN_IMAGE_SIZE_PX = 40;
22537
22569
  var AXIS_LOCK_THRESHOLD_PX = 4;
22570
+ var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
22571
+ var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
22538
22572
  function toNullableNumber(value) {
22539
22573
  if (typeof value === "number" && Number.isFinite(value)) return value;
22540
22574
  if (typeof value === "string") {
@@ -22546,6 +22580,33 @@ function toNullableNumber(value) {
22546
22580
  function clamp8(value, min, max) {
22547
22581
  return Math.min(max, Math.max(min, value));
22548
22582
  }
22583
+ function parseImageLayout(value) {
22584
+ if (typeof value === "string" && IMAGE_LAYOUTS.has(value)) {
22585
+ return value;
22586
+ }
22587
+ return "block";
22588
+ }
22589
+ function parseImageWidthPreset(value) {
22590
+ if (typeof value === "string" && IMAGE_WIDTH_PRESETS.has(value)) {
22591
+ return value;
22592
+ }
22593
+ return null;
22594
+ }
22595
+ function getImageLayoutStyles(layout) {
22596
+ if (layout === "left") {
22597
+ return {
22598
+ "data-image-layout": "left",
22599
+ style: "float:left;display:block;margin:0.25rem 1rem 0.75rem 0;"
22600
+ };
22601
+ }
22602
+ if (layout === "right") {
22603
+ return {
22604
+ "data-image-layout": "right",
22605
+ style: "float:right;display:block;margin:0.25rem 0 0.75rem 1rem;"
22606
+ };
22607
+ }
22608
+ return {};
22609
+ }
22549
22610
  function ResizableImageNodeView(props) {
22550
22611
  const { node, selected, updateAttributes, editor, getPos } = props;
22551
22612
  const wrapperRef = (0, import_react43.useRef)(null);
@@ -22555,6 +22616,8 @@ function ResizableImageNodeView(props) {
22555
22616
  const widthAttr = toNullableNumber(node.attrs["width"]);
22556
22617
  const heightAttr = toNullableNumber(node.attrs["height"]);
22557
22618
  const textAlign = String(node.attrs["textAlign"] ?? "");
22619
+ const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
22620
+ const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
22558
22621
  const dragStateRef = (0, import_react43.useRef)(null);
22559
22622
  (0, import_react43.useEffect)(() => {
22560
22623
  const img = imgRef.current;
@@ -22603,7 +22666,8 @@ function ResizableImageNodeView(props) {
22603
22666
  const dy = event.clientY - drag.startY;
22604
22667
  let nextW = drag.startW;
22605
22668
  let nextH = drag.startH;
22606
- if (event.ctrlKey) {
22669
+ const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
22670
+ if (shouldPreserveAspect) {
22607
22671
  if (Math.abs(dx) >= Math.abs(dy)) {
22608
22672
  nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
22609
22673
  nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
@@ -22630,7 +22694,8 @@ function ResizableImageNodeView(props) {
22630
22694
  if (!drag) return;
22631
22695
  updateAttributes({
22632
22696
  width: Math.round(drag.lastW),
22633
- height: Math.round(drag.lastH)
22697
+ height: Math.round(drag.lastH),
22698
+ imageWidthPreset: null
22634
22699
  });
22635
22700
  };
22636
22701
  const onResizePointerUp = (event) => {
@@ -22648,14 +22713,15 @@ function ResizableImageNodeView(props) {
22648
22713
  finishResize();
22649
22714
  };
22650
22715
  const showHandle = selected || isHovered || isResizing;
22651
- const wrapperAlignClass = textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "";
22652
- const wrapperWidthClass = "w-fit";
22716
+ const wrapperAlignClass = imageLayout === "block" ? textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "" : "";
22717
+ const wrapperLayoutClass = imageLayout === "left" ? "float-left mr-4 mb-3 mt-1 clear-none max-w-[min(45%,20rem)]" : imageLayout === "right" ? "float-right ml-4 mb-3 mt-1 clear-none max-w-[min(45%,20rem)]" : "w-fit";
22653
22718
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
22654
22719
  import_react44.NodeViewWrapper,
22655
22720
  {
22656
22721
  as: "div",
22657
22722
  ref: wrapperRef,
22658
- className: ["relative block align-middle max-w-full my-4", wrapperWidthClass, wrapperAlignClass].filter(Boolean).join(" "),
22723
+ "data-image-layout": imageLayout,
22724
+ className: ["relative block align-middle max-w-full my-4", wrapperLayoutClass, wrapperAlignClass].filter(Boolean).join(" "),
22659
22725
  onMouseEnter: () => setIsHovered(true),
22660
22726
  onMouseLeave: () => setIsHovered(false),
22661
22727
  onClick: (e) => {
@@ -22725,6 +22791,25 @@ var ResizableImage = import_extension_image.default.extend({
22725
22791
  return Number.isFinite(parsed) ? parsed : null;
22726
22792
  },
22727
22793
  renderHTML: (attrs) => typeof attrs.height === "number" ? { height: attrs.height } : {}
22794
+ },
22795
+ imageLayout: {
22796
+ default: "block",
22797
+ parseHTML: (element) => {
22798
+ const explicit = element.getAttribute("data-image-layout");
22799
+ if (explicit) return parseImageLayout(explicit);
22800
+ const floatValue = element.style.float;
22801
+ if (floatValue === "left" || floatValue === "right") return floatValue;
22802
+ return "block";
22803
+ },
22804
+ renderHTML: (attrs) => getImageLayoutStyles(parseImageLayout(attrs.imageLayout))
22805
+ },
22806
+ imageWidthPreset: {
22807
+ default: null,
22808
+ parseHTML: (element) => parseImageWidthPreset(element.getAttribute("data-image-size")),
22809
+ renderHTML: (attrs) => {
22810
+ const preset = parseImageWidthPreset(attrs.imageWidthPreset);
22811
+ return preset ? { "data-image-size": preset } : {};
22812
+ }
22728
22813
  }
22729
22814
  };
22730
22815
  },
@@ -22953,6 +23038,96 @@ var EditorColorPalette = ({
22953
23038
  )) })
22954
23039
  ] });
22955
23040
 
23041
+ // src/components/UEditor/image-commands.ts
23042
+ var import_state4 = require("@tiptap/pm/state");
23043
+ var IMAGE_WIDTHS_BY_LAYOUT = {
23044
+ block: {
23045
+ sm: 180,
23046
+ md: 280,
23047
+ lg: 380
23048
+ },
23049
+ wrap: {
23050
+ sm: 140,
23051
+ md: 200,
23052
+ lg: 260
23053
+ }
23054
+ };
23055
+ function isSelectedImage(editor) {
23056
+ const { selection } = editor.state;
23057
+ return selection instanceof import_state4.NodeSelection && selection.node.type.name === "image";
23058
+ }
23059
+ function applyImageLayout(editor, layout) {
23060
+ const { state, view } = editor;
23061
+ const { selection, schema } = state;
23062
+ if (!(selection instanceof import_state4.NodeSelection) || selection.node.type.name !== "image") {
23063
+ editor.chain().focus().updateAttributes("image", { imageLayout: layout }).run();
23064
+ return;
23065
+ }
23066
+ let transaction = state.tr.setNodeMarkup(selection.from, void 0, {
23067
+ ...selection.node.attrs,
23068
+ imageLayout: layout
23069
+ });
23070
+ if (layout !== "block") {
23071
+ const nextPos = transaction.mapping.map(selection.to);
23072
+ const nextNode = transaction.doc.nodeAt(nextPos);
23073
+ if (!nextNode || nextNode.type.name !== "paragraph") {
23074
+ const paragraph = schema.nodes.paragraph?.create();
23075
+ if (paragraph) {
23076
+ transaction = transaction.insert(nextPos, paragraph);
23077
+ }
23078
+ }
23079
+ const resolvedPos = transaction.doc.resolve(Math.min(nextPos + 1, transaction.doc.content.size));
23080
+ transaction = transaction.setSelection(import_state4.TextSelection.near(resolvedPos));
23081
+ } else {
23082
+ const resolvedPos = transaction.doc.resolve(selection.from);
23083
+ transaction = transaction.setSelection(import_state4.NodeSelection.create(transaction.doc, resolvedPos.pos));
23084
+ }
23085
+ view.dispatch(transaction.scrollIntoView());
23086
+ view.focus();
23087
+ }
23088
+ function applyImageWidthPreset(editor, preset) {
23089
+ const attrs = editor.getAttributes("image");
23090
+ const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
23091
+ const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
23092
+ if (!isSelectedImage(editor)) {
23093
+ editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
23094
+ return;
23095
+ }
23096
+ const { state, view } = editor;
23097
+ const selection = state.selection;
23098
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
23099
+ ...selection.node.attrs,
23100
+ width,
23101
+ imageWidthPreset: preset
23102
+ });
23103
+ view.dispatch(transaction.scrollIntoView());
23104
+ view.focus();
23105
+ }
23106
+ function resetImageSize(editor) {
23107
+ if (!isSelectedImage(editor)) {
23108
+ editor.chain().focus().updateAttributes("image", {
23109
+ width: null,
23110
+ height: null,
23111
+ imageWidthPreset: null
23112
+ }).run();
23113
+ return;
23114
+ }
23115
+ const { state, view } = editor;
23116
+ const selection = state.selection;
23117
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
23118
+ ...selection.node.attrs,
23119
+ width: null,
23120
+ height: null,
23121
+ imageWidthPreset: null
23122
+ });
23123
+ view.dispatch(transaction.scrollIntoView());
23124
+ view.focus();
23125
+ }
23126
+ function deleteSelectedImage(editor) {
23127
+ if (!isSelectedImage(editor)) return;
23128
+ editor.chain().focus().deleteSelection().run();
23129
+ }
23130
+
22956
23131
  // src/components/UEditor/inputs.tsx
22957
23132
  var import_react46 = require("react");
22958
23133
  var import_lucide_react42 = require("lucide-react");
@@ -23289,6 +23464,10 @@ var EditorToolbar = ({
23289
23464
  const fileInputRef = (0, import_react48.useRef)(null);
23290
23465
  const [isUploadingImage, setIsUploadingImage] = (0, import_react48.useState)(false);
23291
23466
  const [imageUploadError, setImageUploadError] = (0, import_react48.useState)(null);
23467
+ const isImageSelected = editor.isActive("image");
23468
+ const imageAttrs = editor.getAttributes("image");
23469
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
23470
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23292
23471
  const insertImageFiles = async (files) => {
23293
23472
  if (files.length === 0) return;
23294
23473
  setIsUploadingImage(true);
@@ -23621,6 +23800,85 @@ var EditorToolbar = ({
23621
23800
  void insertImageFiles(files);
23622
23801
  }
23623
23802
  }
23803
+ ),
23804
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "my-1 border-t" }),
23805
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23806
+ DropdownMenuItem,
23807
+ {
23808
+ icon: import_lucide_react44.AlignCenter,
23809
+ label: t("toolbar.imageLayoutBlock"),
23810
+ onClick: () => applyImageLayout(editor, "block"),
23811
+ active: isImageSelected && imageLayout === "block",
23812
+ disabled: !isImageSelected
23813
+ }
23814
+ ),
23815
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23816
+ DropdownMenuItem,
23817
+ {
23818
+ icon: import_lucide_react44.AlignLeft,
23819
+ label: t("toolbar.imageLayoutLeft"),
23820
+ onClick: () => applyImageLayout(editor, "left"),
23821
+ active: isImageSelected && imageLayout === "left",
23822
+ disabled: !isImageSelected
23823
+ }
23824
+ ),
23825
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23826
+ DropdownMenuItem,
23827
+ {
23828
+ icon: import_lucide_react44.AlignRight,
23829
+ label: t("toolbar.imageLayoutRight"),
23830
+ onClick: () => applyImageLayout(editor, "right"),
23831
+ active: isImageSelected && imageLayout === "right",
23832
+ disabled: !isImageSelected
23833
+ }
23834
+ ),
23835
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "my-1 border-t" }),
23836
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23837
+ DropdownMenuItem,
23838
+ {
23839
+ label: t("toolbar.imageWidthSm"),
23840
+ onClick: () => applyImageWidthPreset(editor, "sm"),
23841
+ active: isImageSelected && imageWidthPreset === "sm",
23842
+ disabled: !isImageSelected
23843
+ }
23844
+ ),
23845
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23846
+ DropdownMenuItem,
23847
+ {
23848
+ label: t("toolbar.imageWidthMd"),
23849
+ onClick: () => applyImageWidthPreset(editor, "md"),
23850
+ active: isImageSelected && imageWidthPreset === "md",
23851
+ disabled: !isImageSelected
23852
+ }
23853
+ ),
23854
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23855
+ DropdownMenuItem,
23856
+ {
23857
+ label: t("toolbar.imageWidthLg"),
23858
+ onClick: () => applyImageWidthPreset(editor, "lg"),
23859
+ active: isImageSelected && imageWidthPreset === "lg",
23860
+ disabled: !isImageSelected
23861
+ }
23862
+ ),
23863
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "my-1 border-t" }),
23864
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23865
+ DropdownMenuItem,
23866
+ {
23867
+ icon: import_lucide_react44.RotateCcw,
23868
+ label: t("toolbar.imageResetSize"),
23869
+ onClick: () => resetImageSize(editor),
23870
+ disabled: !isImageSelected
23871
+ }
23872
+ ),
23873
+ /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
23874
+ DropdownMenuItem,
23875
+ {
23876
+ icon: import_lucide_react44.Trash2,
23877
+ label: t("toolbar.imageDelete"),
23878
+ onClick: () => deleteSelectedImage(editor),
23879
+ disabled: !isImageSelected,
23880
+ destructive: true
23881
+ }
23624
23882
  )
23625
23883
  ] })
23626
23884
  }
@@ -23933,6 +24191,10 @@ var BubbleMenuContent = ({
23933
24191
  const { textColors, highlightColors } = useEditorColors();
23934
24192
  const [showLinkInput, setShowLinkInput] = (0, import_react49.useState)(false);
23935
24193
  const [showEditorColorPalette, setShowEditorColorPalette] = (0, import_react49.useState)(false);
24194
+ const isImageSelected = editor.isActive("image");
24195
+ const imageAttrs = editor.getAttributes("image");
24196
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
24197
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23936
24198
  (0, import_react49.useEffect)(() => {
23937
24199
  onKeepOpenChange?.(showLinkInput);
23938
24200
  }, [onKeepOpenChange, showLinkInput]);
@@ -24007,6 +24269,20 @@ var BubbleMenuContent = ({
24007
24269
  ) })
24008
24270
  ] });
24009
24271
  }
24272
+ if (isImageSelected) {
24273
+ return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "flex items-center gap-0.5 p-1", children: [
24274
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageLayout(editor, "block"), active: imageLayout === "block", title: t("toolbar.imageLayoutBlock"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.AlignCenter, { className: "w-4 h-4" }) }),
24275
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageLayout(editor, "left"), active: imageLayout === "left", title: t("toolbar.imageLayoutLeft"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.AlignLeft, { className: "w-4 h-4" }) }),
24276
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageLayout(editor, "right"), active: imageLayout === "right", title: t("toolbar.imageLayoutRight"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.AlignRight, { className: "w-4 h-4" }) }),
24277
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24278
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "sm"), active: imageWidthPreset === "sm", title: t("toolbar.imageWidthSm"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "text-[10px] font-semibold", children: "S" }) }),
24279
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "md"), active: imageWidthPreset === "md", title: t("toolbar.imageWidthMd"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "text-[10px] font-semibold", children: "M" }) }),
24280
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "lg"), active: imageWidthPreset === "lg", title: t("toolbar.imageWidthLg"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: "text-[10px] font-semibold", children: "L" }) }),
24281
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24282
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => resetImageSize(editor), title: t("toolbar.imageResetSize"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.RotateCcw, { className: "w-4 h-4" }) }),
24283
+ /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => deleteSelectedImage(editor), title: t("toolbar.imageDelete"), className: "text-destructive hover:text-destructive", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.Trash2, { className: "w-4 h-4" }) })
24284
+ ] });
24285
+ }
24010
24286
  return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "flex items-center gap-0.5 p-1", children: [
24011
24287
  /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.Bold, { className: "w-4 h-4" }) }),
24012
24288
  /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(import_lucide_react45.Italic, { className: "w-4 h-4" }) }),
@@ -24059,19 +24335,28 @@ var BubbleMenuContent = ({
24059
24335
  ] });
24060
24336
  };
24061
24337
  var CustomBubbleMenu = ({ editor }) => {
24338
+ const SHOW_DELAY_MS = 180;
24062
24339
  const [isVisible, setIsVisible] = (0, import_react49.useState)(false);
24063
24340
  const [position, setPosition] = (0, import_react49.useState)({ top: 0, left: 0 });
24064
24341
  const menuRef = (0, import_react49.useRef)(null);
24065
24342
  const keepOpenRef = (0, import_react49.useRef)(false);
24343
+ const showTimeoutRef = (0, import_react49.useRef)(null);
24066
24344
  const setKeepOpen = (0, import_react49.useCallback)((next) => {
24067
24345
  keepOpenRef.current = next;
24068
24346
  if (next) setIsVisible(true);
24069
24347
  }, []);
24070
24348
  (0, import_react49.useEffect)(() => {
24349
+ const clearShowTimeout = () => {
24350
+ if (showTimeoutRef.current) {
24351
+ clearTimeout(showTimeoutRef.current);
24352
+ showTimeoutRef.current = null;
24353
+ }
24354
+ };
24071
24355
  const updatePosition = () => {
24072
24356
  const { state, view } = editor;
24073
24357
  const { from, to, empty } = state.selection;
24074
24358
  if (!keepOpenRef.current && (empty || !view.hasFocus())) {
24359
+ clearShowTimeout();
24075
24360
  setIsVisible(false);
24076
24361
  return;
24077
24362
  }
@@ -24080,15 +24365,28 @@ var CustomBubbleMenu = ({ editor }) => {
24080
24365
  const left = (start.left + end.left) / 2;
24081
24366
  const top = start.top - 10;
24082
24367
  setPosition({ top, left });
24083
- setIsVisible(true);
24368
+ if (keepOpenRef.current) {
24369
+ clearShowTimeout();
24370
+ setIsVisible(true);
24371
+ return;
24372
+ }
24373
+ clearShowTimeout();
24374
+ showTimeoutRef.current = setTimeout(() => {
24375
+ setIsVisible(true);
24376
+ showTimeoutRef.current = null;
24377
+ }, SHOW_DELAY_MS);
24084
24378
  };
24085
24379
  const handleBlur = () => {
24086
- if (!keepOpenRef.current) setIsVisible(false);
24380
+ if (!keepOpenRef.current) {
24381
+ clearShowTimeout();
24382
+ setIsVisible(false);
24383
+ }
24087
24384
  };
24088
24385
  editor.on("selectionUpdate", updatePosition);
24089
24386
  editor.on("focus", updatePosition);
24090
24387
  editor.on("blur", handleBlur);
24091
24388
  return () => {
24389
+ clearShowTimeout();
24092
24390
  editor.off("selectionUpdate", updatePosition);
24093
24391
  editor.off("focus", updatePosition);
24094
24392
  editor.off("blur", handleBlur);
@@ -24572,7 +24870,21 @@ var UEditor = import_react50.default.forwardRef(({
24572
24870
  "[&_blockquote]:rounded-r-lg",
24573
24871
  "[&_blockquote]:italic",
24574
24872
  "[&_blockquote]:text-muted-foreground",
24575
- "[&_blockquote_p]:my-0"
24873
+ "[&_blockquote_p]:my-0",
24874
+ "[&_[data-image-layout='left']+p]:mt-1",
24875
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
24876
+ "[&_[data-image-layout='right']+p]:mt-1",
24877
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
24878
+ "max-md:[&_[data-image-layout='left']]:float-none",
24879
+ "max-md:[&_[data-image-layout='left']]:mr-0",
24880
+ "max-md:[&_[data-image-layout='left']]:ml-0",
24881
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
24882
+ "max-md:[&_[data-image-layout='right']]:float-none",
24883
+ "max-md:[&_[data-image-layout='right']]:mr-0",
24884
+ "max-md:[&_[data-image-layout='right']]:ml-0",
24885
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
24886
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
24887
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
24576
24888
  )
24577
24889
  }
24578
24890
  },