@cyberyozh/text-editor 0.1.11 → 0.1.13

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/editor.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/editor/editor.tsx
2
- import { useState as useState13, useEffect as useEffect10 } from "react";
2
+ import { useState as useState12, useEffect as useEffect9 } from "react";
3
3
  import { NodeSelection } from "@tiptap/pm/state";
4
4
  import { Slice } from "@tiptap/pm/model";
5
5
  import { useEditor, EditorContent } from "@tiptap/react";
@@ -1892,14 +1892,39 @@ var ImageDialog = ({
1892
1892
  const [dragActive, setDragActive] = useState6(false);
1893
1893
  const inputRef = useRef6(null);
1894
1894
  const resolvedInitialUrl = initialAttrs ? resolveImageSrc({ fileId: initialAttrs.fileId, src: initialAttrs.src }, { getImageById }) : "";
1895
+ const getConstrainedDimensions = () => {
1896
+ if (!initialAttrs?.width || !initialAttrs?.height) {
1897
+ return { width: 0, height: 0 };
1898
+ }
1899
+ let maxWidth;
1900
+ try {
1901
+ const editorElement = document.querySelector(".tiptap-content-wrapper");
1902
+ if (editorElement && editorElement.clientWidth > 0) {
1903
+ maxWidth = editorElement.clientWidth - 32;
1904
+ }
1905
+ } catch {
1906
+ }
1907
+ if (maxWidth && initialAttrs.width > maxWidth) {
1908
+ const aspectRatio = initialAttrs.height / initialAttrs.width;
1909
+ return {
1910
+ width: maxWidth,
1911
+ height: Math.round(maxWidth * aspectRatio)
1912
+ };
1913
+ }
1914
+ return {
1915
+ width: initialAttrs.width,
1916
+ height: initialAttrs.height
1917
+ };
1918
+ };
1919
+ const constrainedDimensions = getConstrainedDimensions();
1895
1920
  const [formState, setFormState] = useState6({
1896
1921
  file: null,
1897
1922
  title: initialAttrs?.title || "",
1898
1923
  alt: initialAttrs?.alt || "",
1899
1924
  caption: initialAttrs?.caption || "",
1900
1925
  fileUrl: resolvedInitialUrl,
1901
- width: initialAttrs?.width || 0,
1902
- height: initialAttrs?.height || 0
1926
+ width: constrainedDimensions.width,
1927
+ height: constrainedDimensions.height
1903
1928
  });
1904
1929
  const handleSelectFile = (file) => {
1905
1930
  if (!file) return;
@@ -1907,15 +1932,34 @@ var ImageDialog = ({
1907
1932
  const img = new Image();
1908
1933
  img.src = imageUrl;
1909
1934
  img.onload = () => {
1910
- const { width, height } = img;
1935
+ const naturalWidth = img.width;
1936
+ const naturalHeight = img.height;
1937
+ const naturalAspectRatio = naturalHeight / naturalWidth;
1938
+ let maxWidth;
1939
+ try {
1940
+ const editorElement = document.querySelector(".tiptap-content-wrapper");
1941
+ if (editorElement && editorElement.clientWidth > 0) {
1942
+ maxWidth = editorElement.clientWidth - 32;
1943
+ }
1944
+ } catch {
1945
+ }
1946
+ let finalWidth;
1947
+ let finalHeight;
1948
+ if (maxWidth && naturalWidth > maxWidth) {
1949
+ finalWidth = maxWidth;
1950
+ finalHeight = Math.round(maxWidth * naturalAspectRatio);
1951
+ } else {
1952
+ finalWidth = naturalWidth;
1953
+ finalHeight = naturalHeight;
1954
+ }
1911
1955
  setFormState((prev) => ({
1912
1956
  ...prev,
1913
1957
  file,
1914
1958
  fileUrl: imageUrl,
1915
1959
  title: file.name,
1916
1960
  alt: file.name,
1917
- width,
1918
- height
1961
+ width: finalWidth,
1962
+ height: finalHeight
1919
1963
  }));
1920
1964
  };
1921
1965
  };
@@ -1983,7 +2027,9 @@ var ImageDialog = ({
1983
2027
  onImageEdit({
1984
2028
  alt: formState.alt,
1985
2029
  title: formState.title,
1986
- caption: formState.caption
2030
+ caption: formState.caption,
2031
+ width: formState.width || void 0,
2032
+ height: formState.height || void 0
1987
2033
  });
1988
2034
  handleClose();
1989
2035
  };
@@ -3222,8 +3268,9 @@ var BaseFigure = Image2.extend({
3222
3268
  isolating: true,
3223
3269
  selectable: false,
3224
3270
  addOptions() {
3271
+ const parentOptions = this.parent?.() || {};
3225
3272
  return {
3226
- HTMLAttributes: {},
3273
+ ...parentOptions,
3227
3274
  getImageById: void 0,
3228
3275
  onFigureClick: void 0
3229
3276
  };
@@ -3757,7 +3804,7 @@ var BaseLegacyFigure = Image3.extend({
3757
3804
 
3758
3805
  // src/extensions/views/node-views/callout.tsx
3759
3806
  import { NodeViewContent, NodeViewWrapper } from "@tiptap/react";
3760
- import { useState as useState11 } from "react";
3807
+ import { useState as useState10 } from "react";
3761
3808
 
3762
3809
  // src/icons/color/TiptapCalloutError.tsx
3763
3810
  import { jsx as jsx71, jsxs as jsxs22 } from "react/jsx-runtime";
@@ -4346,27 +4393,23 @@ var PromptContent = ({ children, isEmpty, placeholder }) => /* @__PURE__ */ jsx8
4346
4393
  }
4347
4394
  ) });
4348
4395
 
4349
- // src/extensions/views/components/table.tsx
4350
- import { useEffect as useEffect8, useRef as useRef10, useState as useState10 } from "react";
4351
- import { jsx as jsx82, jsxs as jsxs33 } from "react/jsx-runtime";
4352
-
4353
4396
  // src/extensions/views/components/video.tsx
4354
- import { jsx as jsx83, jsxs as jsxs34 } from "react/jsx-runtime";
4397
+ import { jsx as jsx82, jsxs as jsxs33 } from "react/jsx-runtime";
4355
4398
  var Video = ({ src, children }) => {
4356
4399
  const videoInfo = detectVideoHosting(src);
4357
4400
  if (!videoInfo?.embedUrl) {
4358
- return /* @__PURE__ */ jsxs34("div", { className: "video-node", children: [
4359
- /* @__PURE__ */ jsx83("div", { className: "video-error", children: /* @__PURE__ */ jsx83("span", { children: "Unsupported or invalid video URL" }) }),
4401
+ return /* @__PURE__ */ jsxs33("div", { className: "video-node", children: [
4402
+ /* @__PURE__ */ jsx82("div", { className: "video-error", children: /* @__PURE__ */ jsx82("span", { children: "Unsupported or invalid video URL" }) }),
4360
4403
  children
4361
4404
  ] });
4362
4405
  }
4363
- return /* @__PURE__ */ jsxs34(
4406
+ return /* @__PURE__ */ jsxs33(
4364
4407
  "div",
4365
4408
  {
4366
4409
  className: "video-node",
4367
4410
  "data-video": "",
4368
4411
  children: [
4369
- /* @__PURE__ */ jsx83("div", { className: "video-player-wrapper", children: /* @__PURE__ */ jsx83(
4412
+ /* @__PURE__ */ jsx82("div", { className: "video-player-wrapper", children: /* @__PURE__ */ jsx82(
4370
4413
  "iframe",
4371
4414
  {
4372
4415
  className: "video-iframe",
@@ -4383,13 +4426,13 @@ var Video = ({ src, children }) => {
4383
4426
  };
4384
4427
 
4385
4428
  // src/editor/controls/emoji-picker.tsx
4386
- import { useEffect as useEffect9, useRef as useRef11 } from "react";
4429
+ import { useEffect as useEffect8, useRef as useRef10 } from "react";
4387
4430
  import EmojiPicker from "emoji-picker-react";
4388
- import { jsx as jsx84 } from "react/jsx-runtime";
4431
+ import { jsx as jsx83 } from "react/jsx-runtime";
4389
4432
  var EmojiPickerComponent = ({ isOpen, onClose, onEmojiSelect }) => {
4390
- const pickerRef = useRef11(null);
4433
+ const pickerRef = useRef10(null);
4391
4434
  const translations2 = useTiptapTranslations();
4392
- useEffect9(() => {
4435
+ useEffect8(() => {
4393
4436
  const handleClickOutside = (event) => {
4394
4437
  if (pickerRef.current && !pickerRef.current.contains(event.target)) {
4395
4438
  onClose();
@@ -4403,12 +4446,12 @@ var EmojiPickerComponent = ({ isOpen, onClose, onEmojiSelect }) => {
4403
4446
  };
4404
4447
  }, [isOpen, onClose]);
4405
4448
  if (!isOpen) return null;
4406
- return /* @__PURE__ */ jsx84(
4449
+ return /* @__PURE__ */ jsx83(
4407
4450
  "div",
4408
4451
  {
4409
4452
  ref: pickerRef,
4410
4453
  className: "tiptap__emoji-picker-wrapper",
4411
- children: /* @__PURE__ */ jsx84(
4454
+ children: /* @__PURE__ */ jsx83(
4412
4455
  EmojiPicker,
4413
4456
  {
4414
4457
  skinTonesDisabled: true,
@@ -4433,12 +4476,12 @@ var EmojiPickerComponent = ({ isOpen, onClose, onEmojiSelect }) => {
4433
4476
  };
4434
4477
 
4435
4478
  // src/extensions/views/node-views/callout.tsx
4436
- import { jsx as jsx85, jsxs as jsxs35 } from "react/jsx-runtime";
4479
+ import { jsx as jsx84, jsxs as jsxs34 } from "react/jsx-runtime";
4437
4480
  var CalloutView = (props) => {
4438
4481
  const type = props.node.attrs.type || "info";
4439
4482
  const bgColor = props.node.attrs.bgColor;
4440
4483
  const emoji = props.node.attrs.emoji;
4441
- const [showEmojiPicker, setShowEmojiPicker] = useState11(false);
4484
+ const [showEmojiPicker, setShowEmojiPicker] = useState10(false);
4442
4485
  const handleIconClick = (e) => {
4443
4486
  e.preventDefault();
4444
4487
  e.stopPropagation();
@@ -4448,23 +4491,23 @@ var CalloutView = (props) => {
4448
4491
  props.updateAttributes({ emoji: newEmoji });
4449
4492
  setShowEmojiPicker(false);
4450
4493
  };
4451
- return /* @__PURE__ */ jsx85(NodeViewWrapper, { children: /* @__PURE__ */ jsx85(
4494
+ return /* @__PURE__ */ jsx84(NodeViewWrapper, { children: /* @__PURE__ */ jsx84(
4452
4495
  Callout,
4453
4496
  {
4454
4497
  type,
4455
4498
  bgColor,
4456
4499
  emoji,
4457
- iconWrapper: (icon) => /* @__PURE__ */ jsxs35("div", { className: "callout-icon-wrapper", children: [
4458
- /* @__PURE__ */ jsx85(
4500
+ iconWrapper: (icon) => /* @__PURE__ */ jsxs34("div", { className: "callout-icon-wrapper", children: [
4501
+ /* @__PURE__ */ jsx84(
4459
4502
  "button",
4460
4503
  {
4461
4504
  className: "callout-icon-button",
4462
4505
  onClick: handleIconClick,
4463
4506
  type: "button",
4464
- children: /* @__PURE__ */ jsx85("div", { className: "callout-icon", children: icon })
4507
+ children: /* @__PURE__ */ jsx84("div", { className: "callout-icon", children: icon })
4465
4508
  }
4466
4509
  ),
4467
- /* @__PURE__ */ jsx85(
4510
+ /* @__PURE__ */ jsx84(
4468
4511
  EmojiPickerComponent,
4469
4512
  {
4470
4513
  isOpen: showEmojiPicker,
@@ -4473,101 +4516,101 @@ var CalloutView = (props) => {
4473
4516
  }
4474
4517
  )
4475
4518
  ] }),
4476
- children: /* @__PURE__ */ jsx85(NodeViewContent, { as: "div" })
4519
+ children: /* @__PURE__ */ jsx84(NodeViewContent, { as: "div" })
4477
4520
  }
4478
4521
  ) });
4479
4522
  };
4480
4523
 
4481
4524
  // src/extensions/views/node-views/code-block.tsx
4482
- import { useState as useState12 } from "react";
4525
+ import { useState as useState11 } from "react";
4483
4526
  import { NodeViewContent as NodeViewContent2, NodeViewWrapper as NodeViewWrapper2 } from "@tiptap/react";
4484
- import { jsx as jsx86 } from "react/jsx-runtime";
4527
+ import { jsx as jsx85 } from "react/jsx-runtime";
4485
4528
  var CodeBlockView = (props) => {
4486
- const [selectedLanguage, setSelectedLanguage] = useState12(props.node.attrs.language ?? "");
4529
+ const [selectedLanguage, setSelectedLanguage] = useState11(props.node.attrs.language ?? "");
4487
4530
  const translations2 = useTiptapTranslations();
4488
4531
  const handleLanguageChange = (language) => {
4489
4532
  props.updateAttributes({ language });
4490
4533
  setSelectedLanguage(language);
4491
4534
  };
4492
- return /* @__PURE__ */ jsx86(NodeViewWrapper2, { children: /* @__PURE__ */ jsx86(
4535
+ return /* @__PURE__ */ jsx85(NodeViewWrapper2, { children: /* @__PURE__ */ jsx85(
4493
4536
  CodeBlock,
4494
4537
  {
4495
4538
  language: selectedLanguage,
4496
4539
  isEditable: props.editor.isEditable,
4497
4540
  onLanguageChange: handleLanguageChange,
4498
4541
  translations: translations2,
4499
- children: /* @__PURE__ */ jsx86(NodeViewContent2, {})
4542
+ children: /* @__PURE__ */ jsx85(NodeViewContent2, {})
4500
4543
  }
4501
4544
  ) });
4502
4545
  };
4503
4546
 
4504
4547
  // src/extensions/views/node-views/copyable-text.tsx
4505
4548
  import { MarkViewContent } from "@tiptap/react";
4506
- import { jsx as jsx87 } from "react/jsx-runtime";
4549
+ import { jsx as jsx86 } from "react/jsx-runtime";
4507
4550
  var CopyableTextView = () => {
4508
- return /* @__PURE__ */ jsx87(CopyableText2, { children: /* @__PURE__ */ jsx87(MarkViewContent, {}) });
4551
+ return /* @__PURE__ */ jsx86(CopyableText2, { children: /* @__PURE__ */ jsx86(MarkViewContent, {}) });
4509
4552
  };
4510
4553
 
4511
4554
  // src/extensions/views/node-views/custom-quote.tsx
4512
4555
  import { NodeViewContent as NodeViewContent3, NodeViewWrapper as NodeViewWrapper3 } from "@tiptap/react";
4513
- import { jsx as jsx88 } from "react/jsx-runtime";
4556
+ import { jsx as jsx87 } from "react/jsx-runtime";
4514
4557
  var CustomQuoteView = () => {
4515
- return /* @__PURE__ */ jsx88(NodeViewWrapper3, { children: /* @__PURE__ */ jsx88(CustomQuote, { children: /* @__PURE__ */ jsx88(NodeViewContent3, {}) }) });
4558
+ return /* @__PURE__ */ jsx87(NodeViewWrapper3, { children: /* @__PURE__ */ jsx87(CustomQuote, { children: /* @__PURE__ */ jsx87(NodeViewContent3, {}) }) });
4516
4559
  };
4517
4560
  var CustomQuoteTextView = (props) => {
4518
4561
  const isEmpty = !props.node.content.size;
4519
4562
  const translations2 = useTiptapTranslations();
4520
- return /* @__PURE__ */ jsx88(NodeViewWrapper3, { children: /* @__PURE__ */ jsx88(
4563
+ return /* @__PURE__ */ jsx87(NodeViewWrapper3, { children: /* @__PURE__ */ jsx87(
4521
4564
  CustomQuoteText,
4522
4565
  {
4523
4566
  isEmpty,
4524
4567
  placeholder: translations2.customQuote.textPlaceholder,
4525
- children: /* @__PURE__ */ jsx88(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4568
+ children: /* @__PURE__ */ jsx87(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4526
4569
  }
4527
4570
  ) });
4528
4571
  };
4529
4572
  var CustomQuoteAuthorView = (props) => {
4530
4573
  const isEmpty = !props.node.content.size;
4531
4574
  const translations2 = useTiptapTranslations();
4532
- return /* @__PURE__ */ jsx88(NodeViewWrapper3, { children: /* @__PURE__ */ jsx88(
4575
+ return /* @__PURE__ */ jsx87(NodeViewWrapper3, { children: /* @__PURE__ */ jsx87(
4533
4576
  CustomQuoteAuthor,
4534
4577
  {
4535
4578
  isEmpty,
4536
4579
  placeholder: translations2.customQuote.authorPlaceholder,
4537
- children: /* @__PURE__ */ jsx88(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4580
+ children: /* @__PURE__ */ jsx87(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4538
4581
  }
4539
4582
  ) });
4540
4583
  };
4541
4584
  var CustomQuoteSignView = (props) => {
4542
4585
  const isEmpty = !props.node.content.size;
4543
4586
  const translations2 = useTiptapTranslations();
4544
- return /* @__PURE__ */ jsx88(NodeViewWrapper3, { children: /* @__PURE__ */ jsx88(
4587
+ return /* @__PURE__ */ jsx87(NodeViewWrapper3, { children: /* @__PURE__ */ jsx87(
4545
4588
  CustomQuoteSign,
4546
4589
  {
4547
4590
  isEmpty,
4548
4591
  placeholder: translations2.customQuote.signPlaceholder,
4549
- children: /* @__PURE__ */ jsx88(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4592
+ children: /* @__PURE__ */ jsx87(NodeViewContent3, { className: isEmpty ? "is-empty" : "" })
4550
4593
  }
4551
4594
  ) });
4552
4595
  };
4553
4596
 
4554
4597
  // src/extensions/views/node-views/figure.tsx
4555
4598
  import { NodeViewWrapper as NodeViewWrapper4 } from "@tiptap/react";
4556
- import { jsx as jsx89 } from "react/jsx-runtime";
4599
+ import { jsx as jsx88 } from "react/jsx-runtime";
4557
4600
  var FigureView = ({ node, getPos, editor }) => {
4558
- const { fileId, src, alt, title, caption, width, height, storageKey } = node.attrs;
4601
+ const { fileId, src, alt, title, caption, width, height } = node.attrs;
4559
4602
  const imageExtension = editor.extensionManager.extensions.find((ext) => ext.name === "image");
4560
4603
  const getImageById = imageExtension?.options.getImageById;
4561
4604
  const onFigureClick = imageExtension?.options.onFigureClick;
4562
4605
  const handleEditClick = () => {
4563
4606
  onFigureClick?.(node.attrs, getPos());
4564
4607
  };
4565
- return /* @__PURE__ */ jsx89(
4608
+ return /* @__PURE__ */ jsx88(
4566
4609
  NodeViewWrapper4,
4567
4610
  {
4568
4611
  className: "figure-node-view",
4569
4612
  "data-drag-handle": true,
4570
- children: /* @__PURE__ */ jsx89(
4613
+ children: /* @__PURE__ */ jsx88(
4571
4614
  Figure,
4572
4615
  {
4573
4616
  fileId,
@@ -4588,38 +4631,38 @@ var FigureView = ({ node, getPos, editor }) => {
4588
4631
 
4589
4632
  // src/extensions/views/node-views/prompt.tsx
4590
4633
  import { NodeViewContent as NodeViewContent4, NodeViewWrapper as NodeViewWrapper5 } from "@tiptap/react";
4591
- import { jsx as jsx90 } from "react/jsx-runtime";
4634
+ import { jsx as jsx89 } from "react/jsx-runtime";
4592
4635
  var PromptHeaderView = (props) => {
4593
4636
  const isEmpty = !props.node.content.size;
4594
4637
  const translations2 = useTiptapTranslations();
4595
- return /* @__PURE__ */ jsx90(NodeViewWrapper5, { children: /* @__PURE__ */ jsx90(
4638
+ return /* @__PURE__ */ jsx89(NodeViewWrapper5, { children: /* @__PURE__ */ jsx89(
4596
4639
  PromptHeader,
4597
4640
  {
4598
4641
  isEmpty,
4599
4642
  placeholder: translations2.prompt.headerPlaceholder,
4600
- children: /* @__PURE__ */ jsx90(NodeViewContent4, { className: isEmpty ? "is-empty" : "" })
4643
+ children: /* @__PURE__ */ jsx89(NodeViewContent4, { className: isEmpty ? "is-empty" : "" })
4601
4644
  }
4602
4645
  ) });
4603
4646
  };
4604
4647
  var PromptContentView = (props) => {
4605
4648
  const isEmpty = !props.node.content.size;
4606
4649
  const translations2 = useTiptapTranslations();
4607
- return /* @__PURE__ */ jsx90(NodeViewWrapper5, { children: /* @__PURE__ */ jsx90(
4650
+ return /* @__PURE__ */ jsx89(NodeViewWrapper5, { children: /* @__PURE__ */ jsx89(
4608
4651
  PromptContent,
4609
4652
  {
4610
4653
  isEmpty,
4611
4654
  placeholder: translations2.prompt.contentPlaceholder,
4612
- children: /* @__PURE__ */ jsx90(NodeViewContent4, { className: isEmpty ? "is-empty" : "" })
4655
+ children: /* @__PURE__ */ jsx89(NodeViewContent4, { className: isEmpty ? "is-empty" : "" })
4613
4656
  }
4614
4657
  ) });
4615
4658
  };
4616
4659
 
4617
4660
  // src/extensions/views/node-views/video.tsx
4618
4661
  import { NodeViewContent as NodeViewContent5, NodeViewWrapper as NodeViewWrapper6 } from "@tiptap/react";
4619
- import { jsx as jsx91 } from "react/jsx-runtime";
4662
+ import { jsx as jsx90 } from "react/jsx-runtime";
4620
4663
  var VideoView = (props) => {
4621
4664
  const { src } = props.node.attrs;
4622
- return /* @__PURE__ */ jsx91(NodeViewWrapper6, { children: /* @__PURE__ */ jsx91(Video, { src, children: /* @__PURE__ */ jsx91(
4665
+ return /* @__PURE__ */ jsx90(NodeViewWrapper6, { children: /* @__PURE__ */ jsx90(Video, { src, children: /* @__PURE__ */ jsx90(
4623
4666
  NodeViewContent5,
4624
4667
  {
4625
4668
  as: "div",
@@ -5138,9 +5181,172 @@ var LegacyPromptSEContent = BaseLegacyPromptSEContent.extend({
5138
5181
 
5139
5182
  // src/extensions/editor/figure.ts
5140
5183
  import { ReactNodeViewRenderer as ReactNodeViewRenderer6 } from "@tiptap/react";
5184
+ import { ResizableNodeView } from "@tiptap/core";
5141
5185
  var Figure2 = BaseFigure.extend({
5142
5186
  addNodeView() {
5143
- return ReactNodeViewRenderer6(FigureView);
5187
+ const resizeOptions = this.options.resize;
5188
+ if (!resizeOptions) {
5189
+ return ReactNodeViewRenderer6(FigureView);
5190
+ }
5191
+ return (props) => {
5192
+ const { node, getPos, editor } = props;
5193
+ const attrs = node.attrs;
5194
+ const imageExtension = editor.extensionManager.extensions.find(
5195
+ (ext) => ext.name === "image"
5196
+ );
5197
+ const getImageById = imageExtension?.options.getImageById;
5198
+ let imgSrc = attrs.src;
5199
+ if (attrs.fileId && getImageById) {
5200
+ const resolvedSrc = getImageById(attrs.fileId);
5201
+ if (resolvedSrc) imgSrc = resolvedSrc;
5202
+ }
5203
+ let maxWidth;
5204
+ try {
5205
+ if (editor.view?.dom) {
5206
+ const contentWrapper = editor.view.dom.closest(".tiptap-content-wrapper");
5207
+ if (contentWrapper && contentWrapper.clientWidth > 0) {
5208
+ maxWidth = contentWrapper.clientWidth - 32;
5209
+ }
5210
+ }
5211
+ } catch {
5212
+ }
5213
+ let naturalAspectRatio;
5214
+ let maxHeight;
5215
+ const constrainDimensions = (w, h) => {
5216
+ let constrainedW = w;
5217
+ let constrainedH = h;
5218
+ if (maxWidth && maxHeight && naturalAspectRatio && w >= maxWidth) {
5219
+ constrainedW = maxWidth;
5220
+ constrainedH = maxHeight;
5221
+ }
5222
+ return { width: constrainedW, height: constrainedH };
5223
+ };
5224
+ const updateImageDimensions = (width, height) => {
5225
+ imageContainer.style.width = `${width}px`;
5226
+ imageContainer.style.height = `${height}px`;
5227
+ };
5228
+ const figure = document.createElement("figure");
5229
+ figure.setAttribute("data-drag-handle", "");
5230
+ const imageContainer = document.createElement("div");
5231
+ imageContainer.className = "figure-image-container";
5232
+ const img = document.createElement("img");
5233
+ img.src = imgSrc || "";
5234
+ if (attrs.alt) img.alt = attrs.alt;
5235
+ if (attrs.title) img.title = attrs.title;
5236
+ if (attrs.width && attrs.height) {
5237
+ imageContainer.style.width = `${attrs.width}px`;
5238
+ imageContainer.style.height = `${attrs.height}px`;
5239
+ }
5240
+ img.onload = () => {
5241
+ naturalAspectRatio = img.naturalHeight / img.naturalWidth;
5242
+ if (maxWidth && naturalAspectRatio) {
5243
+ maxHeight = Math.round(maxWidth * naturalAspectRatio);
5244
+ }
5245
+ if (attrs.width && attrs.height && maxWidth && attrs.width > maxWidth) {
5246
+ const width = maxWidth;
5247
+ const height = Math.round(attrs.height / attrs.width * maxWidth);
5248
+ imageContainer.style.width = `${width}px`;
5249
+ imageContainer.style.height = `${height}px`;
5250
+ const pos = getPos();
5251
+ if (typeof pos === "number") {
5252
+ editor.chain().updateAttributes("image", {
5253
+ width: Math.round(width),
5254
+ height: Math.round(height)
5255
+ }).run();
5256
+ }
5257
+ }
5258
+ };
5259
+ imageContainer.appendChild(img);
5260
+ if (editor.isEditable) {
5261
+ const editButton = document.createElement("button");
5262
+ editButton.type = "button";
5263
+ editButton.className = "figure-edit-button";
5264
+ editButton.contentEditable = "false";
5265
+ editButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="21" height="20" viewBox="0 0 21 20" fill="none"><path d="M2.52441 14.3751V17.5001H5.64941L14.8661 8.28346L11.7411 5.15846L2.52441 14.3751ZM17.2827 5.8668C17.6077 5.5418 17.6077 5.0168 17.2827 4.6918L15.3327 2.7418C15.0077 2.4168 14.4827 2.4168 14.1577 2.7418L12.6327 4.2668L15.7577 7.3918L17.2827 5.8668Z" fill="currentColor"/></svg>';
5266
+ editButton.addEventListener("click", () => {
5267
+ const onFigureClick = imageExtension?.options.onFigureClick;
5268
+ const pos = getPos();
5269
+ if (onFigureClick && typeof pos === "number") {
5270
+ onFigureClick(attrs, pos);
5271
+ }
5272
+ });
5273
+ imageContainer.appendChild(editButton);
5274
+ }
5275
+ const resizableView = new ResizableNodeView({
5276
+ element: imageContainer,
5277
+ node,
5278
+ getPos,
5279
+ onResize: (w, h) => {
5280
+ const { width, height } = constrainDimensions(w, h);
5281
+ updateImageDimensions(width, height);
5282
+ },
5283
+ onCommit: (w, h) => {
5284
+ const { width, height } = constrainDimensions(w, h);
5285
+ const pos = getPos();
5286
+ if (typeof pos === "number") {
5287
+ updateImageDimensions(Math.round(width), Math.round(height));
5288
+ editor.chain().focus().setNodeSelection(pos).updateAttributes("image", {
5289
+ width: Math.round(width),
5290
+ height: Math.round(height)
5291
+ }).run();
5292
+ }
5293
+ },
5294
+ onUpdate: (updatedNode) => {
5295
+ if (updatedNode.attrs.width) {
5296
+ imageContainer.style.width = `${updatedNode.attrs.width}px`;
5297
+ }
5298
+ if (updatedNode.attrs.height) {
5299
+ imageContainer.style.height = `${updatedNode.attrs.height}px`;
5300
+ }
5301
+ const existingCaption = figure.querySelector("figcaption");
5302
+ if (updatedNode.attrs.caption && !existingCaption) {
5303
+ const figcaption = document.createElement("figcaption");
5304
+ figcaption.textContent = updatedNode.attrs.caption;
5305
+ figure.appendChild(figcaption);
5306
+ } else if (existingCaption) {
5307
+ if (updatedNode.attrs.caption) {
5308
+ existingCaption.textContent = updatedNode.attrs.caption;
5309
+ } else {
5310
+ existingCaption.remove();
5311
+ }
5312
+ }
5313
+ if (updatedNode.type !== node.type) return false;
5314
+ return true;
5315
+ },
5316
+ options: {
5317
+ directions: resizeOptions.directions || [
5318
+ "bottom-right",
5319
+ "bottom-left",
5320
+ "top-right",
5321
+ "top-left"
5322
+ ],
5323
+ min: {
5324
+ width: resizeOptions.minWidth || 100,
5325
+ height: resizeOptions.minHeight || 100
5326
+ },
5327
+ max: maxWidth && maxHeight ? { width: maxWidth, height: maxHeight } : void 0,
5328
+ preserveAspectRatio: true,
5329
+ className: {
5330
+ container: "tiptap-resize-container",
5331
+ wrapper: "tiptap-resize-wrapper",
5332
+ handle: "tiptap-resize-handle",
5333
+ resizing: "is-resizing"
5334
+ }
5335
+ }
5336
+ });
5337
+ figure.appendChild(resizableView.dom);
5338
+ if (attrs.caption) {
5339
+ const figcaption = document.createElement("figcaption");
5340
+ figcaption.textContent = attrs.caption;
5341
+ figure.appendChild(figcaption);
5342
+ }
5343
+ return {
5344
+ dom: figure,
5345
+ contentDOM: resizableView.contentDOM,
5346
+ update: resizableView.update.bind(resizableView),
5347
+ destroy: resizableView.destroy?.bind(resizableView)
5348
+ };
5349
+ };
5144
5350
  }
5145
5351
  });
5146
5352
  var LegacyFigure = BaseLegacyFigure.extend({
@@ -5174,8 +5380,14 @@ function getEditorExtensions(options) {
5174
5380
  PromptHeader2,
5175
5381
  PromptContent2,
5176
5382
  Video2,
5177
- // Configure with optional getImageById for URL resolution and onFigureClick callback
5178
- Figure2.configure({ getImageById, onFigureClick }),
5383
+ Figure2.configure({
5384
+ getImageById,
5385
+ onFigureClick,
5386
+ resize: {
5387
+ enabled: true,
5388
+ directions: ["top-left", "top-right", "bottom-left", "bottom-right"]
5389
+ }
5390
+ }),
5179
5391
  Mathematics.configure({
5180
5392
  katexOptions: {
5181
5393
  throwOnError: false
@@ -5618,7 +5830,7 @@ var filterCommandItems = (items, query) => {
5618
5830
  };
5619
5831
 
5620
5832
  // src/editor/commands/suggestion.tsx
5621
- import { jsx as jsx92 } from "react/jsx-runtime";
5833
+ import { jsx as jsx91 } from "react/jsx-runtime";
5622
5834
  var component = null;
5623
5835
  var closeMenu = () => {
5624
5836
  if (component) {
@@ -5632,7 +5844,7 @@ var mapCommandMenuItemToCommandItem = (item) => {
5632
5844
  const IconComponent = item.icon;
5633
5845
  return {
5634
5846
  title: item.title,
5635
- icon: /* @__PURE__ */ jsx92(IconComponent, {}),
5847
+ icon: /* @__PURE__ */ jsx91(IconComponent, {}),
5636
5848
  command: ({ editor, range }) => {
5637
5849
  editor.chain().focus().deleteRange(range).run();
5638
5850
  item.onClick(editor);
@@ -6063,7 +6275,7 @@ function getTranslations(lang) {
6063
6275
  }
6064
6276
 
6065
6277
  // src/editor/editor.tsx
6066
- import { Fragment as Fragment3, jsx as jsx93, jsxs as jsxs36 } from "react/jsx-runtime";
6278
+ import { Fragment as Fragment3, jsx as jsx92, jsxs as jsxs35 } from "react/jsx-runtime";
6067
6279
  function TiptapContent({
6068
6280
  content,
6069
6281
  handleChange,
@@ -6073,12 +6285,12 @@ function TiptapContent({
6073
6285
  lang,
6074
6286
  error
6075
6287
  }) {
6076
- const [linkDialogContext, setLinkDialogContext] = useState13(null);
6077
- const [katexDialogContext, setKatexDialogContext] = useState13(
6288
+ const [linkDialogContext, setLinkDialogContext] = useState12(null);
6289
+ const [katexDialogContext, setKatexDialogContext] = useState12(
6078
6290
  null
6079
6291
  );
6080
- const [editFigureContext, setEditFigureContext] = useState13(null);
6081
- const [savedDialogCursorPos, setSavedDialogCursorPos] = useState13(void 0);
6292
+ const [editFigureContext, setEditFigureContext] = useState12(null);
6293
+ const [savedDialogCursorPos, setSavedDialogCursorPos] = useState12(void 0);
6082
6294
  const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
6083
6295
  const translations2 = useTiptapTranslations();
6084
6296
  let safeContent;
@@ -6129,7 +6341,7 @@ function TiptapContent({
6129
6341
  },
6130
6342
  [lang]
6131
6343
  );
6132
- useEffect10(() => {
6344
+ useEffect9(() => {
6133
6345
  if (activeDialog && editor && !editor.isDestroyed) {
6134
6346
  const pos = editor.state.selection.from;
6135
6347
  setSavedDialogCursorPos(pos);
@@ -6156,7 +6368,7 @@ function TiptapContent({
6156
6368
  const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
6157
6369
  katexHandler(latex, katexDialogContext?.pos);
6158
6370
  };
6159
- useEffect10(() => {
6371
+ useEffect9(() => {
6160
6372
  if (!editor || editor.isDestroyed) return;
6161
6373
  editor.commands.setContent(safeContent, {
6162
6374
  emitUpdate: false
@@ -6175,16 +6387,16 @@ function TiptapContent({
6175
6387
  return true;
6176
6388
  };
6177
6389
  if (!editor) return null;
6178
- return /* @__PURE__ */ jsxs36(Fragment3, { children: [
6179
- /* @__PURE__ */ jsx93(DragHandle, { editor }),
6180
- isEditable && !isDisabled && /* @__PURE__ */ jsx93(TableMenu, { editor }),
6181
- isEditable && !isDisabled && /* @__PURE__ */ jsx93(
6390
+ return /* @__PURE__ */ jsxs35(Fragment3, { children: [
6391
+ /* @__PURE__ */ jsx92(DragHandle, { editor }),
6392
+ isEditable && !isDisabled && /* @__PURE__ */ jsx92(TableMenu, { editor }),
6393
+ isEditable && !isDisabled && /* @__PURE__ */ jsx92(
6182
6394
  BubbleMenu,
6183
6395
  {
6184
6396
  shouldShow: shouldBubbleMenuShow,
6185
6397
  editor,
6186
6398
  options: { placement: "bottom", offset: 8, flip: true },
6187
- children: /* @__PURE__ */ jsx93(
6399
+ children: /* @__PURE__ */ jsx92(
6188
6400
  BubbleMenuContent,
6189
6401
  {
6190
6402
  editor,
@@ -6193,19 +6405,19 @@ function TiptapContent({
6193
6405
  )
6194
6406
  }
6195
6407
  ),
6196
- /* @__PURE__ */ jsxs36(
6408
+ /* @__PURE__ */ jsxs35(
6197
6409
  "div",
6198
6410
  {
6199
6411
  className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${isDisabled ? "is-disabled" : ""}`,
6200
6412
  children: [
6201
- /* @__PURE__ */ jsx93(EditorContent, { editor }),
6202
- isEditable && !isDisabled && /* @__PURE__ */ jsx93(CharacterCount, { editor })
6413
+ /* @__PURE__ */ jsx92(EditorContent, { editor }),
6414
+ isEditable && !isDisabled && /* @__PURE__ */ jsx92(CharacterCount, { editor })
6203
6415
  ]
6204
6416
  }
6205
6417
  ),
6206
- !!error && /* @__PURE__ */ jsx93("p", { className: "tiptap-error-message", children: error }),
6207
- isEditable && /* @__PURE__ */ jsxs36(Fragment3, { children: [
6208
- activeDialog === "image" && /* @__PURE__ */ jsx93(
6418
+ !!error && /* @__PURE__ */ jsx92("p", { className: "tiptap-error-message", children: error }),
6419
+ isEditable && /* @__PURE__ */ jsxs35(Fragment3, { children: [
6420
+ activeDialog === "image" && /* @__PURE__ */ jsx92(
6209
6421
  ImageDialog,
6210
6422
  {
6211
6423
  open: true,
@@ -6218,7 +6430,7 @@ function TiptapContent({
6218
6430
  mode: editFigureContext ? "edit" : "add"
6219
6431
  }
6220
6432
  ),
6221
- activeDialog === "link" && /* @__PURE__ */ jsx93(
6433
+ activeDialog === "link" && /* @__PURE__ */ jsx92(
6222
6434
  AddLinkDialog,
6223
6435
  {
6224
6436
  open: true,
@@ -6227,7 +6439,7 @@ function TiptapContent({
6227
6439
  linkDialogContext
6228
6440
  }
6229
6441
  ),
6230
- activeDialog === "video" && /* @__PURE__ */ jsx93(
6442
+ activeDialog === "video" && /* @__PURE__ */ jsx92(
6231
6443
  AddVideoDialog,
6232
6444
  {
6233
6445
  open: true,
@@ -6235,7 +6447,7 @@ function TiptapContent({
6235
6447
  onVideoAdd: handleVideoAdd
6236
6448
  }
6237
6449
  ),
6238
- activeDialog === "katex" && /* @__PURE__ */ jsx93(
6450
+ activeDialog === "katex" && /* @__PURE__ */ jsx92(
6239
6451
  AddKatexDialog,
6240
6452
  {
6241
6453
  open: true,
@@ -6250,9 +6462,41 @@ function TiptapContent({
6250
6462
  var Tiptap = (props) => {
6251
6463
  const { lang = "ru" } = props;
6252
6464
  const translations2 = getTranslations(lang);
6253
- return /* @__PURE__ */ jsx93(TiptapTranslationsProvider, { translations: translations2, children: /* @__PURE__ */ jsx93(DialogCommandProvider, { children: /* @__PURE__ */ jsx93(TiptapContent, { ...props }) }) });
6465
+ return /* @__PURE__ */ jsx92(TiptapTranslationsProvider, { translations: translations2, children: /* @__PURE__ */ jsx92(DialogCommandProvider, { children: /* @__PURE__ */ jsx92(TiptapContent, { ...props }) }) });
6254
6466
  };
6467
+
6468
+ // src/utils/html-to-tiptap-content.ts
6469
+ import { generateJSON } from "@tiptap/core";
6470
+ function isJSONContent(value) {
6471
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
6472
+ }
6473
+ function htmlToTiptapContent(content) {
6474
+ if (isJSONContent(content)) {
6475
+ return content;
6476
+ }
6477
+ if (typeof content === "string") {
6478
+ try {
6479
+ const parsed = JSON.parse(content);
6480
+ if (isJSONContent(parsed)) {
6481
+ return parsed;
6482
+ }
6483
+ } catch {
6484
+ }
6485
+ }
6486
+ if (!content || content.trim() === "") {
6487
+ return { type: "doc", content: [{ type: "paragraph" }] };
6488
+ }
6489
+ const sharedExtensions = getSharedExtensions();
6490
+ const extensions = [...sharedExtensions, BaseFigure];
6491
+ try {
6492
+ return generateJSON(content, extensions);
6493
+ } catch (error) {
6494
+ console.error("Failed to convert HTML to Tiptap content:", error);
6495
+ return { type: "doc", content: [{ type: "paragraph" }] };
6496
+ }
6497
+ }
6255
6498
  export {
6256
- Tiptap
6499
+ Tiptap,
6500
+ htmlToTiptapContent
6257
6501
  };
6258
6502
  //# sourceMappingURL=editor.mjs.map