@underverse-ui/underverse 1.0.62 → 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.62",
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",
@@ -16367,6 +16399,7 @@ function Carousel({
16367
16399
  const isDeckAnimation = effectiveAnimation === "coverflow" || effectiveAnimation === "stack";
16368
16400
  const effectiveSlidesToShow = isDeckAnimation ? 1 : slidesToShow;
16369
16401
  const maxIndex = Math.max(0, totalSlides - effectiveSlidesToShow);
16402
+ const shouldShowArrows = showArrows && isHorizontal;
16370
16403
  const presetEffectOptions = React42.useMemo(() => {
16371
16404
  if (effectPreset === "cinematic") {
16372
16405
  return effectiveAnimation === "stack" ? {
@@ -16414,6 +16447,52 @@ function Carousel({
16414
16447
  blur: 1
16415
16448
  };
16416
16449
  }
16450
+ if (effectPreset === "poster") {
16451
+ return effectiveAnimation === "stack" ? {
16452
+ mainScale: 1.12,
16453
+ sideScale: 0.88,
16454
+ farScale: 0.78,
16455
+ sideOpacity: 0.64,
16456
+ farOpacity: 0.22,
16457
+ depthStep: 92,
16458
+ blur: 2.8,
16459
+ stackOffset: 14,
16460
+ stackLift: 18
16461
+ } : {
16462
+ mainScale: 1.16,
16463
+ sideScale: 0.78,
16464
+ farScale: 0.68,
16465
+ sideOpacity: 0.68,
16466
+ farOpacity: 0.18,
16467
+ sideOffset: 18,
16468
+ rotate: 26,
16469
+ depthStep: 140,
16470
+ blur: 3
16471
+ };
16472
+ }
16473
+ if (effectPreset === "minimal") {
16474
+ return effectiveAnimation === "stack" ? {
16475
+ mainScale: 1.01,
16476
+ sideScale: 0.96,
16477
+ farScale: 0.92,
16478
+ sideOpacity: 0.88,
16479
+ farOpacity: 0.66,
16480
+ depthStep: 36,
16481
+ blur: 0,
16482
+ stackOffset: 26,
16483
+ stackLift: 6
16484
+ } : {
16485
+ mainScale: 1.02,
16486
+ sideScale: 0.94,
16487
+ farScale: 0.88,
16488
+ sideOpacity: 0.9,
16489
+ farOpacity: 0.62,
16490
+ sideOffset: 34,
16491
+ rotate: 10,
16492
+ depthStep: 54,
16493
+ blur: 0
16494
+ };
16495
+ }
16417
16496
  return {};
16418
16497
  }, [effectPreset, effectiveAnimation]);
16419
16498
  const mergedEffectOptions = React42.useMemo(
@@ -16616,7 +16695,7 @@ function Carousel({
16616
16695
  {
16617
16696
  className: cn(
16618
16697
  effectiveAnimation === "slide" ? "flex" : "grid",
16619
- effectiveAnimation === "slide" && (isHorizontal ? "flex-row" : "flex-col"),
16698
+ effectiveAnimation === "slide" && (isHorizontal ? "flex-row" : "flex-col h-full"),
16620
16699
  isDeckAnimation && "place-items-center [transform-style:preserve-3d]",
16621
16700
  isHorizontal ? "touch-pan-y" : "touch-pan-x",
16622
16701
  containerClassName
@@ -16637,7 +16716,7 @@ function Carousel({
16637
16716
  {
16638
16717
  className: cn(
16639
16718
  "shrink-0",
16640
- effectiveAnimation === "slide" ? isHorizontal ? "h-full" : "w-full" : "col-start-1 row-start-1",
16719
+ effectiveAnimation === "slide" ? isHorizontal ? "h-full" : "h-full w-full" : "col-start-1 row-start-1",
16641
16720
  effectiveAnimation === "fade" && (idx === currentIndex ? "opacity-100 z-10" : "opacity-0 pointer-events-none z-0"),
16642
16721
  effectiveAnimation === "scale" && (idx === currentIndex ? "opacity-100 scale-100 z-10" : "opacity-0 scale-95 pointer-events-none z-0"),
16643
16722
  isDeckAnimation && "w-full max-w-[78%] md:max-w-[72%] transition-[opacity,transform] duration-500 ease-out",
@@ -16655,7 +16734,7 @@ function Carousel({
16655
16734
  ))
16656
16735
  }
16657
16736
  ),
16658
- showArrows && totalSlides > effectiveSlidesToShow && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
16737
+ shouldShowArrows && totalSlides > effectiveSlidesToShow && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
16659
16738
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
16660
16739
  Button_default,
16661
16740
  {
@@ -22488,6 +22567,8 @@ var import_react44 = require("@tiptap/react");
22488
22567
  var import_jsx_runtime73 = require("react/jsx-runtime");
22489
22568
  var MIN_IMAGE_SIZE_PX = 40;
22490
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"]);
22491
22572
  function toNullableNumber(value) {
22492
22573
  if (typeof value === "number" && Number.isFinite(value)) return value;
22493
22574
  if (typeof value === "string") {
@@ -22499,6 +22580,33 @@ function toNullableNumber(value) {
22499
22580
  function clamp8(value, min, max) {
22500
22581
  return Math.min(max, Math.max(min, value));
22501
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
+ }
22502
22610
  function ResizableImageNodeView(props) {
22503
22611
  const { node, selected, updateAttributes, editor, getPos } = props;
22504
22612
  const wrapperRef = (0, import_react43.useRef)(null);
@@ -22508,6 +22616,8 @@ function ResizableImageNodeView(props) {
22508
22616
  const widthAttr = toNullableNumber(node.attrs["width"]);
22509
22617
  const heightAttr = toNullableNumber(node.attrs["height"]);
22510
22618
  const textAlign = String(node.attrs["textAlign"] ?? "");
22619
+ const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
22620
+ const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
22511
22621
  const dragStateRef = (0, import_react43.useRef)(null);
22512
22622
  (0, import_react43.useEffect)(() => {
22513
22623
  const img = imgRef.current;
@@ -22556,7 +22666,8 @@ function ResizableImageNodeView(props) {
22556
22666
  const dy = event.clientY - drag.startY;
22557
22667
  let nextW = drag.startW;
22558
22668
  let nextH = drag.startH;
22559
- if (event.ctrlKey) {
22669
+ const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
22670
+ if (shouldPreserveAspect) {
22560
22671
  if (Math.abs(dx) >= Math.abs(dy)) {
22561
22672
  nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
22562
22673
  nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
@@ -22583,7 +22694,8 @@ function ResizableImageNodeView(props) {
22583
22694
  if (!drag) return;
22584
22695
  updateAttributes({
22585
22696
  width: Math.round(drag.lastW),
22586
- height: Math.round(drag.lastH)
22697
+ height: Math.round(drag.lastH),
22698
+ imageWidthPreset: null
22587
22699
  });
22588
22700
  };
22589
22701
  const onResizePointerUp = (event) => {
@@ -22601,14 +22713,15 @@ function ResizableImageNodeView(props) {
22601
22713
  finishResize();
22602
22714
  };
22603
22715
  const showHandle = selected || isHovered || isResizing;
22604
- const wrapperAlignClass = textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "";
22605
- 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";
22606
22718
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
22607
22719
  import_react44.NodeViewWrapper,
22608
22720
  {
22609
22721
  as: "div",
22610
22722
  ref: wrapperRef,
22611
- 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(" "),
22612
22725
  onMouseEnter: () => setIsHovered(true),
22613
22726
  onMouseLeave: () => setIsHovered(false),
22614
22727
  onClick: (e) => {
@@ -22678,6 +22791,25 @@ var ResizableImage = import_extension_image.default.extend({
22678
22791
  return Number.isFinite(parsed) ? parsed : null;
22679
22792
  },
22680
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
+ }
22681
22813
  }
22682
22814
  };
22683
22815
  },
@@ -22906,6 +23038,96 @@ var EditorColorPalette = ({
22906
23038
  )) })
22907
23039
  ] });
22908
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
+
22909
23131
  // src/components/UEditor/inputs.tsx
22910
23132
  var import_react46 = require("react");
22911
23133
  var import_lucide_react42 = require("lucide-react");
@@ -23242,6 +23464,10 @@ var EditorToolbar = ({
23242
23464
  const fileInputRef = (0, import_react48.useRef)(null);
23243
23465
  const [isUploadingImage, setIsUploadingImage] = (0, import_react48.useState)(false);
23244
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;
23245
23471
  const insertImageFiles = async (files) => {
23246
23472
  if (files.length === 0) return;
23247
23473
  setIsUploadingImage(true);
@@ -23574,6 +23800,85 @@ var EditorToolbar = ({
23574
23800
  void insertImageFiles(files);
23575
23801
  }
23576
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
+ }
23577
23882
  )
23578
23883
  ] })
23579
23884
  }
@@ -23886,6 +24191,10 @@ var BubbleMenuContent = ({
23886
24191
  const { textColors, highlightColors } = useEditorColors();
23887
24192
  const [showLinkInput, setShowLinkInput] = (0, import_react49.useState)(false);
23888
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;
23889
24198
  (0, import_react49.useEffect)(() => {
23890
24199
  onKeepOpenChange?.(showLinkInput);
23891
24200
  }, [onKeepOpenChange, showLinkInput]);
@@ -23960,6 +24269,20 @@ var BubbleMenuContent = ({
23960
24269
  ) })
23961
24270
  ] });
23962
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
+ }
23963
24286
  return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "flex items-center gap-0.5 p-1", children: [
23964
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" }) }),
23965
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" }) }),
@@ -24012,19 +24335,28 @@ var BubbleMenuContent = ({
24012
24335
  ] });
24013
24336
  };
24014
24337
  var CustomBubbleMenu = ({ editor }) => {
24338
+ const SHOW_DELAY_MS = 180;
24015
24339
  const [isVisible, setIsVisible] = (0, import_react49.useState)(false);
24016
24340
  const [position, setPosition] = (0, import_react49.useState)({ top: 0, left: 0 });
24017
24341
  const menuRef = (0, import_react49.useRef)(null);
24018
24342
  const keepOpenRef = (0, import_react49.useRef)(false);
24343
+ const showTimeoutRef = (0, import_react49.useRef)(null);
24019
24344
  const setKeepOpen = (0, import_react49.useCallback)((next) => {
24020
24345
  keepOpenRef.current = next;
24021
24346
  if (next) setIsVisible(true);
24022
24347
  }, []);
24023
24348
  (0, import_react49.useEffect)(() => {
24349
+ const clearShowTimeout = () => {
24350
+ if (showTimeoutRef.current) {
24351
+ clearTimeout(showTimeoutRef.current);
24352
+ showTimeoutRef.current = null;
24353
+ }
24354
+ };
24024
24355
  const updatePosition = () => {
24025
24356
  const { state, view } = editor;
24026
24357
  const { from, to, empty } = state.selection;
24027
24358
  if (!keepOpenRef.current && (empty || !view.hasFocus())) {
24359
+ clearShowTimeout();
24028
24360
  setIsVisible(false);
24029
24361
  return;
24030
24362
  }
@@ -24033,15 +24365,28 @@ var CustomBubbleMenu = ({ editor }) => {
24033
24365
  const left = (start.left + end.left) / 2;
24034
24366
  const top = start.top - 10;
24035
24367
  setPosition({ top, left });
24036
- 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);
24037
24378
  };
24038
24379
  const handleBlur = () => {
24039
- if (!keepOpenRef.current) setIsVisible(false);
24380
+ if (!keepOpenRef.current) {
24381
+ clearShowTimeout();
24382
+ setIsVisible(false);
24383
+ }
24040
24384
  };
24041
24385
  editor.on("selectionUpdate", updatePosition);
24042
24386
  editor.on("focus", updatePosition);
24043
24387
  editor.on("blur", handleBlur);
24044
24388
  return () => {
24389
+ clearShowTimeout();
24045
24390
  editor.off("selectionUpdate", updatePosition);
24046
24391
  editor.off("focus", updatePosition);
24047
24392
  editor.off("blur", handleBlur);
@@ -24525,7 +24870,21 @@ var UEditor = import_react50.default.forwardRef(({
24525
24870
  "[&_blockquote]:rounded-r-lg",
24526
24871
  "[&_blockquote]:italic",
24527
24872
  "[&_blockquote]:text-muted-foreground",
24528
- "[&_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"
24529
24888
  )
24530
24889
  }
24531
24890
  },