@domternal/core 0.14.0 → 1.0.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
@@ -2,10 +2,10 @@ import { PluginKey, Plugin, TextSelection, Selection, NodeSelection, AllSelectio
2
2
  export { PluginKey } from '@domternal/pm/state';
3
3
  import { DecorationSet, Decoration, EditorView } from '@domternal/pm/view';
4
4
  import { Slice, Fragment, Schema, Node as Node$1, DOMSerializer, DOMParser } from '@domternal/pm/model';
5
+ import { canJoin, canSplit, findWrapping, liftTarget, Transform } from '@domternal/pm/transform';
5
6
  import { keymap } from '@domternal/pm/keymap';
6
7
  import { splitBlock, chainCommands, newlineInCode, createParagraphNear, liftEmptyBlock, baseKeymap, selectNodeBackward as selectNodeBackward$1 } from '@domternal/pm/commands';
7
8
  import { InputRule } from '@domternal/pm/inputrules';
8
- import { canJoin, canSplit, findWrapping, liftTarget } from '@domternal/pm/transform';
9
9
  import { liftListItem, sinkListItem, splitListItem, wrapRangeInList } from '@domternal/pm/schema-list';
10
10
  import { autoUpdate, hide, offset, size, flip, shift, computePosition } from '@floating-ui/dom';
11
11
  import { find } from 'linkifyjs';
@@ -224,6 +224,90 @@ var ExtensionConfigurationError = class extends Error {
224
224
  }
225
225
  };
226
226
 
227
+ // src/utils/prosemirrorSingleton.ts
228
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("domternal.prosemirror.copies");
229
+ var REPORTED_KEY = /* @__PURE__ */ Symbol.for("domternal.prosemirror.copies.reported");
230
+ function registry() {
231
+ const host = globalThis;
232
+ const existing = host[REGISTRY_KEY];
233
+ if (existing instanceof Map) return existing;
234
+ const fresh = /* @__PURE__ */ new Map();
235
+ host[REGISTRY_KEY] = fresh;
236
+ return fresh;
237
+ }
238
+ function reported() {
239
+ const host = globalThis;
240
+ const existing = host[REPORTED_KEY];
241
+ if (existing instanceof Set) return existing;
242
+ const fresh = /* @__PURE__ */ new Set();
243
+ host[REPORTED_KEY] = fresh;
244
+ return fresh;
245
+ }
246
+ var FIXES = [
247
+ 'pnpm: add the package to "pnpm.overrides" in package.json, then run `pnpm dedupe`',
248
+ 'npm: add the package to "overrides" in package.json, then reinstall',
249
+ 'yarn: add the package to "resolutions" in package.json, then reinstall',
250
+ "Vite: depend on '<module>' in the app, then resolve.dedupe: ['<module>']",
251
+ "webpack: depend on '<module>' in the app, then resolve.alias it to that one copy"
252
+ ];
253
+ var DOCS_URL = "https://domternal.dev/v1/guides/single-prosemirror-copy/";
254
+ function describeConflict(module, first, second) {
255
+ return [
256
+ `Two different copies of "${module}" are loaded on this page.`,
257
+ `"${first}" registered one, "${second}" arrived with another.`,
258
+ "ProseMirror compares classes by identity, so objects made by one copy are",
259
+ "rejected by the other and the editor fails as soon as the two meet.",
260
+ "",
261
+ "Force a single copy:",
262
+ ...FIXES.map((line) => ` ${line.replaceAll("<module>", module)}`),
263
+ "",
264
+ DOCS_URL
265
+ ].join("\n");
266
+ }
267
+ function describeForeignExtension(name) {
268
+ return [
269
+ `The extension "${name}" was built by a different copy of "@domternal/core"`,
270
+ "than the one building this editor.",
271
+ "Extensions are bound to the core that created them: their base class, schema",
272
+ "and plugin keys all belong to that copy, so an editor cannot use one built",
273
+ "elsewhere. Two copies usually mean a linked or nested install.",
274
+ "",
275
+ "Force a single copy:",
276
+ ...FIXES.map((line) => ` ${line.replaceAll("<module>", "@domternal/core")}`),
277
+ "",
278
+ DOCS_URL
279
+ ].join("\n");
280
+ }
281
+ function registerProseMirrorCopy(module, copy, consumer) {
282
+ const entries = registry();
283
+ const existing = entries.get(module);
284
+ if (!existing) {
285
+ entries.set(module, { copy, consumer });
286
+ return null;
287
+ }
288
+ if (existing.copy === copy) return null;
289
+ return {
290
+ module,
291
+ firstConsumer: existing.consumer,
292
+ secondConsumer: consumer,
293
+ message: describeConflict(module, existing.consumer, consumer)
294
+ };
295
+ }
296
+ function assertSingleProseMirrorCopy(module, copy, consumer) {
297
+ const conflict = registerProseMirrorCopy(module, copy, consumer);
298
+ if (conflict) throw new ExtensionConfigurationError(conflict.message);
299
+ }
300
+ function warnOnDuplicateProseMirrorCopy(module, copy, consumer) {
301
+ const conflict = registerProseMirrorCopy(module, copy, consumer);
302
+ if (!conflict) return null;
303
+ const seen = reported();
304
+ if (!seen.has(module)) {
305
+ seen.add(module);
306
+ console.warn(conflict.message);
307
+ }
308
+ return conflict;
309
+ }
310
+
227
311
  // src/helpers/callOrReturn.ts
