@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.cjs CHANGED
@@ -33,6 +33,7 @@ __export(src_exports, {
33
33
  ColorHighlighter: () => ColorHighlighter,
34
34
  DataSourceBinder: () => DataSourceBinder,
35
35
  DependencyGraph: () => DependencyGraph,
36
+ DirtyTracker: () => DirtyTracker,
36
37
  EditorController: () => EditorController,
37
38
  FontSize: () => FontSize,
38
39
  FormulaEvaluator: () => FormulaEvaluator,
@@ -50,9 +51,11 @@ __export(src_exports, {
50
51
  TemplateSuggestions: () => TemplateSuggestions,
51
52
  VARIABLE_REGEX: () => VARIABLE_REGEX,
52
53
  colNameToIndex: () => colNameToIndex,
54
+ createDirtyTracker: () => createDirtyTracker,
53
55
  createEditor: () => createEditor,
54
56
  createMentionExtension: () => createMentionExtension,
55
57
  createRichTextPreset: () => createRichTextPreset,
58
+ defaultIsEqual: () => defaultIsEqual,
56
59
  escapeHtml: () => escapeHtml,
57
60
  extractCellDependencies: () => extractCellDependencies,
58
61
  extractInstructionNames: () => extractInstructionNames,
@@ -76,6 +79,7 @@ __export(src_exports, {
76
79
  parseFormula: () => parseFormula,
77
80
  parseRange: () => parseRange,
78
81
  plainTextToTipTapHtml: () => plainTextToTipTapHtml,
82
+ removeImageNode: () => removeImageNode,
79
83
  resolveTemplates: () => resolveTemplates,
80
84
  resolveTemplatesWithMapping: () => resolveTemplatesWithMapping,
81
85
  resolveVariables: () => resolveVariables,
@@ -2003,6 +2007,22 @@ function updateImageSrc(view, oldSrc, newSrc) {
2003
2007
  view.dispatch(tr);
2004
2008
  }
2005
2009
  }
2010
+ function removeImageNode(view, src) {
2011
+ const { doc } = view.state;
2012
+ let foundPos = null;
2013
+ let nodeSize = 0;
2014
+ doc.descendants((node, pos) => {
2015
+ if (node.type.name === "image" && node.attrs.src === src) {
2016
+ foundPos = pos;
2017
+ nodeSize = node.nodeSize;
2018
+ return false;
2019
+ }
2020
+ });
2021
+ if (foundPos !== null) {
2022
+ const tr = view.state.tr.delete(foundPos, foundPos + nodeSize);
2023
+ view.dispatch(tr);
2024
+ }
2025
+ }
2006
2026
  function handleImageInsertion(view, file, options) {
2007
2027
  if (typeof options.upload === "function") {
2008
2028
  const hasObjectUrl = typeof URL !== "undefined" && typeof URL.createObjectURL === "function";
@@ -2011,7 +2031,9 @@ function handleImageInsertion(view, file, options) {
2011
2031
  insertImageNode(view, tempUrl, file.name);
2012
2032
  }
2013
2033
  Promise.resolve(options.upload(file)).then((uploadedUrl) => {
2014
- if (!uploadedUrl) return;
2034
+ if (!uploadedUrl) {
2035
+ throw new Error("Upload handler returned empty URL");
2036
+ }
2015
2037
  if (tempUrl) {
2016
2038
  updateImageSrc(view, tempUrl, uploadedUrl);
2017
2039
  if (typeof URL.revokeObjectURL === "function") {
@@ -2021,9 +2043,32 @@ function handleImageInsertion(view, file, options) {
2021
2043
  insertImageNode(view, uploadedUrl, file.name);
2022
2044
  }
2023
2045
  }).catch((err) => {
2024
- console.error("Failed to upload pasted image:", err);
2046
+ const error = err instanceof Error ? err : new Error(String(err));
2047
+ console.error("Failed to upload pasted image:", error);
2048
+ if (tempUrl) {
2049
+ removeImageNode(view, tempUrl);
2050
+ if (typeof URL.revokeObjectURL === "function") {
2051
+ URL.revokeObjectURL(tempUrl);
2052
+ }
2053
+ }
2054
+ if (typeof options.onUploadError === "function") {
2055
+ options.onUploadError(error, file);
2056
+ }
2025
2057
  });
2026
2058
  } else if (options.allowBase64 !== false) {
2059
+ const maxBase64Size = options.maxBase64Size ?? 65536;
2060
+ if (file.size > maxBase64Size) {
2061
+ const err = new Error(
2062
+ `Image size (${Math.round(file.size / 1024)}KB) exceeds maxBase64Size (${Math.round(
2063
+ maxBase64Size / 1024
2064
+ )}KB). Provide an upload handler to support large images.`
2065
+ );
2066
+ console.warn(`[Editor] ${err.message}`);
2067
+ if (typeof options.onUploadError === "function") {
2068
+ options.onUploadError(err, file);
2069
+ }
2070
+ return;
2071
+ }
2027
2072
  if (typeof FileReader !== "undefined") {
2028
2073
  const reader = new FileReader();
2029
2074
  reader.onload = () => {
@@ -2041,9 +2086,11 @@ var Image2 = BaseImage.extend({
2041
2086
  return {
2042
2087
  ...this.parent?.(),
2043
2088
  allowBase64: true,
2089
+ maxBase64Size: 65536,
2044
2090
  enablePaste: true,
2045
2091
  enableDrop: true,
2046
2092
  upload: void 0,
2093
+ onUploadError: void 0,
2047
2094
  HTMLAttributes: {
2048
2095
  class: "rounded-md max-w-full h-auto my-2"
2049
2096
  }
@@ -2399,6 +2446,7 @@ var EditorController = class {
2399
2446
  onMarkdownChange;
2400
2447
  onTokensChange;
2401
2448
  onSelectionChange;
2449
+ baselineContent;
2402
2450
  constructor(options = {}) {
2403
2451
  const {
2404
2452
  element,
@@ -2436,6 +2484,7 @@ var EditorController = class {
2436
2484
  if (plainTextMode) {
2437
2485
  this.enforcePlainTextClipboard();
2438
2486
  }
2487
+ this.baselineContent = this.getCurrentContentString();
2439
2488
  }
2440
2489
  enforcePlainTextClipboard() {
2441
2490
  this.editor.setOptions({
@@ -2509,6 +2558,23 @@ var EditorController = class {
2509
2558
  replaceRange(from, to, text) {
2510
2559
  this.editor.chain().focus().deleteRange({ from, to }).insertContentAt(from, text).run();
2511
2560
  }
2561
+ getCurrentContentString() {
2562
+ if (this.plainTextMode) return this.getPlainText();
2563
+ if (this.markdownMode) return this.getMarkdown();
2564
+ return this.editor.getHTML();
2565
+ }
2566
+ isDirty() {
2567
+ return this.getCurrentContentString() !== this.baselineContent;
2568
+ }
2569
+ setCheckpoint(content) {
2570
+ this.baselineContent = content !== void 0 ? content : this.getCurrentContentString();
2571
+ }
2572
+ resetDirty() {
2573
+ this.setCheckpoint();
2574
+ }
2575
+ getBaselineContent() {
2576
+ return this.baselineContent;
2577
+ }
2512
2578
  destroy() {
2513
2579
  this.editor.destroy();
2514
2580
  }
@@ -3791,11 +3857,77 @@ var SpreadsheetController = class {
3791
3857
  this.onDocumentChange?.(this.document);
3792
3858
  }
3793
3859
  };
3860
+
3861
+ // src/dirty.ts
3862
+ function defaultIsEqual(a, b) {
3863
+ if (Object.is(a, b)) return true;
3864
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
3865
+ return false;
3866
+ }
3867
+ if (Array.isArray(a) && Array.isArray(b)) {
3868
+ if (a.length !== b.length) return false;
3869
+ for (let i = 0; i < a.length; i++) {
3870
+ if (!defaultIsEqual(a[i], b[i])) return false;
3871
+ }
3872
+ return true;
3873
+ }
3874
+ if (Array.isArray(a) !== Array.isArray(b)) {
3875
+ return false;
3876
+ }
3877
+ const keysA = Object.keys(a);
3878
+ const keysB = Object.keys(b);
3879
+ if (keysA.length !== keysB.length) return false;
3880
+ for (const key of keysA) {
3881
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
3882
+ if (!defaultIsEqual(a[key], b[key])) return false;
3883
+ }
3884
+ return true;
3885
+ }
3886
+ var DirtyTracker = class {
3887
+ baseline;
3888
+ isEqual;
3889
+ constructor(initialBaseline, options) {
3890
+ this.baseline = initialBaseline;
3891
+ if (typeof options === "function") {
3892
+ this.isEqual = options;
3893
+ } else {
3894
+ this.isEqual = options?.isEqual ?? defaultIsEqual;
3895
+ }
3896
+ }
3897
+ /**
3898
+ * Returns current baseline value.
3899
+ */
3900
+ getBaseline() {
3901
+ return this.baseline;
3902
+ }
3903
+ /**
3904
+ * Sets a new baseline value without triggering any dirty state.
3905
+ */
3906
+ setBaseline(value) {
3907
+ this.baseline = value;
3908
+ }
3909
+ /**
3910
+ * Checks whether the given value differs from the baseline.
3911
+ */
3912
+ isDirty(current) {
3913
+ return !this.isEqual(this.baseline, current);
3914
+ }
3915
+ /**
3916
+ * Resets the baseline to the provided current value (or clears dirty status).
3917
+ */
3918
+ reset(current) {
3919
+ this.baseline = current;
3920
+ }
3921
+ };
3922
+ function createDirtyTracker(initialBaseline, options) {
3923
+ return new DirtyTracker(initialBaseline, options);
3924
+ }
3794
3925
  // Annotate the CommonJS export names for ESM import in node:
3795
3926
  0 && (module.exports = {
3796
3927
  ColorHighlighter,
3797
3928
  DataSourceBinder,
3798
3929
  DependencyGraph,
3930
+ DirtyTracker,
3799
3931
  EditorController,
3800
3932
  FontSize,
3801
3933
  FormulaEvaluator,
@@ -3813,9 +3945,11 @@ var SpreadsheetController = class {
3813
3945
  TemplateSuggestions,
3814
3946
  VARIABLE_REGEX,
3815
3947
  colNameToIndex,
3948
+ createDirtyTracker,
3816
3949
  createEditor,
3817
3950
  createMentionExtension,
3818
3951
  createRichTextPreset,
3952
+ defaultIsEqual,
3819
3953
  escapeHtml,
3820
3954
  extractCellDependencies,
3821
3955
  extractInstructionNames,
@@ -3839,6 +3973,7 @@ var SpreadsheetController = class {
3839
3973
  parseFormula,
3840
3974
  parseRange,
3841
3975
  plainTextToTipTapHtml,
3976
+ removeImageNode,
3842
3977
  resolveTemplates,
3843
3978
  resolveTemplatesWithMapping,
3844
3979
  resolveVariables,