"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var history_infra_exports = {};
__export(history_infra_exports, {
  ActionItem: () => ActionItem,
  closeHistory: () => closeHistory,
  history: () => history,
  pushActionItem: () => pushActionItem,
  redo: () => redo,
  redoDepth: () => redoDepth,
  redoNoScroll: () => redoNoScroll,
  undo: () => undo,
  undoDepth: () => undoDepth,
  undoNoScroll: () => undoNoScroll
});
module.exports = __toCommonJS(history_infra_exports);
var import_rope_sequence = __toESM(require("rope-sequence"));
var import_state = require("./../../tiptap/pm/state.js");
var import_transform = require("./../../tiptap/pm/transform.js");
const max_empty_items = 500;
class ActionItem {
  constructor(callback) {
    this.callback = callback;
  }
}
const _Branch = class _Branch {
  constructor(items, eventCount) {
    this.items = items;
    this.eventCount = eventCount;
  }
  // Pop the latest event off the branch's history and apply it
  // to a document transform.
  popEvent(state, preserveItems) {
    if (this.eventCount == 0) {
      return null;
    }
    let end = this.items.length;
    for (; ; end--) {
      const next = this.items.get(end - 1);
      if (next.selection) {
        --end;
        break;
      }
    }
    let remap, mapFrom;
    if (preserveItems) {
      remap = this.remapping(end, this.items.length);
      mapFrom = remap.maps.length;
    }
    const transform = state.tr;
    let selection, remaining;
    const addAfter = [], addBefore = [];
    this.items.forEach(
      (item, i) => {
        if (!item.step) {
          if (!remap) {
            remap = this.remapping(end, i + 1);
            mapFrom = remap.maps.length;
          }
          mapFrom--;
          addBefore.push(item);
          return;
        }
        if (remap) {
          addBefore.push(new Item(item.map));
          let step = item.step.map(remap.slice(mapFrom)), map;
          if (step && transform.maybeStep(step).doc) {
            map = transform.mapping.maps[transform.mapping.maps.length - 1];
            addAfter.push(new Item(map, void 0, void 0, addAfter.length + addBefore.length));
          }
          mapFrom--;
          if (map) {
            remap.appendMap(map, mapFrom);
          }
        } else {
          transform.maybeStep(item.step);
        }
        if (item.selection) {
          selection = remap ? item.selection.map(remap.slice(mapFrom)) : item.selection;
          remaining = new _Branch(
            this.items.slice(0, end).append(addBefore.reverse().concat(addAfter)),
            this.eventCount - 1
          );
          return false;
        }
      },
      this.items.length,
      0
    );
    return { remaining, transform, selection };
  }
  // Create a new branch with the given transform added.
  addTransform(transform, selection, histOptions, preserveItems) {
    let newItems = [], eventCount = this.eventCount;
    let oldItems = this.items, lastItem = !preserveItems && oldItems.length ? oldItems.get(oldItems.length - 1) : null;
    for (let i = 0; i < transform.steps.length; i++) {
      const step = transform.steps[i].invert(transform.docs[i]);
      let item = new Item(transform.mapping.maps[i], step, selection), merged;
      if (merged = lastItem && lastItem.merge(item)) {
        item = merged;
        if (i) {
          newItems.pop();
        } else {
          oldItems = oldItems.slice(0, oldItems.length - 1);
        }
      }
      newItems.push(item);
      if (selection) {
        eventCount++;
        selection = void 0;
      }
      if (!preserveItems) {
        lastItem = item;
      }
    }
    const overflow = eventCount - histOptions.depth;
    if (overflow > DEPTH_OVERFLOW) {
      oldItems = cutOffEvents(oldItems, overflow);
      eventCount -= overflow;
    }
    return new _Branch(oldItems.append(newItems), eventCount);
  }
  remapping(from, to) {
    const maps = new import_transform.Mapping();
    this.items.forEach(
      (item, i) => {
        const mirrorPos = item.mirrorOffset != null && i - item.mirrorOffset >= from ? maps.maps.length - item.mirrorOffset : void 0;
        maps.appendMap(item.map, mirrorPos);
      },
      from,
      to
    );
    return maps;
  }
  addMaps(array) {
    if (this.eventCount == 0) {
      return this;
    }
    return new _Branch(this.items.append(array.map((map) => new Item(map))), this.eventCount);
  }
  // When the collab module receives remote changes, the history has
  // to know about those, so that it can adjust the steps that were
  // rebased on top of the remote changes, and include the position
  // maps for the remote changes in its array of items.
  rebased(rebasedTransform, rebasedCount) {
    if (!this.eventCount) {
      return this;
    }
    const rebasedItems = [], start = Math.max(0, this.items.length - rebasedCount);
    const mapping = rebasedTransform.mapping;
    let newUntil = rebasedTransform.steps.length;
    let eventCount = this.eventCount;
    this.items.forEach((item) => {
      if (item.selection) {
        eventCount--;
      }
    }, start);
    let iRebased = rebasedCount;
    this.items.forEach((item) => {
      const pos = mapping.getMirror(--iRebased);
      if (pos == null) {
        return;
      }
      newUntil = Math.min(newUntil, pos);
      const map = mapping.maps[pos];
      if (item.step) {
        const step = rebasedTransform.steps[pos].invert(rebasedTransform.docs[pos]);
        const selection = item.selection && item.selection.map(mapping.slice(iRebased + 1, pos));
        if (selection) {
          eventCount++;
        }
        rebasedItems.push(new Item(map, step, selection));
      } else {
        rebasedItems.push(new Item(map));
      }
    }, start);
    const newMaps = [];
    for (let i = rebasedCount; i < newUntil; i++) {
      newMaps.push(new Item(mapping.maps[i]));
    }
    const items = this.items.slice(0, start).append(newMaps).append(rebasedItems);
    let branch = new _Branch(items, eventCount);
    if (branch.emptyItemCount() > max_empty_items) {
      branch = branch.compress(this.items.length - rebasedItems.length);
    }
    return branch;
  }
  emptyItemCount() {
    let count = 0;
    this.items.forEach((item) => {
      if (!item.step) {
        count++;
      }
    });
    return count;
  }
  // Compressing a branch means rewriting it to push the air (map-only
  // items) out. During collaboration, these naturally accumulate
  // because each remote change adds one. The `upto` argument is used
  // to ensure that only the items below a given level are compressed,
  // because `rebased` relies on a clean, untouched set of items in
  // order to associate old items with rebased steps.
  compress(upto = this.items.length) {
    let remap = this.remapping(0, upto), mapFrom = remap.maps.length;
    let items = [], events = 0;
    this.items.forEach(
      (item, i) => {
        if (i >= upto) {
          items.push(item);
          if (item.selection) {
            events++;
          }
        } else if (item.step) {
          const step = item.step.map(remap.slice(mapFrom)), map = step && step.getMap();
          mapFrom--;
          if (map) {
            remap.appendMap(map, mapFrom);
          }
          if (step) {
            const selection = item.selection && item.selection.map(remap.slice(mapFrom));
            if (selection) {
              events++;
            }
            let newItem = new Item(map.invert(), step, selection), merged, last = items.length - 1;
            if (merged = items.length && items[last].merge(newItem)) {
              items[last] = merged;
            } else {
              items.push(newItem);
            }
          }
        } else if (item.map) {
          mapFrom--;
        }
      },
      this.items.length,
      0
    );
    return new _Branch(import_rope_sequence.default.from(items.reverse()), events);
  }
};
_Branch.empty = new _Branch(import_rope_sequence.default.empty, 0);
let Branch = _Branch;
function cutOffEvents(items, n) {
  let cutPoint;
  items.forEach((item, i) => {
    if (item.selection && n-- == 0) {
      cutPoint = i;
      return false;
    }
  });
  return items.slice(cutPoint);
}
class Item {
  constructor(map, step, selection, mirrorOffset) {
    this.map = map;
    this.step = step;
    this.selection = selection;
    this.mirrorOffset = mirrorOffset;
  }
  merge(other) {
    if (this.step && other.step && !other.selection) {
      const step = other.step.merge(this.step);
      if (step) {
        return new Item(step.getMap().invert(), step, this.selection);
      }
    }
  }
}
class HistoryState {
  constructor(done, undone, prevRanges, prevTime, prevComposition, actionItems = []) {
    this.done = done;
    this.undone = undone;
    this.prevRanges = prevRanges;
    this.prevTime = prevTime;
    this.prevComposition = prevComposition;
    this.actionItems = actionItems;
  }
  // Add an action item at the current undo position
  pushActionItem(item) {
    return new HistoryState(
      this.done,
      this.undone,
      this.prevRanges,
      this.prevTime,
      this.prevComposition,
      [...this.actionItems, { eventCount: this.done.eventCount, item }]
    );
  }
  // Check if there's an action item at the current undo position
  getActionItemAtTop() {
    const topEventCount = this.done.eventCount;
    const actionItem = this.actionItems.find((a) => a.eventCount === topEventCount);
    return actionItem?.item ?? null;
  }
  // Remove action items at or above a given event count
  removeActionItemsAtOrAbove(eventCount) {
    const filtered = this.actionItems.filter((a) => a.eventCount < eventCount);
    if (filtered.length === this.actionItems.length) {
      return this;
    }
    return new HistoryState(
      this.done,
      this.undone,
      this.prevRanges,
      this.prevTime,
      this.prevComposition,
      filtered
    );
  }
}
const DEPTH_OVERFLOW = 20;
function applyTransaction(history2, state, tr, options) {
  let historyTr = tr.getMeta(historyKey);
  let rebased;
  if (historyTr) {
    return historyTr.historyState;
  }
  if (tr.getMeta(closeHistoryKey)) {
    history2 = new HistoryState(history2.done, history2.undone, null, 0, -1, history2.actionItems);
  }
  const appended = tr.getMeta("appendedTransaction");
  if (tr.steps.length == 0) {
    return history2;
  }
  if (appended && appended.getMeta(historyKey)) {
    if (appended.getMeta(historyKey).redo) {
      return new HistoryState(
        history2.done.addTransform(tr, void 0, options, mustPreserveItems(state)),
        history2.undone,
        rangesFor(tr.mapping.maps),
        history2.prevTime,
        history2.prevComposition,
        history2.actionItems
      );
    } else {
      return new HistoryState(
        history2.done,
        history2.undone.addTransform(tr, void 0, options, mustPreserveItems(state)),
        null,
        history2.prevTime,
        history2.prevComposition,
        history2.actionItems
      );
    }
  } else if (tr.getMeta("addToHistory") !== false && !(appended && appended.getMeta("addToHistory") === false)) {
    const composition = tr.getMeta("composition");
    const newGroup = history2.prevTime == 0 || !appended && history2.prevComposition != composition && (history2.prevTime < (tr.time || 0) - options.newGroupDelay || !isAdjacentTo(tr, history2.prevRanges));
    const prevRanges = appended ? mapRanges(history2.prevRanges, tr.mapping) : rangesFor(tr.mapping.maps);
    if (newGroup) {
      try {
        options.onHistoryChange?.();
      } catch (error) {
        console.error(error);
      }
    }
    return new HistoryState(
      history2.done.addTransform(
        tr,
        newGroup ? state.selection.getBookmark() : void 0,
        options,
        mustPreserveItems(state)
      ),
      Branch.empty,
      prevRanges,
      tr.time,
      composition == null ? history2.prevComposition : composition,
      history2.actionItems
    );
  } else if (rebased = tr.getMeta("rebased")) {
    return new HistoryState(
      history2.done.rebased(tr, rebased),
      history2.undone.rebased(tr, rebased),
      mapRanges(history2.prevRanges, tr.mapping),
      history2.prevTime,
      history2.prevComposition,
      history2.actionItems
    );
  } else {
    return new HistoryState(
      history2.done.addMaps(tr.mapping.maps),
      history2.undone.addMaps(tr.mapping.maps),
      mapRanges(history2.prevRanges, tr.mapping),
      history2.prevTime,
      history2.prevComposition,
      history2.actionItems
    );
  }
}
function isAdjacentTo(transform, prevRanges) {
  if (!prevRanges) {
    return false;
  }
  if (!transform.docChanged) {
    return true;
  }
  let adjacent = false;
  transform.mapping.maps[0].forEach((start, end) => {
    for (let i = 0; i < prevRanges.length; i += 2) {
      if (start <= prevRanges[i + 1] && end >= prevRanges[i]) {
        adjacent = true;
      }
    }
  });
  return adjacent;
}
function rangesFor(maps) {
  const result = [];
  for (let i = maps.length - 1; i >= 0 && result.length == 0; i--) {
    maps[i].forEach((_from, _to, from, to) => result.push(from, to));
  }
  return result;
}
function mapRanges(ranges, mapping) {
  if (!ranges) {
    return null;
  }
  const result = [];
  for (let i = 0; i < ranges.length; i += 2) {
    const from = mapping.map(ranges[i], 1), to = mapping.map(ranges[i + 1], -1);
    if (from <= to) {
      result.push(from, to);
    }
  }
  return result;
}
function histTransaction(history2, state, redo2) {
  const preserveItems = mustPreserveItems(state);
  const histOptions = historyKey.get(state).spec.config;
  const pop = (redo2 ? history2.undone : history2.done).popEvent(state, preserveItems);
  if (!pop) {
    return null;
  }
  const selection = pop.selection.resolve(pop.transform.doc);
  const added = (redo2 ? history2.done : history2.undone).addTransform(
    pop.transform,
    state.selection.getBookmark(),
    histOptions,
    preserveItems
  );
  const updatedActionItems = redo2 ? history2.actionItems : history2.actionItems.filter((a) => a.eventCount < history2.done.eventCount);
  const newHist = new HistoryState(
    redo2 ? added : pop.remaining,
    redo2 ? pop.remaining : added,
    null,
    0,
    -1,
    updatedActionItems
  );
  return pop.transform.setSelection(selection).setMeta(historyKey, { redo: redo2, historyState: newHist });
}
let cachedPreserveItems = false, cachedPreserveItemsPlugins = null;
function mustPreserveItems(state) {
  const plugins = state.plugins;
  if (cachedPreserveItemsPlugins != plugins) {
    cachedPreserveItems = false;
    cachedPreserveItemsPlugins = plugins;
    for (let i = 0; i < plugins.length; i++) {
      if (plugins[i].spec.historyPreserveItems) {
        cachedPreserveItems = true;
        break;
      }
    }
  }
  return cachedPreserveItems;
}
function closeHistory(tr) {
  return tr.setMeta(closeHistoryKey, true);
}
const historyKey = new import_state.PluginKey("history");
const closeHistoryKey = new import_state.PluginKey("closeHistory");
function history(config = {}) {
  const fullConfig = {
    depth: config.depth || 100,
    newGroupDelay: config.newGroupDelay || 500,
    onHistoryChange: config.onHistoryChange
  };
  return new import_state.Plugin({
    key: historyKey,
    state: {
      init() {
        return new HistoryState(Branch.empty, Branch.empty, null, 0, -1, []);
      },
      apply(tr, hist, state) {
        if (tr.getMeta("skip")) {
          return hist;
        }
        return applyTransaction(hist, state, tr, fullConfig);
      }
    },
    config: fullConfig,
    props: {
      handleDOMEvents: {
        beforeinput(view, e) {
          const inputType = e.inputType;
          const command = inputType == "historyUndo" ? undo : inputType == "historyRedo" ? redo : null;
          if (!command || !view.editable) {
            return false;
          }
          e.preventDefault();
          return command(view.state, view.dispatch);
        }
      }
    }
  });
}
function buildCommand(redo2, scroll) {
  return (state, dispatch) => {
    const hist = historyKey.getState(state);
    if (!hist) {
      return false;
    }
    if (!redo2) {
      const actionItem = hist.getActionItemAtTop();
      if (actionItem) {
        try {
          actionItem.callback();
        } catch (error) {
          console.error("ActionItem callback error:", error);
        }
        if (dispatch) {
          const newHist = hist.removeActionItemsAtOrAbove(hist.done.eventCount);
          const tr = state.tr.setMeta(historyKey, { historyState: newHist });
          dispatch(tr);
        }
        return true;
      }
    }
    if ((redo2 ? hist.undone : hist.done).eventCount == 0) {
      return false;
    }
    if (dispatch) {
      const tr = histTransaction(hist, state, redo2);
      if (tr) {
        dispatch(scroll ? tr.scrollIntoView() : tr);
      }
    }
    return true;
  };
}
const undo = buildCommand(false, true);
const redo = buildCommand(true, true);
const undoNoScroll = buildCommand(false, false);
const redoNoScroll = buildCommand(true, false);
function undoDepth(state) {
  const hist = historyKey.getState(state);
  return hist ? hist.done.eventCount : 0;
}
function redoDepth(state) {
  const hist = historyKey.getState(state);
  return hist ? hist.undone.eventCount : 0;
}
function pushActionItem(state, dispatch, callback) {
  const hist = historyKey.getState(state);
  if (!hist) {
    return false;
  }
  const actionItem = new ActionItem(callback);
  const newHist = hist.pushActionItem(actionItem);
  const tr = state.tr.setMeta(historyKey, { historyState: newHist });
  dispatch(tr);
  return true;
}
//# sourceMappingURL=history-infra.js.map
