@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.
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { T as Token } from './tokens-7kRTEtPb.cjs';
2
2
  export { C as ColorGradient, I as INSTRUCTION_REGEX, a as TEMPLATE_REGEX, b as Template, c as TemplateVersion, d as TokenType, V as VARIABLE_REGEX, i as instructionRegex, t as templateRegex, v as variableRegex } from './tokens-7kRTEtPb.cjs';
3
3
  export { ResolveTemplatesOptions, SourceMapSegment, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.cjs';
4
- export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, Mention, MentionEntity, MentionOptions, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, updateImageSrc } from './extensions/index.cjs';
4
+ export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, Mention, MentionEntity, MentionOptions, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc } from './extensions/index.cjs';
5
5
  export { ExtractedMention, escapeHtml, extractMentionsFromDoc, getEditorMarkdown, getEditorText, htmlToPlainText, markdownToTipTapHtml, plainTextToTipTapHtml } from './serializers/index.cjs';
6
6
  import { Editor, EditorOptions, Content, Extensions } from '@tiptap/core';
7
7
  export { Content, Editor, EditorOptions, Extensions } from '@tiptap/core';
@@ -40,6 +40,7 @@ declare class EditorController {
40
40
  private readonly onMarkdownChange?;
41
41
  private readonly onTokensChange?;
42
42
  private readonly onSelectionChange?;
43
+ private baselineContent;
43
44
  constructor(options?: EditorControllerOptions);
44
45
  private enforcePlainTextClipboard;
45
46
  private handleUpdate;
@@ -51,6 +52,11 @@ declare class EditorController {
51
52
  insertText(text: string): void;
52
53
  insertTextAt(pos: number, text: string): void;
53
54
  replaceRange(from: number, to: number, text: string): void;
55
+ getCurrentContentString(): string;
56
+ isDirty(): boolean;
57
+ setCheckpoint(content?: string): void;
58
+ resetDirty(): void;
59
+ getBaselineContent(): string;
54
60
  destroy(): void;
55
61
  }
56
62
  /**
@@ -58,4 +64,41 @@ declare class EditorController {
58
64
  */
59
65
  declare function createEditor(options?: EditorControllerOptions): EditorController;
60
66
 
61
- export { EditorController, type EditorControllerOptions, Token, createEditor };
67
+ /**
68
+ * Compares two values for structural equality.
69
+ * Handles primitives, objects, arrays, and null/undefined.
70
+ */
71
+ declare function defaultIsEqual<T>(a: T, b: T): boolean;
72
+ interface DirtyTrackerOptions<T> {
73
+ isEqual?: (a: T, b: T) => boolean;
74
+ }
75
+ /**
76
+ * Tracks a baseline value and determines if the current state is dirty (modified).
77
+ */
78
+ declare class DirtyTracker<T = unknown> {
79
+ private baseline;
80
+ private readonly isEqual;
81
+ constructor(initialBaseline: T, options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean));
82
+ /**
83
+ * Returns current baseline value.
84
+ */
85
+ getBaseline(): T;
86
+ /**
87
+ * Sets a new baseline value without triggering any dirty state.
88
+ */
89
+ setBaseline(value: T): void;
90
+ /**
91
+ * Checks whether the given value differs from the baseline.
92
+ */
93
+ isDirty(current: T): boolean;
94
+ /**
95
+ * Resets the baseline to the provided current value (or clears dirty status).
96
+ */
97
+ reset(current: T): void;
98
+ }
99
+ /**
100
+ * Creates a DirtyTracker instance.
101
+ */
102
+ declare function createDirtyTracker<T>(initialBaseline: T, options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean)): DirtyTracker<T>;
103
+
104
+ export { DirtyTracker, type DirtyTrackerOptions, EditorController, type EditorControllerOptions, Token, createDirtyTracker, createEditor, defaultIsEqual };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { T as Token } from './tokens-7kRTEtPb.js';
2
2
  export { C as ColorGradient, I as INSTRUCTION_REGEX, a as TEMPLATE_REGEX, b as Template, c as TemplateVersion, d as TokenType, V as VARIABLE_REGEX, i as instructionRegex, t as templateRegex, v as variableRegex } from './tokens-7kRTEtPb.js';