228
312
  function callOrReturn(value, context, ...args) {
229
313
  if (typeof value === "function") {
@@ -232,6 +316,183 @@ function callOrReturn(value, context, ...args) {
232
316
  return value;
233
317
  }
234
318
 
319
+ // src/Extension.ts
320
+ function mergeConfigWithParentBinding(parentConfig, extendedConfig) {
321
+ const parent = parentConfig;
322
+ const merged = { ...parent };
323
+ for (const [key, value] of Object.entries(extendedConfig)) {
324
+ if (typeof value === "function" && typeof parent[key] === "function") {
325
+ const parentFn = parent[key];
326
+ const childFn = value;
327
+ merged[key] = function(...args) {
328
+ const previousParent = this.parent;
329
+ this.parent = (...pArgs) => parentFn.call(this, ...pArgs);
330
+ const result = childFn.call(this, ...args);
331
+ this.parent = previousParent;
332
+ return result;
333
+ };
334
+ } else {
335
+ merged[key] = value;
336
+ }
337
+ }
338
+ return merged;
339
+ }
340
+ var EXTENSION_BRAND = /* @__PURE__ */ Symbol.for("domternal.core.extension");
341
+ var Extension = class _Extension {
342
+ /**
343
+ * Brand read by `ExtensionManager` to tell an extension built by another
344
+ * copy of `@domternal/core` from a plain object. See `EXTENSION_BRAND`.
345
+ */
346
+ [EXTENSION_BRAND] = true;
347
+ /**
348
+ * Extension type identifier
349
+ * Used to distinguish between Extension, Node, and Mark
350
+ * Subclasses override this to 'node' or 'mark'
351
+ */
352
+ type = "extension";
353
+ /**
354
+ * Unique extension name
355
+ */
356
+ name;
357
+ /**
358
+ * Extension options (immutable after creation)
359
+ */
360
+ options;
361
+ /**
362
+ * Extension storage (mutable state)
363
+ * Accessible via editor.storage[extensionName]
364
+ */
365
+ storage;
366
+ /**
367
+ * The original configuration object
368
+ */
369
+ config;
370
+ /**
371
+ * Editor instance (set by ExtensionManager after creation)
372
+ * null until ExtensionManager binds it
373
+ */
374
+ editor = null;
375
+ /**
376
+ * Reference to the parent config method when using extend().
377
+ * Set temporarily during config method execution so overridden
378
+ * methods can call `this.parent?.()` to invoke the original.
379
+ */
380
+ parent;
381
+ /**
382
+ * Protected constructor - use Extension.create() instead
383
+ */
384
+ constructor(config) {
385
+ if (!/^[a-z][a-zA-Z0-9]*$/.test(config.name)) {
386
+ throw new Error(
387
+ `Extension name '${config.name}' is invalid. Names must be camelCase starting with a lowercase letter (e.g., 'myExtension').`
388
+ );
389
+ }
390
+ this.config = config;
391
+ this.name = config.name;
392
+ const defaultOptions = callOrReturn(config.addOptions, this);
393
+ this.options = defaultOptions ?? {};
394
+ const defaultStorage = callOrReturn(config.addStorage, this);
395
+ this.storage = defaultStorage ?? {};
396
+ }
397
+ /**
398
+ * Creates a new extension instance
399
+ *
400
+ * @param config - Extension configuration
401
+ * @returns New extension instance
402
+ *
403
+ * @example
404
+ * const MyExtension = Extension.create({
405
+ * name: 'myExtension',
406
+ * addOptions() {
407
+ * return { enabled: true };
408
+ * },
409
+ * });
410
+ */
411
+ static create(config) {
412
+ return new _Extension(config);
413
+ }
414
+ /**
415
+ * Creates a new extension with merged options
416
+ * Original extension is not modified
417
+ *
418
+ * **Note:** Options are merged shallowly using object spread (`...`).
419
+ * Nested objects are replaced entirely, not deeply merged.
420
+ *
421
+ * @param options - Options to merge with existing options
422
+ * @returns New extension instance with merged options
423
+ *
424
+ * @example
425
+ * const configured = MyExtension.configure({ enabled: false });
426
+ *
427
+ * @example
428
+ * // Shallow merge behavior with nested objects:
429
+ * // Given: options = { nested: { a: 1, b: 2 } }
430
+ * // configure({ nested: { b: 3 } })
431
+ * // Result: { nested: { b: 3 } } - 'a' is lost!
432
+ * // To preserve nested values, spread manually:
433
+ * // configure({ nested: { ...original.options.nested, b: 3 } })
434
+ */
435
+ configure(options) {
436
+ const newConfig = {
437
+ ...this.config,
438
+ // Override addOptions to return merged options
439
+ addOptions: () => ({
440
+ ...this.options,
441
+ ...options
442
+ })
443
+ };
444
+ return new _Extension(newConfig);
445
+ }
446
+ /**
447
+ * Returns a fresh, unbound copy built from the same `config`: `editor` reset to
448
+ * null and `options`/`storage` re-derived, while `configure()`/`extend()`
449
+ * results are preserved (they live in `config`). Polymorphic: a `Node`/`Mark`
450
+ * clones to its own subclass.
451
+ *
452
+ * `ExtensionManager` clones every extension so each editor owns its instances
453
+ * and binding one editor can't mutate extensions shared with another.
454
+ */
455
+ clone() {
456
+ const Ctor = this.constructor;
457
+ return new Ctor(this.config);
458
+ }
459
+ /**
460
+ * Creates a new extension with extended configuration
461
+ * Original extension is not modified
462
+ *
463
+ * **Note:** Config is merged shallowly using object spread (`...`).
464
+ * Config properties (like `addCommands`, `addKeyboardShortcuts`) are
465
+ * replaced entirely, not combined with the base extension's config.
466
+ *
467
+ * @param extendedConfig - Configuration to extend/override
468
+ * @returns New extension instance with extended config
469
+ *
470
+ * @example
471
+ * const Extended = MyExtension.extend({
472
+ * name: 'extendedExtension',
473
+ * addCommands() {
474
+ * return { customCommand: () => ({ tr }) => true };
475
+ * },
476
+ * });
477
+ *
478
+ * @example
479
+ * // To preserve base extension's commands while adding new ones:
480
+ * const Extended = BaseExtension.extend({
481
+ * addCommands() {
482
+ * const baseCommands = BaseExtension.config.addCommands?.call(this) ?? {};
483
+ * return {
484
+ * ...baseCommands,
485
+ * newCommand: () => ({ tr }) => true,
486
+ * };
487
+ * },
488
+ * });
489
+ */
490
+ extend(extendedConfig) {
491
+ const newConfig = mergeConfigWithParentBinding(this.config, extendedConfig);
492
+ return new _Extension(newConfig);
493
+ }
494
+ };
495
+
235
496
  // src/ExtensionManager.ts
236
497
  function mergeHTMLAttrs(target, source) {
237
498
  const result = { ...target };
@@ -246,6 +507,13 @@ function mergeHTMLAttrs(target, source) {
246
507
  }
247
508
  return result;
248
509
  }
510
+ function assertOwnExtension(ext) {
511
+ if (ext instanceof Extension) return;
512
+ const foreign = ext?.[EXTENSION_BRAND];
513
+ if (foreign !== true) return;
514
+ const name = typeof ext.name === "string" ? ext.name : "unknown";
515
+ throw new ExtensionConfigurationError(describeForeignExtension(name));
516
+ }
249
517
  var ExtensionManager = class {
250
518
  /**
251
519
  * Processed extensions (flattened, sorted by priority)
@@ -305,8 +573,9 @@ var ExtensionManager = class {
305
573
  "ExtensionManager requires either extensions or schema. Provide at least Document, Text, and Paragraph extensions."
306
574
  );
307
575
  }
308
- const flattened = this.flattenExtensions(options.extensions);
309
- const deduped = this.deduplicateExtensions(flattened);
576
+ const autoIncluded = /* @__PURE__ */ new Set();
577
+ const flattened = this.flattenExtensions(options.extensions, autoIncluded);
578
+ const deduped = this.deduplicateExtensions(flattened, autoIncluded);
310
579
  const cloned = this.cloneExtensions(deduped);
311
580
  this._extensions = this.resolveExtensions(cloned);
312
581
  this.detectConflicts();
@@ -388,33 +657,57 @@ var ExtensionManager = class {
388
657
  /**
389
658
  * Recursively flattens extensions by expanding addExtensions()
390
659
  * This allows extension bundles like StarterKit to work
660
+ *
661
+ * `autoIncluded` collects everything that arrived through an
662
+ * `addExtensions()` rather than from the caller's own list, which is what
663
+ * lets deduplication tell a default apart from a choice.
391
664
  */
392
- flattenExtensions(extensions) {
665
+ flattenExtensions(extensions, autoIncluded, fromBundle = false) {
393
666
  const result = [];
394
667
  for (const ext of extensions) {
668
+ assertOwnExtension(ext);
669
+ if (fromBundle) autoIncluded.add(ext);
395
670
  result.push(ext);
396
671
  const nested = callOrReturn(
397
672
  ext.config.addExtensions,
398
673
  ext
399
674
  );
400
675
  if (nested && nested.length > 0) {
401
- result.push(...this.flattenExtensions(nested));
676
+ result.push(...this.flattenExtensions(nested, autoIncluded, true));
402
677
  }
403
678
  }
404
679
  return result;
405
680
  }
406
681
  /**
407
- * Removes duplicate extensions by name, keeping the last occurrence.
408
- * This allows parent extensions to auto-include children via addExtensions()
409
- * while letting users override with explicitly configured versions.
682
+ * Removes duplicate extensions by name.
683
+ *
684
+ * A version the caller listed themselves always wins over one a bundle
685
+ * included on their behalf, and position does not enter into it. Keeping
686
+ * the last occurrence alone said the same thing only while every bundle was
687
+ * listed first, which is the habit for StarterKit and no rule at all: an
688
+ * extension that includes a default and is written LOWER in the list, as
689
+ * `Export` and its `Print` are, silently replaced the configured copy
690
+ * above it and the caller's options went missing with it.
691
+ *
692
+ * Between two of the same kind the later one still wins, so two bundles
693
+ * offering the same default resolve as they always have.
410
694
  */
411
- deduplicateExtensions(extensions) {
412
- const seen = /* @__PURE__ */ new Map();
695
+ deduplicateExtensions(extensions, autoIncluded) {
696
+ const winners = /* @__PURE__ */ new Map();
413
697
  for (let i = 0; i < extensions.length; i++) {
414
698
  const ext = extensions[i];
415
- if (ext) seen.set(ext.name, i);
699
+ if (!ext) continue;
700
+ const held = winners.get(ext.name);
701
+ if (held === void 0) {
702
+ winners.set(ext.name, i);
703
+ continue;
704
+ }
705
+ const heldIsAuto = autoIncluded.has(extensions[held]);
706
+ const nextIsAuto = autoIncluded.has(ext);
707
+ if (nextIsAuto && !heldIsAuto) continue;
708
+ winners.set(ext.name, i);
416
709
  }
417
- return extensions.filter((ext, i) => seen.get(ext.name) === i);
710
+ return extensions.filter((ext, i) => winners.get(ext.name) === i);
418
711
  }
419
712
  /**
420
713
  * Clone every extension so this editor owns its instances. Extensions hold
@@ -1139,7 +1432,8 @@ function markInputRule(options) {
1139
1432
  return null;
1140
1433
  }
1141
1434
  const { tr } = state;
1142
- tr.replaceWith(start, end, state.schema.text(textContent));
1435
+ const marks = tr.storedMarks ?? tr.doc.resolve(start).marksAcross(tr.doc.resolve(end));
1436
+ tr.replaceWith(start, end, state.schema.text(textContent, marks));
1143
1437
  tr.addMark(start, start + textContent.length, type.create(attributes ?? void 0));
1144
1438
  tr.removeStoredMark(type);
1145
1439
  return tr;
@@ -1231,7 +1525,10 @@ function textInputRule(options) {
1231
1525
  const { find: find2, replace, undoable } = options;
1232
1526
  return new InputRule(
1233
1527
  find2,
1234
- (state, _match, start, end) => state.tr.replaceWith(start, end, state.schema.text(replace)),
1528
+ // insertText inherits the replaced range's marks (stored marks first,
1529
+ // then marksAcross), so a replacement inside marked text keeps the
1530
+ // marks instead of punching an unmarked hole into them.
1531
+ (state, _match, start, end) => state.tr.insertText(replace, start, end),
1235
1532
  undoable !== void 0 ? { undoable } : {}
1236
1533
  );
1237
1534
  }
@@ -1469,177 +1766,6 @@ var insertContent = (content) => ({ state, tr, dispatch }) => {
1469
1766
  return true;
1470
1767
  };
1471
1768
 
1472
- // src/Extension.ts
1473
- function mergeConfigWithParentBinding(parentConfig, extendedConfig) {
1474
- const parent = parentConfig;
1475
- const merged = { ...parent };
1476
- for (const [key, value] of Object.entries(extendedConfig)) {
1477
- if (typeof value === "function" && typeof parent[key] === "function") {
1478
- const parentFn = parent[key];
1479
- const childFn = value;
1480
- merged[key] = function(...args) {
1481
- const previousParent = this.parent;
1482
- this.parent = (...pArgs) => parentFn.call(this, ...pArgs);
1483
- const result = childFn.call(this, ...args);
1484
- this.parent = previousParent;
1485
- return result;
1486
- };
1487
- } else {
1488
- merged[key] = value;
1489
- }
1490
- }
1491
- return merged;
1492
- }
1493
- var Extension = class _Extension {
1494
- /**
1495
- * Extension type identifier
1496
- * Used to distinguish between Extension, Node, and Mark
1497
- * Subclasses override this to 'node' or 'mark'
1498
- */
1499
- type = "extension";
1500
- /**
1501
- * Unique extension name
1502
- */
1503
- name;
1504
- /**
1505
- * Extension options (immutable after creation)
1506
- */
1507
- options;
1508
- /**
1509
- * Extension storage (mutable state)
1510
- * Accessible via editor.storage[extensionName]
1511
- */
1512
- storage;
1513
- /**
1514
- * The original configuration object
1515
- */
1516
- config;
1517
- /**
1518
- * Editor instance (set by ExtensionManager after creation)
1519
- * null until ExtensionManager binds it
1520
- */
1521
- editor = null;
1522
- /**
1523
- * Reference to the parent config method when using extend().
1524
- * Set temporarily during config method execution so overridden
1525
- * methods can call `this.parent?.()` to invoke the original.
1526
- */
1527
- parent;
1528
- /**
1529
- * Protected constructor - use Extension.create() instead
1530
- */
1531
- constructor(config) {
1532
- if (!/^[a-z][a-zA-Z0-9]*$/.test(config.name)) {
1533
- throw new Error(
1534
- `Extension name '${config.name}' is invalid. Names must be camelCase starting with a lowercase letter (e.g., 'myExtension').`
1535
- );
1536
- }
1537
- this.config = config;
1538
- this.name = config.name;
1539
- const defaultOptions = callOrReturn(config.addOptions, this);
1540
- this.options = defaultOptions ?? {};
1541
- const defaultStorage = callOrReturn(config.addStorage, this);
1542
- this.storage = defaultStorage ?? {};
1543
- }
1544
- /**
1545
- * Creates a new extension instance
1546
- *
1547
- * @param config - Extension configuration
1548
- * @returns New extension instance
1549
- *
1550
- * @example
1551
- * const MyExtension = Extension.create({
1552
- * name: 'myExtension',
1553
- * addOptions() {
1554
- * return { enabled: true };
1555
- * },
1556
- * });
1557
- */
1558
- static create(config) {
1559
- return new _Extension(config);
1560
- }
1561
- /**
1562
- * Creates a new extension with merged options
1563
- * Original extension is not modified
1564
- *
1565
- * **Note:** Options are merged shallowly using object spread (`...`).
1566
- * Nested objects are replaced entirely, not deeply merged.
1567
- *
1568
- * @param options - Options to merge with existing options
1569
- * @returns New extension instance with merged options
1570
- *
1571
- * @example
1572
- * const configured = MyExtension.configure({ enabled: false });
1573
- *
1574
- * @example
1575
- * // Shallow merge behavior with nested objects:
1576
- * // Given: options = { nested: { a: 1, b: 2 } }
1577
- * // configure({ nested: { b: 3 } })
1578
- * // Result: { nested: { b: 3 } } - 'a' is lost!
1579
- * // To preserve nested values, spread manually:
1580
- * // configure({ nested: { ...original.options.nested, b: 3 } })
1581
- */
1582
- configure(options) {
1583
- const newConfig = {
1584
- ...this.config,
1585
- // Override addOptions to return merged options
1586
- addOptions: () => ({
1587
- ...this.options,
1588
- ...options
1589
- })
1590
- };
1591
- return new _Extension(newConfig);
1592
- }
1593
- /**
1594
- * Returns a fresh, unbound copy built from the same `config`: `editor` reset to
1595
- * null and `options`/`storage` re-derived, while `configure()`/`extend()`
1596
- * results are preserved (they live in `config`). Polymorphic: a `Node`/`Mark`
1597
- * clones to its own subclass.
1598
- *
1599
- * `ExtensionManager` clones every extension so each editor owns its instances
1600
- * and binding one editor can't mutate extensions shared with another.
1601
- */
1602
- clone() {
1603
- const Ctor = this.constructor;
1604
- return new Ctor(this.config);
1605
- }
1606
- /**
1607
- * Creates a new extension with extended configuration
1608
- * Original extension is not modified
1609
- *
1610
- * **Note:** Config is merged shallowly using object spread (`...`).
1611
- * Config properties (like `addCommands`, `addKeyboardShortcuts`) are
1612
- * replaced entirely, not combined with the base extension's config.
1613
- *
1614
- * @param extendedConfig - Configuration to extend/override
1615
- * @returns New extension instance with extended config
1616
- *
1617
- * @example
1618
- * const Extended = MyExtension.extend({
1619
- * name: 'extendedExtension',
1620
- * addCommands() {
1621
- * return { customCommand: () => ({ tr }) => true };
1622
- * },
1623
- * });
1624
- *
1625
- * @example
1626
- * // To preserve base extension's commands while adding new ones:
1627
- * const Extended = BaseExtension.extend({
1628
- * addCommands() {
1629
- * const baseCommands = BaseExtension.config.addCommands?.call(this) ?? {};
1630
- * return {
1631
- * ...baseCommands,
1632
- * newCommand: () => ({ tr }) => true,
1633
- * };
1634
- * },
1635
- * });
1636
- */
1637
- extend(extendedConfig) {
1638
- const newConfig = mergeConfigWithParentBinding(this.config, extendedConfig);
1639
- return new _Extension(newConfig);
1640
- }
1641
- };
1642
-
1643
1769
  // src/helpers/specBuilder.ts
1644
1770
  function buildProseMirrorAttrs(attributeSpecs) {
1645
1771
  const attrs = {};
@@ -1810,6 +1936,8 @@ var Mark = class _Mark extends Extension {
1810
1936
  if (this.config.group !== void 0) spec.group = this.config.group;
1811
1937
  if (this.config.spanning !== void 0)
1812
1938
  spec.spanning = this.config.spanning;
1939
+ if (this.config.keepOnDuplicate !== void 0)
1940
+ spec["keepOnDuplicate"] = this.config.keepOnDuplicate;
1813
1941
  const attributeSpecs = callOrReturn(this.config.addAttributes, this);
1814
1942
  if (attributeSpecs) {
1815
1943
  spec.attrs = buildProseMirrorAttrs(attributeSpecs);
@@ -1847,9 +1975,9 @@ var Mark = class _Mark extends Extension {
1847
1975
  const renderFn = this.config.renderHTML;
1848
1976
  const attrSpecs = attributeSpecs;
1849
1977
  const markInstance = this;
1850
- spec.toDOM = (mark, _inline) => {
1851
- const htmlAttrs = attrSpecs ? buildHTMLAttributes(mark.attrs, attrSpecs) : {};
1852
- return renderFn.call(markInstance, { mark, HTMLAttributes: htmlAttrs });
1978
+ spec.toDOM = (mark2, _inline) => {
1979
+ const htmlAttrs = attrSpecs ? buildHTMLAttributes(mark2.attrs, attrSpecs) : {};
1980
+ return renderFn.call(markInstance, { mark: mark2, HTMLAttributes: htmlAttrs });
1853
1981
  };
1854
1982
  }
1855
1983
  return spec;
@@ -1942,8 +2070,8 @@ var setMark = (markName, attributes) => ({ state, tr, dispatch }) => {
1942
2070
  const from = firstRange.$from.pos;
1943
2071
  const existingMark = tr.storedMarks?.find((m) => m.type === markType) ?? state.storedMarks?.find((m) => m.type === markType) ?? tr.doc.resolve(from).marks().find((m) => m.type === markType) ?? null;
1944
2072
  const mergedAttrs = existingMark ? { ...existingMark.attrs, ...attributes } : attributes;
1945
- const mark = markType.create(mergedAttrs);
1946
- tr.addStoredMark(mark);
2073
+ const mark2 = markType.create(mergedAttrs);
2074
+ tr.addStoredMark(mark2);
1947
2075
  dispatch(tr);
1948
2076
  return true;
1949
2077
  }
@@ -2570,12 +2698,12 @@ var updateAttributes = (typeOrName, attributes) => ({ state, tr, dispatch }) =>
2570
2698
  const markType = state.schema.marks[typeOrName];
2571
2699
  tr.doc.nodesBetween(from, to, (node, pos) => {
2572
2700
  if (!node.isInline) return;
2573
- const mark = markType.isInSet(node.marks);
2574
- if (mark) {
2701
+ const mark2 = markType.isInSet(node.marks);
2702
+ if (mark2) {
2575
2703
  markChanges.push({
2576
2704
  pos,
2577
2705
  nodeSize: node.nodeSize,
2578
- attrs: { ...mark.attrs, ...attributes }
2706
+ attrs: { ...mark2.attrs, ...attributes }
2579
2707
  });
2580
2708
  }
2581
2709
  });
@@ -2621,12 +2749,12 @@ var resetAttributes = (typeOrName, attributeName) => ({ state, tr, dispatch }) =
2621
2749
  const defaultValue = markType.spec.attrs?.[attributeName]?.default;
2622
2750
  tr.doc.nodesBetween(from, to, (node, pos) => {
2623
2751
  if (!node.isInline) return;
2624
- const mark = markType.isInSet(node.marks);
2625
- if (mark) {
2752
+ const mark2 = markType.isInSet(node.marks);
2753
+ if (mark2) {
2626
2754
  markChanges.push({
2627
2755
  pos,
2628
2756
  nodeSize: node.nodeSize,
2629
- attrs: { ...mark.attrs, [attributeName]: defaultValue }
2757
+ attrs: { ...mark2.attrs, [attributeName]: defaultValue }
2630
2758
  });
2631
2759
  }
2632
2760
  });
@@ -2675,8 +2803,7 @@ var builtInCommands = {
2675
2803
  function buildCommandProps(options) {
2676
2804
  const { editor, tr, dispatch, chain, can, commands } = options;
2677
2805
  return {
2678
- // Cast required: CommandPropsEditor is a minimal interface, but CommandProps
2679
- // expects full Editor. Callers ensure the actual editor instance is passed.
2806
+ // CommandPropsEditor is intentionally the minimal shape CommandProps consumes here.
2680
2807
  editor,
2681
2808
  state: editor.view.state,
2682
2809
  tr,
@@ -3104,8 +3231,7 @@ var CommandManager = class {
3104
3231
  buildCommandProps(tr, dispatch) {
3105
3232
  const { editor } = this;
3106
3233
  return {
3107
- // Cast needed: CommandManagerEditor is a minimal interface for dependency injection,
3108
- // but CommandProps expects the full Editor type. Callers pass the actual Editor instance.
3234
+ // CommandManagerEditor is intentionally the minimal shape CommandProps consumes here.
3109
3235
  editor,
3110
3236
  state: editor.state,
3111
3237
  tr,
@@ -3257,6 +3383,17 @@ function resolveOverrides(overrides) {
3257
3383
  if (!overrides) return DEFAULTS;
3258
3384
  return { ...DEFAULTS, ...overrides };
3259
3385
  }
3386
+ var BULLET_MARKERS = ["disc", "circle", "square"];
3387
+ var ORDERED_MARKERS = ["decimal", "lower-alpha", "lower-roman"];
3388
+ function listMarkerDepth(el, container) {
3389
+ let depth = 0;
3390
+ for (let parent = el.parentElement; parent !== null && parent !== container; parent = parent.parentElement) {
3391
+ const tag = parent.tagName;
3392
+ if (tag === "TD" || tag === "TH") break;
3393
+ if (tag === "UL" || tag === "OL") depth += 1;
3394
+ }
3395
+ return depth;
3396
+ }
3260
3397
  function applyInlineStyles(container, overrides) {
3261
3398
  const v = resolveOverrides(overrides);
3262
3399
  if (overrides?.codeHighlighter) {
@@ -3338,16 +3475,27 @@ function applyInlineStyles(container, overrides) {
3338
3475
  case "H6":
3339
3476
  styles = "font-size: 0.9em; font-weight: 700; line-height: 1.25; margin: 1.5em 0 0.5em;";
3340
3477
  break;
3478
+ // `type` is not a schema attribute on either list node, so no document
3479
+ // this editor produced carries one.
3341
3480
  case "UL":
3342
3481
  if (el.getAttribute("data-type") === "taskList") {
3343
3482
  styles = "list-style: none; padding-left: 0; margin: 0.75em 0;";
3344
- } else {
3483
+ } else if (el.hasAttribute("type")) {
3345
3484
  styles = "margin: 0.75em 0; padding-left: 1.5em;";
3485
+ } else {
3486
+ const bullet = BULLET_MARKERS[listMarkerDepth(el, container) % 3] ?? "disc";
3487
+ styles = `margin: 0.75em 0; padding-left: 1.5em; list-style-type: ${bullet};`;
3346
3488
  }
3347
3489
  break;
3348
- case "OL":
3349
- styles = "margin: 0.75em 0; padding-left: 1.5em;";
3490
+ case "OL": {
3491
+ if (el.hasAttribute("type")) {
3492
+ styles = "margin: 0.75em 0; padding-left: 1.5em;";
3493
+ break;
3494
+ }
3495
+ const number = ORDERED_MARKERS[listMarkerDepth(el, container) % 3] ?? "decimal";
3496
+ styles = `margin: 0.75em 0; padding-left: 1.5em; list-style-type: ${number};`;
3350
3497
  break;
3498
+ }
3351
3499
  case "LI":
3352
3500
  if (el.getAttribute("data-type") === "taskItem") {
3353
3501
  styles = "display: flex; align-items: flex-start; gap: 0.5em; margin: 0.25em 0;";
@@ -3509,6 +3657,12 @@ var Editor = class _Editor extends EventEmitter {
3509
3657
  * True while EditorView's constructor runs; see buildViewDispatch.
3510
3658
  */
3511
3659
  _isViewConstructing = false;
3660
+ /**
3661
+ * The `.dm-editor` host this editor painted `dm-notion-mode` onto because
3662
+ * of `preset: 'notion'`. Tracked so destroy() removes only a class the
3663
+ * editor itself added, never one the consumer wrote.
3664
+ */
3665
+ _presetClassHost = null;
3512
3666
  /**
3513
3667
  * Creates a new Editor instance
3514
3668
  *
@@ -3528,6 +3682,15 @@ var Editor = class _Editor extends EventEmitter {
3528
3682
  "Editor requires either schema or extensions. Provide a ProseMirror schema directly, or use extensions like [Document, Paragraph, Text]."
3529
3683
  );
3530
3684
  }
3685
+ warnOnDuplicateProseMirrorCopy("prosemirror-model", Fragment, "@domternal/core");
3686
+ warnOnDuplicateProseMirrorCopy("prosemirror-state", Plugin, "@domternal/core");
3687
+ warnOnDuplicateProseMirrorCopy("prosemirror-view", EditorView, "@domternal/core");
3688
+ warnOnDuplicateProseMirrorCopy("prosemirror-transform", Transform, "@domternal/core");
3689
+ warnOnDuplicateProseMirrorCopy(
3690
+ "@domternal/core",
3691
+ ExtensionConfigurationError,
3692
+ "@domternal/core"
3693
+ );
3531
3694
  this.options = {
3532
3695
  editable: true,
3533
3696
  ...options
@@ -3553,6 +3716,43 @@ var Editor = class _Editor extends EventEmitter {
3553
3716
  get isEditable() {
3554
3717
  return this.view ? this.view.editable : this.options.editable ?? true;
3555
3718
  }
3719
+ /**
3720
+ * The resolved editing-experience preset.
3721
+ *
3722
+ * The `preset` option wins when provided (so an explicit 'classic' can
3723
+ * opt out of everything). Otherwise a `dm-notion-mode` class on or above
3724
+ * the view counts as 'notion': consumers that predate the option declare
3725
+ * Notion mode with the theme class alone, and behavior must follow what
3726
+ * the user actually sees. Resolved on every read, not cached, so a class
3727
+ * toggled at runtime is picked up.
3728
+ */
3729
+ get preset() {
3730
+ if (this.options.preset) {
3731
+ return this.options.preset;
3732
+ }
3733
+ if (this.view?.dom.closest(".dm-notion-mode")) {
3734
+ return "notion";
3735
+ }
3736
+ return "classic";
3737
+ }
3738
+ /**
3739
+ * Paints `dm-notion-mode` on the `.dm-editor` host when the editor was
3740
+ * created with `preset: 'notion'`. Runs during creation, and framework
3741
+ * wrappers call it again after adopting the view's DOM: they construct
3742
+ * the editor in a detached element, so the creation-time run cannot see
3743
+ * the host yet. Idempotent; a no-op for any other preset. Only a class
3744
+ * added here is removed again on destroy.
3745
+ */
3746
+ adoptPresetClass() {
3747
+ if (this.options.preset !== "notion" || this._presetClassHost) {
3748
+ return;
3749
+ }
3750
+ const host = this.view.dom.closest(".dm-editor");
3751
+ if (host && !host.classList.contains("dm-notion-mode")) {
3752
+ host.classList.add("dm-notion-mode");
3753
+ this._presetClassHost = host;
3754
+ }
3755
+ }
3556
3756
  /**
3557
3757
  * Checks if the editor content is empty
3558
3758
  */
@@ -3638,11 +3838,11 @@ var Editor = class _Editor extends EventEmitter {
3638
3838
  if (markType) {
3639
3839
  if (selection.empty) {
3640
3840
  const storedMarks = state.storedMarks ?? $from.marks();
3641
- const hasMark = storedMarks.some((mark) => mark.type === markType);
3841
+ const hasMark = storedMarks.some((mark2) => mark2.type === markType);
3642
3842
  if (!hasMark) return false;
3643
3843
  if (attrs) {
3644
- const mark = storedMarks.find((m) => m.type === markType);
3645
- return mark ? this.matchAttributes(mark.attrs, attrs) : false;
3844
+ const mark2 = storedMarks.find((m) => m.type === markType);
3845
+ return mark2 ? this.matchAttributes(mark2.attrs, attrs) : false;
3646
3846
  }
3647
3847
  return true;
3648
3848
  }
@@ -3717,8 +3917,8 @@ var Editor = class _Editor extends EventEmitter {
3717
3917
  const markType = schema.marks[name];
3718
3918
  if (markType) {
3719
3919
  const marks = state.storedMarks ?? $from.marks();
3720
- const mark = marks.find((m) => m.type === markType);
3721
- return mark ? { ...mark.attrs } : {};
3920
+ const mark2 = marks.find((m) => m.type === markType);
3921
+ return mark2 ? { ...mark2.attrs } : {};
3722
3922
  }
3723
3923
  const nodeType = schema.nodes[name];
3724
3924
  if (nodeType) {
@@ -3785,11 +3985,7 @@ var Editor = class _Editor extends EventEmitter {
3785
3985
  */
3786
3986
  getText(options = {}) {
3787
3987
  const { blockSeparator = "\n\n" } = options;
3788
- return this.state.doc.textBetween(
3789
- 0,
3790
- this.state.doc.content.size,
3791
- blockSeparator
3792
- );
3988
+ return this.state.doc.textBetween(0, this.state.doc.content.size, blockSeparator);
3793
3989
  }
3794
3990
  /**
3795
3991
  * Executes a command with proper CommandProps
@@ -3899,6 +4095,10 @@ var Editor = class _Editor extends EventEmitter {
3899
4095
  }
3900
4096
  this.emit("destroy");
3901
4097
  this.options.onDestroy?.();
4098
+ if (this._presetClassHost) {
4099
+ this._presetClassHost.classList.remove("dm-notion-mode");
4100
+ this._presetClassHost = null;
4101
+ }
3902
4102
  this.view.destroy();
3903
4103
  this._extensionManager.destroy();
3904
4104
  this.removeAllListeners();
@@ -3944,10 +4144,7 @@ var Editor = class _Editor extends EventEmitter {
3944
4144
  this._extensionManager.validateSchema();
3945
4145
  let doc;
3946
4146
  try {
3947
- doc = createDocument(
3948
- this.options.content ?? null,
3949
- this._extensionManager.schema
3950
- );
4147
+ doc = createDocument(this.options.content ?? null, this._extensionManager.schema);
3951
4148
  } catch (error) {
3952
4149
  const contentError = error instanceof Error ? error : new Error(String(error));
3953
4150
  this.emit("contentError", {
@@ -3983,7 +4180,10 @@ var Editor = class _Editor extends EventEmitter {
3983
4180
  }),
3984
4181
  ...Object.keys(nodeViews).length > 0 ? { nodeViews } : {},
3985
4182
  // Clipboard transform - apply user-provided transform (e.g. inlineStyles) on copy/cut
3986
- ...this.options.clipboardHTMLTransform ? this.buildClipboardSerializer(this.options.clipboardHTMLTransform, this._extensionManager.schema) : {},
4183
+ ...this.options.clipboardHTMLTransform ? this.buildClipboardSerializer(
4184
+ this.options.clipboardHTMLTransform,
4185
+ this._extensionManager.schema
4186
+ ) : {},
3987
4187
  // Handle focus/blur events
3988
4188
  handleDOMEvents: {
3989
4189
  focus: (_view, event) => {
@@ -4001,6 +4201,7 @@ var Editor = class _Editor extends EventEmitter {
4001
4201
  }
4002
4202
  });
4003
4203
  this._isViewConstructing = false;
4204
+ this.adoptPresetClass();
4004
4205
  this.emit("mount", { editor: this, view: this.view });
4005
4206
  this.options.onMount?.({ editor: this, view: this.view });
4006
4207
  this.commandManager = new CommandManager(this);
@@ -4237,22 +4438,20 @@ function refocusEditorAfterCommand(view) {
4237
4438
  }
4238
4439
 
4239
4440
  // src/utils/defaultBubbleContexts.ts
4240
- var NOTION_MODE_CLASS = "dm-notion-mode";
4241
4441
  var NOTION_TEXT_CONTEXT = Object.freeze([
4242
- // `ai` leads (Notion's "Ask AI"); skipped with its leading separator when
4243
- // the pro extension is absent, exactly like `mathInline`.
4244
4442
  "ai",
4443
+ "comment",
4444
+ "|",
4445
+ "heading",
4446
+ "|",
4447
+ "link",
4245
4448
  "|",
4246
4449
  "bold",
4247
4450
  "italic",
4248
4451
  "underline",
4249
4452
  "strike",
4250
4453
  "code",
4251
- "mathInline",
4252
- "|",
4253
- "link",
4254
- "|",
4255
- "textAlign"
4454
+ "mathInline"
4256
4455
  ]);
4257
4456
  var STANDARD_TEXT_CONTEXT = Object.freeze([
4258
4457
  "bold",
@@ -4265,11 +4464,210 @@ var STANDARD_TEXT_CONTEXT = Object.freeze([
4265
4464
  "link"
4266
4465
  ]);
4267
4466
  function defaultBubbleContexts(editor) {
4268
- const inNotionMode = editor.view.dom.closest("." + NOTION_MODE_CLASS) !== null;
4269
- const text = inNotionMode ? NOTION_TEXT_CONTEXT : STANDARD_TEXT_CONTEXT;
4467
+ const text = editor.preset === "notion" ? NOTION_TEXT_CONTEXT : STANDARD_TEXT_CONTEXT;
4270
4468
  return { text: [...text] };
4271
4469
  }
4272
4470
 
4471
+ // src/utils/collapseSeparators.ts
4472
+ function collapseSeparators(items) {
4473
+ const out = [];
4474
+ for (const item of items) {
4475
+ if (item.type !== "separator") {
4476
+ out.push(item);
4477
+ continue;
4478
+ }
4479
+ if (out.length === 0) continue;
4480
+ if (out[out.length - 1]?.type === "separator") continue;
4481
+ out.push(item);
4482
+ }
4483
+ while (out.length > 0 && out[out.length - 1]?.type === "separator") out.pop();
4484
+ return out.length === items.length ? items : out;
4485
+ }
4486
+
4487
+ // src/utils/bubbleMenuResolver.ts
4488
+ function buildBubbleItemMaps(editor) {
4489
+ const itemMap = /* @__PURE__ */ new Map();
4490
+ const dropdownMap = /* @__PURE__ */ new Map();
4491
+ for (const item of editor.toolbarItems) {
4492
+ if (item.type === "button") {
4493
+ itemMap.set(item.name, item);
4494
+ } else if (item.type === "dropdown") {
4495
+ dropdownMap.set(item.name, item);
4496
+ for (const sub of item.items) {
4497
+ itemMap.set(sub.name, sub);
4498
+ }
4499
+ }
4500
+ }
4501
+ return {
4502
+ itemMap,
4503
+ dropdownMap,
4504
+ bubbleDefaults: buildBubbleDefaults(editor)
4505
+ };
4506
+ }
4507
+ function buildBubbleDefaults(editor) {
4508
+ const byCtx = /* @__PURE__ */ new Map();
4509
+ const addItem = (btn) => {
4510
+ const ctx = btn["bubbleMenu"];
4511
+ if (!ctx) return;
4512
+ let arr = byCtx.get(ctx);
4513
+ if (!arr) {
4514
+ arr = [];
4515
+ byCtx.set(ctx, arr);
4516
+ }
4517
+ arr.push(btn);
4518
+ };
4519
+ for (const item of editor.toolbarItems) {
4520
+ if (item.type === "button") addItem(item);
4521
+ else if (item.type === "dropdown") {
4522
+ for (const sub of item.items) addItem(sub);
4523
+ }
4524
+ }
4525
+ const result = /* @__PURE__ */ new Map();
4526
+ for (const [ctx, ctxItems] of byCtx) {
4527
+ ctxItems.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
4528
+ const list = [];
4529
+ let lastGroup;
4530
+ let sepIdx = 0;
4531
+ for (const item of ctxItems) {
4532
+ if (lastGroup !== void 0 && item.group !== lastGroup) {
4533
+ list.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
4534
+ }
4535
+ list.push(item);
4536
+ lastGroup = item.group;
4537
+ }
4538
+ result.set(ctx, list);
4539
+ }
4540
+ return result;
4541
+ }
4542
+ function resolveBubbleNames(names, itemMap, dropdownMap) {
4543
+ const result = [];
4544
+ let sepIdx = 0;
4545
+ for (const name of names) {
4546
+ if (name === "|") {
4547
+ result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
4548
+ continue;
4549
+ }
4550
+ const dropdown = dropdownMap.get(name);
4551
+ if (dropdown) {
4552
+ result.push(dropdown);
4553
+ continue;
4554
+ }
4555
+ const item = itemMap.get(name);
4556
+ if (item) result.push(item);
4557
+ }
4558
+ return result;
4559
+ }
4560
+ function getBubbleFormatItems(itemMap) {
4561
+ return Array.from(itemMap.values()).filter((item) => item.group === "format").sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
4562
+ }
4563
+ function detectBubbleContext(selection, ctxs) {
4564
+ if ("$anchorCell" in selection) return null;
4565
+ if (selection.node) return selection.node.type.name;
4566
+ if (selection.empty) return null;
4567
+ const fromCell = findCellNode(selection.$from);
4568
+ if (fromCell) {
4569
+ const toCell = findCellNode(selection.$to);
4570
+ if (toCell && fromCell !== toCell) return null;
4571
+ return "table";
4572
+ }
4573
+ const fromName = selection.$from.parent.type.name;
4574
+ if (fromName in ctxs) return fromName;
4575
+ if ("text" in ctxs && selection.$from.parent.type.spec.marks !== "") return "text";
4576
+ const toName = selection.$to.parent.type.name;
4577
+ if (toName in ctxs) return toName;
4578
+ if ("text" in ctxs && selection.$to.parent.type.spec.marks !== "") return "text";
4579
+ return null;
4580
+ }
4581
+ function filterBubbleItemsBySchema(editor, contextName, schemaItems) {
4582
+ if (contextName === "text" || contextName === "table") return schemaItems;
4583
+ const schema = editor.state.schema;
4584
+ if (!schema) return schemaItems;
4585
+ const nodeType = schema.nodes[contextName];
4586
+ if (!nodeType) return schemaItems;
4587
+ return schemaItems.filter((item) => {
4588
+ const markName = typeof item.isActive === "string" ? item.isActive : null;
4589
+ if (!markName) return true;
4590
+ const markType = schema.marks[markName];
4591
+ if (!markType) return true;
4592
+ return nodeType.allowsMarkType(markType);
4593
+ });
4594
+ }
4595
+ function isInsideTableCell($pos) {
4596
+ for (let d = $pos.depth; d > 0; d--) {
4597
+ const name = $pos.node(d).type.name;
4598
+ if (name === "tableCell" || name === "tableHeader") return true;
4599
+ }
4600
+ return false;
4601
+ }
4602
+ function findCellNode(pos) {
4603
+ for (let d = pos.depth; d > 0; d--) {
4604
+ const node = pos.node(d);
4605
+ if (node.type.name === "tableCell" || node.type.name === "tableHeader") return node;
4606
+ }
4607
+ return null;
4608
+ }
4609
+ function resolveBubbleMenuItems(options) {
4610
+ return collapseSeparators(pickBubbleMenuItems(options));
4611
+ }
4612
+ function pickBubbleMenuItems({
4613
+ editor,
4614
+ maps,
4615
+ contexts,
4616
+ fallbackItems
4617
+ }) {
4618
+ const selection = editor.state.selection;
4619
+ if (contexts) {
4620
+ const ctx = detectBubbleContext(selection, contexts);
4621
+ if (!ctx) return [];
4622
+ if (ctx in contexts) {
4623
+ const val = contexts[ctx];
4624
+ if (val === null || Array.isArray(val) && val.length === 0) return [];
4625
+ if (val === true) {
4626
+ return filterBubbleItemsBySchema(editor, ctx, getBubbleFormatItems(maps.itemMap));
4627
+ }
4628
+ if (Array.isArray(val)) {
4629
+ const resolved = resolveBubbleNames(val, maps.itemMap, maps.dropdownMap);
4630
+ const buttons = resolved.filter(
4631
+ (item) => item.type !== "separator"
4632
+ );
4633
+ const allowed = new Set(
4634
+ filterBubbleItemsBySchema(editor, ctx, buttons).map((item) => item.name)
4635
+ );
4636
+ return resolved.filter(
4637
+ (item) => item.type === "separator" || allowed.has(item.name)
4638
+ );
4639
+ }
4640
+ }
4641
+ return maps.bubbleDefaults.get(ctx) ?? [];
4642
+ }
4643
+ if (selection.node && maps.bubbleDefaults.has(selection.node.type.name)) {
4644
+ return maps.bubbleDefaults.get(selection.node.type.name) ?? [];
4645
+ }
4646
+ return fallbackItems;
4647
+ }
4648
+ function createBubbleShouldShow(maps, contexts) {
4649
+ if (contexts) {
4650
+ return ({ state }) => {
4651
+ const selection = state.selection;
4652
+ const ctx = detectBubbleContext(selection, contexts);
4653
+ if (!ctx) return false;
4654
+ if (ctx in contexts) {
4655
+ const val = contexts[ctx];
4656
+ if (val === null) return false;
4657
+ return val === true || Array.isArray(val) && val.length > 0;
4658
+ }
4659
+ return maps.bubbleDefaults.has(ctx);
4660
+ };
4661
+ }
4662
+ return ({ state }) => {
4663
+ const selection = state.selection;
4664
+ if (selection.empty) return false;
4665
+ if (selection.node) return maps.bubbleDefaults.has(selection.node.type.name);
4666
+ if (isInsideTableCell(selection.$from)) return false;
4667
+ return selection.$from.parent.type.spec.marks !== "" || selection.$to.parent.type.spec.marks !== "";
4668
+ };
4669
+ }
4670
+
4273
4671
  // src/utils/insertAsListItemChild.ts
4274
4672
  var LIST_ITEM_TYPES3 = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
4275
4673
  var LIST_WRAPPER_TYPES2 = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
@@ -5025,7 +5423,8 @@ function groupFloatingMenuItems(items) {
5025
5423
  if (!list) {
5026
5424
  list = [];
5027
5425
  map.set(name, list);
5028
- order.push(name);
5426
+ if (name === "") order.unshift(name);
5427
+ else order.push(name);
5029
5428
  }
5030
5429
  list.push(item);
5031
5430
  }
@@ -5237,12 +5636,13 @@ var FloatingMenuController = class _FloatingMenuController {
5237
5636
  */
5238
5637
  updateDisabledStates() {
5239
5638
  let changed = false;
5240
- let canProxy = null;
5241
- try {
5242
- canProxy = this.editor.can();
5243
- } catch {
5244
- canProxy = null;
5245
- }
5639
+ const canProxy = (() => {
5640
+ try {
5641
+ return this.editor.can();
5642
+ } catch {
5643
+ return null;
5644
+ }
5645
+ })();
5246
5646
  for (const item of this._flatItems) {
5247
5647
  const was = this._disabledMap.get(item.name) ?? false;
5248
5648
  let now = false;
@@ -5559,7 +5959,8 @@ var defaultIcons = {
5559
5959
  plus: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,128a8,8,0,0,1-8,8H136v80a8,8,0,0,1-16,0V136H40a8,8,0,0,1,0-16h80V40a8,8,0,0,1,16,0v80h80A8,8,0,0,1,224,128Z"/></svg>',
5560
5960
  dotsSixVertical: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M108,60A16,16,0,1,1,92,44,16,16,0,0,1,108,60Zm56,16A16,16,0,1,0,148,60,16,16,0,0,0,164,76ZM92,112a16,16,0,1,0,16,16A16,16,0,0,0,92,112Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,112ZM92,180a16,16,0,1,0,16,16A16,16,0,0,0,92,180Zm72,0a16,16,0,1,0,16,16A16,16,0,0,0,164,180Z"/></svg>',
5561
5961
  dotsThree: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M140,128a12,12,0,1,1-12-12A12,12,0,0,1,140,128ZM64,116a12,12,0,1,0,12,12A12,12,0,0,0,64,116Zm128,0a12,12,0,1,0,12,12A12,12,0,0,0,192,116Z"/></svg>',
5562
- copy: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"/></svg>'
5962
+ copy: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"/></svg>',
5963
+ printer: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M214.67,72H200V40a8,8,0,0,0-8-8H64a8,8,0,0,0-8,8V72H41.33C27.36,72,16,82.77,16,96v80a8,8,0,0,0,8,8H56v32a8,8,0,0,0,8,8H192a8,8,0,0,0,8-8V184h32a8,8,0,0,0,8-8V96C240,82.77,228.64,72,214.67,72ZM72,48H184V72H72ZM184,208H72V160H184Zm40-40H200V152a8,8,0,0,0-8-8H64a8,8,0,0,0-8,8v16H32V96c0-4.34,4.28-8,9.33-8H214.67c5.05,0,9.33,3.66,9.33,8Zm-24-52a12,12,0,1,1-12-12A12,12,0,0,1,200,116Z"/></svg>'
5563
5964
  };
5564
5965
 
5565
5966
  // src/nodes/Document.ts
@@ -7091,6 +7492,7 @@ var TaskList = Node2.create({
7091
7492
  // src/marks/Bold.ts
7092
7493
  var Bold = Mark.create({
7093
7494
  name: "bold",
7495
+ group: "formatting",
7094
7496
  addOptions() {
7095
7497
  return {
7096
7498
  HTMLAttributes: {}
@@ -7168,6 +7570,7 @@ var Bold = Mark.create({
7168
7570
  // src/marks/Italic.ts
7169
7571
  var Italic = Mark.create({
7170
7572
  name: "italic",
7573
+ group: "formatting",
7171
7574
  addOptions() {
7172
7575
  return {
7173
7576
  HTMLAttributes: {}
@@ -7247,6 +7650,7 @@ var Italic = Mark.create({
7247
7650
  // src/marks/Underline.ts
7248
7651
  var Underline = Mark.create({
7249
7652
  name: "underline",
7653
+ group: "formatting",
7250
7654
  addOptions() {
7251
7655
  return {
7252
7656
  HTMLAttributes: {}
@@ -7299,6 +7703,7 @@ var Underline = Mark.create({
7299
7703
  // src/marks/Strike.ts
7300
7704
  var Strike = Mark.create({
7301
7705
  name: "strike",
7706
+ group: "formatting",
7302
7707
  addOptions() {
7303
7708
  return {
7304
7709
  HTMLAttributes: {}
@@ -7370,9 +7775,14 @@ var Code = Mark.create({
7370
7775
  HTMLAttributes: {}
7371
7776
  };
7372
7777
  },
7373
- // Code mark is exclusive - it cannot be combined with other marks
7374
- // ProseMirror uses '_' to mean "exclude all marks"
7375
- excludes: "_",
7778
+ // Code cannot be combined with other FORMATTING marks (bold, italic,
7779
+ // color...), but semantic marks outside the group (link, a comment
7780
+ // thread anchor) survive: code excludes the 'formatting' group instead
7781
+ // of '_' (everything). Third-party formatting marks opt into the same
7782
+ // exclusion by declaring group: 'formatting'. Code is in the group
7783
+ // itself, which also keeps it self-exclusive.
7784
+ group: "formatting",
7785
+ excludes: "formatting",
7376
7786
  // Code should not span across multiple nodes
7377
7787
  spanning: false,
7378
7788
  parseHTML() {
@@ -7433,7 +7843,7 @@ function linkClickPlugin(options) {
7433
7843
  if (!view.editable) {
7434
7844
  return false;
7435
7845
  }
7436
- let link = null;
7846
+ let link;
7437
7847
  if (event.target instanceof HTMLAnchorElement) {
7438
7848
  link = event.target;
7439
7849
  } else {
@@ -7860,6 +8270,7 @@ var Link = Mark.create({
7860
8270
  // src/marks/Subscript.ts
7861
8271
  var Subscript = Mark.create({
7862
8272
  name: "subscript",
8273
+ group: "formatting",
7863
8274
  // Mutual exclusion handled in toggle commands (not schema)
7864
8275
  // so can() dry-run works correctly for toolbar disabled state
7865
8276
  excludes: "",
@@ -7921,6 +8332,7 @@ var Subscript = Mark.create({
7921
8332
  // src/marks/Superscript.ts
7922
8333
  var Superscript = Mark.create({
7923
8334
  name: "superscript",
8335
+ group: "formatting",
7924
8336
  // Mutual exclusion handled in toggle commands (not schema)
7925
8337
  // so can() dry-run works correctly for toolbar disabled state
7926
8338
  excludes: "",
@@ -7982,6 +8394,7 @@ var Superscript = Mark.create({
7982
8394
  // src/marks/TextStyle.ts
7983
8395
  var TextStyle = Mark.create({
7984
8396
  name: "textStyle",
8397
+ group: "formatting",
7985
8398
  // Lower priority so it renders after other marks
7986
8399
  priority: 101,
7987
8400
  addOptions() {
@@ -8038,7 +8451,7 @@ var TextStyle = Mark.create({
8038
8451
  let hasEmptyTextStyle = false;
8039
8452
  tr.doc.nodesBetween(from, to, (node) => {
8040
8453
  const textStyleMark = node.marks.find(
8041
- (mark) => mark.type.name === this.name
8454
+ (mark2) => mark2.type.name === this.name
8042
8455
  );
8043
8456
  if (textStyleMark) {
8044
8457
  const hasNonNullAttr = Object.values(textStyleMark.attrs).some(
@@ -8052,9 +8465,9 @@ var TextStyle = Mark.create({
8052
8465
  if (!hasEmptyTextStyle) return false;
8053
8466
  if (dispatch) {
8054
8467
  tr.doc.nodesBetween(from, to, (node, pos) => {
8055
- const mark = node.marks.find((m) => m.type === markType);
8056
- if (mark) {
8057
- const hasActiveAttr = Object.values(mark.attrs).some(
8468
+ const mark2 = node.marks.find((m) => m.type === markType);
8469
+ if (mark2) {
8470
+ const hasActiveAttr = Object.values(mark2.attrs).some(
8058
8471
  (v) => v !== null && v !== void 0
8059
8472
  );
8060
8473
  if (!hasActiveAttr) {
@@ -8559,11 +8972,7 @@ var Typography = Extension.create({
8559
8972
  rules.push(
8560
8973
  new InputRule(/"([^"]+)"$/, (state, match, start, end) => {
8561
8974
  const text = match[1] ?? "";
8562
- return state.tr.replaceWith(
8563
- start,
8564
- end,
8565
- state.schema.text(openDoubleQuote + text + closeDoubleQuote)
8566
- );
8975
+ return state.tr.insertText(openDoubleQuote + text + closeDoubleQuote, start, end);
8567
8976
  })
8568
8977
  );
8569
8978
  rules.push(
@@ -8571,12 +8980,10 @@ var Typography = Extension.create({
8571
8980
  const text = match[1] ?? "";
8572
8981
  const prefix = match[0].charAt(0);
8573
8982
  const hasPrefix = prefix !== "'";
8574
- return state.tr.replaceWith(
8983
+ return state.tr.insertText(
8984
+ hasPrefix ? prefix + openSingleQuote + text + closeSingleQuote : openSingleQuote + text + closeSingleQuote,
8575
8985
  start,
8576
- end,
8577
- state.schema.text(
8578
- hasPrefix ? prefix + openSingleQuote + text + closeSingleQuote : openSingleQuote + text + closeSingleQuote
8579
- )
8986
+ end
8580
8987
  );
8581
8988
  })
8582
8989
  );
@@ -8841,6 +9248,9 @@ function generateUUID() {
8841
9248
  });
8842
9249
  }
8843
9250
  var uniqueIDPluginKey = new PluginKey("uniqueID");
9251
+ function isWithin(from, to, ranges) {
9252
+ return ranges.some((range) => from >= range.from && to <= range.to);
9253
+ }
8844
9254
  var UniqueID = Extension.create({
8845
9255
  name: "uniqueID",
8846
9256
  addOptions() {
@@ -8890,12 +9300,27 @@ var UniqueID = Extension.create({
8890
9300
  addProseMirrorPlugins() {
8891
9301
  const { types, attributeName, generateID, filterDuplicates } = this.options;
8892
9302
  const editor = this.editor;
9303
+ const replacedRanges = (view) => {
9304
+ if (view?.dragging) return [];
9305
+ const ranges = view?.state?.selection?.ranges;
9306
+ if (ranges === void 0) return [];
9307
+ const replaced = [];
9308
+ for (const range of ranges) {
9309
+ if (range.$to.pos > range.$from.pos) {
9310
+ replaced.push({ from: range.$from.pos, to: range.$to.pos });
9311
+ }
9312
+ }
9313
+ return replaced;
9314
+ };
8893
9315
  const transformPastedSlice = (slice, view) => {
8894
9316
  if (view?.dragging?.move === true) return slice;
8895
9317
  const existingIDs = /* @__PURE__ */ new Set();
8896
- editor?.state.doc.descendants((node) => {
9318
+ const replaced = replacedRanges(view);
9319
+ editor?.state.doc.descendants((node, pos) => {
9320
+ if (isWithin(pos, pos + node.nodeSize, replaced)) return false;
8897
9321
  const id = node.attrs[attributeName];
8898
9322
  if (id) existingIDs.add(id);
9323
+ return true;
8899
9324
  });
8900
9325
  const transformNode = (node) => {
8901
9326
  if (!types.includes(node.type.name)) {
@@ -9745,13 +10170,13 @@ var Highlight = Extension.create({
9745
10170
  let hasHighlight = false;
9746
10171
  if (empty) {
9747
10172
  const marks = state.storedMarks ?? state.doc.resolve(from).marks();
9748
- const mark = markType.isInSet(marks);
9749
- hasHighlight = !!mark?.attrs["backgroundColor"] || !!mark?.attrs["backgroundColorToken"];
10173
+ const mark2 = markType.isInSet(marks);
10174
+ hasHighlight = !!mark2?.attrs["backgroundColor"] || !!mark2?.attrs["backgroundColorToken"];
9750
10175
  } else {
9751
10176
  state.doc.nodesBetween(from, to, (node) => {
9752
10177
  if (hasHighlight) return false;
9753
- const mark = markType.isInSet(node.marks);
9754
- if (mark?.attrs["backgroundColor"] || mark?.attrs["backgroundColorToken"]) {
10178
+ const mark2 = markType.isInSet(node.marks);
10179
+ if (mark2?.attrs["backgroundColor"] || mark2?.attrs["backgroundColorToken"]) {
9755
10180
  hasHighlight = true;
9756
10181
  return false;
9757
10182
  }
@@ -9798,7 +10223,8 @@ var Highlight = Extension.create({
9798
10223
  const content = match[1];
9799
10224
  if (!content) return null;
9800
10225
  const { tr } = state;
9801
- tr.replaceWith(start, end, state.schema.text(content));
10226
+ const marks = tr.storedMarks ?? tr.doc.resolve(start).marksAcross(tr.doc.resolve(end));
10227
+ tr.replaceWith(start, end, state.schema.text(content, marks));
9802
10228
  tr.addMark(
9803
10229
  start,
9804
10230
  start + content.length,
@@ -10094,6 +10520,153 @@ var ClearFormatting = Extension.create({
10094
10520
  ];
10095
10521
  }
10096
10522
  });
10523
+
10524
+ // src/extensions/Print.ts
10525
+ var ROOT_CLASS = "dm-print-root";
10526
+ var ANCESTOR_CLASS = "dm-print-ancestor";
10527
+ var PRINTING_CLASS = "dm-printing";
10528
+ var marked = /* @__PURE__ */ new Set();
10529
+ var printing = false;
10530
+ var Print = Extension.create({
10531
+ name: "print",
10532
+ addOptions() {
10533
+ return {
10534
+ toolbar: true,
10535
+ root: null,
10536
+ isolateNativePrint: false
10537
+ };
10538
+ },
10539
+ addStorage() {
10540
+ return { cleanup: null };
10541
+ },
10542
+ addCommands() {
10543
+ return {
10544
+ printDocument: () => ({ dispatch }) => {
10545
+ if (!dispatch) return true;
10546
+ const editor = this.editor;
10547
+ if (!editor || typeof window === "undefined") return false;
10548
+ const root = resolveRoot(editor, this.options.root);
10549
+ if (!root) return false;
10550
+ mark(root);
10551
+ try {
10552
+ emit(editor, "beforePrint", { root });
10553
+ window.print();
10554
+ } finally {
10555
+ unmark();
10556
+ emit(editor, "afterPrint", void 0);
10557
+ }
10558
+ return true;
10559
+ }
10560
+ };
10561
+ },
10562
+ addToolbarItems() {
10563
+ if (!this.options.toolbar) return [];
10564
+ return [
10565
+ {
10566
+ type: "button",
10567
+ name: "print",
10568
+ command: "printDocument",
10569
+ icon: "printer",
10570
+ label: "Print",
10571
+ shortcut: "Mod-P",
10572
+ group: "document",
10573
+ priority: 100,
10574
+ // Reading a document out to paper is not editing it, so the button
10575
+ // stays live in a read-only editor.
10576
+ allowReadOnly: true
10577
+ }
10578
+ ];
10579
+ },
10580
+ addKeyboardShortcuts() {
10581
+ return {
10582
+ // Only bound while the caret is in the editor, which is exactly when
10583
+ // the reader means "print this document" rather than "print this
10584
+ // page". Everywhere else the browser's own Ctrl/Cmd+P is untouched.
10585
+ "Mod-p": () => this.editor?.commands.printDocument() ?? false
10586
+ };
10587
+ },
10588
+ onCreate() {
10589
+ if (!this.options.isolateNativePrint) return;
10590
+ if (typeof window === "undefined") return;
10591
+ const editor = this.editor;
10592
+ if (!editor) return;
10593
+ const resolve = this.options.root;
10594
+ const before = () => {
10595
+ if (printing) return;
10596
+ const root = resolveRoot(editor, resolve);
10597
+ if (!root) return;
10598
+ mark(root);
10599
+ emit(editor, "beforePrint", { root });
10600
+ };
10601
+ const after = () => {
10602
+ if (!printing) return;
10603
+ unmark();
10604
+ emit(editor, "afterPrint", void 0);
10605
+ };
10606
+ window.addEventListener("beforeprint", before);
10607
+ window.addEventListener("afterprint", after);
10608
+ let detachMedia = null;
10609
+ const onMediaChange = (event) => {
10610
+ if (event.matches) before();
10611
+ else after();
10612
+ };
10613
+ if (typeof window.matchMedia === "function") {
10614
+ const media = window.matchMedia("print");
10615
+ if (typeof media.addEventListener === "function") {
10616
+ media.addEventListener("change", onMediaChange);
10617
+ detachMedia = () => {
10618
+ media.removeEventListener("change", onMediaChange);
10619
+ };
10620
+ }
10621
+ }
10622
+ this.storage.cleanup = () => {
10623
+ window.removeEventListener("beforeprint", before);
10624
+ window.removeEventListener("afterprint", after);
10625
+ detachMedia?.();
10626
+ };
10627
+ },
10628
+ onDestroy() {
10629
+ this.storage.cleanup?.();
10630
+ this.storage.cleanup = null;
10631
+ unmark();
10632
+ }
10633
+ });
10634
+ function resolveRoot(editor, resolve) {
10635
+ if (resolve) return resolve(editor);
10636
+ const dom = editor.view.dom;
10637
+ return dom.closest(".dm-editor") ?? dom;
10638
+ }
10639
+ function mark(root) {
10640
+ root.classList.add(ROOT_CLASS);
10641
+ marked.add(root);
10642
+ let node = parentOf(root);
10643
+ while (node) {
10644
+ node.classList.add(ANCESTOR_CLASS);
10645
+ marked.add(node);
10646
+ node = parentOf(node);
10647
+ }
10648
+ document.body.classList.add(PRINTING_CLASS);
10649
+ printing = true;
10650
+ }
10651
+ function parentOf(node) {
10652
+ if (node.parentElement) return node.parentElement;
10653
+ if (typeof ShadowRoot === "undefined") return null;
10654
+ const root = node.getRootNode();
10655
+ return root instanceof ShadowRoot ? root.host : null;
10656
+ }
10657
+ function unmark() {
10658
+ printing = false;
10659
+ if (typeof document === "undefined") return;
10660
+ document.body.classList.remove(PRINTING_CLASS);
10661
+ for (const el of marked) {
10662
+ el.classList.remove(ROOT_CLASS, ANCESTOR_CLASS);
10663
+ }
10664
+ marked.clear();
10665
+ }
10666
+ function emit(editor, name, payload) {
10667
+ const bus = editor;
10668
+ bus.emit?.(name, payload);
10669
+ }
10097
10670
  var linkPopoverPluginKey = new PluginKey("linkPopover");
10098
10671
  function linkPopoverPlugin({ editor, markType, protocols }) {
10099
10672
  const el = document.createElement("div");
@@ -10431,9 +11004,10 @@ function createBubbleMenuPlugin(options) {
10431
11004
  const onDocumentMousedown = (e) => {
10432
11005
  const target = e.target;
10433
11006
  if (!target) return;
11007
+ if (!target.isConnected) return;
10434
11008
  if (element.contains(target)) return;
10435
11009
  if (editor.view.dom.contains(target)) return;
10436
- if (target instanceof HTMLElement && target.closest("[data-dm-editor-ui]")) return;
11010
+ if (target instanceof Element && target.closest("[data-dm-editor-ui]")) return;
10437
11011
  hideMenu();
10438
11012
  suppressed = true;
10439
11013
  };
@@ -10667,8 +11241,8 @@ var StarterKit = Extension.create({
10667
11241
  });
10668
11242
 
10669
11243
  // src/index.ts
10670
- var VERSION = "0.14.0";
11244
+ var VERSION = "1.0.0";
10671
11245
 
10672
- export { BaseKeymap, BlockColor, Blockquote, Bold, BubbleMenu, BulletList, CanChecker, ChainBuilder, CharacterCount, ClearFormatting, Code, CodeBlock, CommandManager, DEFAULT_BLOCK_COLORS, DEFAULT_BLOCK_COLOR_TYPES, DEFAULT_HIGHLIGHT_COLORS, DEFAULT_NOTION_COLOR_PALETTE, DEFAULT_TEXT_COLORS, Document, Dropcursor, Editor, EventEmitter, Extension, ExtensionConfigurationError, ExtensionManager, FLOATING_MENU_META, FLOATING_MENU_NO_FOCUS, FloatingMenuController, Focus, FontFamily, FontSize, Gapcursor, HardBreak, Heading, Highlight, History, HorizontalRule, InvisibleChars, Italic, LIST_ITEM_TYPE_NAMES, LineHeight, Link, LinkPopover, ListIndent, ListItem, ListKeymap, Mark, Node2 as Node, NotionColorPicker, OrderedList, Paragraph, Placeholder, Selection5 as Selection, SelectionDecoration, StarterKit, Strike, Subscript, Superscript, TaskItem, TaskList, Text, TextAlign, TextColor, TextStyle, ToolbarController, TrailingNode, Typography, Underline, UniqueID, VERSION, announce, applyInlineStyles, autolinkPlugin, autolinkPluginKey, blur, bubbleMenuPluginKey, buildCommandProps, builtInCommands, callOrReturn, characterCountPluginKey, clearContent, copyThemeClass, createAccumulatingDispatch, createBubbleMenuPlugin, createCanChecker, createChainBuilder, createDocument, createFloatingMenuPlugin, defaultBlockAt, defaultBubbleContexts, defaultFloatingMenuShouldShow, defaultIcons, deleteSelection, findChildren, findListItemAncestorDepth, findParentNode, floatingMenuPluginKey, focus, focusPluginKey, generateHTML, generateJSON, generateText, getListItemCursorContext, getMarkRange, groupFloatingMenuItems, hideFloatingMenu, indentBlockAsListChild, inlineStyles, insertAsListItemChild, insertChildrenZoneSibling, insertContent, insertText, invisibleCharsPluginKey, isDocumentEmpty, isInListItemLabel, isInsideListItem, isNodeEmpty, isValidUrl, lift, liftCurrentListItem, liftEmptyChildrenZoneParagraph, linkClickPlugin, linkClickPluginKey, linkExitPlugin, linkExitPluginKey, linkPastePlugin, linkPastePluginKey, markInputRule, markInputRulePatterns, nodeInputRule, outdentBlockFromListItem, placeholderPluginKey, positionFloating, positionFloatingOnce, refocusEditorAfterCommand, resetAttributes, selectAll, selectNodeBackward, selectionDecorationPluginKey, setBlockType, setContent, setMark, showFloatingMenu, splitListForInsert, stripInlineColorConflicts, textInputRule, textblockTypeInputRule, toggleBlockType, toggleList, toggleMark, toggleWrap, uniqueIDPluginKey, unsetAllMarks, unsetMark, updateAttributes, wrapIn, wrappingInputRule, writeToClipboard };
11246
+ export { BaseKeymap, BlockColor, Blockquote, Bold, BubbleMenu, BulletList, CanChecker, ChainBuilder, CharacterCount, ClearFormatting, Code, CodeBlock, CommandManager, DEFAULT_BLOCK_COLORS, DEFAULT_BLOCK_COLOR_TYPES, DEFAULT_HIGHLIGHT_COLORS, DEFAULT_NOTION_COLOR_PALETTE, DEFAULT_TEXT_COLORS, Document, Dropcursor, Editor, EventEmitter, Extension, ExtensionConfigurationError, ExtensionManager, FLOATING_MENU_META, FLOATING_MENU_NO_FOCUS, FloatingMenuController, Focus, FontFamily, FontSize, Gapcursor, HardBreak, Heading, Highlight, History, HorizontalRule, InvisibleChars, Italic, LIST_ITEM_TYPE_NAMES, LineHeight, Link, LinkPopover, ListIndent, ListItem, ListKeymap, Mark, Node2 as Node, NotionColorPicker, OrderedList, Paragraph, Placeholder, Print, Selection5 as Selection, SelectionDecoration, StarterKit, Strike, Subscript, Superscript, TaskItem, TaskList, Text, TextAlign, TextColor, TextStyle, ToolbarController, TrailingNode, Typography, Underline, UniqueID, VERSION, announce, applyInlineStyles, assertSingleProseMirrorCopy, autolinkPlugin, autolinkPluginKey, blur, bubbleMenuPluginKey, buildBubbleItemMaps, buildCommandProps, builtInCommands, callOrReturn, characterCountPluginKey, clearContent, collapseSeparators, copyThemeClass, createAccumulatingDispatch, createBubbleMenuPlugin, createBubbleShouldShow, createCanChecker, createChainBuilder, createDocument, createFloatingMenuPlugin, defaultBlockAt, defaultBubbleContexts, defaultFloatingMenuShouldShow, defaultIcons, deleteSelection, detectBubbleContext, filterBubbleItemsBySchema, findChildren, findListItemAncestorDepth, findParentNode, floatingMenuPluginKey, focus, focusPluginKey, generateHTML, generateJSON, generateText, getBubbleFormatItems, getListItemCursorContext, getMarkRange, groupFloatingMenuItems, hideFloatingMenu, indentBlockAsListChild, inlineStyles, insertAsListItemChild, insertChildrenZoneSibling, insertContent, insertText, invisibleCharsPluginKey, isDocumentEmpty, isInListItemLabel, isInsideListItem, isInsideTableCell, isNodeEmpty, isValidUrl, lift, liftCurrentListItem, liftEmptyChildrenZoneParagraph, linkClickPlugin, linkClickPluginKey, linkExitPlugin, linkExitPluginKey, linkPastePlugin, linkPastePluginKey, markInputRule, markInputRulePatterns, nodeInputRule, outdentBlockFromListItem, placeholderPluginKey, positionFloating, positionFloatingOnce, refocusEditorAfterCommand, registerProseMirrorCopy, resetAttributes, resolveBubbleMenuItems, resolveBubbleNames, selectAll, selectNodeBackward, selectionDecorationPluginKey, setBlockType, setContent, setMark, showFloatingMenu, splitListForInsert, stripInlineColorConflicts, textInputRule, textblockTypeInputRule, toggleBlockType, toggleList, toggleMark, toggleWrap, uniqueIDPluginKey, unsetAllMarks, unsetMark, updateAttributes, warnOnDuplicateProseMirrorCopy, wrapIn, wrappingInputRule, writeToClipboard };
10673
11247
  //# sourceMappingURL=index.js.map
10674
11248
  //# sourceMappingURL=index.js.map