@notectl/core 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs → AttrRegistry-D9Jx_RVb.mjs} +2 -2
- package/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs.map → AttrRegistry-D9Jx_RVb.mjs.map} +1 -1
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs +80 -0
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs.map +1 -0
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs → CaretNavigation-7JYpIKbs.mjs} +3 -3
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs.map → CaretNavigation-7JYpIKbs.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-BBkN0AOb.mjs → ColorGrid--lBQ7jCK.mjs} +56 -55
- package/dist/chunks/ColorGrid--lBQ7jCK.mjs.map +1 -0
- package/dist/chunks/{ColorPickerPopup-D3ysvOH-.mjs → ColorPickerPopup-TpIRlXc8.mjs} +15 -15
- package/dist/chunks/ColorPickerPopup-TpIRlXc8.mjs.map +1 -0
- package/dist/chunks/{Document-DfZ1NOjs.mjs → Document-Brtw2jL1.mjs} +6 -6
- package/dist/chunks/Document-Brtw2jL1.mjs.map +1 -0
- package/dist/chunks/{MarkCommands-uybmkw9l.mjs → MarkCommands-6RUJ4L3r.mjs} +3 -3
- package/dist/chunks/MarkCommands-6RUJ4L3r.mjs.map +1 -0
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs → NodeResolver-C2XYkeOi.mjs} +2 -2
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs.map → NodeResolver-C2XYkeOi.mjs.map} +1 -1
- package/dist/chunks/NodeSpec-NZVvcCuH.mjs.map +1 -1
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs +34 -0
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs.map +1 -0
- package/dist/chunks/RangeIterator-Bh4Vl_6D.mjs +16 -0
- package/dist/chunks/{RangeIterator-Bye0UtbY.mjs.map → RangeIterator-Bh4Vl_6D.mjs.map} +1 -1
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs +235 -0
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs.map +1 -0
- package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
- package/dist/chunks/{index-Bt_d4IqB.mjs → index--7pBhSF4.mjs} +74 -73
- package/dist/chunks/index--7pBhSF4.mjs.map +1 -0
- package/dist/chunks/{index-DayTn6Re.mjs → index-BXCkjDax.mjs} +78 -83
- package/dist/chunks/index-BXCkjDax.mjs.map +1 -0
- package/dist/chunks/{index-DGKQhSAl.mjs → index-BXtw5PNN.mjs} +2 -2
- package/dist/chunks/{index-DGKQhSAl.mjs.map → index-BXtw5PNN.mjs.map} +1 -1
- package/dist/chunks/{ToolbarPlugin-B7oJRzpl.mjs → index-BcDRT4K2.mjs} +110 -121
- package/dist/chunks/index-BcDRT4K2.mjs.map +1 -0
- package/dist/chunks/{index-DoCvDkfj.mjs → index-BjFBWBOC.mjs} +23 -25
- package/dist/chunks/index-BjFBWBOC.mjs.map +1 -0
- package/dist/chunks/{index-CrKz_IEn.mjs → index-C1opJocO.mjs} +2 -2
- package/dist/chunks/{index-CrKz_IEn.mjs.map → index-C1opJocO.mjs.map} +1 -1
- package/dist/chunks/{index-CfyS_pgH.mjs → index-CCyqQ0Dz.mjs} +32 -31
- package/dist/chunks/index-CCyqQ0Dz.mjs.map +1 -0
- package/dist/chunks/{index-E7OgN7hM.mjs → index-Cd3uJLP3.mjs} +2 -2
- package/dist/chunks/{index-E7OgN7hM.mjs.map → index-Cd3uJLP3.mjs.map} +1 -1
- package/dist/chunks/{index-yhz2qeeh.mjs → index-DSAuGP4n.mjs} +131 -127
- package/dist/chunks/index-DSAuGP4n.mjs.map +1 -0
- package/dist/chunks/{index-CH1r7DL-.mjs → index-DtRfqsq5.mjs} +211 -210
- package/dist/chunks/index-DtRfqsq5.mjs.map +1 -0
- package/dist/chunks/{index-CFPP456m.mjs → index-DxbQirny.mjs} +8 -8
- package/dist/chunks/{index-CFPP456m.mjs.map → index-DxbQirny.mjs.map} +1 -1
- package/dist/chunks/{index-DuRNETFv.mjs → index-ErZ5_a1o.mjs} +3 -3
- package/dist/chunks/{index-DuRNETFv.mjs.map → index-ErZ5_a1o.mjs.map} +1 -1
- package/dist/chunks/{index-Deag_lVi.mjs → index-WpbK_s_H.mjs} +29 -31
- package/dist/chunks/index-WpbK_s_H.mjs.map +1 -0
- package/dist/chunks/{index-DIvHQtE2.mjs → index-WrqH1wG8.mjs} +15 -14
- package/dist/chunks/index-WrqH1wG8.mjs.map +1 -0
- package/dist/chunks/{index-Dy6SllXW.mjs → index-fsVJLutn.mjs} +3 -3
- package/dist/chunks/{index-Dy6SllXW.mjs.map → index-fsVJLutn.mjs.map} +1 -1
- package/dist/chunks/{index-Ckzqtn5D.mjs → index-fx3KIap3.mjs} +15 -14
- package/dist/chunks/index-fx3KIap3.mjs.map +1 -0
- package/dist/chunks/{index-DmuuFS5D.mjs → index-oOJo-ywB.mjs} +4 -4
- package/dist/chunks/{index-DmuuFS5D.mjs.map → index-oOJo-ywB.mjs.map} +1 -1
- package/dist/chunks/{index-CFJFD4lF.mjs → index-toj88jtL.mjs} +19 -18
- package/dist/chunks/index-toj88jtL.mjs.map +1 -0
- package/dist/commands/AttributedMarkCommands.d.ts.map +1 -1
- package/dist/commands/MarkCommands.d.ts +3 -3
- package/dist/commands/MarkCommands.d.ts.map +1 -1
- package/dist/editor/CSSClassCollector.d.ts +36 -0
- package/dist/editor/CSSClassCollector.d.ts.map +1 -0
- package/dist/editor/ContentHTMLTypes.d.ts +29 -0
- package/dist/editor/ContentHTMLTypes.d.ts.map +1 -0
- package/dist/editor/ContentStyleInjector.d.ts +69 -0
- package/dist/editor/ContentStyleInjector.d.ts.map +1 -0
- package/dist/editor/DocumentParser.d.ts +9 -1
- package/dist/editor/DocumentParser.d.ts.map +1 -1
- package/dist/editor/DocumentSerializer.d.ts +6 -0
- package/dist/editor/DocumentSerializer.d.ts.map +1 -1
- package/dist/editor/EditorThemeController.d.ts +3 -0
- package/dist/editor/EditorThemeController.d.ts.map +1 -1
- package/dist/editor/MarkSerializer.d.ts +7 -0
- package/dist/editor/MarkSerializer.d.ts.map +1 -1
- package/dist/editor/NotectlEditor.d.ts +13 -3
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/PaperLayoutController.d.ts.map +1 -1
- package/dist/editor/styles/font-select.d.ts +1 -1
- package/dist/editor/styles/font-select.d.ts.map +1 -1
- package/dist/editor/styles/font-size-select.d.ts +1 -1
- package/dist/editor/styles/font-size-select.d.ts.map +1 -1
- package/dist/editor/styles/heading-select.d.ts +1 -1
- package/dist/editor/styles/heading-select.d.ts.map +1 -1
- package/dist/editor/styles/toolbar.d.ts +1 -1
- package/dist/editor/styles/toolbar.d.ts.map +1 -1
- package/dist/index.d.ts +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/input/LegacyHTMLNormalizer.d.ts.map +1 -1
- package/dist/model/Document.d.ts +0 -4
- package/dist/model/Document.d.ts.map +1 -1
- package/dist/model/NodeSpec.d.ts +19 -2
- package/dist/model/NodeSpec.d.ts.map +1 -1
- package/dist/notectl-core.mjs +613 -477
- package/dist/notectl-core.mjs.map +1 -1
- package/dist/notectl-core.umd.js +1571 -1225
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockNodeView.d.ts.map +1 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts +0 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts.map +1 -1
- package/dist/plugins/code-block/index.d.ts +2 -1
- package/dist/plugins/code-block/index.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font/FontPlugin.d.ts +0 -1
- package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -2
- package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts +3 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts.map +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +6 -6
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image/ImageNodeView.d.ts.map +1 -1
- package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print/PrintContentPreparer.d.ts.map +1 -1
- package/dist/plugins/shared/ColorGrid.d.ts.map +1 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts +2 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
- package/dist/plugins/shared/PopupManager.d.ts +12 -2
- package/dist/plugins/shared/PopupManager.d.ts.map +1 -1
- package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
- package/dist/plugins/table/TableControls.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -1
- package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
- package/dist/plugins/table/TablePlugin.d.ts.map +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar/ToolbarItem.d.ts +3 -2
- package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts +0 -3
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarRenderers.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarTooltip.d.ts.map +1 -1
- package/dist/plugins/toolbar.mjs +2 -2
- package/dist/presets/PresetTypes.d.ts +1 -1
- package/dist/presets/PresetTypes.d.ts.map +1 -1
- package/dist/style/StyleRuntime.d.ts +34 -0
- package/dist/style/StyleRuntime.d.ts.map +1 -0
- package/dist/view/Reconciler.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs +0 -58
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs.map +0 -1
- package/dist/chunks/ColorGrid-BBkN0AOb.mjs.map +0 -1
- package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs.map +0 -1
- package/dist/chunks/Document-DfZ1NOjs.mjs.map +0 -1
- package/dist/chunks/MarkCommands-uybmkw9l.mjs.map +0 -1
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs +0 -30
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs.map +0 -1
- package/dist/chunks/RangeIterator-Bye0UtbY.mjs +0 -16
- package/dist/chunks/ToolbarPlugin-B7oJRzpl.mjs.map +0 -1
- package/dist/chunks/index-Bt_d4IqB.mjs.map +0 -1
- package/dist/chunks/index-CFJFD4lF.mjs.map +0 -1
- package/dist/chunks/index-CH1r7DL-.mjs.map +0 -1
- package/dist/chunks/index-CfyS_pgH.mjs.map +0 -1
- package/dist/chunks/index-Ckzqtn5D.mjs.map +0 -1
- package/dist/chunks/index-DIvHQtE2.mjs.map +0 -1
- package/dist/chunks/index-DayTn6Re.mjs.map +0 -1
- package/dist/chunks/index-Deag_lVi.mjs.map +0 -1
- package/dist/chunks/index-DoCvDkfj.mjs.map +0 -1
- package/dist/chunks/index-yhz2qeeh.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as n } from "./Document-Brtw2jL1.mjs";
|
|
2
2
|
function t(n2, t2) {
|
|
3
3
|
return n2.type === t2;
|
|
4
4
|
}
|
|
@@ -13,4 +13,4 @@ export {
|
|
|
13
13
|
r as b,
|
|
14
14
|
e as i
|
|
15
15
|
};
|
|
16
|
-
//# sourceMappingURL=AttrRegistry-
|
|
16
|
+
//# sourceMappingURL=AttrRegistry-D9Jx_RVb.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttrRegistry-
|
|
1
|
+
{"version":3,"file":"AttrRegistry-D9Jx_RVb.mjs","sources":["../../src/model/AttrRegistry.ts"],"sourcesContent":["/**\n * Module-augmentable attribute registries for type-safe node/mark attributes.\n *\n * Plugins extend these interfaces via `declare module`:\n * @example\n * declare module '../../model/AttrRegistry.js' {\n * interface NodeAttrRegistry {\n * heading: { level: HeadingLevel; align?: BlockAlignment };\n * }\n * }\n */\n\nimport type { BlockAlignment } from '../plugins/alignment/AlignmentPlugin.js';\nimport type { BlockNode, InlineNode, Mark } from './Document.js';\nimport { isInlineNode } from './Document.js';\n\n/** Plugins augment this interface to register typed node attributes. */\nexport interface NodeAttrRegistry {\n\tparagraph: { align?: BlockAlignment };\n}\n\n/** Plugins augment this interface to register typed mark attributes. */\nexport interface MarkAttrRegistry {\n\tbold: Record<string, never>;\n\titalic: Record<string, never>;\n\tunderline: Record<string, never>;\n}\n\n/** Resolves typed attributes for known node types, falls back for unknown. */\nexport type NodeAttrsFor<T extends string> = T extends keyof NodeAttrRegistry\n\t? NodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Resolves typed attributes for known mark types, falls back for unknown. */\nexport type MarkAttrsFor<T extends string> = T extends keyof MarkAttrRegistry\n\t? MarkAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows a BlockNode to a typed variant with known attributes. */\nexport function isNodeOfType<T extends keyof NodeAttrRegistry>(\n\tnode: BlockNode,\n\ttype: T,\n): node is BlockNode & { readonly type: T; readonly attrs: NodeAttrRegistry[T] } {\n\treturn (node.type as string) === type;\n}\n\n/** Narrows a Mark to a typed variant with known attributes. */\nexport function isMarkOfType<T extends keyof MarkAttrRegistry>(\n\tmark: Mark,\n\ttype: T,\n): mark is Mark & { readonly type: T; readonly attrs: MarkAttrRegistry[T] } {\n\treturn (mark.type as string) === type;\n}\n\n// --- InlineNode Attribute Registry ---\n\n/** Plugins augment this interface to register typed inline node attributes. */\n// biome-ignore lint/suspicious/noEmptyInterface: module augmentation requires interface\nexport interface InlineNodeAttrRegistry {}\n\n/** Resolves typed attributes for known inline node types, falls back for unknown. */\nexport type InlineNodeAttrsFor<T extends string> = T extends keyof InlineNodeAttrRegistry\n\t? InlineNodeAttrRegistry[T]\n\t: Record<string, string | number | boolean>;\n\n/** Narrows an InlineNode to a typed variant with known attributes. */\nexport function isInlineNodeOfType<T extends keyof InlineNodeAttrRegistry>(\n\tnode: InlineNode,\n\tinlineNodeType: T,\n): node is InlineNode & {\n\treadonly inlineType: T;\n\treadonly attrs: InlineNodeAttrRegistry[T];\n} {\n\treturn isInlineNode(node) && (node.inlineType as string) === inlineNodeType;\n}\n"],"names":["isNodeOfType","node","type","isMarkOfType","mark","isInlineNodeOfType","inlineNodeType","isInlineNode","inlineType"],"mappings":";AAuCO,SAASA,EACfC,IACAC,IAAAA;AAEA,SAAQD,GAAKC,SAAoBA;AAClC;AAGO,SAASC,EACfC,IACAF,IAAAA;AAEA,SAAQE,GAAKF,SAAoBA;AAClC;AAcO,SAASG,EACfJ,IACAK,IAAAA;AAKA,SAAOC,EAAaN,EAAAA,KAAUA,GAAKO,eAA0BF;AAC9D;"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { i as t } from "./AttrRegistry-D9Jx_RVb.mjs";
|
|
2
|
+
import { g as n, h as o, a as r, i as e } from "./Document-Brtw2jL1.mjs";
|
|
3
|
+
import { i as s, a as c, b as a, s as i } from "./Selection-BRTab5hO.mjs";
|
|
4
|
+
import { m as l } from "./TypeBrands-C09nE2qO.mjs";
|
|
5
|
+
import { f } from "./RangeIterator-Bh4Vl_6D.mjs";
|
|
6
|
+
function u(t2, o2) {
|
|
7
|
+
const r2 = t2.selection;
|
|
8
|
+
if (s(r2) || c(r2)) return null;
|
|
9
|
+
const e2 = l(o2.type);
|
|
10
|
+
if (a(r2)) {
|
|
11
|
+
const s2 = t2.getBlock(r2.anchor.blockId);
|
|
12
|
+
if (!s2) return null;
|
|
13
|
+
const c2 = [...(t2.storedMarks ?? n(s2, r2.anchor.offset)).filter((t3) => t3.type !== e2), o2];
|
|
14
|
+
return t2.transaction("command").setStoredMarks(c2, t2.storedMarks).setSelection(r2).build();
|
|
15
|
+
}
|
|
16
|
+
const u2 = i(r2, t2.getBlockOrder()), d2 = t2.transaction("command");
|
|
17
|
+
return f(t2, u2, (n2, r3, s2) => {
|
|
18
|
+
const c2 = t2.getBlock(n2);
|
|
19
|
+
if (c2) {
|
|
20
|
+
const t3 = p(c2, e2, r3, s2);
|
|
21
|
+
t3 && d2.removeMark(n2, r3, s2, t3);
|
|
22
|
+
}
|
|
23
|
+
d2.addMark(n2, r3, s2, o2);
|
|
24
|
+
}), d2.setSelection(r2), d2.build();
|
|
25
|
+
}
|
|
26
|
+
function d(t2, r2) {
|
|
27
|
+
const e2 = t2.selection;
|
|
28
|
+
if (s(e2) || c(e2)) return null;
|
|
29
|
+
if (a(e2)) {
|
|
30
|
+
const s2 = t2.getBlock(e2.anchor.blockId);
|
|
31
|
+
if (!s2) return null;
|
|
32
|
+
const c2 = t2.storedMarks ?? n(s2, e2.anchor.offset);
|
|
33
|
+
if (!o(c2, r2)) return null;
|
|
34
|
+
const a2 = c2.filter((t3) => t3.type !== r2);
|
|
35
|
+
return t2.transaction("command").setStoredMarks(a2, t2.storedMarks).setSelection(e2).build();
|
|
36
|
+
}
|
|
37
|
+
const l2 = i(e2, t2.getBlockOrder()), u2 = t2.transaction("command");
|
|
38
|
+
return f(t2, l2, (n2, o2, e3) => {
|
|
39
|
+
const s2 = t2.getBlock(n2), c2 = s2 ? p(s2, r2, o2, e3) : void 0;
|
|
40
|
+
u2.removeMark(n2, o2, e3, c2 ?? { type: r2 });
|
|
41
|
+
}), u2.setSelection(e2), u2.build();
|
|
42
|
+
}
|
|
43
|
+
function m(o2, r2, e2) {
|
|
44
|
+
const i2 = o2.selection;
|
|
45
|
+
if (s(i2) || c(i2)) return null;
|
|
46
|
+
if (a(i2) && o2.storedMarks) {
|
|
47
|
+
const n2 = o2.storedMarks.find((t2) => t2.type === r2);
|
|
48
|
+
return n2 && t(n2, r2) ? e2(n2) : null;
|
|
49
|
+
}
|
|
50
|
+
const l2 = o2.getBlock(i2.anchor.blockId);
|
|
51
|
+
if (!l2) return null;
|
|
52
|
+
const f2 = n(l2, i2.anchor.offset).find((t2) => t2.type === r2);
|
|
53
|
+
return f2 && t(f2, r2) ? e2(f2) : null;
|
|
54
|
+
}
|
|
55
|
+
function k(t2, n2) {
|
|
56
|
+
return null !== m(t2, n2, () => true);
|
|
57
|
+
}
|
|
58
|
+
function p(t2, n2, o2, s2) {
|
|
59
|
+
const c2 = r(t2);
|
|
60
|
+
let a2 = 0;
|
|
61
|
+
for (const t3 of c2) {
|
|
62
|
+
if (!e(t3)) {
|
|
63
|
+
a2 += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const r2 = a2 + t3.text.length;
|
|
67
|
+
if (r2 > o2 && a2 < s2) {
|
|
68
|
+
const o3 = t3.marks.find((t4) => t4.type === n2);
|
|
69
|
+
if (o3) return o3;
|
|
70
|
+
}
|
|
71
|
+
a2 = r2;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
u as a,
|
|
76
|
+
m as g,
|
|
77
|
+
k as i,
|
|
78
|
+
d as r
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=AttributedMarkCommands-BtLea72z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttributedMarkCommands-BtLea72z.mjs","sources":["../../src/commands/AttributedMarkCommands.ts"],"sourcesContent":["/**\n * Generic helpers for attributed marks (marks with key/value attrs).\n *\n * Unlike boolean marks (`bold`, `italic`) that toggle on/off, attributed\n * marks (`font`, `fontSize`, `textColor`, `highlight`) carry data and use\n * **replace** semantics: applying a new value removes the old one first.\n *\n * These helpers extract the duplicated collapsed-vs-range branching that\n * was previously copy-pasted across ColorMarkOperations, FontSizeOperations,\n * and FontPlugin.\n */\n\nimport { isMarkOfType } from '../model/AttrRegistry.js';\nimport type { MarkAttrRegistry } from '../model/AttrRegistry.js';\nimport type { BlockNode, Mark, TextNode } from '../model/Document.js';\nimport {\n\tgetBlockMarksAtOffset,\n\tgetInlineChildren,\n\thasMark,\n\tisTextNode,\n} from '../model/Document.js';\nimport type { InlineNode } from '../model/Document.js';\nimport { isCollapsed, isGapCursor, isNodeSelection, selectionRange } from '../model/Selection.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { MarkTypeName } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { forEachBlockInRange } from './RangeIterator.js';\n\n/**\n * Applies an attributed mark to the current selection.\n *\n * - **Collapsed cursor**: replaces stored marks (filters out old mark of the\n * same type, appends the new one).\n * - **Range selection**: removes then re-adds the mark in every block span.\n *\n * Returns the built `Transaction`, or `null` when the selection is\n * unsupported (node-selection / gap-cursor) or the anchor block is missing.\n */\nexport function applyAttributedMark(state: EditorState, mark: Mark): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst typeName: MarkTypeName = markType(mark.type as string);\n\n\tif (isCollapsed(sel)) {\n\t\tconst anchorBlock = state.getBlock(sel.anchor.blockId);\n\t\tif (!anchorBlock) return null;\n\t\tconst currentMarks: readonly Mark[] =\n\t\t\tstate.storedMarks ?? getBlockMarksAtOffset(anchorBlock, sel.anchor.offset);\n\t\tconst withoutMark: readonly Mark[] = currentMarks.filter((m) => m.type !== typeName);\n\t\tconst newMarks: readonly Mark[] = [...withoutMark, mark];\n\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setStoredMarks(newMarks, state.storedMarks)\n\t\t\t.setSelection(sel)\n\t\t\t.build();\n\t}\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\n\tforEachBlockInRange(state, range, (blockId, from, to) => {\n\t\tconst block: BlockNode | undefined = state.getBlock(blockId);\n\t\tif (block) {\n\t\t\tconst existing: Mark | undefined = findMarkInRange(block, typeName, from, to);\n\t\t\tif (existing) {\n\t\t\t\tbuilder.removeMark(blockId, from, to, existing);\n\t\t\t}\n\t\t}\n\t\tbuilder.addMark(blockId, from, to, mark);\n\t});\n\n\tbuilder.setSelection(sel);\n\treturn builder.build();\n}\n\n/**\n * Removes an attributed mark from the current selection.\n *\n * - **Collapsed cursor**: filters the mark out of stored marks.\n * Returns `null` when the mark was not present.\n * - **Range selection**: removes the mark across all block spans.\n *\n * Returns the built `Transaction`, or `null` when the selection is\n * unsupported or the mark is absent at a collapsed cursor.\n */\nexport function removeAttributedMark(\n\tstate: EditorState,\n\tmarkTypeName: MarkTypeName,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (isCollapsed(sel)) {\n\t\tconst anchorBlock = state.getBlock(sel.anchor.blockId);\n\t\tif (!anchorBlock) return null;\n\t\tconst currentMarks: readonly Mark[] =\n\t\t\tstate.storedMarks ?? getBlockMarksAtOffset(anchorBlock, sel.anchor.offset);\n\t\tif (!hasMark(currentMarks, markTypeName)) return null;\n\n\t\tconst newMarks: readonly Mark[] = currentMarks.filter((m) => m.type !== markTypeName);\n\t\treturn state\n\t\t\t.transaction('command')\n\t\t\t.setStoredMarks(newMarks, state.storedMarks)\n\t\t\t.setSelection(sel)\n\t\t\t.build();\n\t}\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\n\tforEachBlockInRange(state, range, (blockId, from, to) => {\n\t\tconst block: BlockNode | undefined = state.getBlock(blockId);\n\t\tconst existing: Mark | undefined = block\n\t\t\t? findMarkInRange(block, markTypeName, from, to)\n\t\t\t: undefined;\n\t\tbuilder.removeMark(blockId, from, to, existing ?? { type: markTypeName });\n\t});\n\n\tbuilder.setSelection(sel);\n\treturn builder.build();\n}\n\n/**\n * Extracts a typed attribute value from the mark at the current selection.\n *\n * Looks at stored marks (collapsed w/ stored), block marks at cursor offset\n * (collapsed w/o stored), or anchor-block marks (range).\n *\n * @param extractFn — Narrows the found mark and returns the desired value,\n * or `null` when the mark's attrs don't match expectations.\n */\nexport function getMarkAttrAtSelection<K extends keyof MarkAttrRegistry, V>(\n\tstate: EditorState,\n\tmarkTypeName: K,\n\textractFn: (mark: Mark & { readonly type: K; readonly attrs: MarkAttrRegistry[K] }) => V | null,\n): V | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (isCollapsed(sel) && state.storedMarks) {\n\t\tconst found = state.storedMarks.find((m) => (m.type as string) === markTypeName);\n\t\treturn found && isMarkOfType(found, markTypeName) ? extractFn(found) : null;\n\t}\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return null;\n\tconst marks: readonly Mark[] = getBlockMarksAtOffset(block, sel.anchor.offset);\n\tconst found = marks.find((m) => (m.type as string) === markTypeName);\n\treturn found && isMarkOfType(found, markTypeName) ? extractFn(found) : null;\n}\n\n/**\n * Returns `true` when the given attributed mark type is present at the\n * current selection (collapsed or range).\n */\nexport function isAttributedMarkActive<K extends keyof MarkAttrRegistry>(\n\tstate: EditorState,\n\tmarkTypeName: K,\n): boolean {\n\treturn getMarkAttrAtSelection(state, markTypeName, () => true as const) !== null;\n}\n\n/**\n * Finds the first mark of the given type within an offset range of a block.\n *\n * Returns the full mark (including attrs) so callers can pass it to\n * `removeMark` for correct undo inversion, or `undefined` when absent.\n */\nfunction findMarkInRange(\n\tblock: BlockNode,\n\ttypeName: MarkTypeName,\n\tfrom: number,\n\tto: number,\n): Mark | undefined {\n\tconst children: readonly (TextNode | InlineNode)[] = getInlineChildren(block);\n\tlet pos = 0;\n\n\tfor (const child of children) {\n\t\tif (!isTextNode(child)) {\n\t\t\tpos += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst nodeEnd: number = pos + child.text.length;\n\t\tif (nodeEnd > from && pos < to) {\n\t\t\tconst found: Mark | undefined = child.marks.find((m) => m.type === typeName);\n\t\t\tif (found) return found;\n\t\t}\n\t\tpos = nodeEnd;\n\t}\n\treturn undefined;\n}\n"],"names":["applyAttributedMark","state","mark","sel","selection","isNodeSelection","isGapCursor","typeName","markType","type","isCollapsed","anchorBlock","getBlock","anchor","blockId","newMarks","storedMarks","getBlockMarksAtOffset","offset","filter","m","transaction","setStoredMarks","setSelection","build","range","selectionRange","getBlockOrder","builder","forEachBlockInRange","from","to","block","existing","findMarkInRange","removeMark","addMark","removeAttributedMark","markTypeName","currentMarks","hasMark","getMarkAttrAtSelection","extractFn","found","find","isMarkOfType","isAttributedMarkActive","children","getInlineChildren","pos","child","isTextNode","nodeEnd","text","length","marks"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as t, e } from "./Document-Brtw2jL1.mjs";
|
|
2
2
|
import { i as n, c as o } from "./NavigationUtils-Cspt2ZrK.mjs";
|
|
3
3
|
import { c as r, d as i, e as f, a as l, i as c, f as s, b as u } from "./Selection-BRTab5hO.mjs";
|
|
4
|
-
import { f as a } from "./NodeResolver-
|
|
4
|
+
import { f as a } from "./NodeResolver-C2XYkeOi.mjs";
|
|
5
5
|
import { g as d } from "./Platform-S_xqIqcM.mjs";
|
|
6
6
|
import { b as g } from "./TypeBrands-C09nE2qO.mjs";
|
|
7
7
|
function h(t2, e2, n2) {
|
|
@@ -308,4 +308,4 @@ export {
|
|
|
308
308
|
T as r,
|
|
309
309
|
p as s
|
|
310
310
|
};
|
|
311
|
-
//# sourceMappingURL=CaretNavigation-
|
|
311
|
+
//# sourceMappingURL=CaretNavigation-7JYpIKbs.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CaretNavigation-qLfeL0So.mjs","sources":["../../src/state/SelectionTransactions.ts","../../src/view/DomPointUtils.ts","../../src/view/SelectionSync.ts","../../src/view/CaretNavigation.ts"],"sourcesContent":["/**\n * Shared transaction builders for cursor and selection movement.\n *\n * These helpers encapsulate the common pattern of building a transaction\n * that sets a new selection and clears storedMarks.\n */\n\nimport { findNodePath } from '../model/NodeResolver.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tcreateSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from './EditorState.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Builds a collapsed-cursor transaction that clears storedMarks. */\nexport function moveTx(state: EditorState, blockId: BlockId, offset: number): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createCollapsedSelection(blockId, offset))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a NodeSelection transaction that clears storedMarks. */\nexport function nodeSelTx(state: EditorState, targetId: BlockId): Transaction {\n\tconst path: BlockId[] = (findNodePath(state.doc, targetId) ?? []) as BlockId[];\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createNodeSelection(targetId, path))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a range-selection transaction that clears storedMarks. */\nexport function extendTx(\n\tstate: EditorState,\n\tanchorBlockId: BlockId,\n\tanchorOffset: number,\n\theadBlockId: BlockId,\n\theadOffset: number,\n): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(\n\t\t\tcreateSelection(\n\t\t\t\t{ blockId: anchorBlockId, offset: anchorOffset },\n\t\t\t\t{ blockId: headBlockId, offset: headOffset },\n\t\t\t),\n\t\t)\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n","/**\n * Utilities for resolving a DOM position from screen coordinates.\n *\n * Provides a fallback chain: `caretPositionFromPoint` → `caretRangeFromPoint`\n * → document fallback (when root is a ShadowRoot).\n */\n\n/** Result of resolving a DOM position from screen coordinates. */\nexport interface DomPoint {\n\treadonly node: Node;\n\treadonly offset: number;\n}\n\n/**\n * Resolves a DOM position from screen coordinates using the best available API.\n *\n * Tries `caretPositionFromPoint` first (standard), then `caretRangeFromPoint`\n * (WebKit fallback), and finally tries the ownerDocument when the root is\n * a ShadowRoot and the first two returned nothing.\n */\nexport function domPositionFromPoint(\n\troot: Document | ShadowRoot,\n\tx: number,\n\ty: number,\n\townerDoc?: Document,\n): DomPoint | null {\n\tlet domNode: Node | null = null;\n\tlet domOffset = 0;\n\n\t// Standard API\n\tif ('caretPositionFromPoint' in root) {\n\t\tconst cp = (root as Document).caretPositionFromPoint(x, y);\n\t\tif (cp) {\n\t\t\tdomNode = cp.offsetNode;\n\t\t\tdomOffset = cp.offset;\n\t\t}\n\t}\n\n\t// Fallback\n\tif (!domNode && 'caretRangeFromPoint' in root) {\n\t\tconst range = (root as Document).caretRangeFromPoint(x, y);\n\t\tif (range) {\n\t\t\tdomNode = range.startContainer;\n\t\t\tdomOffset = range.startOffset;\n\t\t}\n\t}\n\n\t// Also try on the document when the root is a ShadowRoot and returned nothing\n\tif (!domNode && ownerDoc && root !== ownerDoc) {\n\t\tif ('caretRangeFromPoint' in ownerDoc) {\n\t\t\tconst range = ownerDoc.caretRangeFromPoint(x, y);\n\t\t\tif (range) {\n\t\t\t\tdomNode = range.startContainer;\n\t\t\t\tdomOffset = range.startOffset;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!domNode) return null;\n\treturn { node: domNode, offset: domOffset };\n}\n","/**\n * Selection synchronization between editor state and DOM.\n */\n\nimport type { EditorSelection, Position, Selection } from '../model/Selection.js';\nimport {\n\tcreatePosition,\n\tcreateSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\n\ninterface DOMPosition {\n\tnode: Node;\n\toffset: number;\n}\n\n/** Gets the selection object, preferring shadow root's selection for Shadow DOM. */\nexport function getSelection(container: HTMLElement): globalThis.Selection | null {\n\tconst root = container.getRootNode();\n\tif (root instanceof ShadowRoot && 'getSelection' in root) {\n\t\treturn (root as ShadowRoot & { getSelection(): globalThis.Selection | null }).getSelection();\n\t}\n\treturn window.getSelection();\n}\n\n/** Syncs the editor state selection to the DOM. */\nexport function syncSelectionToDOM(container: HTMLElement, selection: EditorSelection): void {\n\tconst domSel = getSelection(container);\n\tif (!domSel) return;\n\n\t// GapCursor: clear browser selection (gap cursor has no DOM equivalent)\n\tif (isGapCursor(selection)) {\n\t\tdomSel.removeAllRanges();\n\t\treturn;\n\t}\n\n\t// NodeSelection: select the entire block element\n\tif (isNodeSelection(selection)) {\n\t\tconst blockEl = container.querySelector(`[data-block-id=\"${selection.nodeId}\"]`);\n\t\tif (!blockEl) return;\n\t\tconst parent = blockEl.parentNode;\n\t\tif (!parent) return;\n\t\tconst childIdx = childIndexOf(parent, blockEl);\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(parent, childIdx, parent, childIdx + 1);\n\t\t} catch {\n\t\t\t// Selection may fail if DOM is not yet rendered\n\t\t}\n\t\treturn;\n\t}\n\n\tconst anchorPos = statePositionToDOM(container, selection.anchor);\n\tconst headPos = statePositionToDOM(container, selection.head);\n\n\tif (!anchorPos || !headPos) return;\n\n\ttry {\n\t\tdomSel.setBaseAndExtent(anchorPos.node, anchorPos.offset, headPos.node, headPos.offset);\n\t} catch {\n\t\t// Selection may fail if DOM is not yet rendered\n\t}\n}\n\n/** Reads the current DOM selection and converts it to a state selection. */\nexport function readSelectionFromDOM(container: HTMLElement): Selection | null {\n\tconst domSel = getSelection(container);\n\tif (!domSel || domSel.rangeCount === 0) return null;\n\n\tconst anchorNode = domSel.anchorNode;\n\tconst focusNode = domSel.focusNode;\n\n\tif (!anchorNode || !focusNode) return null;\n\tif (!container.contains(anchorNode) || !container.contains(focusNode)) return null;\n\n\tconst anchor = domPositionToState(container, anchorNode, domSel.anchorOffset);\n\tconst head = domPositionToState(container, focusNode, domSel.focusOffset);\n\n\tif (!anchor || !head) return null;\n\n\treturn createSelection(anchor, head);\n}\n\n/** Converts a state position (blockId + offset) to a DOM position (node + offset). */\nfunction statePositionToDOM(container: HTMLElement, pos: Position): DOMPosition | null {\n\t// If path is available, navigate directly to the leaf block\n\tlet blockEl: Element | null = null;\n\tif (pos.path && pos.path.length > 0) {\n\t\tlet current: Element = container;\n\t\tfor (const id of pos.path) {\n\t\t\tconst found = current.querySelector(`:scope [data-block-id=\"${id}\"]`);\n\t\t\tif (!found) break;\n\t\t\tcurrent = found;\n\t\t}\n\t\tif (current !== container && current.getAttribute('data-block-id') === pos.blockId) {\n\t\t\tblockEl = current;\n\t\t}\n\t}\n\tblockEl ??= container.querySelector(`[data-block-id=\"${pos.blockId}\"]`);\n\tif (!blockEl) return null;\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle empty paragraphs with <br>\n\tif (contentEl.childNodes.length === 1 && contentEl.firstChild?.nodeName === 'BR') {\n\t\treturn { node: contentEl, offset: 0 };\n\t}\n\n\t// Walk through text nodes and inline elements to find the correct position\n\tlet remaining = pos.offset;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet current = walker.nextNode();\n\twhile (current) {\n\t\tif (current.nodeType === Node.TEXT_NODE) {\n\t\t\tconst len = current.textContent?.length ?? 0;\n\t\t\tif (remaining <= len) {\n\t\t\t\treturn { node: current, offset: remaining };\n\t\t\t}\n\t\t\tremaining -= len;\n\t\t} else if (\n\t\t\tcurrent instanceof HTMLElement &&\n\t\t\tcurrent.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\t// InlineNode element — width 1 in state offset space\n\t\t\tif (remaining === 0) {\n\t\t\t\t// Position before this inline element\n\t\t\t\tconst parent = current.parentNode;\n\t\t\t\tif (parent) {\n\t\t\t\t\tconst childIndex = childIndexOf(parent, current);\n\t\t\t\t\treturn { node: parent, offset: childIndex };\n\t\t\t\t}\n\t\t\t}\n\t\t\tremaining -= 1;\n\t\t}\n\t\tcurrent = walker.nextNode();\n\t}\n\n\t// Fallback: position at end of block\n\tconst lastChild = contentEl.lastChild;\n\tif (lastChild) {\n\t\tif (lastChild.nodeType === Node.TEXT_NODE) {\n\t\t\treturn { node: lastChild, offset: lastChild.textContent?.length ?? 0 };\n\t\t}\n\t\treturn { node: contentEl, offset: contentEl.childNodes.length };\n\t}\n\n\treturn { node: contentEl, offset: 0 };\n}\n\n/** Converts a DOM position to a state position, including nested path. */\nexport function domPositionToState(\n\tcontainer: HTMLElement,\n\tnode: Node,\n\tdomOffset: number,\n): Position | null {\n\t// Find the innermost block element\n\tconst blockEl = findBlockElement(container, node);\n\tif (!blockEl) return null;\n\n\tconst rawBlockId = blockEl.getAttribute('data-block-id');\n\tif (!rawBlockId) return null;\n\tconst bid = toBlockId(rawBlockId);\n\n\t// Build path by collecting all data-block-id ancestors from leaf to root\n\tconst path = buildBlockPath(container, blockEl);\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle clicks on the block or content element itself (e.g. empty paragraph with <br>)\n\tif (node === blockEl || node === contentEl) {\n\t\tconst targetEl: Element = node === contentEl ? contentEl : blockEl;\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\t\tfor (const child of Array.from(targetEl.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\t// If node is outside the contentDOM (e.g. in NodeView header), map to offset 0\n\tif (contentEl !== blockEl && !contentEl.contains(node)) {\n\t\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n\t}\n\n\t// Calculate offset by walking text nodes and inline elements\n\tlet offset = 0;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet walkerNode = walker.nextNode();\n\twhile (walkerNode) {\n\t\tif (walkerNode === node) {\n\t\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\treturn createPosition(bid, offset + domOffset, path.length > 1 ? path : undefined);\n\t\t\t}\n\t\t\t// Node is an inline element — return offset at its start\n\t\t\treturn createPosition(bid, offset, path.length > 1 ? path : undefined);\n\t\t}\n\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\toffset += walkerNode.textContent?.length ?? 0;\n\t\t} else if (\n\t\t\twalkerNode instanceof HTMLElement &&\n\t\t\twalkerNode.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\toffset += 1;\n\t\t}\n\t\twalkerNode = walker.nextNode();\n\t}\n\n\t// If the node is not a text node, try to find the offset from element context\n\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\n\t\tfor (const child of Array.from(node.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n}\n\n/** Builds an array of block IDs from root to leaf by walking up from a block element. */\nfunction buildBlockPath(container: HTMLElement, leafBlockEl: HTMLElement): BlockId[] {\n\tconst path: BlockId[] = [];\n\tlet current: HTMLElement | null = leafBlockEl;\n\n\twhile (current && current !== container) {\n\t\tif (current.hasAttribute('data-block-id')) {\n\t\t\tpath.unshift(toBlockId(current.getAttribute('data-block-id') ?? ''));\n\t\t}\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn path;\n}\n\n/** Finds the closest block element ancestor. */\nfunction findBlockElement(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n\n/** Checks if a node is inside a contentEditable=\"false\" inline element. */\nfunction isInsideInlineElement(node: Node, root: Element): boolean {\n\tlet parent: Node | null = node.parentNode;\n\twhile (parent && parent !== root) {\n\t\tif (parent instanceof HTMLElement && parent.getAttribute('contenteditable') === 'false') {\n\t\t\treturn true;\n\t\t}\n\t\tparent = parent.parentNode;\n\t}\n\treturn false;\n}\n\n/**\n * Creates a TreeWalker that visits text nodes and contentEditable=\"false\"\n * inline elements within a block, skipping mark wrappers and nested blocks.\n */\nfunction createInlineContentWalker(blockEl: Element): TreeWalker {\n\treturn document.createTreeWalker(blockEl, NodeFilter.SHOW_ALL, {\n\t\tacceptNode: (n: Node) => {\n\t\t\t// Skip cursor wrapper (ZWS for stored marks during IME composition)\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-cursor-wrapper')) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Skip anything inside an inline element (contentEditable=\"false\")\n\t\t\tif (isInsideInlineElement(n, blockEl)) return NodeFilter.FILTER_REJECT;\n\t\t\t// Skip nested block elements and their descendants\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-block-id') && n !== blockEl) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Accept text nodes\n\t\t\tif (n.nodeType === Node.TEXT_NODE) return NodeFilter.FILTER_ACCEPT;\n\t\t\t// Accept inline elements (contentEditable=\"false\")\n\t\t\tif (n instanceof HTMLElement && n.getAttribute('contenteditable') === 'false') {\n\t\t\t\treturn NodeFilter.FILTER_ACCEPT;\n\t\t\t}\n\t\t\t// Skip other elements (mark wrappers, decoration wrappers) — descend\n\t\t\treturn NodeFilter.FILTER_SKIP;\n\t\t},\n\t});\n}\n\n/** Returns the child index of a node within its parent. */\nfunction childIndexOf(parent: Node, child: Node): number {\n\tlet idx = 0;\n\tfor (const c of Array.from(parent.childNodes)) {\n\t\tif (c === child) return idx;\n\t\tidx++;\n\t}\n\treturn idx;\n}\n\n/**\n * Resolves the content root for inline content walking.\n * If the block element has a contentDOM (marked with data-content-dom),\n * returns it so the walker skips NodeView structural elements (headers, etc.).\n */\nfunction resolveContentRoot(blockEl: Element): Element {\n\tconst contentDOM: Element | null = blockEl.querySelector('[data-content-dom]');\n\treturn contentDOM ?? blockEl;\n}\n\n/** Counts the inline content width of a DOM node (text length + 1 per inline element). */\nfunction inlineContentWidth(node: Node): number {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent?.length ?? 0;\n\t}\n\tif (node instanceof HTMLElement && node.getAttribute('contenteditable') === 'false') {\n\t\treturn 1;\n\t}\n\t// Mark wrapper or other element — sum children\n\tlet width = 0;\n\tfor (const child of Array.from(node.childNodes)) {\n\t\twidth += inlineContentWidth(child);\n\t}\n\treturn width;\n}\n","/**\n * Caret navigation utilities for layout-aware cursor movement.\n *\n * Provides `endOfTextblock()` for detecting visual block boundaries,\n * `navigateAcrossBlocks()` for cross-block cursor transitions, and\n * `navigateVerticalWithGoalColumn()` for column-preserving vertical nav.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { domPositionFromPoint } from './DomPointUtils.js';\n\n/** Inset in pixels from block edge for vertical goal-column probing. */\nconst BLOCK_EDGE_INSET_PX = 2;\n\n/** Minimum vertical movement (pixels) for a Selection.modify probe to be considered a real move. */\nconst VERTICAL_MOVEMENT_THRESHOLD_PX = 1;\nimport { getTextDirection } from './Platform.js';\nimport { domPositionToState, getSelection } from './SelectionSync.js';\n\nexport type CaretDirection = 'left' | 'right' | 'up' | 'down';\n\n/** Result of resolving the adjacent block for vertical navigation. */\ninterface NavigationTarget {\n\treadonly targetId: BlockId;\n\treadonly isVoid: boolean;\n}\n\n/**\n * Checks whether the cursor is at the visual edge of its text block.\n *\n * For horizontal directions (left/right), uses a pure offset check.\n * For vertical directions (up/down), tries `Selection.modify` probing\n * to detect visual line boundaries, falling back to offset heuristic.\n *\n * An optional `caretRect` parameter avoids redundant `getBoundingClientRect`\n * calls when the caller already measured the caret position.\n */\nexport function endOfTextblock(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: CaretDirection,\n\tcaretRect?: DOMRect | null,\n): boolean {\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLength: number = getBlockLength(block);\n\n\t// Horizontal: direction-aware offset check\n\tconst blockEl: Element | null = container.querySelector(\n\t\t`[data-block-id=\"${sel.anchor.blockId}\"]`,\n\t);\n\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\n\tif (direction === 'left') return isRtl ? offset === blockLength : offset === 0;\n\tif (direction === 'right') return isRtl ? offset === 0 : offset === blockLength;\n\n\t// Vertical: quick offset check first\n\tif (direction === 'up' && offset === 0) return true;\n\tif (direction === 'down' && offset === blockLength) return true;\n\n\t// Vertical: try Selection.modify probing for multi-line blocks\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel || !domSel.modify) {\n\t\t// Fallback: offset heuristic (no Selection.modify in test environments)\n\t\treturn direction === 'up' ? offset === 0 : offset === blockLength;\n\t}\n\n\treturn probeVerticalBoundary(container, domSel, direction, caretRect);\n}\n\n/**\n * Creates a transaction to move the cursor to an adjacent block.\n *\n * Returns `null` if navigation is not possible (document boundary,\n * isolating boundary, or no adjacent block).\n */\nexport function navigateAcrossBlocks(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\t// Target is text → collapsed selection at start or end\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'right' || direction === 'down' ? 0 : getBlockLength(targetBlock);\n\n\treturn moveTx(state, target.targetId, targetOffset);\n}\n\n/**\n * Navigates vertically using a goal column for visual column preservation.\n *\n * Uses `caretPositionFromPoint` / `caretRangeFromPoint` to find the offset\n * in the target block that is closest to the given goalColumn X coordinate.\n * Falls back to start/end offset when goal column positioning is unavailable.\n */\nexport function navigateVerticalWithGoalColumn(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number | null,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\t// Void → NodeSelection (goalColumn irrelevant)\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\tconst blockLen: number = getBlockLength(targetBlock);\n\n\t// Try to resolve position using goalColumn\n\tif (goalColumn !== null) {\n\t\tconst position = resolveGoalColumnPosition(container, target.targetId, direction, goalColumn);\n\t\tif (position) return moveTx(state, position.blockId, position.offset);\n\t}\n\n\t// Fallback: start (down) or end (up)\n\tconst fallbackOffset: number = direction === 'down' ? 0 : blockLen;\n\treturn moveTx(state, target.targetId, fallbackOffset);\n}\n\n/**\n * Skips over an InlineNode atomically when arrowing left/right.\n *\n * Returns a transaction moving the cursor past the InlineNode,\n * or `null` if the adjacent content is not an InlineNode (or\n * the direction is vertical).\n */\nexport function skipInlineNode(state: EditorState, direction: CaretDirection): Transaction | null {\n\tif (direction === 'up' || direction === 'down') return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\tif (!isCollapsed(sel)) return null;\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\n\treturn direction === 'right'\n\t\t? skipInlineNodeRight(state, blockId, block, offset)\n\t\t: skipInlineNodeLeft(state, blockId, block, offset);\n}\n\nfunction skipInlineNodeRight(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tconst content = getContentAtOffset(block, offset);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset + 1);\n}\n\nfunction skipInlineNodeLeft(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tif (offset === 0) return null;\n\tconst content = getContentAtOffset(block, offset - 1);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset - 1);\n}\n\n/**\n * Navigates from a GapCursor position.\n *\n * - Arrow toward the adjacent void block → NodeSelection of that block.\n * - Arrow away from the void block → find next block: void → NodeSelection,\n * text → TextSelection, document boundary → no-op.\n */\nexport function navigateFromGapCursor(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (!isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst blockIdx: number = blockOrder.indexOf(sel.blockId);\n\tif (blockIdx < 0) return null;\n\n\tconst towardVoid: boolean =\n\t\t(sel.side === 'before' && (direction === 'right' || direction === 'down')) ||\n\t\t(sel.side === 'after' && (direction === 'left' || direction === 'up'));\n\n\tif (towardVoid) return nodeSelTx(state, sel.blockId);\n\n\t// Moving away from the void block — find the neighbor in the other direction\n\tconst awayIdx: number = sel.side === 'before' ? blockIdx - 1 : blockIdx + 1;\n\n\tif (awayIdx < 0 || awayIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[awayIdx];\n\tif (!targetId) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\t// Text block: place cursor at start or end\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'left' || direction === 'up' ? getBlockLength(targetBlock) : 0;\n\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n/** Returns the bounding rect of the current caret position, or null. */\nexport function getCaretRectFromSelection(domSel: globalThis.Selection): DOMRect | null {\n\tif (domSel.rangeCount === 0) return null;\n\tconst range: Range = domSel.getRangeAt(0);\n\tconst rects: DOMRectList = range.getClientRects();\n\tif (rects.length > 0) return rects[0] ?? null;\n\treturn range.getBoundingClientRect();\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves the adjacent block in the given direction for cross-block navigation.\n * Performs boundary checks, isolating checks, and void detection.\n */\nfunction resolveNavigationTarget(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): NavigationTarget | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst currentIdx: number = blockOrder.indexOf(sel.anchor.blockId);\n\tif (currentIdx < 0) return null;\n\n\tconst targetIdx: number =\n\t\tdirection === 'left' || direction === 'up' ? currentIdx - 1 : currentIdx + 1;\n\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\tif (!targetId) return null;\n\n\tif (!canCrossBlockBoundary(state, sel.anchor.blockId, targetId)) return null;\n\n\treturn { targetId, isVoid: isVoidBlock(state, targetId) };\n}\n\n/**\n * Resolves an editor position in the target block by using the goalColumn\n * X coordinate and `caretPositionFromPoint` / `caretRangeFromPoint`.\n *\n * Returns `null` when the point-to-caret API is unavailable, when the\n * resolved DOM node falls outside the target block, or when the resulting\n * position doesn't belong to the target block — triggering the fallback\n * (block-end for up, block-start for down) in `navigateVerticalWithGoalColumn`.\n */\nfunction resolveGoalColumnPosition(\n\tcontainer: HTMLElement,\n\ttargetId: BlockId,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number,\n): { blockId: BlockId; offset: number } | null {\n\tconst blockEl: Element | null = container.querySelector(`[data-block-id=\"${targetId}\"]`);\n\tif (!blockEl) return null;\n\n\tconst rect: DOMRect = blockEl.getBoundingClientRect();\n\t// Small inset to ensure we hit inside the line, not on the border\n\tconst y: number =\n\t\tdirection === 'down' ? rect.top + BLOCK_EDGE_INSET_PX : rect.bottom - BLOCK_EDGE_INSET_PX;\n\t// Clamp goalColumn within the block's horizontal bounds to avoid\n\t// caretPositionFromPoint returning unexpected results for out-of-bounds X.\n\tconst clampedX: number = Math.min(Math.max(goalColumn, rect.left), rect.right - 1);\n\n\tconst root: Document | ShadowRoot = container.getRootNode() as Document | ShadowRoot;\n\tconst domPoint = domPositionFromPoint(root, clampedX, y, container.ownerDocument);\n\tif (!domPoint) return null;\n\n\t// Verify the DOM node is inside the target block\n\tif (!blockEl.contains(domPoint.node)) return null;\n\n\tconst position = domPositionToState(container, domPoint.node, domPoint.offset);\n\tif (!position) return null;\n\n\t// Ensure the resolved position is in the target block\n\tif (position.blockId !== targetId) return null;\n\n\treturn position;\n}\n\n/**\n * Probes whether the cursor is at the top/bottom visual line of a block.\n *\n * Strategy:\n * 1. `Selection.modify('move', direction, 'line')` — moves one visual line.\n * 2. If the cursor left the block or didn't move, it's at the boundary.\n * 3. Cross-check with `getBoundingClientRect`: compare the caret rect before\n * and after the probe. If the vertical position didn't change, the probe\n * hit the block edge even though `Selection.modify` reported movement\n * (can happen with certain inline element layouts).\n *\n * Original DOM selection is always restored before returning.\n */\nfunction probeVerticalBoundary(\n\tcontainer: HTMLElement,\n\tdomSel: globalThis.Selection,\n\tdirection: 'up' | 'down',\n\tpreReadRect?: DOMRect | null,\n): boolean {\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOffset: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOffset: number = domSel.focusOffset;\n\n\t// Cannot determine position → safe default: not at boundary (let browser handle)\n\tif (!origAnchor || !origFocus) return false;\n\n\tconst origRect: DOMRect | null = preReadRect ?? getCaretRectFromSelection(domSel);\n\tconst dirStr: string = direction === 'up' ? 'backward' : 'forward';\n\n\ttry {\n\t\tdomSel.modify('move', dirStr, 'line');\n\n\t\tconst newFocus: Node | null = domSel.focusNode;\n\t\tif (!newFocus) return false;\n\n\t\t// Check if the new position is still within the same block\n\t\tconst origBlock: HTMLElement | null = findBlockAncestor(container, origFocus);\n\t\tconst newBlock: HTMLElement | null = findBlockAncestor(container, newFocus);\n\n\t\t// Moved to a different block → definitely at boundary\n\t\tif (origBlock !== newBlock) return true;\n\n\t\t// Selection didn't move at all → at boundary\n\t\tconst didNotMove: boolean =\n\t\t\tdomSel.anchorNode === origAnchor &&\n\t\t\tdomSel.anchorOffset === origAnchorOffset &&\n\t\t\tdomSel.focusNode === origFocus &&\n\t\t\tdomSel.focusOffset === origFocusOffset;\n\t\tif (didNotMove) return true;\n\n\t\t// Cross-check with getBoundingClientRect: if vertical position unchanged,\n\t\t// the probe was a no-op visually (edge case with inline elements)\n\t\tif (origRect) {\n\t\t\tconst newRect: DOMRect | null = getCaretRectFromSelection(domSel);\n\t\t\tif (newRect) {\n\t\t\t\tconst verticalDelta: number = Math.abs(newRect.top - origRect.top);\n\t\t\t\tif (verticalDelta < VERTICAL_MOVEMENT_THRESHOLD_PX) return true;\n\t\t\t}\n\t\t}\n\n\t\t// Stayed in same block and moved vertically → not at boundary\n\t\treturn false;\n\t} finally {\n\t\t// Restore original selection\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOffset, origFocus, origFocusOffset);\n\t\t} catch {\n\t\t\t// Selection restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/** Finds the closest ancestor element with `data-block-id`. */\nfunction findBlockAncestor(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n"],"names":["moveTx","state","blockId","offset","transaction","setSelection","createCollapsedSelection","setStoredMarks","storedMarks","build","nodeSelTx","targetId","path","findNodePath","doc","createNodeSelection","extendTx","anchorBlockId","anchorOffset","headBlockId","headOffset","createSelection","domPositionFromPoint","root","x","y","ownerDoc","domNode","domOffset","cp","caretPositionFromPoint","offsetNode","range","caretRangeFromPoint","startContainer","startOffset","node","getSelection","container","getRootNode","ShadowRoot","window","syncSelectionToDOM","selection","domSel","isGapCursor","removeAllRanges","isNodeSelection","blockEl","querySelector","nodeId","parent","parentNode","childIdx","childIndexOf","setBaseAndExtent","anchorPos","statePositionToDOM","anchor","headPos","head","readSelectionFromDOM","rangeCount","anchorNode","focusNode","contains","domPositionToState","focusOffset","pos","length","current","id","found","getAttribute","contentEl","resolveContentRoot","childNodes","firstChild","nodeName","remaining","walker","createInlineContentWalker","nextNode","nodeType","Node","TEXT_NODE","len","textContent","HTMLElement","lastChild","hasAttribute","rawBlockId","bid","toBlockId","leafBlockEl","unshift","parentElement","targetEl","childOffset","child","Array","from","inlineContentWidth","createPosition","walkerNode","ELEMENT_NODE","document","createTreeWalker","NodeFilter","SHOW_ALL","acceptNode","n","FILTER_REJECT","FILTER_ACCEPT","FILTER_SKIP","idx","c","width","endOfTextblock","direction","caretRect","sel","isCollapsed","block","getBlock","blockLength","getBlockLength","isRtl","getTextDirection","modify","preReadRect","origAnchor","origAnchorOffset","origFocus","origFocusOffset","origRect","getCaretRectFromSelection","dirStr","newFocus","findBlockAncestor","newRect","Math","abs","top","navigateAcrossBlocks","target","resolveNavigationTarget","isVoid","targetBlock","targetOffset","navigateVerticalWithGoalColumn","goalColumn","blockLen","position","rect","getBoundingClientRect","bottom","clampedX","min","max","left","right","domPoint","ownerDocument","fallbackOffset","skipInlineNode","content","getContentAtOffset","kind","navigateFromGapCursor","blockOrder","getBlockOrder","blockIdx","indexOf","side","awayIdx","isVoidBlock","getRangeAt","rects","getClientRects","currentIdx","targetIdx","canCrossBlockBoundary"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CaretNavigation-7JYpIKbs.mjs","sources":["../../src/state/SelectionTransactions.ts","../../src/view/DomPointUtils.ts","../../src/view/SelectionSync.ts","../../src/view/CaretNavigation.ts"],"sourcesContent":["/**\n * Shared transaction builders for cursor and selection movement.\n *\n * These helpers encapsulate the common pattern of building a transaction\n * that sets a new selection and clears storedMarks.\n */\n\nimport { findNodePath } from '../model/NodeResolver.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tcreateSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from './EditorState.js';\nimport type { Transaction } from './Transaction.js';\n\n/** Builds a collapsed-cursor transaction that clears storedMarks. */\nexport function moveTx(state: EditorState, blockId: BlockId, offset: number): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createCollapsedSelection(blockId, offset))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a NodeSelection transaction that clears storedMarks. */\nexport function nodeSelTx(state: EditorState, targetId: BlockId): Transaction {\n\tconst path: BlockId[] = (findNodePath(state.doc, targetId) ?? []) as BlockId[];\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(createNodeSelection(targetId, path))\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n\n/** Builds a range-selection transaction that clears storedMarks. */\nexport function extendTx(\n\tstate: EditorState,\n\tanchorBlockId: BlockId,\n\tanchorOffset: number,\n\theadBlockId: BlockId,\n\theadOffset: number,\n): Transaction {\n\treturn state\n\t\t.transaction('input')\n\t\t.setSelection(\n\t\t\tcreateSelection(\n\t\t\t\t{ blockId: anchorBlockId, offset: anchorOffset },\n\t\t\t\t{ blockId: headBlockId, offset: headOffset },\n\t\t\t),\n\t\t)\n\t\t.setStoredMarks(null, state.storedMarks)\n\t\t.build();\n}\n","/**\n * Utilities for resolving a DOM position from screen coordinates.\n *\n * Provides a fallback chain: `caretPositionFromPoint` → `caretRangeFromPoint`\n * → document fallback (when root is a ShadowRoot).\n */\n\n/** Result of resolving a DOM position from screen coordinates. */\nexport interface DomPoint {\n\treadonly node: Node;\n\treadonly offset: number;\n}\n\n/**\n * Resolves a DOM position from screen coordinates using the best available API.\n *\n * Tries `caretPositionFromPoint` first (standard), then `caretRangeFromPoint`\n * (WebKit fallback), and finally tries the ownerDocument when the root is\n * a ShadowRoot and the first two returned nothing.\n */\nexport function domPositionFromPoint(\n\troot: Document | ShadowRoot,\n\tx: number,\n\ty: number,\n\townerDoc?: Document,\n): DomPoint | null {\n\tlet domNode: Node | null = null;\n\tlet domOffset = 0;\n\n\t// Standard API\n\tif ('caretPositionFromPoint' in root) {\n\t\tconst cp = (root as Document).caretPositionFromPoint(x, y);\n\t\tif (cp) {\n\t\t\tdomNode = cp.offsetNode;\n\t\t\tdomOffset = cp.offset;\n\t\t}\n\t}\n\n\t// Fallback\n\tif (!domNode && 'caretRangeFromPoint' in root) {\n\t\tconst range = (root as Document).caretRangeFromPoint(x, y);\n\t\tif (range) {\n\t\t\tdomNode = range.startContainer;\n\t\t\tdomOffset = range.startOffset;\n\t\t}\n\t}\n\n\t// Also try on the document when the root is a ShadowRoot and returned nothing\n\tif (!domNode && ownerDoc && root !== ownerDoc) {\n\t\tif ('caretRangeFromPoint' in ownerDoc) {\n\t\t\tconst range = ownerDoc.caretRangeFromPoint(x, y);\n\t\t\tif (range) {\n\t\t\t\tdomNode = range.startContainer;\n\t\t\t\tdomOffset = range.startOffset;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!domNode) return null;\n\treturn { node: domNode, offset: domOffset };\n}\n","/**\n * Selection synchronization between editor state and DOM.\n */\n\nimport type { EditorSelection, Position, Selection } from '../model/Selection.js';\nimport {\n\tcreatePosition,\n\tcreateSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport { blockId as toBlockId } from '../model/TypeBrands.js';\n\ninterface DOMPosition {\n\tnode: Node;\n\toffset: number;\n}\n\n/** Gets the selection object, preferring shadow root's selection for Shadow DOM. */\nexport function getSelection(container: HTMLElement): globalThis.Selection | null {\n\tconst root = container.getRootNode();\n\tif (root instanceof ShadowRoot && 'getSelection' in root) {\n\t\treturn (root as ShadowRoot & { getSelection(): globalThis.Selection | null }).getSelection();\n\t}\n\treturn window.getSelection();\n}\n\n/** Syncs the editor state selection to the DOM. */\nexport function syncSelectionToDOM(container: HTMLElement, selection: EditorSelection): void {\n\tconst domSel = getSelection(container);\n\tif (!domSel) return;\n\n\t// GapCursor: clear browser selection (gap cursor has no DOM equivalent)\n\tif (isGapCursor(selection)) {\n\t\tdomSel.removeAllRanges();\n\t\treturn;\n\t}\n\n\t// NodeSelection: select the entire block element\n\tif (isNodeSelection(selection)) {\n\t\tconst blockEl = container.querySelector(`[data-block-id=\"${selection.nodeId}\"]`);\n\t\tif (!blockEl) return;\n\t\tconst parent = blockEl.parentNode;\n\t\tif (!parent) return;\n\t\tconst childIdx = childIndexOf(parent, blockEl);\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(parent, childIdx, parent, childIdx + 1);\n\t\t} catch {\n\t\t\t// Selection may fail if DOM is not yet rendered\n\t\t}\n\t\treturn;\n\t}\n\n\tconst anchorPos = statePositionToDOM(container, selection.anchor);\n\tconst headPos = statePositionToDOM(container, selection.head);\n\n\tif (!anchorPos || !headPos) return;\n\n\ttry {\n\t\tdomSel.setBaseAndExtent(anchorPos.node, anchorPos.offset, headPos.node, headPos.offset);\n\t} catch {\n\t\t// Selection may fail if DOM is not yet rendered\n\t}\n}\n\n/** Reads the current DOM selection and converts it to a state selection. */\nexport function readSelectionFromDOM(container: HTMLElement): Selection | null {\n\tconst domSel = getSelection(container);\n\tif (!domSel || domSel.rangeCount === 0) return null;\n\n\tconst anchorNode = domSel.anchorNode;\n\tconst focusNode = domSel.focusNode;\n\n\tif (!anchorNode || !focusNode) return null;\n\tif (!container.contains(anchorNode) || !container.contains(focusNode)) return null;\n\n\tconst anchor = domPositionToState(container, anchorNode, domSel.anchorOffset);\n\tconst head = domPositionToState(container, focusNode, domSel.focusOffset);\n\n\tif (!anchor || !head) return null;\n\n\treturn createSelection(anchor, head);\n}\n\n/** Converts a state position (blockId + offset) to a DOM position (node + offset). */\nfunction statePositionToDOM(container: HTMLElement, pos: Position): DOMPosition | null {\n\t// If path is available, navigate directly to the leaf block\n\tlet blockEl: Element | null = null;\n\tif (pos.path && pos.path.length > 0) {\n\t\tlet current: Element = container;\n\t\tfor (const id of pos.path) {\n\t\t\tconst found = current.querySelector(`:scope [data-block-id=\"${id}\"]`);\n\t\t\tif (!found) break;\n\t\t\tcurrent = found;\n\t\t}\n\t\tif (current !== container && current.getAttribute('data-block-id') === pos.blockId) {\n\t\t\tblockEl = current;\n\t\t}\n\t}\n\tblockEl ??= container.querySelector(`[data-block-id=\"${pos.blockId}\"]`);\n\tif (!blockEl) return null;\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle empty paragraphs with <br>\n\tif (contentEl.childNodes.length === 1 && contentEl.firstChild?.nodeName === 'BR') {\n\t\treturn { node: contentEl, offset: 0 };\n\t}\n\n\t// Walk through text nodes and inline elements to find the correct position\n\tlet remaining = pos.offset;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet current = walker.nextNode();\n\twhile (current) {\n\t\tif (current.nodeType === Node.TEXT_NODE) {\n\t\t\tconst len = current.textContent?.length ?? 0;\n\t\t\tif (remaining <= len) {\n\t\t\t\treturn { node: current, offset: remaining };\n\t\t\t}\n\t\t\tremaining -= len;\n\t\t} else if (\n\t\t\tcurrent instanceof HTMLElement &&\n\t\t\tcurrent.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\t// InlineNode element — width 1 in state offset space\n\t\t\tif (remaining === 0) {\n\t\t\t\t// Position before this inline element\n\t\t\t\tconst parent = current.parentNode;\n\t\t\t\tif (parent) {\n\t\t\t\t\tconst childIndex = childIndexOf(parent, current);\n\t\t\t\t\treturn { node: parent, offset: childIndex };\n\t\t\t\t}\n\t\t\t}\n\t\t\tremaining -= 1;\n\t\t}\n\t\tcurrent = walker.nextNode();\n\t}\n\n\t// Fallback: position at end of block\n\tconst lastChild = contentEl.lastChild;\n\tif (lastChild) {\n\t\tif (lastChild.nodeType === Node.TEXT_NODE) {\n\t\t\treturn { node: lastChild, offset: lastChild.textContent?.length ?? 0 };\n\t\t}\n\t\treturn { node: contentEl, offset: contentEl.childNodes.length };\n\t}\n\n\treturn { node: contentEl, offset: 0 };\n}\n\n/** Converts a DOM position to a state position, including nested path. */\nexport function domPositionToState(\n\tcontainer: HTMLElement,\n\tnode: Node,\n\tdomOffset: number,\n): Position | null {\n\t// Find the innermost block element\n\tconst blockEl = findBlockElement(container, node);\n\tif (!blockEl) return null;\n\n\tconst rawBlockId = blockEl.getAttribute('data-block-id');\n\tif (!rawBlockId) return null;\n\tconst bid = toBlockId(rawBlockId);\n\n\t// Build path by collecting all data-block-id ancestors from leaf to root\n\tconst path = buildBlockPath(container, blockEl);\n\n\t// Use contentDOM if available (NodeView blocks like code_block)\n\tconst contentEl: Element = resolveContentRoot(blockEl);\n\n\t// Handle clicks on the block or content element itself (e.g. empty paragraph with <br>)\n\tif (node === blockEl || node === contentEl) {\n\t\tconst targetEl: Element = node === contentEl ? contentEl : blockEl;\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\t\tfor (const child of Array.from(targetEl.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\t// If node is outside the contentDOM (e.g. in NodeView header), map to offset 0\n\tif (contentEl !== blockEl && !contentEl.contains(node)) {\n\t\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n\t}\n\n\t// Calculate offset by walking text nodes and inline elements\n\tlet offset = 0;\n\tconst walker = createInlineContentWalker(contentEl);\n\n\tlet walkerNode = walker.nextNode();\n\twhile (walkerNode) {\n\t\tif (walkerNode === node) {\n\t\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\t\treturn createPosition(bid, offset + domOffset, path.length > 1 ? path : undefined);\n\t\t\t}\n\t\t\t// Node is an inline element — return offset at its start\n\t\t\treturn createPosition(bid, offset, path.length > 1 ? path : undefined);\n\t\t}\n\t\tif (walkerNode.nodeType === Node.TEXT_NODE) {\n\t\t\toffset += walkerNode.textContent?.length ?? 0;\n\t\t} else if (\n\t\t\twalkerNode instanceof HTMLElement &&\n\t\t\twalkerNode.getAttribute('contenteditable') === 'false'\n\t\t) {\n\t\t\toffset += 1;\n\t\t}\n\t\twalkerNode = walker.nextNode();\n\t}\n\n\t// If the node is not a text node, try to find the offset from element context\n\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\tlet childOffset = 0;\n\t\tlet childIdx = 0;\n\n\t\tfor (const child of Array.from(node.childNodes)) {\n\t\t\tif (childIdx >= domOffset) break;\n\t\t\tchildOffset += inlineContentWidth(child);\n\t\t\tchildIdx++;\n\t\t}\n\n\t\treturn createPosition(bid, childOffset, path.length > 1 ? path : undefined);\n\t}\n\n\treturn createPosition(bid, 0, path.length > 1 ? path : undefined);\n}\n\n/** Builds an array of block IDs from root to leaf by walking up from a block element. */\nfunction buildBlockPath(container: HTMLElement, leafBlockEl: HTMLElement): BlockId[] {\n\tconst path: BlockId[] = [];\n\tlet current: HTMLElement | null = leafBlockEl;\n\n\twhile (current && current !== container) {\n\t\tif (current.hasAttribute('data-block-id')) {\n\t\t\tpath.unshift(toBlockId(current.getAttribute('data-block-id') ?? ''));\n\t\t}\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn path;\n}\n\n/** Finds the closest block element ancestor. */\nfunction findBlockElement(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n\n/** Checks if a node is inside a contentEditable=\"false\" inline element. */\nfunction isInsideInlineElement(node: Node, root: Element): boolean {\n\tlet parent: Node | null = node.parentNode;\n\twhile (parent && parent !== root) {\n\t\tif (parent instanceof HTMLElement && parent.getAttribute('contenteditable') === 'false') {\n\t\t\treturn true;\n\t\t}\n\t\tparent = parent.parentNode;\n\t}\n\treturn false;\n}\n\n/**\n * Creates a TreeWalker that visits text nodes and contentEditable=\"false\"\n * inline elements within a block, skipping mark wrappers and nested blocks.\n */\nfunction createInlineContentWalker(blockEl: Element): TreeWalker {\n\treturn document.createTreeWalker(blockEl, NodeFilter.SHOW_ALL, {\n\t\tacceptNode: (n: Node) => {\n\t\t\t// Skip cursor wrapper (ZWS for stored marks during IME composition)\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-cursor-wrapper')) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Skip anything inside an inline element (contentEditable=\"false\")\n\t\t\tif (isInsideInlineElement(n, blockEl)) return NodeFilter.FILTER_REJECT;\n\t\t\t// Skip nested block elements and their descendants\n\t\t\tif (n instanceof HTMLElement && n.hasAttribute('data-block-id') && n !== blockEl) {\n\t\t\t\treturn NodeFilter.FILTER_REJECT;\n\t\t\t}\n\t\t\t// Accept text nodes\n\t\t\tif (n.nodeType === Node.TEXT_NODE) return NodeFilter.FILTER_ACCEPT;\n\t\t\t// Accept inline elements (contentEditable=\"false\")\n\t\t\tif (n instanceof HTMLElement && n.getAttribute('contenteditable') === 'false') {\n\t\t\t\treturn NodeFilter.FILTER_ACCEPT;\n\t\t\t}\n\t\t\t// Skip other elements (mark wrappers, decoration wrappers) — descend\n\t\t\treturn NodeFilter.FILTER_SKIP;\n\t\t},\n\t});\n}\n\n/** Returns the child index of a node within its parent. */\nfunction childIndexOf(parent: Node, child: Node): number {\n\tlet idx = 0;\n\tfor (const c of Array.from(parent.childNodes)) {\n\t\tif (c === child) return idx;\n\t\tidx++;\n\t}\n\treturn idx;\n}\n\n/**\n * Resolves the content root for inline content walking.\n * If the block element has a contentDOM (marked with data-content-dom),\n * returns it so the walker skips NodeView structural elements (headers, etc.).\n */\nfunction resolveContentRoot(blockEl: Element): Element {\n\tconst contentDOM: Element | null = blockEl.querySelector('[data-content-dom]');\n\treturn contentDOM ?? blockEl;\n}\n\n/** Counts the inline content width of a DOM node (text length + 1 per inline element). */\nfunction inlineContentWidth(node: Node): number {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn node.textContent?.length ?? 0;\n\t}\n\tif (node instanceof HTMLElement && node.getAttribute('contenteditable') === 'false') {\n\t\treturn 1;\n\t}\n\t// Mark wrapper or other element — sum children\n\tlet width = 0;\n\tfor (const child of Array.from(node.childNodes)) {\n\t\twidth += inlineContentWidth(child);\n\t}\n\treturn width;\n}\n","/**\n * Caret navigation utilities for layout-aware cursor movement.\n *\n * Provides `endOfTextblock()` for detecting visual block boundaries,\n * `navigateAcrossBlocks()` for cross-block cursor transitions, and\n * `navigateVerticalWithGoalColumn()` for column-preserving vertical nav.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\nimport { canCrossBlockBoundary, isVoidBlock } from '../model/NavigationUtils.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../model/Selection.js';\nimport type { BlockId } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport { moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { domPositionFromPoint } from './DomPointUtils.js';\n\n/** Inset in pixels from block edge for vertical goal-column probing. */\nconst BLOCK_EDGE_INSET_PX = 2;\n\n/** Minimum vertical movement (pixels) for a Selection.modify probe to be considered a real move. */\nconst VERTICAL_MOVEMENT_THRESHOLD_PX = 1;\nimport { getTextDirection } from './Platform.js';\nimport { domPositionToState, getSelection } from './SelectionSync.js';\n\nexport type CaretDirection = 'left' | 'right' | 'up' | 'down';\n\n/** Result of resolving the adjacent block for vertical navigation. */\ninterface NavigationTarget {\n\treadonly targetId: BlockId;\n\treadonly isVoid: boolean;\n}\n\n/**\n * Checks whether the cursor is at the visual edge of its text block.\n *\n * For horizontal directions (left/right), uses a pure offset check.\n * For vertical directions (up/down), tries `Selection.modify` probing\n * to detect visual line boundaries, falling back to offset heuristic.\n *\n * An optional `caretRect` parameter avoids redundant `getBoundingClientRect`\n * calls when the caller already measured the caret position.\n */\nexport function endOfTextblock(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: CaretDirection,\n\tcaretRect?: DOMRect | null,\n): boolean {\n\tconst sel = state.selection;\n\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tconst offset: number = sel.anchor.offset;\n\tconst blockLength: number = getBlockLength(block);\n\n\t// Horizontal: direction-aware offset check\n\tconst blockEl: Element | null = container.querySelector(\n\t\t`[data-block-id=\"${sel.anchor.blockId}\"]`,\n\t);\n\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\n\tif (direction === 'left') return isRtl ? offset === blockLength : offset === 0;\n\tif (direction === 'right') return isRtl ? offset === 0 : offset === blockLength;\n\n\t// Vertical: quick offset check first\n\tif (direction === 'up' && offset === 0) return true;\n\tif (direction === 'down' && offset === blockLength) return true;\n\n\t// Vertical: try Selection.modify probing for multi-line blocks\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel || !domSel.modify) {\n\t\t// Fallback: offset heuristic (no Selection.modify in test environments)\n\t\treturn direction === 'up' ? offset === 0 : offset === blockLength;\n\t}\n\n\treturn probeVerticalBoundary(container, domSel, direction, caretRect);\n}\n\n/**\n * Creates a transaction to move the cursor to an adjacent block.\n *\n * Returns `null` if navigation is not possible (document boundary,\n * isolating boundary, or no adjacent block).\n */\nexport function navigateAcrossBlocks(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\t// Target is text → collapsed selection at start or end\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'right' || direction === 'down' ? 0 : getBlockLength(targetBlock);\n\n\treturn moveTx(state, target.targetId, targetOffset);\n}\n\n/**\n * Navigates vertically using a goal column for visual column preservation.\n *\n * Uses `caretPositionFromPoint` / `caretRangeFromPoint` to find the offset\n * in the target block that is closest to the given goalColumn X coordinate.\n * Falls back to start/end offset when goal column positioning is unavailable.\n */\nexport function navigateVerticalWithGoalColumn(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number | null,\n): Transaction | null {\n\tconst target: NavigationTarget | null = resolveNavigationTarget(state, direction);\n\tif (!target) return null;\n\n\t// Void → NodeSelection (goalColumn irrelevant)\n\tif (target.isVoid) return nodeSelTx(state, target.targetId);\n\n\tconst targetBlock = state.getBlock(target.targetId);\n\tif (!targetBlock) return null;\n\tconst blockLen: number = getBlockLength(targetBlock);\n\n\t// Try to resolve position using goalColumn\n\tif (goalColumn !== null) {\n\t\tconst position = resolveGoalColumnPosition(container, target.targetId, direction, goalColumn);\n\t\tif (position) return moveTx(state, position.blockId, position.offset);\n\t}\n\n\t// Fallback: start (down) or end (up)\n\tconst fallbackOffset: number = direction === 'down' ? 0 : blockLen;\n\treturn moveTx(state, target.targetId, fallbackOffset);\n}\n\n/**\n * Skips over an InlineNode atomically when arrowing left/right.\n *\n * Returns a transaction moving the cursor past the InlineNode,\n * or `null` if the adjacent content is not an InlineNode (or\n * the direction is vertical).\n */\nexport function skipInlineNode(state: EditorState, direction: CaretDirection): Transaction | null {\n\tif (direction === 'up' || direction === 'down') return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\tif (!isCollapsed(sel)) return null;\n\n\tconst blockId: BlockId = sel.anchor.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst offset: number = sel.anchor.offset;\n\n\treturn direction === 'right'\n\t\t? skipInlineNodeRight(state, blockId, block, offset)\n\t\t: skipInlineNodeLeft(state, blockId, block, offset);\n}\n\nfunction skipInlineNodeRight(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tconst content = getContentAtOffset(block, offset);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset + 1);\n}\n\nfunction skipInlineNodeLeft(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tblock: BlockNode,\n\toffset: number,\n): Transaction | null {\n\tif (offset === 0) return null;\n\tconst content = getContentAtOffset(block, offset - 1);\n\tif (!content || content.kind !== 'inline') return null;\n\treturn moveTx(state, blockId, offset - 1);\n}\n\n/**\n * Navigates from a GapCursor position.\n *\n * - Arrow toward the adjacent void block → NodeSelection of that block.\n * - Arrow away from the void block → find next block: void → NodeSelection,\n * text → TextSelection, document boundary → no-op.\n */\nexport function navigateFromGapCursor(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (!isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst blockIdx: number = blockOrder.indexOf(sel.blockId);\n\tif (blockIdx < 0) return null;\n\n\tconst towardVoid: boolean =\n\t\t(sel.side === 'before' && (direction === 'right' || direction === 'down')) ||\n\t\t(sel.side === 'after' && (direction === 'left' || direction === 'up'));\n\n\tif (towardVoid) return nodeSelTx(state, sel.blockId);\n\n\t// Moving away from the void block — find the neighbor in the other direction\n\tconst awayIdx: number = sel.side === 'before' ? blockIdx - 1 : blockIdx + 1;\n\n\tif (awayIdx < 0 || awayIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[awayIdx];\n\tif (!targetId) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\t// Text block: place cursor at start or end\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number =\n\t\tdirection === 'left' || direction === 'up' ? getBlockLength(targetBlock) : 0;\n\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n/** Returns the bounding rect of the current caret position, or null. */\nexport function getCaretRectFromSelection(domSel: globalThis.Selection): DOMRect | null {\n\tif (domSel.rangeCount === 0) return null;\n\tconst range: Range = domSel.getRangeAt(0);\n\tconst rects: DOMRectList = range.getClientRects();\n\tif (rects.length > 0) return rects[0] ?? null;\n\treturn range.getBoundingClientRect();\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves the adjacent block in the given direction for cross-block navigation.\n * Performs boundary checks, isolating checks, and void detection.\n */\nfunction resolveNavigationTarget(\n\tstate: EditorState,\n\tdirection: CaretDirection,\n): NavigationTarget | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst currentIdx: number = blockOrder.indexOf(sel.anchor.blockId);\n\tif (currentIdx < 0) return null;\n\n\tconst targetIdx: number =\n\t\tdirection === 'left' || direction === 'up' ? currentIdx - 1 : currentIdx + 1;\n\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\tif (!targetId) return null;\n\n\tif (!canCrossBlockBoundary(state, sel.anchor.blockId, targetId)) return null;\n\n\treturn { targetId, isVoid: isVoidBlock(state, targetId) };\n}\n\n/**\n * Resolves an editor position in the target block by using the goalColumn\n * X coordinate and `caretPositionFromPoint` / `caretRangeFromPoint`.\n *\n * Returns `null` when the point-to-caret API is unavailable, when the\n * resolved DOM node falls outside the target block, or when the resulting\n * position doesn't belong to the target block — triggering the fallback\n * (block-end for up, block-start for down) in `navigateVerticalWithGoalColumn`.\n */\nfunction resolveGoalColumnPosition(\n\tcontainer: HTMLElement,\n\ttargetId: BlockId,\n\tdirection: 'up' | 'down',\n\tgoalColumn: number,\n): { blockId: BlockId; offset: number } | null {\n\tconst blockEl: Element | null = container.querySelector(`[data-block-id=\"${targetId}\"]`);\n\tif (!blockEl) return null;\n\n\tconst rect: DOMRect = blockEl.getBoundingClientRect();\n\t// Small inset to ensure we hit inside the line, not on the border\n\tconst y: number =\n\t\tdirection === 'down' ? rect.top + BLOCK_EDGE_INSET_PX : rect.bottom - BLOCK_EDGE_INSET_PX;\n\t// Clamp goalColumn within the block's horizontal bounds to avoid\n\t// caretPositionFromPoint returning unexpected results for out-of-bounds X.\n\tconst clampedX: number = Math.min(Math.max(goalColumn, rect.left), rect.right - 1);\n\n\tconst root: Document | ShadowRoot = container.getRootNode() as Document | ShadowRoot;\n\tconst domPoint = domPositionFromPoint(root, clampedX, y, container.ownerDocument);\n\tif (!domPoint) return null;\n\n\t// Verify the DOM node is inside the target block\n\tif (!blockEl.contains(domPoint.node)) return null;\n\n\tconst position = domPositionToState(container, domPoint.node, domPoint.offset);\n\tif (!position) return null;\n\n\t// Ensure the resolved position is in the target block\n\tif (position.blockId !== targetId) return null;\n\n\treturn position;\n}\n\n/**\n * Probes whether the cursor is at the top/bottom visual line of a block.\n *\n * Strategy:\n * 1. `Selection.modify('move', direction, 'line')` — moves one visual line.\n * 2. If the cursor left the block or didn't move, it's at the boundary.\n * 3. Cross-check with `getBoundingClientRect`: compare the caret rect before\n * and after the probe. If the vertical position didn't change, the probe\n * hit the block edge even though `Selection.modify` reported movement\n * (can happen with certain inline element layouts).\n *\n * Original DOM selection is always restored before returning.\n */\nfunction probeVerticalBoundary(\n\tcontainer: HTMLElement,\n\tdomSel: globalThis.Selection,\n\tdirection: 'up' | 'down',\n\tpreReadRect?: DOMRect | null,\n): boolean {\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOffset: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOffset: number = domSel.focusOffset;\n\n\t// Cannot determine position → safe default: not at boundary (let browser handle)\n\tif (!origAnchor || !origFocus) return false;\n\n\tconst origRect: DOMRect | null = preReadRect ?? getCaretRectFromSelection(domSel);\n\tconst dirStr: string = direction === 'up' ? 'backward' : 'forward';\n\n\ttry {\n\t\tdomSel.modify('move', dirStr, 'line');\n\n\t\tconst newFocus: Node | null = domSel.focusNode;\n\t\tif (!newFocus) return false;\n\n\t\t// Check if the new position is still within the same block\n\t\tconst origBlock: HTMLElement | null = findBlockAncestor(container, origFocus);\n\t\tconst newBlock: HTMLElement | null = findBlockAncestor(container, newFocus);\n\n\t\t// Moved to a different block → definitely at boundary\n\t\tif (origBlock !== newBlock) return true;\n\n\t\t// Selection didn't move at all → at boundary\n\t\tconst didNotMove: boolean =\n\t\t\tdomSel.anchorNode === origAnchor &&\n\t\t\tdomSel.anchorOffset === origAnchorOffset &&\n\t\t\tdomSel.focusNode === origFocus &&\n\t\t\tdomSel.focusOffset === origFocusOffset;\n\t\tif (didNotMove) return true;\n\n\t\t// Cross-check with getBoundingClientRect: if vertical position unchanged,\n\t\t// the probe was a no-op visually (edge case with inline elements)\n\t\tif (origRect) {\n\t\t\tconst newRect: DOMRect | null = getCaretRectFromSelection(domSel);\n\t\t\tif (newRect) {\n\t\t\t\tconst verticalDelta: number = Math.abs(newRect.top - origRect.top);\n\t\t\t\tif (verticalDelta < VERTICAL_MOVEMENT_THRESHOLD_PX) return true;\n\t\t\t}\n\t\t}\n\n\t\t// Stayed in same block and moved vertically → not at boundary\n\t\treturn false;\n\t} finally {\n\t\t// Restore original selection\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOffset, origFocus, origFocusOffset);\n\t\t} catch {\n\t\t\t// Selection restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/** Finds the closest ancestor element with `data-block-id`. */\nfunction findBlockAncestor(container: HTMLElement, node: Node): HTMLElement | null {\n\tlet current: Node | null = node;\n\twhile (current && current !== container) {\n\t\tif (current instanceof HTMLElement && current.hasAttribute('data-block-id')) {\n\t\t\treturn current;\n\t\t}\n\t\tcurrent = current.parentNode;\n\t}\n\treturn null;\n}\n"],"names":["moveTx","state","blockId","offset","transaction","setSelection","createCollapsedSelection","setStoredMarks","storedMarks","build","nodeSelTx","targetId","path","findNodePath","doc","createNodeSelection","extendTx","anchorBlockId","anchorOffset","headBlockId","headOffset","createSelection","domPositionFromPoint","root","x","y","ownerDoc","domNode","domOffset","cp","caretPositionFromPoint","offsetNode","range","caretRangeFromPoint","startContainer","startOffset","node","getSelection","container","getRootNode","ShadowRoot","window","syncSelectionToDOM","selection","domSel","isGapCursor","removeAllRanges","isNodeSelection","blockEl","querySelector","nodeId","parent","parentNode","childIdx","childIndexOf","setBaseAndExtent","anchorPos","statePositionToDOM","anchor","headPos","head","readSelectionFromDOM","rangeCount","anchorNode","focusNode","contains","domPositionToState","focusOffset","pos","length","current","id","found","getAttribute","contentEl","resolveContentRoot","childNodes","firstChild","nodeName","remaining","walker","createInlineContentWalker","nextNode","nodeType","Node","TEXT_NODE","len","textContent","HTMLElement","lastChild","hasAttribute","rawBlockId","bid","toBlockId","leafBlockEl","unshift","parentElement","targetEl","childOffset","child","Array","from","inlineContentWidth","createPosition","walkerNode","ELEMENT_NODE","document","createTreeWalker","NodeFilter","SHOW_ALL","acceptNode","n","FILTER_REJECT","FILTER_ACCEPT","FILTER_SKIP","idx","c","width","endOfTextblock","direction","caretRect","sel","isCollapsed","block","getBlock","blockLength","getBlockLength","isRtl","getTextDirection","modify","preReadRect","origAnchor","origAnchorOffset","origFocus","origFocusOffset","origRect","getCaretRectFromSelection","dirStr","newFocus","findBlockAncestor","newRect","Math","abs","top","navigateAcrossBlocks","target","resolveNavigationTarget","isVoid","targetBlock","targetOffset","navigateVerticalWithGoalColumn","goalColumn","blockLen","position","rect","getBoundingClientRect","bottom","clampedX","min","max","left","right","domPoint","ownerDocument","fallbackOffset","skipInlineNode","content","getContentAtOffset","kind","navigateFromGapCursor","blockOrder","getBlockOrder","blockIdx","indexOf","side","awayIdx","isVoidBlock","getRangeAt","rects","getClientRects","currentIdx","targetIdx","canCrossBlockBoundary"],"mappings":"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"}
|
|
@@ -1,80 +1,81 @@
|
|
|
1
|
-
import { a as e,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { a as e, s as a } from "./StyleRuntime-CP4NCwKT.mjs";
|
|
2
|
+
import { a as r, n as t } from "./ToolbarKeyboardNav-DhpL8zNV.mjs";
|
|
3
|
+
const f = /* @__PURE__ */ new Map([["#000000", "Black"], ["#434343", "Dark Gray 4"], ["#666666", "Dark Gray 3"], ["#999999", "Dark Gray 2"], ["#b7b7b7", "Dark Gray 1"], ["#cccccc", "Light Gray 3"], ["#d9d9d9", "Light Gray 2"], ["#efefef", "Light Gray 1"], ["#f3f3f3", "Near White"], ["#ffffff", "White"], ["#980000", "Dark Red Berry"], ["#ff0000", "Red"], ["#ff9900", "Orange"], ["#ffff00", "Yellow"], ["#00ff00", "Green"], ["#00ffff", "Cyan"], ["#4a86e8", "Cornflower Blue"], ["#0000ff", "Blue"], ["#9900ff", "Purple"], ["#ff00ff", "Magenta"], ["#e6b8af", "Light Red Berry 3"], ["#f4cccc", "Light Red 3"], ["#fce5cd", "Light Orange 3"], ["#fff2cc", "Light Yellow 3"], ["#d9ead3", "Light Green 3"], ["#d0e0e3", "Light Cyan 3"], ["#c9daf8", "Light Cornflower Blue 3"], ["#cfe2f3", "Light Blue 3"], ["#d9d2e9", "Light Purple 3"], ["#ead1dc", "Light Magenta 3"], ["#dd7e6b", "Light Red Berry 2"], ["#ea9999", "Light Red 2"], ["#f9cb9c", "Light Orange 2"], ["#ffe599", "Light Yellow 2"], ["#b6d7a8", "Light Green 2"], ["#a2c4c9", "Light Cyan 2"], ["#a4c2f4", "Light Cornflower Blue 2"], ["#9fc5e8", "Light Blue 2"], ["#b4a7d6", "Light Purple 2"], ["#d5a6bd", "Light Magenta 2"], ["#cc4125", "Light Red Berry 1"], ["#e06666", "Light Red 1"], ["#f6b26b", "Light Orange 1"], ["#ffd966", "Light Yellow 1"], ["#93c47d", "Light Green 1"], ["#76a5af", "Light Cyan 1"], ["#6d9eeb", "Light Cornflower Blue 1"], ["#6fa8dc", "Light Blue 1"], ["#8e7cc3", "Light Purple 1"], ["#c27ba0", "Light Magenta 1"], ["#a61c00", "Dark Red Berry 1"], ["#cc0000", "Dark Red 1"], ["#e69138", "Dark Orange 1"], ["#f1c232", "Dark Yellow 1"], ["#6aa84f", "Dark Green 1"], ["#45818e", "Dark Cyan 1"], ["#3c78d8", "Dark Cornflower Blue 1"], ["#3d85c6", "Dark Blue 1"], ["#674ea7", "Dark Purple 1"], ["#a64d79", "Dark Magenta 1"], ["#85200c", "Dark Red Berry 2"], ["#990000", "Dark Red 2"], ["#b45f06", "Dark Orange 2"], ["#bf9000", "Dark Yellow 2"], ["#38761d", "Dark Green 2"], ["#134f5c", "Dark Cyan 2"], ["#1155cc", "Dark Cornflower Blue 2"], ["#0b5394", "Dark Blue 2"], ["#351c75", "Dark Purple 2"], ["#741b47", "Dark Magenta 2"], ["#fff176", "Bright Yellow"], ["#aed581", "Light Green"], ["#4dd0e1", "Bright Cyan"], ["#64b5f6", "Sky Blue"], ["#ce93d8", "Lavender"], ["#f48fb1", "Pink"], ["#ffab91", "Peach"], ["#ff8a65", "Coral"], ["#e6ee9c", "Lime"], ["#80cbc4", "Teal"], ["#fff9c4", "Pale Yellow"], ["#dcedc8", "Pale Green"], ["#e0f7fa", "Pale Cyan"], ["#e3f2fd", "Pale Blue"], ["#f3e5f5", "Pale Purple"], ["#fce4ec", "Pale Pink"], ["#fff3e0", "Pale Orange"], ["#fbe9e7", "Pale Red"], ["#f9fbe7", "Pale Lime"], ["#e0f2f1", "Pale Teal"], ["#fff59d", "Soft Yellow"], ["#c5e1a5", "Soft Green"], ["#80deea", "Soft Cyan"], ["#90caf9", "Soft Blue"], ["#e1bee7", "Soft Purple"], ["#f8bbd0", "Soft Pink"], ["#ffcc80", "Soft Orange"], ["#a5d6a7", "Mint"], ["#ffee58", "Vivid Yellow"], ["#9ccc65", "Vivid Green"], ["#26c6da", "Vivid Cyan"], ["#42a5f5", "Vivid Blue"], ["#ab47bc", "Vivid Purple"], ["#ec407a", "Vivid Pink"], ["#ffa726", "Vivid Orange"], ["#ff7043", "Red Orange"], ["#d4e157", "Vivid Lime"], ["#66bb6a", "Emerald"], ["#fafafa", "Almost White"], ["#f5f5f5", "Lightest Gray"], ["#eeeeee", "Very Light Gray"], ["#e0e0e0", "Silver"], ["#bdbdbd", "Medium Gray"], ["#e8eaf6", "Lavender Gray"], ["#efebe9", "Warm Gray"], ["#eceff1", "Cool Gray"]]);
|
|
4
|
+
function n(e2) {
|
|
5
|
+
return f.get(e2.toLowerCase()) || (function(e3) {
|
|
6
|
+
const a2 = i(e3), r2 = Number.parseInt(a2.slice(1, 3), 16) / 255, t2 = Number.parseInt(a2.slice(3, 5), 16) / 255, f2 = Number.parseInt(a2.slice(5, 7), 16) / 255, n2 = Math.max(r2, t2, f2), o2 = Math.min(r2, t2, f2), l2 = (n2 + o2) / 2, d2 = n2 - o2;
|
|
7
|
+
if (d2 < 0.05) return l2 < 0.15 ? "Black" : l2 < 0.35 ? "Dark Gray" : l2 < 0.65 ? "Gray" : l2 < 0.85 ? "Light Gray" : "Near White";
|
|
8
|
+
let s2 = 0;
|
|
9
|
+
s2 = n2 === r2 ? 60 * ((t2 - f2) / d2 + (t2 < f2 ? 6 : 0)) : n2 === t2 ? 60 * ((f2 - r2) / d2 + 2) : 60 * ((r2 - t2) / d2 + 4);
|
|
9
10
|
let g = "Red";
|
|
10
|
-
for (const [e4, a3] of
|
|
11
|
+
for (const [e4, a3] of c) if (s2 < e4) {
|
|
11
12
|
g = a3;
|
|
12
13
|
break;
|
|
13
14
|
}
|
|
14
|
-
return
|
|
15
|
+
return l2 < 0.25 ? `Very Dark ${g}` : l2 < 0.4 ? `Dark ${g}` : l2 > 0.8 ? `Very Light ${g}` : l2 > 0.65 ? `Light ${g}` : g;
|
|
15
16
|
})(e2);
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
+
function o(e2) {
|
|
18
19
|
const a2 = (function(e3) {
|
|
19
|
-
const a3 =
|
|
20
|
-
return 0.2126 *
|
|
20
|
+
const a3 = i(e3);
|
|
21
|
+
return 0.2126 * l(Number.parseInt(a3.slice(1, 3), 16) / 255) + 0.7152 * l(Number.parseInt(a3.slice(3, 5), 16) / 255) + 0.0722 * l(Number.parseInt(a3.slice(5, 7), 16) / 255);
|
|
21
22
|
})(e2);
|
|
22
23
|
return a2 > 0.3;
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
+
function i(e2) {
|
|
25
26
|
const a2 = e2.toLowerCase();
|
|
26
27
|
return 4 === a2.length ? `#${a2[1]}${a2[1]}${a2[2]}${a2[2]}${a2[3]}${a2[3]}` : a2;
|
|
27
28
|
}
|
|
28
|
-
function
|
|
29
|
+
function l(e2) {
|
|
29
30
|
return e2 <= 0.03928 ? e2 / 12.92 : ((e2 + 0.055) / 1.055) ** 2.4;
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
-
function
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
let
|
|
37
|
-
for (let
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
for (let
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}),
|
|
32
|
+
const c = [[15, "Red"], [45, "Orange"], [65, "Yellow"], [150, "Green"], [190, "Cyan"], [250, "Blue"], [290, "Purple"], [330, "Pink"], [361, "Red"]], d = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
|
|
33
|
+
function s(f2, i2) {
|
|
34
|
+
const l2 = i2.columns, c2 = i2.colors.length, s2 = Math.ceil(c2 / l2), g = document.createElement("div");
|
|
35
|
+
g.className = "notectl-color-picker__grid", g.setAttribute("role", "grid"), g.setAttribute("aria-label", i2.ariaLabel);
|
|
36
|
+
const u = [];
|
|
37
|
+
let h = 0;
|
|
38
|
+
for (let r2 = 0; r2 < s2; r2++) {
|
|
39
|
+
const t2 = document.createElement("div");
|
|
40
|
+
t2.setAttribute("role", "row");
|
|
41
|
+
for (let f3 = 0; f3 < l2; f3++) {
|
|
42
|
+
const d2 = r2 * l2 + f3;
|
|
43
|
+
if (d2 >= c2) break;
|
|
44
|
+
const s3 = i2.colors[d2], g2 = n(s3), b = !!i2.activeColor && i2.activeColor.toLowerCase() === s3.toLowerCase(), L = document.createElement("button");
|
|
45
|
+
L.type = "button", L.className = "notectl-color-picker__swatch", L.setAttribute("role", "gridcell");
|
|
46
|
+
const k = i2.swatchLabel ? i2.swatchLabel(g2) : `${i2.ariaLabelPrefix} ${g2}`;
|
|
47
|
+
L.setAttribute("aria-label", k), L.setAttribute("aria-selected", String(b)), L.dataset.index = String(d2), e(L, "backgroundColor", s3), L.title = i2.titleAsName ? g2 : s3, o(s3) && a(L, { border: "1px solid #d0d0d0" }), b && (L.classList.add("notectl-color-picker__swatch--active"), h = d2), L.addEventListener("mousedown", (e2) => {
|
|
48
|
+
e2.preventDefault(), e2.stopPropagation(), i2.onSelect(s3);
|
|
49
|
+
}), u.push(L), t2.appendChild(L);
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
+
g.appendChild(t2);
|
|
51
52
|
}
|
|
52
|
-
|
|
53
|
-
if ("Escape" ===
|
|
54
|
-
if ("Enter" ===
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
if (void 0 !==
|
|
58
|
-
const
|
|
59
|
-
|
|
53
|
+
r(u, h), g.addEventListener("keydown", (e2) => {
|
|
54
|
+
if ("Escape" === e2.key) return e2.preventDefault(), e2.stopPropagation(), void i2.onClose();
|
|
55
|
+
if ("Enter" === e2.key || " " === e2.key) {
|
|
56
|
+
e2.preventDefault(), e2.stopPropagation();
|
|
57
|
+
const a2 = e2.target.dataset.index;
|
|
58
|
+
if (void 0 !== a2) {
|
|
59
|
+
const e3 = i2.colors[Number(a2)];
|
|
60
|
+
e3 && i2.onSelect(e3);
|
|
60
61
|
}
|
|
61
62
|
return;
|
|
62
63
|
}
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
let
|
|
67
|
-
return
|
|
64
|
+
if (d.has(e2.key)) {
|
|
65
|
+
e2.preventDefault(), e2.stopPropagation();
|
|
66
|
+
const a2 = Math.floor(h / l2) + 1, f3 = h % l2 + 1, [n2, o2] = t(a2, f3, s2, l2, e2.key);
|
|
67
|
+
let i3 = (n2 - 1) * l2 + (o2 - 1);
|
|
68
|
+
return i3 >= c2 && (i3 = c2 - 1), h = i3, r(u, h), void u[h]?.focus();
|
|
68
69
|
}
|
|
69
|
-
if ("Home" ===
|
|
70
|
-
|
|
71
|
-
const a2 = Math.floor(
|
|
72
|
-
|
|
70
|
+
if ("Home" === e2.key || "End" === e2.key) {
|
|
71
|
+
e2.preventDefault(), e2.stopPropagation();
|
|
72
|
+
const a2 = Math.floor(h / l2) * l2, t2 = Math.min(a2 + l2 - 1, c2 - 1);
|
|
73
|
+
h = "Home" === e2.key ? a2 : t2, r(u, h), u[h]?.focus();
|
|
73
74
|
}
|
|
74
|
-
}),
|
|
75
|
+
}), f2.appendChild(g);
|
|
75
76
|
}
|
|
76
77
|
export {
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
n as g,
|
|
79
|
+
s as r
|
|
79
80
|
};
|
|
80
|
-
//# sourceMappingURL=ColorGrid
|
|
81
|
+
//# sourceMappingURL=ColorGrid--lBQ7jCK.mjs.map
|