@pixerate/editor 0.6.0 → 0.7.0

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.
@@ -108,6 +108,17 @@ interface ImageOptions extends Partial<ImageOptions$1> {
108
108
  * Resolves to the uploaded image URL string.
109
109
  */
110
110
  upload?: (file: File) => Promise<string> | string;
111
+ /**
112
+ * Callback invoked if the upload handler throws or rejects, or if an image
113
+ * exceeds maxBase64Size when no upload handler is configured.
114
+ */
115
+ onUploadError?: (error: Error, file: File) => void;
116
+ /**
117
+ * Maximum allowed file size (in bytes) when embedding images as base64 data URLs
118
+ * without an upload handler. Defaults to 65536 (64 KB).
119
+ * Files larger than this will be rejected to avoid memory bloat and prompt/stream saturation.
120
+ */
121
+ maxBase64Size?: number;
111
122
  /**
112
123
  * Whether to enable automatic image pasting from clipboard. Defaults to true.
113
124
  */
@@ -126,6 +137,10 @@ declare function insertImageNode(view: any, src: string, alt?: string): void;
126
137
  * Updates an existing image node's src attribute across document positions.
127
138
  */
128
139
  declare function updateImageSrc(view: any, oldSrc: string, newSrc: string): void;
140
+ /**
141
+ * Removes an existing image node matching src attribute across document positions.
142
+ */
143
+ declare function removeImageNode(view: any, src: string): void;
129
144
  /**
130
145
  * Handles inserting a pasted or dropped image file, supporting both async upload
131
146
  * handlers and direct base64/object-URL fallbacks.
@@ -151,4 +166,4 @@ interface RichTextPresetOptions {
151
166
  */
152
167
  declare function createRichTextPreset(options?: RichTextPresetOptions): Extensions;
153
168
 
154
- export { ColorHighlighter, FontSize, GradientText, type GradientTextOptions, Image, type ImageOptions, LoadingNode, Markdown, Mention, type MentionEntity, type MentionOptions, type RichTextPresetOptions, type SlashCommandMatchData, SlashCommands, type SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, type TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, updateImageSrc };
169
+ export { ColorHighlighter, FontSize, GradientText, type GradientTextOptions, Image, type ImageOptions, LoadingNode, Markdown, Mention, type MentionEntity, type MentionOptions, type RichTextPresetOptions, type SlashCommandMatchData, SlashCommands, type SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, type TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc };
@@ -108,6 +108,17 @@ interface ImageOptions extends Partial<ImageOptions$1> {
108
108
  * Resolves to the uploaded image URL string.
109
109
  */
110
110
  upload?: (file: File) => Promise<string> | string;
111
+ /**
112
+ * Callback invoked if the upload handler throws or rejects, or if an image
113
+ * exceeds maxBase64Size when no upload handler is configured.
114
+ */
115
+ onUploadError?: (error: Error, file: File) => void;
116
+ /**
117
+ * Maximum allowed file size (in bytes) when embedding images as base64 data URLs
118
+ * without an upload handler. Defaults to 65536 (64 KB).
119
+ * Files larger than this will be rejected to avoid memory bloat and prompt/stream saturation.
120
+ */
121
+ maxBase64Size?: number;
111
122
  /**
112
123
  * Whether to enable automatic image pasting from clipboard. Defaults to true.
113
124
  */
@@ -126,6 +137,10 @@ declare function insertImageNode(view: any, src: string, alt?: string): void;
126
137
  * Updates an existing image node's src attribute across document positions.
127
138
  */
128
139
  declare function updateImageSrc(view: any, oldSrc: string, newSrc: string): void;
140
+ /**
141
+ * Removes an existing image node matching src attribute across document positions.
142
+ */
143
+ declare function removeImageNode(view: any, src: string): void;
129
144
  /**
130
145
  * Handles inserting a pasted or dropped image file, supporting both async upload
131
146
  * handlers and direct base64/object-URL fallbacks.
@@ -151,4 +166,4 @@ interface RichTextPresetOptions {
151
166
  */
152
167
  declare function createRichTextPreset(options?: RichTextPresetOptions): Extensions;
153
168
 
154
- export { ColorHighlighter, FontSize, GradientText, type GradientTextOptions, Image, type ImageOptions, LoadingNode, Markdown, Mention, type MentionEntity, type MentionOptions, type RichTextPresetOptions, type SlashCommandMatchData, SlashCommands, type SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, type TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, updateImageSrc };
169
+ export { ColorHighlighter, FontSize, GradientText, type GradientTextOptions, Image, type ImageOptions, LoadingNode, Markdown, Mention, type MentionEntity, type MentionOptions, type RichTextPresetOptions, type SlashCommandMatchData, SlashCommands, type SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, type TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc };
@@ -1585,6 +1585,22 @@ function updateImageSrc(view, oldSrc, newSrc) {
1585
1585
  view.dispatch(tr);
1586
1586
  }
1587
1587
  }
1588
+ function removeImageNode(view, src) {
1589
+ const { doc } = view.state;
1590
+ let foundPos = null;
1591
+ let nodeSize = 0;
1592
+ doc.descendants((node, pos) => {
1593
+ if (node.type.name === "image" && node.attrs.src === src) {
1594
+ foundPos = pos;
1595
+ nodeSize = node.nodeSize;
1596
+ return false;
1597
+ }
1598
+ });
1599
+ if (foundPos !== null) {
1600
+ const tr = view.state.tr.delete(foundPos, foundPos + nodeSize);
1601
+ view.dispatch(tr);
1602
+ }
1603
+ }
1588
1604
  function handleImageInsertion(view, file, options) {
1589
1605
  if (typeof options.upload === "function") {
1590
1606
  const hasObjectUrl = typeof URL !== "undefined" && typeof URL.createObjectURL === "function";
@@ -1593,7 +1609,9 @@ function handleImageInsertion(view, file, options) {
1593
1609
  insertImageNode(view, tempUrl, file.name);
1594
1610
  }
1595
1611
  Promise.resolve(options.upload(file)).then((uploadedUrl) => {
1596
- if (!uploadedUrl) return;
1612
+ if (!uploadedUrl) {
1613
+ throw new Error("Upload handler returned empty URL");
1614
+ }
1597
1615
  if (tempUrl) {
1598
1616
  updateImageSrc(view, tempUrl, uploadedUrl);
1599
1617
  if (typeof URL.revokeObjectURL === "function") {
@@ -1603,9 +1621,32 @@ function handleImageInsertion(view, file, options) {
1603
1621
  insertImageNode(view, uploadedUrl, file.name);
1604
1622
  }
1605
1623
  }).catch((err) => {
1606
- console.error("Failed to upload pasted image:", err);
1624
+ const error = err instanceof Error ? err : new Error(String(err));
1625
+ console.error("Failed to upload pasted image:", error);
1626
+ if (tempUrl) {
1627
+ removeImageNode(view, tempUrl);
1628
+ if (typeof URL.revokeObjectURL === "function") {
1629
+ URL.revokeObjectURL(tempUrl);
1630
+ }
1631
+ }
1632
+ if (typeof options.onUploadError === "function") {
1633
+ options.onUploadError(error, file);
1634
+ }
1607
1635
  });
1608
1636
  } else if (options.allowBase64 !== false) {
1637
+ const maxBase64Size = options.maxBase64Size ?? 65536;
1638
+ if (file.size > maxBase64Size) {
1639
+ const err = new Error(
1640
+ `Image size (${Math.round(file.size / 1024)}KB) exceeds maxBase64Size (${Math.round(
1641
+ maxBase64Size / 1024
1642
+ )}KB). Provide an upload handler to support large images.`
1643
+ );
1644
+ console.warn(`[Editor] ${err.message}`);
1645
+ if (typeof options.onUploadError === "function") {
1646
+ options.onUploadError(err, file);
1647
+ }
1648
+ return;
1649
+ }
1609
1650
  if (typeof FileReader !== "undefined") {
1610
1651
  const reader = new FileReader();
1611
1652
  reader.onload = () => {
@@ -1623,9 +1664,11 @@ var Image2 = BaseImage.extend({
1623
1664
  return {
1624
1665
  ...this.parent?.(),
1625
1666
  allowBase64: true,
1667
+ maxBase64Size: 65536,
1626
1668
  enablePaste: true,
1627
1669
  enableDrop: true,
1628
1670
  upload: void 0,
1671
+ onUploadError: void 0,
1629
1672
  HTMLAttributes: {
1630
1673
  class: "rounded-md max-w-full h-auto my-2"
1631
1674
  }
@@ -1838,6 +1881,7 @@ export {
1838
1881
  handleImageInsertion,
1839
1882
  imagePastePluginKey,
1840
1883
  insertImageNode,
1884
+ removeImageNode,
1841
1885
  updateImageSrc
1842
1886
  };
1843
1887
  //# sourceMappingURL=index.js.map