@pixerate/editor 0.5.1 → 0.6.1

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.
@@ -33,6 +33,7 @@ __export(extensions_exports, {
33
33
  ColorHighlighter: () => ColorHighlighter,
34
34
  FontSize: () => FontSize,
35
35
  GradientText: () => GradientText,
36
+ Image: () => Image2,
36
37
  LoadingNode: () => LoadingNode,
37
38
  Markdown: () => Markdown2,
38
39
  Mention: () => Mention,
@@ -41,7 +42,12 @@ __export(extensions_exports, {
41
42
  TemplateSuggestions: () => TemplateSuggestions,
42
43
  createMentionExtension: () => createMentionExtension,
43
44
  createRichTextPreset: () => createRichTextPreset,
44
- findHexColors: () => findHexColors
45
+ findHexColors: () => findHexColors,
46
+ handleImageInsertion: () => handleImageInsertion,
47
+ imagePastePluginKey: () => imagePastePluginKey,
48
+ insertImageNode: () => insertImageNode,
49
+ removeImageNode: () => removeImageNode,
50
+ updateImageSrc: () => updateImageSrc
45
51
  });
46
52
  module.exports = __toCommonJS(extensions_exports);
47
53
 
@@ -1594,20 +1600,183 @@ var Markdown = import_core8.Extension.create({
1594
1600
  var resolveExt = (mod, name) => mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;
1595
1601
  var Markdown2 = resolveExt(Markdown, "Markdown");
1596
1602
 
1597
- // src/extensions/rich-text-preset.ts
1603
+ // src/extensions/image.ts
1604
+ var _Image = __toESM(require("@tiptap/extension-image"), 1);
1605
+ var import_state5 = require("@tiptap/pm/state");
1598
1606
  var resolveExt2 = (mod, name) => mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;
1599
- var StarterKit = resolveExt2(_StarterKit, "StarterKit");
1600
- var Color = resolveExt2(_Color, "Color");
1601
- var Highlight = resolveExt2(_Highlight, "Highlight");
1602
- var Link2 = resolveExt2(_Link, "Link");
1603
- var TaskList2 = resolveExt2(_TaskList, "TaskList");
1604
- var TaskItem2 = resolveExt2(_TaskItem, "TaskItem");
1605
- var TextAlign = resolveExt2(_TextAlign, "TextAlign");
1606
- var TextStyle = resolveExt2(_TextStyle, "TextStyle");
1607
- var Typography = resolveExt2(_Typography, "Typography");
1608
- var Underline = resolveExt2(_Underline, "Underline");
1609
- var Placeholder = resolveExt2(_Placeholder, "Placeholder");
1610
- var CharacterCount = resolveExt2(_CharacterCount, "CharacterCount");
1607
+ var BaseImage = resolveExt2(_Image, "Image");
1608
+ var imagePastePluginKey = new import_state5.PluginKey("imagePasteHandler");
1609
+ function insertImageNode(view, src, alt) {
1610
+ const { schema } = view.state;
1611
+ const imageType = schema.nodes.image;
1612
+ if (!imageType) return;
1613
+ const node = imageType.create({ src, alt: alt || "" });
1614
+ const tr = view.state.tr.replaceSelectionWith(node);
1615
+ view.dispatch(tr);
1616
+ }
1617
+ function updateImageSrc(view, oldSrc, newSrc) {
1618
+ const { doc } = view.state;
1619
+ let foundPos = null;
1620
+ let foundAttrs = null;
1621
+ doc.descendants((node, pos) => {
1622
+ if (node.type.name === "image" && node.attrs.src === oldSrc) {
1623
+ foundPos = pos;
1624
+ foundAttrs = node.attrs;
1625
+ return false;
1626
+ }
1627
+ });
1628
+ if (foundPos !== null && foundAttrs) {
1629
+ const tr = view.state.tr.setNodeMarkup(foundPos, void 0, {
1630
+ ...foundAttrs,
1631
+ src: newSrc
1632
+ });
1633
+ view.dispatch(tr);
1634
+ }
1635
+ }
1636
+ function removeImageNode(view, src) {
1637
+ const { doc } = view.state;
1638
+ let foundPos = null;
1639
+ let nodeSize = 0;
1640
+ doc.descendants((node, pos) => {
1641
+ if (node.type.name === "image" && node.attrs.src === src) {
1642
+ foundPos = pos;
1643
+ nodeSize = node.nodeSize;
1644
+ return false;
1645
+ }
1646
+ });
1647
+ if (foundPos !== null) {
1648
+ const tr = view.state.tr.delete(foundPos, foundPos + nodeSize);
1649
+ view.dispatch(tr);
1650
+ }
1651
+ }
1652
+ function handleImageInsertion(view, file, options) {
1653
+ if (typeof options.upload === "function") {
1654
+ const hasObjectUrl = typeof URL !== "undefined" && typeof URL.createObjectURL === "function";
1655
+ const tempUrl = hasObjectUrl ? URL.createObjectURL(file) : null;
1656
+ if (tempUrl) {
1657
+ insertImageNode(view, tempUrl, file.name);
1658
+ }
1659
+ Promise.resolve(options.upload(file)).then((uploadedUrl) => {
1660
+ if (!uploadedUrl) {
1661
+ throw new Error("Upload handler returned empty URL");
1662
+ }
1663
+ if (tempUrl) {
1664
+ updateImageSrc(view, tempUrl, uploadedUrl);
1665
+ if (typeof URL.revokeObjectURL === "function") {
1666
+ URL.revokeObjectURL(tempUrl);
1667
+ }
1668
+ } else {
1669
+ insertImageNode(view, uploadedUrl, file.name);
1670
+ }
1671
+ }).catch((err) => {
1672
+ const error = err instanceof Error ? err : new Error(String(err));
1673
+ console.error("Failed to upload pasted image:", error);
1674
+ if (tempUrl) {
1675
+ removeImageNode(view, tempUrl);
1676
+ if (typeof URL.revokeObjectURL === "function") {
1677
+ URL.revokeObjectURL(tempUrl);
1678
+ }
1679
+ }
1680
+ if (typeof options.onUploadError === "function") {
1681
+ options.onUploadError(error, file);
1682
+ }
1683
+ });
1684
+ } else if (options.allowBase64 !== false) {
1685
+ const maxBase64Size = options.maxBase64Size ?? 65536;
1686
+ if (file.size > maxBase64Size) {
1687
+ const err = new Error(
1688
+ `Image size (${Math.round(file.size / 1024)}KB) exceeds maxBase64Size (${Math.round(
1689
+ maxBase64Size / 1024
1690
+ )}KB). Provide an upload handler to support large images.`
1691
+ );
1692
+ console.warn(`[Editor] ${err.message}`);
1693
+ if (typeof options.onUploadError === "function") {
1694
+ options.onUploadError(err, file);
1695
+ }
1696
+ return;
1697
+ }
1698
+ if (typeof FileReader !== "undefined") {
1699
+ const reader = new FileReader();
1700
+ reader.onload = () => {
1701
+ const src = reader.result;
1702
+ if (src) {
1703
+ insertImageNode(view, src, file.name);
1704
+ }
1705
+ };
1706
+ reader.readAsDataURL(file);
1707
+ }
1708
+ }
1709
+ }
1710
+ var Image2 = BaseImage.extend({
1711
+ addOptions() {
1712
+ return {
1713
+ ...this.parent?.(),
1714
+ allowBase64: true,
1715
+ maxBase64Size: 65536,
1716
+ enablePaste: true,
1717
+ enableDrop: true,
1718
+ upload: void 0,
1719
+ onUploadError: void 0,
1720
+ HTMLAttributes: {
1721
+ class: "rounded-md max-w-full h-auto my-2"
1722
+ }
1723
+ };
1724
+ },
1725
+ addProseMirrorPlugins() {
1726
+ const parentPlugins = this.parent?.() || [];
1727
+ const options = this.options;
1728
+ if (!options.enablePaste && !options.enableDrop) {
1729
+ return parentPlugins;
1730
+ }
1731
+ const pastePlugin = new import_state5.Plugin({
1732
+ key: imagePastePluginKey,
1733
+ props: {
1734
+ handlePaste(view, event) {
1735
+ if (!options.enablePaste) return false;
1736
+ const items = Array.from(event.clipboardData?.items || []);
1737
+ const imageItems = items.filter(
1738
+ (item) => item.kind === "file" && item.type.startsWith("image/")
1739
+ );
1740
+ if (imageItems.length === 0) return false;
1741
+ event.preventDefault();
1742
+ for (const item of imageItems) {
1743
+ const file = item.getAsFile();
1744
+ if (!file) continue;
1745
+ handleImageInsertion(view, file, options);
1746
+ }
1747
+ return true;
1748
+ },
1749
+ handleDrop(view, event) {
1750
+ if (!options.enableDrop) return false;
1751
+ const files = Array.from(event.dataTransfer?.files || []);
1752
+ const imageFiles = files.filter((f) => f.type.startsWith("image/"));
1753
+ if (imageFiles.length === 0) return false;
1754
+ event.preventDefault();
1755
+ for (const file of imageFiles) {
1756
+ handleImageInsertion(view, file, options);
1757
+ }
1758
+ return true;
1759
+ }
1760
+ }
1761
+ });
1762
+ return [...parentPlugins, pastePlugin];
1763
+ }
1764
+ });
1765
+
1766
+ // src/extensions/rich-text-preset.ts
1767
+ var resolveExt3 = (mod, name) => mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;
1768
+ var StarterKit = resolveExt3(_StarterKit, "StarterKit");
1769
+ var Color = resolveExt3(_Color, "Color");
1770
+ var Highlight = resolveExt3(_Highlight, "Highlight");
1771
+ var Link2 = resolveExt3(_Link, "Link");
1772
+ var TaskList2 = resolveExt3(_TaskList, "TaskList");
1773
+ var TaskItem2 = resolveExt3(_TaskItem, "TaskItem");
1774
+ var TextAlign = resolveExt3(_TextAlign, "TextAlign");
1775
+ var TextStyle = resolveExt3(_TextStyle, "TextStyle");
1776
+ var Typography = resolveExt3(_Typography, "Typography");
1777
+ var Underline = resolveExt3(_Underline, "Underline");
1778
+ var Placeholder = resolveExt3(_Placeholder, "Placeholder");
1779
+ var CharacterCount = resolveExt3(_CharacterCount, "CharacterCount");
1611
1780
  function createRichTextPreset(options = {}) {
1612
1781
  const {
1613
1782
  placeholder = "Start typing...",
@@ -1729,6 +1898,18 @@ function createRichTextPreset(options = {}) {
1729
1898
  })
1730
1899
  );
1731
1900
  }
1901
+ if (options.image !== false) {
1902
+ const imageConfig = typeof options.image === "object" ? options.image : {};
1903
+ extensions2.push(
1904
+ Image2.configure({
1905
+ HTMLAttributes: {
1906
+ class: "rounded-md max-w-full h-auto my-2",
1907
+ ...imageConfig.HTMLAttributes || {}
1908
+ },
1909
+ ...imageConfig
1910
+ })
1911
+ );
1912
+ }
1732
1913
  return extensions2;
1733
1914
  }
1734
1915
  // Annotate the CommonJS export names for ESM import in node:
@@ -1736,6 +1917,7 @@ function createRichTextPreset(options = {}) {
1736
1917
  ColorHighlighter,
1737
1918
  FontSize,
1738
1919
  GradientText,
1920
+ Image,
1739
1921
  LoadingNode,
1740
1922
  Markdown,
1741
1923
  Mention,
@@ -1744,6 +1926,11 @@ function createRichTextPreset(options = {}) {
1744
1926
  TemplateSuggestions,
1745
1927
  createMentionExtension,
1746
1928
  createRichTextPreset,
1747
- findHexColors
1929
+ findHexColors,
1930
+ handleImageInsertion,
1931
+ imagePastePluginKey,
1932
+ insertImageNode,
1933
+ removeImageNode,
1934
+ updateImageSrc
1748
1935
  });
1749
1936
  //# sourceMappingURL=index.cjs.map