@domternal/core 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -271,6 +271,10 @@ var ExtensionManager = class {
271
271
  * Cached toolbar items (collected lazily)
272
272
  */
273
273
  _toolbarItems = null;
274
+ /**
275
+ * Cached floating-menu items (collected lazily)
276
+ */
277
+ _floatingMenuItems = null;
274
278
  /**
275
279
  * Cached node views (collected lazily)
276
280
  */
@@ -344,6 +348,14 @@ var ExtensionManager = class {
344
348
  this._toolbarItems ??= this.collectToolbarItems();
345
349
  return this._toolbarItems;
346
350
  }
351
+ /**
352
+ * Gets floating-menu items from all extensions
353
+ * Cached after first call
354
+ */
355
+ get floatingMenuItems() {
356
+ this._floatingMenuItems ??= this.collectFloatingMenuItems();
357
+ return this._floatingMenuItems;
358
+ }
347
359
  /**
348
360
  * Gets node views from all Node extensions that define addNodeView
349
361
  */
@@ -360,6 +372,7 @@ var ExtensionManager = class {
360
372
  this._plugins = null;
361
373
  this._commands = null;
362
374
  this._toolbarItems = null;
375
+ this._floatingMenuItems = null;
363
376
  this._nodeViews = null;
364
377
  }
365
378
  // === Extension Processing ===
@@ -406,7 +419,7 @@ var ExtensionManager = class {
406
419
  });
407
420
  }
408
421
  /**
409
- * Detects duplicate extension names (AD-7: Schema Conflict Detection)
422
+ * Detects duplicate extension names.
410
423
  * @throws Error if duplicate names found
411
424
  */
412
425
  detectConflicts() {
@@ -707,6 +720,25 @@ var ExtensionManager = class {
707
720
  }
708
721
  return items;
709
722
  }
723
+ /**
724
+ * Collects floating-menu items from all extensions via `addFloatingMenuItems()`.
725
+ */
726
+ collectFloatingMenuItems() {
727
+ const items = [];
728
+ for (const ext of this._extensions) {
729
+ const addItems = ext.config.addFloatingMenuItems;
730
+ if (addItems) {
731
+ const extItems = this.safeCall(
732
+ () => callOrReturn(addItems, ext),
733
+ `${ext.name}.addFloatingMenuItems`
734
+ );
735
+ if (extItems && extItems.length > 0) {
736
+ items.push(...extItems);
737
+ }
738
+ }
739
+ }
740
+ return items;
741
+ }
710
742
  /**
711
743
  * Collects node views from all Node extensions.
712
744
  * Returns a map of node name to NodeViewConstructor for EditorView.
@@ -1477,7 +1509,7 @@ var Extension = class _Extension {
1477
1509
  * // Shallow merge behavior with nested objects:
1478
1510
  * // Given: options = { nested: { a: 1, b: 2 } }
1479
1511
  * // configure({ nested: { b: 3 } })
1480
- * // Result: { nested: { b: 3 } } 'a' is lost!
1512
+ * // Result: { nested: { b: 3 } } - 'a' is lost!
1481
1513
  * // To preserve nested values, spread manually:
1482
1514
  * // configure({ nested: { ...original.options.nested, b: 3 } })
1483
1515
  */
@@ -1631,25 +1663,12 @@ var Mark = class _Mark extends Extension {
1631
1663
  return new _Mark(config);
1632
1664
  }
1633
1665
  /**
1634
- * Creates a new mark with merged options
1635
- * Original mark is not modified
1636
- *
1637
- * **Note:** Options are merged shallowly using object spread (`...`).
1638
- * Nested objects are replaced entirely, not deeply merged.
1639
- *
1640
- * @param options - Options to merge with existing options
1641
- * @returns New mark instance with merged options
1666
+ * Creates a new mark with merged options. Original mark is not modified.
1667
+ * Options merge shallowly (object spread); see {@link Extension.configure}
1668
+ * for the nested-object gotcha and a workaround.
1642
1669
  *
1643
1670
  * @example
1644
1671
  * const CustomBold = Bold.configure({ HTMLAttributes: { class: 'custom-bold' } });
1645
- *
1646
- * @example
1647
- * // Shallow merge behavior with nested objects:
1648
- * // Given: options = { HTMLAttributes: { class: 'a', id: 'b' } }
1649
- * // configure({ HTMLAttributes: { class: 'c' } })
1650
- * // Result: { HTMLAttributes: { class: 'c' } } — 'id' is lost!
1651
- * // To preserve nested values, spread manually:
1652
- * // configure({ HTMLAttributes: { ...original.options.HTMLAttributes, class: 'c' } })
1653
1672
  */
1654
1673
  configure(options) {
1655
1674
  const { isFormatting, ...restOptions } = options;
@@ -1924,6 +1943,40 @@ var unsetAllMarks = () => ({ state, tr, dispatch, editor }) => {
1924
1943
  dispatch(tr);
1925
1944
  return true;
1926
1945
  };
1946
+
1947
+ // src/utils/listItemAncestor.ts
1948
+ var LIST_ITEM_TYPE_NAMES = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
1949
+ function findListItemAncestorDepth($pos) {
1950
+ for (let d = $pos.depth; d >= 1; d--) {
1951
+ if (LIST_ITEM_TYPE_NAMES.has($pos.node(d).type.name)) return d;
1952
+ }
1953
+ return -1;
1954
+ }
1955
+ function isInsideListItem($pos) {
1956
+ return findListItemAncestorDepth($pos) !== -1;
1957
+ }
1958
+ function isInListItemLabel($pos) {
1959
+ const d = findListItemAncestorDepth($pos);
1960
+ if (d === -1) return false;
1961
+ return $pos.index(d) === 0;
1962
+ }
1963
+
1964
+ // src/utils/liftCurrentListItem.ts
1965
+ function liftCurrentListItem(state, tr) {
1966
+ if (!tr.selection.empty) return false;
1967
+ if (tr.steps.length !== 0) return false;
1968
+ const { $from } = tr.selection;
1969
+ const listItemDepth = findListItemAncestorDepth($from);
1970
+ if (listItemDepth === -1) return false;
1971
+ if ($from.index(listItemDepth) !== 0) return false;
1972
+ const listItemType = $from.node(listItemDepth).type;
1973
+ return schemaList.liftListItem(listItemType)(state, (liftTr) => {
1974
+ for (const step of liftTr.steps) tr.step(step);
1975
+ if (liftTr.selectionSet) tr.setSelection(liftTr.selection);
1976
+ });
1977
+ }
1978
+
1979
+ // src/commands/nodeCommands.ts
1927
1980
  var setBlockType = (nodeName, attributes) => ({ state, tr, dispatch }) => {
1928
1981
  const nodeType = state.schema.nodes[nodeName];
1929
1982
  if (!nodeType) {
@@ -1947,7 +2000,26 @@ var setBlockType = (nodeName, attributes) => ({ state, tr, dispatch }) => {
1947
2000
  });
1948
2001
  return found;
1949
2002
  });
1950
- if (!canApply) return false;
2003
+ if (!canApply) {
2004
+ const liftOk = liftCurrentListItem(state, tr);
2005
+ if (!liftOk) return false;
2006
+ const $reFrom = tr.doc.resolve(tr.selection.from);
2007
+ if ($reFrom.depth < 1) return false;
2008
+ const blockParent = $reFrom.node($reFrom.depth - 1);
2009
+ const blockIndex = $reFrom.index($reFrom.depth - 1);
2010
+ if (!blockParent.canReplaceWith(blockIndex, blockIndex + 1, nodeType)) {
2011
+ return false;
2012
+ }
2013
+ if (!dispatch) return true;
2014
+ tr.setBlockType(
2015
+ tr.selection.from,
2016
+ tr.selection.to,
2017
+ nodeType,
2018
+ (node) => ({ ...node.attrs, ...attributes ?? {} })
2019
+ );
2020
+ dispatch(tr.scrollIntoView());
2021
+ return true;
2022
+ }
1951
2023
  if (!dispatch) return true;
