@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.
package/dist/index.js CHANGED
@@ -708,7 +708,15 @@ var en_default = {
708
708
  heading1: "Heading 1",
709
709
  heading2: "Heading 2",
710
710
  heading3: "Heading 3",
711
- emoji: "Insert Emoji"
711
+ emoji: "Insert Emoji",
712
+ imageLayoutBlock: "Image Block",
713
+ imageLayoutLeft: "Wrap Text Right",
714
+ imageLayoutRight: "Wrap Text Left",
715
+ imageWidthSm: "Image Width Small",
716
+ imageWidthMd: "Image Width Medium",
717
+ imageWidthLg: "Image Width Large",
718
+ imageResetSize: "Reset Image Size",
719
+ imageDelete: "Delete Image"
712
720
  },
713
721
  slashCommand: {
714
722
  basicBlocks: "Basic Blocks",
@@ -932,7 +940,15 @@ var vi_default = {
932
940
  heading1: "Ti\xEAu \u0111\u1EC1 1",
933
941
  heading2: "Ti\xEAu \u0111\u1EC1 2",
934
942
  heading3: "Ti\xEAu \u0111\u1EC1 3",
935
- emoji: "Ch\xE8n Emoji"
943
+ emoji: "Ch\xE8n Emoji",
944
+ imageLayoutBlock: "\u1EA2nh D\u1EA1ng Kh\u1ED1i",
945
+ imageLayoutLeft: "\u1EA2nh Tr\xE1i, Ch\u1EEF Ph\u1EA3i",
946
+ imageLayoutRight: "\u1EA2nh Ph\u1EA3i, Ch\u1EEF Tr\xE1i",
947
+ imageWidthSm: "\u1EA2nh Nh\u1ECF",
948
+ imageWidthMd: "\u1EA2nh V\u1EEBa",
949
+ imageWidthLg: "\u1EA2nh L\u1EDBn",
950
+ imageResetSize: "\u0110\u1EB7t L\u1EA1i K\xEDch Th\u01B0\u1EDBc \u1EA2nh",
951
+ imageDelete: "X\xF3a \u1EA2nh"
936
952
  },
937
953
  slashCommand: {
938
954
  basicBlocks: "Kh\u1ED1i c\u01A1 b\u1EA3n",
@@ -1156,7 +1172,15 @@ var ko_default = {
1156
1172
  heading1: "\uC81C\uBAA9 1",
1157
1173
  heading2: "\uC81C\uBAA9 2",
1158
1174
  heading3: "\uC81C\uBAA9 3",
1159
- emoji: "\uC774\uBAA8\uC9C0 \uC0BD\uC785"
1175
+ emoji: "\uC774\uBAA8\uC9C0 \uC0BD\uC785",
1176
+ imageLayoutBlock: "\uC774\uBBF8\uC9C0 \uBE14\uB85D",
1177
+ imageLayoutLeft: "\uC67C\uCABD \uC774\uBBF8\uC9C0, \uC624\uB978\uCABD \uD14D\uC2A4\uD2B8",
1178
+ imageLayoutRight: "\uC624\uB978\uCABD \uC774\uBBF8\uC9C0, \uC67C\uCABD \uD14D\uC2A4\uD2B8",
1179
+ imageWidthSm: "\uC791\uC740 \uC774\uBBF8\uC9C0",
1180
+ imageWidthMd: "\uC911\uAC04 \uC774\uBBF8\uC9C0",
1181
+ imageWidthLg: "\uD070 \uC774\uBBF8\uC9C0",
1182
+ imageResetSize: "\uC774\uBBF8\uC9C0 \uD06C\uAE30 \uCD08\uAE30\uD654",
1183
+ imageDelete: "\uC774\uBBF8\uC9C0 \uC0AD\uC81C"
1160
1184
  },
1161
1185
  slashCommand: {
1162
1186
  basicBlocks: "\uAE30\uBCF8 \uBE14\uB85D",
@@ -1379,7 +1403,15 @@ var ja_default = {
1379
1403
  heading1: "\u898B\u51FA\u3057 1",
1380
1404
  heading2: "\u898B\u51FA\u3057 2",
1381
1405
  heading3: "\u898B\u51FA\u3057 3",
1382
- emoji: "\u7D75\u6587\u5B57\u3092\u633F\u5165"
1406
+ emoji: "\u7D75\u6587\u5B57\u3092\u633F\u5165",
1407
+ imageLayoutBlock: "\u753B\u50CF\u30D6\u30ED\u30C3\u30AF",
1408
+ imageLayoutLeft: "\u753B\u50CF\u3092\u5DE6\u3001\u6587\u5B57\u3092\u53F3\u306B\u56DE\u308A\u8FBC\u307F",
1409
+ imageLayoutRight: "\u753B\u50CF\u3092\u53F3\u3001\u6587\u5B57\u3092\u5DE6\u306B\u56DE\u308A\u8FBC\u307F",
1410
+ imageWidthSm: "\u753B\u50CF \u5C0F",
1411
+ imageWidthMd: "\u753B\u50CF \u4E2D",
1412
+ imageWidthLg: "\u753B\u50CF \u5927",
1413
+ imageResetSize: "\u753B\u50CF\u30B5\u30A4\u30BA\u3092\u30EA\u30BB\u30C3\u30C8",
1414
+ imageDelete: "\u753B\u50CF\u3092\u524A\u9664"
1383
1415
  },
1384
1416
  slashCommand: {
1385
1417
  basicBlocks: "\u57FA\u672C\u30D6\u30ED\u30C3\u30AF",
@@ -22381,6 +22413,8 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
22381
22413
  import { jsx as jsx73, jsxs as jsxs63 } from "react/jsx-runtime";
22382
22414
  var MIN_IMAGE_SIZE_PX = 40;
22383
22415
  var AXIS_LOCK_THRESHOLD_PX = 4;
22416
+ var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
22417
+ var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
22384
22418
  function toNullableNumber(value) {
22385
22419
  if (typeof value === "number" && Number.isFinite(value)) return value;
22386
22420
  if (typeof value === "string") {
@@ -22392,6 +22426,33 @@ function toNullableNumber(value) {
22392
22426
  function clamp8(value, min, max) {
22393
22427
  return Math.min(max, Math.max(min, value));
22394
22428
  }
22429
+ function parseImageLayout(value) {
22430
+ if (typeof value === "string" && IMAGE_LAYOUTS.has(value)) {
22431
+ return value;
22432
+ }
22433
+ return "block";
22434
+ }
22435
+ function parseImageWidthPreset(value) {
22436
+ if (typeof value === "string" && IMAGE_WIDTH_PRESETS.has(value)) {
22437
+ return value;
22438
+ }
22439
+ return null;
22440
+ }
22441
+ function getImageLayoutStyles(layout) {
22442
+ if (layout === "left") {
22443
+ return {
22444
+ "data-image-layout": "left",
22445
+ style: "float:left;display:block;margin:0.25rem 1rem 0.75rem 0;"
22446
+ };
22447
+ }
22448
+ if (layout === "right") {
22449
+ return {
22450
+ "data-image-layout": "right",
22451
+ style: "float:right;display:block;margin:0.25rem 0 0.75rem 1rem;"
22452
+ };
22453
+ }
22454
+ return {};
22455
+ }
22395
22456
  function ResizableImageNodeView(props) {
22396
22457
  const { node, selected, updateAttributes, editor, getPos } = props;
22397
22458
  const wrapperRef = useRef25(null);
@@ -22401,6 +22462,8 @@ function ResizableImageNodeView(props) {
22401
22462
  const widthAttr = toNullableNumber(node.attrs["width"]);
22402
22463
  const heightAttr = toNullableNumber(node.attrs["height"]);
22403
22464
  const textAlign = String(node.attrs["textAlign"] ?? "");
22465
+ const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
22466
+ const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
22404
22467
  const dragStateRef = useRef25(null);
22405
22468
  useEffect30(() => {
22406
22469
  const img = imgRef.current;
@@ -22449,7 +22512,8 @@ function ResizableImageNodeView(props) {
22449
22512
  const dy = event.clientY - drag.startY;
22450
22513
  let nextW = drag.startW;
22451
22514
  let nextH = drag.startH;
22452
- if (event.ctrlKey) {
22515
+ const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
22516
+ if (shouldPreserveAspect) {
22453
22517
  if (Math.abs(dx) >= Math.abs(dy)) {
22454
22518
  nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
22455
22519
  nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
@@ -22476,7 +22540,8 @@ function ResizableImageNodeView(props) {
22476
22540
  if (!drag) return;
22477
22541
  updateAttributes({
22478
22542
  width: Math.round(drag.lastW),
22479
- height: Math.round(drag.lastH)
22543
+ height: Math.round(drag.lastH),
22544
+ imageWidthPreset: null
22480
22545
  });
22481
22546
  };
22482
22547
  const onResizePointerUp = (event) => {
@@ -22494,14 +22559,15 @@ function ResizableImageNodeView(props) {
22494
22559
  finishResize();
22495
22560
  };
22496
22561
  const showHandle = selected || isHovered || isResizing;
22497
- const wrapperAlignClass = textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "";
22498
- const wrapperWidthClass = "w-fit";
22562
+ const wrapperAlignClass = imageLayout === "block" ? textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "" : "";
22563
+ 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";
22499
22564
  return /* @__PURE__ */ jsxs63(
22500
22565
  NodeViewWrapper,
22501
22566
  {
22502
22567
  as: "div",
22503
22568
  ref: wrapperRef,
22504
- className: ["relative block align-middle max-w-full my-4", wrapperWidthClass, wrapperAlignClass].filter(Boolean).join(" "),
22569
+ "data-image-layout": imageLayout,
22570
+ className: ["relative block align-middle max-w-full my-4", wrapperLayoutClass, wrapperAlignClass].filter(Boolean).join(" "),
22505
22571
  onMouseEnter: () => setIsHovered(true),
22506
22572
  onMouseLeave: () => setIsHovered(false),
22507
22573
  onClick: (e) => {
@@ -22571,6 +22637,25 @@ var ResizableImage = Image3.extend({
22571
22637
  return Number.isFinite(parsed) ? parsed : null;
22572
22638
  },
22573
22639
  renderHTML: (attrs) => typeof attrs.height === "number" ? { height: attrs.height } : {}
22640
+ },
22641
+ imageLayout: {
22642
+ default: "block",
22643
+ parseHTML: (element) => {
22644
+ const explicit = element.getAttribute("data-image-layout");
22645
+ if (explicit) return parseImageLayout(explicit);
22646
+ const floatValue = element.style.float;
22647
+ if (floatValue === "left" || floatValue === "right") return floatValue;
22648
+ return "block";
22649
+ },
22650
+ renderHTML: (attrs) => getImageLayoutStyles(parseImageLayout(attrs.imageLayout))
22651
+ },
22652
+ imageWidthPreset: {
22653
+ default: null,
22654
+ parseHTML: (element) => parseImageWidthPreset(element.getAttribute("data-image-size")),
22655
+ renderHTML: (attrs) => {
22656
+ const preset = parseImageWidthPreset(attrs.imageWidthPreset);
22657
+ return preset ? { "data-image-size": preset } : {};
22658
+ }
22574
22659
  }
22575
22660
  };
22576
22661
  },
@@ -22758,6 +22843,7 @@ import {
22758
22843
  Palette as Palette2,
22759
22844
  Quote as QuoteIcon,
22760
22845
  Redo as RedoIcon,
22846
+ RotateCcw as RotateCcw2,
22761
22847
  Smile as Smile3,
22762
22848
  Strikethrough as StrikethroughIcon,
22763
22849
  Subscript as SubscriptIcon,
@@ -22833,6 +22919,96 @@ var EditorColorPalette = ({
22833
22919
  )) })
22834
22920
  ] });
22835
22921
 
22922
+ // src/components/UEditor/image-commands.ts
22923
+ import { NodeSelection, TextSelection } from "@tiptap/pm/state";
22924
+ var IMAGE_WIDTHS_BY_LAYOUT = {
22925
+ block: {
22926
+ sm: 180,
22927
+ md: 280,
22928
+ lg: 380
22929
+ },
22930
+ wrap: {
22931
+ sm: 140,
22932
+ md: 200,
22933
+ lg: 260
22934
+ }
22935
+ };
22936
+ function isSelectedImage(editor) {
22937
+ const { selection } = editor.state;
22938
+ return selection instanceof NodeSelection && selection.node.type.name === "image";
22939
+ }
22940
+ function applyImageLayout(editor, layout) {
22941
+ const { state, view } = editor;
22942
+ const { selection, schema } = state;
22943
+ if (!(selection instanceof NodeSelection) || selection.node.type.name !== "image") {
22944
+ editor.chain().focus().updateAttributes("image", { imageLayout: layout }).run();
22945
+ return;
22946
+ }
22947
+ let transaction = state.tr.setNodeMarkup(selection.from, void 0, {
22948
+ ...selection.node.attrs,
22949
+ imageLayout: layout
22950
+ });
22951
+ if (layout !== "block") {
22952
+ const nextPos = transaction.mapping.map(selection.to);
22953
+ const nextNode = transaction.doc.nodeAt(nextPos);
22954
+ if (!nextNode || nextNode.type.name !== "paragraph") {
22955
+ const paragraph = schema.nodes.paragraph?.create();
22956
+ if (paragraph) {
22957
+ transaction = transaction.insert(nextPos, paragraph);
22958
+ }
22959
+ }
22960
+ const resolvedPos = transaction.doc.resolve(Math.min(nextPos + 1, transaction.doc.content.size));
22961
+ transaction = transaction.setSelection(TextSelection.near(resolvedPos));
22962
+ } else {
22963
+ const resolvedPos = transaction.doc.resolve(selection.from);
22964
+ transaction = transaction.setSelection(NodeSelection.create(transaction.doc, resolvedPos.pos));
22965
+ }
22966
+ view.dispatch(transaction.scrollIntoView());
22967
+ view.focus();
22968
+ }
22969
+ function applyImageWidthPreset(editor, preset) {
22970
+ const attrs = editor.getAttributes("image");
22971
+ const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
22972
+ const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
22973
+ if (!isSelectedImage(editor)) {
22974
+ editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
22975
+ return;
22976
+ }
22977
+ const { state, view } = editor;
22978
+ const selection = state.selection;
22979
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
22980
+ ...selection.node.attrs,
22981
+ width,
22982
+ imageWidthPreset: preset
22983
+ });
22984
+ view.dispatch(transaction.scrollIntoView());
22985
+ view.focus();
22986
+ }
22987
+ function resetImageSize(editor) {
22988
+ if (!isSelectedImage(editor)) {
22989
+ editor.chain().focus().updateAttributes("image", {
22990
+ width: null,
22991
+ height: null,
22992
+ imageWidthPreset: null
22993
+ }).run();
22994
+ return;
22995
+ }
22996
+ const { state, view } = editor;
22997
+ const selection = state.selection;
22998
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
22999
+ ...selection.node.attrs,
23000
+ width: null,
23001
+ height: null,
23002
+ imageWidthPreset: null
23003
+ });
23004
+ view.dispatch(transaction.scrollIntoView());
23005
+ view.focus();
23006
+ }
23007
+ function deleteSelectedImage(editor) {
23008
+ if (!isSelectedImage(editor)) return;
23009
+ editor.chain().focus().deleteSelection().run();
23010
+ }
23011
+
22836
23012
  // src/components/UEditor/inputs.tsx
22837
23013
  import { useEffect as useEffect31, useRef as useRef26, useState as useState42 } from "react";
22838
23014
  import { Check as Check10, X as X18 } from "lucide-react";
@@ -23169,6 +23345,10 @@ var EditorToolbar = ({
23169
23345
  const fileInputRef = useRef28(null);
23170
23346
  const [isUploadingImage, setIsUploadingImage] = useState44(false);
23171
23347
  const [imageUploadError, setImageUploadError] = useState44(null);
23348
+ const isImageSelected = editor.isActive("image");
23349
+ const imageAttrs = editor.getAttributes("image");
23350
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
23351
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23172
23352
  const insertImageFiles = async (files) => {
23173
23353
  if (files.length === 0) return;
23174
23354
  setIsUploadingImage(true);
@@ -23501,6 +23681,85 @@ var EditorToolbar = ({
23501
23681
  void insertImageFiles(files);
23502
23682
  }
23503
23683
  }
23684
+ ),
23685
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23686
+ /* @__PURE__ */ jsx77(
23687
+ DropdownMenuItem,
23688
+ {
23689
+ icon: AlignCenter,
23690
+ label: t("toolbar.imageLayoutBlock"),
23691
+ onClick: () => applyImageLayout(editor, "block"),
23692
+ active: isImageSelected && imageLayout === "block",
23693
+ disabled: !isImageSelected
23694
+ }
23695
+ ),
23696
+ /* @__PURE__ */ jsx77(
23697
+ DropdownMenuItem,
23698
+ {
23699
+ icon: AlignLeft,
23700
+ label: t("toolbar.imageLayoutLeft"),
23701
+ onClick: () => applyImageLayout(editor, "left"),
23702
+ active: isImageSelected && imageLayout === "left",
23703
+ disabled: !isImageSelected
23704
+ }
23705
+ ),
23706
+ /* @__PURE__ */ jsx77(
23707
+ DropdownMenuItem,
23708
+ {
23709
+ icon: AlignRight,
23710
+ label: t("toolbar.imageLayoutRight"),
23711
+ onClick: () => applyImageLayout(editor, "right"),
23712
+ active: isImageSelected && imageLayout === "right",
23713
+ disabled: !isImageSelected
23714
+ }
23715
+ ),
23716
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23717
+ /* @__PURE__ */ jsx77(
23718
+ DropdownMenuItem,
23719
+ {
23720
+ label: t("toolbar.imageWidthSm"),
23721
+ onClick: () => applyImageWidthPreset(editor, "sm"),
23722
+ active: isImageSelected && imageWidthPreset === "sm",
23723
+ disabled: !isImageSelected
23724
+ }
23725
+ ),
23726
+ /* @__PURE__ */ jsx77(
23727
+ DropdownMenuItem,
23728
+ {
23729
+ label: t("toolbar.imageWidthMd"),
23730
+ onClick: () => applyImageWidthPreset(editor, "md"),
23731
+ active: isImageSelected && imageWidthPreset === "md",
23732
+ disabled: !isImageSelected
23733
+ }
23734
+ ),
23735
+ /* @__PURE__ */ jsx77(
23736
+ DropdownMenuItem,
23737
+ {
23738
+ label: t("toolbar.imageWidthLg"),
23739
+ onClick: () => applyImageWidthPreset(editor, "lg"),
23740
+ active: isImageSelected && imageWidthPreset === "lg",
23741
+ disabled: !isImageSelected
23742
+ }
23743
+ ),
23744
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23745
+ /* @__PURE__ */ jsx77(
23746
+ DropdownMenuItem,
23747
+ {
23748
+ icon: RotateCcw2,
23749
+ label: t("toolbar.imageResetSize"),
23750
+ onClick: () => resetImageSize(editor),
23751
+ disabled: !isImageSelected
23752
+ }
23753
+ ),
23754
+ /* @__PURE__ */ jsx77(
23755
+ DropdownMenuItem,
23756
+ {
23757
+ icon: Trash22,
23758
+ label: t("toolbar.imageDelete"),
23759
+ onClick: () => deleteSelectedImage(editor),
23760
+ disabled: !isImageSelected,
23761
+ destructive: true
23762
+ }
23504
23763
  )
23505
23764
  ] })
23506
23765
  }
@@ -23619,6 +23878,9 @@ var EditorToolbar = ({
23619
23878
  import React70, { useCallback as useCallback17, useEffect as useEffect33, useMemo as useMemo22, useRef as useRef29, useState as useState45 } from "react";
23620
23879
  import { createPortal as createPortal8 } from "react-dom";
23621
23880
  import {
23881
+ AlignCenter as AlignCenter2,
23882
+ AlignLeft as AlignLeft2,
23883
+ AlignRight as AlignRight2,
23622
23884
  Bold as BoldIcon2,
23623
23885
  Code as CodeIcon2,
23624
23886
  FileCode as FileCode4,
@@ -23634,9 +23896,11 @@ import {
23634
23896
  Palette as Palette3,
23635
23897
  Plus as Plus3,
23636
23898
  Quote as QuoteIcon2,
23899
+ RotateCcw as RotateCcw3,
23637
23900
  Subscript as SubscriptIcon2,
23638
23901
  Superscript as SuperscriptIcon2,
23639
23902
  Table as TableIcon2,
23903
+ Trash2 as Trash23,
23640
23904
  Type as Type3,
23641
23905
  Underline as UnderlineIcon2,
23642
23906
  Strikethrough as StrikethroughIcon2
@@ -23835,6 +24099,10 @@ var BubbleMenuContent = ({
23835
24099
  const { textColors, highlightColors } = useEditorColors();
23836
24100
  const [showLinkInput, setShowLinkInput] = useState45(false);
23837
24101
  const [showEditorColorPalette, setShowEditorColorPalette] = useState45(false);
24102
+ const isImageSelected = editor.isActive("image");
24103
+ const imageAttrs = editor.getAttributes("image");
24104
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
24105
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23838
24106
  useEffect33(() => {
23839
24107
  onKeepOpenChange?.(showLinkInput);
23840
24108
  }, [onKeepOpenChange, showLinkInput]);
@@ -23909,6 +24177,20 @@ var BubbleMenuContent = ({
23909
24177
  ) })
23910
24178
  ] });
23911
24179
  }
24180
+ if (isImageSelected) {
24181
+ return /* @__PURE__ */ jsxs68("div", { className: "flex items-center gap-0.5 p-1", children: [
24182
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "block"), active: imageLayout === "block", title: t("toolbar.imageLayoutBlock"), children: /* @__PURE__ */ jsx78(AlignCenter2, { className: "w-4 h-4" }) }),
24183
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "left"), active: imageLayout === "left", title: t("toolbar.imageLayoutLeft"), children: /* @__PURE__ */ jsx78(AlignLeft2, { className: "w-4 h-4" }) }),
24184
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "right"), active: imageLayout === "right", title: t("toolbar.imageLayoutRight"), children: /* @__PURE__ */ jsx78(AlignRight2, { className: "w-4 h-4" }) }),
24185
+ /* @__PURE__ */ jsx78("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24186
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "sm"), active: imageWidthPreset === "sm", title: t("toolbar.imageWidthSm"), children: /* @__PURE__ */ jsx78("span", { className: "text-[10px] font-semibold", children: "S" }) }),
24187
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "md"), active: imageWidthPreset === "md", title: t("toolbar.imageWidthMd"), children: /* @__PURE__ */ jsx78("span", { className: "text-[10px] font-semibold", children: "M" }) }),
24188
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageWidthPreset(editor, "lg"), active: imageWidthPreset === "lg", title: t("toolbar.imageWidthLg"), children: /* @__PURE__ */ jsx78("span", { className: "text-[10px] font-semibold", children: "L" }) }),
24189
+ /* @__PURE__ */ jsx78("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24190
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => resetImageSize(editor), title: t("toolbar.imageResetSize"), children: /* @__PURE__ */ jsx78(RotateCcw3, { className: "w-4 h-4" }) }),
24191
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => deleteSelectedImage(editor), title: t("toolbar.imageDelete"), className: "text-destructive hover:text-destructive", children: /* @__PURE__ */ jsx78(Trash23, { className: "w-4 h-4" }) })
24192
+ ] });
24193
+ }
23912
24194
  return /* @__PURE__ */ jsxs68("div", { className: "flex items-center gap-0.5 p-1", children: [
23913
24195
  /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), title: t("toolbar.bold"), children: /* @__PURE__ */ jsx78(BoldIcon2, { className: "w-4 h-4" }) }),
