@underverse-ui/underverse 1.0.63 → 1.0.65

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",
@@ -16218,12 +16250,12 @@ function Carousel({
16218
16250
  mainScale: 1.12,
16219
16251
  sideScale: 0.82,
16220
16252
  farScale: 0.72,
16221
- sideOpacity: 0.72,
16222
- farOpacity: 0.24,
16253
+ sideOpacity: 0.84,
16254
+ farOpacity: 0.44,
16223
16255
  sideOffset: 22,
16224
16256
  rotate: 20,
16225
16257
  depthStep: 120,
16226
- blur: 2.6
16258
+ blur: 1.6
16227
16259
  };
16228
16260
  }
16229
16261
  if (effectPreset === "gallery") {
@@ -16302,12 +16334,12 @@ function Carousel({
16302
16334
  mainScale: 1.04,
16303
16335
  sideScale: effectiveAnimation === "stack" ? 0.93 : 0.88,
16304
16336
  farScale: effectiveAnimation === "stack" ? 0.86 : 0.76,
16305
- sideOpacity: effectiveAnimation === "stack" ? 0.74 : 0.78,
16306
- farOpacity: effectiveAnimation === "stack" ? 0.42 : 0.38,
16337
+ sideOpacity: effectiveAnimation === "stack" ? 0.8 : 0.86,
16338
+ farOpacity: effectiveAnimation === "stack" ? 0.5 : 0.48,
16307
16339
  sideOffset: effectiveAnimation === "stack" ? 20 : 28,
16308
16340
  rotate: 24,
16309
16341
  depthStep: effectiveAnimation === "stack" ? 60 : 90,
16310
- blur: 1.5,
16342
+ blur: 1.1,
16311
16343
  stackOffset: 20,
16312
16344
  stackLift: 12,
16313
16345
  ...presetEffectOptions,
@@ -16460,7 +16492,7 @@ function Carousel({
16460
16492
  opacity: distance === 0 ? 1 : distance === 1 || distance === -1 ? mergedEffectOptions.sideOpacity : mergedEffectOptions.farOpacity,
16461
16493
  transform: `translate3d(${xOffset2}px, ${yOffset}px, -${absDistance * mergedEffectOptions.depthStep}px) scale(${scale2})`,
16462
16494
  filter: distance === 0 ? "blur(0px)" : `blur(${Math.min(absDistance, 2) * mergedEffectOptions.blur}px)`,
16463
- pointerEvents: distance === 0 ? "auto" : "none"
16495
+ pointerEvents: "auto"
16464
16496
  };
16465
16497
  }
16466
16498
  const xOffset = distance * mergedEffectOptions.sideOffset;
@@ -16470,7 +16502,7 @@ function Carousel({
16470
16502
  opacity: distance === 0 ? 1 : distance === 1 || distance === -1 ? mergedEffectOptions.sideOpacity : mergedEffectOptions.farOpacity,
16471
16503
  transform: `translate3d(${xOffset}%, 0, -${absDistance * mergedEffectOptions.depthStep}px) rotateY(${rotateY}deg) scale(${scale})`,
16472
16504
  filter: distance === 0 ? "blur(0px)" : `blur(${Math.min(absDistance, 2) * mergedEffectOptions.blur}px)`,
16473
- pointerEvents: distance === 0 ? "auto" : "none"
16505
+ pointerEvents: "auto"
16474
16506
  };
16475
16507
  },
16476
16508
  [effectiveAnimation, getLoopDistance, mergedEffectOptions]
@@ -16522,10 +16554,12 @@ function Carousel({
16522
16554
  effectiveAnimation === "fade" && (idx === currentIndex ? "opacity-100 z-10" : "opacity-0 pointer-events-none z-0"),
16523
16555
  effectiveAnimation === "scale" && (idx === currentIndex ? "opacity-100 scale-100 z-10" : "opacity-0 scale-95 pointer-events-none z-0"),
16524
16556
  isDeckAnimation && "w-full max-w-[78%] md:max-w-[72%] transition-[opacity,transform] duration-500 ease-out",
16557
+ isDeckAnimation && idx !== currentIndex && "cursor-pointer",
16525
16558
  effectiveAnimation !== "slide" && "transition-[opacity,transform] duration-500 ease-in-out",
16526
16559
  slideClassName
16527
16560
  ),
16528
16561
  style: effectiveAnimation === "slide" ? { [isHorizontal ? "width" : "height"]: `${slideWidth}%` } : isDeckAnimation ? getDeckSlideStyles(idx) : void 0,
16562
+ onClick: isDeckAnimation && idx !== currentIndex ? () => scrollTo(idx) : void 0,
16529
16563
  role: "group",
16530
16564
  "aria-roledescription": "slide",
16531
16565
  "aria-label": `${idx + 1} of ${totalSlides}`,
@@ -22381,6 +22415,8 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
22381
22415
  import { jsx as jsx73, jsxs as jsxs63 } from "react/jsx-runtime";
22382
22416
  var MIN_IMAGE_SIZE_PX = 40;
22383
22417
  var AXIS_LOCK_THRESHOLD_PX = 4;
22418
+ var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
22419
+ var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
22384
22420
  function toNullableNumber(value) {
22385
22421
  if (typeof value === "number" && Number.isFinite(value)) return value;
22386
22422
  if (typeof value === "string") {
@@ -22392,6 +22428,33 @@ function toNullableNumber(value) {
22392
22428
  function clamp8(value, min, max) {
22393
22429
  return Math.min(max, Math.max(min, value));
22394
22430
  }
22431
+ function parseImageLayout(value) {
22432
+ if (typeof value === "string" && IMAGE_LAYOUTS.has(value)) {
22433
+ return value;
22434
+ }
22435
+ return "block";
22436
+ }
22437
+ function parseImageWidthPreset(value) {
22438
+ if (typeof value === "string" && IMAGE_WIDTH_PRESETS.has(value)) {
22439
+ return value;
22440
+ }
22441
+ return null;
22442
+ }
22443
+ function getImageLayoutStyles(layout) {
22444
+ if (layout === "left") {
22445
+ return {
22446
+ "data-image-layout": "left",
22447
+ style: "float:left;display:block;margin:0.25rem 1rem 0.75rem 0;"
22448
+ };
22449
+ }
22450
+ if (layout === "right") {
22451
+ return {
22452
+ "data-image-layout": "right",
22453
+ style: "float:right;display:block;margin:0.25rem 0 0.75rem 1rem;"
22454
+ };
22455
+ }
22456
+ return {};
22457
+ }
22395
22458
  function ResizableImageNodeView(props) {
22396
22459
  const { node, selected, updateAttributes, editor, getPos } = props;
22397
22460
  const wrapperRef = useRef25(null);
@@ -22401,6 +22464,8 @@ function ResizableImageNodeView(props) {
22401
22464
  const widthAttr = toNullableNumber(node.attrs["width"]);
22402
22465
  const heightAttr = toNullableNumber(node.attrs["height"]);
22403
22466
  const textAlign = String(node.attrs["textAlign"] ?? "");
22467
+ const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
22468
+ const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
22404
22469
  const dragStateRef = useRef25(null);
22405
22470
  useEffect30(() => {
22406
22471
  const img = imgRef.current;
@@ -22449,7 +22514,8 @@ function ResizableImageNodeView(props) {
22449
22514
  const dy = event.clientY - drag.startY;
22450
22515
  let nextW = drag.startW;
22451
22516
  let nextH = drag.startH;
22452
- if (event.ctrlKey) {
22517
+ const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
22518
+ if (shouldPreserveAspect) {
22453
22519
  if (Math.abs(dx) >= Math.abs(dy)) {
22454
22520
  nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
22455
22521
  nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
@@ -22476,7 +22542,8 @@ function ResizableImageNodeView(props) {
22476
22542
  if (!drag) return;
22477
22543
  updateAttributes({
22478
22544
  width: Math.round(drag.lastW),
22479
- height: Math.round(drag.lastH)
22545
+ height: Math.round(drag.lastH),
22546
+ imageWidthPreset: null
22480
22547
  });
22481
22548
  };
22482
22549
  const onResizePointerUp = (event) => {
@@ -22494,14 +22561,15 @@ function ResizableImageNodeView(props) {
22494
22561
  finishResize();
22495
22562
  };
22496
22563
  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";
22564
+ const wrapperAlignClass = imageLayout === "block" ? textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "" : "";
22565
+ 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
22566
  return /* @__PURE__ */ jsxs63(
22500
22567
  NodeViewWrapper,
22501
22568
  {
22502
22569
  as: "div",
22503
22570
  ref: wrapperRef,
22504
- className: ["relative block align-middle max-w-full my-4", wrapperWidthClass, wrapperAlignClass].filter(Boolean).join(" "),
22571
+ "data-image-layout": imageLayout,
22572
+ className: ["relative block align-middle max-w-full my-4", wrapperLayoutClass, wrapperAlignClass].filter(Boolean).join(" "),
22505
22573
  onMouseEnter: () => setIsHovered(true),
22506
22574
  onMouseLeave: () => setIsHovered(false),
22507
22575
  onClick: (e) => {
@@ -22571,6 +22639,25 @@ var ResizableImage = Image3.extend({
22571
22639
  return Number.isFinite(parsed) ? parsed : null;
22572
22640
  },
22573
22641
  renderHTML: (attrs) => typeof attrs.height === "number" ? { height: attrs.height } : {}
22642
+ },
22643
+ imageLayout: {
22644
+ default: "block",
22645
+ parseHTML: (element) => {
22646
+ const explicit = element.getAttribute("data-image-layout");
22647
+ if (explicit) return parseImageLayout(explicit);
22648
+ const floatValue = element.style.float;
22649
+ if (floatValue === "left" || floatValue === "right") return floatValue;
22650
+ return "block";
22651
+ },
22652
+ renderHTML: (attrs) => getImageLayoutStyles(parseImageLayout(attrs.imageLayout))
22653
+ },
22654
+ imageWidthPreset: {
22655
+ default: null,
22656
+ parseHTML: (element) => parseImageWidthPreset(element.getAttribute("data-image-size")),
22657
+ renderHTML: (attrs) => {
22658
+ const preset = parseImageWidthPreset(attrs.imageWidthPreset);
22659
+ return preset ? { "data-image-size": preset } : {};
22660
+ }
22574
22661
  }
22575
22662
  };
22576
22663
  },
@@ -22758,6 +22845,7 @@ import {
22758
22845
  Palette as Palette2,
22759
22846
  Quote as QuoteIcon,
22760
22847
  Redo as RedoIcon,
22848
+ RotateCcw as RotateCcw2,
22761
22849
  Smile as Smile3,
22762
22850
  Strikethrough as StrikethroughIcon,
22763
22851
  Subscript as SubscriptIcon,
@@ -22833,6 +22921,96 @@ var EditorColorPalette = ({
22833
22921
  )) })
22834
22922
  ] });
22835
22923
 
22924
+ // src/components/UEditor/image-commands.ts
22925
+ import { NodeSelection, TextSelection } from "@tiptap/pm/state";
22926
+ var IMAGE_WIDTHS_BY_LAYOUT = {
22927
+ block: {
22928
+ sm: 180,
22929
+ md: 280,
22930
+ lg: 380
22931
+ },
22932
+ wrap: {
22933
+ sm: 140,
22934
+ md: 200,
22935
+ lg: 260
22936
+ }
22937
+ };
22938
+ function isSelectedImage(editor) {
22939
+ const { selection } = editor.state;
22940
+ return selection instanceof NodeSelection && selection.node.type.name === "image";
22941
+ }
22942
+ function applyImageLayout(editor, layout) {
22943
+ const { state, view } = editor;
22944
+ const { selection, schema } = state;
22945
+ if (!(selection instanceof NodeSelection) || selection.node.type.name !== "image") {
22946
+ editor.chain().focus().updateAttributes("image", { imageLayout: layout }).run();
22947
+ return;
22948
+ }
22949
+ let transaction = state.tr.setNodeMarkup(selection.from, void 0, {
22950
+ ...selection.node.attrs,
22951
+ imageLayout: layout
22952
+ });
22953
+ if (layout !== "block") {
22954
+ const nextPos = transaction.mapping.map(selection.to);
22955
+ const nextNode = transaction.doc.nodeAt(nextPos);
22956
+ if (!nextNode || nextNode.type.name !== "paragraph") {
22957
+ const paragraph = schema.nodes.paragraph?.create();
22958
+ if (paragraph) {
22959
+ transaction = transaction.insert(nextPos, paragraph);
22960
+ }
22961
+ }
22962
+ const resolvedPos = transaction.doc.resolve(Math.min(nextPos + 1, transaction.doc.content.size));
22963
+ transaction = transaction.setSelection(TextSelection.near(resolvedPos));
22964
+ } else {
22965
+ const resolvedPos = transaction.doc.resolve(selection.from);
22966
+ transaction = transaction.setSelection(NodeSelection.create(transaction.doc, resolvedPos.pos));
22967
+ }
22968
+ view.dispatch(transaction.scrollIntoView());
22969
+ view.focus();
22970
+ }
22971
+ function applyImageWidthPreset(editor, preset) {
22972
+ const attrs = editor.getAttributes("image");
22973
+ const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
22974
+ const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
22975
+ if (!isSelectedImage(editor)) {
22976
+ editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
22977
+ return;
22978
+ }
22979
+ const { state, view } = editor;
22980
+ const selection = state.selection;
22981
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
22982
+ ...selection.node.attrs,
22983
+ width,
22984
+ imageWidthPreset: preset
22985
+ });
22986
+ view.dispatch(transaction.scrollIntoView());
22987
+ view.focus();
22988
+ }
22989
+ function resetImageSize(editor) {
22990
+ if (!isSelectedImage(editor)) {
22991
+ editor.chain().focus().updateAttributes("image", {
22992
+ width: null,
22993
+ height: null,
22994
+ imageWidthPreset: null
22995
+ }).run();
22996
+ return;
22997
+ }
22998
+ const { state, view } = editor;
22999
+ const selection = state.selection;
23000
+ const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
23001
+ ...selection.node.attrs,
23002
+ width: null,
23003
+ height: null,
23004
+ imageWidthPreset: null
23005
+ });
23006
+ view.dispatch(transaction.scrollIntoView());
23007
+ view.focus();
23008
+ }
23009
+ function deleteSelectedImage(editor) {
23010
+ if (!isSelectedImage(editor)) return;
23011
+ editor.chain().focus().deleteSelection().run();
23012
+ }
23013
+
22836
23014
  // src/components/UEditor/inputs.tsx
22837
23015
  import { useEffect as useEffect31, useRef as useRef26, useState as useState42 } from "react";
22838
23016
  import { Check as Check10, X as X18 } from "lucide-react";
@@ -23169,6 +23347,10 @@ var EditorToolbar = ({
23169
23347
  const fileInputRef = useRef28(null);
23170
23348
  const [isUploadingImage, setIsUploadingImage] = useState44(false);
23171
23349
  const [imageUploadError, setImageUploadError] = useState44(null);
23350
+ const isImageSelected = editor.isActive("image");
23351
+ const imageAttrs = editor.getAttributes("image");
23352
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
23353
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23172
23354
  const insertImageFiles = async (files) => {
23173
23355
  if (files.length === 0) return;
23174
23356
  setIsUploadingImage(true);
@@ -23501,6 +23683,85 @@ var EditorToolbar = ({
23501
23683
  void insertImageFiles(files);
23502
23684
  }
23503
23685
  }
23686
+ ),
23687
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23688
+ /* @__PURE__ */ jsx77(
23689
+ DropdownMenuItem,
23690
+ {
23691
+ icon: AlignCenter,
23692
+ label: t("toolbar.imageLayoutBlock"),
23693
+ onClick: () => applyImageLayout(editor, "block"),
23694
+ active: isImageSelected && imageLayout === "block",
23695
+ disabled: !isImageSelected
23696
+ }
23697
+ ),
23698
+ /* @__PURE__ */ jsx77(
23699
+ DropdownMenuItem,
23700
+ {
23701
+ icon: AlignLeft,
23702
+ label: t("toolbar.imageLayoutLeft"),
23703
+ onClick: () => applyImageLayout(editor, "left"),
23704
+ active: isImageSelected && imageLayout === "left",
23705
+ disabled: !isImageSelected
23706
+ }
23707
+ ),
23708
+ /* @__PURE__ */ jsx77(
23709
+ DropdownMenuItem,
23710
+ {
23711
+ icon: AlignRight,
23712
+ label: t("toolbar.imageLayoutRight"),
23713
+ onClick: () => applyImageLayout(editor, "right"),
23714
+ active: isImageSelected && imageLayout === "right",
23715
+ disabled: !isImageSelected
23716
+ }
23717
+ ),
23718
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23719
+ /* @__PURE__ */ jsx77(
23720
+ DropdownMenuItem,
23721
+ {
23722
+ label: t("toolbar.imageWidthSm"),
23723
+ onClick: () => applyImageWidthPreset(editor, "sm"),
23724
+ active: isImageSelected && imageWidthPreset === "sm",
23725
+ disabled: !isImageSelected
23726
+ }
23727
+ ),
23728
+ /* @__PURE__ */ jsx77(
23729
+ DropdownMenuItem,
23730
+ {
23731
+ label: t("toolbar.imageWidthMd"),
23732
+ onClick: () => applyImageWidthPreset(editor, "md"),
23733
+ active: isImageSelected && imageWidthPreset === "md",
23734
+ disabled: !isImageSelected
23735
+ }
23736
+ ),
23737
+ /* @__PURE__ */ jsx77(
23738
+ DropdownMenuItem,
23739
+ {
23740
+ label: t("toolbar.imageWidthLg"),
23741
+ onClick: () => applyImageWidthPreset(editor, "lg"),
23742
+ active: isImageSelected && imageWidthPreset === "lg",
23743
+ disabled: !isImageSelected
23744
+ }
23745
+ ),
23746
+ /* @__PURE__ */ jsx77("div", { className: "my-1 border-t" }),
23747
+ /* @__PURE__ */ jsx77(
23748
+ DropdownMenuItem,
23749
+ {
23750
+ icon: RotateCcw2,
23751
+ label: t("toolbar.imageResetSize"),
23752
+ onClick: () => resetImageSize(editor),
23753
+ disabled: !isImageSelected
23754
+ }
23755
+ ),
23756
+ /* @__PURE__ */ jsx77(
23757
+ DropdownMenuItem,
23758
+ {
23759
+ icon: Trash22,
23760
+ label: t("toolbar.imageDelete"),
23761
+ onClick: () => deleteSelectedImage(editor),
23762
+ disabled: !isImageSelected,
23763
+ destructive: true
23764
+ }
23504
23765
  )
23505
23766
  ] })
23506
23767
  }
@@ -23619,6 +23880,9 @@ var EditorToolbar = ({
23619
23880
  import React70, { useCallback as useCallback17, useEffect as useEffect33, useMemo as useMemo22, useRef as useRef29, useState as useState45 } from "react";
23620
23881
  import { createPortal as createPortal8 } from "react-dom";
23621
23882
  import {
23883
+ AlignCenter as AlignCenter2,
23884
+ AlignLeft as AlignLeft2,
23885
+ AlignRight as AlignRight2,
23622
23886
  Bold as BoldIcon2,
23623
23887
  Code as CodeIcon2,
23624
23888
  FileCode as FileCode4,
@@ -23634,9 +23898,11 @@ import {
23634
23898
  Palette as Palette3,
23635
23899
  Plus as Plus3,
23636
23900
  Quote as QuoteIcon2,
23901
+ RotateCcw as RotateCcw3,
23637
23902
  Subscript as SubscriptIcon2,
23638
23903
  Superscript as SuperscriptIcon2,
23639
23904
  Table as TableIcon2,
23905
+ Trash2 as Trash23,
23640
23906
  Type as Type3,
23641
23907
  Underline as UnderlineIcon2,
23642
23908
  Strikethrough as StrikethroughIcon2
@@ -23835,6 +24101,10 @@ var BubbleMenuContent = ({
23835
24101
  const { textColors, highlightColors } = useEditorColors();
23836
24102
  const [showLinkInput, setShowLinkInput] = useState45(false);
23837
24103
  const [showEditorColorPalette, setShowEditorColorPalette] = useState45(false);
24104
+ const isImageSelected = editor.isActive("image");
24105
+ const imageAttrs = editor.getAttributes("image");
24106
+ const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
24107
+ const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
23838
24108
  useEffect33(() => {
23839
24109
  onKeepOpenChange?.(showLinkInput);
23840
24110
  }, [onKeepOpenChange, showLinkInput]);
@@ -23909,6 +24179,20 @@ var BubbleMenuContent = ({
23909
24179
  ) })
23910
24180
  ] });
23911
24181
  }
24182
+ if (isImageSelected) {
24183
+ return /* @__PURE__ */ jsxs68("div", { className: "flex items-center gap-0.5 p-1", children: [
24184
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "block"), active: imageLayout === "block", title: t("toolbar.imageLayoutBlock"), children: /* @__PURE__ */ jsx78(AlignCenter2, { className: "w-4 h-4" }) }),
24185
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "left"), active: imageLayout === "left", title: t("toolbar.imageLayoutLeft"), children: /* @__PURE__ */ jsx78(AlignLeft2, { className: "w-4 h-4" }) }),
24186
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => applyImageLayout(editor, "right"), active: imageLayout === "right", title: t("toolbar.imageLayoutRight"), children: /* @__PURE__ */ jsx78(AlignRight2, { className: "w-4 h-4" }) }),
24187
+ /* @__PURE__ */ jsx78("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24188
+ /* @__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" }) }),
24189
+ /* @__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" }) }),
24190
+ /* @__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" }) }),
24191
+ /* @__PURE__ */ jsx78("div", { className: "w-px h-6 bg-border/50 mx-1" }),
24192
+ /* @__PURE__ */ jsx78(ToolbarButton, { onClick: () => resetImageSize(editor), title: t("toolbar.imageResetSize"), children: /* @__PURE__ */ jsx78(RotateCcw3, { className: "w-4 h-4" }) }),
24193
+ /* @__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" }) })
24194
+ ] });
24195
+ }
23912
24196
  return /* @__PURE__ */ jsxs68("div", { className: "flex items-center gap-0.5 p-1", children: [
23913
24197
  /* @__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
24198
  /* @__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 +24245,28 @@ var BubbleMenuContent = ({
23961
24245
  ] });
23962
24246
  };
23963
24247
  var CustomBubbleMenu = ({ editor }) => {
24248
+ const SHOW_DELAY_MS = 180;
23964
24249
  const [isVisible, setIsVisible] = useState45(false);
23965
24250
  const [position, setPosition] = useState45({ top: 0, left: 0 });
23966
24251
  const menuRef = useRef29(null);
23967
24252
  const keepOpenRef = useRef29(false);
24253
+ const showTimeoutRef = useRef29(null);
23968
24254
  const setKeepOpen = useCallback17((next) => {
23969
24255
  keepOpenRef.current = next;
23970
24256
  if (next) setIsVisible(true);
23971
24257
  }, []);
23972
24258
  useEffect33(() => {
24259
+ const clearShowTimeout = () => {
24260
+ if (showTimeoutRef.current) {
24261
+ clearTimeout(showTimeoutRef.current);
24262
+ showTimeoutRef.current = null;
24263
+ }
24264
+ };
23973
24265
  const updatePosition = () => {
23974
24266
  const { state, view } = editor;
23975
24267
  const { from, to, empty } = state.selection;
23976
24268
  if (!keepOpenRef.current && (empty || !view.hasFocus())) {
24269
+ clearShowTimeout();
23977
24270
  setIsVisible(false);
23978
24271
  return;
23979
24272
  }
@@ -23982,15 +24275,28 @@ var CustomBubbleMenu = ({ editor }) => {
23982
24275
  const left = (start.left + end.left) / 2;
23983
24276
  const top = start.top - 10;
23984
24277
  setPosition({ top, left });
23985
- setIsVisible(true);
24278
+ if (keepOpenRef.current) {
24279
+ clearShowTimeout();
24280
+ setIsVisible(true);
24281
+ return;
24282
+ }
24283
+ clearShowTimeout();
24284
+ showTimeoutRef.current = setTimeout(() => {
24285
+ setIsVisible(true);
24286
+ showTimeoutRef.current = null;
24287
+ }, SHOW_DELAY_MS);
23986
24288
  };
23987
24289
  const handleBlur = () => {
23988
- if (!keepOpenRef.current) setIsVisible(false);
24290
+ if (!keepOpenRef.current) {
24291
+ clearShowTimeout();
24292
+ setIsVisible(false);
24293
+ }
23989
24294
  };
23990
24295
  editor.on("selectionUpdate", updatePosition);
23991
24296
  editor.on("focus", updatePosition);
23992
24297
  editor.on("blur", handleBlur);
23993
24298
  return () => {
24299
+ clearShowTimeout();
23994
24300
  editor.off("selectionUpdate", updatePosition);
23995
24301
  editor.off("focus", updatePosition);
23996
24302
  editor.off("blur", handleBlur);
@@ -24474,7 +24780,21 @@ var UEditor = React71.forwardRef(({
24474
24780
  "[&_blockquote]:rounded-r-lg",
24475
24781
  "[&_blockquote]:italic",
24476
24782
  "[&_blockquote]:text-muted-foreground",
24477
- "[&_blockquote_p]:my-0"
24783
+ "[&_blockquote_p]:my-0",
24784
+ "[&_[data-image-layout='left']+p]:mt-1",
24785
+ "[&_[data-image-layout='left']+p]:min-h-[5rem]",
24786
+ "[&_[data-image-layout='right']+p]:mt-1",
24787
+ "[&_[data-image-layout='right']+p]:min-h-[5rem]",
24788
+ "max-md:[&_[data-image-layout='left']]:float-none",
24789
+ "max-md:[&_[data-image-layout='left']]:mr-0",
24790
+ "max-md:[&_[data-image-layout='left']]:ml-0",
24791
+ "max-md:[&_[data-image-layout='left']]:max-w-full",
24792
+ "max-md:[&_[data-image-layout='right']]:float-none",
24793
+ "max-md:[&_[data-image-layout='right']]:mr-0",
24794
+ "max-md:[&_[data-image-layout='right']]:ml-0",
24795
+ "max-md:[&_[data-image-layout='right']]:max-w-full",
24796
+ "max-md:[&_[data-image-layout='left']+p]:min-h-0",
24797
+ "max-md:[&_[data-image-layout='right']+p]:min-h-0"
24478
24798
  )
24479
24799
  }
24480
24800
  },