1952
2024
  for (const range of tr.selection.ranges) {
1953
2025
  const from = range.$from.pos;
@@ -1992,9 +2064,22 @@ var wrapIn = (nodeName, attributes) => ({ state, tr, dispatch }) => {
1992
2064
  const range = $from.blockRange($to);
1993
2065
  if (!range) return false;
1994
2066
  const wrapping = transform.findWrapping(range, nodeType, attributes);
1995
- if (!wrapping) return false;
2067
+ if (wrapping) {
2068
+ if (!dispatch) return true;
2069
+ tr.wrap(range, wrapping).scrollIntoView();
2070
+ dispatch(tr);
2071
+ return true;
2072
+ }
2073
+ const liftOk = liftCurrentListItem(state, tr);
2074
+ if (!liftOk) return false;
2075
+ const $reFrom = tr.doc.resolve(tr.selection.from);
2076
+ const $reTo = tr.doc.resolve(tr.selection.to);
2077
+ const reRange = $reFrom.blockRange($reTo);
2078
+ if (!reRange) return false;
2079
+ const reWrapping = transform.findWrapping(reRange, nodeType, attributes);
2080
+ if (!reWrapping) return false;
1996
2081
  if (!dispatch) return true;
1997
- tr.wrap(range, wrapping).scrollIntoView();
2082
+ tr.wrap(reRange, reWrapping).scrollIntoView();
1998
2083
  dispatch(tr);
1999
2084
  return true;
2000
2085
  };
@@ -2089,6 +2174,30 @@ var lift = () => ({ tr, dispatch }) => {
2089
2174
  dispatch(tr);
2090
2175
  return true;
2091
2176
  };
2177
+
2178
+ // src/utils/listItemCursorContext.ts
2179
+ var LIST_ITEM_TYPES = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
2180
+ function getListItemCursorContext($from) {
2181
+ if ($from.parent.type.name !== "paragraph") return null;
2182
+ const itemDepth = $from.depth - 1;
2183
+ if (itemDepth < 1) return null;
2184
+ const itemNode = $from.node(itemDepth);
2185
+ if (!LIST_ITEM_TYPES.has(itemNode.type.name)) return null;
2186
+ const wrapperDepth = itemDepth - 1;
2187
+ const childIndex = $from.index(itemDepth);
2188
+ return {
2189
+ itemDepth,
2190
+ itemPos: $from.before(itemDepth),
2191
+ wrapperPos: $from.before(wrapperDepth),
2192
+ isInChildrenZone: childIndex > 0,
2193
+ paragraphIsEmpty: $from.parent.content.size === 0,
2194
+ isLastChild: childIndex === itemNode.childCount - 1,
2195
+ childIndex,
2196
+ itemIndexInWrapper: $from.index(wrapperDepth)
2197
+ };
2198
+ }
2199
+
2200
+ // src/commands/listCommands.ts
2092
2201
  function joinListBackwards(tr, listType) {
2093
2202
  const { $from } = tr.selection;
2094
2203
  for (let d = $from.depth; d >= 0; d--) {
@@ -2235,6 +2344,19 @@ var toggleList = (listNodeName, listItemNodeName, attributes) => ({ state: state
2235
2344
  return true;
2236
2345
  }
2237
2346
  const { from, to } = tr.selection;
2347
+ if (from === to) {
2348
+ const ctx = getListItemCursorContext(tr.selection.$from);
2349
+ if (ctx?.isInChildrenZone) {
2350
+ if (!dispatch) return true;
2351
+ const blockRange = tr.selection.$from.blockRange();
2352
+ if (blockRange && schemaList.wrapRangeInList(tr, blockRange, listType, attributes)) {
2353
+ joinListBackwards(tr, listType);
2354
+ joinListForwards(tr, listType);
2355
+ dispatch(tr.scrollIntoView());
2356
+ return true;
2357
+ }
2358
+ }
2359
+ }
2238
2360
  const contentBlocks = collectListContext(tr.doc, from, to);
2239
2361
  const allInTargetList = contentBlocks.length > 0 && contentBlocks.every((b) => b.inTargetList);
2240
2362
  const allInSomeList = contentBlocks.length > 0 && contentBlocks.every((b) => b.inSomeList);
@@ -2893,8 +3015,8 @@ var CommandManager = class {
2893
3015
  };
2894
3016
  }
2895
3017
  /**
2896
- * Single commands that execute immediately
2897
- * Uses Proxy to dynamically generate command methods (ID-1)
3018
+ * Single commands that execute immediately.
3019
+ * Uses a Proxy to dynamically generate command methods.
2898
3020
  *
2899
3021
  * @example
2900
3022
  * editor.commands.focus('end');
@@ -3373,12 +3495,18 @@ var Editor = class extends EventEmitter {
3373
3495
  return this._extensionManager.storage;
3374
3496
  }
3375
3497
  /**
3376
- * Gets toolbar items registered by all extensions.
3377
- * Used by framework toolbar components to auto-generate UI.
3498
+ * Toolbar items registered by all extensions.
3378
3499
  */
3379
3500
  get toolbarItems() {
3380
3501
  return this._extensionManager.toolbarItems;
3381
3502
  }
3503
+ /**
3504
+ * Floating-menu items registered by all extensions, rendered as the
3505
+ * block-insert menu shown on empty paragraphs.
3506
+ */
3507
+ get floatingMenuItems() {
3508
+ return this._extensionManager.floatingMenuItems;
3509
+ }
3382
3510
  // === Active State Methods ===
3383
3511
  /**
3384
3512
  * Checks if a node or mark is currently active
@@ -3629,9 +3757,8 @@ var Editor = class extends EventEmitter {
3629
3757
  }
3630
3758
  // === Dynamic Plugin Management ===
3631
3759
  /**
3632
- * Registers a ProseMirror plugin dynamically at runtime.
3633
- * Used by framework wrappers (e.g. Angular BubbleMenu component) to add
3634
- * plugins after the editor is created.
3760
+ * Registers a ProseMirror plugin dynamically at runtime, after the editor
3761
+ * has been created. Safe to call repeatedly with the same plugin key.
3635
3762
  */
3636
3763
  registerPlugin(plugin) {
3637
3764
  if (plugin.spec.key?.get(this.view.state)) return;
@@ -3746,7 +3873,7 @@ var Editor = class extends EventEmitter {
3746
3873
  ...this.options.editable ?? true ? {} : { "aria-readonly": "true" }
3747
3874
  }),
3748
3875
  ...Object.keys(nodeViews).length > 0 ? { nodeViews } : {},
3749
- // Clipboard transform apply user-provided transform (e.g. inlineStyles) on copy/cut
3876
+ // Clipboard transform - apply user-provided transform (e.g. inlineStyles) on copy/cut
3750
3877
  ...this.options.clipboardHTMLTransform ? this.buildClipboardSerializer(this.options.clipboardHTMLTransform, this._extensionManager.schema) : {},
3751
3878
  // Handle focus/blur events
3752
3879
  handleDOMEvents: {
@@ -3879,8 +4006,225 @@ function positionFloatingOnce(reference, floating, options) {
3879
4006
  });
3880
4007
  }
3881
4008
 
4009
+ // src/utils/clipboard.ts
4010
+ async function writeToClipboard(text) {
4011
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
4012
+ try {
4013
+ await navigator.clipboard.writeText(text);
4014
+ return true;
4015
+ } catch {
4016
+ }
4017
+ }
4018
+ if (typeof document === "undefined") return false;
4019
+ const textarea = document.createElement("textarea");
4020
+ textarea.value = text;
4021
+ textarea.style.position = "fixed";
4022
+ textarea.style.top = "-1000px";
4023
+ textarea.style.left = "-1000px";
4024
+ textarea.style.opacity = "0";
4025
+ textarea.setAttribute("readonly", "");
4026
+ document.body.appendChild(textarea);
4027
+ try {
4028
+ textarea.select();
4029
+ textarea.setSelectionRange(0, text.length);
4030
+ return document.execCommand("copy");
4031
+ } catch {
4032
+ return false;
4033
+ } finally {
4034
+ textarea.remove();
4035
+ }
4036
+ }
4037
+
4038
+ // src/utils/defaultBubbleContexts.ts
4039
+ var NOTION_MODE_CLASS = "dm-notion-mode";
4040
+ var NOTION_TEXT_CONTEXT = Object.freeze([
4041
+ "bold",
4042
+ "italic",
4043
+ "underline",
4044
+ "strike",
4045
+ "code",
4046
+ "|",
4047
+ "link",
4048
+ "|",
4049
+ "textAlign"
4050
+ ]);
4051
+ var STANDARD_TEXT_CONTEXT = Object.freeze([
4052
+ "bold",
4053
+ "italic",
4054
+ "underline",
4055
+ "strike",
4056
+ "code",
4057
+ "|",
4058
+ "link"
4059
+ ]);
4060
+ function defaultBubbleContexts(editor) {
4061
+ const inNotionMode = editor.view.dom.closest("." + NOTION_MODE_CLASS) !== null;
4062
+ const text = inNotionMode ? NOTION_TEXT_CONTEXT : STANDARD_TEXT_CONTEXT;
4063
+ return { text: [...text] };
4064
+ }
4065
+
4066
+ // src/utils/insertAsListItemChild.ts
4067
+ var LIST_ITEM_TYPES2 = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
4068
+ var LIST_WRAPPER_TYPES = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
4069
+ function insertAsListItemChild(args) {
4070
+ const { tr, wrapperPos, targetItemPos, blockNode, sourceRange } = args;
4071
+ if (wrapperPos < 0 || wrapperPos >= tr.doc.content.size) return { ok: false };
4072
+ const wrapper = tr.doc.nodeAt(wrapperPos);
4073
+ if (!wrapper || !LIST_WRAPPER_TYPES.has(wrapper.type.name)) return { ok: false };
4074
+ if (wrapper.childCount === 0) return { ok: false };
4075
+ let targetItem;
4076
+ let targetItemStart;
4077
+ if (targetItemPos !== void 0) {
4078
+ if (targetItemPos < 0 || targetItemPos >= tr.doc.content.size) return { ok: false };
4079
+ const candidate = tr.doc.nodeAt(targetItemPos);
4080
+ if (!candidate || !LIST_ITEM_TYPES2.has(candidate.type.name)) return { ok: false };
4081
+ if (targetItemPos < wrapperPos + 1 || targetItemPos >= wrapperPos + wrapper.nodeSize) {
4082
+ return { ok: false };
4083
+ }
4084
+ targetItem = candidate;
4085
+ targetItemStart = targetItemPos;
4086
+ } else {
4087
+ const last = wrapper.lastChild;
4088
+ if (!last || !LIST_ITEM_TYPES2.has(last.type.name)) return { ok: false };
4089
+ let pos = wrapperPos + 1;
4090
+ for (let i = 0; i < wrapper.childCount - 1; i++) {
4091
+ pos += wrapper.child(i).nodeSize;
4092
+ }
4093
+ targetItem = last;
4094
+ targetItemStart = pos;
4095
+ }
4096
+ if (!targetItem.canReplaceWith(targetItem.childCount, targetItem.childCount, blockNode.type)) {
4097
+ return { ok: false };
4098
+ }
4099
+ const targetItemContentEnd = targetItemStart + targetItem.nodeSize - 1;
4100
+ if (sourceRange) {
4101
+ const { from, to } = sourceRange;
4102
+ if (targetItemContentEnd >= from && targetItemContentEnd <= to) {
4103
+ return { ok: false };
4104
+ }
4105
+ tr.delete(from, to);
4106
+ const adjustedInsertPos = targetItemContentEnd > from ? targetItemContentEnd - (to - from) : targetItemContentEnd;
4107
+ tr.insert(adjustedInsertPos, blockNode);
4108
+ return { ok: true, insertedAt: adjustedInsertPos };
4109
+ }
4110
+ tr.insert(targetItemContentEnd, blockNode);
4111
+ return { ok: true, insertedAt: targetItemContentEnd };
4112
+ }
4113
+ function liftEmptyChildrenZoneParagraph(state$1, dispatch, ctx) {
4114
+ if (!ctx.isInChildrenZone || !ctx.paragraphIsEmpty) return false;
4115
+ const { $from } = state$1.selection;
4116
+ if ($from.parent.type.name !== "paragraph") return false;
4117
+ if ($from.parent.content.size !== 0) return false;
4118
+ const paragraphType = state$1.schema.nodes["paragraph"];
4119
+ if (!paragraphType) return false;
4120
+ const range = $from.blockRange();
4121
+ if (range) {
4122
+ const target = transform.liftTarget(range);
4123
+ if (target !== null) {
4124
+ if (!dispatch) return true;
4125
+ const tr2 = state$1.tr.lift(range, target);
4126
+ const oldParaStart = $from.before($from.depth);
4127
+ const newParaStart = tr2.mapping.map(oldParaStart);
4128
+ tr2.setSelection(state.TextSelection.create(tr2.doc, newParaStart + 1));
4129
+ dispatch(tr2.scrollIntoView());
4130
+ return true;
4131
+ }
4132
+ }
4133
+ const paraStart = $from.before($from.depth);
4134
+ const paraEnd = $from.after($from.depth);
4135
+ const itemNode = state$1.doc.nodeAt(ctx.itemPos);
4136
+ if (!itemNode) return false;
4137
+ const itemEnd = ctx.itemPos + itemNode.nodeSize;
4138
+ const wrapperNode = state$1.doc.nodeAt(ctx.wrapperPos);
4139
+ if (!wrapperNode) return false;
4140
+ const wrapperEnd = ctx.wrapperPos + wrapperNode.nodeSize;
4141
+ const isLastItem = ctx.itemIndexInWrapper === wrapperNode.childCount - 1;
4142
+ const wrapperParentDepth = ctx.itemDepth - 2;
4143
+ if (wrapperParentDepth < 0) return false;
4144
+ const wrapperParent = $from.node(wrapperParentDepth);
4145
+ const wrapperIndexInParent = $from.index(wrapperParentDepth);
4146
+ if (!wrapperParent.canReplaceWith(
4147
+ wrapperIndexInParent + 1,
4148
+ wrapperIndexInParent + 1,
4149
+ paragraphType
4150
+ )) {
4151
+ return false;
4152
+ }
4153
+ if (!dispatch) return true;
4154
+ const newPara = paragraphType.create();
4155
+ const tr = state$1.tr;
4156
+ if (isLastItem) {
4157
+ tr.delete(paraStart, paraEnd);
4158
+ const insertPos = tr.mapping.map(wrapperEnd);
4159
+ tr.insert(insertPos, newPara);
4160
+ tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
4161
+ } else {
4162
+ tr.delete(paraStart, paraEnd);
4163
+ const splitAt = tr.mapping.map(itemEnd);
4164
+ tr.split(splitAt, 1);
4165
+ const insertPos = splitAt + 1;
4166
+ tr.insert(insertPos, newPara);
4167
+ tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
4168
+ }
4169
+ dispatch(tr.scrollIntoView());
4170
+ return true;
4171
+ }
4172
+ function insertChildrenZoneSibling(state$1, dispatch, ctx) {
4173
+ if (!ctx.isInChildrenZone || !ctx.paragraphIsEmpty) return false;
4174
+ const paragraphType = state$1.schema.nodes["paragraph"];
4175
+ if (!paragraphType) return false;
4176
+ const { $from } = state$1.selection;
4177
+ if ($from.parent.type.name !== "paragraph") return false;
4178
+ if ($from.parent.content.size !== 0) return false;
4179
+ if (!dispatch) return true;
4180
+ const insertPos = $from.after($from.depth);
4181
+ const tr = state$1.tr.insert(insertPos, paragraphType.create());
4182
+ tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
4183
+ dispatch(tr.scrollIntoView());
4184
+ return true;
4185
+ }
4186
+ function splitListForInsert(state, tr) {
4187
+ if (!tr.selection.empty) return null;
4188
+ if (tr.steps.length !== 0) return null;
4189
+ const { $from } = tr.selection;
4190
+ const listItemDepth = findListItemAncestorDepth($from);
4191
+ if (listItemDepth === -1) return null;
4192
+ if ($from.index(listItemDepth) !== 0) return null;
4193
+ const listDepth = listItemDepth - 1;
4194
+ const listItemNode = $from.node(listItemDepth);
4195
+ const labelParagraph = listItemNode.firstChild;
4196
+ const labelIsEmpty = listItemNode.childCount === 1 && (labelParagraph?.content.size ?? 0) === 0;
4197
+ if (labelIsEmpty) {
4198
+ const listItemType = listItemNode.type;
4199
+ let safetyLimit = 10;
4200
+ while (safetyLimit-- > 0) {
4201
+ const stepState = state.apply(tr);
4202
+ const liftOk = schemaList.liftListItem(listItemType)(stepState, (liftTr) => {
4203
+ for (const step of liftTr.steps) tr.step(step);
4204
+ if (liftTr.selectionSet) tr.setSelection(liftTr.selection);
4205
+ });
4206
+ if (!liftOk) break;
4207
+ if (!isInsideListItem(tr.selection.$from)) break;
4208
+ }
4209
+ const $afterLift = tr.selection.$from;
4210
+ if (isInsideListItem($afterLift)) return null;
4211
+ return { from: $afterLift.before(), to: $afterLift.after() };
4212
+ }
4213
+ const listNode = $from.node(listDepth);
4214
+ const indexInList = $from.index(listDepth);
4215
+ const isLast = indexInList === listNode.childCount - 1;
4216
+ if (isLast) {
4217
+ const pos2 = $from.after(listDepth);
4218
+ return { from: pos2, to: pos2 };
4219
+ }
4220
+ const splitPos = $from.after(listItemDepth);
4221
+ tr.split(splitPos, 1);
4222
+ const pos = splitPos + 1;
4223
+ return { from: pos, to: pos };
4224
+ }
4225
+
3882
4226
  // src/Node.ts
3883
- var Node = class _Node extends Extension {
4227
+ var Node2 = class _Node extends Extension {
3884
4228
  /**
3885
4229
  * Node type identifier
3886
4230
  * Distinguishes nodes from extensions and marks
@@ -3942,25 +4286,12 @@ var Node = class _Node extends Extension {
3942
4286
  return new _Node(config);
3943
4287
  }
3944
4288
  /**
3945
- * Creates a new node with merged options
3946
- * Original node is not modified
3947
- *
3948
- * **Note:** Options are merged shallowly using object spread (`...`).
3949
- * Nested objects are replaced entirely, not deeply merged.
3950
- *
3951
- * @param options - Options to merge with existing options
3952
- * @returns New node instance with merged options
4289
+ * Creates a new node with merged options. Original node is not modified.
4290
+ * Options merge shallowly (object spread); see {@link Extension.configure}
4291
+ * for the nested-object gotcha and a workaround.
3953
4292
  *
3954
4293
  * @example
3955
4294
  * const CustomParagraph = Paragraph.configure({ HTMLAttributes: { class: 'custom' } });
3956
- *
3957
- * @example
3958
- * // Shallow merge behavior with nested objects:
3959
- * // Given: options = { HTMLAttributes: { class: 'a', id: 'b' } }
3960
- * // configure({ HTMLAttributes: { class: 'c' } })
3961
- * // Result: { HTMLAttributes: { class: 'c' } } — 'id' is lost!
3962
- * // To preserve nested values, spread manually:
3963
- * // configure({ HTMLAttributes: { ...original.options.HTMLAttributes, class: 'c' } })
3964
4295
  */
3965
4296
  configure(options) {
3966
4297
  const newConfig = {
@@ -4134,7 +4465,7 @@ var ToolbarController = class _ToolbarController {
4134
4465
  _activeMap = /* @__PURE__ */ new Map();
4135
4466
  /** Disabled state for each button (keyed by item.name) */
4136
4467
  _disabledMap = /* @__PURE__ */ new Map();
4137
- /** Expanded state for emitEvent buttons true when their panel is open */
4468
+ /** Expanded state for emitEvent buttons - true when their panel is open */
4138
4469
  _expandedMap = /* @__PURE__ */ new Map();
4139
4470
  /** Currently open dropdown name (null = none) */
4140
4471
  _openDropdown = null;
@@ -4469,6 +4800,262 @@ var ToolbarController = class _ToolbarController {
4469
4800
  }
4470
4801
  };
4471
4802
 
4803
+ // src/utils/groupFloatingMenuItems.ts
4804
+ function groupFloatingMenuItems(items) {
4805
+ const map = /* @__PURE__ */ new Map();
4806
+ const order = [];
4807
+ for (const item of items) {
4808
+ const name = item.group ?? "";
4809
+ let list = map.get(name);
4810
+ if (!list) {
4811
+ list = [];
4812
+ map.set(name, list);
4813
+ order.push(name);
4814
+ }
4815
+ list.push(item);
4816
+ }
4817
+ const groups = [];
4818
+ for (const name of order) {
4819
+ const list = (map.get(name) ?? []).slice();
4820
+ list.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
4821
+ groups.push({ name, items: list });
4822
+ }
4823
+ return groups;
4824
+ }
4825
+
4826
+ // src/FloatingMenuController.ts
4827
+ var FLOATING_MENU_NO_FOCUS = -1;
4828
+ var FloatingMenuController = class _FloatingMenuController {
4829
+ /**
4830
+ * Resolves an `items` option (array | function) against the editor's
4831
+ * default floating-menu items. Exposed as static so the plugin can
4832
+ * resolve once at init without constructing a controller.
4833
+ */
4834
+ static resolveItems(editor, override) {
4835
+ const defaults = editor.floatingMenuItems;
4836
+ if (!override) return defaults;
4837
+ if (Array.isArray(override)) return override;
4838
+ try {
4839
+ return override(defaults, editor);
4840
+ } catch {
4841
+ return defaults;
4842
+ }
4843
+ }
4844
+ /**
4845
+ * Executes a floating-menu item's command on the editor.
4846
+ * String commands are dispatched via `editor.commands[name](...args)`;
4847
+ * function commands are called directly.
4848
+ */
4849
+ static executeItem(editor, item) {
4850
+ if (typeof item.command === "function") {
4851
+ item.command(editor);
4852
+ return;
4853
+ }
4854
+ const commands = editor.commands;
4855
+ const cmd = commands[item.command];
4856
+ if (!cmd) return;
4857
+ if (item.commandArgs?.length) {
4858
+ cmd(...item.commandArgs);
4859
+ } else {
4860
+ cmd();
4861
+ }
4862
+ }
4863
+ editor;
4864
+ onChange;
4865
+ override;
4866
+ transactionHandler = null;
4867
+ _groups = [];
4868
+ _flatItems = [];
4869
+ _disabledMap = /* @__PURE__ */ new Map();
4870
+ _focusedIndex = FLOATING_MENU_NO_FOCUS;
4871
+ constructor(editor, onChange, override) {
4872
+ this.editor = editor;
4873
+ this.onChange = onChange;
4874
+ this.override = override;
4875
+ this.rebuild();
4876
+ }
4877
+ // === Getters ===
4878
+ get groups() {
4879
+ return this._groups;
4880
+ }
4881
+ get flatItems() {
4882
+ return this._flatItems;
4883
+ }
4884
+ get disabledMap() {
4885
+ return this._disabledMap;
4886
+ }
4887
+ get focusedIndex() {
4888
+ return this._focusedIndex;
4889
+ }
4890
+ /** True when keyboard focus is inside the menu (at least one item focused). */
4891
+ get isEntered() {
4892
+ return this._focusedIndex >= 0;
4893
+ }
4894
+ get itemCount() {
4895
+ return this._flatItems.length;
4896
+ }
4897
+ // === State Methods ===
4898
+ isDisabled(item) {
4899
+ return this._disabledMap.get(item.name) ?? false;
4900
+ }
4901
+ /**
4902
+ * Executes an item's command, then closes the menu keyboard focus.
4903
+ * Callers should refocus the editor after calling this.
4904
+ */
4905
+ execute(item) {
4906
+ if (this.isDisabled(item)) return;
4907
+ _FloatingMenuController.executeItem(this.editor, item);
4908
+ this._focusedIndex = FLOATING_MENU_NO_FOCUS;
4909
+ this.onChange();
4910
+ }
4911
+ /**
4912
+ * Rebuilds items from the editor. Call when the editor's extensions
4913
+ * change (rare) or on explicit refresh. Notification is delegated to
4914
+ * `updateDisabledStates` which fires `onChange` only when a disabled
4915
+ * state flipped - wrappers that need to react to pure group-structure
4916
+ * changes do so by bumping their own render signal after constructing
4917
+ * / re-using the controller (see framework wrapper usage).
4918
+ */
4919
+ rebuild() {
4920
+ const items = _FloatingMenuController.resolveItems(this.editor, this.override);
4921
+ this._groups = this.groupItems(items);
4922
+ this._flatItems = this._groups.flatMap((g) => g.items);
4923
+ if (this._focusedIndex >= this._flatItems.length) {
4924
+ this._focusedIndex = FLOATING_MENU_NO_FOCUS;
4925
+ }
4926
+ this.updateDisabledStates();
4927
+ }
4928
+ // === Keyboard Navigation (roving tabindex) ===
4929
+ /** Enter keyboard focus on the menu (first item). Called by the plugin's keymap. */
4930
+ enterMenu() {
4931
+ if (this._flatItems.length === 0) {
4932
+ this._focusedIndex = FLOATING_MENU_NO_FOCUS;
4933
+ return this._focusedIndex;
4934
+ }
4935
+ this._focusedIndex = 0;
4936
+ this.onChange();
4937
+ return this._focusedIndex;
4938
+ }
4939
+ /** Leave keyboard focus (Escape). Refocus of the editor is the caller's job. */
4940
+ leaveMenu() {
4941
+ if (this._focusedIndex === FLOATING_MENU_NO_FOCUS) return;
4942
+ this._focusedIndex = FLOATING_MENU_NO_FOCUS;
4943
+ this.onChange();
4944
+ }
4945
+ /** ArrowDown - wrap to first at end. */
4946
+ next() {
4947
+ if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
4948
+ const cur = this._focusedIndex < 0 ? -1 : this._focusedIndex;
4949
+ this._focusedIndex = (cur + 1) % this._flatItems.length;
4950
+ this.onChange();
4951
+ return this._focusedIndex;
4952
+ }
4953
+ /** ArrowUp - wrap to last at start. */
4954
+ prev() {
4955
+ if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
4956
+ const len = this._flatItems.length;
4957
+ const cur = this._focusedIndex < 0 ? len : this._focusedIndex;
4958
+ this._focusedIndex = (cur - 1 + len) % len;
4959
+ this.onChange();
4960
+ return this._focusedIndex;
4961
+ }
4962
+ first() {
4963
+ if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
4964
+ this._focusedIndex = 0;
4965
+ this.onChange();
4966
+ return this._focusedIndex;
4967
+ }
4968
+ last() {
4969
+ if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
4970
+ this._focusedIndex = this._flatItems.length - 1;
4971
+ this.onChange();
4972
+ return this._focusedIndex;
4973
+ }
4974
+ /** Set focused index directly (e.g. on pointer hover). */
4975
+ setFocusedIndex(index) {
4976
+ if (index < 0 || index >= this._flatItems.length) return;
4977
+ if (this._focusedIndex === index) return;
4978
+ this._focusedIndex = index;
4979
+ this.onChange();
4980
+ }
4981
+ /** Get focused item (or null). */
4982
+ focusedItem() {
4983
+ return this._flatItems[this._focusedIndex] ?? null;
4984
+ }
4985
+ /** Get flat index of item by name (for wrappers binding roving tabindex). */
4986
+ getFlatIndex(name) {
4987
+ return this._flatItems.findIndex((i) => i.name === name);
4988
+ }
4989
+ // === Lifecycle ===
4990
+ /** Subscribes to editor transactions for disabled-state tracking. */
4991
+ subscribe() {
4992
+ this.transactionHandler = () => {
4993
+ this.updateDisabledStates();
4994
+ };
4995
+ this.editor.on("transaction", this.transactionHandler);
4996
+ }
4997
+ /** Unsubscribes and clears internal state. */
4998
+ destroy() {
4999
+ if (this.transactionHandler) {
5000
+ this.editor.off("transaction", this.transactionHandler);
5001
+ this.transactionHandler = null;
5002
+ }
5003
+ this._groups = [];
5004
+ this._flatItems = [];
5005
+ this._disabledMap.clear();
5006
+ this._focusedIndex = FLOATING_MENU_NO_FOCUS;
5007
+ }
5008
+ // === Internal ===
5009
+ /**
5010
+ * Groups items by `group` preserving insertion order, then sorts by
5011
+ * priority (higher first) within each group. Delegates to the shared
5012
+ * utility so `SlashCommand`'s renderer and this controller always
5013
+ * produce identical ordering.
5014
+ */
5015
+ groupItems(items) {
5016
+ return groupFloatingMenuItems(items);
5017
+ }
5018
+ /**
5019
+ * Updates disabled state for each item. Uses custom predicate when
5020
+ * provided; otherwise tries a dry-run against `editor.can()[command]`.
5021
+ * Only notifies on change to avoid noisy re-renders.
5022
+ */
5023
+ updateDisabledStates() {
5024
+ let changed = false;
5025
+ let canProxy = null;
5026
+ try {
5027
+ canProxy = this.editor.can();
5028
+ } catch {
5029
+ canProxy = null;
5030
+ }
5031
+ for (const item of this._flatItems) {
5032
+ const was = this._disabledMap.get(item.name) ?? false;
5033
+ let now = false;
5034
+ if (item.isDisabled) {
5035
+ try {
5036
+ now = item.isDisabled(this.editor);
5037
+ } catch {
5038
+ now = false;
5039
+ }
5040
+ } else if (typeof item.command === "string" && canProxy) {
5041
+ try {
5042
+ const canCmd = canProxy[item.command];
5043
+ if (canCmd) {
5044
+ now = item.commandArgs?.length ? !canCmd(...item.commandArgs) : !canCmd();
5045
+ }
5046
+ } catch {
5047
+ now = false;
5048
+ }
5049
+ }
5050
+ if (was !== now) {
5051
+ this._disabledMap.set(item.name, now);
5052
+ changed = true;
5053
+ }
5054
+ }
5055
+ if (changed) this.onChange();
5056
+ }
5057
+ };
5058
+
4472
5059
  // src/icons/phosphor.ts
4473
5060
  var defaultIcons = {
4474
5061
  // --- Format: Inline ---
@@ -4528,24 +5115,29 @@ var defaultIcons = {
4528
5115
  arrowClockwise: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z"/></svg>',
4529
5116
  // --- Table ---
4530
5117
  table: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48ZM40,112H80v32H40Zm56,0H216v32H96ZM216,64V96H40V64ZM40,160H80v32H40Zm176,32H96V160H216v32Z"/></svg>',
4531
- gridNine: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,48H40A16,16,0,0,0,24,64V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V64A16,16,0,0,0,216,48ZM104,144V112h48v32Zm48,16v32H104V160ZM40,112H88v32H40Zm64-16V64h48V96Zm64,16h48v32H168Zm48-16H168V64h48ZM88,64V96H40V64ZM40,160H88v32H40Zm176,32H168V160h48v32Z"/></svg>'
5118
+ gridNine: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,48H40A16,16,0,0,0,24,64V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V64A16,16,0,0,0,216,48ZM104,144V112h48v32Zm48,16v32H104V160ZM40,112H88v32H40Zm64-16V64h48V96Zm64,16h48v32H168Zm48-16H168V64h48ZM88,64V96H40V64ZM40,160H88v32H40Zm176,32H168V160h48v32Z"/></svg>',
5119
+ // --- Block Handle / Context Menu ---
5120
+ 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>',
5121
+ 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>',
5122
+ 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>',
5123
+ 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>'
4532
5124
  };
4533
5125
 
4534
5126
  // src/nodes/Document.ts
4535
- var Document = Node.create({
5127
+ var Document = Node2.create({
4536
5128
  name: "doc",
4537
5129
  topNode: true,
4538
5130
  content: "block+"
4539
5131
  });
4540
5132
 
4541
5133
  // src/nodes/Text.ts
4542
- var Text = Node.create({
5134
+ var Text = Node2.create({
4543
5135
  name: "text",
4544
5136
  group: "inline"
4545
5137
  });
4546
5138
 
4547
5139
  // src/nodes/Paragraph.ts
4548
- var Paragraph = Node.create({
5140
+ var Paragraph = Node2.create({
4549
5141
  name: "paragraph",
4550
5142
  group: "block",
4551
5143
  content: "inline*",
@@ -4578,7 +5170,7 @@ var Paragraph = Node.create({
4578
5170
  };
4579
5171
  }
4580
5172
  });
4581
- var Heading = Node.create({
5173
+ var Heading = Node2.create({
4582
5174
  name: "heading",
4583
5175
  group: "block",
4584
5176
  content: "inline*",
@@ -4641,6 +5233,33 @@ var Heading = Node.create({
4641
5233
  return editor?.commands["toggleHeading"]?.({ level }) ?? false;
4642
5234
  };
4643
5235
  });
5236
+ shortcuts["Enter"] = () => {
5237
+ if (!editor) return false;
5238
+ const { state: state$1, view } = editor;
5239
+ const { selection } = state$1;
5240
+ if (!selection.empty) return false;
5241
+ const { $from } = selection;
5242
+ if ($from.parent.type.name !== "heading") return false;
5243
+ const paragraphType = state$1.schema.nodes["paragraph"];
5244
+ if (!paragraphType) return false;
5245
+ if ($from.parent.content.size === 0) {
5246
+ view.dispatch(
5247
+ state$1.tr.setNodeMarkup($from.before($from.depth), paragraphType).scrollIntoView()
5248
+ );
5249
+ return true;
5250
+ }
5251
+ if ($from.parentOffset !== $from.parent.content.size) return false;
5252
+ const after = $from.after($from.depth);
5253
+ const $after = state$1.doc.resolve(after);
5254
+ const indexAfter = $after.index();
5255
+ if (!$after.parent.canReplaceWith(indexAfter, indexAfter, paragraphType)) {
5256
+ return false;
5257
+ }
5258
+ const tr = state$1.tr.insert(after, paragraphType.create());
5259
+ tr.setSelection(state.TextSelection.create(tr.doc, after + 1));
5260
+ view.dispatch(tr.scrollIntoView());
5261
+ return true;
5262
+ };
4644
5263
  return shortcuts;
4645
5264
  },
4646
5265
  addToolbarItems() {
@@ -4682,6 +5301,30 @@ var Heading = Node.create({
4682
5301
  }
4683
5302
  ];
4684
5303
  },
5304
+ addFloatingMenuItems() {
5305
+ const iconMap = {
5306
+ 1: "textHOne",
5307
+ 2: "textHTwo",
5308
+ 3: "textHThree"
5309
+ };
5310
+ const descriptionMap = {
5311
+ 1: "Big section heading",
5312
+ 2: "Medium section heading",
5313
+ 3: "Small section heading"
5314
+ };
5315
+ return this.options.levels.filter((level) => level <= 3).map((level) => ({
5316
+ name: `heading-${String(level)}`,
5317
+ label: `Heading ${String(level)}`,
5318
+ description: descriptionMap[level] ?? "Section heading",
5319
+ icon: iconMap[level] ?? "textH",
5320
+ group: "Basic",
5321
+ priority: 210 - level * 10,
5322
+ keywords: ["heading", `h${String(level)}`, "title"],
5323
+ shortcut: "#".repeat(level) + " ",
5324
+ command: "toggleHeading",
5325
+ commandArgs: [{ level }]
5326
+ }));
5327
+ },
4685
5328
  addProseMirrorPlugins() {
4686
5329
  const { options, editor } = this;
4687
5330
  const codeToLevel = {};
@@ -4755,7 +5398,7 @@ var Heading = Node.create({
4755
5398
  });
4756
5399
 
4757
5400
  // src/nodes/Blockquote.ts
4758
- var Blockquote = Node.create({
5401
+ var Blockquote = Node2.create({
4759
5402
  name: "blockquote",
4760
5403
  group: "block",
4761
5404
  content: "block+",
@@ -4808,6 +5451,21 @@ var Blockquote = Node.create({
4808
5451
  }
4809
5452
  ];
4810
5453
  },
5454
+ addFloatingMenuItems() {
5455
+ return [
5456
+ {
5457
+ name: "blockquote",
5458
+ label: "Quote",
5459
+ description: "Capture a quote",
5460
+ icon: "quotes",
5461
+ group: "Basic",
5462
+ priority: 170,
5463
+ keywords: ["quote", "blockquote", "citation"],
5464
+ shortcut: "> ",
5465
+ command: "toggleBlockquote"
5466
+ }
5467
+ ];
5468
+ },
4811
5469
  addInputRules() {
4812
5470
  const { nodeType } = this;
4813
5471
  if (!nodeType) {
@@ -4818,7 +5476,7 @@ var Blockquote = Node.create({
4818
5476
  ];
4819
5477
  }
4820
5478
  });
4821
- var CodeBlock = Node.create({
5479
+ var CodeBlock = Node2.create({
4822
5480
  name: "codeBlock",
4823
5481
  group: "block",
4824
5482
  content: "text*",
@@ -4938,6 +5596,21 @@ var CodeBlock = Node.create({
4938
5596
  }
4939
5597
  ];
4940
5598
  },
5599
+ addFloatingMenuItems() {
5600
+ return [
5601
+ {
5602
+ name: "code-block",
5603
+ label: "Code block",
5604
+ description: "Capture a code snippet",
5605
+ icon: "codeBlock",
5606
+ group: "Basic",
5607
+ priority: 160,
5608
+ keywords: ["code", "snippet", "pre"],
5609
+ shortcut: "``` ",
5610
+ command: "toggleCodeBlock"
5611
+ }
5612
+ ];
5613
+ },
4941
5614
  addInputRules() {
4942
5615
  const { nodeType } = this;
4943
5616
  if (!nodeType) {
@@ -4955,6 +5628,7 @@ var CodeBlock = Node.create({
4955
5628
  ];
4956
5629
  }
4957
5630
  });
5631
+ var LIST_GROUP_TYPES = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
4958
5632
  function getListItemContext(editor, listItemName) {
4959
5633
  const { state, view } = editor;
4960
5634
  const listItemType = state.schema.nodes[listItemName];
@@ -5001,10 +5675,39 @@ var ListKeymap = Extension.create({
5001
5675
  Backspace: () => {
5002
5676
  const editor = this.editor;
5003
5677
  if (!editor) return false;
5004
- const { state, view } = editor;
5005
- const { $from, empty } = state.selection;
5678
+ const { state: state$1, view } = editor;
5679
+ const { $from, empty } = state$1.selection;
5006
5680
  if (!empty || $from.parentOffset !== 0) return false;
5007
- const listItemType = state.schema.nodes[this.options.listItem];
5681
+ if ($from.parent.type.name === "paragraph" && $from.parent.content.size === 0 && $from.depth >= 1) {
5682
+ const containerDepth = $from.depth - 1;
5683
+ const idx = $from.index(containerDepth);
5684
+ if (idx > 0) {
5685
+ const container = $from.node(containerDepth);
5686
+ const prev = container.child(idx - 1);
5687
+ if (LIST_GROUP_TYPES.has(prev.type.name)) {
5688
+ const paraStart = $from.before($from.depth);
5689
+ const paraEnd = $from.after($from.depth);
5690
+ const listEnd = paraStart;
5691
+ const listStart = listEnd - prev.nodeSize;
5692
+ let lastTextblockEnd = -1;
5693
+ state$1.doc.nodesBetween(listStart, listEnd, (n, p) => {
5694
+ if (n.isTextblock) lastTextblockEnd = p + 1 + n.content.size;
5695
+ return true;
5696
+ });
5697
+ if (lastTextblockEnd !== -1) {
5698
+ const tr = state$1.tr.delete(paraStart, paraEnd);
5699
+ const next = idx + 1 < container.childCount ? container.child(idx + 1) : null;
5700
+ if (next?.type === prev.type && transform.canJoin(tr.doc, paraStart)) {
5701
+ tr.join(paraStart);
5702
+ }
5703
+ tr.setSelection(state.TextSelection.create(tr.doc, lastTextblockEnd));
5704
+ view.dispatch(tr.scrollIntoView());
5705
+ return true;
5706
+ }
5707
+ }
5708
+ }
5709
+ }
5710
+ const listItemType = state$1.schema.nodes[this.options.listItem];
5008
5711
  if (!listItemType) return false;
5009
5712
  let listItemDepth = -1;
5010
5713
  for (let d = $from.depth; d > 0; d--) {
@@ -5019,7 +5722,7 @@ var ListKeymap = Extension.create({
5019
5722
  if (firstChild?.isTextblock) {
5020
5723
  const posInListItem = $from.pos - $from.start(listItemDepth);
5021
5724
  if (posInListItem <= 1) {
5022
- return schemaList.liftListItem(listItemType)(state, view.dispatch);
5725
+ return schemaList.liftListItem(listItemType)(state$1, view.dispatch);
5023
5726
  }
5024
5727
  }
5025
5728
  return false;
@@ -5029,9 +5732,11 @@ var ListKeymap = Extension.create({
5029
5732
  });
5030
5733
 
5031
5734
  // src/nodes/ListItem.ts
5032
- var ListItem = Node.create({
5735
+ var ListItem = Node2.create({
5033
5736
  name: "listItem",
5034
- content: "block+",
5737
+ // Notion-strict: paragraph must be the first child (the "label" line aligned
5738
+ // with the bullet); additional blocks render below as nested children.
5739
+ content: "paragraph block*",
5035
5740
  defining: true,
5036
5741
  addOptions() {
5037
5742
  return {
@@ -5054,6 +5759,15 @@ var ListItem = Node.create({
5054
5759
  const { state: state$1, view } = this.editor;
5055
5760
  const { $from } = state$1.selection;
5056
5761
  if ($from.depth < 1 || $from.node(-1).type !== this.nodeType) return false;
5762
+ if ($from.parent.type.name !== "paragraph") return false;
5763
+ const ctx = getListItemCursorContext($from);
5764
+ if (ctx?.isInChildrenZone) {
5765
+ if (ctx.paragraphIsEmpty) {
5766
+ if (insertChildrenZoneSibling(state$1, view.dispatch, ctx)) return true;
5767
+ } else {
5768
+ if (commands.splitBlock(state$1, view.dispatch)) return true;
5769
+ }
5770
+ }
5057
5771
  if (schemaList.splitListItem(this.nodeType)(state$1, view.dispatch)) return true;
5058
5772
  const listDepth = $from.depth - 2;
5059
5773
  const taskItemType = state$1.schema.nodes["taskItem"];
@@ -5072,13 +5786,25 @@ var ListItem = Node.create({
5072
5786
  }
5073
5787
  }
5074
5788
  return schemaList.liftListItem(this.nodeType)(state$1, view.dispatch);
5789
+ },
5790
+ Backspace: () => {
5791
+ if (!this.editor || !this.nodeType) return false;
5792
+ const { state, view } = this.editor;
5793
+ const { $from, empty } = state.selection;
5794
+ if (!empty || $from.parentOffset !== 0) return false;
5795
+ if ($from.parent.content.size !== 0) return false;
5796
+ const ctx = getListItemCursorContext($from);
5797
+ if (ctx && ctx.isInChildrenZone && ctx.paragraphIsEmpty) {
5798
+ if (liftEmptyChildrenZoneParagraph(state, view.dispatch, ctx)) return true;
5799
+ }
5800
+ return false;
5075
5801
  }
5076
5802
  };
5077
5803
  }
5078
5804
  });
5079
5805
 
5080
5806
  // src/nodes/BulletList.ts
5081
- var BulletList = Node.create({
5807
+ var BulletList = Node2.create({
5082
5808
  name: "bulletList",
5083
5809
  group: "block list",
5084
5810
  content: "listItem+",
@@ -5117,6 +5843,24 @@ var BulletList = Node.create({
5117
5843
  }
5118
5844
  ];
5119
5845
  },
5846
+ addFloatingMenuItems() {
5847
+ return [
5848
+ {
5849
+ name: "bullet-list",
5850
+ label: "Bulleted list",
5851
+ description: "Create a simple bulleted list",
5852
+ icon: "listBullets",
5853
+ group: "Lists",
5854
+ priority: 200,
5855
+ keywords: ["bullet", "list", "unordered", "ul"],
5856
+ shortcut: "- ",
5857
+ command: "toggleBulletList",
5858
+ // Don't offer "Bulleted list" while cursor is already inside one,
5859
+ // otherwise picking it lifts the user out of the list.
5860
+ hideWhenInside: ["bulletList"]
5861
+ }
5862
+ ];
5863
+ },
5120
5864
  addKeyboardShortcuts() {
5121
5865
  const { editor } = this;
5122
5866
  return {
@@ -5145,7 +5889,7 @@ var BulletList = Node.create({
5145
5889
  });
5146
5890
 
5147
5891
  // src/nodes/OrderedList.ts
5148
- var OrderedList = Node.create({
5892
+ var OrderedList = Node2.create({
5149
5893
  name: "orderedList",
5150
5894
  group: "block list",
5151
5895
  content: "listItem+",
@@ -5202,6 +5946,22 @@ var OrderedList = Node.create({
5202
5946
  }
5203
5947
  ];
5204
5948
  },
5949
+ addFloatingMenuItems() {
5950
+ return [
5951
+ {
5952
+ name: "ordered-list",
5953
+ label: "Numbered list",
5954
+ description: "Create a numbered list",
5955
+ icon: "listNumbers",
5956
+ group: "Lists",
5957
+ priority: 190,
5958
+ keywords: ["ordered", "numbered", "list", "ol", "1."],
5959
+ shortcut: "1. ",
5960
+ command: "toggleOrderedList",
5961
+ hideWhenInside: ["orderedList"]
5962
+ }
5963
+ ];
5964
+ },
5205
5965
  addKeyboardShortcuts() {
5206
5966
  const { editor } = this;
5207
5967
  return {
@@ -5232,7 +5992,7 @@ var OrderedList = Node.create({
5232
5992
  ];
5233
5993
  }
5234
5994
  });
5235
- var HorizontalRule = Node.create({
5995
+ var HorizontalRule = Node2.create({
5236
5996
  name: "horizontalRule",
5237
5997
  group: "block",
5238
5998
  addOptions() {
@@ -5253,19 +6013,31 @@ var HorizontalRule = Node.create({
5253
6013
  const { $from } = tr.selection;
5254
6014
  const parent = $from.parent;
5255
6015
  if (!parent.isTextblock) return false;
6016
+ const paragraphType = state$1.schema.nodes["paragraph"];
6017
+ const hrNode = this.nodeType.create();
6018
+ const trailingParagraph = paragraphType?.create();
6019
+ const nodes = trailingParagraph ? [hrNode, trailingParagraph] : [hrNode];
6020
+ const listRange = splitListForInsert(state$1, tr);
6021
+ if (listRange) {
6022
+ if (!dispatch) return true;
6023
+ tr.replaceWith(listRange.from, listRange.to, nodes);
6024
+ const sel = state.TextSelection.findFrom(
6025
+ tr.doc.resolve(listRange.from + 1),
6026
+ 1
6027
+ );
6028
+ if (sel) tr.setSelection(sel);
6029
+ dispatch(tr.scrollIntoView());
6030
+ return true;
6031
+ }
5256
6032
  if (dispatch) {
5257
6033
  if (parent.content.size === 0 && parent.type.name === "paragraph") {
5258
6034
  const from = $from.before();
5259
6035
  const to = $from.after();
5260
- const paragraph = state$1.schema.nodes["paragraph"]?.create();
5261
- const nodes = paragraph ? [this.nodeType.create(), paragraph] : [this.nodeType.create()];
5262
6036
  tr.replaceWith(from, to, nodes);
5263
6037
  const sel = state.TextSelection.findFrom(tr.doc.resolve(from + 1), 1);
5264
6038
  if (sel) tr.setSelection(sel);
5265
6039
  } else {
5266
6040
  const end = $from.after();
5267
- const paragraph = state$1.schema.nodes["paragraph"]?.create();
5268
- const nodes = paragraph ? [this.nodeType.create(), paragraph] : [this.nodeType.create()];
5269
6041
  tr.insert(end, nodes);
5270
6042
  const sel = state.TextSelection.findFrom(tr.doc.resolve(end + 1), 1);
5271
6043
  if (sel) tr.setSelection(sel);
@@ -5289,6 +6061,21 @@ var HorizontalRule = Node.create({
5289
6061
  }
5290
6062
  ];
5291
6063
  },
6064
+ addFloatingMenuItems() {
6065
+ return [
6066
+ {
6067
+ name: "horizontal-rule",
6068
+ label: "Divider",
6069
+ description: "Insert a horizontal rule",
6070
+ icon: "minus",
6071
+ group: "Basic",
6072
+ priority: 150,
6073
+ keywords: ["divider", "hr", "line", "separator", "horizontal rule"],
6074
+ shortcut: "--- ",
6075
+ command: "setHorizontalRule"
6076
+ }
6077
+ ];
6078
+ },
5292
6079
  addInputRules() {
5293
6080
  const { nodeType } = this;
5294
6081
  if (!nodeType) {
@@ -5319,7 +6106,7 @@ var HorizontalRule = Node.create({
5319
6106
  });
5320
6107
 
5321
6108
  // src/nodes/HardBreak.ts
5322
- var HardBreak = Node.create({
6109
+ var HardBreak = Node2.create({
5323
6110
  name: "hardBreak",
5324
6111
  group: "inline",
5325
6112
  inline: true,
@@ -5379,9 +6166,84 @@ var HardBreak = Node.create({
5379
6166
  };
5380
6167
  }
5381
6168
  });
5382
- var TaskItem = Node.create({
6169
+
6170
+ // src/nodes/TaskItemNodeView.ts
6171
+ var TaskItemNodeView = class {
6172
+ dom;
6173
+ contentDOM;
6174
+ label;
6175
+ input;
6176
+ view;
6177
+ nodeType;
6178
+ getPos;
6179
+ node;
6180
+ constructor({ options, node, view, getPos }) {
6181
+ this.view = view;
6182
+ this.nodeType = node.type;
6183
+ this.node = node;
6184
+ this.getPos = getPos;
6185
+ this.dom = document.createElement("li");
6186
+ for (const [key, value] of Object.entries(options.HTMLAttributes)) {
6187
+ if (value !== null && value !== void 0) {
6188
+ this.dom.setAttribute(key, typeof value === "string" ? value : JSON.stringify(value));
6189
+ }
6190
+ }
6191
+ this.dom.setAttribute("data-type", "taskItem");
6192
+ this.dom.setAttribute("data-checked", node.attrs["checked"] ? "true" : "false");
6193
+ this.label = document.createElement("label");
6194
+ this.label.setAttribute("contenteditable", "false");
6195
+ this.input = document.createElement("input");
6196
+ this.input.type = "checkbox";
6197
+ this.input.checked = !!node.attrs["checked"];
6198
+ this.input.disabled = !view.editable;
6199
+ this.input.setAttribute("aria-label", "Task status");
6200
+ this.label.appendChild(this.input);
6201
+ this.dom.appendChild(this.label);
6202
+ this.contentDOM = document.createElement("div");
6203
+ this.dom.appendChild(this.contentDOM);
6204
+ this.input.addEventListener("change", this.handleChange);
6205
+ }
6206
+ handleChange = (event) => {
6207
+ event.preventDefault();
6208
+ if (!this.view.editable) {
6209
+ this.input.checked = !!this.node.attrs["checked"];
6210
+ return;
6211
+ }
6212
+ const pos = this.getPos();
6213
+ if (pos === void 0) return;
6214
+ const { state, dispatch } = this.view;
6215
+ const tr = state.tr.setNodeMarkup(pos, void 0, {
6216
+ ...this.node.attrs,
6217
+ checked: this.input.checked
6218
+ });
6219
+ dispatch(tr);
6220
+ };
6221
+ update(node) {
6222
+ if (node.type !== this.nodeType) return false;
6223
+ this.node = node;
6224
+ const checked = !!node.attrs["checked"];
6225
+ this.dom.setAttribute("data-checked", checked ? "true" : "false");
6226
+ this.input.checked = checked;
6227
+ this.input.disabled = !this.view.editable;
6228
+ return true;
6229
+ }
6230
+ stopEvent(event) {
6231
+ return event.target instanceof Node && this.label.contains(event.target);
6232
+ }
6233
+ ignoreMutation(mutation) {
6234
+ return mutation.target instanceof Node && this.label.contains(mutation.target);
6235
+ }
6236
+ destroy() {
6237
+ this.input.removeEventListener("change", this.handleChange);
6238
+ }
6239
+ };
6240
+
6241
+ // src/nodes/TaskItem.ts
6242
+ var TaskItem = Node2.create({
5383
6243
  name: "taskItem",
5384
- content: "block+",
6244
+ // Paragraph must be the first child so flex alignment binds to the label
6245
+ // baseline; a heading-first child would visually break the checkbox.
6246
+ content: "paragraph block*",
5385
6247
  defining: true,
5386
6248
  addOptions() {
5387
6249
  return {
@@ -5435,6 +6297,10 @@ var TaskItem = Node.create({
5435
6297
  ["div", 0]
5436
6298
  ];
5437
6299
  },
6300
+ addNodeView() {
6301
+ const options = this.options;
6302
+ return (node, view, getPos) => new TaskItemNodeView({ options, node, view, getPos });
6303
+ },
5438
6304
  addCommands() {
5439
6305
  const { name } = this;
5440
6306
  return {
@@ -5470,7 +6336,16 @@ var TaskItem = Node.create({
5470
6336
  const { state: state$1, view } = this.editor;
5471
6337
  const { $from } = state$1.selection;
5472
6338
  if ($from.depth < 1 || $from.node(-1).type !== this.nodeType) return false;
5473
- if (schemaList.splitListItem(this.nodeType)(state$1, view.dispatch)) return true;
6339
+ if ($from.parent.type.name !== "paragraph") return false;
6340
+ const ctx = getListItemCursorContext($from);
6341
+ if (ctx?.isInChildrenZone) {
6342
+ if (ctx.paragraphIsEmpty) {
6343
+ if (insertChildrenZoneSibling(state$1, view.dispatch, ctx)) return true;
6344
+ } else {
6345
+ if (commands.splitBlock(state$1, view.dispatch)) return true;
6346
+ }
6347
+ }
6348
+ if (schemaList.splitListItem(this.nodeType, { checked: false })(state$1, view.dispatch)) return true;
5474
6349
  if ($from.parent.content.size === 0) {
5475
6350
  const listItemType = state$1.schema.nodes["listItem"];
5476
6351
  if (listItemType) {
@@ -5526,6 +6401,12 @@ var TaskItem = Node.create({
5526
6401
  const { state, view } = this.editor;
5527
6402
  const { $from, empty } = state.selection;
5528
6403
  if (!empty || $from.parentOffset !== 0) return false;
6404
+ if ($from.parent.content.size === 0) {
6405
+ const ctx = getListItemCursorContext($from);
6406
+ if (ctx && ctx.isInChildrenZone && ctx.paragraphIsEmpty) {
6407
+ if (liftEmptyChildrenZoneParagraph(state, view.dispatch, ctx)) return true;
6408
+ }
6409
+ }
5529
6410
  let taskItemDepth = -1;
5530
6411
  for (let d = $from.depth; d > 0; d--) {
5531
6412
  if ($from.node(d).type === this.nodeType) {
@@ -5545,7 +6426,7 @@ var TaskItem = Node.create({
5545
6426
  });
5546
6427
 
5547
6428
  // src/nodes/TaskList.ts
5548
- var TaskList = Node.create({
6429
+ var TaskList = Node2.create({
5549
6430
  name: "taskList",
5550
6431
  group: "block list",
5551
6432
  content: "taskItem+",
@@ -5606,6 +6487,22 @@ var TaskList = Node.create({
5606
6487
  }
5607
6488
  ];
5608
6489
  },
6490
+ addFloatingMenuItems() {
6491
+ return [
6492
+ {
6493
+ name: "task-list",
6494
+ label: "To-do list",
6495
+ description: "Track tasks with a checkbox list",
6496
+ icon: "listChecks",
6497
+ group: "Lists",
6498
+ priority: 180,
6499
+ keywords: ["todo", "task", "checkbox", "check"],
6500
+ shortcut: "[ ] ",
6501
+ command: "toggleTaskList",
6502
+ hideWhenInside: ["taskList"]
6503
+ }
6504
+ ];
6505
+ },
5609
6506
  addExtensions() {
5610
6507
  return [TaskItem];
5611
6508
  },
@@ -6531,7 +7428,8 @@ var TextStyle = Mark.create({
6531
7428
  getAttrs: (element) => {
6532
7429
  if (typeof element === "string") return false;
6533
7430
  const hasStyles = element.hasAttribute("style");
6534
- if (!hasStyles) return false;
7431
+ const hasColorTokens = element.hasAttribute("data-text-color") || element.hasAttribute("data-bg-color");
7432
+ if (!hasStyles && !hasColorTokens) return false;
6535
7433
  return {};
6536
7434
  }
6537
7435
  },
@@ -6800,6 +7698,123 @@ var Placeholder = Extension.create({
6800
7698
  ];
6801
7699
  }
6802
7700
  });
7701
+ var LIST_ITEM_TYPES3 = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
7702
+ var LIST_WRAPPER_TYPES2 = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
7703
+ function isCursorInsideListItem(state) {
7704
+ const { $from } = state.selection;
7705
+ for (let d = $from.depth; d > 0; d--) {
7706
+ if (LIST_ITEM_TYPES3.has($from.node(d).type.name)) return true;
7707
+ }
7708
+ return false;
7709
+ }
7710
+ function indentBlockAsListChild(state$1, dispatch) {
7711
+ const { selection } = state$1;
7712
+ let blockIndex;
7713
+ let blockNode;
7714
+ let blockStart;
7715
+ let blockEnd;
7716
+ if (selection instanceof state.NodeSelection) {
7717
+ const node = selection.node;
7718
+ const $pos = selection.$from;
7719
+ if ($pos.depth !== 0) return false;
7720
+ if (isCursorInsideListItem(state$1)) return false;
7721
+ blockIndex = $pos.index(0);
7722
+ blockNode = node;
7723
+ blockStart = selection.from;
7724
+ blockEnd = selection.to;
7725
+ } else {
7726
+ if (!selection.empty) return false;
7727
+ if (isCursorInsideListItem(state$1)) return false;
7728
+ const { $from } = selection;
7729
+ if ($from.depth !== 1) return false;
7730
+ blockIndex = $from.index(0);
7731
+ blockNode = $from.node(1);
7732
+ blockStart = $from.before(1);
7733
+ blockEnd = $from.after(1);
7734
+ }
7735
+ if (blockIndex === 0) return false;
7736
+ const prevSibling = state$1.doc.child(blockIndex - 1);
7737
+ if (!LIST_WRAPPER_TYPES2.has(prevSibling.type.name)) return false;
7738
+ let wrapperPos = 0;
7739
+ for (let i = 0; i < blockIndex - 1; i++) {
7740
+ wrapperPos += state$1.doc.child(i).nodeSize;
7741
+ }
7742
+ const tr = state$1.tr;
7743
+ const result = insertAsListItemChild({
7744
+ tr,
7745
+ wrapperPos,
7746
+ blockNode,
7747
+ sourceRange: { from: blockStart, to: blockEnd }
7748
+ });
7749
+ if (!result.ok || result.insertedAt === void 0) return false;
7750
+ if (!dispatch) return true;
7751
+ tr.setSelection(state.Selection.near(tr.doc.resolve(result.insertedAt + 1)));
7752
+ dispatch(tr.scrollIntoView());
7753
+ return true;
7754
+ }
7755
+ function outdentBlockFromListItem(state$1, dispatch) {
7756
+ const { selection } = state$1;
7757
+ if (!selection.empty) return false;
7758
+ const { $from } = selection;
7759
+ let listItemDepth = -1;
7760
+ for (let d = $from.depth; d > 0; d--) {
7761
+ if (LIST_ITEM_TYPES3.has($from.node(d).type.name)) {
7762
+ listItemDepth = d;
7763
+ break;
7764
+ }
7765
+ }
7766
+ if (listItemDepth === -1) return false;
7767
+ const blockDepth = listItemDepth + 1;
7768
+ if ($from.depth < blockDepth) return false;
7769
+ const listItem = $from.node(listItemDepth);
7770
+ const blockIndexInItem = $from.index(listItemDepth);
7771
+ if (blockIndexInItem === 0) return false;
7772
+ if (blockIndexInItem !== listItem.childCount - 1) return false;
7773
+ const wrapperDepth = listItemDepth - 1;
7774
+ if (wrapperDepth < 0) return false;
7775
+ const liIndexInWrapper = $from.index(wrapperDepth);
7776
+ const wrapper = $from.node(wrapperDepth);
7777
+ if (liIndexInWrapper !== wrapper.childCount - 1) return false;
7778
+ if (wrapperDepth - 1 < 0) return false;
7779
+ const wrapperParent = $from.node(wrapperDepth - 1);
7780
+ const wrapperIndexInParent = $from.index(wrapperDepth - 1);
7781
+ const blockNode = $from.node(blockDepth);
7782
+ if (!wrapperParent.canReplaceWith(
7783
+ wrapperIndexInParent + 1,
7784
+ wrapperIndexInParent + 1,
7785
+ blockNode.type
7786
+ )) {
7787
+ return false;
7788
+ }
7789
+ if (!dispatch) return true;
7790
+ const blockStart = $from.before(blockDepth);
7791
+ const blockEnd = $from.after(blockDepth);
7792
+ const wrapperEnd = $from.after(wrapperDepth);
7793
+ const tr = state$1.tr;
7794
+ const blockSize = blockEnd - blockStart;
7795
+ tr.delete(blockStart, blockEnd);
7796
+ const insertAt = wrapperEnd - blockSize;
7797
+ tr.insert(insertAt, blockNode);
7798
+ tr.setSelection(state.Selection.near(tr.doc.resolve(insertAt + 1)));
7799
+ dispatch(tr.scrollIntoView());
7800
+ return true;
7801
+ }
7802
+ var ListIndent = Extension.create({
7803
+ name: "listIndent",
7804
+ addKeyboardShortcuts() {
7805
+ const { editor } = this;
7806
+ return {
7807
+ Tab: () => {
7808
+ if (!editor) return false;
7809
+ return indentBlockAsListChild(editor.state, editor.view.dispatch);
7810
+ },
7811
+ "Shift-Tab": () => {
7812
+ if (!editor) return false;
7813
+ return outdentBlockFromListItem(editor.state, editor.view.dispatch);
7814
+ }
7815
+ };
7816
+ }
7817
+ });
6803
7818
  var characterCountPluginKey = new state.PluginKey("characterCount");
6804
7819
  var CharacterCount = Extension.create({
6805
7820
  name: "characterCount",
@@ -7226,6 +8241,10 @@ var LineHeight = Extension.create({
7226
8241
  }
7227
8242
  });
7228
8243
  function generateUUID() {
8244
+ if (typeof globalThis !== "undefined") {
8245
+ const c = globalThis.crypto;
8246
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
8247
+ }
7229
8248
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
7230
8249
  const r = Math.random() * 16 | 0;
7231
8250
  const v = c === "x" ? r : r & 3 | 8;
@@ -7311,15 +8330,31 @@ var UniqueID = Extension.create({
7311
8330
  );
7312
8331
  };
7313
8332
  const assignMissingIDs = (doc, tr) => {
8333
+ const seen = /* @__PURE__ */ new Set();
7314
8334
  doc.descendants((node, pos) => {
7315
8335
  if (!types.includes(node.type.name)) return;
7316
8336
  const existingID = node.attrs[attributeName];
7317
8337
  if (!existingID) {
7318
- tr.setNodeMarkup(pos, void 0, {
8338
+ let id = generateID();
8339
+ while (seen.has(id)) id = generateID();
8340
+ seen.add(id);
8341
+ tr.setNodeMarkup(tr.mapping.map(pos), void 0, {
7319
8342
  ...node.attrs,
7320
- [attributeName]: generateID()
8343
+ [attributeName]: id
7321
8344
  });
8345
+ return;
7322
8346
  }
8347
+ if (seen.has(existingID)) {
8348
+ let id = generateID();
8349
+ while (seen.has(id)) id = generateID();
8350
+ seen.add(id);
8351
+ tr.setNodeMarkup(tr.mapping.map(pos), void 0, {
8352
+ ...node.attrs,
8353
+ [attributeName]: id
8354
+ });
8355
+ return;
8356
+ }
8357
+ seen.add(existingID);
7323
8358
  });
7324
8359
  };
7325
8360
  return [
@@ -7356,7 +8391,148 @@ var UniqueID = Extension.create({
7356
8391
  ];
7357
8392
  }
7358
8393
  });
7359
- var Selection3 = Extension.create({
8394
+
8395
+ // src/extensions/BlockColor.ts
8396
+ var DEFAULT_BLOCK_COLORS = [
8397
+ "gray",
8398
+ "brown",
8399
+ "orange",
8400
+ "yellow",
8401
+ "green",
8402
+ "blue",
8403
+ "purple",
8404
+ "pink",
8405
+ "red"
8406
+ ];
8407
+ var DEFAULT_BLOCK_COLOR_TYPES = [
8408
+ "paragraph",
8409
+ "heading",
8410
+ "blockquote",
8411
+ "bulletList",
8412
+ "orderedList",
8413
+ "taskList",
8414
+ "listItem",
8415
+ "taskItem"
8416
+ ];
8417
+ function stripInlineColorConflicts(tr, state, from, to, which) {
8418
+ const textStyleType = state.schema.marks["textStyle"];
8419
+ if (!textStyleType) return;
8420
+ const inlineKeys = [];
8421
+ if (which === "text" || which === "both") inlineKeys.push("color", "colorToken");
8422
+ if (which === "bg" || which === "both") inlineKeys.push("backgroundColor", "backgroundColorToken");
8423
+ state.doc.nodesBetween(from, to, (node, pos) => {
8424
+ if (!node.isText) return true;
8425
+ const existing = node.marks.find((m) => m.type === textStyleType);
8426
+ if (!existing) return false;
8427
+ const hasConflict = inlineKeys.some((k) => existing.attrs[k] !== null && existing.attrs[k] !== void 0);
8428
+ if (!hasConflict) return false;
8429
+ const start = Math.max(pos, from);
8430
+ const end = Math.min(pos + node.nodeSize, to);
8431
+ const newAttrs = { ...existing.attrs };
8432
+ for (const k of inlineKeys) newAttrs[k] = null;
8433
+ const stillUsed = Object.values(newAttrs).some((v) => v !== null && v !== void 0);
8434
+ tr.removeMark(start, end, existing);
8435
+ if (stillUsed) tr.addMark(start, end, textStyleType.create(newAttrs));
8436
+ return false;
8437
+ });
8438
+ }
8439
+ var BlockColor = Extension.create({
8440
+ name: "blockColor",
8441
+ addOptions() {
8442
+ return {
8443
+ types: DEFAULT_BLOCK_COLOR_TYPES,
8444
+ bgColors: DEFAULT_BLOCK_COLORS,
8445
+ textColors: DEFAULT_BLOCK_COLORS
8446
+ };
8447
+ },
8448
+ // Defensive fallback: an empty `bgColors`/`textColors` array would render
8449
+ // a broken Colors UI (section title with only the null-reset swatch).
8450
+ // Replace empties with the default palette so users who pass `{}` as an
8451
+ // override still get a working picker. Runs AFTER `addOptions()` merges
8452
+ // user config with defaults, so this only fires for explicit empty arrays.
8453
+ onBeforeCreate() {
8454
+ if (this.options.bgColors.length === 0) this.options.bgColors = DEFAULT_BLOCK_COLORS;
8455
+ if (this.options.textColors.length === 0) this.options.textColors = DEFAULT_BLOCK_COLORS;
8456
+ },
8457
+ addGlobalAttributes() {
8458
+ return [
8459
+ {
8460
+ types: this.options.types,
8461
+ attributes: {
8462
+ bgColor: {
8463
+ default: null,
8464
+ parseHTML: (element) => element.getAttribute("data-bg-color"),
8465
+ renderHTML: (attributes) => {
8466
+ const v = attributes["bgColor"];
8467
+ if (!v) return null;
8468
+ return { "data-bg-color": v };
8469
+ }
8470
+ },
8471
+ textColor: {
8472
+ default: null,
8473
+ parseHTML: (element) => element.getAttribute("data-text-color"),
8474
+ renderHTML: (attributes) => {
8475
+ const v = attributes["textColor"];
8476
+ if (!v) return null;
8477
+ return { "data-text-color": v };
8478
+ }
8479
+ }
8480
+ }
8481
+ }
8482
+ ];
8483
+ },
8484
+ addCommands() {
8485
+ const types = this.options.types;
8486
+ const bgColors = this.options.bgColors;
8487
+ const textColors = this.options.textColors;
8488
+ function findTargetPos(state) {
8489
+ const { $from } = state.selection;
8490
+ for (let depth = $from.depth; depth >= 0; depth--) {
8491
+ const node = $from.node(depth);
8492
+ if (types.includes(node.type.name)) {
8493
+ return depth === 0 ? 0 : $from.before(depth);
8494
+ }
8495
+ }
8496
+ return null;
8497
+ }
8498
+ function setAttr(attr, palette) {
8499
+ return (color) => ({ state, dispatch }) => {
8500
+ if (color !== null && !palette.includes(color)) return false;
8501
+ const pos = findTargetPos(state);
8502
+ if (pos === null) return false;
8503
+ const node = state.doc.nodeAt(pos);
8504
+ if (!node) return false;
8505
+ if (dispatch) {
8506
+ const tr = state.tr.setNodeMarkup(pos, void 0, { ...node.attrs, [attr]: color });
8507
+ stripInlineColorConflicts(tr, state, pos, pos + node.nodeSize, attr === "textColor" ? "text" : "bg");
8508
+ dispatch(tr);
8509
+ }
8510
+ return true;
8511
+ };
8512
+ }
8513
+ return {
8514
+ setBlockBgColor: setAttr("bgColor", bgColors),
8515
+ setBlockTextColor: setAttr("textColor", textColors),
8516
+ unsetBlockColors: () => ({ state, dispatch }) => {
8517
+ const pos = findTargetPos(state);
8518
+ if (pos === null) return false;
8519
+ const node = state.doc.nodeAt(pos);
8520
+ if (!node) return false;
8521
+ if (dispatch) {
8522
+ const tr = state.tr.setNodeMarkup(pos, void 0, {
8523
+ ...node.attrs,
8524
+ bgColor: null,
8525
+ textColor: null
8526
+ });
8527
+ stripInlineColorConflicts(tr, state, pos, pos + node.nodeSize, "both");
8528
+ dispatch(tr);
8529
+ }
8530
+ return true;
8531
+ }
8532
+ };
8533
+ }
8534
+ });
8535
+ var Selection4 = Extension.create({
7360
8536
  name: "selection",
7361
8537
  addStorage() {
7362
8538
  return {
@@ -7746,9 +8922,19 @@ var TextColor = Extension.create({
7746
8922
  },
7747
8923
  renderHTML: (attributes) => {
7748
8924
  const color = attributes["color"];
7749
- if (!color) return null;
8925
+ const token = attributes["colorToken"];
8926
+ if (!color || token) return null;
7750
8927
  return { style: `color: ${color}` };
7751
8928
  }
8929
+ },
8930
+ colorToken: {
8931
+ default: null,
8932
+ parseHTML: (element) => element.getAttribute("data-text-color"),
8933
+ renderHTML: (attributes) => {
8934
+ const token = attributes["colorToken"];
8935
+ if (!token) return null;
8936
+ return { "data-text-color": token };
8937
+ }
7752
8938
  }
7753
8939
  }
7754
8940
  }
@@ -7756,13 +8942,28 @@ var TextColor = Extension.create({
7756
8942
  },
7757
8943
  addCommands() {
7758
8944
  return {
8945
+ // Hex-based color. Mutual exclusion: clears the named token so the
8946
+ // legacy hex picker and Notion-style picker can't write conflicting
8947
+ // values to the same mark.
7759
8948
  setTextColor: (color) => ({ commands }) => {
7760
- return commands.setMark("textStyle", { color });
8949
+ return commands.setMark("textStyle", { color, colorToken: null });
7761
8950
  },
7762
8951
  unsetTextColor: () => ({ commands }) => {
7763
8952
  if (!commands.setMark("textStyle", { color: null })) return false;
7764
8953
  commands.removeEmptyTextStyle();
7765
8954
  return true;
8955
+ },
8956
+ // Named-token color. Mutual exclusion: clears the hex `color` so the
8957
+ // theme-aware data attribute is the only source of truth.
8958
+ setTextColorToken: (token) => ({ commands }) => {
8959
+ if (!commands.setMark("textStyle", { colorToken: token, color: null })) return false;
8960
+ if (token === null) commands.removeEmptyTextStyle();
8961
+ return true;
8962
+ },
8963
+ unsetTextColorToken: () => ({ commands }) => {
8964
+ if (!commands.setMark("textStyle", { colorToken: null })) return false;
8965
+ commands.removeEmptyTextStyle();
8966
+ return true;
7766
8967
  }
7767
8968
  };
7768
8969
  },
@@ -7864,9 +9065,19 @@ var Highlight = Extension.create({
7864
9065
  },
7865
9066
  renderHTML: (attributes) => {
7866
9067
  const bg = attributes["backgroundColor"];
7867
- if (!bg) return null;
9068
+ const token = attributes["backgroundColorToken"];
9069
+ if (!bg || token) return null;
7868
9070
  return { style: `background-color: ${bg}` };
7869
9071
  }
9072
+ },
9073
+ backgroundColorToken: {
9074
+ default: null,
9075
+ parseHTML: (element) => element.getAttribute("data-bg-color"),
9076
+ renderHTML: (attributes) => {
9077
+ const token = attributes["backgroundColorToken"];
9078
+ if (!token) return null;
9079
+ return { "data-bg-color": token };
9080
+ }
7870
9081
  }
7871
9082
  }
7872
9083
  }
@@ -7875,9 +9086,12 @@ var Highlight = Extension.create({
7875
9086
  addCommands() {
7876
9087
  const defaultColor = this.options.defaultColor;
7877
9088
  return {
9089
+ // Hex-based highlight. Mutual exclusion: clears any named bg token so
9090
+ // the legacy hex picker and the Notion-style picker can't write
9091
+ // conflicting values to the same mark.
7878
9092
  setHighlight: (attributes) => ({ commands }) => {
7879
9093
  const color = attributes?.color ?? defaultColor;
7880
- return commands.setMark("textStyle", { backgroundColor: color });
9094
+ return commands.setMark("textStyle", { backgroundColor: color, backgroundColorToken: null });
7881
9095
  },
7882
9096
  unsetHighlight: () => ({ commands }) => {
7883
9097
  if (!commands.setMark("textStyle", { backgroundColor: null })) return false;
@@ -7893,12 +9107,12 @@ var Highlight = Extension.create({
7893
9107
  if (empty) {
7894
9108
  const marks = state.storedMarks ?? state.doc.resolve(from).marks();
7895
9109
  const mark = markType.isInSet(marks);
7896
- hasHighlight = !!mark?.attrs["backgroundColor"];
9110
+ hasHighlight = !!mark?.attrs["backgroundColor"] || !!mark?.attrs["backgroundColorToken"];
7897
9111
  } else {
7898
9112
  state.doc.nodesBetween(from, to, (node) => {
7899
9113
  if (hasHighlight) return false;
7900
9114
  const mark = markType.isInSet(node.marks);
7901
- if (mark?.attrs["backgroundColor"]) {
9115
+ if (mark?.attrs["backgroundColor"] || mark?.attrs["backgroundColorToken"]) {
7902
9116
  hasHighlight = true;
7903
9117
  return false;
7904
9118
  }
@@ -7906,11 +9120,26 @@ var Highlight = Extension.create({
7906
9120
  });
7907
9121
  }
7908
9122
  if (hasHighlight) {
7909
- commands.setMark("textStyle", { backgroundColor: null });
9123
+ commands.setMark("textStyle", { backgroundColor: null, backgroundColorToken: null });
7910
9124
  commands.removeEmptyTextStyle();
7911
9125
  return true;
7912
9126
  }
7913
- return commands.setMark("textStyle", { backgroundColor: color });
9127
+ return commands.setMark("textStyle", { backgroundColor: color, backgroundColorToken: null });
9128
+ },
9129
+ // Named-token highlight. Mutual exclusion: clears the hex
9130
+ // backgroundColor so the theme-aware data attribute is the sole source
9131
+ // of truth.
9132
+ setBackgroundColorToken: (token) => ({ commands }) => {
9133
+ if (!commands.setMark("textStyle", { backgroundColorToken: token, backgroundColor: null })) {
9134
+ return false;
9135
+ }
9136
+ if (token === null) commands.removeEmptyTextStyle();
9137
+ return true;
9138
+ },
9139
+ unsetBackgroundColorToken: () => ({ commands }) => {
9140
+ if (!commands.setMark("textStyle", { backgroundColorToken: null })) return false;
9141
+ commands.removeEmptyTextStyle();
9142
+ return true;
7914
9143
  }
7915
9144
  };
7916
9145
  },
@@ -8154,6 +9383,61 @@ var FontSize = Extension.create({
8154
9383
  }
8155
9384
  });
8156
9385
 
9386
+ // src/extensions/NotionColorPicker.ts
9387
+ var DEFAULT_NOTION_COLOR_PALETTE = Object.freeze([
9388
+ "gray",
9389
+ "brown",
9390
+ "orange",
9391
+ "yellow",
9392
+ "green",
9393
+ "blue",
9394
+ "purple",
9395
+ "pink",
9396
+ "red"
9397
+ ]);
9398
+ var NotionColorPicker = Extension.create({
9399
+ name: "notionColorPicker",
9400
+ // The picker writes textStyle.colorToken / textStyle.backgroundColorToken,
9401
+ // so TextStyle must be present. TextColor and Highlight are recommended
9402
+ // (they own those attribute schemas) but not strictly required: a host app
9403
+ // could supply its own attribute providers if it really wants to.
9404
+ dependencies: ["textStyle"],
9405
+ addOptions() {
9406
+ return {
9407
+ palette: DEFAULT_NOTION_COLOR_PALETTE
9408
+ };
9409
+ },
9410
+ addStorage() {
9411
+ return {
9412
+ isOpen: false
9413
+ };
9414
+ },
9415
+ addToolbarItems() {
9416
+ return [
9417
+ {
9418
+ type: "button",
9419
+ name: "notionColor",
9420
+ // emitEvent takes priority over `command` at click time. The `command`
9421
+ // field is required by the ToolbarButton schema, so we point it at a
9422
+ // harmless built-in that fires only if a host disables the popover UI.
9423
+ command: "focus",
9424
+ // The "A with an underline" glyph is the closest match in our icon
9425
+ // set; the framework wrapper can repaint the underline to reflect
9426
+ // the current selection's color.
9427
+ icon: "textAUnderline",
9428
+ label: "Text and background color",
9429
+ emitEvent: "notionColorOpen",
9430
+ group: "textStyle",
9431
+ priority: 250,
9432
+ // Bubble-menu-only: the legacy hex pickers (TextColor / Highlight)
9433
+ // stay in the main toolbar; this token-based picker is a Notion-mode
9434
+ // affordance surfaced beside the text-format buttons in the bubble.
9435
+ toolbar: false
9436
+ }
9437
+ ];
9438
+ }
9439
+ });
9440
+
8157
9441
  // src/extensions/ClearFormatting.ts
8158
9442
  var ClearFormatting = Extension.create({
8159
9443
  name: "clearFormatting",
@@ -8242,9 +9526,17 @@ function linkPopoverPlugin({ editor, markType, protocols }) {
8242
9526
  return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
8243
9527
  }
8244
9528
  };
9529
+ if (anchorElement) {
9530
+ const editorEl = anchorElement.closest(".dm-editor");
9531
+ if (editorEl && el.parentElement !== editorEl) {
9532
+ editorEl.appendChild(el);
9533
+ }
9534
+ } else if (el.parentElement !== document.body) {
9535
+ document.body.appendChild(el);
9536
+ }
8245
9537
  cleanupFloating?.();
8246
9538
  cleanupFloating = positionFloating(reference, el, {
8247
- placement: "bottom",
9539
+ placement: anchorElement ? "bottom-start" : "bottom",
8248
9540
  offsetValue: 4
8249
9541
  });
8250
9542
  input.focus();
@@ -8501,6 +9793,7 @@ function createBubbleMenuPlugin(options) {
8501
9793
  if (!target) return;
8502
9794
  if (element.contains(target)) return;
8503
9795
  if (editor.view.dom.contains(target)) return;
9796
+ if (target instanceof HTMLElement && target.closest("[data-dm-editor-ui]")) return;
8504
9797
  hideMenu();
8505
9798
  suppressed = true;
8506
9799
  };
@@ -8577,8 +9870,10 @@ function createBubbleMenuPlugin(options) {
8577
9870
  });
8578
9871
  };
8579
9872
  const onBlur = ({ event }) => {
8580
- if (event.relatedTarget && element.contains(event.relatedTarget)) {
8581
- return;
9873
+ const related = event.relatedTarget;
9874
+ if (related instanceof HTMLElement) {
9875
+ if (element.contains(related)) return;
9876
+ if (related.closest("[data-dm-editor-ui]")) return;
8582
9877
  }
8583
9878
  hideMenu();
8584
9879
  };
@@ -8678,132 +9973,6 @@ var BubbleMenu = Extension.create({
8678
9973
  ];
8679
9974
  }
8680
9975
  });
8681
- var floatingMenuPluginKey = new state.PluginKey("floatingMenu");
8682
- function defaultShouldShow2({
8683
- editor,
8684
- state
8685
- }) {
8686
- if (!editor.isEditable) return false;
8687
- const { selection } = state;
8688
- const { $from, empty } = selection;
8689
- if (!empty) return false;
8690
- if ($from.parent.type.name !== "paragraph") return false;
8691
- if ($from.parent.content.size !== 0) return false;
8692
- if ($from.parentOffset !== 0) return false;
8693
- return true;
8694
- }
8695
- function createFloatingMenuPlugin(options) {
8696
- const {
8697
- pluginKey,
8698
- editor,
8699
- element,
8700
- shouldShow = defaultShouldShow2,
8701
- offset: offset2 = 0
8702
- } = options;
8703
- if (!element.getAttribute("role")) {
8704
- element.setAttribute("role", "toolbar");
8705
- element.setAttribute("aria-label", "Floating menu");
8706
- }
8707
- let cleanupFloating = null;
8708
- const updatePosition = (view) => {
8709
- const { selection } = view.state;
8710
- const { $from } = selection;
8711
- const depth = $from.depth;
8712
- const startPos = $from.start(depth);
8713
- const domNode = view.nodeDOM(startPos - 1);
8714
- if (domNode instanceof HTMLElement) {
8715
- cleanupFloating?.();
8716
- cleanupFloating = positionFloatingOnce(domNode, element, {
8717
- placement: "bottom-start",
8718
- offsetValue: offset2
8719
- });
8720
- element.setAttribute("data-show", "");
8721
- }
8722
- };
8723
- const hideMenu = () => {
8724
- cleanupFloating?.();
8725
- cleanupFloating = null;
8726
- element.removeAttribute("data-show");
8727
- };
8728
- hideMenu();
8729
- return new state.Plugin({
8730
- key: pluginKey,
8731
- view: (editorView) => {
8732
- const editorEl = editorView.dom.closest(".dm-editor");
8733
- if (editorEl && element.parentElement !== editorEl) {
8734
- editorEl.appendChild(element);
8735
- }
8736
- const onFocus = () => {
8737
- const visible = shouldShow({
8738
- editor,
8739
- view: editor.view,
8740
- state: editor.view.state
8741
- });
8742
- if (visible) {
8743
- updatePosition(editor.view);
8744
- } else {
8745
- hideMenu();
8746
- }
8747
- };
8748
- const onBlur = ({ event }) => {
8749
- if (event.relatedTarget && element.contains(event.relatedTarget)) {
8750
- return;
8751
- }
8752
- hideMenu();
8753
- };
8754
- editor.on("focus", onFocus);
8755
- editor.on("blur", onBlur);
8756
- return {
8757
- update: (view) => {
8758
- const visible = shouldShow({
8759
- editor,
8760
- view,
8761
- state: view.state
8762
- });
8763
- if (visible) {
8764
- updatePosition(view);
8765
- } else {
8766
- hideMenu();
8767
- }
8768
- },
8769
- destroy: () => {
8770
- hideMenu();
8771
- editor.off("focus", onFocus);
8772
- editor.off("blur", onBlur);
8773
- }
8774
- };
8775
- }
8776
- });
8777
- }
8778
- var FloatingMenu = Extension.create({
8779
- name: "floatingMenu",
8780
- addOptions() {
8781
- return {
8782
- element: null,
8783
- shouldShow: defaultShouldShow2,
8784
- offset: 0
8785
- };
8786
- },
8787
- addProseMirrorPlugins() {
8788
- const { element, shouldShow, offset: offset2 } = this.options;
8789
- if (!element) {
8790
- return [];
8791
- }
8792
- const editor = this.editor;
8793
- if (!editor) {
8794
- return [];
8795
- }
8796
- return [
8797
- createFloatingMenuPlugin({
8798
- pluginKey: floatingMenuPluginKey,
8799
- editor,
8800
- element,
8801
- shouldShow,
8802
- offset: offset2
8803
- })
8804
- ];
8805
- }
8806
- });
8807
9976
 
8808
9977
  // src/extensions/StarterKit.ts
8809
9978
  var StarterKit = Extension.create({
@@ -8846,6 +10015,7 @@ var StarterKit = Extension.create({
8846
10015
  maybeAdd(Gapcursor, this.options.gapcursor);
8847
10016
  maybeAdd(TrailingNode, this.options.trailingNode);
8848
10017
  maybeAdd(ListKeymap, this.options.listKeymap);
10018
+ maybeAdd(ListIndent, this.options.listIndent);
8849
10019
  maybeAdd(LinkPopover, this.options.linkPopover);
8850
10020
  maybeAdd(SelectionDecoration, this.options.selectionDecoration);
8851
10021
  return extensions;
@@ -8860,6 +10030,7 @@ Object.defineProperty(exports, "PluginKey", {
8860
10030
  get: function () { return state.PluginKey; }
8861
10031
  });
8862
10032
  exports.BaseKeymap = BaseKeymap;
10033
+ exports.BlockColor = BlockColor;
8863
10034
  exports.Blockquote = Blockquote;
8864
10035
  exports.Bold = Bold;
8865
10036
  exports.BubbleMenu = BubbleMenu;
@@ -8871,7 +10042,10 @@ exports.ClearFormatting = ClearFormatting;
8871
10042
  exports.Code = Code;
8872
10043
  exports.CodeBlock = CodeBlock;
8873
10044
  exports.CommandManager = CommandManager;
10045
+ exports.DEFAULT_BLOCK_COLORS = DEFAULT_BLOCK_COLORS;
10046
+ exports.DEFAULT_BLOCK_COLOR_TYPES = DEFAULT_BLOCK_COLOR_TYPES;
8874
10047
  exports.DEFAULT_HIGHLIGHT_COLORS = DEFAULT_HIGHLIGHT_COLORS;
10048
+ exports.DEFAULT_NOTION_COLOR_PALETTE = DEFAULT_NOTION_COLOR_PALETTE;
8875
10049
  exports.DEFAULT_TEXT_COLORS = DEFAULT_TEXT_COLORS;
8876
10050
  exports.Document = Document;
8877
10051
  exports.Dropcursor = Dropcursor;
@@ -8879,7 +10053,8 @@ exports.Editor = Editor;
8879
10053
  exports.EventEmitter = EventEmitter;
8880
10054
  exports.Extension = Extension;
8881
10055
  exports.ExtensionManager = ExtensionManager;
8882
- exports.FloatingMenu = FloatingMenu;
10056
+ exports.FLOATING_MENU_NO_FOCUS = FLOATING_MENU_NO_FOCUS;
10057
+ exports.FloatingMenuController = FloatingMenuController;
8883
10058
  exports.Focus = Focus;
8884
10059
  exports.FontFamily = FontFamily;
8885
10060
  exports.FontSize = FontSize;
@@ -8891,17 +10066,20 @@ exports.History = History;
8891
10066
  exports.HorizontalRule = HorizontalRule;
8892
10067
  exports.InvisibleChars = InvisibleChars;
8893
10068
  exports.Italic = Italic;
10069
+ exports.LIST_ITEM_TYPE_NAMES = LIST_ITEM_TYPE_NAMES;
8894
10070
  exports.LineHeight = LineHeight;
8895
10071
  exports.Link = Link;
8896
10072
  exports.LinkPopover = LinkPopover;
10073
+ exports.ListIndent = ListIndent;
8897
10074
  exports.ListItem = ListItem;
8898
10075
  exports.ListKeymap = ListKeymap;
8899
10076
  exports.Mark = Mark;
8900
- exports.Node = Node;
10077
+ exports.Node = Node2;
10078
+ exports.NotionColorPicker = NotionColorPicker;
8901
10079
  exports.OrderedList = OrderedList;
8902
10080
  exports.Paragraph = Paragraph;
8903
10081
  exports.Placeholder = Placeholder;
8904
- exports.Selection = Selection3;
10082
+ exports.Selection = Selection4;
8905
10083
  exports.SelectionDecoration = SelectionDecoration;
8906
10084
  exports.StarterKit = StarterKit;
8907
10085
  exports.Strike = Strike;
@@ -8934,27 +10112,36 @@ exports.createBubbleMenuPlugin = createBubbleMenuPlugin;
8934
10112
  exports.createCanChecker = createCanChecker;
8935
10113
  exports.createChainBuilder = createChainBuilder;
8936
10114
  exports.createDocument = createDocument;
8937
- exports.createFloatingMenuPlugin = createFloatingMenuPlugin;
8938
10115
  exports.defaultBlockAt = defaultBlockAt;
10116
+ exports.defaultBubbleContexts = defaultBubbleContexts;
8939
10117
  exports.defaultIcons = defaultIcons;
8940
10118
  exports.deleteSelection = deleteSelection;
8941
10119
  exports.findChildren = findChildren;
10120
+ exports.findListItemAncestorDepth = findListItemAncestorDepth;
8942
10121
  exports.findParentNode = findParentNode;
8943
- exports.floatingMenuPluginKey = floatingMenuPluginKey;
8944
10122
  exports.focus = focus;
8945
10123
  exports.focusPluginKey = focusPluginKey;
8946
10124
  exports.generateHTML = generateHTML;
8947
10125
  exports.generateJSON = generateJSON;
8948
10126
  exports.generateText = generateText;
10127
+ exports.getListItemCursorContext = getListItemCursorContext;
8949
10128
  exports.getMarkRange = getMarkRange;
10129
+ exports.groupFloatingMenuItems = groupFloatingMenuItems;
10130
+ exports.indentBlockAsListChild = indentBlockAsListChild;
8950
10131
  exports.inlineStyles = inlineStyles;
10132
+ exports.insertAsListItemChild = insertAsListItemChild;
10133
+ exports.insertChildrenZoneSibling = insertChildrenZoneSibling;
8951
10134
  exports.insertContent = insertContent;
8952
10135
  exports.insertText = insertText;
8953
10136
  exports.invisibleCharsPluginKey = invisibleCharsPluginKey;
8954
10137
  exports.isDocumentEmpty = isDocumentEmpty;
10138
+ exports.isInListItemLabel = isInListItemLabel;
10139
+ exports.isInsideListItem = isInsideListItem;
8955
10140
  exports.isNodeEmpty = isNodeEmpty;
8956
10141
  exports.isValidUrl = isValidUrl;
8957
10142
  exports.lift = lift;
10143
+ exports.liftCurrentListItem = liftCurrentListItem;
10144
+ exports.liftEmptyChildrenZoneParagraph = liftEmptyChildrenZoneParagraph;
8958
10145
  exports.linkClickPlugin = linkClickPlugin;
8959
10146
  exports.linkClickPluginKey = linkClickPluginKey;
8960
10147
  exports.linkExitPlugin = linkExitPlugin;
@@ -8964,6 +10151,7 @@ exports.linkPastePluginKey = linkPastePluginKey;
8964
10151
  exports.markInputRule = markInputRule;
8965
10152
  exports.markInputRulePatterns = markInputRulePatterns;
8966
10153
  exports.nodeInputRule = nodeInputRule;
10154
+ exports.outdentBlockFromListItem = outdentBlockFromListItem;
8967
10155
  exports.placeholderPluginKey = placeholderPluginKey;
8968
10156
  exports.positionFloating = positionFloating;
8969
10157
  exports.positionFloatingOnce = positionFloatingOnce;
@@ -8974,6 +10162,8 @@ exports.selectionDecorationPluginKey = selectionDecorationPluginKey;
8974
10162
  exports.setBlockType = setBlockType;
8975
10163
  exports.setContent = setContent;
8976
10164
  exports.setMark = setMark;
10165
+ exports.splitListForInsert = splitListForInsert;
10166
+ exports.stripInlineColorConflicts = stripInlineColorConflicts;
8977
10167
  exports.textInputRule = textInputRule;
8978
10168
  exports.textblockTypeInputRule = textblockTypeInputRule;
8979
10169
  exports.toggleBlockType = toggleBlockType;
@@ -8986,5 +10176,6 @@ exports.unsetMark = unsetMark;
8986
10176
  exports.updateAttributes = updateAttributes;
8987
10177
  exports.wrapIn = wrapIn;
8988
10178
  exports.wrappingInputRule = wrappingInputRule;
10179
+ exports.writeToClipboard = writeToClipboard;
8989
10180
  //# sourceMappingURL=index.cjs.map
8990
10181
  //# sourceMappingURL=index.cjs.map