@pixerate/editor 0.7.0 → 0.8.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.
package/dist/index.d.cts CHANGED
@@ -6,6 +6,7 @@ export { ExtractedMention, escapeHtml, extractMentionsFromDoc, getEditorMarkdown
6
6
  import { Editor, EditorOptions, Content, Extensions } from '@tiptap/core';
7
7
  export { Content, Editor, EditorOptions, Extensions } from '@tiptap/core';
8
8
  export { ASTNode, BinaryOpNode, CellCoordinate, CellData, CellDep, CellFormat, CellRange, CellRefNode, ColumnBinding, ColumnType, DataSourceBinder, DependencyGraph, EvaluationContext, FormulaEvaluator, FormulaParser, FormulaToken, FormulaTokenType, FunctionCallNode, LiteralNode, PropertyRefNode, RangeNode, RowType, SpreadsheetColumn, SpreadsheetController, SpreadsheetControllerOptions, SpreadsheetDataSource, SpreadsheetDataSourceField, SpreadsheetDocument, SpreadsheetRow, UnaryOpNode, colNameToIndex, extractCellDependencies, indexToColName, parseCellReference, parseFormula, parseRange, tokenizeFormula } from './spreadsheet/index.cjs';
9
+ export { Command, HistoryManager, HistoryManagerOptions, HistoryState, MergedExecutionOptions, SnapshotCommandOptions, createHistoryManager } from './history/index.cjs';
9
10
  export { MarkdownOptions } from 'tiptap-markdown';
10
11
  import '@tiptap/pm/view';
11
12
  import '@tiptap/pm/model';
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { ExtractedMention, escapeHtml, extractMentionsFromDoc, getEditorMarkdown
6
6
  import { Editor, EditorOptions, Content, Extensions } from '@tiptap/core';
7
7
  export { Content, Editor, EditorOptions, Extensions } from '@tiptap/core';
8
8
  export { ASTNode, BinaryOpNode, CellCoordinate, CellData, CellDep, CellFormat, CellRange, CellRefNode, ColumnBinding, ColumnType, DataSourceBinder, DependencyGraph, EvaluationContext, FormulaEvaluator, FormulaParser, FormulaToken, FormulaTokenType, FunctionCallNode, LiteralNode, PropertyRefNode, RangeNode, RowType, SpreadsheetColumn, SpreadsheetController, SpreadsheetControllerOptions, SpreadsheetDataSource, SpreadsheetDataSourceField, SpreadsheetDocument, SpreadsheetRow, UnaryOpNode, colNameToIndex, extractCellDependencies, indexToColName, parseCellReference, parseFormula, parseRange, tokenizeFormula } from './spreadsheet/index.js';
9
+ export { Command, HistoryManager, HistoryManagerOptions, HistoryState, MergedExecutionOptions, SnapshotCommandOptions, createHistoryManager } from './history/index.js';
9
10
  export { MarkdownOptions } from 'tiptap-markdown';
10
11
  import '@tiptap/pm/view';
11
12
  import '@tiptap/pm/model';
package/dist/index.js CHANGED
@@ -2115,6 +2115,7 @@ function createRichTextPreset(options = {}) {
2115
2115
  openOnClick: openLinksOnClick,
2116
2116
  autolink: true,
2117
2117
  defaultProtocol: "https",
2118
+ protocols: ["file", "vscode", "cursor"],
2118
2119
  HTMLAttributes: {
2119
2120
  target: "_blank",
2120
2121
  rel: "noopener noreferrer",
@@ -2335,6 +2336,13 @@ var md2 = new MarkdownIt({
2335
2336
  linkify: true,
2336
2337
  breaks: true
2337
2338
  }).use(taskLists);
2339
+ var defaultValidateLink = md2.validateLink;
2340
+ md2.validateLink = (url) => {
2341
+ if (url.startsWith("file://") || url.startsWith("vscode://") || url.startsWith("cursor://")) {
2342
+ return true;
2343
+ }
2344
+ return defaultValidateLink.call(md2, url);
2345
+ };
2338
2346
  function markdownToTipTapHtml(markdown) {
2339
2347
  if (!markdown) return "<p></p>";
2340
2348
  return md2.render(markdown);
@@ -3834,6 +3842,234 @@ var DirtyTracker = class {
3834
3842
  function createDirtyTracker(initialBaseline, options) {
3835
3843
  return new DirtyTracker(initialBaseline, options);
3836
3844
  }
3845
+
3846
+ // src/history/HistoryManager.ts
3847
+ var HistoryManager = class {
3848
+ undoStack = [];
3849
+ redoStack = [];
3850
+ maxDepth;
3851
+ isApplying = false;
3852
+ batchDepth = 0;
3853
+ currentBatch = null;
3854
+ listeners = /* @__PURE__ */ new Set();
3855
+ onStateChange;
3856
+ cachedState;
3857
+ constructor(options = {}) {
3858
+ this.maxDepth = options.maxDepth ?? 50;
3859
+ this.onStateChange = options.onStateChange;
3860
+ this.cachedState = this.computeState();
3861
+ }
3862
+ computeState() {
3863
+ const canUndo = this.undoStack.length > 0;
3864
+ const canRedo = this.redoStack.length > 0;
3865
+ return {
3866
+ canUndo,
3867
+ canRedo,
3868
+ undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : "",
3869
+ redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : "",
3870
+ undoCount: this.undoStack.length,
3871
+ redoCount: this.redoStack.length
3872
+ };
3873
+ }
3874
+ /**
3875
+ * Returns a snapshot of the current history state.
3876
+ * Cached to maintain referential equality across renders for useSyncExternalStore.
3877
+ */
3878
+ getState() {
3879
+ return this.cachedState;
3880
+ }
3881
+ notify() {
3882
+ this.cachedState = this.computeState();
3883
+ const state = this.cachedState;
3884
+ this.onStateChange?.(state);
3885
+ this.listeners.forEach((listener) => {
3886
+ try {
3887
+ listener(state);
3888
+ } catch (err) {
3889
+ console.error("[HistoryManager] Error in state listener:", err);
3890
+ }
3891
+ });
3892
+ }
3893
+ /**
3894
+ * Executes a command and pushes it onto the undo stack.
3895
+ * Clears the redo stack.
3896
+ */
3897
+ execute(command) {
3898
+ if (this.isApplying) {
3899
+ command.execute();
3900
+ return;
3901
+ }
3902
+ if (this.batchDepth > 0 && this.currentBatch) {
3903
+ command.execute();
3904
+ this.currentBatch.push({
3905
+ ...command,
3906
+ timestamp: command.timestamp ?? Date.now()
3907
+ });
3908
+ return;
3909
+ }
3910
+ command.execute();
3911
+ this.undoStack.push({
3912
+ ...command,
3913
+ timestamp: command.timestamp ?? Date.now()
3914
+ });
3915
+ if (this.undoStack.length > this.maxDepth) {
3916
+ this.undoStack.shift();
3917
+ }
3918
+ this.redoStack = [];
3919
+ this.notify();
3920
+ }
3921
+ /**
3922
+ * Executes a command, coalescing it with the previous command if they share
3923
+ * the same `tag` and occurred within `windowMs` (default: 300ms).
3924
+ * Useful for high-frequency interactions like slider scrubs or node drags.
3925
+ */
3926
+ executeMerged(command, options = {}) {
3927
+ const windowMs = options.windowMs ?? 300;
3928
+ const now = Date.now();
3929
+ if (command.tag && this.undoStack.length > 0 && this.batchDepth === 0 && !this.isApplying) {
3930
+ const top = this.undoStack[this.undoStack.length - 1];
3931
+ const elapsed = top.timestamp ? now - top.timestamp : Infinity;
3932
+ if (top.tag === command.tag && elapsed <= windowMs) {
3933
+ command.execute();
3934
+ const initialUndo = top.undo;
3935
+ this.undoStack[this.undoStack.length - 1] = {
3936
+ name: command.name,
3937
+ tag: command.tag,
3938
+ timestamp: now,
3939
+ execute: command.execute,
3940
+ undo: initialUndo,
3941
+ redo: command.redo ?? command.execute
3942
+ };
3943
+ this.redoStack = [];
3944
+ this.notify();
3945
+ return;
3946
+ }
3947
+ }
3948
+ this.execute({ ...command, timestamp: now });
3949
+ }
3950
+ /**
3951
+ * Batches multiple commands executed within `fn` into a single atomic undo entry.
3952
+ */
3953
+ batch(name, fn) {
3954
+ if (this.isApplying) {
3955
+ fn();
3956
+ return;
3957
+ }
3958
+ if (this.batchDepth > 0) {
3959
+ fn();
3960
+ return;
3961
+ }
3962
+ this.batchDepth++;
3963
+ this.currentBatch = [];
3964
+ try {
3965
+ fn();
3966
+ } finally {
3967
+ this.batchDepth--;
3968
+ const batchCommands = this.currentBatch;
3969
+ this.currentBatch = null;
3970
+ if (batchCommands && batchCommands.length > 0) {
3971
+ const compoundCommand = {
3972
+ name,
3973
+ timestamp: Date.now(),
3974
+ execute: () => {
3975
+ for (let i = 0; i < batchCommands.length; i++) {
3976
+ (batchCommands[i].redo ?? batchCommands[i].execute)();
3977
+ }
3978
+ },
3979
+ undo: () => {
3980
+ for (let i = batchCommands.length - 1; i >= 0; i--) {
3981
+ batchCommands[i].undo();
3982
+ }
3983
+ },
3984
+ redo: () => {
3985
+ for (let i = 0; i < batchCommands.length; i++) {
3986
+ (batchCommands[i].redo ?? batchCommands[i].execute)();
3987
+ }
3988
+ }
3989
+ };
3990
+ this.undoStack.push(compoundCommand);
3991
+ if (this.undoStack.length > this.maxDepth) {
3992
+ this.undoStack.shift();
3993
+ }
3994
+ this.redoStack = [];
3995
+ this.notify();
3996
+ }
3997
+ }
3998
+ }
3999
+ /**
4000
+ * Reverts the most recent command on the undo stack.
4001
+ * Returns true if a command was undone, false otherwise.
4002
+ */
4003
+ undo() {
4004
+ if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
4005
+ return false;
4006
+ }
4007
+ this.isApplying = true;
4008
+ try {
4009
+ const command = this.undoStack.pop();
4010
+ command.undo();
4011
+ this.redoStack.push(command);
4012
+ return true;
4013
+ } finally {
4014
+ this.isApplying = false;
4015
+ this.notify();
4016
+ }
4017
+ }
4018
+ /**
4019
+ * Re-applies the most recent undone command on the redo stack.
4020
+ * Returns true if a command was redone, false otherwise.
4021
+ */
4022
+ redo() {
4023
+ if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
4024
+ return false;
4025
+ }
4026
+ this.isApplying = true;
4027
+ try {
4028
+ const command = this.redoStack.pop();
4029
+ (command.redo ?? command.execute)();
4030
+ this.undoStack.push(command);
4031
+ return true;
4032
+ } finally {
4033
+ this.isApplying = false;
4034
+ this.notify();
4035
+ }
4036
+ }
4037
+ /**
4038
+ * Clears both undo and redo stacks.
4039
+ */
4040
+ clear() {
4041
+ this.undoStack = [];
4042
+ this.redoStack = [];
4043
+ this.notify();
4044
+ }
4045
+ /**
4046
+ * Subscribes to history state changes.
4047
+ * The listener is immediately invoked with the current state.
4048
+ * Returns an unsubscribe function.
4049
+ */
4050
+ subscribe(listener) {
4051
+ this.listeners.add(listener);
4052
+ listener(this.getState());
4053
+ return () => {
4054
+ this.listeners.delete(listener);
4055
+ };
4056
+ }
4057
+ /**
4058
+ * Utility helper to create a Command from before/after state snapshots.
4059
+ */
4060
+ static createSnapshotCommand(options) {
4061
+ return {
4062
+ name: options.name,
4063
+ tag: options.tag,
4064
+ execute: () => options.apply(options.after),
4065
+ undo: () => options.apply(options.before),
4066
+ redo: () => options.apply(options.after)
4067
+ };
4068
+ }
4069
+ };
4070
+ function createHistoryManager(options = {}) {
4071
+ return new HistoryManager(options);
4072
+ }
3837
4073
  export {
3838
4074
  ColorHighlighter,
3839
4075
  DataSourceBinder,
@@ -3844,6 +4080,7 @@ export {
3844
4080
  FormulaEvaluator,
3845
4081
  FormulaParser,
3846
4082
  GradientText,
4083
+ HistoryManager,
3847
4084
  INSTRUCTION_REGEX,
3848
4085
  Image2 as Image,
3849
4086
  LoadingNode,
@@ -3858,6 +4095,7 @@ export {
3858
4095
  colNameToIndex,
3859
4096
  createDirtyTracker,
3860
4097
  createEditor,
4098
+ createHistoryManager,
3861
4099
  createMentionExtension,
3862
4100
  createRichTextPreset,
3863
4101
  defaultIsEqual,