@tiptap/extensions 3.23.6 → 3.24.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.js CHANGED
@@ -202,9 +202,20 @@ var Gapcursor = Extension4.create({
202
202
  }
203
203
  });
204
204
 
205
+ // src/placeholder/constants.ts
206
+ import { PluginKey as PluginKey3 } from "@tiptap/pm/state";
207
+ var DEFAULT_DATA_ATTRIBUTE = "placeholder";
208
+ var PLUGIN_KEY = new PluginKey3("tiptap__placeholder");
209
+ var VIEWPORT_OVERSCAN_PX = 200;
210
+
205
211
  // src/placeholder/placeholder.ts
206
- import { Extension as Extension5, isNodeEmpty } from "@tiptap/core";
207
- import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
212
+ import { Extension as Extension5 } from "@tiptap/core";
213
+
214
+ // src/placeholder/plugins/PlaceholderPlugin.ts
215
+ import { Plugin as Plugin3 } from "@tiptap/pm/state";
216
+
217
+ // src/placeholder/utils/buildPlaceholderDecorations.ts
218
+ import { isNodeEmpty } from "@tiptap/core";
208
219
  import { DecorationSet as DecorationSet2 } from "@tiptap/pm/view";
209
220
 
210
221
  // src/placeholder/utils/createPlaceholderDecoration.ts
@@ -235,6 +246,81 @@ function createPlaceholderDecoration(options) {
235
246
  });
236
247
  }
237
248
 
249
+ // src/placeholder/utils/buildPlaceholderDecorations.ts
250
+ function buildPlaceholderDecorations({
251
+ editor,
252
+ options,
253
+ dataAttribute,
254
+ doc,
255
+ selection
256
+ }) {
257
+ var _a, _b;
258
+ const active = editor.isEditable || !options.showOnlyWhenEditable;
259
+ if (!active) {
260
+ return null;
261
+ }
262
+ const { anchor } = selection;
263
+ const decorations = [];
264
+ const isEmptyDoc = editor.isEmpty;
265
+ const classes = {
266
+ emptyEditor: options.emptyEditorClass,
267
+ emptyNode: options.emptyNodeClass
268
+ };
269
+ const useResolvedPath = options.showOnlyCurrent && !options.includeChildren;
270
+ if (useResolvedPath) {
271
+ const resolved = doc.resolve(anchor);
272
+ const node = resolved.depth > 0 ? resolved.node(1) : resolved.nodeAfter;
273
+ const nodeStart = resolved.depth > 0 ? resolved.before(1) : anchor;
274
+ if (node && node.type.isTextblock && isNodeEmpty(node)) {
275
+ const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize;
276
+ decorations.push(
277
+ createPlaceholderDecoration({
278
+ editor,
279
+ isEmptyDoc,
280
+ dataAttribute,
281
+ hasAnchor,
282
+ placeholder: options.placeholder,
283
+ classes,
284
+ node,
285
+ pos: nodeStart
286
+ })
287
+ );
288
+ }
289
+ } else {
290
+ const pluginState = PLUGIN_KEY.getState(editor.state);
291
+ const from = (_a = pluginState == null ? void 0 : pluginState.topPos) != null ? _a : 0;
292
+ const to = (_b = pluginState == null ? void 0 : pluginState.bottomPos) != null ? _b : doc.content.size;
293
+ doc.nodesBetween(from, to, (node, pos) => {
294
+ const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
295
+ const isEmpty = !node.isLeaf && isNodeEmpty(node);
296
+ if (!node.type.isTextblock) {
297
+ return options.includeChildren;
298
+ }
299
+ if ((hasAnchor || !options.showOnlyCurrent) && isEmpty) {
300
+ decorations.push(
301
+ createPlaceholderDecoration({
302
+ editor,
303
+ isEmptyDoc,
304
+ dataAttribute,
305
+ hasAnchor,
306
+ placeholder: options.placeholder,
307
+ classes,
308
+ node,
309
+ pos
310
+ })
311
+ );
312
+ }
313
+ return options.includeChildren;
314
+ });
315
+ }
316
+ return DecorationSet2.create(doc, decorations);
317
+ }
318
+
319
+ // src/placeholder/utils/preparePlaceholderAttribute.ts
320
+ function preparePlaceholderAttribute(attr) {
321
+ return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
322
+ }
323
+
238
324
  // src/placeholder/utils/findScrollParent.ts
239
325
  function isScrollable(el) {
240
326
  const style = getComputedStyle(el);
@@ -275,8 +361,8 @@ function getViewportBoundaryPositions({
275
361
  }) {
276
362
  const editorRect = view.dom.getBoundingClientRect();
277
363
  const containerRect = scrollContainer ? getContainerRect(scrollContainer) : { top: 0, bottom: window.innerHeight };
278
- const visibleTop = Math.max(editorRect.top, containerRect.top);
279
- const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom);
364
+ const visibleTop = Math.max(editorRect.top, containerRect.top) - VIEWPORT_OVERSCAN_PX;
365
+ const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom) + VIEWPORT_OVERSCAN_PX;
280
366
  if (visibleTop >= visibleBottom) {
281
367
  return { top: 0, bottom: doc.content.size };
282
368
  }
@@ -290,33 +376,97 @@ function getViewportBoundaryPositions({
290
376
  };
291
377
  }
292
378
 
