@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.
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",
@@ -16201,6 +16233,7 @@ function Carousel({
16201
16233
  const isDeckAnimation = effectiveAnimation === "coverflow" || effectiveAnimation === "stack";
16202
16234
  const effectiveSlidesToShow = isDeckAnimation ? 1 : slidesToShow;
16203
16235
  const maxIndex = Math.max(0, totalSlides - effectiveSlidesToShow);
16236
+ const shouldShowArrows = showArrows && isHorizontal;
16204
16237
  const presetEffectOptions = React42.useMemo(() => {
16205
16238
  if (effectPreset === "cinematic") {
16206
16239
  return effectiveAnimation === "stack" ? {
@@ -16248,6 +16281,52 @@ function Carousel({
16248
16281
  blur: 1
16249
16282
  };
16250
16283
  }
16284
+ if (effectPreset === "poster") {
16285
+ return effectiveAnimation === "stack" ? {
16286
+ mainScale: 1.12,
16287
+ sideScale: 0.88,
16288
+ farScale: 0.78,
16289
+ sideOpacity: 0.64,
16290
+ farOpacity: 0.22,
16291
+ depthStep: 92,
16292
+ blur: 2.8,
16293
+ stackOffset: 14,
16294
+ stackLift: 18
16295
+ } : {
16296
+ mainScale: 1.16,
16297
+ sideScale: 0.78,
16298
+ farScale: 0.68,
16299
+ sideOpacity: 0.68,
16300
+ farOpacity: 0.18,
16301
+ sideOffset: 18,
16302
+ rotate: 26,
16303
+ depthStep: 140,
16304
+ blur: 3
16305
+ };
16306
+ }
16307
+ if (effectPreset === "minimal") {
16308
+ return effectiveAnimation === "stack" ? {
16309
+ mainScale: 1.01,
16310
+ sideScale: 0.96,
16311
+ farScale: 0.92,
16312
+ sideOpacity: 0.88,
16313
+ farOpacity: 0.66,
16314
+ depthStep: 36,
16315
+ blur: 0,
16316
+ stackOffset: 26,
16317
+ stackLift: 6
16318
+ } : {
16319
+ mainScale: 1.02,
16320
+ sideScale: 0.94,
16321
+ farScale: 0.88,
16322
+ sideOpacity: 0.9,
16323
+ farOpacity: 0.62,
16324
+ sideOffset: 34,
16325
+ rotate: 10,
16326
+ depthStep: 54,
16327
+ blur: 0
16328
+ };
16329
+ }
16251
16330
  return {};
16252
16331
  }, [effectPreset, effectiveAnimation]);
16253
16332
  const mergedEffectOptions = React42.useMemo(
@@ -16450,7 +16529,7 @@ function Carousel({
16450
16529
  {
16451
16530
  className: cn(
16452
16531
  effectiveAnimation === "slide" ? "flex" : "grid",
16453
- effectiveAnimation === "slide" && (isHorizontal ? "flex-row" : "flex-col"),
16532
+ effectiveAnimation === "slide" && (isHorizontal ? "flex-row" : "flex-col h-full"),
16454
16533
  isDeckAnimation && "place-items-center [transform-style:preserve-3d]",
16455
16534
  isHorizontal ? "touch-pan-y" : "touch-pan-x",
16456
16535
  containerClassName
@@ -16471,7 +16550,7 @@ function Carousel({
16471
16550
  {
16472
16551
  className: cn(
16473
16552
  "shrink-0",
16474
- effectiveAnimation === "slide" ? isHorizontal ? "h-full" : "w-full" : "col-start-1 row-start-1",
16553
+ effectiveAnimation === "slide" ? isHorizontal ? "h-full" : "h-full w-full" : "col-start-1 row-start-1",
16475
16554
  effectiveAnimation === "fade" && (idx === currentIndex ? "opacity-100 z-10" : "opacity-0 pointer-events-none z-0"),
16476
16555
  effectiveAnimation === "scale" && (idx === currentIndex ? "opacity-100 scale-100 z-10" : "opacity-0 scale-95 pointer-events-none z-0"),
16477
16556
  isDeckAnimation && "w-full max-w-[78%] md:max-w-[72%] transition-[opacity,transform] duration-500 ease-out",
@@ -16489,7 +16568,7 @@ function Carousel({
16489
16568
  ))
16490
16569
  }
16491
16570
  ),
16492
- showArrows && totalSlides > effectiveSlidesToShow && /* @__PURE__ */ jsxs41(Fragment17, { children: [
16571
+ shouldShowArrows && totalSlides > effectiveSlidesToShow && /* @__PURE__ */ jsxs41(Fragment17, { children: [
16493
16572
  /* @__PURE__ */ jsx49(
16494
16573
  Button_default,
16495
16574
  {
@@ -22334,6 +22413,8 @@ import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react";
22334
22413
  import { jsx as jsx73, jsxs as jsxs63 } from "react/jsx-runtime";
22335
22414
  var MIN_IMAGE_SIZE_PX = 40;
22336
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"]);
22337
22418
  function toNullableNumber(value) {
22338
22419
  if (typeof value === "number" && Number.isFinite(value)) return value;
22339
22420
  if (typeof value === "string") {
@@ -22345,6 +22426,33 @@ function toNullableNumber(value) {
22345
22426
  function clamp8(value, min, max) {
22346
22427
  return Math.min(max, Math.max(min, value));
22347
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
+ }
22348
22456
  function ResizableImageNodeView(props) {
22349
22457
  const { node, selected, updateAttributes, editor, getPos } = props;
22350
22458
  const wrapperRef = useRef25(null);
@@ -22354,6 +22462,8 @@ function ResizableImageNodeView(props) {
22354
22462
  const widthAttr = toNullableNumber(node.attrs["width"]);
22355
22463
  const heightAttr = toNullableNumber(node.attrs["height"]);
22356
22464
  const textAlign = String(node.attrs["textAlign"] ?? "");
22465
+ const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
22466
+ const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
22357
22467
  const dragStateRef = useRef25(null);
22358
22468
  useEffect30(() => {
22359
22469
  const img = imgRef.current;
@@ -22402,7 +22512,8 @@ function ResizableImageNodeView(props) {
22402
22512
  const dy = event.clientY - drag.startY;
22403
22513
  let nextW = drag.startW;
22404
22514
  let nextH = drag.startH;
22405
- if (event.ctrlKey) {
22515
+ const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
22516
+ if (shouldPreserveAspect) {
22406
22517
  if (Math.abs(dx) >= Math.abs(dy)) {
22407
22518
  nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
22408
22519
  nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
@@ -22429,7 +22540,8 @@ function ResizableImageNodeView(props) {
22429
22540
  if (!drag) return;
22430
22541
  updateAttributes({
22431
22542
  width: Math.round(drag.lastW),
22432
- height: Math.round(drag.lastH)
22543
+ height: Math.round(drag.lastH),
22544
+ imageWidthPreset: null
22433
22545
  });
22434
22546
  };
22435
22547
  const onResizePointerUp = (event) => {
@@ -22447,14 +22559,15 @@ function ResizableImageNodeView(props) {
22447
22559
  finishResize();
22448
22560
  };
22449
22561
  const showHandle = selected || isHovered || isResizing;
22450
- const wrapperAlignClass = textAlign === "center" ? "mx-auto" : textAlign === "right" ? "ml-auto" : textAlign === "justify" ? "mx-auto" : "";
22451
- 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";
22452
22564
  return /* @__PURE__ */ jsxs63(
22453
22565
  NodeViewWrapper,
22454
22566
  {
22455
22567
  as: "div",
22456
22568
  ref: wrapperRef,
22457
- 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(" "),
22458
22571
  onMouseEnter: () => setIsHovered(true),
22459
22572
  onMouseLeave: () => setIsHovered(false),
22460
22573
  onClick: (e) => {
@@ -22524,6 +22637,25 @@ var ResizableImage = Image3.extend({
22524
22637
  return Number.isFinite(parsed) ? parsed : null;
22525
22638
  },
22526
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
+ }
22527
22659
  }
22528
22660
  };
22529
22661
  },
@@ -22711,6 +22843,7 @@ import {
22711
22843
  Palette as Palette2,
22712
22844
  Quote as QuoteIcon,
22713
22845
  Redo as RedoIcon,
22846
+ RotateCcw as RotateCcw2,
22714
22847
  Smile as Smile3,
22715
22848
  Strikethrough as StrikethroughIcon,
22716
22849
  Subscript as SubscriptIcon,
@@ -22786,6 +22919,96 @@ var EditorColorPalette = ({
22786
22919
  )) })
22787
22920
  ] });
22788
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
+
22789
23012
  // src/components/UEditor/inputs.tsx
22790
23013
  import { useEffect as useEffect31, useRef as useRef26, useState as useState42 } from "react";
22791
23014
  import { Check as Check10, X as X18 } from "lucide-react";
@@ -23122,6 +23345,10 @@ var EditorToolbar = ({
23122
23345
  const fileInputRef = useRef28(null);
23123
23346
  const [isUploadingImage, setIsUploadingImage] = useState44(false);
23124
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;
23125
23352
  const insertImageFiles = async (files) => {
23126
23353
  if (files.length === 0) return;
23127
23354
  setIsUploadingImage(true);
@@ -23454,6 +23681,85 @@ var EditorToolbar = ({
23454
23681
  void insertImageFiles(files);
23455
23682
  }
23456
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
+ }
23457
23763
  )
23458
23764
  ] })
23459
23765
  }
@@ -23572,6 +23878,9 @@ var EditorToolbar = ({
23572
23878
  import React70, { useCallback as useCallback17, useEffect as useEffect33, useMemo as useMemo22, useRef as useRef29, useState as useState45 } from "react";
23573
23879
  import { createPortal as createPortal8 } from "react-dom";
23574
23880
  import {
23881
+ AlignCenter as AlignCenter2,
23882
+ AlignLeft as AlignLeft2,
23883
+ AlignRight as AlignRight2,
23575
23884
  Bold as BoldIcon2,
23576
23885
  Code as CodeIcon2,
23577
23886
  FileCode as FileCode4,
@@ -23587,9 +23896,11 @@ import {
23587
23896
  Palette as Palette3,
23588
23897
  Plus as Plus3,
23589
23898
  Quote as QuoteIcon2,
23899
+ RotateCcw as RotateCcw3,
23590
23900
  Subscript as SubscriptIcon2,
23591
23901
  Superscript as SuperscriptIcon2,
23592
23902
  Table as TableIcon2,
23903
+ Trash2 as Trash23,
23593
23904
  Type as Type3,
23594
23905
  Underline as UnderlineIcon2,
23595
23906
  Strikethrough as StrikethroughIcon2
@@ -23788,6 +24099,10 @@ var BubbleMenuContent = ({
23788
24099
  const { textColors, highlightColors } = useEditorColors();
23789
24100
  const [showLinkInput, setShowLinkInput] = useState45(false);
23790
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;
23791
24106
  useEffect33(() => {
23792
24107
  onKeepOpenChange?.(showLinkInput);
23793
24108
  }, [onKeepOpenChange, showLinkInput]);
@@ -23862,6 +24177,20 @@ var BubbleMenuContent = ({
23862
24177
  ) })
23863
24178
  ] });
23864
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
+ }
23865
24194
  return /* @__PURE__ */ jsxs68("div", { className: "flex items-center gap-0.5 p-1", children: [
23866
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" }) }),
23867
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" }) }),
@@ -23914,19 +24243,28 @@ var BubbleMenuContent = ({
23914
24243
  ] });
23915
24244
  };
23916
24245
  var CustomBubbleMenu = ({ editor }) => {
24246
+ const SHOW_DELAY_MS = 180;
23917
24247
  const [isVisible, setIsVisible] = useState45(false);
23918
24248
  const [position, setPosition] = useState45({ top: 0, left: 0 });
23919
24249
  const menuRef = useRef29(null);
23920
24250
  const keepOpenRef = useRef29(false);
24251
+ const showTimeoutRef = useRef29(null);
23921
24252
  const setKeepOpen = useCallback17((next) => {
23922
24253
  keepOpenRef.current = next;
23923
24254
  if (next) setIsVisible(true);
23924
24255
  }, []);
23925
24256
  useEffect33(() => {
24257
+ const clearShowTimeout = () => {
24258
+ if (showTimeoutRef.current) {
24259
+ clearTimeout(showTimeoutRef.current);
24260
+ showTimeoutRef.current = null;
24261
+ }
24262
+ };
23926
24263
  const updatePosition = () => {
23927
24264
  const { state, view } = editor;
23928
24265
  const { from, to, empty } = state.selection;
23929
24266
  if (!keepOpenRef.current && (empty || !view.hasFocus())) {
24267
+ clearShowTimeout();
23930
24268
  setIsVisible(false);
23931
24269
  return;
23932
24270
  }
@@ -23935,15 +24273,28 @@ var CustomBubbleMenu = ({ editor }) => {
23935
24273
  const left = (start.left + end.left) / 2;
23936
24274
  const top = start.top - 10;
23937
24275
  setPosition({ top, left });
23938
- 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);
23939
24286
  };
23940
24287
  const handleBlur = () => {
23941
- if (!keepOpenRef.current) setIsVisible(false);
24288
+ if (!keepOpenRef.current) {
24289
+ clearShowTimeout();
24290
+ setIsVisible(false);
24291
+ }
23942
24292
  };
23943
24293
  editor.on("selectionUpdate", updatePosition);
23944
24294
  editor.on("focus", updatePosition);
23945
24295
  editor.on("blur", handleBlur);
23946
24296
  return () => {
24297
+ clearShowTimeout();
23947
24298
  editor.off("selectionUpdate", updatePosition);
23948
24299
  editor.off("focus", updatePosition);
23949
24300
  editor.off("blur", handleBlur);
@@ -24427,7 +24778,21 @@ var UEditor = React71.forwardRef(({
24427
24778
  "[&_blockquote]:rounded-r-lg",
24428
24779
  "[&_blockquote]:italic",
24429
24780
  "[&_blockquote]:text-muted-foreground",
24430
- "[&_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"
24431
24796
  )
24432
24797
  }
24433
24798
  },