3
3
  export { ResolveTemplatesOptions, SourceMapSegment, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.js';
4
- export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, Mention, MentionEntity, MentionOptions, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, updateImageSrc } from './extensions/index.js';
4
+ export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, Mention, MentionEntity, MentionOptions, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createMentionExtension, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc } from './extensions/index.js';
5
5
  export { ExtractedMention, escapeHtml, extractMentionsFromDoc, getEditorMarkdown, getEditorText, htmlToPlainText, markdownToTipTapHtml, plainTextToTipTapHtml } from './serializers/index.js';
6
6
  import { Editor, EditorOptions, Content, Extensions } from '@tiptap/core';
7
7
  export { Content, Editor, EditorOptions, Extensions } from '@tiptap/core';
@@ -40,6 +40,7 @@ declare class EditorController {
40
40
  private readonly onMarkdownChange?;
41
41
  private readonly onTokensChange?;
42
42
  private readonly onSelectionChange?;
43
+ private baselineContent;
43
44
  constructor(options?: EditorControllerOptions);
44
45
  private enforcePlainTextClipboard;
45
46
  private handleUpdate;
@@ -51,6 +52,11 @@ declare class EditorController {
51
52
  insertText(text: string): void;
52
53
  insertTextAt(pos: number, text: string): void;
53
54
  replaceRange(from: number, to: number, text: string): void;
55
+ getCurrentContentString(): string;
56
+ isDirty(): boolean;
57
+ setCheckpoint(content?: string): void;
58
+ resetDirty(): void;
59
+ getBaselineContent(): string;
54
60
  destroy(): void;
55
61
  }
56
62
  /**
@@ -58,4 +64,41 @@ declare class EditorController {
58
64
  */
59
65
  declare function createEditor(options?: EditorControllerOptions): EditorController;
60
66
 
61
- export { EditorController, type EditorControllerOptions, Token, createEditor };
67
+ /**
68
+ * Compares two values for structural equality.
69
+ * Handles primitives, objects, arrays, and null/undefined.
70
+ */
71
+ declare function defaultIsEqual<T>(a: T, b: T): boolean;
72
+ interface DirtyTrackerOptions<T> {
73
+ isEqual?: (a: T, b: T) => boolean;
74
+ }
75
+ /**
76
+ * Tracks a baseline value and determines if the current state is dirty (modified).
77
+ */
78
+ declare class DirtyTracker<T = unknown> {
79
+ private baseline;
80
+ private readonly isEqual;
81
+ constructor(initialBaseline: T, options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean));
82
+ /**
83
+ * Returns current baseline value.
84
+ */
85
+ getBaseline(): T;
86
+ /**
87
+ * Sets a new baseline value without triggering any dirty state.
88
+ */
89
+ setBaseline(value: T): void;
90
+ /**
91
+ * Checks whether the given value differs from the baseline.
92
+ */
93
+ isDirty(current: T): boolean;
94
+ /**
95
+ * Resets the baseline to the provided current value (or clears dirty status).
96
+ */
97
+ reset(current: T): void;
98
+ }
99
+ /**
100
+ * Creates a DirtyTracker instance.
101
+ */
102
+ declare function createDirtyTracker<T>(initialBaseline: T, options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean)): DirtyTracker<T>;
103
+
104
+ export { DirtyTracker, type DirtyTrackerOptions, EditorController, type EditorControllerOptions, Token, createDirtyTracker, createEditor, defaultIsEqual };
package/dist/index.js CHANGED
@@ -1919,6 +1919,22 @@ function updateImageSrc(view, oldSrc, newSrc) {
1919
1919
  view.dispatch(tr);
1920
1920
  }
1921
1921
  }
