@underverse-ui/underverse 1.0.141 → 1.0.142

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
@@ -25261,6 +25261,7 @@ import Typography from "@tiptap/extension-typography";
25261
25261
  import Subscript from "@tiptap/extension-subscript";
25262
25262
  import Superscript from "@tiptap/extension-superscript";
25263
25263
  import HorizontalRule from "@tiptap/extension-horizontal-rule";
25264
+ import Gapcursor from "@tiptap/extension-gapcursor";
25264
25265
  import { common, createLowlight } from "lowlight";
25265
25266
 
25266
25267
  // src/components/UEditor/slash-command.tsx
@@ -26051,7 +26052,6 @@ import { mergeAttributes as mergeAttributes4 } from "@tiptap/core";
26051
26052
  import { NodeViewWrapper as NodeViewWrapper4, ReactNodeViewRenderer as ReactNodeViewRenderer4 } from "@tiptap/react";
26052
26053
  import { jsx as jsx81, jsxs as jsxs67 } from "react/jsx-runtime";
26053
26054
  var MIN_IMAGE_SIZE_PX = 40;
26054
- var AXIS_LOCK_THRESHOLD_PX = 4;
26055
26055
  var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
26056
26056
  var IMAGE_WIDTH_PRESETS = /* @__PURE__ */ new Set(["sm", "md", "lg"]);
26057
26057
  function toNullableNumber(value) {
@@ -26092,6 +26092,41 @@ function getImageLayoutStyles(layout) {
26092
26092
  }
26093
26093
  return {};
26094
26094
  }