23914
24196
  /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), title: t("toolbar.italic"), children: /* @__PURE__ */ jsx78(ItalicIcon2, { className: "w-4 h-4" }) }),
@@ -23961,19 +24243,28 @@ var BubbleMenuContent = ({
23961
24243
  ] });
23962
24244
  };
23963
24245
  var CustomBubbleMenu = ({ editor }) => {
24246
+ const SHOW_DELAY_MS = 180;
23964
24247
  const [isVisible, setIsVisible] = useState45(false);
23965
24248
  const [position, setPosition] = useState45({ top: 0, left: 0 });
23966
24249
  const menuRef = useRef29(null);
23967
24250
  const keepOpenRef = useRef29(false);
24251
+ const showTimeoutRef = useRef29(null);
23968
24252
  const setKeepOpen = useCallback17((next) => {
23969
24253
  keepOpenRef.current = next;
23970
24254
  if (next) setIsVisible(true);
23971
24255
  }, []);
23972
24256
  useEffect33(() => {
24257
+ const clearShowTimeout = () => {
24258
+ if (showTimeoutRef.current) {
24259
+ clearTimeout(showTimeoutRef.current);
24260
+ showTimeoutRef.current = null;
24261
+ }
24262
+ };
23973
24263
  const updatePosition = () => {
23974
24264
  const { state, view } = editor;
23975
24265
  const { from, to, empty } = state.selection;
23976
24266
  if (!keepOpenRef.current && (empty || !view.hasFocus())) {
24267
+ clearShowTimeout();
23977
24268
  setIsVisible(false);
23978
24269
  return;
23979
24270
  }
@@ -23982,15 +24273,28 @@ var CustomBubbleMenu = ({ editor }) => {
23982
24273
  const left = (start.left + end.left) / 2;
23983
24274
  const top = start.top - 10;
23984
24275
  setPosition({ top, left });
23985
- setIsVisible(true);
24276
+ if (keepOpenRef.current) {
24277
+ clearShowTimeout();
24278
+ setIsVisible(true);
24279
+ return;
24280
+ }
24281
+ clearShowTimeout();
24282
+ showTimeoutRef.current = setTimeout(() => {
24283
+ setIsVisible(true);
24284
+ showTimeoutRef.current = null;
24285
+ }, SHOW_DELAY_MS);
23986
24286
  };
23987
24287
  const handleBlur = () => {
23988
- if (!keepOpenRef.current) setIsVisible(false);
24288
+ if (!keepOpenRef.current) {
24289
+ clearShowTimeout();
24290
+ setIsVisible(false);
24291
+ }
23989
24292
  };
23990
24293
  editor.on("selectionUpdate", updatePosition);
23991
24294
  editor.on("focus", updatePosition);
23992
24295
  editor.on("blur", handleBlur);
23993
24296
  return () => {
24297
+ clearShowTimeout();
23994
24298
  editor.off("selectionUpdate", updatePosition);
23995
24299
  editor.off("focus", updatePosition);
23996
24300
  editor.off("blur", handleBlur);
@@ -24474,7 +24778,21 @@ var UEditor = React71.forwardRef(({
24474
24778
  "[&_blockquote]:rounded-r-lg",
24475
24779
  "[&_blockquote]:italic",
24476
24780
  "[&_blockquote]:text-muted-foreground",
24477
- "[&_blockquote_p]:my-0"
24781
+ "[&_blockquote_p]:my-0",
24782
+ "[&_[data-image-layout='left']+p]:mt-1",
24783
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
24784
+ "[&_[data-image-layout='right']+p]:mt-1",
24785
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
24786
+ "max-md:[&_[data-image-layout='left']]:float-none",
24787
+ "max-md:[&_[data-image-layout='left']]:mr-0",
24788
+ "max-md:[&_[data-image-layout='left']]:ml-0",
24789
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
24790
+ "max-md:[&_[data-image-layout='right']]:float-none",
24791
+ "max-md:[&_[data-image-layout='right']]:mr-0",
24792
+ "max-md:[&_[data-image-layout='right']]:ml-0",
24793
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
24794
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
24795
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
24478
24796
  )
24479
24797
  }
24480
24798
  },