1922
+ function removeImageNode(view, src) {
1923
+ const { doc } = view.state;
1924
+ let foundPos = null;
1925
+ let nodeSize = 0;
1926
+ doc.descendants((node, pos) => {
1927
+ if (node.type.name === "image" && node.attrs.src === src) {
1928
+ foundPos = pos;
1929
+ nodeSize = node.nodeSize;
1930
+ return false;
1931
+ }
1932
+ });
1933
+ if (foundPos !== null) {
1934
+ const tr = view.state.tr.delete(foundPos, foundPos + nodeSize);
1935
+ view.dispatch(tr);
1936
+ }
1937
+ }
1922
1938
  function handleImageInsertion(view, file, options) {
1923
1939
  if (typeof options.upload === "function") {
1924
1940
  const hasObjectUrl = typeof URL !== "undefined" && typeof URL.createObjectURL === "function";
@@ -1927,7 +1943,9 @@ function handleImageInsertion(view, file, options) {
1927
1943
  insertImageNode(view, tempUrl, file.name);
1928
1944
  }
1929
1945
  Promise.resolve(options.upload(file)).then((uploadedUrl) => {
1930
- if (!uploadedUrl) return;
1946
+ if (!uploadedUrl) {
1947
+ throw new Error("Upload handler returned empty URL");
1948
+ }
1931
1949
  if (tempUrl) {
1932
1950
  updateImageSrc(view, tempUrl, uploadedUrl);
1933
1951
  if (typeof URL.revokeObjectURL === "function") {
@@ -1937,9 +1955,32 @@ function handleImageInsertion(view, file, options) {
1937
1955
  insertImageNode(view, uploadedUrl, file.name);
1938
1956
  }
1939
1957
  }).catch((err) => {
1940
- console.error("Failed to upload pasted image:", err);
1958
+ const error = err instanceof Error ? err : new Error(String(err));
1959
+ console.error("Failed to upload pasted image:", error);
1960
+ if (tempUrl) {
1961
+ removeImageNode(view, tempUrl);
1962
+ if (typeof URL.revokeObjectURL === "function") {
1963
+ URL.revokeObjectURL(tempUrl);
1964
+ }
1965
+ }
1966
+ if (typeof options.onUploadError === "function") {
1967
+ options.onUploadError(error, file);
1968
+ }
1941
1969
  });
1942
1970
  } else if (options.allowBase64 !== false) {
1971
+ const maxBase64Size = options.maxBase64Size ?? 65536;
1972
+ if (file.size > maxBase64Size) {
1973
+ const err = new Error(
1974
+ `Image size (${Math.round(file.size / 1024)}KB) exceeds maxBase64Size (${Math.round(
1975
+ maxBase64Size / 1024
1976
+ )}KB). Provide an upload handler to support large images.`
1977
+ );
1978
+ console.warn(`[Editor] ${err.message}`);
1979
+ if (typeof options.onUploadError === "function") {
1980
+ options.onUploadError(err, file);
1981
+ }
1982
+ return;
1983
+ }
1943
1984
  if (typeof FileReader !== "undefined") {
1944
1985
  const reader = new FileReader();
1945
1986
  reader.onload = () => {
@@ -1957,9 +1998,11 @@ var Image2 = BaseImage.extend({
1957
1998
  return {
1958
1999
  ...this.parent?.(),
1959
2000
  allowBase64: true,
2001
+ maxBase64Size: 65536,
1960
2002
  enablePaste: true,
1961
2003
  enableDrop: true,
1962
2004
  upload: void 0,
2005
+ onUploadError: void 0,
1963
2006
  HTMLAttributes: {
1964
2007
  class: "rounded-md max-w-full h-auto my-2"
1965
2008
  }
@@ -2315,6 +2358,7 @@ var EditorController = class {
2315
2358
  onMarkdownChange;
2316
2359
  onTokensChange;
2317
2360
  onSelectionChange;
2361
+ baselineContent;
2318
2362
  constructor(options = {}) {
2319
2363
  const {
2320
2364
  element,
@@ -2352,6 +2396,7 @@ var EditorController = class {
2352
2396
  if (plainTextMode) {
2353
2397
  this.enforcePlainTextClipboard();
2354
2398
  }
2399
+ this.baselineContent = this.getCurrentContentString();
2355
2400
  }
2356
2401
  enforcePlainTextClipboard() {
2357
2402
  this.editor.setOptions({
@@ -2425,6 +2470,23 @@ var EditorController = class {
2425
2470
  replaceRange(from, to, text) {
2426
2471
  this.editor.chain().focus().deleteRange({ from, to }).insertContentAt(from, text).run();
2427
2472
  }
2473
+ getCurrentContentString() {
2474
+ if (this.plainTextMode) return this.getPlainText();
2475
+ if (this.markdownMode) return this.getMarkdown();
2476
+ return this.editor.getHTML();
2477
+ }
2478
+ isDirty() {
2479
+ return this.getCurrentContentString() !== this.baselineContent;
2480
+ }
2481
+ setCheckpoint(content) {
2482
+ this.baselineContent = content !== void 0 ? content : this.getCurrentContentString();
2483
+ }
2484
+ resetDirty() {
2485
+ this.setCheckpoint();
2486
+ }
2487
+ getBaselineContent() {
2488
+ return this.baselineContent;
2489
+ }
2428
2490
  destroy() {
2429
2491
  this.editor.destroy();
2430
2492
  }
@@ -3707,10 +3769,76 @@ var SpreadsheetController = class {
3707
3769
  this.onDocumentChange?.(this.document);
3708
3770
  }
3709
3771
  };
3772
+
3773
+ // src/dirty.ts
3774
+ function defaultIsEqual(a, b) {
3775
+ if (Object.is(a, b)) return true;
3776
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
3777
+ return false;
3778
+ }
3779
+ if (Array.isArray(a) && Array.isArray(b)) {
3780
+ if (a.length !== b.length) return false;
3781
+ for (let i = 0; i < a.length; i++) {
3782
+ if (!defaultIsEqual(a[i], b[i])) return false;
3783
+ }
3784
+ return true;
3785
+ }
3786
+ if (Array.isArray(a) !== Array.isArray(b)) {
3787
+ return false;
3788
+ }
3789
+ const keysA = Object.keys(a);
3790
+ const keysB = Object.keys(b);
3791
+ if (keysA.length !== keysB.length) return false;
3792
+ for (const key of keysA) {
3793
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
3794
+ if (!defaultIsEqual(a[key], b[key])) return false;
3795
+ }
3796
+ return true;
3797
+ }
3798
+ var DirtyTracker = class {
3799
+ baseline;
3800
+ isEqual;
3801
+ constructor(initialBaseline, options) {
3802
+ this.baseline = initialBaseline;
3803
+ if (typeof options === "function") {
3804
+ this.isEqual = options;
3805
+ } else {
3806
+ this.isEqual = options?.isEqual ?? defaultIsEqual;
3807
+ }
3808
+ }
3809
+ /**
3810
+ * Returns current baseline value.
3811
+ */
3812
+ getBaseline() {
3813
+ return this.baseline;
3814
+ }
3815
+ /**
3816
+ * Sets a new baseline value without triggering any dirty state.
3817
+ */
3818
+ setBaseline(value) {
3819
+ this.baseline = value;
3820
+ }
3821
+ /**
3822
+ * Checks whether the given value differs from the baseline.
3823
+ */
3824
+ isDirty(current) {
3825
+ return !this.isEqual(this.baseline, current);
3826
+ }
3827
+ /**
3828
+ * Resets the baseline to the provided current value (or clears dirty status).
3829
+ */
3830
+ reset(current) {
3831
+ this.baseline = current;
3832
+ }
3833
+ };
3834
+ function createDirtyTracker(initialBaseline, options) {
3835
+ return new DirtyTracker(initialBaseline, options);
3836
+ }
3710
3837
  export {
3711
3838
  ColorHighlighter,
3712
3839
  DataSourceBinder,
3713
3840
  DependencyGraph,
3841
+ DirtyTracker,
3714
3842
  EditorController,
3715
3843
  FontSize,
3716
3844
  FormulaEvaluator,
@@ -3728,9 +3856,11 @@ export {
3728
3856
  TemplateSuggestions,
3729
3857
  VARIABLE_REGEX,
3730
3858
  colNameToIndex,
3859
+ createDirtyTracker,
3731
3860
  createEditor,
3732
3861
  createMentionExtension,
3733
3862
  createRichTextPreset,
3863
+ defaultIsEqual,
3734
3864
  escapeHtml,
3735
3865
  extractCellDependencies,
3736
3866
  extractInstructionNames,
@@ -3754,6 +3884,7 @@ export {
3754
3884
  parseFormula,
3755
3885
  parseRange,
3756
3886
  plainTextToTipTapHtml,
3887
+ removeImageNode,
3757
3888
  resolveTemplates,
3758
3889
  resolveTemplatesWithMapping,
3759
3890
  resolveVariables,