26095
+ function getAspectRatio({
26096
+ img,
26097
+ widthAttr,
26098
+ heightAttr,
26099
+ fallbackWidth,
26100
+ fallbackHeight
26101
+ }) {
26102
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
26103
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) return img.naturalWidth / img.naturalHeight;
26104
+ if (fallbackWidth > 0 && fallbackHeight > 0) return fallbackWidth / fallbackHeight;
26105
+ return 1;
26106
+ }
26107
+ function sizeFromWidth(width, aspect, maxWidth) {
26108
+ let nextW = clamp8(width, MIN_IMAGE_SIZE_PX, maxWidth);
26109
+ let nextH = nextW / aspect;
26110
+ if (nextH < MIN_IMAGE_SIZE_PX) {
26111
+ nextH = MIN_IMAGE_SIZE_PX;
26112
+ nextW = clamp8(nextH * aspect, MIN_IMAGE_SIZE_PX, maxWidth);
26113
+ nextH = nextW / aspect;
26114
+ }
26115
+ return { width: nextW, height: nextH };
26116
+ }
26117
+ function sizeFromHeight(height, aspect, maxWidth) {
26118
+ let nextH = Math.max(MIN_IMAGE_SIZE_PX, height);
26119
+ let nextW = nextH * aspect;
26120
+ if (nextW > maxWidth) {
26121
+ nextW = maxWidth;
26122
+ nextH = nextW / aspect;
26123
+ }
26124
+ if (nextW < MIN_IMAGE_SIZE_PX) {
26125
+ nextW = MIN_IMAGE_SIZE_PX;
26126
+ nextH = nextW / aspect;
26127
+ }
26128
+ return { width: nextW, height: nextH };
26129
+ }
26095
26130
  function ResizableImageNodeView(props) {
26096
26131
  const { node, selected, updateAttributes, editor, getPos } = props;
26097
26132
  const wrapperRef = useRef29(null);
@@ -26102,7 +26137,6 @@ function ResizableImageNodeView(props) {
26102
26137
  const heightAttr = toNullableNumber(node.attrs["height"]);
26103
26138
  const textAlign = String(node.attrs["textAlign"] ?? "");
26104
26139
  const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
26105
- const preserveAspectByDefault = imageLayout === "left" || imageLayout === "right";
26106
26140
  const dragStateRef = useRef29(null);
26107
26141
  useEffect34(() => {
26108
26142
  const img = imgRef.current;
@@ -26126,7 +26160,13 @@ function ResizableImageNodeView(props) {
26126
26160
  const maxW = editorEl ? editorEl.getBoundingClientRect().width : Number.POSITIVE_INFINITY;
26127
26161
  const startW = Math.max(MIN_IMAGE_SIZE_PX, rect.width);
26128
26162
  const startH = Math.max(MIN_IMAGE_SIZE_PX, rect.height);
26129
- const aspect = startH > 0 ? startW / startH : 1;
26163
+ const aspect = getAspectRatio({
26164
+ img,
26165
+ widthAttr,
26166
+ heightAttr,
26167
+ fallbackWidth: startW,
26168
+ fallbackHeight: startH
26169
+ });
26130
26170
  dragStateRef.current = {
26131
26171
  pointerId: event.pointerId,
26132
26172
  startX: event.clientX,
@@ -26135,7 +26175,6 @@ function ResizableImageNodeView(props) {
26135
26175
  startH,
26136
26176
  lastW: startW,
26137
26177
  lastH: startH,
26138
- axis: null,
26139
26178
  aspect,
26140
26179
  maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW)
26141
26180
  };
@@ -26149,24 +26188,9 @@ function ResizableImageNodeView(props) {
26149
26188
  if (event.pointerId !== drag.pointerId) return;
26150
26189
  const dx = event.clientX - drag.startX;
26151
26190
  const dy = event.clientY - drag.startY;
26152
- let nextW = drag.startW;
26153
- let nextH = drag.startH;
26154
- const shouldPreserveAspect = preserveAspectByDefault ? !event.ctrlKey : event.ctrlKey;
26155
- if (shouldPreserveAspect) {
26156
- if (Math.abs(dx) >= Math.abs(dy)) {
26157
- nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26158
- nextH = clamp8(nextW / drag.aspect, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26159
- } else {
26160
- nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26161
- nextW = clamp8(nextH * drag.aspect, MIN_IMAGE_SIZE_PX, drag.maxW);
26162
- }
26163
- } else {
26164
- if (!drag.axis && (Math.abs(dx) > AXIS_LOCK_THRESHOLD_PX || Math.abs(dy) > AXIS_LOCK_THRESHOLD_PX)) {
26165
- drag.axis = Math.abs(dx) >= Math.abs(dy) ? "x" : "y";
26166
- }
26167
- if (drag.axis === "x") nextW = clamp8(drag.startW + dx, MIN_IMAGE_SIZE_PX, drag.maxW);
26168
- if (drag.axis === "y") nextH = clamp8(drag.startH + dy, MIN_IMAGE_SIZE_PX, Number.POSITIVE_INFINITY);
26169
- }
26191
+ const nextSize = Math.abs(dx) >= Math.abs(dy) ? sizeFromWidth(drag.startW + dx, drag.aspect, drag.maxW) : sizeFromHeight(drag.startH + dy, drag.aspect, drag.maxW);
26192
+ const nextW = nextSize.width;
26193
+ const nextH = nextSize.height;
26170
26194
  drag.lastW = nextW;
26171
26195
  drag.lastH = nextH;
26172
26196
  img.style.width = `${Math.round(nextW)}px`;
@@ -27089,6 +27113,7 @@ function buildUEditorExtensions({
27089
27113
  }
27090
27114
  }),
27091
27115
  HorizontalRule,
27116
+ Gapcursor,
27092
27117
  Link.configure({
27093
27118
  openOnClick: false,
27094
27119
  protocols: ["http", "https", "mailto", "tel"],
@@ -27475,6 +27500,43 @@ function isSelectedImage(editor) {
27475
27500
  const { selection } = editor.state;
27476
27501
  return selection instanceof NodeSelection && selection.node.type.name === "image";
27477
27502
  }
27503
+ function toPositiveNumber(value) {
27504
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
27505
+ if (typeof value === "string") {
27506
+ const parsed = Number.parseInt(value, 10);
27507
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
27508
+ }
27509
+ return null;
27510
+ }
27511
+ function getImageElementAtSelection(editor, pos) {
27512
+ const nodeDom = editor.view.nodeDOM(pos);
27513
+ if (!(nodeDom instanceof HTMLElement)) return null;
27514
+ if (nodeDom.tagName === "IMG") return nodeDom;
27515
+ return nodeDom.querySelector("img");
27516
+ }
27517
+ function getImageAspectRatio(editor, attrs, pos) {
27518
+ const widthAttr = toPositiveNumber(attrs.width);
27519
+ const heightAttr = toPositiveNumber(attrs.height);
27520
+ if (widthAttr && heightAttr) return widthAttr / heightAttr;
27521
+ const imageElement = typeof pos === "number" ? getImageElementAtSelection(editor, pos) : null;
27522
+ if (!imageElement) return null;
27523
+ if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
27524
+ return imageElement.naturalWidth / imageElement.naturalHeight;
27525
+ }
27526
+ const rect = imageElement.getBoundingClientRect();
27527
+ if (rect.width > 0 && rect.height > 0) return rect.width / rect.height;
27528
+ const width = toPositiveNumber(imageElement.getAttribute("width")) ?? toPositiveNumber(imageElement.style.width);
27529
+ const height = toPositiveNumber(imageElement.getAttribute("height")) ?? toPositiveNumber(imageElement.style.height);
27530
+ return width && height ? width / height : null;
27531
+ }
27532
+ function getImagePresetAttributes(editor, width, preset, attrs, pos) {
27533
+ const aspect = getImageAspectRatio(editor, attrs, pos);
27534
+ return {
27535
+ width,
27536
+ height: aspect ? Math.round(width / aspect) : toPositiveNumber(attrs.height),
27537
+ imageWidthPreset: preset
27538
+ };
27539
+ }
27478
27540
  function applyImageLayout(editor, layout) {
27479
27541
  const { state, view } = editor;
27480
27542
  const { selection, schema } = state;
@@ -27509,15 +27571,15 @@ function applyImageWidthPreset(editor, preset) {
27509
27571
  const mode = attrs.imageLayout === "left" || attrs.imageLayout === "right" ? "wrap" : "block";
27510
27572
  const width = IMAGE_WIDTHS_BY_LAYOUT[mode][preset];
27511
27573
  if (!isSelectedImage(editor)) {
27512
- editor.chain().focus().updateAttributes("image", { width, imageWidthPreset: preset }).run();
27574
+ editor.chain().focus().updateAttributes("image", getImagePresetAttributes(editor, width, preset, attrs)).run();
27513
27575
  return;
27514
27576
  }
27515
27577
  const { state, view } = editor;
27516
27578
  const selection = state.selection;
27579
+ const nextAttrs = getImagePresetAttributes(editor, width, preset, selection.node.attrs, selection.from);
27517
27580
  const transaction = state.tr.setNodeMarkup(selection.from, void 0, {
27518
27581
  ...selection.node.attrs,
27519
- width,
27520
- imageWidthPreset: preset
27582
+ ...nextAttrs
27521
27583
  });
27522
27584
  view.dispatch(transaction.scrollIntoView());
27523
27585
  view.focus();
@@ -29465,10 +29527,22 @@ var CustomBubbleMenu = ({
29465
29527
  setIsVisible(false);
29466
29528
  return;
29467
29529
  }
29468
- const start = view.coordsAtPos(from);
29469
- const end = view.coordsAtPos(to);
29470
- const left = (start.left + end.left) / 2;
29471
- const top = start.top - BUBBLE_MENU_OFFSET;
29530
+ let start;
29531
+ let end;
29532
+ try {
29533
+ start = view.coordsAtPos(from);
29534
+ end = view.coordsAtPos(to);
29535
+ } catch {
29536
+ clearShowTimeout();
29537
+ setIsVisible(false);
29538
+ return;
29539
+ }
29540
+ const viewportPadding = 8;
29541
+ const left = Math.min(
29542
+ window.innerWidth - viewportPadding,
29543
+ Math.max(viewportPadding, (start.left + end.left) / 2)
29544
+ );
29545
+ const top = Math.max(viewportPadding, start.top - BUBBLE_MENU_OFFSET);
29472
29546
  setPosition({ top, left });
29473
29547
  if (keepOpenRef.current) {
29474
29548
  clearShowTimeout();
@@ -29490,11 +29564,17 @@ var CustomBubbleMenu = ({
29490
29564
  editor.on("selectionUpdate", updatePosition);
29491
29565
  editor.on("focus", updatePosition);
29492
29566
  editor.on("blur", handleBlur);
29567
+ editor.on("transaction", updatePosition);
29568
+ editor.on("update", updatePosition);
29569
+ const animationFrameId = requestAnimationFrame(updatePosition);
29493
29570
  return () => {
29571
+ cancelAnimationFrame(animationFrameId);
29494
29572
  clearShowTimeout();
29495
29573
  editor.off("selectionUpdate", updatePosition);
29496
29574
  editor.off("focus", updatePosition);
29497
29575
  editor.off("blur", handleBlur);
29576
+ editor.off("transaction", updatePosition);
29577
+ editor.off("update", updatePosition);
29498
29578
  };
29499
29579
  }, [editor]);
29500
29580
  if (!isVisible) return null;
@@ -29504,7 +29584,7 @@ var CustomBubbleMenu = ({
29504
29584
  {
29505
29585
  ref: menuRef,
29506
29586
  "data-popover": true,
29507
- className: "fixed z-99999 flex rounded-xl border border-border/40 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 zoom-in-95",
29587
+ className: "fixed z-[99999] flex rounded-xl border border-border/40 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 zoom-in-95",
29508
29588
  style: {
29509
29589
  top: `${position.top}px`,
29510
29590
  left: `${position.left}px`,
@@ -35427,6 +35507,17 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
35427
35507
  "[&_.is-editor-empty]:before:float-left",
35428
35508
  "[&_.is-editor-empty]:before:pointer-events-none",
35429
35509
  "[&_.is-editor-empty]:before:h-0",
35510
+ "[&_.ProseMirror-gapcursor]:pointer-events-none",
35511
+ "[&_.ProseMirror-gapcursor]:absolute",
35512
+ "[&_.ProseMirror-gapcursor]:hidden",
35513
+ "[&_.ProseMirror-gapcursor:after]:content-['']",
35514
+ "[&_.ProseMirror-gapcursor:after]:block",
35515
+ "[&_.ProseMirror-gapcursor:after]:absolute",
35516
+ "[&_.ProseMirror-gapcursor:after]:top-[-2px]",
35517
+ "[&_.ProseMirror-gapcursor:after]:w-8",
35518
+ "[&_.ProseMirror-gapcursor:after]:border-t-2",
35519
+ "[&_.ProseMirror-gapcursor:after]:border-primary",
35520
+ "[&.ProseMirror-focused_.ProseMirror-gapcursor]:block",
35430
35521
  "[&_ul[data-type='taskList']]:list-none",
35431
35522
  "[&_ul[data-type='taskList']]:pl-0",
35432
35523
  "[&_ul[data-type='taskList']_li]:flex",