@domternal/core 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/index.cjs +1428 -221
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1835 -1360
- package/dist/index.d.ts +1835 -1360
- package/dist/index.js +1399 -214
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
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 } }
|
|
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
|
-
*
|
|
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)
|
|
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 (
|
|
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(
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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,239 @@ 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/copyThemeClass.ts
|
|
4039
|
+
function copyThemeClass(view, target) {
|
|
4040
|
+
const stale = [];
|
|
4041
|
+
target.classList.forEach((cls) => {
|
|
4042
|
+
if (cls.startsWith("dm-theme-")) stale.push(cls);
|
|
4043
|
+
});
|
|
4044
|
+
for (const cls of stale) target.classList.remove(cls);
|
|
4045
|
+
const source = view.dom.closest('[class*="dm-theme-"]') ?? view.dom.closest(".dm-editor");
|
|
4046
|
+
if (!source) return;
|
|
4047
|
+
source.classList.forEach((cls) => {
|
|
4048
|
+
if (cls.startsWith("dm-theme-")) target.classList.add(cls);
|
|
4049
|
+
});
|
|
4050
|
+
}
|
|
4051
|
+
|
|
4052
|
+
// src/utils/defaultBubbleContexts.ts
|
|
4053
|
+
var NOTION_MODE_CLASS = "dm-notion-mode";
|
|
4054
|
+
var NOTION_TEXT_CONTEXT = Object.freeze([
|
|
4055
|
+
"bold",
|
|
4056
|
+
"italic",
|
|
4057
|
+
"underline",
|
|
4058
|
+
"strike",
|
|
4059
|
+
"code",
|
|
4060
|
+
"|",
|
|
4061
|
+
"link",
|
|
4062
|
+
"|",
|
|
4063
|
+
"textAlign"
|
|
4064
|
+
]);
|
|
4065
|
+
var STANDARD_TEXT_CONTEXT = Object.freeze([
|
|
4066
|
+
"bold",
|
|
4067
|
+
"italic",
|
|
4068
|
+
"underline",
|
|
4069
|
+
"strike",
|
|
4070
|
+
"code",
|
|
4071
|
+
"|",
|
|
4072
|
+
"link"
|
|
4073
|
+
]);
|
|
4074
|
+
function defaultBubbleContexts(editor) {
|
|
4075
|
+
const inNotionMode = editor.view.dom.closest("." + NOTION_MODE_CLASS) !== null;
|
|
4076
|
+
const text = inNotionMode ? NOTION_TEXT_CONTEXT : STANDARD_TEXT_CONTEXT;
|
|
4077
|
+
return { text: [...text] };
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
// src/utils/insertAsListItemChild.ts
|
|
4081
|
+
var LIST_ITEM_TYPES2 = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
|
|
4082
|
+
var LIST_WRAPPER_TYPES = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
|
|
4083
|
+
function insertAsListItemChild(args) {
|
|
4084
|
+
const { tr, wrapperPos, targetItemPos, blockNode, sourceRange } = args;
|
|
4085
|
+
if (wrapperPos < 0 || wrapperPos >= tr.doc.content.size) return { ok: false };
|
|
4086
|
+
const wrapper = tr.doc.nodeAt(wrapperPos);
|
|
4087
|
+
if (!wrapper || !LIST_WRAPPER_TYPES.has(wrapper.type.name)) return { ok: false };
|
|
4088
|
+
if (wrapper.childCount === 0) return { ok: false };
|
|
4089
|
+
let targetItem;
|
|
4090
|
+
let targetItemStart;
|
|
4091
|
+
if (targetItemPos !== void 0) {
|
|
4092
|
+
if (targetItemPos < 0 || targetItemPos >= tr.doc.content.size) return { ok: false };
|
|
4093
|
+
const candidate = tr.doc.nodeAt(targetItemPos);
|
|
4094
|
+
if (!candidate || !LIST_ITEM_TYPES2.has(candidate.type.name)) return { ok: false };
|
|
4095
|
+
if (targetItemPos < wrapperPos + 1 || targetItemPos >= wrapperPos + wrapper.nodeSize) {
|
|
4096
|
+
return { ok: false };
|
|
4097
|
+
}
|
|
4098
|
+
targetItem = candidate;
|
|
4099
|
+
targetItemStart = targetItemPos;
|
|
4100
|
+
} else {
|
|
4101
|
+
const last = wrapper.lastChild;
|
|
4102
|
+
if (!last || !LIST_ITEM_TYPES2.has(last.type.name)) return { ok: false };
|
|
4103
|
+
let pos = wrapperPos + 1;
|
|
4104
|
+
for (let i = 0; i < wrapper.childCount - 1; i++) {
|
|
4105
|
+
pos += wrapper.child(i).nodeSize;
|
|
4106
|
+
}
|
|
4107
|
+
targetItem = last;
|
|
4108
|
+
targetItemStart = pos;
|
|
4109
|
+
}
|
|
4110
|
+
if (!targetItem.canReplaceWith(targetItem.childCount, targetItem.childCount, blockNode.type)) {
|
|
4111
|
+
return { ok: false };
|
|
4112
|
+
}
|
|
4113
|
+
const targetItemContentEnd = targetItemStart + targetItem.nodeSize - 1;
|
|
4114
|
+
if (sourceRange) {
|
|
4115
|
+
const { from, to } = sourceRange;
|
|
4116
|
+
if (targetItemContentEnd >= from && targetItemContentEnd <= to) {
|
|
4117
|
+
return { ok: false };
|
|
4118
|
+
}
|
|
4119
|
+
tr.delete(from, to);
|
|
4120
|
+
const adjustedInsertPos = targetItemContentEnd > from ? targetItemContentEnd - (to - from) : targetItemContentEnd;
|
|
4121
|
+
tr.insert(adjustedInsertPos, blockNode);
|
|
4122
|
+
return { ok: true, insertedAt: adjustedInsertPos };
|
|
4123
|
+
}
|
|
4124
|
+
tr.insert(targetItemContentEnd, blockNode);
|
|
4125
|
+
return { ok: true, insertedAt: targetItemContentEnd };
|
|
4126
|
+
}
|
|
4127
|
+
function liftEmptyChildrenZoneParagraph(state$1, dispatch, ctx) {
|
|
4128
|
+
if (!ctx.isInChildrenZone || !ctx.paragraphIsEmpty) return false;
|
|
4129
|
+
const { $from } = state$1.selection;
|
|
4130
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
4131
|
+
if ($from.parent.content.size !== 0) return false;
|
|
4132
|
+
const paragraphType = state$1.schema.nodes["paragraph"];
|
|
4133
|
+
if (!paragraphType) return false;
|
|
4134
|
+
const range = $from.blockRange();
|
|
4135
|
+
if (range) {
|
|
4136
|
+
const target = transform.liftTarget(range);
|
|
4137
|
+
if (target !== null) {
|
|
4138
|
+
if (!dispatch) return true;
|
|
4139
|
+
const tr2 = state$1.tr.lift(range, target);
|
|
4140
|
+
const oldParaStart = $from.before($from.depth);
|
|
4141
|
+
const newParaStart = tr2.mapping.map(oldParaStart);
|
|
4142
|
+
tr2.setSelection(state.TextSelection.create(tr2.doc, newParaStart + 1));
|
|
4143
|
+
dispatch(tr2.scrollIntoView());
|
|
4144
|
+
return true;
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
const paraStart = $from.before($from.depth);
|
|
4148
|
+
const paraEnd = $from.after($from.depth);
|
|
4149
|
+
const itemNode = state$1.doc.nodeAt(ctx.itemPos);
|
|
4150
|
+
if (!itemNode) return false;
|
|
4151
|
+
const itemEnd = ctx.itemPos + itemNode.nodeSize;
|
|
4152
|
+
const wrapperNode = state$1.doc.nodeAt(ctx.wrapperPos);
|
|
4153
|
+
if (!wrapperNode) return false;
|
|
4154
|
+
const wrapperEnd = ctx.wrapperPos + wrapperNode.nodeSize;
|
|
4155
|
+
const isLastItem = ctx.itemIndexInWrapper === wrapperNode.childCount - 1;
|
|
4156
|
+
const wrapperParentDepth = ctx.itemDepth - 2;
|
|
4157
|
+
if (wrapperParentDepth < 0) return false;
|
|
4158
|
+
const wrapperParent = $from.node(wrapperParentDepth);
|
|
4159
|
+
const wrapperIndexInParent = $from.index(wrapperParentDepth);
|
|
4160
|
+
if (!wrapperParent.canReplaceWith(
|
|
4161
|
+
wrapperIndexInParent + 1,
|
|
4162
|
+
wrapperIndexInParent + 1,
|
|
4163
|
+
paragraphType
|
|
4164
|
+
)) {
|
|
4165
|
+
return false;
|
|
4166
|
+
}
|
|
4167
|
+
if (!dispatch) return true;
|
|
4168
|
+
const newPara = paragraphType.create();
|
|
4169
|
+
const tr = state$1.tr;
|
|
4170
|
+
if (isLastItem) {
|
|
4171
|
+
tr.delete(paraStart, paraEnd);
|
|
4172
|
+
const insertPos = tr.mapping.map(wrapperEnd);
|
|
4173
|
+
tr.insert(insertPos, newPara);
|
|
4174
|
+
tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
|
|
4175
|
+
} else {
|
|
4176
|
+
tr.delete(paraStart, paraEnd);
|
|
4177
|
+
const splitAt = tr.mapping.map(itemEnd);
|
|
4178
|
+
tr.split(splitAt, 1);
|
|
4179
|
+
const insertPos = splitAt + 1;
|
|
4180
|
+
tr.insert(insertPos, newPara);
|
|
4181
|
+
tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
|
|
4182
|
+
}
|
|
4183
|
+
dispatch(tr.scrollIntoView());
|
|
4184
|
+
return true;
|
|
4185
|
+
}
|
|
4186
|
+
function insertChildrenZoneSibling(state$1, dispatch, ctx) {
|
|
4187
|
+
if (!ctx.isInChildrenZone || !ctx.paragraphIsEmpty) return false;
|
|
4188
|
+
const paragraphType = state$1.schema.nodes["paragraph"];
|
|
4189
|
+
if (!paragraphType) return false;
|
|
4190
|
+
const { $from } = state$1.selection;
|
|
4191
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
4192
|
+
if ($from.parent.content.size !== 0) return false;
|
|
4193
|
+
if (!dispatch) return true;
|
|
4194
|
+
const insertPos = $from.after($from.depth);
|
|
4195
|
+
const tr = state$1.tr.insert(insertPos, paragraphType.create());
|
|
4196
|
+
tr.setSelection(state.TextSelection.create(tr.doc, insertPos + 1));
|
|
4197
|
+
dispatch(tr.scrollIntoView());
|
|
4198
|
+
return true;
|
|
4199
|
+
}
|
|
4200
|
+
function splitListForInsert(state, tr) {
|
|
4201
|
+
if (!tr.selection.empty) return null;
|
|
4202
|
+
if (tr.steps.length !== 0) return null;
|
|
4203
|
+
const { $from } = tr.selection;
|
|
4204
|
+
const listItemDepth = findListItemAncestorDepth($from);
|
|
4205
|
+
if (listItemDepth === -1) return null;
|
|
4206
|
+
if ($from.index(listItemDepth) !== 0) return null;
|
|
4207
|
+
const listDepth = listItemDepth - 1;
|
|
4208
|
+
const listItemNode = $from.node(listItemDepth);
|
|
4209
|
+
const labelParagraph = listItemNode.firstChild;
|
|
4210
|
+
const labelIsEmpty = listItemNode.childCount === 1 && (labelParagraph?.content.size ?? 0) === 0;
|
|
4211
|
+
if (labelIsEmpty) {
|
|
4212
|
+
const listItemType = listItemNode.type;
|
|
4213
|
+
let safetyLimit = 10;
|
|
4214
|
+
while (safetyLimit-- > 0) {
|
|
4215
|
+
const stepState = state.apply(tr);
|
|
4216
|
+
const liftOk = schemaList.liftListItem(listItemType)(stepState, (liftTr) => {
|
|
4217
|
+
for (const step of liftTr.steps) tr.step(step);
|
|
4218
|
+
if (liftTr.selectionSet) tr.setSelection(liftTr.selection);
|
|
4219
|
+
});
|
|
4220
|
+
if (!liftOk) break;
|
|
4221
|
+
if (!isInsideListItem(tr.selection.$from)) break;
|
|
4222
|
+
}
|
|
4223
|
+
const $afterLift = tr.selection.$from;
|
|
4224
|
+
if (isInsideListItem($afterLift)) return null;
|
|
4225
|
+
return { from: $afterLift.before(), to: $afterLift.after() };
|
|
4226
|
+
}
|
|
4227
|
+
const listNode = $from.node(listDepth);
|
|
4228
|
+
const indexInList = $from.index(listDepth);
|
|
4229
|
+
const isLast = indexInList === listNode.childCount - 1;
|
|
4230
|
+
if (isLast) {
|
|
4231
|
+
const pos2 = $from.after(listDepth);
|
|
4232
|
+
return { from: pos2, to: pos2 };
|
|
4233
|
+
}
|
|
4234
|
+
const splitPos = $from.after(listItemDepth);
|
|
4235
|
+
tr.split(splitPos, 1);
|
|
4236
|
+
const pos = splitPos + 1;
|
|
4237
|
+
return { from: pos, to: pos };
|
|
4238
|
+
}
|
|
4239
|
+
|
|
3882
4240
|
// src/Node.ts
|
|
3883
|
-
var
|
|
4241
|
+
var Node2 = class _Node extends Extension {
|
|
3884
4242
|
/**
|
|
3885
4243
|
* Node type identifier
|
|
3886
4244
|
* Distinguishes nodes from extensions and marks
|
|
@@ -3942,25 +4300,12 @@ var Node = class _Node extends Extension {
|
|
|
3942
4300
|
return new _Node(config);
|
|
3943
4301
|
}
|
|
3944
4302
|
/**
|
|
3945
|
-
* Creates a new node with merged options
|
|
3946
|
-
*
|
|
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
|
|
4303
|
+
* Creates a new node with merged options. Original node is not modified.
|
|
4304
|
+
* Options merge shallowly (object spread); see {@link Extension.configure}
|
|
4305
|
+
* for the nested-object gotcha and a workaround.
|
|
3953
4306
|
*
|
|
3954
4307
|
* @example
|
|
3955
4308
|
* 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
4309
|
*/
|
|
3965
4310
|
configure(options) {
|
|
3966
4311
|
const newConfig = {
|
|
@@ -4134,7 +4479,7 @@ var ToolbarController = class _ToolbarController {
|
|
|
4134
4479
|
_activeMap = /* @__PURE__ */ new Map();
|
|
4135
4480
|
/** Disabled state for each button (keyed by item.name) */
|
|
4136
4481
|
_disabledMap = /* @__PURE__ */ new Map();
|
|
4137
|
-
/** Expanded state for emitEvent buttons
|
|
4482
|
+
/** Expanded state for emitEvent buttons - true when their panel is open */
|
|
4138
4483
|
_expandedMap = /* @__PURE__ */ new Map();
|
|
4139
4484
|
/** Currently open dropdown name (null = none) */
|
|
4140
4485
|
_openDropdown = null;
|
|
@@ -4469,6 +4814,262 @@ var ToolbarController = class _ToolbarController {
|
|
|
4469
4814
|
}
|
|
4470
4815
|
};
|
|
4471
4816
|
|
|
4817
|
+
// src/utils/groupFloatingMenuItems.ts
|
|
4818
|
+
function groupFloatingMenuItems(items) {
|
|
4819
|
+
const map = /* @__PURE__ */ new Map();
|
|
4820
|
+
const order = [];
|
|
4821
|
+
for (const item of items) {
|
|
4822
|
+
const name = item.group ?? "";
|
|
4823
|
+
let list = map.get(name);
|
|
4824
|
+
if (!list) {
|
|
4825
|
+
list = [];
|
|
4826
|
+
map.set(name, list);
|
|
4827
|
+
order.push(name);
|
|
4828
|
+
}
|
|
4829
|
+
list.push(item);
|
|
4830
|
+
}
|
|
4831
|
+
const groups = [];
|
|
4832
|
+
for (const name of order) {
|
|
4833
|
+
const list = (map.get(name) ?? []).slice();
|
|
4834
|
+
list.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
4835
|
+
groups.push({ name, items: list });
|
|
4836
|
+
}
|
|
4837
|
+
return groups;
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
// src/FloatingMenuController.ts
|
|
4841
|
+
var FLOATING_MENU_NO_FOCUS = -1;
|
|
4842
|
+
var FloatingMenuController = class _FloatingMenuController {
|
|
4843
|
+
/**
|
|
4844
|
+
* Resolves an `items` option (array | function) against the editor's
|
|
4845
|
+
* default floating-menu items. Exposed as static so the plugin can
|
|
4846
|
+
* resolve once at init without constructing a controller.
|
|
4847
|
+
*/
|
|
4848
|
+
static resolveItems(editor, override) {
|
|
4849
|
+
const defaults = editor.floatingMenuItems;
|
|
4850
|
+
if (!override) return defaults;
|
|
4851
|
+
if (Array.isArray(override)) return override;
|
|
4852
|
+
try {
|
|
4853
|
+
return override(defaults, editor);
|
|
4854
|
+
} catch {
|
|
4855
|
+
return defaults;
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
/**
|
|
4859
|
+
* Executes a floating-menu item's command on the editor.
|
|
4860
|
+
* String commands are dispatched via `editor.commands[name](...args)`;
|
|
4861
|
+
* function commands are called directly.
|
|
4862
|
+
*/
|
|
4863
|
+
static executeItem(editor, item) {
|
|
4864
|
+
if (typeof item.command === "function") {
|
|
4865
|
+
item.command(editor);
|
|
4866
|
+
return;
|
|
4867
|
+
}
|
|
4868
|
+
const commands = editor.commands;
|
|
4869
|
+
const cmd = commands[item.command];
|
|
4870
|
+
if (!cmd) return;
|
|
4871
|
+
if (item.commandArgs?.length) {
|
|
4872
|
+
cmd(...item.commandArgs);
|
|
4873
|
+
} else {
|
|
4874
|
+
cmd();
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
editor;
|
|
4878
|
+
onChange;
|
|
4879
|
+
override;
|
|
4880
|
+
transactionHandler = null;
|
|
4881
|
+
_groups = [];
|
|
4882
|
+
_flatItems = [];
|
|
4883
|
+
_disabledMap = /* @__PURE__ */ new Map();
|
|
4884
|
+
_focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
4885
|
+
constructor(editor, onChange, override) {
|
|
4886
|
+
this.editor = editor;
|
|
4887
|
+
this.onChange = onChange;
|
|
4888
|
+
this.override = override;
|
|
4889
|
+
this.rebuild();
|
|
4890
|
+
}
|
|
4891
|
+
// === Getters ===
|
|
4892
|
+
get groups() {
|
|
4893
|
+
return this._groups;
|
|
4894
|
+
}
|
|
4895
|
+
get flatItems() {
|
|
4896
|
+
return this._flatItems;
|
|
4897
|
+
}
|
|
4898
|
+
get disabledMap() {
|
|
4899
|
+
return this._disabledMap;
|
|
4900
|
+
}
|
|
4901
|
+
get focusedIndex() {
|
|
4902
|
+
return this._focusedIndex;
|
|
4903
|
+
}
|
|
4904
|
+
/** True when keyboard focus is inside the menu (at least one item focused). */
|
|
4905
|
+
get isEntered() {
|
|
4906
|
+
return this._focusedIndex >= 0;
|
|
4907
|
+
}
|
|
4908
|
+
get itemCount() {
|
|
4909
|
+
return this._flatItems.length;
|
|
4910
|
+
}
|
|
4911
|
+
// === State Methods ===
|
|
4912
|
+
isDisabled(item) {
|
|
4913
|
+
return this._disabledMap.get(item.name) ?? false;
|
|
4914
|
+
}
|
|
4915
|
+
/**
|
|
4916
|
+
* Executes an item's command, then closes the menu keyboard focus.
|
|
4917
|
+
* Callers should refocus the editor after calling this.
|
|
4918
|
+
*/
|
|
4919
|
+
execute(item) {
|
|
4920
|
+
if (this.isDisabled(item)) return;
|
|
4921
|
+
_FloatingMenuController.executeItem(this.editor, item);
|
|
4922
|
+
this._focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
4923
|
+
this.onChange();
|
|
4924
|
+
}
|
|
4925
|
+
/**
|
|
4926
|
+
* Rebuilds items from the editor. Call when the editor's extensions
|
|
4927
|
+
* change (rare) or on explicit refresh. Notification is delegated to
|
|
4928
|
+
* `updateDisabledStates` which fires `onChange` only when a disabled
|
|
4929
|
+
* state flipped - wrappers that need to react to pure group-structure
|
|
4930
|
+
* changes do so by bumping their own render signal after constructing
|
|
4931
|
+
* / re-using the controller (see framework wrapper usage).
|
|
4932
|
+
*/
|
|
4933
|
+
rebuild() {
|
|
4934
|
+
const items = _FloatingMenuController.resolveItems(this.editor, this.override);
|
|
4935
|
+
this._groups = this.groupItems(items);
|
|
4936
|
+
this._flatItems = this._groups.flatMap((g) => g.items);
|
|
4937
|
+
if (this._focusedIndex >= this._flatItems.length) {
|
|
4938
|
+
this._focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
4939
|
+
}
|
|
4940
|
+
this.updateDisabledStates();
|
|
4941
|
+
}
|
|
4942
|
+
// === Keyboard Navigation (roving tabindex) ===
|
|
4943
|
+
/** Enter keyboard focus on the menu (first item). Called by the plugin's keymap. */
|
|
4944
|
+
enterMenu() {
|
|
4945
|
+
if (this._flatItems.length === 0) {
|
|
4946
|
+
this._focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
4947
|
+
return this._focusedIndex;
|
|
4948
|
+
}
|
|
4949
|
+
this._focusedIndex = 0;
|
|
4950
|
+
this.onChange();
|
|
4951
|
+
return this._focusedIndex;
|
|
4952
|
+
}
|
|
4953
|
+
/** Leave keyboard focus (Escape). Refocus of the editor is the caller's job. */
|
|
4954
|
+
leaveMenu() {
|
|
4955
|
+
if (this._focusedIndex === FLOATING_MENU_NO_FOCUS) return;
|
|
4956
|
+
this._focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
4957
|
+
this.onChange();
|
|
4958
|
+
}
|
|
4959
|
+
/** ArrowDown - wrap to first at end. */
|
|
4960
|
+
next() {
|
|
4961
|
+
if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
|
|
4962
|
+
const cur = this._focusedIndex < 0 ? -1 : this._focusedIndex;
|
|
4963
|
+
this._focusedIndex = (cur + 1) % this._flatItems.length;
|
|
4964
|
+
this.onChange();
|
|
4965
|
+
return this._focusedIndex;
|
|
4966
|
+
}
|
|
4967
|
+
/** ArrowUp - wrap to last at start. */
|
|
4968
|
+
prev() {
|
|
4969
|
+
if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
|
|
4970
|
+
const len = this._flatItems.length;
|
|
4971
|
+
const cur = this._focusedIndex < 0 ? len : this._focusedIndex;
|
|
4972
|
+
this._focusedIndex = (cur - 1 + len) % len;
|
|
4973
|
+
this.onChange();
|
|
4974
|
+
return this._focusedIndex;
|
|
4975
|
+
}
|
|
4976
|
+
first() {
|
|
4977
|
+
if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
|
|
4978
|
+
this._focusedIndex = 0;
|
|
4979
|
+
this.onChange();
|
|
4980
|
+
return this._focusedIndex;
|
|
4981
|
+
}
|
|
4982
|
+
last() {
|
|
4983
|
+
if (this._flatItems.length === 0) return FLOATING_MENU_NO_FOCUS;
|
|
4984
|
+
this._focusedIndex = this._flatItems.length - 1;
|
|
4985
|
+
this.onChange();
|
|
4986
|
+
return this._focusedIndex;
|
|
4987
|
+
}
|
|
4988
|
+
/** Set focused index directly (e.g. on pointer hover). */
|
|
4989
|
+
setFocusedIndex(index) {
|
|
4990
|
+
if (index < 0 || index >= this._flatItems.length) return;
|
|
4991
|
+
if (this._focusedIndex === index) return;
|
|
4992
|
+
this._focusedIndex = index;
|
|
4993
|
+
this.onChange();
|
|
4994
|
+
}
|
|
4995
|
+
/** Get focused item (or null). */
|
|
4996
|
+
focusedItem() {
|
|
4997
|
+
return this._flatItems[this._focusedIndex] ?? null;
|
|
4998
|
+
}
|
|
4999
|
+
/** Get flat index of item by name (for wrappers binding roving tabindex). */
|
|
5000
|
+
getFlatIndex(name) {
|
|
5001
|
+
return this._flatItems.findIndex((i) => i.name === name);
|
|
5002
|
+
}
|
|
5003
|
+
// === Lifecycle ===
|
|
5004
|
+
/** Subscribes to editor transactions for disabled-state tracking. */
|
|
5005
|
+
subscribe() {
|
|
5006
|
+
this.transactionHandler = () => {
|
|
5007
|
+
this.updateDisabledStates();
|
|
5008
|
+
};
|
|
5009
|
+
this.editor.on("transaction", this.transactionHandler);
|
|
5010
|
+
}
|
|
5011
|
+
/** Unsubscribes and clears internal state. */
|
|
5012
|
+
destroy() {
|
|
5013
|
+
if (this.transactionHandler) {
|
|
5014
|
+
this.editor.off("transaction", this.transactionHandler);
|
|
5015
|
+
this.transactionHandler = null;
|
|
5016
|
+
}
|
|
5017
|
+
this._groups = [];
|
|
5018
|
+
this._flatItems = [];
|
|
5019
|
+
this._disabledMap.clear();
|
|
5020
|
+
this._focusedIndex = FLOATING_MENU_NO_FOCUS;
|
|
5021
|
+
}
|
|
5022
|
+
// === Internal ===
|
|
5023
|
+
/**
|
|
5024
|
+
* Groups items by `group` preserving insertion order, then sorts by
|
|
5025
|
+
* priority (higher first) within each group. Delegates to the shared
|
|
5026
|
+
* utility so `SlashCommand`'s renderer and this controller always
|
|
5027
|
+
* produce identical ordering.
|
|
5028
|
+
*/
|
|
5029
|
+
groupItems(items) {
|
|
5030
|
+
return groupFloatingMenuItems(items);
|
|
5031
|
+
}
|
|
5032
|
+
/**
|
|
5033
|
+
* Updates disabled state for each item. Uses custom predicate when
|
|
5034
|
+
* provided; otherwise tries a dry-run against `editor.can()[command]`.
|
|
5035
|
+
* Only notifies on change to avoid noisy re-renders.
|
|
5036
|
+
*/
|
|
5037
|
+
updateDisabledStates() {
|
|
5038
|
+
let changed = false;
|
|
5039
|
+
let canProxy = null;
|
|
5040
|
+
try {
|
|
5041
|
+
canProxy = this.editor.can();
|
|
5042
|
+
} catch {
|
|
5043
|
+
canProxy = null;
|
|
5044
|
+
}
|
|
5045
|
+
for (const item of this._flatItems) {
|
|
5046
|
+
const was = this._disabledMap.get(item.name) ?? false;
|
|
5047
|
+
let now = false;
|
|
5048
|
+
if (item.isDisabled) {
|
|
5049
|
+
try {
|
|
5050
|
+
now = item.isDisabled(this.editor);
|
|
5051
|
+
} catch {
|
|
5052
|
+
now = false;
|
|
5053
|
+
}
|
|
5054
|
+
} else if (typeof item.command === "string" && canProxy) {
|
|
5055
|
+
try {
|
|
5056
|
+
const canCmd = canProxy[item.command];
|
|
5057
|
+
if (canCmd) {
|
|
5058
|
+
now = item.commandArgs?.length ? !canCmd(...item.commandArgs) : !canCmd();
|
|
5059
|
+
}
|
|
5060
|
+
} catch {
|
|
5061
|
+
now = false;
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
if (was !== now) {
|
|
5065
|
+
this._disabledMap.set(item.name, now);
|
|
5066
|
+
changed = true;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
if (changed) this.onChange();
|
|
5070
|
+
}
|
|
5071
|
+
};
|
|
5072
|
+
|
|
4472
5073
|
// src/icons/phosphor.ts
|
|
4473
5074
|
var defaultIcons = {
|
|
4474
5075
|
// --- Format: Inline ---
|
|
@@ -4528,24 +5129,29 @@ var defaultIcons = {
|
|
|
4528
5129
|
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
5130
|
// --- Table ---
|
|
4530
5131
|
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>'
|
|
5132
|
+
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>',
|
|
5133
|
+
// --- Block Handle / Context Menu ---
|
|
5134
|
+
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>',
|
|
5135
|
+
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>',
|
|
5136
|
+
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>',
|
|
5137
|
+
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
5138
|
};
|
|
4533
5139
|
|
|
4534
5140
|
// src/nodes/Document.ts
|
|
4535
|
-
var Document =
|
|
5141
|
+
var Document = Node2.create({
|
|
4536
5142
|
name: "doc",
|
|
4537
5143
|
topNode: true,
|
|
4538
5144
|
content: "block+"
|
|
4539
5145
|
});
|
|
4540
5146
|
|
|
4541
5147
|
// src/nodes/Text.ts
|
|
4542
|
-
var Text =
|
|
5148
|
+
var Text = Node2.create({
|
|
4543
5149
|
name: "text",
|
|
4544
5150
|
group: "inline"
|
|
4545
5151
|
});
|
|
4546
5152
|
|
|
4547
5153
|
// src/nodes/Paragraph.ts
|
|
4548
|
-
var Paragraph =
|
|
5154
|
+
var Paragraph = Node2.create({
|
|
4549
5155
|
name: "paragraph",
|
|
4550
5156
|
group: "block",
|
|
4551
5157
|
content: "inline*",
|
|
@@ -4578,7 +5184,7 @@ var Paragraph = Node.create({
|
|
|
4578
5184
|
};
|
|
4579
5185
|
}
|
|
4580
5186
|
});
|
|
4581
|
-
var Heading =
|
|
5187
|
+
var Heading = Node2.create({
|
|
4582
5188
|
name: "heading",
|
|
4583
5189
|
group: "block",
|
|
4584
5190
|
content: "inline*",
|
|
@@ -4641,6 +5247,33 @@ var Heading = Node.create({
|
|
|
4641
5247
|
return editor?.commands["toggleHeading"]?.({ level }) ?? false;
|
|
4642
5248
|
};
|
|
4643
5249
|
});
|
|
5250
|
+
shortcuts["Enter"] = () => {
|
|
5251
|
+
if (!editor) return false;
|
|
5252
|
+
const { state: state$1, view } = editor;
|
|
5253
|
+
const { selection } = state$1;
|
|
5254
|
+
if (!selection.empty) return false;
|
|
5255
|
+
const { $from } = selection;
|
|
5256
|
+
if ($from.parent.type.name !== "heading") return false;
|
|
5257
|
+
const paragraphType = state$1.schema.nodes["paragraph"];
|
|
5258
|
+
if (!paragraphType) return false;
|
|
5259
|
+
if ($from.parent.content.size === 0) {
|
|
5260
|
+
view.dispatch(
|
|
5261
|
+
state$1.tr.setNodeMarkup($from.before($from.depth), paragraphType).scrollIntoView()
|
|
5262
|
+
);
|
|
5263
|
+
return true;
|
|
5264
|
+
}
|
|
5265
|
+
if ($from.parentOffset !== $from.parent.content.size) return false;
|
|
5266
|
+
const after = $from.after($from.depth);
|
|
5267
|
+
const $after = state$1.doc.resolve(after);
|
|
5268
|
+
const indexAfter = $after.index();
|
|
5269
|
+
if (!$after.parent.canReplaceWith(indexAfter, indexAfter, paragraphType)) {
|
|
5270
|
+
return false;
|
|
5271
|
+
}
|
|
5272
|
+
const tr = state$1.tr.insert(after, paragraphType.create());
|
|
5273
|
+
tr.setSelection(state.TextSelection.create(tr.doc, after + 1));
|
|
5274
|
+
view.dispatch(tr.scrollIntoView());
|
|
5275
|
+
return true;
|
|
5276
|
+
};
|
|
4644
5277
|
return shortcuts;
|
|
4645
5278
|
},
|
|
4646
5279
|
addToolbarItems() {
|
|
@@ -4682,6 +5315,30 @@ var Heading = Node.create({
|
|
|
4682
5315
|
}
|
|
4683
5316
|
];
|
|
4684
5317
|
},
|
|
5318
|
+
addFloatingMenuItems() {
|
|
5319
|
+
const iconMap = {
|
|
5320
|
+
1: "textHOne",
|
|
5321
|
+
2: "textHTwo",
|
|
5322
|
+
3: "textHThree"
|
|
5323
|
+
};
|
|
5324
|
+
const descriptionMap = {
|
|
5325
|
+
1: "Big section heading",
|
|
5326
|
+
2: "Medium section heading",
|
|
5327
|
+
3: "Small section heading"
|
|
5328
|
+
};
|
|
5329
|
+
return this.options.levels.filter((level) => level <= 3).map((level) => ({
|
|
5330
|
+
name: `heading-${String(level)}`,
|
|
5331
|
+
label: `Heading ${String(level)}`,
|
|
5332
|
+
description: descriptionMap[level] ?? "Section heading",
|
|
5333
|
+
icon: iconMap[level] ?? "textH",
|
|
5334
|
+
group: "Basic",
|
|
5335
|
+
priority: 210 - level * 10,
|
|
5336
|
+
keywords: ["heading", `h${String(level)}`, "title"],
|
|
5337
|
+
shortcut: "#".repeat(level) + " ",
|
|
5338
|
+
command: "toggleHeading",
|
|
5339
|
+
commandArgs: [{ level }]
|
|
5340
|
+
}));
|
|
5341
|
+
},
|
|
4685
5342
|
addProseMirrorPlugins() {
|
|
4686
5343
|
const { options, editor } = this;
|
|
4687
5344
|
const codeToLevel = {};
|
|
@@ -4755,7 +5412,7 @@ var Heading = Node.create({
|
|
|
4755
5412
|
});
|
|
4756
5413
|
|
|
4757
5414
|
// src/nodes/Blockquote.ts
|
|
4758
|
-
var Blockquote =
|
|
5415
|
+
var Blockquote = Node2.create({
|
|
4759
5416
|
name: "blockquote",
|
|
4760
5417
|
group: "block",
|
|
4761
5418
|
content: "block+",
|
|
@@ -4808,9 +5465,24 @@ var Blockquote = Node.create({
|
|
|
4808
5465
|
}
|
|
4809
5466
|
];
|
|
4810
5467
|
},
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
5468
|
+
addFloatingMenuItems() {
|
|
5469
|
+
return [
|
|
5470
|
+
{
|
|
5471
|
+
name: "blockquote",
|
|
5472
|
+
label: "Quote",
|
|
5473
|
+
description: "Capture a quote",
|
|
5474
|
+
icon: "quotes",
|
|
5475
|
+
group: "Basic",
|
|
5476
|
+
priority: 170,
|
|
5477
|
+
keywords: ["quote", "blockquote", "citation"],
|
|
5478
|
+
shortcut: "> ",
|
|
5479
|
+
command: "toggleBlockquote"
|
|
5480
|
+
}
|
|
5481
|
+
];
|
|
5482
|
+
},
|
|
5483
|
+
addInputRules() {
|
|
5484
|
+
const { nodeType } = this;
|
|
5485
|
+
if (!nodeType) {
|
|
4814
5486
|
return [];
|
|
4815
5487
|
}
|
|
4816
5488
|
return [
|
|
@@ -4818,7 +5490,7 @@ var Blockquote = Node.create({
|
|
|
4818
5490
|
];
|
|
4819
5491
|
}
|
|
4820
5492
|
});
|
|
4821
|
-
var CodeBlock =
|
|
5493
|
+
var CodeBlock = Node2.create({
|
|
4822
5494
|
name: "codeBlock",
|
|
4823
5495
|
group: "block",
|
|
4824
5496
|
content: "text*",
|
|
@@ -4938,6 +5610,21 @@ var CodeBlock = Node.create({
|
|
|
4938
5610
|
}
|
|
4939
5611
|
];
|
|
4940
5612
|
},
|
|
5613
|
+
addFloatingMenuItems() {
|
|
5614
|
+
return [
|
|
5615
|
+
{
|
|
5616
|
+
name: "code-block",
|
|
5617
|
+
label: "Code block",
|
|
5618
|
+
description: "Capture a code snippet",
|
|
5619
|
+
icon: "codeBlock",
|
|
5620
|
+
group: "Basic",
|
|
5621
|
+
priority: 160,
|
|
5622
|
+
keywords: ["code", "snippet", "pre"],
|
|
5623
|
+
shortcut: "``` ",
|
|
5624
|
+
command: "toggleCodeBlock"
|
|
5625
|
+
}
|
|
5626
|
+
];
|
|
5627
|
+
},
|
|
4941
5628
|
addInputRules() {
|
|
4942
5629
|
const { nodeType } = this;
|
|
4943
5630
|
if (!nodeType) {
|
|
@@ -4955,6 +5642,7 @@ var CodeBlock = Node.create({
|
|
|
4955
5642
|
];
|
|
4956
5643
|
}
|
|
4957
5644
|
});
|
|
5645
|
+
var LIST_GROUP_TYPES = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
|
|
4958
5646
|
function getListItemContext(editor, listItemName) {
|
|
4959
5647
|
const { state, view } = editor;
|
|
4960
5648
|
const listItemType = state.schema.nodes[listItemName];
|
|
@@ -5001,10 +5689,39 @@ var ListKeymap = Extension.create({
|
|
|
5001
5689
|
Backspace: () => {
|
|
5002
5690
|
const editor = this.editor;
|
|
5003
5691
|
if (!editor) return false;
|
|
5004
|
-
const { state, view } = editor;
|
|
5005
|
-
const { $from, empty } = state.selection;
|
|
5692
|
+
const { state: state$1, view } = editor;
|
|
5693
|
+
const { $from, empty } = state$1.selection;
|
|
5006
5694
|
if (!empty || $from.parentOffset !== 0) return false;
|
|
5007
|
-
|
|
5695
|
+
if ($from.parent.type.name === "paragraph" && $from.parent.content.size === 0 && $from.depth >= 1) {
|
|
5696
|
+
const containerDepth = $from.depth - 1;
|
|
5697
|
+
const idx = $from.index(containerDepth);
|
|
5698
|
+
if (idx > 0) {
|
|
5699
|
+
const container = $from.node(containerDepth);
|
|
5700
|
+
const prev = container.child(idx - 1);
|
|
5701
|
+
if (LIST_GROUP_TYPES.has(prev.type.name)) {
|
|
5702
|
+
const paraStart = $from.before($from.depth);
|
|
5703
|
+
const paraEnd = $from.after($from.depth);
|
|
5704
|
+
const listEnd = paraStart;
|
|
5705
|
+
const listStart = listEnd - prev.nodeSize;
|
|
5706
|
+
let lastTextblockEnd = -1;
|
|
5707
|
+
state$1.doc.nodesBetween(listStart, listEnd, (n, p) => {
|
|
5708
|
+
if (n.isTextblock) lastTextblockEnd = p + 1 + n.content.size;
|
|
5709
|
+
return true;
|
|
5710
|
+
});
|
|
5711
|
+
if (lastTextblockEnd !== -1) {
|
|
5712
|
+
const tr = state$1.tr.delete(paraStart, paraEnd);
|
|
5713
|
+
const next = idx + 1 < container.childCount ? container.child(idx + 1) : null;
|
|
5714
|
+
if (next?.type === prev.type && transform.canJoin(tr.doc, paraStart)) {
|
|
5715
|
+
tr.join(paraStart);
|
|
5716
|
+
}
|
|
5717
|
+
tr.setSelection(state.TextSelection.create(tr.doc, lastTextblockEnd));
|
|
5718
|
+
view.dispatch(tr.scrollIntoView());
|
|
5719
|
+
return true;
|
|
5720
|
+
}
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
const listItemType = state$1.schema.nodes[this.options.listItem];
|
|
5008
5725
|
if (!listItemType) return false;
|
|
5009
5726
|
let listItemDepth = -1;
|
|
5010
5727
|
for (let d = $from.depth; d > 0; d--) {
|
|
@@ -5019,7 +5736,7 @@ var ListKeymap = Extension.create({
|
|
|
5019
5736
|
if (firstChild?.isTextblock) {
|
|
5020
5737
|
const posInListItem = $from.pos - $from.start(listItemDepth);
|
|
5021
5738
|
if (posInListItem <= 1) {
|
|
5022
|
-
return schemaList.liftListItem(listItemType)(state, view.dispatch);
|
|
5739
|
+
return schemaList.liftListItem(listItemType)(state$1, view.dispatch);
|
|
5023
5740
|
}
|
|
5024
5741
|
}
|
|
5025
5742
|
return false;
|
|
@@ -5029,9 +5746,11 @@ var ListKeymap = Extension.create({
|
|
|
5029
5746
|
});
|
|
5030
5747
|
|
|
5031
5748
|
// src/nodes/ListItem.ts
|
|
5032
|
-
var ListItem =
|
|
5749
|
+
var ListItem = Node2.create({
|
|
5033
5750
|
name: "listItem",
|
|
5034
|
-
|
|
5751
|
+
// Notion-strict: paragraph must be the first child (the "label" line aligned
|
|
5752
|
+
// with the bullet); additional blocks render below as nested children.
|
|
5753
|
+
content: "paragraph block*",
|
|
5035
5754
|
defining: true,
|
|
5036
5755
|
addOptions() {
|
|
5037
5756
|
return {
|
|
@@ -5054,6 +5773,15 @@ var ListItem = Node.create({
|
|
|
5054
5773
|
const { state: state$1, view } = this.editor;
|
|
5055
5774
|
const { $from } = state$1.selection;
|
|
5056
5775
|
if ($from.depth < 1 || $from.node(-1).type !== this.nodeType) return false;
|
|
5776
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
5777
|
+
const ctx = getListItemCursorContext($from);
|
|
5778
|
+
if (ctx?.isInChildrenZone) {
|
|
5779
|
+
if (ctx.paragraphIsEmpty) {
|
|
5780
|
+
if (insertChildrenZoneSibling(state$1, view.dispatch, ctx)) return true;
|
|
5781
|
+
} else {
|
|
5782
|
+
if (commands.splitBlock(state$1, view.dispatch)) return true;
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5057
5785
|
if (schemaList.splitListItem(this.nodeType)(state$1, view.dispatch)) return true;
|
|
5058
5786
|
const listDepth = $from.depth - 2;
|
|
5059
5787
|
const taskItemType = state$1.schema.nodes["taskItem"];
|
|
@@ -5072,13 +5800,25 @@ var ListItem = Node.create({
|
|
|
5072
5800
|
}
|
|
5073
5801
|
}
|
|
5074
5802
|
return schemaList.liftListItem(this.nodeType)(state$1, view.dispatch);
|
|
5803
|
+
},
|
|
5804
|
+
Backspace: () => {
|
|
5805
|
+
if (!this.editor || !this.nodeType) return false;
|
|
5806
|
+
const { state, view } = this.editor;
|
|
5807
|
+
const { $from, empty } = state.selection;
|
|
5808
|
+
if (!empty || $from.parentOffset !== 0) return false;
|
|
5809
|
+
if ($from.parent.content.size !== 0) return false;
|
|
5810
|
+
const ctx = getListItemCursorContext($from);
|
|
5811
|
+
if (ctx && ctx.isInChildrenZone && ctx.paragraphIsEmpty) {
|
|
5812
|
+
if (liftEmptyChildrenZoneParagraph(state, view.dispatch, ctx)) return true;
|
|
5813
|
+
}
|
|
5814
|
+
return false;
|
|
5075
5815
|
}
|
|
5076
5816
|
};
|
|
5077
5817
|
}
|
|
5078
5818
|
});
|
|
5079
5819
|
|
|
5080
5820
|
// src/nodes/BulletList.ts
|
|
5081
|
-
var BulletList =
|
|
5821
|
+
var BulletList = Node2.create({
|
|
5082
5822
|
name: "bulletList",
|
|
5083
5823
|
group: "block list",
|
|
5084
5824
|
content: "listItem+",
|
|
@@ -5117,6 +5857,24 @@ var BulletList = Node.create({
|
|
|
5117
5857
|
}
|
|
5118
5858
|
];
|
|
5119
5859
|
},
|
|
5860
|
+
addFloatingMenuItems() {
|
|
5861
|
+
return [
|
|
5862
|
+
{
|
|
5863
|
+
name: "bullet-list",
|
|
5864
|
+
label: "Bulleted list",
|
|
5865
|
+
description: "Create a simple bulleted list",
|
|
5866
|
+
icon: "listBullets",
|
|
5867
|
+
group: "Lists",
|
|
5868
|
+
priority: 200,
|
|
5869
|
+
keywords: ["bullet", "list", "unordered", "ul"],
|
|
5870
|
+
shortcut: "- ",
|
|
5871
|
+
command: "toggleBulletList",
|
|
5872
|
+
// Don't offer "Bulleted list" while cursor is already inside one,
|
|
5873
|
+
// otherwise picking it lifts the user out of the list.
|
|
5874
|
+
hideWhenInside: ["bulletList"]
|
|
5875
|
+
}
|
|
5876
|
+
];
|
|
5877
|
+
},
|
|
5120
5878
|
addKeyboardShortcuts() {
|
|
5121
5879
|
const { editor } = this;
|
|
5122
5880
|
return {
|
|
@@ -5145,7 +5903,7 @@ var BulletList = Node.create({
|
|
|
5145
5903
|
});
|
|
5146
5904
|
|
|
5147
5905
|
// src/nodes/OrderedList.ts
|
|
5148
|
-
var OrderedList =
|
|
5906
|
+
var OrderedList = Node2.create({
|
|
5149
5907
|
name: "orderedList",
|
|
5150
5908
|
group: "block list",
|
|
5151
5909
|
content: "listItem+",
|
|
@@ -5202,6 +5960,22 @@ var OrderedList = Node.create({
|
|
|
5202
5960
|
}
|
|
5203
5961
|
];
|
|
5204
5962
|
},
|
|
5963
|
+
addFloatingMenuItems() {
|
|
5964
|
+
return [
|
|
5965
|
+
{
|
|
5966
|
+
name: "ordered-list",
|
|
5967
|
+
label: "Numbered list",
|
|
5968
|
+
description: "Create a numbered list",
|
|
5969
|
+
icon: "listNumbers",
|
|
5970
|
+
group: "Lists",
|
|
5971
|
+
priority: 190,
|
|
5972
|
+
keywords: ["ordered", "numbered", "list", "ol", "1."],
|
|
5973
|
+
shortcut: "1. ",
|
|
5974
|
+
command: "toggleOrderedList",
|
|
5975
|
+
hideWhenInside: ["orderedList"]
|
|
5976
|
+
}
|
|
5977
|
+
];
|
|
5978
|
+
},
|
|
5205
5979
|
addKeyboardShortcuts() {
|
|
5206
5980
|
const { editor } = this;
|
|
5207
5981
|
return {
|
|
@@ -5232,7 +6006,7 @@ var OrderedList = Node.create({
|
|
|
5232
6006
|
];
|
|
5233
6007
|
}
|
|
5234
6008
|
});
|
|
5235
|
-
var HorizontalRule =
|
|
6009
|
+
var HorizontalRule = Node2.create({
|
|
5236
6010
|
name: "horizontalRule",
|
|
5237
6011
|
group: "block",
|
|
5238
6012
|
addOptions() {
|
|
@@ -5253,19 +6027,31 @@ var HorizontalRule = Node.create({
|
|
|
5253
6027
|
const { $from } = tr.selection;
|
|
5254
6028
|
const parent = $from.parent;
|
|
5255
6029
|
if (!parent.isTextblock) return false;
|
|
6030
|
+
const paragraphType = state$1.schema.nodes["paragraph"];
|
|
6031
|
+
const hrNode = this.nodeType.create();
|
|
6032
|
+
const trailingParagraph = paragraphType?.create();
|
|
6033
|
+
const nodes = trailingParagraph ? [hrNode, trailingParagraph] : [hrNode];
|
|
6034
|
+
const listRange = splitListForInsert(state$1, tr);
|
|
6035
|
+
if (listRange) {
|
|
6036
|
+
if (!dispatch) return true;
|
|
6037
|
+
tr.replaceWith(listRange.from, listRange.to, nodes);
|
|
6038
|
+
const sel = state.TextSelection.findFrom(
|
|
6039
|
+
tr.doc.resolve(listRange.from + 1),
|
|
6040
|
+
1
|
|
6041
|
+
);
|
|
6042
|
+
if (sel) tr.setSelection(sel);
|
|
6043
|
+
dispatch(tr.scrollIntoView());
|
|
6044
|
+
return true;
|
|
6045
|
+
}
|
|
5256
6046
|
if (dispatch) {
|
|
5257
6047
|
if (parent.content.size === 0 && parent.type.name === "paragraph") {
|
|
5258
6048
|
const from = $from.before();
|
|
5259
6049
|
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
6050
|
tr.replaceWith(from, to, nodes);
|
|
5263
6051
|
const sel = state.TextSelection.findFrom(tr.doc.resolve(from + 1), 1);
|
|
5264
6052
|
if (sel) tr.setSelection(sel);
|
|
5265
6053
|
} else {
|
|
5266
6054
|
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
6055
|
tr.insert(end, nodes);
|
|
5270
6056
|
const sel = state.TextSelection.findFrom(tr.doc.resolve(end + 1), 1);
|
|
5271
6057
|
if (sel) tr.setSelection(sel);
|
|
@@ -5289,6 +6075,21 @@ var HorizontalRule = Node.create({
|
|
|
5289
6075
|
}
|
|
5290
6076
|
];
|
|
5291
6077
|
},
|
|
6078
|
+
addFloatingMenuItems() {
|
|
6079
|
+
return [
|
|
6080
|
+
{
|
|
6081
|
+
name: "horizontal-rule",
|
|
6082
|
+
label: "Divider",
|
|
6083
|
+
description: "Insert a horizontal rule",
|
|
6084
|
+
icon: "minus",
|
|
6085
|
+
group: "Basic",
|
|
6086
|
+
priority: 150,
|
|
6087
|
+
keywords: ["divider", "hr", "line", "separator", "horizontal rule"],
|
|
6088
|
+
shortcut: "--- ",
|
|
6089
|
+
command: "setHorizontalRule"
|
|
6090
|
+
}
|
|
6091
|
+
];
|
|
6092
|
+
},
|
|
5292
6093
|
addInputRules() {
|
|
5293
6094
|
const { nodeType } = this;
|
|
5294
6095
|
if (!nodeType) {
|
|
@@ -5319,7 +6120,7 @@ var HorizontalRule = Node.create({
|
|
|
5319
6120
|
});
|
|
5320
6121
|
|
|
5321
6122
|
// src/nodes/HardBreak.ts
|
|
5322
|
-
var HardBreak =
|
|
6123
|
+
var HardBreak = Node2.create({
|
|
5323
6124
|
name: "hardBreak",
|
|
5324
6125
|
group: "inline",
|
|
5325
6126
|
inline: true,
|
|
@@ -5379,9 +6180,84 @@ var HardBreak = Node.create({
|
|
|
5379
6180
|
};
|
|
5380
6181
|
}
|
|
5381
6182
|
});
|
|
5382
|
-
|
|
6183
|
+
|
|
6184
|
+
// src/nodes/TaskItemNodeView.ts
|
|
6185
|
+
var TaskItemNodeView = class {
|
|
6186
|
+
dom;
|
|
6187
|
+
contentDOM;
|
|
6188
|
+
label;
|
|
6189
|
+
input;
|
|
6190
|
+
view;
|
|
6191
|
+
nodeType;
|
|
6192
|
+
getPos;
|
|
6193
|
+
node;
|
|
6194
|
+
constructor({ options, node, view, getPos }) {
|
|
6195
|
+
this.view = view;
|
|
6196
|
+
this.nodeType = node.type;
|
|
6197
|
+
this.node = node;
|
|
6198
|
+
this.getPos = getPos;
|
|
6199
|
+
this.dom = document.createElement("li");
|
|
6200
|
+
for (const [key, value] of Object.entries(options.HTMLAttributes)) {
|
|
6201
|
+
if (value !== null && value !== void 0) {
|
|
6202
|
+
this.dom.setAttribute(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
this.dom.setAttribute("data-type", "taskItem");
|
|
6206
|
+
this.dom.setAttribute("data-checked", node.attrs["checked"] ? "true" : "false");
|
|
6207
|
+
this.label = document.createElement("label");
|
|
6208
|
+
this.label.setAttribute("contenteditable", "false");
|
|
6209
|
+
this.input = document.createElement("input");
|
|
6210
|
+
this.input.type = "checkbox";
|
|
6211
|
+
this.input.checked = !!node.attrs["checked"];
|
|
6212
|
+
this.input.disabled = !view.editable;
|
|
6213
|
+
this.input.setAttribute("aria-label", "Task status");
|
|
6214
|
+
this.label.appendChild(this.input);
|
|
6215
|
+
this.dom.appendChild(this.label);
|
|
6216
|
+
this.contentDOM = document.createElement("div");
|
|
6217
|
+
this.dom.appendChild(this.contentDOM);
|
|
6218
|
+
this.input.addEventListener("change", this.handleChange);
|
|
6219
|
+
}
|
|
6220
|
+
handleChange = (event) => {
|
|
6221
|
+
event.preventDefault();
|
|
6222
|
+
if (!this.view.editable) {
|
|
6223
|
+
this.input.checked = !!this.node.attrs["checked"];
|
|
6224
|
+
return;
|
|
6225
|
+
}
|
|
6226
|
+
const pos = this.getPos();
|
|
6227
|
+
if (pos === void 0) return;
|
|
6228
|
+
const { state, dispatch } = this.view;
|
|
6229
|
+
const tr = state.tr.setNodeMarkup(pos, void 0, {
|
|
6230
|
+
...this.node.attrs,
|
|
6231
|
+
checked: this.input.checked
|
|
6232
|
+
});
|
|
6233
|
+
dispatch(tr);
|
|
6234
|
+
};
|
|
6235
|
+
update(node) {
|
|
6236
|
+
if (node.type !== this.nodeType) return false;
|
|
6237
|
+
this.node = node;
|
|
6238
|
+
const checked = !!node.attrs["checked"];
|
|
6239
|
+
this.dom.setAttribute("data-checked", checked ? "true" : "false");
|
|
6240
|
+
this.input.checked = checked;
|
|
6241
|
+
this.input.disabled = !this.view.editable;
|
|
6242
|
+
return true;
|
|
6243
|
+
}
|
|
6244
|
+
stopEvent(event) {
|
|
6245
|
+
return event.target instanceof Node && this.label.contains(event.target);
|
|
6246
|
+
}
|
|
6247
|
+
ignoreMutation(mutation) {
|
|
6248
|
+
return mutation.target instanceof Node && this.label.contains(mutation.target);
|
|
6249
|
+
}
|
|
6250
|
+
destroy() {
|
|
6251
|
+
this.input.removeEventListener("change", this.handleChange);
|
|
6252
|
+
}
|
|
6253
|
+
};
|
|
6254
|
+
|
|
6255
|
+
// src/nodes/TaskItem.ts
|
|
6256
|
+
var TaskItem = Node2.create({
|
|
5383
6257
|
name: "taskItem",
|
|
5384
|
-
|
|
6258
|
+
// Paragraph must be the first child so flex alignment binds to the label
|
|
6259
|
+
// baseline; a heading-first child would visually break the checkbox.
|
|
6260
|
+
content: "paragraph block*",
|
|
5385
6261
|
defining: true,
|
|
5386
6262
|
addOptions() {
|
|
5387
6263
|
return {
|
|
@@ -5435,6 +6311,10 @@ var TaskItem = Node.create({
|
|
|
5435
6311
|
["div", 0]
|
|
5436
6312
|
];
|
|
5437
6313
|
},
|
|
6314
|
+
addNodeView() {
|
|
6315
|
+
const options = this.options;
|
|
6316
|
+
return (node, view, getPos) => new TaskItemNodeView({ options, node, view, getPos });
|
|
6317
|
+
},
|
|
5438
6318
|
addCommands() {
|
|
5439
6319
|
const { name } = this;
|
|
5440
6320
|
return {
|
|
@@ -5470,7 +6350,16 @@ var TaskItem = Node.create({
|
|
|
5470
6350
|
const { state: state$1, view } = this.editor;
|
|
5471
6351
|
const { $from } = state$1.selection;
|
|
5472
6352
|
if ($from.depth < 1 || $from.node(-1).type !== this.nodeType) return false;
|
|
5473
|
-
if (
|
|
6353
|
+
if ($from.parent.type.name !== "paragraph") return false;
|
|
6354
|
+
const ctx = getListItemCursorContext($from);
|
|
6355
|
+
if (ctx?.isInChildrenZone) {
|
|
6356
|
+
if (ctx.paragraphIsEmpty) {
|
|
6357
|
+
if (insertChildrenZoneSibling(state$1, view.dispatch, ctx)) return true;
|
|
6358
|
+
} else {
|
|
6359
|
+
if (commands.splitBlock(state$1, view.dispatch)) return true;
|
|
6360
|
+
}
|
|
6361
|
+
}
|
|
6362
|
+
if (schemaList.splitListItem(this.nodeType, { checked: false })(state$1, view.dispatch)) return true;
|
|
5474
6363
|
if ($from.parent.content.size === 0) {
|
|
5475
6364
|
const listItemType = state$1.schema.nodes["listItem"];
|
|
5476
6365
|
if (listItemType) {
|
|
@@ -5526,6 +6415,12 @@ var TaskItem = Node.create({
|
|
|
5526
6415
|
const { state, view } = this.editor;
|
|
5527
6416
|
const { $from, empty } = state.selection;
|
|
5528
6417
|
if (!empty || $from.parentOffset !== 0) return false;
|
|
6418
|
+
if ($from.parent.content.size === 0) {
|
|
6419
|
+
const ctx = getListItemCursorContext($from);
|
|
6420
|
+
if (ctx && ctx.isInChildrenZone && ctx.paragraphIsEmpty) {
|
|
6421
|
+
if (liftEmptyChildrenZoneParagraph(state, view.dispatch, ctx)) return true;
|
|
6422
|
+
}
|
|
6423
|
+
}
|
|
5529
6424
|
let taskItemDepth = -1;
|
|
5530
6425
|
for (let d = $from.depth; d > 0; d--) {
|
|
5531
6426
|
if ($from.node(d).type === this.nodeType) {
|
|
@@ -5545,7 +6440,7 @@ var TaskItem = Node.create({
|
|
|
5545
6440
|
});
|
|
5546
6441
|
|
|
5547
6442
|
// src/nodes/TaskList.ts
|
|
5548
|
-
var TaskList =
|
|
6443
|
+
var TaskList = Node2.create({
|
|
5549
6444
|
name: "taskList",
|
|
5550
6445
|
group: "block list",
|
|
5551
6446
|
content: "taskItem+",
|
|
@@ -5606,6 +6501,22 @@ var TaskList = Node.create({
|
|
|
5606
6501
|
}
|
|
5607
6502
|
];
|
|
5608
6503
|
},
|
|
6504
|
+
addFloatingMenuItems() {
|
|
6505
|
+
return [
|
|
6506
|
+
{
|
|
6507
|
+
name: "task-list",
|
|
6508
|
+
label: "To-do list",
|
|
6509
|
+
description: "Track tasks with a checkbox list",
|
|
6510
|
+
icon: "listChecks",
|
|
6511
|
+
group: "Lists",
|
|
6512
|
+
priority: 180,
|
|
6513
|
+
keywords: ["todo", "task", "checkbox", "check"],
|
|
6514
|
+
shortcut: "[ ] ",
|
|
6515
|
+
command: "toggleTaskList",
|
|
6516
|
+
hideWhenInside: ["taskList"]
|
|
6517
|
+
}
|
|
6518
|
+
];
|
|
6519
|
+
},
|
|
5609
6520
|
addExtensions() {
|
|
5610
6521
|
return [TaskItem];
|
|
5611
6522
|
},
|
|
@@ -6531,7 +7442,8 @@ var TextStyle = Mark.create({
|
|
|
6531
7442
|
getAttrs: (element) => {
|
|
6532
7443
|
if (typeof element === "string") return false;
|
|
6533
7444
|
const hasStyles = element.hasAttribute("style");
|
|
6534
|
-
|
|
7445
|
+
const hasColorTokens = element.hasAttribute("data-text-color") || element.hasAttribute("data-bg-color");
|
|
7446
|
+
if (!hasStyles && !hasColorTokens) return false;
|
|
6535
7447
|
return {};
|
|
6536
7448
|
}
|
|
6537
7449
|
},
|
|
@@ -6800,6 +7712,123 @@ var Placeholder = Extension.create({
|
|
|
6800
7712
|
];
|
|
6801
7713
|
}
|
|
6802
7714
|
});
|
|
7715
|
+
var LIST_ITEM_TYPES3 = /* @__PURE__ */ new Set(["listItem", "taskItem"]);
|
|
7716
|
+
var LIST_WRAPPER_TYPES2 = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
|
|
7717
|
+
function isCursorInsideListItem(state) {
|
|
7718
|
+
const { $from } = state.selection;
|
|
7719
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
7720
|
+
if (LIST_ITEM_TYPES3.has($from.node(d).type.name)) return true;
|
|
7721
|
+
}
|
|
7722
|
+
return false;
|
|
7723
|
+
}
|
|
7724
|
+
function indentBlockAsListChild(state$1, dispatch) {
|
|
7725
|
+
const { selection } = state$1;
|
|
7726
|
+
let blockIndex;
|
|
7727
|
+
let blockNode;
|
|
7728
|
+
let blockStart;
|
|
7729
|
+
let blockEnd;
|
|
7730
|
+
if (selection instanceof state.NodeSelection) {
|
|
7731
|
+
const node = selection.node;
|
|
7732
|
+
const $pos = selection.$from;
|
|
7733
|
+
if ($pos.depth !== 0) return false;
|
|
7734
|
+
if (isCursorInsideListItem(state$1)) return false;
|
|
7735
|
+
blockIndex = $pos.index(0);
|
|
7736
|
+
blockNode = node;
|
|
7737
|
+
blockStart = selection.from;
|
|
7738
|
+
blockEnd = selection.to;
|
|
7739
|
+
} else {
|
|
7740
|
+
if (!selection.empty) return false;
|
|
7741
|
+
if (isCursorInsideListItem(state$1)) return false;
|
|
7742
|
+
const { $from } = selection;
|
|
7743
|
+
if ($from.depth !== 1) return false;
|
|
7744
|
+
blockIndex = $from.index(0);
|
|
7745
|
+
blockNode = $from.node(1);
|
|
7746
|
+
blockStart = $from.before(1);
|
|
7747
|
+
blockEnd = $from.after(1);
|
|
7748
|
+
}
|
|
7749
|
+
if (blockIndex === 0) return false;
|
|
7750
|
+
const prevSibling = state$1.doc.child(blockIndex - 1);
|
|
7751
|
+
if (!LIST_WRAPPER_TYPES2.has(prevSibling.type.name)) return false;
|
|
7752
|
+
let wrapperPos = 0;
|
|
7753
|
+
for (let i = 0; i < blockIndex - 1; i++) {
|
|
7754
|
+
wrapperPos += state$1.doc.child(i).nodeSize;
|
|
7755
|
+
}
|
|
7756
|
+
const tr = state$1.tr;
|
|
7757
|
+
const result = insertAsListItemChild({
|
|
7758
|
+
tr,
|
|
7759
|
+
wrapperPos,
|
|
7760
|
+
blockNode,
|
|
7761
|
+
sourceRange: { from: blockStart, to: blockEnd }
|
|
7762
|
+
});
|
|
7763
|
+
if (!result.ok || result.insertedAt === void 0) return false;
|
|
7764
|
+
if (!dispatch) return true;
|
|
7765
|
+
tr.setSelection(state.Selection.near(tr.doc.resolve(result.insertedAt + 1)));
|
|
7766
|
+
dispatch(tr.scrollIntoView());
|
|
7767
|
+
return true;
|
|
7768
|
+
}
|
|
7769
|
+
function outdentBlockFromListItem(state$1, dispatch) {
|
|
7770
|
+
const { selection } = state$1;
|
|
7771
|
+
if (!selection.empty) return false;
|
|
7772
|
+
const { $from } = selection;
|
|
7773
|
+
let listItemDepth = -1;
|
|
7774
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
7775
|
+
if (LIST_ITEM_TYPES3.has($from.node(d).type.name)) {
|
|
7776
|
+
listItemDepth = d;
|
|
7777
|
+
break;
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7780
|
+
if (listItemDepth === -1) return false;
|
|
7781
|
+
const blockDepth = listItemDepth + 1;
|
|
7782
|
+
if ($from.depth < blockDepth) return false;
|
|
7783
|
+
const listItem = $from.node(listItemDepth);
|
|
7784
|
+
const blockIndexInItem = $from.index(listItemDepth);
|
|
7785
|
+
if (blockIndexInItem === 0) return false;
|
|
7786
|
+
if (blockIndexInItem !== listItem.childCount - 1) return false;
|
|
7787
|
+
const wrapperDepth = listItemDepth - 1;
|
|
7788
|
+
if (wrapperDepth < 0) return false;
|
|
7789
|
+
const liIndexInWrapper = $from.index(wrapperDepth);
|
|
7790
|
+
const wrapper = $from.node(wrapperDepth);
|
|
7791
|
+
if (liIndexInWrapper !== wrapper.childCount - 1) return false;
|
|
7792
|
+
if (wrapperDepth - 1 < 0) return false;
|
|
7793
|
+
const wrapperParent = $from.node(wrapperDepth - 1);
|
|
7794
|
+
const wrapperIndexInParent = $from.index(wrapperDepth - 1);
|
|
7795
|
+
const blockNode = $from.node(blockDepth);
|
|
7796
|
+
if (!wrapperParent.canReplaceWith(
|
|
7797
|
+
wrapperIndexInParent + 1,
|
|
7798
|
+
wrapperIndexInParent + 1,
|
|
7799
|
+
blockNode.type
|
|
7800
|
+
)) {
|
|
7801
|
+
return false;
|
|
7802
|
+
}
|
|
7803
|
+
if (!dispatch) return true;
|
|
7804
|
+
const blockStart = $from.before(blockDepth);
|
|
7805
|
+
const blockEnd = $from.after(blockDepth);
|
|
7806
|
+
const wrapperEnd = $from.after(wrapperDepth);
|
|
7807
|
+
const tr = state$1.tr;
|
|
7808
|
+
const blockSize = blockEnd - blockStart;
|
|
7809
|
+
tr.delete(blockStart, blockEnd);
|
|
7810
|
+
const insertAt = wrapperEnd - blockSize;
|
|
7811
|
+
tr.insert(insertAt, blockNode);
|
|
7812
|
+
tr.setSelection(state.Selection.near(tr.doc.resolve(insertAt + 1)));
|
|
7813
|
+
dispatch(tr.scrollIntoView());
|
|
7814
|
+
return true;
|
|
7815
|
+
}
|
|
7816
|
+
var ListIndent = Extension.create({
|
|
7817
|
+
name: "listIndent",
|
|
7818
|
+
addKeyboardShortcuts() {
|
|
7819
|
+
const { editor } = this;
|
|
7820
|
+
return {
|
|
7821
|
+
Tab: () => {
|
|
7822
|
+
if (!editor) return false;
|
|
7823
|
+
return indentBlockAsListChild(editor.state, editor.view.dispatch);
|
|
7824
|
+
},
|
|
7825
|
+
"Shift-Tab": () => {
|
|
7826
|
+
if (!editor) return false;
|
|
7827
|
+
return outdentBlockFromListItem(editor.state, editor.view.dispatch);
|
|
7828
|
+
}
|
|
7829
|
+
};
|
|
7830
|
+
}
|
|
7831
|
+
});
|
|
6803
7832
|
var characterCountPluginKey = new state.PluginKey("characterCount");
|
|
6804
7833
|
var CharacterCount = Extension.create({
|
|
6805
7834
|
name: "characterCount",
|
|
@@ -7226,6 +8255,10 @@ var LineHeight = Extension.create({
|
|
|
7226
8255
|
}
|
|
7227
8256
|
});
|
|
7228
8257
|
function generateUUID() {
|
|
8258
|
+
if (typeof globalThis !== "undefined") {
|
|
8259
|
+
const c = globalThis.crypto;
|
|
8260
|
+
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
8261
|
+
}
|
|
7229
8262
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
7230
8263
|
const r = Math.random() * 16 | 0;
|
|
7231
8264
|
const v = c === "x" ? r : r & 3 | 8;
|
|
@@ -7311,15 +8344,31 @@ var UniqueID = Extension.create({
|
|
|
7311
8344
|
);
|
|
7312
8345
|
};
|
|
7313
8346
|
const assignMissingIDs = (doc, tr) => {
|
|
8347
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7314
8348
|
doc.descendants((node, pos) => {
|
|
7315
8349
|
if (!types.includes(node.type.name)) return;
|
|
7316
8350
|
const existingID = node.attrs[attributeName];
|
|
7317
8351
|
if (!existingID) {
|
|
7318
|
-
|
|
8352
|
+
let id = generateID();
|
|
8353
|
+
while (seen.has(id)) id = generateID();
|
|
8354
|
+
seen.add(id);
|
|
8355
|
+
tr.setNodeMarkup(tr.mapping.map(pos), void 0, {
|
|
8356
|
+
...node.attrs,
|
|
8357
|
+
[attributeName]: id
|
|
8358
|
+
});
|
|
8359
|
+
return;
|
|
8360
|
+
}
|
|
8361
|
+
if (seen.has(existingID)) {
|
|
8362
|
+
let id = generateID();
|
|
8363
|
+
while (seen.has(id)) id = generateID();
|
|
8364
|
+
seen.add(id);
|
|
8365
|
+
tr.setNodeMarkup(tr.mapping.map(pos), void 0, {
|
|
7319
8366
|
...node.attrs,
|
|
7320
|
-
[attributeName]:
|
|
8367
|
+
[attributeName]: id
|
|
7321
8368
|
});
|
|
8369
|
+
return;
|
|
7322
8370
|
}
|
|
8371
|
+
seen.add(existingID);
|
|
7323
8372
|
});
|
|
7324
8373
|
};
|
|
7325
8374
|
return [
|
|
@@ -7356,7 +8405,148 @@ var UniqueID = Extension.create({
|
|
|
7356
8405
|
];
|
|
7357
8406
|
}
|
|
7358
8407
|
});
|
|
7359
|
-
|
|
8408
|
+
|
|
8409
|
+
// src/extensions/BlockColor.ts
|
|
8410
|
+
var DEFAULT_BLOCK_COLORS = [
|
|
8411
|
+
"gray",
|
|
8412
|
+
"brown",
|
|
8413
|
+
"orange",
|
|
8414
|
+
"yellow",
|
|
8415
|
+
"green",
|
|
8416
|
+
"blue",
|
|
8417
|
+
"purple",
|
|
8418
|
+
"pink",
|
|
8419
|
+
"red"
|
|
8420
|
+
];
|
|
8421
|
+
var DEFAULT_BLOCK_COLOR_TYPES = [
|
|
8422
|
+
"paragraph",
|
|
8423
|
+
"heading",
|
|
8424
|
+
"blockquote",
|
|
8425
|
+
"bulletList",
|
|
8426
|
+
"orderedList",
|
|
8427
|
+
"taskList",
|
|
8428
|
+
"listItem",
|
|
8429
|
+
"taskItem"
|
|
8430
|
+
];
|
|
8431
|
+
function stripInlineColorConflicts(tr, state, from, to, which) {
|
|
8432
|
+
const textStyleType = state.schema.marks["textStyle"];
|
|
8433
|
+
if (!textStyleType) return;
|
|
8434
|
+
const inlineKeys = [];
|
|
8435
|
+
if (which === "text" || which === "both") inlineKeys.push("color", "colorToken");
|
|
8436
|
+
if (which === "bg" || which === "both") inlineKeys.push("backgroundColor", "backgroundColorToken");
|
|
8437
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
8438
|
+
if (!node.isText) return true;
|
|
8439
|
+
const existing = node.marks.find((m) => m.type === textStyleType);
|
|
8440
|
+
if (!existing) return false;
|
|
8441
|
+
const hasConflict = inlineKeys.some((k) => existing.attrs[k] !== null && existing.attrs[k] !== void 0);
|
|
8442
|
+
if (!hasConflict) return false;
|
|
8443
|
+
const start = Math.max(pos, from);
|
|
8444
|
+
const end = Math.min(pos + node.nodeSize, to);
|
|
8445
|
+
const newAttrs = { ...existing.attrs };
|
|
8446
|
+
for (const k of inlineKeys) newAttrs[k] = null;
|
|
8447
|
+
const stillUsed = Object.values(newAttrs).some((v) => v !== null && v !== void 0);
|
|
8448
|
+
tr.removeMark(start, end, existing);
|
|
8449
|
+
if (stillUsed) tr.addMark(start, end, textStyleType.create(newAttrs));
|
|
8450
|
+
return false;
|
|
8451
|
+
});
|
|
8452
|
+
}
|
|
8453
|
+
var BlockColor = Extension.create({
|
|
8454
|
+
name: "blockColor",
|
|
8455
|
+
addOptions() {
|
|
8456
|
+
return {
|
|
8457
|
+
types: DEFAULT_BLOCK_COLOR_TYPES,
|
|
8458
|
+
bgColors: DEFAULT_BLOCK_COLORS,
|
|
8459
|
+
textColors: DEFAULT_BLOCK_COLORS
|
|
8460
|
+
};
|
|
8461
|
+
},
|
|
8462
|
+
// Defensive fallback: an empty `bgColors`/`textColors` array would render
|
|
8463
|
+
// a broken Colors UI (section title with only the null-reset swatch).
|
|
8464
|
+
// Replace empties with the default palette so users who pass `{}` as an
|
|
8465
|
+
// override still get a working picker. Runs AFTER `addOptions()` merges
|
|
8466
|
+
// user config with defaults, so this only fires for explicit empty arrays.
|
|
8467
|
+
onBeforeCreate() {
|
|
8468
|
+
if (this.options.bgColors.length === 0) this.options.bgColors = DEFAULT_BLOCK_COLORS;
|
|
8469
|
+
if (this.options.textColors.length === 0) this.options.textColors = DEFAULT_BLOCK_COLORS;
|
|
8470
|
+
},
|
|
8471
|
+
addGlobalAttributes() {
|
|
8472
|
+
return [
|
|
8473
|
+
{
|
|
8474
|
+
types: this.options.types,
|
|
8475
|
+
attributes: {
|
|
8476
|
+
bgColor: {
|
|
8477
|
+
default: null,
|
|
8478
|
+
parseHTML: (element) => element.getAttribute("data-bg-color"),
|
|
8479
|
+
renderHTML: (attributes) => {
|
|
8480
|
+
const v = attributes["bgColor"];
|
|
8481
|
+
if (!v) return null;
|
|
8482
|
+
return { "data-bg-color": v };
|
|
8483
|
+
}
|
|
8484
|
+
},
|
|
8485
|
+
textColor: {
|
|
8486
|
+
default: null,
|
|
8487
|
+
parseHTML: (element) => element.getAttribute("data-text-color"),
|
|
8488
|
+
renderHTML: (attributes) => {
|
|
8489
|
+
const v = attributes["textColor"];
|
|
8490
|
+
if (!v) return null;
|
|
8491
|
+
return { "data-text-color": v };
|
|
8492
|
+
}
|
|
8493
|
+
}
|
|
8494
|
+
}
|
|
8495
|
+
}
|
|
8496
|
+
];
|
|
8497
|
+
},
|
|
8498
|
+
addCommands() {
|
|
8499
|
+
const types = this.options.types;
|
|
8500
|
+
const bgColors = this.options.bgColors;
|
|
8501
|
+
const textColors = this.options.textColors;
|
|
8502
|
+
function findTargetPos(state) {
|
|
8503
|
+
const { $from } = state.selection;
|
|
8504
|
+
for (let depth = $from.depth; depth >= 0; depth--) {
|
|
8505
|
+
const node = $from.node(depth);
|
|
8506
|
+
if (types.includes(node.type.name)) {
|
|
8507
|
+
return depth === 0 ? 0 : $from.before(depth);
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
return null;
|
|
8511
|
+
}
|
|
8512
|
+
function setAttr(attr, palette) {
|
|
8513
|
+
return (color) => ({ state, dispatch }) => {
|
|
8514
|
+
if (color !== null && !palette.includes(color)) return false;
|
|
8515
|
+
const pos = findTargetPos(state);
|
|
8516
|
+
if (pos === null) return false;
|
|
8517
|
+
const node = state.doc.nodeAt(pos);
|
|
8518
|
+
if (!node) return false;
|
|
8519
|
+
if (dispatch) {
|
|
8520
|
+
const tr = state.tr.setNodeMarkup(pos, void 0, { ...node.attrs, [attr]: color });
|
|
8521
|
+
stripInlineColorConflicts(tr, state, pos, pos + node.nodeSize, attr === "textColor" ? "text" : "bg");
|
|
8522
|
+
dispatch(tr);
|
|
8523
|
+
}
|
|
8524
|
+
return true;
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
return {
|
|
8528
|
+
setBlockBgColor: setAttr("bgColor", bgColors),
|
|
8529
|
+
setBlockTextColor: setAttr("textColor", textColors),
|
|
8530
|
+
unsetBlockColors: () => ({ state, dispatch }) => {
|
|
8531
|
+
const pos = findTargetPos(state);
|
|
8532
|
+
if (pos === null) return false;
|
|
8533
|
+
const node = state.doc.nodeAt(pos);
|
|
8534
|
+
if (!node) return false;
|
|
8535
|
+
if (dispatch) {
|
|
8536
|
+
const tr = state.tr.setNodeMarkup(pos, void 0, {
|
|
8537
|
+
...node.attrs,
|
|
8538
|
+
bgColor: null,
|
|
8539
|
+
textColor: null
|
|
8540
|
+
});
|
|
8541
|
+
stripInlineColorConflicts(tr, state, pos, pos + node.nodeSize, "both");
|
|
8542
|
+
dispatch(tr);
|
|
8543
|
+
}
|
|
8544
|
+
return true;
|
|
8545
|
+
}
|
|
8546
|
+
};
|
|
8547
|
+
}
|
|
8548
|
+
});
|
|
8549
|
+
var Selection4 = Extension.create({
|
|
7360
8550
|
name: "selection",
|
|
7361
8551
|
addStorage() {
|
|
7362
8552
|
return {
|
|
@@ -7746,9 +8936,19 @@ var TextColor = Extension.create({
|
|
|
7746
8936
|
},
|
|
7747
8937
|
renderHTML: (attributes) => {
|
|
7748
8938
|
const color = attributes["color"];
|
|
7749
|
-
|
|
8939
|
+
const token = attributes["colorToken"];
|
|
8940
|
+
if (!color || token) return null;
|
|
7750
8941
|
return { style: `color: ${color}` };
|
|
7751
8942
|
}
|
|
8943
|
+
},
|
|
8944
|
+
colorToken: {
|
|
8945
|
+
default: null,
|
|
8946
|
+
parseHTML: (element) => element.getAttribute("data-text-color"),
|
|
8947
|
+
renderHTML: (attributes) => {
|
|
8948
|
+
const token = attributes["colorToken"];
|
|
8949
|
+
if (!token) return null;
|
|
8950
|
+
return { "data-text-color": token };
|
|
8951
|
+
}
|
|
7752
8952
|
}
|
|
7753
8953
|
}
|
|
7754
8954
|
}
|
|
@@ -7756,13 +8956,28 @@ var TextColor = Extension.create({
|
|
|
7756
8956
|
},
|
|
7757
8957
|
addCommands() {
|
|
7758
8958
|
return {
|
|
8959
|
+
// Hex-based color. Mutual exclusion: clears the named token so the
|
|
8960
|
+
// legacy hex picker and Notion-style picker can't write conflicting
|
|
8961
|
+
// values to the same mark.
|
|
7759
8962
|
setTextColor: (color) => ({ commands }) => {
|
|
7760
|
-
return commands.setMark("textStyle", { color });
|
|
8963
|
+
return commands.setMark("textStyle", { color, colorToken: null });
|
|
7761
8964
|
},
|
|
7762
8965
|
unsetTextColor: () => ({ commands }) => {
|
|
7763
8966
|
if (!commands.setMark("textStyle", { color: null })) return false;
|
|
7764
8967
|
commands.removeEmptyTextStyle();
|
|
7765
8968
|
return true;
|
|
8969
|
+
},
|
|
8970
|
+
// Named-token color. Mutual exclusion: clears the hex `color` so the
|
|
8971
|
+
// theme-aware data attribute is the only source of truth.
|
|
8972
|
+
setTextColorToken: (token) => ({ commands }) => {
|
|
8973
|
+
if (!commands.setMark("textStyle", { colorToken: token, color: null })) return false;
|
|
8974
|
+
if (token === null) commands.removeEmptyTextStyle();
|
|
8975
|
+
return true;
|
|
8976
|
+
},
|
|
8977
|
+
unsetTextColorToken: () => ({ commands }) => {
|
|
8978
|
+
if (!commands.setMark("textStyle", { colorToken: null })) return false;
|
|
8979
|
+
commands.removeEmptyTextStyle();
|
|
8980
|
+
return true;
|
|
7766
8981
|
}
|
|
7767
8982
|
};
|
|
7768
8983
|
},
|
|
@@ -7864,9 +9079,19 @@ var Highlight = Extension.create({
|
|
|
7864
9079
|
},
|
|
7865
9080
|
renderHTML: (attributes) => {
|
|
7866
9081
|
const bg = attributes["backgroundColor"];
|
|
7867
|
-
|
|
9082
|
+
const token = attributes["backgroundColorToken"];
|
|
9083
|
+
if (!bg || token) return null;
|
|
7868
9084
|
return { style: `background-color: ${bg}` };
|
|
7869
9085
|
}
|
|
9086
|
+
},
|
|
9087
|
+
backgroundColorToken: {
|
|
9088
|
+
default: null,
|
|
9089
|
+
parseHTML: (element) => element.getAttribute("data-bg-color"),
|
|
9090
|
+
renderHTML: (attributes) => {
|
|
9091
|
+
const token = attributes["backgroundColorToken"];
|
|
9092
|
+
if (!token) return null;
|
|
9093
|
+
return { "data-bg-color": token };
|
|
9094
|
+
}
|
|
7870
9095
|
}
|
|
7871
9096
|
}
|
|
7872
9097
|
}
|
|
@@ -7875,9 +9100,12 @@ var Highlight = Extension.create({
|
|
|
7875
9100
|
addCommands() {
|
|
7876
9101
|
const defaultColor = this.options.defaultColor;
|
|
7877
9102
|
return {
|
|
9103
|
+
// Hex-based highlight. Mutual exclusion: clears any named bg token so
|
|
9104
|
+
// the legacy hex picker and the Notion-style picker can't write
|
|
9105
|
+
// conflicting values to the same mark.
|
|
7878
9106
|
setHighlight: (attributes) => ({ commands }) => {
|
|
7879
9107
|
const color = attributes?.color ?? defaultColor;
|
|
7880
|
-
return commands.setMark("textStyle", { backgroundColor: color });
|
|
9108
|
+
return commands.setMark("textStyle", { backgroundColor: color, backgroundColorToken: null });
|
|
7881
9109
|
},
|
|
7882
9110
|
unsetHighlight: () => ({ commands }) => {
|
|
7883
9111
|
if (!commands.setMark("textStyle", { backgroundColor: null })) return false;
|
|
@@ -7893,12 +9121,12 @@ var Highlight = Extension.create({
|
|
|
7893
9121
|
if (empty) {
|
|
7894
9122
|
const marks = state.storedMarks ?? state.doc.resolve(from).marks();
|
|
7895
9123
|
const mark = markType.isInSet(marks);
|
|
7896
|
-
hasHighlight = !!mark?.attrs["backgroundColor"];
|
|
9124
|
+
hasHighlight = !!mark?.attrs["backgroundColor"] || !!mark?.attrs["backgroundColorToken"];
|
|
7897
9125
|
} else {
|
|
7898
9126
|
state.doc.nodesBetween(from, to, (node) => {
|
|
7899
9127
|
if (hasHighlight) return false;
|
|
7900
9128
|
const mark = markType.isInSet(node.marks);
|
|
7901
|
-
if (mark?.attrs["backgroundColor"]) {
|
|
9129
|
+
if (mark?.attrs["backgroundColor"] || mark?.attrs["backgroundColorToken"]) {
|
|
7902
9130
|
hasHighlight = true;
|
|
7903
9131
|
return false;
|
|
7904
9132
|
}
|
|
@@ -7906,11 +9134,26 @@ var Highlight = Extension.create({
|
|
|
7906
9134
|
});
|
|
7907
9135
|
}
|
|
7908
9136
|
if (hasHighlight) {
|
|
7909
|
-
commands.setMark("textStyle", { backgroundColor: null });
|
|
9137
|
+
commands.setMark("textStyle", { backgroundColor: null, backgroundColorToken: null });
|
|
7910
9138
|
commands.removeEmptyTextStyle();
|
|
7911
9139
|
return true;
|
|
7912
9140
|
}
|
|
7913
|
-
return commands.setMark("textStyle", { backgroundColor: color });
|
|
9141
|
+
return commands.setMark("textStyle", { backgroundColor: color, backgroundColorToken: null });
|
|
9142
|
+
},
|
|
9143
|
+
// Named-token highlight. Mutual exclusion: clears the hex
|
|
9144
|
+
// backgroundColor so the theme-aware data attribute is the sole source
|
|
9145
|
+
// of truth.
|
|
9146
|
+
setBackgroundColorToken: (token) => ({ commands }) => {
|
|
9147
|
+
if (!commands.setMark("textStyle", { backgroundColorToken: token, backgroundColor: null })) {
|
|
9148
|
+
return false;
|
|
9149
|
+
}
|
|
9150
|
+
if (token === null) commands.removeEmptyTextStyle();
|
|
9151
|
+
return true;
|
|
9152
|
+
},
|
|
9153
|
+
unsetBackgroundColorToken: () => ({ commands }) => {
|
|
9154
|
+
if (!commands.setMark("textStyle", { backgroundColorToken: null })) return false;
|
|
9155
|
+
commands.removeEmptyTextStyle();
|
|
9156
|
+
return true;
|
|
7914
9157
|
}
|
|
7915
9158
|
};
|
|
7916
9159
|
},
|
|
@@ -8154,6 +9397,61 @@ var FontSize = Extension.create({
|
|
|
8154
9397
|
}
|
|
8155
9398
|
});
|
|
8156
9399
|
|
|
9400
|
+
// src/extensions/NotionColorPicker.ts
|
|
9401
|
+
var DEFAULT_NOTION_COLOR_PALETTE = Object.freeze([
|
|
9402
|
+
"gray",
|
|
9403
|
+
"brown",
|
|
9404
|
+
"orange",
|
|
9405
|
+
"yellow",
|
|
9406
|
+
"green",
|
|
9407
|
+
"blue",
|
|
9408
|
+
"purple",
|
|
9409
|
+
"pink",
|
|
9410
|
+
"red"
|
|
9411
|
+
]);
|
|
9412
|
+
var NotionColorPicker = Extension.create({
|
|
9413
|
+
name: "notionColorPicker",
|
|
9414
|
+
// The picker writes textStyle.colorToken / textStyle.backgroundColorToken,
|
|
9415
|
+
// so TextStyle must be present. TextColor and Highlight are recommended
|
|
9416
|
+
// (they own those attribute schemas) but not strictly required: a host app
|
|
9417
|
+
// could supply its own attribute providers if it really wants to.
|
|
9418
|
+
dependencies: ["textStyle"],
|
|
9419
|
+
addOptions() {
|
|
9420
|
+
return {
|
|
9421
|
+
palette: DEFAULT_NOTION_COLOR_PALETTE
|
|
9422
|
+
};
|
|
9423
|
+
},
|
|
9424
|
+
addStorage() {
|
|
9425
|
+
return {
|
|
9426
|
+
isOpen: false
|
|
9427
|
+
};
|
|
9428
|
+
},
|
|
9429
|
+
addToolbarItems() {
|
|
9430
|
+
return [
|
|
9431
|
+
{
|
|
9432
|
+
type: "button",
|
|
9433
|
+
name: "notionColor",
|
|
9434
|
+
// emitEvent takes priority over `command` at click time. The `command`
|
|
9435
|
+
// field is required by the ToolbarButton schema, so we point it at a
|
|
9436
|
+
// harmless built-in that fires only if a host disables the popover UI.
|
|
9437
|
+
command: "focus",
|
|
9438
|
+
// The "A with an underline" glyph is the closest match in our icon
|
|
9439
|
+
// set; the framework wrapper can repaint the underline to reflect
|
|
9440
|
+
// the current selection's color.
|
|
9441
|
+
icon: "textAUnderline",
|
|
9442
|
+
label: "Text and background color",
|
|
9443
|
+
emitEvent: "notionColorOpen",
|
|
9444
|
+
group: "textStyle",
|
|
9445
|
+
priority: 250,
|
|
9446
|
+
// Bubble-menu-only: the legacy hex pickers (TextColor / Highlight)
|
|
9447
|
+
// stay in the main toolbar; this token-based picker is a Notion-mode
|
|
9448
|
+
// affordance surfaced beside the text-format buttons in the bubble.
|
|
9449
|
+
toolbar: false
|
|
9450
|
+
}
|
|
9451
|
+
];
|
|
9452
|
+
}
|
|
9453
|
+
});
|
|
9454
|
+
|
|
8157
9455
|
// src/extensions/ClearFormatting.ts
|
|
8158
9456
|
var ClearFormatting = Extension.create({
|
|
8159
9457
|
name: "clearFormatting",
|
|
@@ -8242,9 +9540,18 @@ function linkPopoverPlugin({ editor, markType, protocols }) {
|
|
|
8242
9540
|
return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
|
|
8243
9541
|
}
|
|
8244
9542
|
};
|
|
9543
|
+
if (anchorElement) {
|
|
9544
|
+
const editorEl = anchorElement.closest(".dm-editor");
|
|
9545
|
+
if (editorEl && el.parentElement !== editorEl) {
|
|
9546
|
+
editorEl.appendChild(el);
|
|
9547
|
+
}
|
|
9548
|
+
} else if (el.parentElement !== document.body) {
|
|
9549
|
+
document.body.appendChild(el);
|
|
9550
|
+
}
|
|
9551
|
+
copyThemeClass(editor.view, el);
|
|
8245
9552
|
cleanupFloating?.();
|
|
8246
9553
|
cleanupFloating = positionFloating(reference, el, {
|
|
8247
|
-
placement: "bottom",
|
|
9554
|
+
placement: anchorElement ? "bottom-start" : "bottom",
|
|
8248
9555
|
offsetValue: 4
|
|
8249
9556
|
});
|
|
8250
9557
|
input.focus();
|
|
@@ -8501,6 +9808,7 @@ function createBubbleMenuPlugin(options) {
|
|
|
8501
9808
|
if (!target) return;
|
|
8502
9809
|
if (element.contains(target)) return;
|
|
8503
9810
|
if (editor.view.dom.contains(target)) return;
|
|
9811
|
+
if (target instanceof HTMLElement && target.closest("[data-dm-editor-ui]")) return;
|
|
8504
9812
|
hideMenu();
|
|
8505
9813
|
suppressed = true;
|
|
8506
9814
|
};
|
|
@@ -8577,8 +9885,10 @@ function createBubbleMenuPlugin(options) {
|
|
|
8577
9885
|
});
|
|
8578
9886
|
};
|
|
8579
9887
|
const onBlur = ({ event }) => {
|
|
8580
|
-
|
|
8581
|
-
|
|
9888
|
+
const related = event.relatedTarget;
|
|
9889
|
+
if (related instanceof HTMLElement) {
|
|
9890
|
+
if (element.contains(related)) return;
|
|
9891
|
+
if (related.closest("[data-dm-editor-ui]")) return;
|
|
8582
9892
|
}
|
|
8583
9893
|
hideMenu();
|
|
8584
9894
|
};
|
|
@@ -8678,132 +9988,6 @@ var BubbleMenu = Extension.create({
|
|
|
8678
9988
|
];
|
|
8679
9989
|
}
|
|
8680
9990
|
});
|
|
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
9991
|
|
|
8808
9992
|
// src/extensions/StarterKit.ts
|
|
8809
9993
|
var StarterKit = Extension.create({
|
|
@@ -8846,6 +10030,7 @@ var StarterKit = Extension.create({
|
|
|
8846
10030
|
maybeAdd(Gapcursor, this.options.gapcursor);
|
|
8847
10031
|
maybeAdd(TrailingNode, this.options.trailingNode);
|
|
8848
10032
|
maybeAdd(ListKeymap, this.options.listKeymap);
|
|
10033
|
+
maybeAdd(ListIndent, this.options.listIndent);
|
|
8849
10034
|
maybeAdd(LinkPopover, this.options.linkPopover);
|
|
8850
10035
|
maybeAdd(SelectionDecoration, this.options.selectionDecoration);
|
|
8851
10036
|
return extensions;
|
|
@@ -8860,6 +10045,7 @@ Object.defineProperty(exports, "PluginKey", {
|
|
|
8860
10045
|
get: function () { return state.PluginKey; }
|
|
8861
10046
|
});
|
|
8862
10047
|
exports.BaseKeymap = BaseKeymap;
|
|
10048
|
+
exports.BlockColor = BlockColor;
|
|
8863
10049
|
exports.Blockquote = Blockquote;
|
|
8864
10050
|
exports.Bold = Bold;
|
|
8865
10051
|
exports.BubbleMenu = BubbleMenu;
|
|
@@ -8871,7 +10057,10 @@ exports.ClearFormatting = ClearFormatting;
|
|
|
8871
10057
|
exports.Code = Code;
|
|
8872
10058
|
exports.CodeBlock = CodeBlock;
|
|
8873
10059
|
exports.CommandManager = CommandManager;
|
|
10060
|
+
exports.DEFAULT_BLOCK_COLORS = DEFAULT_BLOCK_COLORS;
|
|
10061
|
+
exports.DEFAULT_BLOCK_COLOR_TYPES = DEFAULT_BLOCK_COLOR_TYPES;
|
|
8874
10062
|
exports.DEFAULT_HIGHLIGHT_COLORS = DEFAULT_HIGHLIGHT_COLORS;
|
|
10063
|
+
exports.DEFAULT_NOTION_COLOR_PALETTE = DEFAULT_NOTION_COLOR_PALETTE;
|
|
8875
10064
|
exports.DEFAULT_TEXT_COLORS = DEFAULT_TEXT_COLORS;
|
|
8876
10065
|
exports.Document = Document;
|
|
8877
10066
|
exports.Dropcursor = Dropcursor;
|
|
@@ -8879,7 +10068,8 @@ exports.Editor = Editor;
|
|
|
8879
10068
|
exports.EventEmitter = EventEmitter;
|
|
8880
10069
|
exports.Extension = Extension;
|
|
8881
10070
|
exports.ExtensionManager = ExtensionManager;
|
|
8882
|
-
exports.
|
|
10071
|
+
exports.FLOATING_MENU_NO_FOCUS = FLOATING_MENU_NO_FOCUS;
|
|
10072
|
+
exports.FloatingMenuController = FloatingMenuController;
|
|
8883
10073
|
exports.Focus = Focus;
|
|
8884
10074
|
exports.FontFamily = FontFamily;
|
|
8885
10075
|
exports.FontSize = FontSize;
|
|
@@ -8891,17 +10081,20 @@ exports.History = History;
|
|
|
8891
10081
|
exports.HorizontalRule = HorizontalRule;
|
|
8892
10082
|
exports.InvisibleChars = InvisibleChars;
|
|
8893
10083
|
exports.Italic = Italic;
|
|
10084
|
+
exports.LIST_ITEM_TYPE_NAMES = LIST_ITEM_TYPE_NAMES;
|
|
8894
10085
|
exports.LineHeight = LineHeight;
|
|
8895
10086
|
exports.Link = Link;
|
|
8896
10087
|
exports.LinkPopover = LinkPopover;
|
|
10088
|
+
exports.ListIndent = ListIndent;
|
|
8897
10089
|
exports.ListItem = ListItem;
|
|
8898
10090
|
exports.ListKeymap = ListKeymap;
|
|
8899
10091
|
exports.Mark = Mark;
|
|
8900
|
-
exports.Node =
|
|
10092
|
+
exports.Node = Node2;
|
|
10093
|
+
exports.NotionColorPicker = NotionColorPicker;
|
|
8901
10094
|
exports.OrderedList = OrderedList;
|
|
8902
10095
|
exports.Paragraph = Paragraph;
|
|
8903
10096
|
exports.Placeholder = Placeholder;
|
|
8904
|
-
exports.Selection =
|
|
10097
|
+
exports.Selection = Selection4;
|
|
8905
10098
|
exports.SelectionDecoration = SelectionDecoration;
|
|
8906
10099
|
exports.StarterKit = StarterKit;
|
|
8907
10100
|
exports.Strike = Strike;
|
|
@@ -8929,32 +10122,42 @@ exports.builtInCommands = builtInCommands;
|
|
|
8929
10122
|
exports.callOrReturn = callOrReturn;
|
|
8930
10123
|
exports.characterCountPluginKey = characterCountPluginKey;
|
|
8931
10124
|
exports.clearContent = clearContent;
|
|
10125
|
+
exports.copyThemeClass = copyThemeClass;
|
|
8932
10126
|
exports.createAccumulatingDispatch = createAccumulatingDispatch;
|
|
8933
10127
|
exports.createBubbleMenuPlugin = createBubbleMenuPlugin;
|
|
8934
10128
|
exports.createCanChecker = createCanChecker;
|
|
8935
10129
|
exports.createChainBuilder = createChainBuilder;
|
|
8936
10130
|
exports.createDocument = createDocument;
|
|
8937
|
-
exports.createFloatingMenuPlugin = createFloatingMenuPlugin;
|
|
8938
10131
|
exports.defaultBlockAt = defaultBlockAt;
|
|
10132
|
+
exports.defaultBubbleContexts = defaultBubbleContexts;
|
|
8939
10133
|
exports.defaultIcons = defaultIcons;
|
|
8940
10134
|
exports.deleteSelection = deleteSelection;
|
|
8941
10135
|
exports.findChildren = findChildren;
|
|
10136
|
+
exports.findListItemAncestorDepth = findListItemAncestorDepth;
|
|
8942
10137
|
exports.findParentNode = findParentNode;
|
|
8943
|
-
exports.floatingMenuPluginKey = floatingMenuPluginKey;
|
|
8944
10138
|
exports.focus = focus;
|
|
8945
10139
|
exports.focusPluginKey = focusPluginKey;
|
|
8946
10140
|
exports.generateHTML = generateHTML;
|
|
8947
10141
|
exports.generateJSON = generateJSON;
|
|
8948
10142
|
exports.generateText = generateText;
|
|
10143
|
+
exports.getListItemCursorContext = getListItemCursorContext;
|
|
8949
10144
|
exports.getMarkRange = getMarkRange;
|
|
10145
|
+
exports.groupFloatingMenuItems = groupFloatingMenuItems;
|
|
10146
|
+
exports.indentBlockAsListChild = indentBlockAsListChild;
|
|
8950
10147
|
exports.inlineStyles = inlineStyles;
|
|
10148
|
+
exports.insertAsListItemChild = insertAsListItemChild;
|
|
10149
|
+
exports.insertChildrenZoneSibling = insertChildrenZoneSibling;
|
|
8951
10150
|
exports.insertContent = insertContent;
|
|
8952
10151
|
exports.insertText = insertText;
|
|
8953
10152
|
exports.invisibleCharsPluginKey = invisibleCharsPluginKey;
|
|
8954
10153
|
exports.isDocumentEmpty = isDocumentEmpty;
|
|
10154
|
+
exports.isInListItemLabel = isInListItemLabel;
|
|
10155
|
+
exports.isInsideListItem = isInsideListItem;
|
|
8955
10156
|
exports.isNodeEmpty = isNodeEmpty;
|
|
8956
10157
|
exports.isValidUrl = isValidUrl;
|
|
8957
10158
|
exports.lift = lift;
|
|
10159
|
+
exports.liftCurrentListItem = liftCurrentListItem;
|
|
10160
|
+
exports.liftEmptyChildrenZoneParagraph = liftEmptyChildrenZoneParagraph;
|
|
8958
10161
|
exports.linkClickPlugin = linkClickPlugin;
|
|
8959
10162
|
exports.linkClickPluginKey = linkClickPluginKey;
|
|
8960
10163
|
exports.linkExitPlugin = linkExitPlugin;
|
|
@@ -8964,6 +10167,7 @@ exports.linkPastePluginKey = linkPastePluginKey;
|
|
|
8964
10167
|
exports.markInputRule = markInputRule;
|
|
8965
10168
|
exports.markInputRulePatterns = markInputRulePatterns;
|
|
8966
10169
|
exports.nodeInputRule = nodeInputRule;
|
|
10170
|
+
exports.outdentBlockFromListItem = outdentBlockFromListItem;
|
|
8967
10171
|
exports.placeholderPluginKey = placeholderPluginKey;
|
|
8968
10172
|
exports.positionFloating = positionFloating;
|
|
8969
10173
|
exports.positionFloatingOnce = positionFloatingOnce;
|
|
@@ -8974,6 +10178,8 @@ exports.selectionDecorationPluginKey = selectionDecorationPluginKey;
|
|
|
8974
10178
|
exports.setBlockType = setBlockType;
|
|
8975
10179
|
exports.setContent = setContent;
|
|
8976
10180
|
exports.setMark = setMark;
|
|
10181
|
+
exports.splitListForInsert = splitListForInsert;
|
|
10182
|
+
exports.stripInlineColorConflicts = stripInlineColorConflicts;
|
|
8977
10183
|
exports.textInputRule = textInputRule;
|
|
8978
10184
|
exports.textblockTypeInputRule = textblockTypeInputRule;
|
|
8979
10185
|
exports.toggleBlockType = toggleBlockType;
|
|
@@ -8986,5 +10192,6 @@ exports.unsetMark = unsetMark;
|
|
|
8986
10192
|
exports.updateAttributes = updateAttributes;
|
|
8987
10193
|
exports.wrapIn = wrapIn;
|
|
8988
10194
|
exports.wrappingInputRule = wrappingInputRule;
|
|
10195
|
+
exports.writeToClipboard = writeToClipboard;
|
|
8989
10196
|
//# sourceMappingURL=index.cjs.map
|
|
8990
10197
|
//# sourceMappingURL=index.cjs.map
|