293
- // src/placeholder/utils/throttle.ts
294
- function throttle(fn, delay) {
295
- let timer = null;
296
- const call = ((...args) => {
297
- if (timer) {
379
+ // src/placeholder/utils/viewportTracking.ts
380
+ var viewportPluginState = {
381
+ /**
382
+ * Initialises the viewport state with no known positions.
383
+ * @returns The initial viewport state.
384
+ */
385
+ init() {
386
+ return { topPos: null, bottomPos: null };
387
+ },
388
+ /**
389
+ * Updates the viewport state from incoming transactions.
390
+ * @param tr - The transaction being applied.
391
+ * @param prev - The previous viewport state.
392
+ * @returns The next viewport state.
393
+ */
394
+ apply(tr, prev) {
395
+ const meta = tr.getMeta(PLUGIN_KEY);
396
+ if (meta == null ? void 0 : meta.positions) {
397
+ return { topPos: meta.positions.top, bottomPos: meta.positions.bottom };
398
+ }
399
+ if (!tr.docChanged) {
400
+ return prev;
401
+ }
402
+ return {
403
+ topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null,
404
+ bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null
405
+ };
406
+ }
407
+ };
408
+ function createViewportPluginView(view) {
409
+ const scrollContainer = findScrollParent(view.dom);
410
+ const computeAndDispatch = () => {
411
+ const positions = getViewportBoundaryPositions({
412
+ view,
413
+ doc: view.state.doc,
414
+ scrollContainer
415
+ });
416
+ const prev = PLUGIN_KEY.getState(view.state);
417
+ if ((prev == null ? void 0 : prev.topPos) === positions.top && (prev == null ? void 0 : prev.bottomPos) === positions.bottom) {
298
418
  return;
299
419
  }
300
- fn(...args);
301
- timer = setTimeout(() => {
302
- timer = null;
303
- }, delay);
304
- });
305
- const cancel = () => {
306
- if (timer) {
307
- clearTimeout(timer);
308
- timer = null;
420
+ const tr = view.state.tr.setMeta(PLUGIN_KEY, { positions });
421
+ view.dispatch(tr);
422
+ };
423
+ let frame = null;
424
+ let lastCompute = 0;
425
+ const MIN_SCROLL_INTERVAL = 150;
426
+ const scheduleFrame = () => {
427
+ if (frame !== null) return;
428
+ frame = requestAnimationFrame(() => {
429
+ frame = null;
430
+ const now = performance.now();
431
+ if (now - lastCompute >= MIN_SCROLL_INTERVAL) {
432
+ lastCompute = now;
433
+ computeAndDispatch();
434
+ } else {
435
+ scheduleFrame();
436
+ }
437
+ });
438
+ };
439
+ scrollContainer.addEventListener("scroll", scheduleFrame, { passive: true });
440
+ computeAndDispatch();
441
+ return {
442
+ update(_view, prevState) {
443
+ if (view.state.doc.content.size !== prevState.doc.content.size) {
444
+ scheduleFrame();
445
+ }
446
+ },
447
+ destroy: () => {
448
+ if (frame !== null) {
449
+ cancelAnimationFrame(frame);
450
+ }
451
+ scrollContainer.removeEventListener("scroll", scheduleFrame);
309
452
  }
310
453
  };
311
- return { call, cancel };
312
454
  }
313
455
 
314
- // src/placeholder/placeholder.ts
315
- var DEFAULT_DATA_ATTRIBUTE = "placeholder";
316
- function preparePlaceholderAttribute(attr) {
317
- return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
456
+ // src/placeholder/plugins/PlaceholderPlugin.ts
457
+ function createPlaceholderPlugin({ editor, options }) {
458
+ const dataAttribute = options.dataAttribute ? `data-${preparePlaceholderAttribute(options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
459
+ return new Plugin3({
460
+ key: PLUGIN_KEY,
461
+ state: viewportPluginState,
462
+ view: createViewportPluginView,
463
+ props: {
464
+ decorations: ({ doc, selection }) => buildPlaceholderDecorations({ editor, options, dataAttribute, doc, selection })
465
+ }
466
+ });
318
467
  }
319
- var PLUGIN_KEY = new PluginKey3("tiptap__placeholder");
468
+
469
+ // src/placeholder/placeholder.ts
320
470
  var Placeholder = Extension5.create({
321
471
  name: "placeholder",
322
472
  addOptions() {
@@ -331,132 +481,7 @@ var Placeholder = Extension5.create({
331
481
  };
332
482
  },
333
483
  addProseMirrorPlugins() {
334
- const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
335
- return [
336
- new Plugin3({
337
- state: {
338
- init() {
339
- return {
340
- // null means "no viewport info yet" — decoration callback falls
341
- // back to full document scan until the scroll handler fires.
342
- topPos: null,
343
- bottomPos: null
344
- };
345
- },
346
- apply(tr, prev) {
347
- const meta = tr.getMeta(PLUGIN_KEY);
348
- if (meta == null ? void 0 : meta.positions) {
349
- return {
350
- topPos: meta.positions.top,
351
- bottomPos: meta.positions.bottom
352
- };
353
- }
354
- if (!tr.docChanged) {
355
- return prev;
356
- }
357
- return {
358
- topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null,
359
- bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null
360
- };
361
- }
362
- },
363
- key: PLUGIN_KEY,
364
- view(view) {
365
- const scrollContainer = findScrollParent(view.dom);
366
- const computeAndDispatch = () => {
367
- const positions = getViewportBoundaryPositions({
368
- view,
369
- doc: view.state.doc,
370
- scrollContainer
371
- });
372
- const prev = PLUGIN_KEY.getState(view.state);
373
- if (prev.topPos === positions.top && prev.bottomPos === positions.bottom) {
374
- return;
375
- }
376
- const tr = view.state.tr.setMeta(PLUGIN_KEY, { positions }).setMeta("tiptap__viewportUpdate", true);
377
- view.dispatch(tr);
378
- };
379
- const { call: throttledUpdate, cancel: cancelThrottle } = throttle(computeAndDispatch, 250);
380
- const scrollParent = scrollContainer;
381
- scrollParent.addEventListener("scroll", throttledUpdate, { passive: true });
382
- computeAndDispatch();
383
- return {
384
- update(_, prevState) {
385
- if (view.state.doc.content.size !== prevState.doc.content.size) {
386
- computeAndDispatch();
387
- }
388
- },
389
- destroy: () => {
390
- cancelThrottle();
391
- scrollParent.removeEventListener("scroll", throttledUpdate);
392
- }
393
- };
394
- },
395
- props: {
396
- decorations: ({ doc, selection }) => {
397
- var _a, _b;
398
- const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
399
- if (!active) {
400
- return null;
401
- }
402
- const { anchor } = selection;
403
- const decorations = [];
404
- const isEmptyDoc = this.editor.isEmpty;
405
- const useResolvedPath = this.options.showOnlyCurrent && !this.options.includeChildren;
406
- if (useResolvedPath) {
407
- const resolved = doc.resolve(anchor);
408
- if (resolved.depth > 0) {
409
- const node = resolved.node(1);
410
- const nodeStart = resolved.before(1);
411
- if (node.type.isTextblock && isNodeEmpty(node)) {
412
- const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize;
413
- const decoration = createPlaceholderDecoration({
414
- node,
415
- dataAttribute,
416
- hasAnchor,
417
- placeholder: this.options.placeholder,
418
- classes: {
419
- emptyEditor: this.options.emptyEditorClass,
420
- emptyNode: this.options.emptyNodeClass
421
- },
422
- editor: this.editor,
423
- isEmptyDoc,
424
- pos: resolved.before(1)
425
- });
426
- decorations.push(decoration);
427
- }
428
- }
429
- } else {
430
- const pluginState = PLUGIN_KEY.getState(this.editor.state);
431
- const from = (_a = pluginState.topPos) != null ? _a : 0;
432
- const to = (_b = pluginState.bottomPos) != null ? _b : doc.content.size;
433
- doc.nodesBetween(from, to, (node, pos) => {
434
- const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
435
- const isEmpty = !node.isLeaf && isNodeEmpty(node);
436
- if (!node.type.isTextblock) {
437
- return this.options.includeChildren;
438
- }
439
- if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
440
- const decoration = createPlaceholderDecoration({
441
- classes: { emptyEditor: this.options.emptyEditorClass, emptyNode: this.options.emptyNodeClass },
442
- editor: this.editor,
443
- isEmptyDoc,
444
- dataAttribute,
445
- hasAnchor,
446
- placeholder: this.options.placeholder,
447
- node,
448
- pos
449
- });
450
- decorations.push(decoration);
451
- }
452
- return this.options.includeChildren;
453
- });
454
- }
455
- return DecorationSet2.create(doc, decorations);
456
- }
457
- }
458
- })
459
- ];
484
+ return [createPlaceholderPlugin({ editor: this.editor, options: this.options })];
460
485
  }
461
486
  });
462
487
 
@@ -497,7 +522,10 @@ var Selection = Extension6.create({
497
522
  import { Extension as Extension7 } from "@tiptap/core";
498
523
  import { Plugin as Plugin5, PluginKey as PluginKey5 } from "@tiptap/pm/state";
499
524
  var skipTrailingNodeMeta = "skipTrailingNode";
500
- function nodeEqualsType({ types, node }) {
525
+ function nodeEqualsType({
526
+ types,
527
+ node
528
+ }) {
501
529
  return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
502
530
  }
503
531
  var TrailingNode = Extension7.create({
@@ -587,6 +615,7 @@ var UndoRedo = Extension8.create({
587
615
  });
588
616
  export {
589
617
  CharacterCount,
618
+ DEFAULT_DATA_ATTRIBUTE,
590
619
  Dropcursor,
591
620
  Focus,
592
621
  Gapcursor,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/placeholder.ts","../src/placeholder/utils/createPlaceholderDecoration.ts","../src/placeholder/utils/findScrollParent.ts","../src/placeholder/utils/getViewportBoundaryPositions.ts","../src/placeholder/utils/throttle.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * Sets whether the content will be automatically trimmed when programatically setting content over the limit.\n * If set to false, the user will be able to trim the text manually.\n * @default true\n * @example false\n */\n autoTrim?: boolean\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n autoTrim: true,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n const autoTrim = this.options.autoTrim\n\n if (limit === null || limit === undefined || limit === 0 || autoTrim === false) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import { Extension, isNodeEmpty } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport type { Decoration } from '@tiptap/pm/view'\nimport { DecorationSet } from '@tiptap/pm/view'\n\nimport type { PlaceholderOptions } from './types.js'\nimport { createPlaceholderDecoration } from './utils/createPlaceholderDecoration.js'\nimport { findScrollParent } from './utils/findScrollParent.js'\nimport { getViewportBoundaryPositions } from './utils/getViewportBoundaryPositions.js'\nimport { throttle } from './utils/throttle.js'\n\n/**\n * The default data attribute label\n */\nconst DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n\nexport const PLUGIN_KEY = new PluginKey('tiptap__placeholder')\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n const dataAttribute = this.options.dataAttribute\n ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return [\n new Plugin({\n state: {\n init() {\n return {\n // null means \"no viewport info yet\" — decoration callback falls\n // back to full document scan until the scroll handler fires.\n topPos: null as number | null,\n bottomPos: null as number | null,\n }\n },\n apply(tr, prev) {\n const meta = tr.getMeta(PLUGIN_KEY) as { positions?: { top: number; bottom: number } } | undefined\n\n if (meta?.positions) {\n return {\n topPos: meta.positions.top,\n bottomPos: meta.positions.bottom,\n }\n }\n\n if (!tr.docChanged) {\n return prev\n }\n\n // Preserve last known viewport positions across transactions.\n // Without this, every keystroke resets back to a full document\n // scan, defeating the viewport optimisation.\n // Only map when we have actual positions — null means \"no viewport\n // info yet\" and should stay null to fall back to full doc scan.\n return {\n topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null,\n bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null,\n }\n },\n },\n key: PLUGIN_KEY,\n view(view) {\n const scrollContainer = findScrollParent(view.dom)\n\n const computeAndDispatch = () => {\n const positions = getViewportBoundaryPositions({\n view,\n doc: view.state.doc,\n scrollContainer,\n })\n\n const prev = PLUGIN_KEY.getState(view.state)\n if (prev.topPos === positions.top && prev.bottomPos === positions.bottom) {\n return\n }\n\n const tr = view.state.tr\n .setMeta(PLUGIN_KEY, { positions })\n // Flag this transaction so the update() method can detect\n // it and avoid re-entrant computation.\n .setMeta('tiptap__viewportUpdate', true)\n view.dispatch(tr)\n }\n\n const { call: throttledUpdate, cancel: cancelThrottle } = throttle(computeAndDispatch, 250)\n const scrollParent = scrollContainer\n\n scrollParent.addEventListener('scroll', throttledUpdate, { passive: true })\n\n // Fire once to populate initial viewport (bypass throttle)\n computeAndDispatch()\n\n return {\n update(_, prevState) {\n // Skip re-entry: the dispatch inside computeAndDispatch would\n // trigger this update again, but the doc didn't change so the\n // size guard catches that. The meta flag is an extra safeguard.\n if (view.state.doc.content.size !== prevState.doc.content.size) {\n computeAndDispatch()\n }\n },\n destroy: () => {\n cancelThrottle()\n scrollParent.removeEventListener('scroll', throttledUpdate)\n },\n }\n },\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n\n if (!active) {\n return null\n }\n\n const { anchor } = selection\n const decorations: Decoration[] = []\n const isEmptyDoc = this.editor.isEmpty\n\n const useResolvedPath = this.options.showOnlyCurrent && !this.options.includeChildren\n\n if (useResolvedPath) {\n const resolved = doc.resolve(anchor)\n\n if (resolved.depth > 0) {\n const node = resolved.node(1)\n const nodeStart = resolved.before(1)\n\n if (node.type.isTextblock && isNodeEmpty(node)) {\n const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize\n const decoration = createPlaceholderDecoration({\n node,\n dataAttribute,\n hasAnchor,\n placeholder: this.options.placeholder,\n classes: {\n emptyEditor: this.options.emptyEditorClass,\n emptyNode: this.options.emptyNodeClass,\n },\n editor: this.editor,\n isEmptyDoc,\n pos: resolved.before(1),\n })\n\n decorations.push(decoration)\n }\n }\n } else {\n const pluginState = PLUGIN_KEY.getState(this.editor.state)\n const from = pluginState.topPos ?? 0\n const to = pluginState.bottomPos ?? doc.content.size\n\n doc.nodesBetween(from, to, (node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if (!node.type.isTextblock) {\n return this.options.includeChildren\n }\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const decoration = createPlaceholderDecoration({\n classes: { emptyEditor: this.options.emptyEditorClass, emptyNode: this.options.emptyNodeClass },\n editor: this.editor,\n isEmptyDoc,\n dataAttribute,\n hasAnchor,\n placeholder: this.options.placeholder,\n node,\n pos,\n })\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n }\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport { Decoration } from '@tiptap/pm/view'\n\nimport type { PlaceholderOptions } from '../types.js'\n\n/**\n * Creates a ProseMirror node decoration that applies a placeholder\n * CSS class and data attribute to an empty node.\n * @param options.editor - The editor instance\n * @param options.pos - The position of the node in the document\n * @param options.node - The ProseMirror node\n * @param options.isEmptyDoc - Whether the entire document is empty\n * @param options.hasAnchor - Whether the selection anchor is within the node\n * @param options.dataAttribute - The data attribute name (e.g. `data-placeholder`)\n * @param options.classes - CSS classes for empty nodes and the empty editor\n * @param options.placeholder - The placeholder text or a function that returns it\n * @returns A ProseMirror node decoration with placeholder classes and data attribute\n */\nexport function createPlaceholderDecoration(options: {\n editor: Editor\n pos: number\n node: Node\n isEmptyDoc: boolean\n hasAnchor: boolean\n dataAttribute: string\n classes: {\n emptyEditor: PlaceholderOptions['emptyEditorClass']\n emptyNode: PlaceholderOptions['emptyNodeClass']\n }\n placeholder: PlaceholderOptions['placeholder']\n}) {\n const {\n editor,\n placeholder,\n dataAttribute,\n pos,\n node,\n isEmptyDoc,\n hasAnchor,\n classes: { emptyNode, emptyEditor },\n } = options\n const classes = [emptyNode]\n\n if (isEmptyDoc) {\n classes.push(emptyEditor)\n }\n\n return Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof placeholder === 'function'\n ? placeholder({\n editor,\n node,\n pos,\n hasAnchor,\n })\n : placeholder,\n })\n}\n","/**\n * Checks if an element is scrollable by testing its overflow properties.\n * Elements with `overflow: hidden` or `overflow: clip` are intentionally\n * excluded — they clip content but don't emit scroll events.\n */\nfunction isScrollable(el: HTMLElement): boolean {\n const style = getComputedStyle(el)\n const overflow = `${style.overflow} ${style.overflowY} ${style.overflowX}`\n\n return /auto|scroll|overlay/.test(overflow)\n}\n\nexport function findScrollParent(element: HTMLElement): HTMLElement | Window {\n let el: HTMLElement | null = element\n\n while (el) {\n if (isScrollable(el)) {\n return el\n }\n\n // Check if we hit a Shadow DOM boundary. If so, jump to the shadow host\n // and continue traversing the light DOM.\n const parent = el.parentElement\n if (!parent) {\n const root = el.getRootNode()\n if (root instanceof ShadowRoot) {\n el = root.host as HTMLElement\n continue\n }\n\n return window\n }\n\n el = parent\n }\n\n return window\n}\n","import type { Node } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nfunction getContainerRect(container: HTMLElement | Window): { top: number; bottom: number } {\n if (container === window) {\n return { top: 0, bottom: window.innerHeight }\n }\n\n return (container as HTMLElement).getBoundingClientRect()\n}\n\nexport function getViewportBoundaryPositions({\n doc,\n view,\n scrollContainer,\n}: {\n doc: Node\n view: EditorView\n scrollContainer?: HTMLElement | Window\n}) {\n const editorRect = view.dom.getBoundingClientRect()\n const containerRect = scrollContainer ? getContainerRect(scrollContainer) : { top: 0, bottom: window.innerHeight }\n\n const visibleTop = Math.max(editorRect.top, containerRect.top)\n const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom)\n\n if (visibleTop >= visibleBottom) {\n // Editor is not visible — fall back to full document range\n return { top: 0, bottom: doc.content.size }\n }\n\n // Pick the x-coordinate based on text direction. In LTR the content\n // starts at the left edge; in RTL it starts at the right edge.\n // Clamp to ensure the coordinate stays inside the editor bounds.\n const isRTL = getComputedStyle(view.dom).direction === 'rtl'\n const x = isRTL ? Math.max(editorRect.right - 2, editorRect.left + 2) : editorRect.left + 2\n\n const topPos = view.posAtCoords({ left: x, top: visibleTop + 2 })\n const bottomPos = view.posAtCoords({ left: x, top: visibleBottom - 2 })\n\n return {\n top: topPos ? topPos.pos : 0,\n bottom: bottomPos ? bottomPos.pos : doc.content.size,\n }\n}\n","export function throttle<T extends (...args: any[]) => void>(fn: T, delay: number): { call: T; cancel: () => void } {\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const call = ((...args: any[]) => {\n if (timer) {\n return\n }\n\n // Leading-edge: fire immediately, then prevent subsequent calls\n // until the timer fires and resets.\n fn(...args)\n timer = setTimeout(() => {\n timer = null\n }, delay)\n }) as T\n\n const cancel = () => {\n if (timer) {\n clearTimeout(timer)\n timer = null\n }\n }\n\n return { call, cancel }\n}\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport const skipTrailingNodeMeta = 'skipTrailingNode'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (transactions, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (transactions.some(transaction => transaction.getMeta(skipTrailingNodeMeta))) {\n return\n }\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":";AAAA,SAAS,iBAAiB;AAE1B,SAAS,QAAQ,iBAAiB;AAgE3B,IAAM,iBAAiB,UAAU,OAAqD;AAAA,EAC3F,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,MAAM;AAAA,MACN,aAAa,UAAQ,KAAK;AAAA,MAC1B,aAAa,UAAQ,KAAK,MAAM,GAAG,EAAE,OAAO,UAAQ,SAAS,EAAE,EAAE;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,aAAa;AACX,WAAO;AAAA,MACL,YAAY,MAAM;AAAA,MAClB,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAAA,EAEA,iBAAiB;AACf,SAAK,QAAQ,aAAa,aAAW;AACnC,YAAM,QAAO,mCAAS,SAAQ,KAAK,OAAO,MAAM;AAChD,YAAM,QAAO,mCAAS,SAAQ,KAAK,QAAQ;AAE3C,UAAI,SAAS,YAAY;AACvB,cAAM,OAAO,KAAK,YAAY,GAAG,KAAK,QAAQ,MAAM,QAAW,GAAG;AAElE,eAAO,KAAK,QAAQ,YAAY,IAAI;AAAA,MACtC;AAEA,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,QAAQ,QAAQ,aAAW;AAC9B,YAAM,QAAO,mCAAS,SAAQ,KAAK,OAAO,MAAM;AAChD,YAAM,OAAO,KAAK,YAAY,GAAG,KAAK,QAAQ,MAAM,KAAK,GAAG;AAE5D,aAAO,KAAK,QAAQ,YAAY,IAAI;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,QAAI,wBAAwB;AAE5B,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK,IAAI,UAAU,gBAAgB;AAAA,QACnC,mBAAmB,CAAC,cAAc,UAAU,aAAa;AACvD,cAAI,uBAAuB;AACzB;AAAA,UACF;AAEA,gBAAM,QAAQ,KAAK,QAAQ;AAC3B,gBAAM,WAAW,KAAK,QAAQ;AAE9B,cAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,aAAa,OAAO;AAC9E,oCAAwB;AACxB;AAAA,UACF;AAEA,gBAAM,qBAAqB,KAAK,QAAQ,WAAW,EAAE,MAAM,SAAS,IAAI,CAAC;AAEzE,cAAI,qBAAqB,OAAO;AAC9B,kBAAM,OAAO,qBAAqB;AAClC,kBAAM,OAAO;AACb,kBAAM,KAAK;AAEX,oBAAQ;AAAA,cACN,sDAAsD,KAAK;AAAA,YAC7D;AACA,kBAAM,KAAK,SAAS,GAAG,YAAY,MAAM,EAAE;AAC3C,oCAAwB;AACxB,mBAAO;AAAA,UACT;AAEA,kCAAwB;AAAA,QAC1B;AAAA,QACA,mBAAmB,CAAC,aAAa,UAAU;AACzC,gBAAM,QAAQ,KAAK,QAAQ;AAG3B,cAAI,CAAC,YAAY,cAAc,UAAU,KAAK,UAAU,QAAQ,UAAU,QAAW;AACnF,mBAAO;AAAA,UACT;AAEA,gBAAM,UAAU,KAAK,QAAQ,WAAW,EAAE,MAAM,MAAM,IAAI,CAAC;AAC3D,gBAAM,UAAU,KAAK,QAAQ,WAAW,EAAE,MAAM,YAAY,IAAI,CAAC;AAGjE,cAAI,WAAW,OAAO;AACpB,mBAAO;AAAA,UACT;AAGA,cAAI,UAAU,SAAS,UAAU,SAAS,WAAW,SAAS;AAC5D,mBAAO;AAAA,UACT;AAGA,cAAI,UAAU,SAAS,UAAU,SAAS,UAAU,SAAS;AAC3D,mBAAO;AAAA,UACT;AAEA,gBAAM,UAAU,YAAY,QAAQ,OAAO;AAG3C,cAAI,CAAC,SAAS;AACZ,mBAAO;AAAA,UACT;AAGA,gBAAM,MAAM,YAAY,UAAU,MAAM;AACxC,gBAAM,OAAO,UAAU;AACvB,gBAAM,OAAO,MAAM;AACnB,gBAAM,KAAK;AAIX,sBAAY,YAAY,MAAM,EAAE;AAMhC,gBAAM,cAAc,KAAK,QAAQ,WAAW,EAAE,MAAM,YAAY,IAAI,CAAC;AAErE,cAAI,cAAc,OAAO;AACvB,mBAAO;AAAA,UACT;AAEA,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AC1MD,SAAS,aAAAA,kBAAiB;AAC1B,SAAS,kBAAkB;AA+BpB,IAAM,aAAaA,WAAU,OAA0B;AAAA,EAC5D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,CAAC,WAAW,KAAK,OAAO,CAAC;AAAA,EAClC;AACF,CAAC;;;AC9CD,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,UAAAC,SAAQ,aAAAC,kBAAiB;AAClC,SAAS,YAAY,qBAAqB;AA2BnC,IAAM,QAAQF,WAAU,OAAqB;AAAA,EAClD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,MACX,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAIC,QAAO;AAAA,QACT,KAAK,IAAIC,WAAU,OAAO;AAAA,QAC1B,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,EAAE,YAAY,UAAU,IAAI,KAAK;AACvC,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,qBAAO,cAAc,OAAO,KAAK,CAAC,CAAC;AAAA,YACrC;AAGA,gBAAI,YAAY;AAEhB,gBAAI,KAAK,QAAQ,SAAS,WAAW;AACnC,kBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAI,KAAK,QAAQ;AACf;AAAA,gBACF;AAEA,sBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,oBAAI,CAAC,WAAW;AACd,yBAAO;AAAA,gBACT;AAEA,6BAAa;AAAA,cACf,CAAC;AAAA,YACH;AAGA,gBAAI,eAAe;AAEnB,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,kBAAI,KAAK,QAAQ;AACf,uBAAO;AAAA,cACT;AAEA,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,kBAAI,CAAC,WAAW;AACd,uBAAO;AAAA,cACT;AAEA,8BAAgB;AAEhB,oBAAM,aACH,KAAK,QAAQ,SAAS,aAAa,YAAY,eAAe,KAC9D,KAAK,QAAQ,SAAS,gBAAgB,eAAe;AAExD,kBAAI,YAAY;AACd,uBAAO,KAAK,QAAQ,SAAS;AAAA,cAC/B;AAEA,0BAAY;AAAA,gBACV,WAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBACxC,OAAO,KAAK,QAAQ;AAAA,gBACtB,CAAC;AAAA,cACH;AAAA,YACF,CAAC;AAED,mBAAO,cAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AC5GD,SAAS,cAAc,aAAAC,YAAW,yBAAyB;AAC3D,SAAS,iBAAiB;AA0BnB,IAAM,YAAYA,WAAU,OAAO;AAAA,EACxC,MAAM;AAAA,EAEN,wBAAwB;AACtB,WAAO,CAAC,UAAU,CAAC;AAAA,EACrB;AAAA,EAEA,iBAAiB,WAAW;AAnC9B;AAoCI,UAAM,UAAU;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,IACrB;AAEA,WAAO;AAAA,MACL,iBAAgB,kBAAa,kBAAkB,WAAW,kBAAkB,OAAO,CAAC,MAApE,YAAyE;AAAA,IAC3F;AAAA,EACF;AACF,CAAC;;;AC9CD,SAAS,aAAAC,YAAW,mBAAmB;AACvC,SAAS,UAAAC,SAAQ,aAAAC,kBAAiB;AAElC,SAAS,iBAAAC,sBAAqB;;;ACD9B,SAAS,cAAAC,mBAAkB;AAiBpB,SAAS,4BAA4B,SAYzC;AACD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,EAAE,WAAW,YAAY;AAAA,EACpC,IAAI;AACJ,QAAM,UAAU,CAAC,SAAS;AAE1B,MAAI,YAAY;AACd,YAAQ,KAAK,WAAW;AAAA,EAC1B;AAEA,SAAOA,YAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,IAC/C,OAAO,QAAQ,KAAK,GAAG;AAAA,IACvB,CAAC,aAAa,GACZ,OAAO,gBAAgB,aACnB,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC,IACD;AAAA,EACR,CAAC;AACH;;;ACvDA,SAAS,aAAa,IAA0B;AAC9C,QAAM,QAAQ,iBAAiB,EAAE;AACjC,QAAM,WAAW,GAAG,MAAM,QAAQ,IAAI,MAAM,SAAS,IAAI,MAAM,SAAS;AAExE,SAAO,sBAAsB,KAAK,QAAQ;AAC5C;AAEO,SAAS,iBAAiB,SAA4C;AAC3E,MAAI,KAAyB;AAE7B,SAAO,IAAI;AACT,QAAI,aAAa,EAAE,GAAG;AACpB,aAAO;AAAA,IACT;AAIA,UAAM,SAAS,GAAG;AAClB,QAAI,CAAC,QAAQ;AACX,YAAM,OAAO,GAAG,YAAY;AAC5B,UAAI,gBAAgB,YAAY;AAC9B,aAAK,KAAK;AACV;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAEA,SAAK;AAAA,EACP;AAEA,SAAO;AACT;;;AClCA,SAAS,iBAAiB,WAAkE;AAC1F,MAAI,cAAc,QAAQ;AACxB,WAAO,EAAE,KAAK,GAAG,QAAQ,OAAO,YAAY;AAAA,EAC9C;AAEA,SAAQ,UAA0B,sBAAsB;AAC1D;AAEO,SAAS,6BAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,aAAa,KAAK,IAAI,sBAAsB;AAClD,QAAM,gBAAgB,kBAAkB,iBAAiB,eAAe,IAAI,EAAE,KAAK,GAAG,QAAQ,OAAO,YAAY;AAEjH,QAAM,aAAa,KAAK,IAAI,WAAW,KAAK,cAAc,GAAG;AAC7D,QAAM,gBAAgB,KAAK,IAAI,WAAW,QAAQ,cAAc,MAAM;AAEtE,MAAI,cAAc,eAAe;AAE/B,WAAO,EAAE,KAAK,GAAG,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAC5C;AAKA,QAAM,QAAQ,iBAAiB,KAAK,GAAG,EAAE,cAAc;AACvD,QAAM,IAAI,QAAQ,KAAK,IAAI,WAAW,QAAQ,GAAG,WAAW,OAAO,CAAC,IAAI,WAAW,OAAO;AAE1F,QAAM,SAAS,KAAK,YAAY,EAAE,MAAM,GAAG,KAAK,aAAa,EAAE,CAAC;AAChE,QAAM,YAAY,KAAK,YAAY,EAAE,MAAM,GAAG,KAAK,gBAAgB,EAAE,CAAC;AAEtE,SAAO;AAAA,IACL,KAAK,SAAS,OAAO,MAAM;AAAA,IAC3B,QAAQ,YAAY,UAAU,MAAM,IAAI,QAAQ;AAAA,EAClD;AACF;;;AC5CO,SAAS,SAA6C,IAAO,OAAgD;AAClH,MAAI,QAA8C;AAElD,QAAM,QAAQ,IAAI,SAAgB;AAChC,QAAI,OAAO;AACT;AAAA,IACF;AAIA,OAAG,GAAG,IAAI;AACV,YAAQ,WAAW,MAAM;AACvB,cAAQ;AAAA,IACV,GAAG,KAAK;AAAA,EACV;AAEA,QAAM,SAAS,MAAM;AACnB,QAAI,OAAO;AACT,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,OAAO;AACxB;;;AJVA,IAAM,yBAAyB;AAOxB,SAAS,4BAA4B,MAAsB;AAChE,SACE,KAEG,QAAQ,QAAQ,GAAG,EAInB,QAAQ,kBAAkB,EAAE,EAE5B,QAAQ,YAAY,EAAE,EAEtB,QAAQ,OAAO,EAAE,EACjB,YAAY;AAEnB;AAEO,IAAM,aAAa,IAAIC,WAAU,qBAAqB;AAOtD,IAAM,cAAcC,WAAU,OAA2B;AAAA,EAC9D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf,aAAa;AAAA,MACb,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,gBAAgB,KAAK,QAAQ,gBAC/B,QAAQ,4BAA4B,KAAK,QAAQ,aAAa,CAAC,KAC/D,QAAQ,sBAAsB;AAElC,WAAO;AAAA,MACL,IAAIC,QAAO;AAAA,QACT,OAAO;AAAA,UACL,OAAO;AACL,mBAAO;AAAA;AAAA;AAAA,cAGL,QAAQ;AAAA,cACR,WAAW;AAAA,YACb;AAAA,UACF;AAAA,UACA,MAAM,IAAI,MAAM;AACd,kBAAM,OAAO,GAAG,QAAQ,UAAU;AAElC,gBAAI,6BAAM,WAAW;AACnB,qBAAO;AAAA,gBACL,QAAQ,KAAK,UAAU;AAAA,gBACvB,WAAW,KAAK,UAAU;AAAA,cAC5B;AAAA,YACF;AAEA,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAOA,mBAAO;AAAA,cACL,QAAQ,KAAK,WAAW,OAAO,GAAG,QAAQ,IAAI,KAAK,MAAM,IAAI;AAAA,cAC7D,WAAW,KAAK,cAAc,OAAO,GAAG,QAAQ,IAAI,KAAK,SAAS,IAAI;AAAA,YACxE;AAAA,UACF;AAAA,QACF;AAAA,QACA,KAAK;AAAA,QACL,KAAK,MAAM;AACT,gBAAM,kBAAkB,iBAAiB,KAAK,GAAG;AAEjD,gBAAM,qBAAqB,MAAM;AAC/B,kBAAM,YAAY,6BAA6B;AAAA,cAC7C;AAAA,cACA,KAAK,KAAK,MAAM;AAAA,cAChB;AAAA,YACF,CAAC;AAED,kBAAM,OAAO,WAAW,SAAS,KAAK,KAAK;AAC3C,gBAAI,KAAK,WAAW,UAAU,OAAO,KAAK,cAAc,UAAU,QAAQ;AACxE;AAAA,YACF;AAEA,kBAAM,KAAK,KAAK,MAAM,GACnB,QAAQ,YAAY,EAAE,UAAU,CAAC,EAGjC,QAAQ,0BAA0B,IAAI;AACzC,iBAAK,SAAS,EAAE;AAAA,UAClB;AAEA,gBAAM,EAAE,MAAM,iBAAiB,QAAQ,eAAe,IAAI,SAAS,oBAAoB,GAAG;AAC1F,gBAAM,eAAe;AAErB,uBAAa,iBAAiB,UAAU,iBAAiB,EAAE,SAAS,KAAK,CAAC;AAG1E,6BAAmB;AAEnB,iBAAO;AAAA,YACL,OAAO,GAAG,WAAW;AAInB,kBAAI,KAAK,MAAM,IAAI,QAAQ,SAAS,UAAU,IAAI,QAAQ,MAAM;AAC9D,mCAAmB;AAAA,cACrB;AAAA,YACF;AAAA,YACA,SAAS,MAAM;AACb,6BAAe;AACf,2BAAa,oBAAoB,UAAU,eAAe;AAAA,YAC5D;AAAA,UACF;AAAA,QACF;AAAA,QACA,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AArJ/C;AAsJY,kBAAM,SAAS,KAAK,OAAO,cAAc,CAAC,KAAK,QAAQ;AAEvD,gBAAI,CAAC,QAAQ;AACX,qBAAO;AAAA,YACT;AAEA,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AACnC,kBAAM,aAAa,KAAK,OAAO;AAE/B,kBAAM,kBAAkB,KAAK,QAAQ,mBAAmB,CAAC,KAAK,QAAQ;AAEtE,gBAAI,iBAAiB;AACnB,oBAAM,WAAW,IAAI,QAAQ,MAAM;AAEnC,kBAAI,SAAS,QAAQ,GAAG;AACtB,sBAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,sBAAM,YAAY,SAAS,OAAO,CAAC;AAEnC,oBAAI,KAAK,KAAK,eAAe,YAAY,IAAI,GAAG;AAC9C,wBAAM,YAAY,UAAU,aAAa,UAAU,YAAY,KAAK;AACpE,wBAAM,aAAa,4BAA4B;AAAA,oBAC7C;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,aAAa,KAAK,QAAQ;AAAA,oBAC1B,SAAS;AAAA,sBACP,aAAa,KAAK,QAAQ;AAAA,sBAC1B,WAAW,KAAK,QAAQ;AAAA,oBAC1B;AAAA,oBACA,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA,KAAK,SAAS,OAAO,CAAC;AAAA,kBACxB,CAAC;AAED,8BAAY,KAAK,UAAU;AAAA,gBAC7B;AAAA,cACF;AAAA,YACF,OAAO;AACL,oBAAM,cAAc,WAAW,SAAS,KAAK,OAAO,KAAK;AACzD,oBAAM,QAAO,iBAAY,WAAZ,YAAsB;AACnC,oBAAM,MAAK,iBAAY,cAAZ,YAAyB,IAAI,QAAQ;AAEhD,kBAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AACxC,sBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK;AACxD,sBAAM,UAAU,CAAC,KAAK,UAAU,YAAY,IAAI;AAEhD,oBAAI,CAAC,KAAK,KAAK,aAAa;AAC1B,yBAAO,KAAK,QAAQ;AAAA,gBACtB;AAEA,qBAAK,aAAa,CAAC,KAAK,QAAQ,oBAAoB,SAAS;AAC3D,wBAAM,aAAa,4BAA4B;AAAA,oBAC7C,SAAS,EAAE,aAAa,KAAK,QAAQ,kBAAkB,WAAW,KAAK,QAAQ,eAAe;AAAA,oBAC9F,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,aAAa,KAAK,QAAQ;AAAA,oBAC1B;AAAA,oBACA;AAAA,kBACF,CAAC;AACD,8BAAY,KAAK,UAAU;AAAA,gBAC7B;AAEA,uBAAO,KAAK,QAAQ;AAAA,cACtB,CAAC;AAAA,YACH;AAEA,mBAAOC,eAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AKjOD,SAAS,aAAAC,YAAW,uBAAuB;AAC3C,SAAS,UAAAC,SAAQ,aAAAC,kBAAiB;AAClC,SAAS,cAAAC,aAAY,iBAAAC,sBAAqB;AAenC,IAAM,YAAYJ,WAAU,OAAyB;AAAA,EAC1D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,EAAE,QAAQ,QAAQ,IAAI;AAE5B,WAAO;AAAA,MACL,IAAIC,QAAO;AAAA,QACT,KAAK,IAAIC,WAAU,WAAW;AAAA,QAC9B,OAAO;AAAA,UACL,YAAY,OAAO;AACjB,gBACE,MAAM,UAAU,SAChB,OAAO,aACP,CAAC,OAAO,cACR,gBAAgB,MAAM,SAAS,KAC/B,OAAO,KAAK,UACZ;AACA,qBAAO;AAAA,YACT;AAEA,mBAAOE,eAAc,OAAO,MAAM,KAAK;AAAA,cACrCD,YAAW,OAAO,MAAM,UAAU,MAAM,MAAM,UAAU,IAAI;AAAA,gBAC1D,OAAO,QAAQ;AAAA,cACjB,CAAC;AAAA,YACH,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;ACtDD,SAAS,aAAAE,kBAAiB;AAE1B,SAAS,UAAAC,SAAQ,aAAAC,kBAAiB;AAE3B,IAAM,uBAAuB;AAEpC,SAAS,eAAe,EAAE,OAAO,KAAK,GAAoE;AACxG,SAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,IAAI,MAAM,6BAAM,UAAS;AACvF;AA2BO,IAAM,eAAeF,WAAU,OAA4B;AAAA,EAChE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AA7C1B;AA8CI,UAAM,SAAS,IAAIE,WAAU,KAAK,IAAI;AACtC,UAAM,cACJ,KAAK,QAAQ,UAAQ,UAAK,OAAO,OAAO,YAAY,aAAa,gBAA5C,mBAAyD,SAAQ;AAExF,UAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK,EAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,EACxB,OAAO,WAAS,KAAK,QAAQ,YAAY,CAAC,GAAG,OAAO,WAAW,EAAE,SAAS,KAAK,IAAI,CAAC;AAEvF,WAAO;AAAA,MACL,IAAID,QAAO;AAAA,QACT,KAAK;AAAA,QACL,mBAAmB,CAAC,cAAc,IAAI,UAAU;AAC9C,gBAAM,EAAE,KAAK,IAAI,OAAO,IAAI;AAC5B,gBAAM,wBAAwB,OAAO,SAAS,KAAK;AACnD,gBAAM,cAAc,IAAI,QAAQ;AAChC,gBAAM,OAAO,OAAO,MAAM,WAAW;AAErC,cAAI,aAAa,KAAK,iBAAe,YAAY,QAAQ,oBAAoB,CAAC,GAAG;AAC/E;AAAA,UACF;AAEA,cAAI,CAAC,uBAAuB;AAC1B;AAAA,UACF;AAEA,iBAAO,GAAG,OAAO,aAAa,KAAK,OAAO,CAAC;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,MAAM,CAAC,GAAG,UAAU;AAClB,kBAAM,WAAW,MAAM,GAAG,IAAI;AAE9B,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,UACA,OAAO,CAAC,IAAI,UAAU;AACpB,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAIA,gBAAI,GAAG,QAAQ,uBAAuB,GAAG;AACvC,qBAAO;AAAA,YACT;AAEA,kBAAM,WAAW,GAAG,IAAI;AAExB,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AClGD,SAAS,aAAAE,kBAAiB;AAC1B,SAAS,SAAS,MAAM,YAAY;AA4C7B,IAAM,WAAWA,WAAU,OAAwB;AAAA,EACxD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,eAAO,KAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,MACF,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,eAAO,KAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAAA,EAC/B;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,eAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MAC/C,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA;AAAA,MAGzC,cAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,oBAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,IACjD;AAAA,EACF;AACF,CAAC;","names":["Extension","Extension","Plugin","PluginKey","Extension","Extension","Plugin","PluginKey","DecorationSet","Decoration","PluginKey","Extension","Plugin","DecorationSet","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Extension"]}
1
+ {"version":3,"sources":["../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/constants.ts","../src/placeholder/placeholder.ts","../src/placeholder/plugins/PlaceholderPlugin.ts","../src/placeholder/utils/buildPlaceholderDecorations.ts","../src/placeholder/utils/createPlaceholderDecoration.ts","../src/placeholder/utils/preparePlaceholderAttribute.ts","../src/placeholder/utils/findScrollParent.ts","../src/placeholder/utils/getViewportBoundaryPositions.ts","../src/placeholder/utils/viewportTracking.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * Sets whether the content will be automatically trimmed when programatically setting content over the limit.\n * If set to false, the user will be able to trim the text manually.\n * @default true\n * @example false\n */\n autoTrim?: boolean\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n autoTrim: true,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n const autoTrim = this.options.autoTrim\n\n if (limit === null || limit === undefined || limit === 0 || autoTrim === false) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import { PluginKey } from '@tiptap/pm/state'\n\nimport type { ViewportState } from './types.js'\n\n/** The default data attribute label */\nexport const DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/** The plugin key used to store and read the placeholder viewport state */\nexport const PLUGIN_KEY = new PluginKey<ViewportState>('tiptap__placeholder')\n\n/**\n * Extra pixels added above and below the visible viewport when computing the\n * range of nodes to decorate. Decorating slightly beyond the fold means a\n * node already has its placeholder before it scrolls into view, which hides\n * the latency introduced by the throttled viewport recompute.\n */\nexport const VIEWPORT_OVERSCAN_PX = 200\n","import { Extension } from '@tiptap/core'\n\nimport { DEFAULT_DATA_ATTRIBUTE } from './constants.js'\nimport { createPlaceholderPlugin } from './plugins/PlaceholderPlugin.js'\nimport type { PlaceholderOptions } from './types.js'\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n return [createPlaceholderPlugin({ editor: this.editor, options: this.options })]\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport { Plugin } from '@tiptap/pm/state'\n\nimport { DEFAULT_DATA_ATTRIBUTE, PLUGIN_KEY } from '../constants.js'\nimport type { PlaceholderOptions } from '../types.js'\nimport { buildPlaceholderDecorations } from '../utils/buildPlaceholderDecorations.js'\nimport { preparePlaceholderAttribute } from '../utils/preparePlaceholderAttribute.js'\nimport { createViewportPluginView, viewportPluginState } from '../utils/viewportTracking.js'\n\nexport type CreatePluginOptions = {\n editor: Editor\n options: PlaceholderOptions\n}\n\n/**\n * Creates the ProseMirror plugin that renders placeholder decorations.\n * @param options.editor - The editor instance.\n * @param options.options - The resolved placeholder options.\n * @returns The configured placeholder plugin.\n */\nexport function createPlaceholderPlugin({ editor, options }: CreatePluginOptions) {\n const dataAttribute = options.dataAttribute\n ? `data-${preparePlaceholderAttribute(options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return new Plugin({\n key: PLUGIN_KEY,\n state: viewportPluginState,\n view: createViewportPluginView,\n props: {\n decorations: ({ doc, selection }) =>\n buildPlaceholderDecorations({ editor, options, dataAttribute, doc, selection }),\n },\n })\n}\n","import type { Editor } from '@tiptap/core'\nimport { isNodeEmpty } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\nimport type { Decoration } from '@tiptap/pm/view'\nimport { DecorationSet } from '@tiptap/pm/view'\n\nimport { PLUGIN_KEY } from '../constants.js'\nimport type { PlaceholderOptions } from '../types.js'\nimport { createPlaceholderDecoration } from './createPlaceholderDecoration.js'\n\n/**\n * Builds the placeholder decorations for the current document state.\n * @param options.editor - The editor instance.\n * @param options.options - The resolved placeholder options.\n * @param options.dataAttribute - The prepared `data-*` attribute name.\n * @param options.doc - The current document node.\n * @param options.selection - The current selection.\n * @returns A decoration set, or `null` when no placeholders should be shown.\n */\nexport function buildPlaceholderDecorations({\n editor,\n options,\n dataAttribute,\n doc,\n selection,\n}: {\n editor: Editor\n options: PlaceholderOptions\n dataAttribute: string\n doc: Node\n selection: Selection\n}): DecorationSet | null {\n const active = editor.isEditable || !options.showOnlyWhenEditable\n\n if (!active) {\n return null\n }\n\n const { anchor } = selection\n const decorations: Decoration[] = []\n const isEmptyDoc = editor.isEmpty\n\n const classes = {\n emptyEditor: options.emptyEditorClass,\n emptyNode: options.emptyNodeClass,\n }\n\n const useResolvedPath = options.showOnlyCurrent && !options.includeChildren\n\n if (useResolvedPath) {\n const resolved = doc.resolve(anchor)\n\n // When the selection spans the whole document (e.g. an `AllSelection`\n // after Cmd+A), the anchor resolves to the document level (depth 0). In\n // that case the relevant textblock is the node directly after the\n // position rather than an ancestor. otherwise the placeholder would\n // disappear after selecting all and deleting.\n const node = resolved.depth > 0 ? resolved.node(1) : resolved.nodeAfter\n const nodeStart = resolved.depth > 0 ? resolved.before(1) : anchor\n\n if (node && node.type.isTextblock && isNodeEmpty(node)) {\n const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize\n\n decorations.push(\n createPlaceholderDecoration({\n editor,\n isEmptyDoc,\n dataAttribute,\n hasAnchor,\n placeholder: options.placeholder,\n classes,\n node,\n pos: nodeStart,\n }),\n )\n }\n } else {\n const pluginState = PLUGIN_KEY.getState(editor.state)\n const from = pluginState?.topPos ?? 0\n const to = pluginState?.bottomPos ?? doc.content.size\n\n doc.nodesBetween(from, to, (node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if (!node.type.isTextblock) {\n return options.includeChildren\n }\n\n if ((hasAnchor || !options.showOnlyCurrent) && isEmpty) {\n decorations.push(\n createPlaceholderDecoration({\n editor,\n isEmptyDoc,\n dataAttribute,\n hasAnchor,\n placeholder: options.placeholder,\n classes,\n node,\n pos,\n }),\n )\n }\n\n return options.includeChildren\n })\n }\n\n return DecorationSet.create(doc, decorations)\n}\n","import type { Editor } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport { Decoration } from '@tiptap/pm/view'\n\nimport type { PlaceholderOptions } from '../types.js'\n\n/**\n * Creates a ProseMirror node decoration that applies a placeholder\n * CSS class and data attribute to an empty node.\n * @param options.editor - The editor instance\n * @param options.pos - The position of the node in the document\n * @param options.node - The ProseMirror node\n * @param options.isEmptyDoc - Whether the entire document is empty\n * @param options.hasAnchor - Whether the selection anchor is within the node\n * @param options.dataAttribute - The data attribute name (e.g. `data-placeholder`)\n * @param options.classes - CSS classes for empty nodes and the empty editor\n * @param options.placeholder - The placeholder text or a function that returns it\n * @returns A ProseMirror node decoration with placeholder classes and data attribute\n */\nexport function createPlaceholderDecoration(options: {\n editor: Editor\n pos: number\n node: Node\n isEmptyDoc: boolean\n hasAnchor: boolean\n dataAttribute: string\n classes: {\n emptyEditor: PlaceholderOptions['emptyEditorClass']\n emptyNode: PlaceholderOptions['emptyNodeClass']\n }\n placeholder: PlaceholderOptions['placeholder']\n}) {\n const {\n editor,\n placeholder,\n dataAttribute,\n pos,\n node,\n isEmptyDoc,\n hasAnchor,\n classes: { emptyNode, emptyEditor },\n } = options\n const classes = [emptyNode]\n\n if (isEmptyDoc) {\n classes.push(emptyEditor)\n }\n\n return Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof placeholder === 'function'\n ? placeholder({\n editor,\n node,\n pos,\n hasAnchor,\n })\n : placeholder,\n })\n}\n","/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n","/**\n * Checks if an element is scrollable by testing its overflow properties.\n * Elements with `overflow: hidden` or `overflow: clip` are intentionally\n * excluded — they clip content but don't emit scroll events.\n */\nfunction isScrollable(el: HTMLElement): boolean {\n const style = getComputedStyle(el)\n const overflow = `${style.overflow} ${style.overflowY} ${style.overflowX}`\n\n return /auto|scroll|overlay/.test(overflow)\n}\n\nexport function findScrollParent(element: HTMLElement): HTMLElement | Window {\n let el: HTMLElement | null = element\n\n while (el) {\n if (isScrollable(el)) {\n return el\n }\n\n // Check if we hit a Shadow DOM boundary. If so, jump to the shadow host\n // and continue traversing the light DOM.\n const parent = el.parentElement\n if (!parent) {\n const root = el.getRootNode()\n if (root instanceof ShadowRoot) {\n el = root.host as HTMLElement\n continue\n }\n\n return window\n }\n\n el = parent\n }\n\n return window\n}\n","import type { Node } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport { VIEWPORT_OVERSCAN_PX } from '../constants.js'\n\nfunction getContainerRect(container: HTMLElement | Window): { top: number; bottom: number } {\n if (container === window) {\n return { top: 0, bottom: window.innerHeight }\n }\n\n return (container as HTMLElement).getBoundingClientRect()\n}\n\nexport function getViewportBoundaryPositions({\n doc,\n view,\n scrollContainer,\n}: {\n doc: Node\n view: EditorView\n scrollContainer?: HTMLElement | Window\n}) {\n const editorRect = view.dom.getBoundingClientRect()\n const containerRect = scrollContainer\n ? getContainerRect(scrollContainer)\n : { top: 0, bottom: window.innerHeight }\n\n const visibleTop = Math.max(editorRect.top, containerRect.top) - VIEWPORT_OVERSCAN_PX\n const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom) + VIEWPORT_OVERSCAN_PX\n\n if (visibleTop >= visibleBottom) {\n // Editor is not visible — fall back to full document range\n return { top: 0, bottom: doc.content.size }\n }\n\n // Pick the x-coordinate based on text direction. In LTR the content\n // starts at the left edge; in RTL it starts at the right edge.\n // Clamp to ensure the coordinate stays inside the editor bounds.\n const isRTL = getComputedStyle(view.dom).direction === 'rtl'\n const x = isRTL ? Math.max(editorRect.right - 2, editorRect.left + 2) : editorRect.left + 2\n\n const topPos = view.posAtCoords({ left: x, top: visibleTop + 2 })\n const bottomPos = view.posAtCoords({ left: x, top: visibleBottom - 2 })\n\n return {\n top: topPos ? topPos.pos : 0,\n bottom: bottomPos ? bottomPos.pos : doc.content.size,\n }\n}\n","import type { EditorState, PluginView, StateField, Transaction } from '@tiptap/pm/state'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport { PLUGIN_KEY } from '../constants.js'\nimport type { ViewportState } from '../types.js'\nimport { findScrollParent } from './findScrollParent.js'\nimport { getViewportBoundaryPositions } from './getViewportBoundaryPositions.js'\n\n/**\n * The plugin `state` config that tracks the visible viewport boundaries so the\n * decoration callback only scans the nodes currently on screen.\n */\nexport const viewportPluginState: StateField<ViewportState> = {\n /**\n * Initialises the viewport state with no known positions.\n * @returns The initial viewport state.\n */\n init(): ViewportState {\n return { topPos: null, bottomPos: null }\n },\n\n /**\n * Updates the viewport state from incoming transactions.\n * @param tr - The transaction being applied.\n * @param prev - The previous viewport state.\n * @returns The next viewport state.\n */\n apply(tr: Transaction, prev: ViewportState): ViewportState {\n const meta = tr.getMeta(PLUGIN_KEY) as\n | { positions?: { top: number; bottom: number } }\n | undefined\n\n if (meta?.positions) {\n return { topPos: meta.positions.top, bottomPos: meta.positions.bottom }\n }\n\n if (!tr.docChanged) {\n return prev\n }\n\n // Preserve last known viewport positions across transactions.\n // Without this, every keystroke resets back to a full document\n // scan, defeating the viewport optimisation.\n // Only map when we have actual positions — null means \"no viewport\n // info yet\" and should stay null to fall back to full doc scan.\n return {\n topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null,\n bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null,\n }\n },\n}\n\n/**\n * Creates the plugin `view` that recomputes the visible viewport on scroll and\n * document size changes, dispatching the result into the plugin state.\n * @param view - The editor view the plugin is attached to.\n * @returns A plugin view with `update` and `destroy` handlers.\n */\nexport function createViewportPluginView(view: EditorView): PluginView {\n const scrollContainer = findScrollParent(view.dom)\n\n const computeAndDispatch = () => {\n const positions = getViewportBoundaryPositions({\n view,\n doc: view.state.doc,\n scrollContainer,\n })\n\n const prev = PLUGIN_KEY.getState(view.state)\n if (prev?.topPos === positions.top && prev?.bottomPos === positions.bottom) {\n return\n }\n\n const tr = view.state.tr.setMeta(PLUGIN_KEY, { positions })\n view.dispatch(tr)\n }\n\n // rAF-based scheduler with interval guard (150ms → ~6–7 Hz) used by\n // scroll events and update(). The overscan margin hides the extra\n // latency. When the guard blocks, the callback reschedules itself so\n // the trailing position is always captured.\n let frame: number | null = null\n let lastCompute = 0\n const MIN_SCROLL_INTERVAL = 150\n\n const scheduleFrame = () => {\n if (frame !== null) return\n frame = requestAnimationFrame(() => {\n frame = null\n const now = performance.now()\n if (now - lastCompute >= MIN_SCROLL_INTERVAL) {\n lastCompute = now\n computeAndDispatch()\n } else {\n scheduleFrame()\n }\n })\n }\n\n scrollContainer.addEventListener('scroll', scheduleFrame, { passive: true })\n\n // Fire once to populate initial viewport\n computeAndDispatch()\n\n return {\n update(_view: EditorView, prevState: EditorState) {\n if (view.state.doc.content.size !== prevState.doc.content.size) {\n scheduleFrame()\n }\n },\n destroy: () => {\n if (frame !== null) {\n cancelAnimationFrame(frame)\n }\n scrollContainer.removeEventListener('scroll', scheduleFrame)\n },\n }\n}\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport const skipTrailingNodeMeta = 'skipTrailingNode'\n\nfunction nodeEqualsType({\n types,\n node,\n}: {\n types: NodeType | NodeType[]\n node: Node | null | undefined\n}) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node ||\n this.editor.schema.topNodeType.contentMatch.defaultType?.name ||\n 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (transactions, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (transactions.some(transaction => transaction.getMeta(skipTrailingNodeMeta))) {\n return\n }\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":"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","names":["Extension","Extension","Plugin","PluginKey","Extension","PluginKey","Extension","Plugin","DecorationSet","Decoration","DecorationSet","Plugin","Extension","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Extension"]}