@notectl/core 1.6.1 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs → AttrRegistry-Ci0qAvtS.mjs} +2 -2
- package/dist/chunks/{AttrRegistry-I-VdmRnN.mjs.map → AttrRegistry-Ci0qAvtS.mjs.map} +1 -1
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs +58 -0
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs.map +1 -0
- package/dist/chunks/{CaretNavigation-CAJXoaae.mjs → CaretNavigation-qLfeL0So.mjs} +56 -56
- package/dist/chunks/{CaretNavigation-CAJXoaae.mjs.map → CaretNavigation-qLfeL0So.mjs.map} +1 -1
- package/dist/chunks/ColorGrid-BBkN0AOb.mjs +80 -0
- package/dist/chunks/ColorGrid-BBkN0AOb.mjs.map +1 -0
- package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs +37 -0
- package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs.map +1 -0
- package/dist/chunks/ColorValidation-CV4f1wu6.mjs +39 -0
- package/dist/chunks/ColorValidation-CV4f1wu6.mjs.map +1 -0
- package/dist/chunks/{Document-CuwE73FF.mjs → Document-DfZ1NOjs.mjs} +15 -15
- package/dist/chunks/{Document-CuwE73FF.mjs.map → Document-DfZ1NOjs.mjs.map} +1 -1
- package/dist/chunks/MarkCommands-uybmkw9l.mjs +94 -0
- package/dist/chunks/MarkCommands-uybmkw9l.mjs.map +1 -0
- package/dist/chunks/{NavigationUtils-CyW0Vo8A.mjs → NavigationUtils-Cspt2ZrK.mjs} +3 -3
- package/dist/chunks/{NavigationUtils-CyW0Vo8A.mjs.map → NavigationUtils-Cspt2ZrK.mjs.map} +1 -1
- package/dist/chunks/{NodeResolver-B-wzthAQ.mjs → NodeResolver-CzLmmrph.mjs} +2 -2
- package/dist/chunks/{NodeResolver-B-wzthAQ.mjs.map → NodeResolver-CzLmmrph.mjs.map} +1 -1
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs +30 -0
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs.map +1 -0
- package/dist/chunks/RangeIterator-Bye0UtbY.mjs +16 -0
- package/dist/chunks/RangeIterator-Bye0UtbY.mjs.map +1 -0
- package/dist/chunks/{Selection-DmFDwiPJ.mjs → Selection-BRTab5hO.mjs} +7 -7
- package/dist/chunks/{Selection-DmFDwiPJ.mjs.map → Selection-BRTab5hO.mjs.map} +1 -1
- package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
- package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs → ToolbarKeyboardNav-DhpL8zNV.mjs} +3 -3
- package/dist/chunks/{ToolbarKeyboardNav-B32OMv8r.mjs.map → ToolbarKeyboardNav-DhpL8zNV.mjs.map} +1 -1
- package/dist/chunks/{ToolbarPlugin-B6JETWaw.mjs → ToolbarPlugin-B7oJRzpl.mjs} +339 -127
- package/dist/chunks/ToolbarPlugin-B7oJRzpl.mjs.map +1 -0
- package/dist/chunks/{index-CPyHuEzh.mjs → index-Bt_d4IqB.mjs} +12 -12
- package/dist/chunks/{index-CPyHuEzh.mjs.map → index-Bt_d4IqB.mjs.map} +1 -1
- package/dist/chunks/{index-BigJ0z6n.mjs → index-CFJFD4lF.mjs} +8 -8
- package/dist/chunks/{index-BigJ0z6n.mjs.map → index-CFJFD4lF.mjs.map} +1 -1
- package/dist/chunks/{index-D67mP19g.mjs → index-CFPP456m.mjs} +13 -13
- package/dist/chunks/{index-D67mP19g.mjs.map → index-CFPP456m.mjs.map} +1 -1
- package/dist/chunks/{index-DQryjfJs.mjs → index-CH1r7DL-.mjs} +259 -300
- package/dist/chunks/index-CH1r7DL-.mjs.map +1 -0
- package/dist/chunks/{index-tBDVaSjG.mjs → index-CfyS_pgH.mjs} +28 -21
- package/dist/chunks/index-CfyS_pgH.mjs.map +1 -0
- package/dist/chunks/{index-DIAXWwgJ.mjs → index-Ckzqtn5D.mjs} +17 -14
- package/dist/chunks/{index-DIAXWwgJ.mjs.map → index-Ckzqtn5D.mjs.map} +1 -1
- package/dist/chunks/{index-BDMP86F4.mjs → index-CrKz_IEn.mjs} +35 -35
- package/dist/chunks/{index-BDMP86F4.mjs.map → index-CrKz_IEn.mjs.map} +1 -1
- package/dist/chunks/{index-B0UFAfyb.mjs → index-DGKQhSAl.mjs} +5 -5
- package/dist/chunks/{index-B0UFAfyb.mjs.map → index-DGKQhSAl.mjs.map} +1 -1
- package/dist/chunks/{index-83QoUENW.mjs → index-DIvHQtE2.mjs} +12 -9
- package/dist/chunks/{index-83QoUENW.mjs.map → index-DIvHQtE2.mjs.map} +1 -1
- package/dist/chunks/index-DayTn6Re.mjs +166 -0
- package/dist/chunks/index-DayTn6Re.mjs.map +1 -0
- package/dist/chunks/index-Deag_lVi.mjs +121 -0
- package/dist/chunks/index-Deag_lVi.mjs.map +1 -0
- package/dist/chunks/{index--z_Txzej.mjs → index-DmuuFS5D.mjs} +6 -6
- package/dist/chunks/{index--z_Txzej.mjs.map → index-DmuuFS5D.mjs.map} +1 -1
- package/dist/chunks/{index-DRZwd-1W.mjs → index-DoCvDkfj.mjs} +79 -96
- package/dist/chunks/index-DoCvDkfj.mjs.map +1 -0
- package/dist/chunks/index-DuRNETFv.mjs +296 -0
- package/dist/chunks/index-DuRNETFv.mjs.map +1 -0
- package/dist/chunks/{index-CPjf45-o.mjs → index-Dum8yE_t.mjs} +14 -14
- package/dist/chunks/{index-CPjf45-o.mjs.map → index-Dum8yE_t.mjs.map} +1 -1
- package/dist/chunks/{index-BWXkaC2Z.mjs → index-Dy6SllXW.mjs} +53 -65
- package/dist/chunks/index-Dy6SllXW.mjs.map +1 -0
- package/dist/chunks/{index-D8tbbW97.mjs → index-E7OgN7hM.mjs} +2 -2
- package/dist/chunks/{index-D8tbbW97.mjs.map → index-E7OgN7hM.mjs.map} +1 -1
- package/dist/chunks/{index-D4GKlc-A.mjs → index-yhz2qeeh.mjs} +224 -222
- package/dist/chunks/index-yhz2qeeh.mjs.map +1 -0
- package/dist/commands/AttributedMarkCommands.d.ts +46 -0
- package/dist/commands/AttributedMarkCommands.d.ts.map +1 -0
- package/dist/commands/CommandHelpers.d.ts +21 -0
- package/dist/commands/CommandHelpers.d.ts.map +1 -0
- package/dist/commands/Commands.d.ts +2 -8
- package/dist/commands/Commands.d.ts.map +1 -1
- package/dist/commands/DeleteCommands.d.ts.map +1 -1
- package/dist/commands/GapCursorCommands.d.ts.map +1 -1
- package/dist/commands/NodeSelectionCommands.d.ts.map +1 -1
- package/dist/commands/PasteCommand.d.ts.map +1 -1
- package/dist/commands/RangeIterator.d.ts +10 -0
- package/dist/commands/RangeIterator.d.ts.map +1 -0
- package/dist/editor/DocumentParser.d.ts.map +1 -1
- package/dist/editor/DocumentSerializer.d.ts +2 -0
- package/dist/editor/DocumentSerializer.d.ts.map +1 -1
- package/dist/editor/MarkSerializer.d.ts +11 -0
- package/dist/editor/MarkSerializer.d.ts.map +1 -0
- package/dist/editor/NotectlEditor.d.ts +6 -3
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/PaperLayoutController.d.ts +2 -0
- 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 +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/input/ClipboardHandler.d.ts +0 -2
- package/dist/input/ClipboardHandler.d.ts.map +1 -1
- package/dist/model/MarkSpec.d.ts +6 -0
- package/dist/model/MarkSpec.d.ts.map +1 -1
- package/dist/notectl-core.mjs +2245 -2085
- package/dist/notectl-core.mjs.map +1 -1
- package/dist/notectl-core.umd.js +5568 -5345
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/PluginManager.d.ts +2 -0
- package/dist/plugins/PluginManager.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +3 -3
- package/dist/plugins/blockquote.mjs +2 -2
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block.mjs +3 -3
- package/dist/plugins/font/FontPlugin.d.ts +0 -6
- package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizeOperations.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -5
- package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
- package/dist/plugins/font-size.mjs +2 -2
- package/dist/plugins/font.mjs +2 -2
- package/dist/plugins/gap-cursor.mjs +7 -7
- package/dist/plugins/hard-break.mjs +2 -25
- package/dist/plugins/hard-break.mjs.map +1 -1
- package/dist/plugins/heading/HeadingBlockTypePicker.d.ts +1 -7
- package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
- package/dist/plugins/heading/HeadingPlugin.d.ts +0 -6
- package/dist/plugins/heading/HeadingPlugin.d.ts.map +1 -1
- package/dist/plugins/heading.mjs +2 -2
- package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
- package/dist/plugins/highlight.mjs +2 -2
- package/dist/plugins/horizontal-rule.mjs +2 -2
- package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
- package/dist/plugins/image.mjs +3 -3
- package/dist/plugins/link/LinkPlugin.d.ts.map +1 -1
- package/dist/plugins/link.mjs +2 -2
- package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
- package/dist/plugins/list.mjs +2 -2
- package/dist/plugins/shared/ColorGrid.d.ts +25 -0
- package/dist/plugins/shared/ColorGrid.d.ts.map +1 -0
- package/dist/plugins/shared/ColorMarkOperations.d.ts.map +1 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
- package/dist/plugins/shared/ColorValidation.d.ts +13 -2
- package/dist/plugins/shared/ColorValidation.d.ts.map +1 -1
- package/dist/plugins/shared/PopupKeyboardPatterns.d.ts +43 -0
- package/dist/plugins/shared/PopupKeyboardPatterns.d.ts.map +1 -0
- package/dist/plugins/shared/PopupManager.d.ts +45 -0
- package/dist/plugins/shared/PopupManager.d.ts.map +1 -0
- package/dist/plugins/shared/PopupPositioning.d.ts +21 -0
- package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -0
- package/dist/plugins/strikethrough.mjs +2 -2
- package/dist/plugins/super-sub.mjs +2 -2
- package/dist/plugins/table/TableBorderColor.d.ts.map +1 -1
- package/dist/plugins/table/TableContextMenu.d.ts +2 -1
- package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
- package/dist/plugins/table.mjs +10 -10
- package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
- package/dist/plugins/text-color.mjs +2 -2
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar/ToolbarItem.d.ts +11 -2
- package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarOverflowController.d.ts +5 -1
- package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarPlugin.d.ts +1 -0
- package/dist/plugins/toolbar/ToolbarPlugin.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts +9 -1
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
- package/dist/plugins/toolbar/index.d.ts +1 -1
- package/dist/plugins/toolbar/index.d.ts.map +1 -1
- package/dist/plugins/toolbar.mjs +1 -1
- package/dist/presets/FullPreset.d.ts +16 -0
- package/dist/presets/FullPreset.d.ts.map +1 -0
- package/dist/presets/MinimalPreset.d.ts +7 -0
- package/dist/presets/MinimalPreset.d.ts.map +1 -0
- package/dist/presets/PresetTypes.d.ts +50 -0
- package/dist/presets/PresetTypes.d.ts.map +1 -0
- package/dist/state/BlockTreeOps.d.ts +21 -0
- package/dist/state/BlockTreeOps.d.ts.map +1 -0
- package/dist/state/InlineContentOps.d.ts +37 -0
- package/dist/state/InlineContentOps.d.ts.map +1 -0
- package/dist/state/StepApplication.d.ts.map +1 -1
- package/dist/test/PluginTestUtils.d.ts +4 -0
- package/dist/test/PluginTestUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/ColorNames-BdsHwA2f.mjs +0 -59
- package/dist/chunks/ColorNames-BdsHwA2f.mjs.map +0 -1
- package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs +0 -125
- package/dist/chunks/ColorPickerPopup-xL8ZUfpB.mjs.map +0 -1
- package/dist/chunks/Commands-CicwYoTQ.mjs +0 -286
- package/dist/chunks/Commands-CicwYoTQ.mjs.map +0 -1
- package/dist/chunks/MarkCommands-CC37WA7Z.mjs +0 -94
- package/dist/chunks/MarkCommands-CC37WA7Z.mjs.map +0 -1
- package/dist/chunks/ToolbarPlugin-B6JETWaw.mjs.map +0 -1
- package/dist/chunks/index-BWXkaC2Z.mjs.map +0 -1
- package/dist/chunks/index-D4GKlc-A.mjs.map +0 -1
- package/dist/chunks/index-DQryjfJs.mjs.map +0 -1
- package/dist/chunks/index-DRZwd-1W.mjs.map +0 -1
- package/dist/chunks/index-DUC1X43Z.mjs +0 -209
- package/dist/chunks/index-DUC1X43Z.mjs.map +0 -1
- package/dist/chunks/index-DipAT_Q9.mjs +0 -191
- package/dist/chunks/index-DipAT_Q9.mjs.map +0 -1
- package/dist/chunks/index-tBDVaSjG.mjs.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { i as t, g as e, a as n, r as o } from "./AttributedMarkCommands-BETuHXj8.mjs";
|
|
2
|
+
import { r as i } from "./resolvePluginLocale-B4w1OkPy.mjs";
|
|
3
|
+
import { e as r } from "./HTMLUtils-DTqQzcKR.mjs";
|
|
4
|
+
import { m as a } from "./TypeBrands-C09nE2qO.mjs";
|
|
5
|
+
import { b as l } from "./ColorValidation-CV4f1wu6.mjs";
|
|
6
|
+
import { a as s } from "./ToolbarPlugin-B7oJRzpl.mjs";
|
|
7
|
+
const c = { label: "Font", tooltip: "Font Family" }, p = { en: c, de: { label: "Schriftart", tooltip: "Schriftfamilie" }, es: { label: "Fuente", tooltip: "Familia de fuente" }, fr: { label: "Police", tooltip: "Famille de police" }, zh: { label: "字体", tooltip: "字体系列" }, ru: { label: "Шрифт", tooltip: "Семейство шрифтов" }, ar: { label: "الخط", tooltip: "عائلة الخط" }, hi: { label: "फ़ॉन्ट", tooltip: "फ़ॉन्ट परिवार" } };
|
|
8
|
+
class f {
|
|
9
|
+
id = "font";
|
|
10
|
+
name = "Font";
|
|
11
|
+
priority = 22;
|
|
12
|
+
config;
|
|
13
|
+
locale;
|
|
14
|
+
injectedStyleElement = null;
|
|
15
|
+
context = null;
|
|
16
|
+
constructor(t2) {
|
|
17
|
+
this.config = t2;
|
|
18
|
+
}
|
|
19
|
+
init(t2) {
|
|
20
|
+
this.locale = i(p, t2, this.config.locale), t2.registerStyleSheet("\n/* Font Select — Width overrides for combobox button */\n.notectl-toolbar-btn--font {\n min-width: 100px;\n max-width: 160px;\n}\n\n/* Font Picker Popup */\n.notectl-font-picker {\n min-width: 200px;\n max-height: 320px;\n overflow-y: auto;\n}\n\n.notectl-font-picker__list {\n padding: 4px 0;\n}\n\n.notectl-font-picker__item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 7px 12px;\n border: none;\n background: none;\n cursor: pointer;\n font-size: 14px;\n color: var(--notectl-fg);\n line-height: 1.4;\n text-align: left;\n white-space: nowrap;\n font-family: inherit;\n transition: background 0.1s;\n}\n\n.notectl-font-picker__item:hover {\n background: var(--notectl-hover-bg);\n}\n\n.notectl-font-picker__item--active {\n background: var(--notectl-active-bg);\n color: var(--notectl-primary-fg);\n}\n\n.notectl-font-picker__item--active:hover {\n background: var(--notectl-active-bg);\n}\n\n.notectl-font-picker__check {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n flex-shrink: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--notectl-primary-fg);\n}\n\n.notectl-font-picker__label {\n flex: 1;\n}\n\n.notectl-font-picker__separator {\n height: 1px;\n background: var(--notectl-border);\n margin: 4px 8px;\n}\n"), this.context = t2, this.registerMarkSpec(t2), this.registerCommands(t2), this.registerToolbarItem(t2), this.injectFontFaces();
|
|
21
|
+
}
|
|
22
|
+
destroy() {
|
|
23
|
+
this.injectedStyleElement?.remove(), this.injectedStyleElement = null, this.context = null;
|
|
24
|
+
}
|
|
25
|
+
registerMarkSpec(t2) {
|
|
26
|
+
t2.registerMarkSpec({ type: "font", rank: 6, attrs: { family: { default: "" } }, toDOM(t3) {
|
|
27
|
+
const e2 = document.createElement("span"), n2 = t3.attrs?.family ?? "";
|
|
28
|
+
return n2 && (e2.style.fontFamily = n2), e2;
|
|
29
|
+
}, toHTMLString: (t3, e2) => {
|
|
30
|
+
const n2 = String(t3.attrs?.family ?? "");
|
|
31
|
+
return n2 && l(n2) ? `<span style="font-family: ${r(n2)}">${e2}</span>` : e2;
|
|
32
|
+
}, toHTMLStyle: (t3) => {
|
|
33
|
+
const e2 = String(t3.attrs?.family ?? "");
|
|
34
|
+
return e2 && l(e2) ? `font-family: ${r(e2)}` : null;
|
|
35
|
+
}, parseHTML: [{ tag: "span", getAttrs: (t3) => {
|
|
36
|
+
const e2 = t3.style.fontFamily;
|
|
37
|
+
return !!e2 && { family: e2 };
|
|
38
|
+
} }], sanitize: { tags: ["span"] } });
|
|
39
|
+
}
|
|
40
|
+
registerCommands(t2) {
|
|
41
|
+
t2.registerCommand("removeFont", () => {
|
|
42
|
+
const e2 = t2.getState();
|
|
43
|
+
return this.removeFont(t2, e2);
|
|
44
|
+
}), t2.registerCommand("setFont", () => false);
|
|
45
|
+
}
|
|
46
|
+
get defaultFont() {
|
|
47
|
+
if (this.config.defaultFont) {
|
|
48
|
+
const t3 = this.config.fonts.find((t4) => t4.name === this.config.defaultFont);
|
|
49
|
+
if (t3) return t3;
|
|
50
|
+
}
|
|
51
|
+
const t2 = this.config.fonts[0];
|
|
52
|
+
if (!t2) throw new Error("FontPlugin: fonts list is empty");
|
|
53
|
+
return t2;
|
|
54
|
+
}
|
|
55
|
+
registerToolbarItem(t2) {
|
|
56
|
+
t2.registerToolbarItem({ id: "font", group: "format", label: this.locale.label, tooltip: this.locale.tooltip, command: "removeFont", priority: 5, popupType: "combobox", separatorAfter: this.config.separatorAfter, getLabel: (t3) => this.resolveFontName(this.getActiveFont(t3)), renderPopup: (t3, e2) => {
|
|
57
|
+
this.renderFontPopup(t3, e2);
|
|
58
|
+
}, isActive: (t3) => this.isFontActive(t3) });
|
|
59
|
+
}
|
|
60
|
+
resolveFontName(t2) {
|
|
61
|
+
if (!t2) return this.defaultFont.name;
|
|
62
|
+
const e2 = this.config.fonts.find((e3) => e3.family === t2);
|
|
63
|
+
return e2?.name ?? (t2.split(",")[0] ?? "").trim().replace(/'/g, "");
|
|
64
|
+
}
|
|
65
|
+
isFontActive(e2) {
|
|
66
|
+
return t(e2, "font");
|
|
67
|
+
}
|
|
68
|
+
getActiveFont(t2) {
|
|
69
|
+
return e(t2, "font", (t3) => t3.attrs.family ?? null);
|
|
70
|
+
}
|
|
71
|
+
applyFont(t2, e2, o2) {
|
|
72
|
+
const i2 = { type: a("font"), attrs: { family: o2 } }, r2 = n(e2, i2);
|
|
73
|
+
return !!r2 && (t2.dispatch(r2), true);
|
|
74
|
+
}
|
|
75
|
+
removeFont(t2, e2) {
|
|
76
|
+
const n2 = o(e2, a("font"));
|
|
77
|
+
return !!n2 && (t2.dispatch(n2), true);
|
|
78
|
+
}
|
|
79
|
+
injectFontFaces() {
|
|
80
|
+
const t2 = [];
|
|
81
|
+
for (const e3 of this.config.fonts) if (e3.fontFaces?.length) for (const n2 of e3.fontFaces) {
|
|
82
|
+
const o2 = [`font-family: '${(e3.family.split(",")[0] ?? "").trim().replace(/'/g, "")}'`, `src: ${n2.src}`];
|
|
83
|
+
n2.weight && o2.push(`font-weight: ${n2.weight}`), n2.style && o2.push(`font-style: ${n2.style}`), o2.push(`font-display: ${n2.display ?? "swap"}`), t2.push(`@font-face {
|
|
84
|
+
${o2.join(";\n ")};
|
|
85
|
+
}`);
|
|
86
|
+
}
|
|
87
|
+
if (0 === t2.length) return;
|
|
88
|
+
const e2 = document.createElement("style");
|
|
89
|
+
e2.setAttribute("data-notectl-fonts", ""), e2.textContent = t2.join("\n\n"), document.head.appendChild(e2), this.injectedStyleElement = e2;
|
|
90
|
+
}
|
|
91
|
+
dismissPopup() {
|
|
92
|
+
const t2 = this.context?.getService(s);
|
|
93
|
+
t2?.closePopup();
|
|
94
|
+
}
|
|
95
|
+
renderFontPopup(t2, e2) {
|
|
96
|
+
t2.classList.add("notectl-font-picker");
|
|
97
|
+
const n2 = e2.getState(), o2 = this.getActiveFont(n2), i2 = this.defaultFont.family, r2 = document.createElement("div");
|
|
98
|
+
r2.className = "notectl-font-picker__list", r2.setAttribute("role", "listbox"), r2.setAttribute("aria-label", this.locale.label);
|
|
99
|
+
for (const t3 of this.config.fonts) {
|
|
100
|
+
const n3 = t3.family === i2, a2 = n3 && !o2 || o2 === t3.family, l2 = this.createFontItem(t3.name, t3.family, a2, (o3) => {
|
|
101
|
+
o3.preventDefault(), o3.stopPropagation(), n3 ? e2.executeCommand("removeFont") : this.applyFont(e2, e2.getState(), t3.family), this.dismissPopup();
|
|
102
|
+
});
|
|
103
|
+
t3.category && l2.setAttribute("data-category", t3.category), r2.appendChild(l2);
|
|
104
|
+
}
|
|
105
|
+
t2.appendChild(r2);
|
|
106
|
+
}
|
|
107
|
+
createFontItem(t2, e2, n2, o2) {
|
|
108
|
+
const i2 = document.createElement("button");
|
|
109
|
+
i2.type = "button", i2.className = "notectl-font-picker__item", i2.setAttribute("role", "option"), i2.setAttribute("aria-selected", String(n2)), n2 && i2.classList.add("notectl-font-picker__item--active");
|
|
110
|
+
const r2 = document.createElement("span");
|
|
111
|
+
r2.className = "notectl-font-picker__check", r2.textContent = n2 ? "✓" : "", i2.appendChild(r2);
|
|
112
|
+
const a2 = document.createElement("span");
|
|
113
|
+
return a2.className = "notectl-font-picker__label", a2.textContent = t2, e2 && (a2.style.fontFamily = e2), i2.appendChild(a2), i2.addEventListener("mousedown", o2), i2;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
f as F,
|
|
118
|
+
p as a,
|
|
119
|
+
c as b
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=index-Deag_lVi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Deag_lVi.mjs","sources":["../../src/editor/styles/font-select.ts","../../src/plugins/font/FontLocale.ts","../../src/plugins/font/FontPlugin.ts"],"sourcesContent":["/** Font picker toolbar combobox + popup styles. */\nexport const FONT_SELECT_CSS = `\n/* Font Select — Width overrides for combobox button */\n.notectl-toolbar-btn--font {\n\tmin-width: 100px;\n\tmax-width: 160px;\n}\n\n/* Font Picker Popup */\n.notectl-font-picker {\n\tmin-width: 200px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.notectl-font-picker__list {\n\tpadding: 4px 0;\n}\n\n.notectl-font-picker__item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tpadding: 7px 12px;\n\tborder: none;\n\tbackground: none;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tcolor: var(--notectl-fg);\n\tline-height: 1.4;\n\ttext-align: left;\n\twhite-space: nowrap;\n\tfont-family: inherit;\n\ttransition: background 0.1s;\n}\n\n.notectl-font-picker__item:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-font-picker__item--active {\n\tbackground: var(--notectl-active-bg);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-font-picker__item--active:hover {\n\tbackground: var(--notectl-active-bg);\n}\n\n.notectl-font-picker__check {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 16px;\n\tflex-shrink: 0;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-font-picker__label {\n\tflex: 1;\n}\n\n.notectl-font-picker__separator {\n\theight: 1px;\n\tbackground: var(--notectl-border);\n\tmargin: 4px 8px;\n}\n`;\n","/** Locale interface and default English locale for the FontPlugin. */\n\n// --- Locale Interface ---\n\nexport interface FontLocale {\n\treadonly label: string;\n\treadonly tooltip: string;\n}\n\n// --- Default English Locale ---\n\nexport const FONT_LOCALE_EN: FontLocale = {\n\tlabel: 'Font',\n\ttooltip: 'Font Family',\n};\n\n// --- German Locale ---\n\nexport const FONT_LOCALE_DE: FontLocale = {\n\tlabel: 'Schriftart',\n\ttooltip: 'Schriftfamilie',\n};\n\n// --- Spanish Locale ---\n\nexport const FONT_LOCALE_ES: FontLocale = {\n\tlabel: 'Fuente',\n\ttooltip: 'Familia de fuente',\n};\n\n// --- French Locale ---\n\nexport const FONT_LOCALE_FR: FontLocale = {\n\tlabel: 'Police',\n\ttooltip: 'Famille de police',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const FONT_LOCALE_ZH: FontLocale = {\n\tlabel: '字体',\n\ttooltip: '字体系列',\n};\n\n// --- Russian Locale ---\n\nexport const FONT_LOCALE_RU: FontLocale = {\n\tlabel: 'Шрифт',\n\ttooltip: 'Семейство шрифтов',\n};\n\n// --- Arabic Locale ---\n\nexport const FONT_LOCALE_AR: FontLocale = {\n\tlabel: 'الخط',\n\ttooltip: 'عائلة الخط',\n};\n\n// --- Hindi Locale ---\n\nexport const FONT_LOCALE_HI: FontLocale = {\n\tlabel: 'फ़ॉन्ट',\n\ttooltip: 'फ़ॉन्ट परिवार',\n};\n\n// --- Locale Map ---\n\nexport const FONT_LOCALES: Record<string, FontLocale> = {\n\ten: FONT_LOCALE_EN,\n\tde: FONT_LOCALE_DE,\n\tes: FONT_LOCALE_ES,\n\tfr: FONT_LOCALE_FR,\n\tzh: FONT_LOCALE_ZH,\n\tru: FONT_LOCALE_RU,\n\tar: FONT_LOCALE_AR,\n\thi: FONT_LOCALE_HI,\n};\n","/**\n * FontPlugin: registers a font-family mark, a combobox-style toolbar selector\n * for font selection, and a developer-friendly API for registering custom fonts\n * (WOFF2, TTF, OTF).\n */\n\nimport {\n\tapplyAttributedMark,\n\tgetMarkAttrAtSelection,\n\tisAttributedMarkActive,\n\tremoveAttributedMark,\n} from '../../commands/AttributedMarkCommands.js';\nimport { FONT_SELECT_CSS } from '../../editor/styles/font-select.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport { markType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isValidCSSFontFamily } from '../shared/ColorValidation.js';\nimport { ToolbarServiceKey } from '../toolbar/ToolbarPlugin.js';\nimport { FONT_LOCALES, type FontLocale } from './FontLocale.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface MarkAttrRegistry {\n\t\tfont: { family: string };\n\t}\n}\n\n// --- Public Types ---\n\n/** Describes a single @font-face source. */\nexport interface FontFaceDescriptor {\n\t/** CSS `src` value, e.g. `\"url('/fonts/My.woff2') format('woff2')\"`. */\n\treadonly src: string;\n\t/** Font weight, e.g. `'400'` or `'300 700'` for variable fonts. */\n\treadonly weight?: string;\n\t/** Font style, e.g. `'normal'` or `'italic'`. */\n\treadonly style?: string;\n\t/** Font display strategy. Defaults to `'swap'`. */\n\treadonly display?: string;\n}\n\n/** Defines a font available in the editor. */\nexport interface FontDefinition {\n\t/** Display name shown in the toolbar dropdown. */\n\treadonly name: string;\n\t/** CSS `font-family` value, e.g. `\"'Fira Code', monospace\"`. */\n\treadonly family: string;\n\t/** Font category for grouping in the UI. */\n\treadonly category?: 'serif' | 'sans-serif' | 'monospace' | 'display' | 'handwriting';\n\t/**\n\t * Optional `@font-face` descriptors. When provided, the plugin\n\t * auto-injects the corresponding CSS rules into the document.\n\t */\n\treadonly fontFaces?: readonly FontFaceDescriptor[];\n}\n\n// --- Configuration ---\n\nexport interface FontConfig {\n\t/**\n\t * Fonts available in the editor. System fonts require no `fontFaces`.\n\t * Custom fonts with `fontFaces` get their `@font-face` rules auto-injected.\n\t *\n\t * Use `STARTER_FONTS` for a pre-configured set of fonts:\n\t * ```ts\n\t * new FontPlugin({ fonts: [...STARTER_FONTS] })\n\t * ```\n\t */\n\treadonly fonts: readonly FontDefinition[];\n\t/**\n\t * Name of the font that acts as the editor's default.\n\t * Selecting this font removes the mark (since the editor already uses it).\n\t * Defaults to the first font in the list.\n\t */\n\treadonly defaultFont?: string;\n\t/** When true, a separator is rendered after the font toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: FontLocale;\n}\n\n// --- Plugin ---\n\nexport class FontPlugin implements Plugin {\n\treadonly id = 'font';\n\treadonly name = 'Font';\n\treadonly priority = 22;\n\n\tprivate readonly config: FontConfig;\n\tprivate locale!: FontLocale;\n\tprivate injectedStyleElement: HTMLStyleElement | null = null;\n\tprivate context: PluginContext | null = null;\n\n\tconstructor(config: FontConfig) {\n\t\tthis.config = config;\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(FONT_LOCALES, context, this.config.locale);\n\n\t\tcontext.registerStyleSheet(FONT_SELECT_CSS);\n\t\tthis.context = context;\n\t\tthis.registerMarkSpec(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerToolbarItem(context);\n\t\tthis.injectFontFaces();\n\t}\n\n\tdestroy(): void {\n\t\tthis.injectedStyleElement?.remove();\n\t\tthis.injectedStyleElement = null;\n\t\tthis.context = null;\n\t}\n\n\t// --- Schema ---\n\n\tprivate registerMarkSpec(context: PluginContext): void {\n\t\tcontext.registerMarkSpec({\n\t\t\ttype: 'font',\n\t\t\trank: 6,\n\t\t\tattrs: {\n\t\t\t\tfamily: { default: '' },\n\t\t\t},\n\t\t\ttoDOM(mark) {\n\t\t\t\tconst span: HTMLElement = document.createElement('span');\n\t\t\t\tconst family: string = mark.attrs?.family ?? '';\n\t\t\t\tif (family) {\n\t\t\t\t\tspan.style.fontFamily = family;\n\t\t\t\t}\n\t\t\t\treturn span;\n\t\t\t},\n\t\t\ttoHTMLString: (mark, content) => {\n\t\t\t\tconst family: string = String(mark.attrs?.family ?? '');\n\t\t\t\tif (!family || !isValidCSSFontFamily(family)) return content;\n\t\t\t\treturn `<span style=\"font-family: ${escapeHTML(family)}\">${content}</span>`;\n\t\t\t},\n\t\t\ttoHTMLStyle: (mark) => {\n\t\t\t\tconst family: string = String(mark.attrs?.family ?? '');\n\t\t\t\tif (!family || !isValidCSSFontFamily(family)) return null;\n\t\t\t\treturn `font-family: ${escapeHTML(family)}`;\n\t\t\t},\n\t\t\tparseHTML: [\n\t\t\t\t{\n\t\t\t\t\ttag: 'span',\n\t\t\t\t\tgetAttrs: (el) => {\n\t\t\t\t\t\tconst family: string = el.style.fontFamily;\n\t\t\t\t\t\tif (!family) return false;\n\t\t\t\t\t\treturn { family };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\tsanitize: { tags: ['span'] },\n\t\t});\n\t}\n\n\t// --- Commands ---\n\n\tprivate registerCommands(context: PluginContext): void {\n\t\tcontext.registerCommand('removeFont', () => {\n\t\t\tconst state: EditorState = context.getState();\n\t\t\treturn this.removeFont(context, state);\n\t\t});\n\n\t\tcontext.registerCommand('setFont', () => {\n\t\t\treturn false;\n\t\t});\n\t}\n\n\t// --- Toolbar ---\n\n\tprivate get defaultFont(): FontDefinition {\n\t\tif (this.config.defaultFont) {\n\t\t\tconst found: FontDefinition | undefined = this.config.fonts.find(\n\t\t\t\t(f) => f.name === this.config.defaultFont,\n\t\t\t);\n\t\t\tif (found) return found;\n\t\t}\n\t\tconst first: FontDefinition | undefined = this.config.fonts[0];\n\t\tif (!first) throw new Error('FontPlugin: fonts list is empty');\n\t\treturn first;\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'font',\n\t\t\tgroup: 'format',\n\t\t\tlabel: this.locale.label,\n\t\t\ttooltip: this.locale.tooltip,\n\t\t\tcommand: 'removeFont',\n\t\t\tpriority: 5,\n\t\t\tpopupType: 'combobox',\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\tgetLabel: (state: EditorState): string => this.resolveFontName(this.getActiveFont(state)),\n\t\t\trenderPopup: (container, ctx) => {\n\t\t\t\tthis.renderFontPopup(container, ctx);\n\t\t\t},\n\t\t\tisActive: (state) => this.isFontActive(state),\n\t\t});\n\t}\n\n\tprivate resolveFontName(family: string | null): string {\n\t\tif (!family) return this.defaultFont.name;\n\t\tconst match: FontDefinition | undefined = this.config.fonts.find((f) => f.family === family);\n\t\treturn match?.name ?? (family.split(',')[0] ?? '').trim().replace(/'/g, '');\n\t}\n\n\t// --- State Queries ---\n\n\tprivate isFontActive(state: EditorState): boolean {\n\t\treturn isAttributedMarkActive(state, 'font');\n\t}\n\n\tgetActiveFont(state: EditorState): string | null {\n\t\treturn getMarkAttrAtSelection(state, 'font', (m) => m.attrs.family ?? null);\n\t}\n\n\t// --- Font Application ---\n\n\tapplyFont(context: PluginContext, state: EditorState, family: string): boolean {\n\t\tconst mark = { type: markType('font'), attrs: { family } };\n\t\tconst tr = applyAttributedMark(state, mark);\n\t\tif (!tr) return false;\n\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t}\n\n\tprivate removeFont(context: PluginContext, state: EditorState): boolean {\n\t\tconst tr = removeAttributedMark(state, markType('font'));\n\t\tif (!tr) return false;\n\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t}\n\n\t// --- @font-face Injection ---\n\n\tprivate injectFontFaces(): void {\n\t\tconst rules: string[] = [];\n\n\t\tfor (const font of this.config.fonts) {\n\t\t\tif (!font.fontFaces?.length) continue;\n\n\t\t\tfor (const face of font.fontFaces) {\n\t\t\t\tconst familyName: string = (font.family.split(',')[0] ?? '').trim().replace(/'/g, '');\n\t\t\t\tconst declarations: string[] = [`font-family: '${familyName}'`, `src: ${face.src}`];\n\t\t\t\tif (face.weight) {\n\t\t\t\t\tdeclarations.push(`font-weight: ${face.weight}`);\n\t\t\t\t}\n\t\t\t\tif (face.style) {\n\t\t\t\t\tdeclarations.push(`font-style: ${face.style}`);\n\t\t\t\t}\n\t\t\t\tdeclarations.push(`font-display: ${face.display ?? 'swap'}`);\n\n\t\t\t\trules.push(`@font-face {\\n\\t${declarations.join(';\\n\\t')};\\n}`);\n\t\t\t}\n\t\t}\n\n\t\tif (rules.length === 0) return;\n\n\t\tconst style: HTMLStyleElement = document.createElement('style');\n\t\tstyle.setAttribute('data-notectl-fonts', '');\n\t\tstyle.textContent = rules.join('\\n\\n');\n\t\tdocument.head.appendChild(style);\n\t\tthis.injectedStyleElement = style;\n\t}\n\n\t// --- Popup Rendering ---\n\n\tprivate dismissPopup(): void {\n\t\tconst toolbar = this.context?.getService(ToolbarServiceKey);\n\t\ttoolbar?.closePopup();\n\t}\n\n\tprivate renderFontPopup(container: HTMLElement, context: PluginContext): void {\n\t\tcontainer.classList.add('notectl-font-picker');\n\n\t\tconst state: EditorState = context.getState();\n\t\tconst activeFont: string | null = this.getActiveFont(state);\n\t\tconst defaultFamily: string = this.defaultFont.family;\n\n\t\tconst list: HTMLDivElement = document.createElement('div');\n\t\tlist.className = 'notectl-font-picker__list';\n\t\tlist.setAttribute('role', 'listbox');\n\t\tlist.setAttribute('aria-label', this.locale.label);\n\n\t\tfor (const font of this.config.fonts) {\n\t\t\tconst isDefault: boolean = font.family === defaultFamily;\n\t\t\tconst isActive: boolean = isDefault\n\t\t\t\t? !activeFont || activeFont === font.family\n\t\t\t\t: activeFont === font.family;\n\n\t\t\tconst item: HTMLButtonElement = this.createFontItem(\n\t\t\t\tfont.name,\n\t\t\t\tfont.family,\n\t\t\t\tisActive,\n\t\t\t\t(e: MouseEvent) => {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\te.stopPropagation();\n\t\t\t\t\tif (isDefault) {\n\t\t\t\t\t\tcontext.executeCommand('removeFont');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.applyFont(context, context.getState(), font.family);\n\t\t\t\t\t}\n\t\t\t\t\tthis.dismissPopup();\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (font.category) {\n\t\t\t\titem.setAttribute('data-category', font.category);\n\t\t\t}\n\n\t\t\tlist.appendChild(item);\n\t\t}\n\n\t\tcontainer.appendChild(list);\n\t}\n\n\tprivate createFontItem(\n\t\tname: string,\n\t\tfamily: string,\n\t\tisActive: boolean,\n\t\thandler: (e: MouseEvent) => void,\n\t): HTMLButtonElement {\n\t\tconst item: HTMLButtonElement = document.createElement('button');\n\t\titem.type = 'button';\n\t\titem.className = 'notectl-font-picker__item';\n\t\titem.setAttribute('role', 'option');\n\t\titem.setAttribute('aria-selected', String(isActive));\n\n\t\tif (isActive) {\n\t\t\titem.classList.add('notectl-font-picker__item--active');\n\t\t}\n\n\t\t// Checkmark indicator\n\t\tconst check: HTMLSpanElement = document.createElement('span');\n\t\tcheck.className = 'notectl-font-picker__check';\n\t\tcheck.textContent = isActive ? '\\u2713' : '';\n\t\titem.appendChild(check);\n\n\t\t// Font name label (rendered in the font itself for preview)\n\t\tconst label: HTMLSpanElement = document.createElement('span');\n\t\tlabel.className = 'notectl-font-picker__label';\n\t\tlabel.textContent = name;\n\t\tif (family) {\n\t\t\tlabel.style.fontFamily = family;\n\t\t}\n\t\titem.appendChild(label);\n\n\t\titem.addEventListener('mousedown', handler);\n\t\treturn item;\n\t}\n}\n"],"names":["FONT_LOCALE_EN","label","tooltip","FONT_LOCALES","en","de","es","fr","zh","ru","ar","hi","FontPlugin","id","name","priority","config","locale","injectedStyleElement","context","this","resolvePluginLocale","registerStyleSheet","registerMarkSpec","registerCommands","registerToolbarItem","injectFontFaces","destroy","remove","type","rank","attrs","family","default","mark","span","document","createElement","style","fontFamily","toHTMLString","content","String","isValidCSSFontFamily","escapeHTML","toHTMLStyle","parseHTML","tag","getAttrs","el","sanitize","tags","registerCommand","state","getState","removeFont","defaultFont","found","fonts","find","f","first","Error","group","command","popupType","separatorAfter","getLabel","resolveFontName","getActiveFont","renderPopup","container","ctx","renderFontPopup","isActive","isFontActive","match","split","trim","replace","isAttributedMarkActive","getMarkAttrAtSelection","m","markType","tr","applyAttributedMark","dispatch","removeAttributedMark","rules","font","fontFaces","length","face","declarations","src","weight","push","display","join","setAttribute","textContent","head","appendChild","dismissPopup","toolbar","getService","ToolbarServiceKey","closePopup","classList","add","activeFont","defaultFamily","list","className","isDefault","item","createFontItem","e","preventDefault","stopPropagation","executeCommand","applyFont","category","handler","check","addEventListener"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as r, i } from "./NavigationUtils-
|
|
3
|
-
import { i as l, a as c, b as a } from "./Selection-
|
|
4
|
-
import { i as s, m as f, j as u, g as d, r as h, c as k } from "./CaretNavigation-
|
|
1
|
+
import { e as n, a as t, v as o, m as e } from "./Document-DfZ1NOjs.mjs";
|
|
2
|
+
import { c as r, i } from "./NavigationUtils-Cspt2ZrK.mjs";
|
|
3
|
+
import { i as l, a as c, b as a } from "./Selection-BRTab5hO.mjs";
|
|
4
|
+
import { i as s, m as f, j as u, g as d, r as h, c as k } from "./CaretNavigation-qLfeL0So.mjs";
|
|
5
5
|
import { m } from "./TypeBrands-C09nE2qO.mjs";
|
|
6
|
-
import { b as w } from "./MarkCommands-
|
|
6
|
+
import { b as w } from "./MarkCommands-uybmkw9l.mjs";
|
|
7
7
|
import { i as b, g } from "./Platform-S_xqIqcM.mjs";
|
|
8
8
|
const I = "undefined" != typeof Intl && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
|
|
9
9
|
function v(n2, t2) {
|
|
@@ -464,4 +464,4 @@ export {
|
|
|
464
464
|
F as y,
|
|
465
465
|
P as z
|
|
466
466
|
};
|
|
467
|
-
//# sourceMappingURL=index
|
|
467
|
+
//# sourceMappingURL=index-DmuuFS5D.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index--z_Txzej.mjs","sources":["../../src/model/GraphemeUtils.ts","../../src/commands/WordBoundary.ts","../../src/commands/MovementCommands.ts","../../src/view/ViewMovementCommands.ts","../../src/editor/Announcer.ts","../../src/plugins/caret-navigation/CaretNavigationPlugin.ts"],"sourcesContent":["/**\n * Grapheme-cluster utilities for correct character navigation.\n *\n * Uses `Intl.Segmenter` (with `granularity: 'grapheme'`) to correctly\n * handle multi-code-unit characters such as emoji, ZWJ sequences, and\n * combining characters. Falls back to +1/-1 when `Intl.Segmenter` is\n * not available.\n */\n\nconst SEGMENTER_SUPPORTED: boolean = typeof Intl !== 'undefined' && 'Segmenter' in Intl;\nconst SEGMENTER: Intl.Segmenter | null = SEGMENTER_SUPPORTED\n\t? new Intl.Segmenter(undefined, { granularity: 'grapheme' })\n\t: null;\n\n/**\n * Returns the number of UTF-16 code units in the next grapheme cluster\n * starting at `offset`. Returns 0 if `offset` is at or past the end.\n */\nexport function nextGraphemeSize(text: string, offset: number): number {\n\tif (offset >= text.length || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index >= offset) {\n\t\t\t\treturn segment.segment.length;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/**\n * Returns the number of UTF-16 code units in the grapheme cluster\n * immediately before `offset`. Returns 0 if `offset` is 0 or the\n * string is empty.\n */\nexport function prevGraphemeSize(text: string, offset: number): number {\n\tif (offset <= 0 || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tlet lastSize = 1;\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index + segment.segment.length >= offset) {\n\t\t\t\tlastSize = segment.segment.length;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlastSize = segment.segment.length;\n\t\t}\n\t\treturn lastSize;\n\t}\n\n\treturn 1;\n}\n","/**\n * Word boundary detection for delete-word and word-movement commands.\n * InlineNodes act as word boundaries.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\n\n/**\n * Finds the word boundary backward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryBackward(block: BlockNode, offset: number): number {\n\tlet pos = offset - 1;\n\t// Skip trailing whitespace\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\t// If at InlineNode, delete just it (treat as word boundary)\n\tif (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos;\n\t}\n\t// Skip word characters until whitespace or InlineNode\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\treturn pos + 1;\n}\n\n/**\n * Finds the word boundary forward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryForward(block: BlockNode, offset: number): number {\n\tconst len = getBlockLength(block);\n\tlet pos = offset;\n\t// Skip word characters first\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\t// If at InlineNode and haven't moved, delete just the InlineNode\n\tif (pos === offset && pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos + 1;\n\t}\n\t// Skip trailing whitespace\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\treturn pos;\n}\n","/**\n * Model-based movement commands for programmatic cursor navigation.\n *\n * All functions are pure: they take an EditorState and return a Transaction\n * or `null` (when the movement is not applicable). None touch the DOM.\n *\n * Move commands produce a collapsed cursor. Extend commands keep the anchor\n * and move the head, producing a range selection. All clear storedMarks.\n */\n\nimport {\n\tblockOffsetToTextOffset,\n\tgetBlockLength,\n\tgetBlockText,\n\tgetContentAtOffset,\n} from '../model/Document.js';\nimport { nextGraphemeSize, prevGraphemeSize } from '../model/GraphemeUtils.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 { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolves the effective head position from any text selection. */\nfunction resolveHead(state: EditorState): { blockId: BlockId; offset: number } | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\treturn { blockId: sel.head.blockId, offset: sel.head.offset };\n}\n\n/** Returns the adjacent block ID in the given direction, or `null`. */\nfunction adjacentBlockId(\n\tstate: EditorState,\n\tcurrentBlockId: BlockId,\n\tdirection: 'forward' | 'backward',\n): BlockId | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(currentBlockId);\n\tif (idx < 0) return null;\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\treturn blockOrder[targetIdx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Character Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one character forward, skipping InlineNodes atomically. */\nexport function moveCharacterForward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\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\tconst blockLen: number = getBlockLength(block);\n\n\tif (offset < blockLen) {\n\t\t// InlineNode: skip atomically (+1)\n\t\tconst content = getContentAtOffset(block, offset);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset + 1);\n\t\t}\n\t\t// Text: advance by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = nextGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset + step);\n\t}\n\n\t// At block end → cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, nextId)) return null;\n\n\tif (isVoidBlock(state, nextId)) return nodeSelTx(state, nextId);\n\n\treturn moveTx(state, nextId, 0);\n}\n\n/** Moves the cursor one character backward, skipping InlineNodes atomically. */\nexport function moveCharacterBackward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\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\tif (offset > 0) {\n\t\t// InlineNode: skip atomically (-1)\n\t\tconst content = getContentAtOffset(block, offset - 1);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset - 1);\n\t\t}\n\t\t// Text: retreat by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = prevGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset - step);\n\t}\n\n\t// At block start → cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, prevId)) return null;\n\n\tif (isVoidBlock(state, prevId)) return nodeSelTx(state, prevId);\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\treturn moveTx(state, prevId, getBlockLength(prevBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Block Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the start of the current block. */\nexport function moveToBlockStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === 0) return null;\n\treturn moveTx(state, blockId, 0);\n}\n\n/** Moves the cursor to the end of the current block. */\nexport function moveToBlockEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === blockLen) return null;\n\treturn moveTx(state, blockId, blockLen);\n}\n\n// ---------------------------------------------------------------------------\n// Document Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the beginning of the document. */\nexport function moveToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\tif (isVoidBlock(state, firstId)) return nodeSelTx(state, firstId);\n\n\treturn moveTx(state, firstId, 0);\n}\n\n/** Moves the cursor to the end of the document. */\nexport function moveToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tif (isVoidBlock(state, lastId)) return nodeSelTx(state, lastId);\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\treturn moveTx(state, lastId, getBlockLength(lastBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Extend (Selection Extension) Commands\n// ---------------------------------------------------------------------------\n\n/** Extends the selection one character forward. */\nexport function extendCharacterForward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (head.offset < blockLen) {\n\t\tconst content = getContentAtOffset(block, head.offset);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: nextGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset + step);\n\t}\n\n\t// Cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, head.blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, nextId)) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, nextId, 0);\n}\n\n/** Extends the selection one character backward. */\nexport function extendCharacterBackward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset > 0) {\n\t\tconst block = state.getBlock(head.blockId);\n\t\tif (!block) return null;\n\t\tconst content = getContentAtOffset(block, head.offset - 1);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: prevGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset - step);\n\t}\n\n\t// Cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, head.blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, prevId)) return null;\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, prevId, getBlockLength(prevBlock));\n}\n\n/** Extends the selection to the start of the current block. */\nexport function extendToBlockStart(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset === 0) return null;\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, 0);\n}\n\n/** Extends the selection to the end of the current block. */\nexport function extendToBlockEnd(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tif (head.offset === blockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, blockLen);\n}\n\n/** Extends the selection to the start of the document. */\nexport function extendToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\t// No-op if head is already at document start\n\tif (sel.head.blockId === firstId && sel.head.offset === 0) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, firstId, 0);\n}\n\n/** Extends the selection to the end of the document. */\nexport function extendToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\n\tconst lastBlockLen: number = getBlockLength(lastBlock);\n\n\t// No-op if head is already at document end\n\tif (sel.head.blockId === lastId && sel.head.offset === lastBlockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, lastId, lastBlockLen);\n}\n","/**\n * View-based movement commands that use the browser's `Selection.modify()` API\n * for word, line-boundary, and line granularity movement.\n *\n * Falls back to model-based commands when `Selection.modify` is unavailable\n * (e.g. in happy-dom test environments).\n */\n\nimport { findWordBoundaryBackward, findWordBoundaryForward } from '../commands/Commands.js';\nimport { moveToBlockEnd, moveToBlockStart } from '../commands/MovementCommands.js';\nimport { 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 { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { navigateAcrossBlocks } from './CaretNavigation.js';\nimport { getSelection, readSelectionFromDOM } from './SelectionSync.js';\n\ntype Direction = 'forward' | 'backward';\ntype Granularity = 'word' | 'lineboundary' | 'line';\n\n// ---------------------------------------------------------------------------\n// Core view movement\n// ---------------------------------------------------------------------------\n\n/**\n * Moves the cursor using `Selection.modify('move', direction, granularity)`.\n * Falls back to model-based movement when the API is unavailable.\n */\nexport function viewMove(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackMove(state, direction, granularity);\n\t}\n\n\t// Save original DOM selection\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackMove(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('move', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tconst oldBlockId: BlockId = sel.anchor.blockId;\n\n\t\t// Validate isolating boundary crossing\n\t\tif (newBlockId !== oldBlockId && !canCrossBlockBoundary(state, oldBlockId, newBlockId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newOffset: number = newSel.anchor.offset;\n\n\t\t// InlineNode boundary correction\n\t\tconst block = state.getBlock(newBlockId);\n\t\tif (block) {\n\t\t\tconst content = getContentAtOffset(block, newOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\t// Prefer landing after the InlineNode when moving forward\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewOffset = Math.min(newOffset + 1, getBlockLength(block));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Void block → NodeSelection\n\t\tif (isVoidBlock(state, newBlockId)) return nodeSelTx(state, newBlockId);\n\n\t\treturn moveTx(state, newBlockId, newOffset);\n\t} finally {\n\t\t// Restore original DOM selection (dispatch cycle will set the correct one)\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/**\n * Extends the selection using `Selection.modify('extend', direction, granularity)`.\n * Falls back to model-based extension when the API is unavailable.\n */\nexport function viewExtend(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackExtend(state, direction, granularity);\n\t}\n\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackExtend(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('extend', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\t// Validate isolating boundary crossing for head\n\t\tconst oldHeadBlockId: BlockId = sel.head.blockId;\n\t\tconst newHeadBlockId: BlockId = newSel.head.blockId;\n\n\t\tif (\n\t\t\tnewHeadBlockId !== oldHeadBlockId &&\n\t\t\t!canCrossBlockBoundary(state, oldHeadBlockId, newHeadBlockId)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newHeadOffset: number = newSel.head.offset;\n\n\t\t// InlineNode boundary correction for head position\n\t\tconst headBlock = state.getBlock(newSel.head.blockId);\n\t\tif (headBlock) {\n\t\t\tconst content = getContentAtOffset(headBlock, newHeadOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewHeadOffset = Math.min(newHeadOffset + 1, getBlockLength(headBlock));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn extendTx(\n\t\t\tstate,\n\t\t\tsel.anchor.blockId,\n\t\t\tsel.anchor.offset,\n\t\t\tnewSel.head.blockId,\n\t\t\tnewHeadOffset,\n\t\t);\n\t} finally {\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fallback (model-based) when Selection.modify is unavailable\n// ---------------------------------------------------------------------------\n\nfunction fallbackMove(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tif (granularity === 'word') {\n\t\treturn fallbackWordMove(state, direction);\n\t}\n\tif (granularity === 'lineboundary') {\n\t\treturn direction === 'forward' ? moveToBlockEnd(state) : moveToBlockStart(state);\n\t}\n\t// granularity === 'line' → cross-block\n\tconst caretDir = direction === 'forward' ? 'down' : 'up';\n\treturn navigateAcrossBlocks(state, caretDir);\n}\n\nfunction fallbackExtend(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst head = sel.head;\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tlet newOffset: number = head.offset;\n\n\tif (granularity === 'word') {\n\t\tnewOffset =\n\t\t\tdirection === 'forward'\n\t\t\t\t? findWordBoundaryForward(block, head.offset)\n\t\t\t\t: findWordBoundaryBackward(block, head.offset);\n\t} else if (granularity === 'lineboundary') {\n\t\tnewOffset = direction === 'forward' ? getBlockLength(block) : 0;\n\t} else {\n\t\t// line: extend to next block boundary\n\t\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\t\tconst idx: number = blockOrder.indexOf(head.blockId);\n\t\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\t\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\t\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\t\tif (!targetId) return null;\n\t\tif (!canCrossBlockBoundary(state, head.blockId, targetId)) return null;\n\n\t\tif (isVoidBlock(state, targetId)) {\n\t\t\t// Cannot extend a text selection into a void block\n\t\t\treturn null;\n\t\t}\n\n\t\tconst targetBlock = state.getBlock(targetId);\n\t\tif (!targetBlock) return null;\n\n\t\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, targetId, targetOffset);\n\t}\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, newOffset);\n}\n\nfunction fallbackWordMove(state: EditorState, direction: Direction): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst newOffset: number =\n\t\tdirection === 'forward'\n\t\t\t? findWordBoundaryForward(block, offset)\n\t\t\t: findWordBoundaryBackward(block, offset);\n\n\tif (newOffset === offset) {\n\t\t// At block boundary — try crossing blocks\n\t\treturn fallbackCrossBlock(state, blockId, direction);\n\t}\n\n\treturn moveTx(state, blockId, newOffset);\n}\n\n/**\n * Crosses a block boundary from `blockId` in the given direction.\n * Checks `canCrossBlockBoundary` and handles void blocks (→ NodeSelection).\n */\nfunction fallbackCrossBlock(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tdirection: Direction,\n): Transaction | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(blockId);\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 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\tif (!canCrossBlockBoundary(state, blockId, targetId)) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one word forward. */\nexport function moveWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'word');\n}\n\n/** Moves the cursor one word backward. */\nexport function moveWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'word');\n}\n\n/** Moves the cursor to the start of the line (visual). */\nexport function moveToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'lineboundary');\n}\n\n/** Moves the cursor to the end of the line (visual). */\nexport function moveToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'lineboundary');\n}\n\n/** Moves the cursor one visual line up. */\nexport function moveLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'line');\n}\n\n/** Moves the cursor one visual line down. */\nexport function moveLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'line');\n}\n\n/** Extends the selection one word forward. */\nexport function extendWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'word');\n}\n\n/** Extends the selection one word backward. */\nexport function extendWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'word');\n}\n\n/** Extends the selection to the start of the line. */\nexport function extendToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'lineboundary');\n}\n\n/** Extends the selection to the end of the line. */\nexport function extendToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'lineboundary');\n}\n\n/** Extends the selection one visual line up. */\nexport function extendLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'line');\n}\n\n/** Extends the selection one visual line down. */\nexport function extendLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'line');\n}\n","/**\n * Announcer: builds screen-reader announcement text for editor state changes.\n * Pure functions — no DOM mutation. The caller applies the result to a live region.\n */\n\nimport { isMarkActive } from '../commands/Commands.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nconst BLOCK_TYPE_LABELS: Record<string, string> = {\n\tparagraph: 'Paragraph',\n\theading: 'Heading',\n\tcode_block: 'Code Block',\n\tblockquote: 'Block Quote',\n\tlist_item: 'List Item',\n\thorizontal_rule: 'Horizontal Rule',\n\timage: 'Image',\n\ttable: 'Table',\n};\n\n/** Returns a human-readable label for a block type (used in screen reader announcements). */\nexport function getBlockTypeLabel(typeName: string, attrs?: Record<string, unknown>): string {\n\tif (typeName === 'heading' && attrs?.level) {\n\t\treturn `Heading ${attrs.level}`;\n\t}\n\treturn BLOCK_TYPE_LABELS[typeName] ?? typeName;\n}\n\n/**\n * Derives an announcement string from a state transition.\n * Returns `null` when no announcement is warranted.\n *\n * Priority order: undo/redo > block-type change > mark toggle.\n */\nexport function buildAnnouncement(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n): string | null {\n\t// Priority 1: Undo/Redo\n\tif (tr.metadata.historyDirection === 'undo') return 'Undo';\n\tif (tr.metadata.historyDirection === 'redo') return 'Redo';\n\n\t// Priority 2: Block type changes\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setBlockType') {\n\t\t\treturn getBlockTypeLabel(step.nodeType, step.attrs);\n\t\t}\n\t}\n\n\t// Priority 3: Mark changes\n\tfor (const mt of newState.schema.markTypes) {\n\t\tconst branded = markType(mt);\n\t\tconst wasActive: boolean = isMarkActive(oldState, branded);\n\t\tconst nowActive: boolean = isMarkActive(newState, branded);\n\t\tif (wasActive !== nowActive) {\n\t\t\treturn `${mt} ${nowActive ? 'on' : 'off'}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n","/**\n * CaretNavigationPlugin: registers platform-aware keymaps for all\n * programmatic movement commands (character, word, line, document).\n *\n * Movement priorities use 'navigation' to match GapCursorPlugin.\n * Also announces block-type changes to screen readers on cross-block navigation.\n */\n\nimport {\n\textendCharacterBackward,\n\textendCharacterForward,\n\textendToDocumentEnd,\n\textendToDocumentStart,\n\tmoveToDocumentEnd,\n\tmoveToDocumentStart,\n} from '../../commands/MovementCommands.js';\nimport { getBlockTypeLabel } from '../../editor/Announcer.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport { getTextDirection, isMac } from '../../view/Platform.js';\nimport {\n\textendLineDown,\n\textendLineUp,\n\textendToLineEnd,\n\textendToLineStart,\n\textendWordBackward,\n\textendWordForward,\n\tmoveLineDown,\n\tmoveLineUp,\n\tmoveToLineEnd,\n\tmoveToLineStart,\n\tmoveWordBackward,\n\tmoveWordForward,\n} from '../../view/ViewMovementCommands.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\n\ntype ViewCommandFn = (container: HTMLElement, state: EditorState) => Transaction | null;\ntype ViewCommandKey = `${'word' | 'lineboundary' | 'line'}:${'forward' | 'backward'}`;\ntype ViewCommandLookup = Readonly<Record<ViewCommandKey, ViewCommandFn>>;\n\nconst VIEW_MOVE_FNS: ViewCommandLookup = {\n\t'word:forward': moveWordForward,\n\t'word:backward': moveWordBackward,\n\t'lineboundary:forward': moveToLineEnd,\n\t'lineboundary:backward': moveToLineStart,\n\t'line:forward': moveLineDown,\n\t'line:backward': moveLineUp,\n};\n\nconst VIEW_EXTEND_FNS: ViewCommandLookup = {\n\t'word:forward': extendWordForward,\n\t'word:backward': extendWordBackward,\n\t'lineboundary:forward': extendToLineEnd,\n\t'lineboundary:backward': extendToLineStart,\n\t'line:forward': extendLineDown,\n\t'line:backward': extendLineUp,\n};\n\n/** Debounce delay for block-type announcements (ms). */\nconst ANNOUNCE_DEBOUNCE_MS = 150;\n\nexport class CaretNavigationPlugin implements Plugin {\n\treadonly id = 'caret-navigation';\n\treadonly name = 'Caret Navigation';\n\treadonly priority = 4;\n\n\tprivate context: PluginContext | null = null;\n\tprivate lastAnnouncedBlockId: BlockId | null = null;\n\tprivate announceTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate announce: ((text: string) => void) | null = null;\n\n\tinit(context: PluginContext): void {\n\t\tthis.context = context;\n\t\tthis.announce = (text: string) => context.announce(text);\n\t\tconst mac: boolean = isMac();\n\n\t\tconst keymap: Record<string, () => boolean> = {\n\t\t\t// --- Character movement (no modifier) ---\n\t\t\t// Handled natively by browser for basic cases, but Shift+Arrow\n\t\t\t// needs programmatic extend to correctly clear storedMarks.\n\n\t\t\t// --- Character extend ---\n\t\t\t'Shift-ArrowRight': () => this.extendCharacterVisual('right'),\n\t\t\t'Shift-ArrowLeft': () => this.extendCharacterVisual('left'),\n\n\t\t\t// --- Line extend ---\n\t\t\t'Shift-ArrowUp': () => this.viewExtendCommand('backward', 'line'),\n\t\t\t'Shift-ArrowDown': () => this.viewExtendCommand('forward', 'line'),\n\n\t\t\t// --- Word movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Alt-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Alt-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Line boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'lineboundary'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tHome: () => this.viewMoveCommand('backward', 'lineboundary'),\n\t\t\t\t\t\tEnd: () => this.viewMoveCommand('forward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-Home': () => this.viewExtendCommand('backward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-End': () => this.viewExtendCommand('forward', 'lineboundary'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Document boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowUp': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-ArrowDown': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-ArrowUp': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-ArrowDown': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-Home': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-End': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-Home': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-End': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}),\n\t\t};\n\n\t\tcontext.registerKeymap(keymap, { priority: 'navigation' });\n\t}\n\n\t/** Announces block-type on cross-block cursor movement (debounced). */\n\tonStateChange(_oldState: EditorState, newState: EditorState, _tr: Transaction): void {\n\t\tconst newSel = newState.selection;\n\n\t\t// Only announce for collapsed text selections\n\t\tif (!isCollapsed(newSel) || isNodeSelection(newSel) || isGapCursor(newSel)) {\n\t\t\tif (this.announceTimer !== null) {\n\t\t\t\tclearTimeout(this.announceTimer);\n\t\t\t\tthis.announceTimer = null;\n\t\t\t}\n\t\t\tthis.lastAnnouncedBlockId = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tif (newBlockId === this.lastAnnouncedBlockId) return;\n\n\t\t// First observed block (initial focus) — remember but don't announce\n\t\tif (this.lastAnnouncedBlockId === null) {\n\t\t\tthis.lastAnnouncedBlockId = newBlockId;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.lastAnnouncedBlockId = newBlockId;\n\n\t\t// Debounce rapid navigation\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = setTimeout(() => {\n\t\t\tthis.announceTimer = null;\n\t\t\t// Skip if another plugin already announced (e.g. CodeBlockPlugin's \"Left code block\")\n\t\t\tif (this.context?.hasAnnouncement()) return;\n\t\t\tconst block = newState.getBlock(newBlockId);\n\t\t\tif (!block) return;\n\t\t\tconst label: string = getBlockTypeLabel(\n\t\t\t\tblock.type,\n\t\t\t\tblock.attrs as Record<string, unknown> | undefined,\n\t\t\t);\n\t\t\tthis.announce?.(label);\n\t\t}, ANNOUNCE_DEBOUNCE_MS);\n\t}\n\n\tdestroy(): void {\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = null;\n\t\tthis.announce = null;\n\t\tthis.context = null;\n\t}\n\n\t/** Dispatches a model-based movement command (no DOM needed). */\n\tprivate modelCommand(fn: (state: EditorState) => Transaction | null): boolean {\n\t\tif (!this.context) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Extends selection by one character based on visual left/right intent. */\n\tprivate extendCharacterVisual(visual: 'left' | 'right'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst state: EditorState = this.context.getState();\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(state, visual);\n\t\tconst tr: Transaction | null =\n\t\t\tdir === 'forward' ? extendCharacterForward(state) : extendCharacterBackward(state);\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based movement command (uses container + Selection.modify). */\n\tprivate viewMoveCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_MOVE_FNS, direction, granularity);\n\t}\n\n\t/** Dispatches a view-based move command with visual direction mapping for RTL. */\n\tprivate viewMoveVisual(visual: 'left' | 'right', granularity: 'word' | 'lineboundary'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewMoveCommand(dir, granularity);\n\t}\n\n\t/** Dispatches a view-based extend command. */\n\tprivate viewExtendCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_EXTEND_FNS, direction, granularity);\n\t}\n\n\tprivate dispatchViewCommand(\n\t\tlookup: ViewCommandLookup,\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst fn: ViewCommandFn | undefined = lookup[`${granularity}:${direction}`];\n\t\tif (!fn) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getContainer(), this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based extend command with visual direction mapping for RTL. */\n\tprivate viewExtendVisual(\n\t\tvisual: 'left' | 'right',\n\t\tgranularity: 'word' | 'lineboundary',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewExtendCommand(dir, granularity);\n\t}\n\n\t/**\n\t * Resolves logical movement direction from visual left/right intent.\n\t * In RTL blocks, visual directions are mirrored in offset space.\n\t */\n\tprivate resolveLogicalHorizontalDirection(\n\t\tstate: EditorState,\n\t\tvisual: 'left' | 'right',\n\t): 'forward' | 'backward' {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel) || isGapCursor(sel)) {\n\t\t\treturn visual === 'left' ? 'backward' : 'forward';\n\t\t}\n\t\tconst container: HTMLElement | undefined = this.context?.getContainer();\n\t\tif (!container) return visual === 'left' ? 'backward' : 'forward';\n\t\tconst blockEl: Element | null = container.querySelector(\n\t\t\t`[data-block-id=\"${sel.head.blockId}\"]`,\n\t\t);\n\t\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\t\tif (!isRtl) return visual === 'left' ? 'backward' : 'forward';\n\t\treturn visual === 'left' ? 'forward' : 'backward';\n\t}\n}\n"],"names":["SEGMENTER","Intl","Segmenter","granularity","nextGraphemeSize","text","offset","length","segment","index","prevGraphemeSize","lastSize","findWordBoundaryBackward","block","pos","content","getContentAtOffset","kind","test","char","findWordBoundaryForward","len","getBlockLength","resolveHead","state","sel","selection","isNodeSelection","isGapCursor","blockId","head","adjacentBlockId","currentBlockId","direction","blockOrder","getBlockOrder","idx","indexOf","targetIdx","moveCharacterForward","isCollapsed","moveTx","anchor","getBlock","step","getBlockText","blockOffsetToTextOffset","nextId","canCrossBlockBoundary","isVoidBlock","nodeSelTx","moveCharacterBackward","prevId","prevBlock","moveToBlockStart","moveToBlockEnd","blockLen","moveToDocumentStart","firstId","moveToDocumentEnd","lastId","lastBlock","extendCharacterForward","extendTx","extendCharacterBackward","extendToBlockStart","extendToBlockEnd","extendToDocumentStart","extendToDocumentEnd","lastBlockLen","viewMove","container","domSel","getSelection","modify","fallbackMove","origAnchor","anchorNode","origAnchorOff","anchorOffset","origFocus","focusNode","origFocusOff","focusOffset","newSel","readSelectionFromDOM","newBlockId","oldBlockId","newOffset","Math","min","setBaseAndExtent","viewExtend","fallbackExtend","oldHeadBlockId","newHeadBlockId","newHeadOffset","headBlock","targetId","targetBlock","targetOffset","navigateAcrossBlocks","moveWordForward","moveWordBackward","moveToLineStart","moveToLineEnd","moveLineUp","moveLineDown","extendWordForward","extendWordBackward","extendToLineStart","extendToLineEnd","extendLineUp","extendLineDown","BLOCK_TYPE_LABELS","paragraph","heading","code_block","blockquote","list_item","horizontal_rule","image","table","getBlockTypeLabel","typeName","attrs","level","buildAnnouncement","oldState","newState","tr","metadata","historyDirection","steps","type","nodeType","mt","schema","markTypes","branded","markType","wasActive","isMarkActive","nowActive","VIEW_MOVE_FNS","VIEW_EXTEND_FNS","CaretNavigationPlugin","id","name","priority","context","lastAnnouncedBlockId","announceTimer","announce","this","mac","isMac","keymap","extendCharacterVisual","viewExtendCommand","viewMoveVisual","viewExtendVisual","Home","viewMoveCommand","End","modelCommand","registerKeymap","_oldState","_tr","clearTimeout","setTimeout","hasAnnouncement","label","destroy","fn","getState","dispatch","visual","resolveLogicalHorizontalDirection","dispatchViewCommand","dir","lookup","getContainer","blockEl","querySelector","HTMLElement","getTextDirection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-DmuuFS5D.mjs","sources":["../../src/model/GraphemeUtils.ts","../../src/commands/WordBoundary.ts","../../src/commands/MovementCommands.ts","../../src/view/ViewMovementCommands.ts","../../src/editor/Announcer.ts","../../src/plugins/caret-navigation/CaretNavigationPlugin.ts"],"sourcesContent":["/**\n * Grapheme-cluster utilities for correct character navigation.\n *\n * Uses `Intl.Segmenter` (with `granularity: 'grapheme'`) to correctly\n * handle multi-code-unit characters such as emoji, ZWJ sequences, and\n * combining characters. Falls back to +1/-1 when `Intl.Segmenter` is\n * not available.\n */\n\nconst SEGMENTER_SUPPORTED: boolean = typeof Intl !== 'undefined' && 'Segmenter' in Intl;\nconst SEGMENTER: Intl.Segmenter | null = SEGMENTER_SUPPORTED\n\t? new Intl.Segmenter(undefined, { granularity: 'grapheme' })\n\t: null;\n\n/**\n * Returns the number of UTF-16 code units in the next grapheme cluster\n * starting at `offset`. Returns 0 if `offset` is at or past the end.\n */\nexport function nextGraphemeSize(text: string, offset: number): number {\n\tif (offset >= text.length || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index >= offset) {\n\t\t\t\treturn segment.segment.length;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n/**\n * Returns the number of UTF-16 code units in the grapheme cluster\n * immediately before `offset`. Returns 0 if `offset` is 0 or the\n * string is empty.\n */\nexport function prevGraphemeSize(text: string, offset: number): number {\n\tif (offset <= 0 || text.length === 0) return 0;\n\n\tif (SEGMENTER) {\n\t\tlet lastSize = 1;\n\t\tfor (const segment of SEGMENTER.segment(text)) {\n\t\t\tif (segment.index + segment.segment.length >= offset) {\n\t\t\t\tlastSize = segment.segment.length;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlastSize = segment.segment.length;\n\t\t}\n\t\treturn lastSize;\n\t}\n\n\treturn 1;\n}\n","/**\n * Word boundary detection for delete-word and word-movement commands.\n * InlineNodes act as word boundaries.\n */\n\nimport { type BlockNode, getBlockLength, getContentAtOffset } from '../model/Document.js';\n\n/**\n * Finds the word boundary backward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryBackward(block: BlockNode, offset: number): number {\n\tlet pos = offset - 1;\n\t// Skip trailing whitespace\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\t// If at InlineNode, delete just it (treat as word boundary)\n\tif (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos;\n\t}\n\t// Skip word characters until whitespace or InlineNode\n\twhile (pos >= 0) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos--;\n\t}\n\treturn pos + 1;\n}\n\n/**\n * Finds the word boundary forward from the given offset.\n * InlineNodes act as word boundaries.\n */\nexport function findWordBoundaryForward(block: BlockNode, offset: number): number {\n\tconst len = getBlockLength(block);\n\tlet pos = offset;\n\t// Skip word characters first\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\t// If at InlineNode and haven't moved, delete just the InlineNode\n\tif (pos === offset && pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (content?.kind === 'inline') return pos + 1;\n\t}\n\t// Skip trailing whitespace\n\twhile (pos < len) {\n\t\tconst content = getContentAtOffset(block, pos);\n\t\tif (!content || content.kind === 'inline') break;\n\t\tif (!/\\s/.test(content.char)) break;\n\t\tpos++;\n\t}\n\treturn pos;\n}\n","/**\n * Model-based movement commands for programmatic cursor navigation.\n *\n * All functions are pure: they take an EditorState and return a Transaction\n * or `null` (when the movement is not applicable). None touch the DOM.\n *\n * Move commands produce a collapsed cursor. Extend commands keep the anchor\n * and move the head, producing a range selection. All clear storedMarks.\n */\n\nimport {\n\tblockOffsetToTextOffset,\n\tgetBlockLength,\n\tgetBlockText,\n\tgetContentAtOffset,\n} from '../model/Document.js';\nimport { nextGraphemeSize, prevGraphemeSize } from '../model/GraphemeUtils.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 { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Resolves the effective head position from any text selection. */\nfunction resolveHead(state: EditorState): { blockId: BlockId; offset: number } | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\treturn { blockId: sel.head.blockId, offset: sel.head.offset };\n}\n\n/** Returns the adjacent block ID in the given direction, or `null`. */\nfunction adjacentBlockId(\n\tstate: EditorState,\n\tcurrentBlockId: BlockId,\n\tdirection: 'forward' | 'backward',\n): BlockId | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(currentBlockId);\n\tif (idx < 0) return null;\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\treturn blockOrder[targetIdx] ?? null;\n}\n\n// ---------------------------------------------------------------------------\n// Character Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one character forward, skipping InlineNodes atomically. */\nexport function moveCharacterForward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\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\tconst blockLen: number = getBlockLength(block);\n\n\tif (offset < blockLen) {\n\t\t// InlineNode: skip atomically (+1)\n\t\tconst content = getContentAtOffset(block, offset);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset + 1);\n\t\t}\n\t\t// Text: advance by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = nextGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset + step);\n\t}\n\n\t// At block end → cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, nextId)) return null;\n\n\tif (isVoidBlock(state, nextId)) return nodeSelTx(state, nextId);\n\n\treturn moveTx(state, nextId, 0);\n}\n\n/** Moves the cursor one character backward, skipping InlineNodes atomically. */\nexport function moveCharacterBackward(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\t// If range selection, collapse to head\n\tif (!isCollapsed(sel)) {\n\t\treturn moveTx(state, sel.head.blockId, sel.head.offset);\n\t}\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\tif (offset > 0) {\n\t\t// InlineNode: skip atomically (-1)\n\t\tconst content = getContentAtOffset(block, offset - 1);\n\t\tif (content?.kind === 'inline') {\n\t\t\treturn moveTx(state, blockId, offset - 1);\n\t\t}\n\t\t// Text: retreat by one grapheme cluster\n\t\tconst text: string = getBlockText(block);\n\t\tconst textOffset: number = blockOffsetToTextOffset(block, offset);\n\t\tconst step: number = prevGraphemeSize(text, textOffset) || 1;\n\t\treturn moveTx(state, blockId, offset - step);\n\t}\n\n\t// At block start → cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, blockId, prevId)) return null;\n\n\tif (isVoidBlock(state, prevId)) return nodeSelTx(state, prevId);\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\treturn moveTx(state, prevId, getBlockLength(prevBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Block Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the start of the current block. */\nexport function moveToBlockStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === 0) return null;\n\treturn moveTx(state, blockId, 0);\n}\n\n/** Moves the cursor to the end of the current block. */\nexport function moveToBlockEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\n\tif (offset === blockLen) return null;\n\treturn moveTx(state, blockId, blockLen);\n}\n\n// ---------------------------------------------------------------------------\n// Document Boundary Movement\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor to the beginning of the document. */\nexport function moveToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\tif (isVoidBlock(state, firstId)) return nodeSelTx(state, firstId);\n\n\treturn moveTx(state, firstId, 0);\n}\n\n/** Moves the cursor to the end of the document. */\nexport function moveToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tif (isVoidBlock(state, lastId)) return nodeSelTx(state, lastId);\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\treturn moveTx(state, lastId, getBlockLength(lastBlock));\n}\n\n// ---------------------------------------------------------------------------\n// Extend (Selection Extension) Commands\n// ---------------------------------------------------------------------------\n\n/** Extends the selection one character forward. */\nexport function extendCharacterForward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\n\tif (head.offset < blockLen) {\n\t\tconst content = getContentAtOffset(block, head.offset);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: nextGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset + step);\n\t}\n\n\t// Cross-block\n\tconst nextId: BlockId | null = adjacentBlockId(state, head.blockId, 'forward');\n\tif (!nextId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, nextId)) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, nextId, 0);\n}\n\n/** Extends the selection one character backward. */\nexport function extendCharacterBackward(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset > 0) {\n\t\tconst block = state.getBlock(head.blockId);\n\t\tif (!block) return null;\n\t\tconst content = getContentAtOffset(block, head.offset - 1);\n\t\tconst step: number =\n\t\t\tcontent?.kind === 'inline'\n\t\t\t\t? 1\n\t\t\t\t: prevGraphemeSize(getBlockText(block), blockOffsetToTextOffset(block, head.offset)) || 1;\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, head.offset - step);\n\t}\n\n\t// Cross-block\n\tconst prevId: BlockId | null = adjacentBlockId(state, head.blockId, 'backward');\n\tif (!prevId) return null;\n\tif (!canCrossBlockBoundary(state, head.blockId, prevId)) return null;\n\n\tconst prevBlock = state.getBlock(prevId);\n\tif (!prevBlock) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, prevId, getBlockLength(prevBlock));\n}\n\n/** Extends the selection to the start of the current block. */\nexport function extendToBlockStart(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tif (head.offset === 0) return null;\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, 0);\n}\n\n/** Extends the selection to the end of the current block. */\nexport function extendToBlockEnd(state: EditorState): Transaction | null {\n\tconst head = resolveHead(state);\n\tif (!head) return null;\n\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tconst blockLen: number = getBlockLength(block);\n\tif (head.offset === blockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, blockLen);\n}\n\n/** Extends the selection to the start of the document. */\nexport function extendToDocumentStart(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst firstId: BlockId | undefined = blockOrder[0];\n\tif (!firstId) return null;\n\n\t// No-op if head is already at document start\n\tif (sel.head.blockId === firstId && sel.head.offset === 0) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, firstId, 0);\n}\n\n/** Extends the selection to the end of the document. */\nexport function extendToDocumentEnd(state: EditorState): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst lastId: BlockId | undefined = blockOrder[blockOrder.length - 1];\n\tif (!lastId) return null;\n\n\tconst lastBlock = state.getBlock(lastId);\n\tif (!lastBlock) return null;\n\n\tconst lastBlockLen: number = getBlockLength(lastBlock);\n\n\t// No-op if head is already at document end\n\tif (sel.head.blockId === lastId && sel.head.offset === lastBlockLen) return null;\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, lastId, lastBlockLen);\n}\n","/**\n * View-based movement commands that use the browser's `Selection.modify()` API\n * for word, line-boundary, and line granularity movement.\n *\n * Falls back to model-based commands when `Selection.modify` is unavailable\n * (e.g. in happy-dom test environments).\n */\n\nimport { findWordBoundaryBackward, findWordBoundaryForward } from '../commands/Commands.js';\nimport { moveToBlockEnd, moveToBlockStart } from '../commands/MovementCommands.js';\nimport { 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 { extendTx, moveTx, nodeSelTx } from '../state/SelectionTransactions.js';\nimport type { Transaction } from '../state/Transaction.js';\nimport { navigateAcrossBlocks } from './CaretNavigation.js';\nimport { getSelection, readSelectionFromDOM } from './SelectionSync.js';\n\ntype Direction = 'forward' | 'backward';\ntype Granularity = 'word' | 'lineboundary' | 'line';\n\n// ---------------------------------------------------------------------------\n// Core view movement\n// ---------------------------------------------------------------------------\n\n/**\n * Moves the cursor using `Selection.modify('move', direction, granularity)`.\n * Falls back to model-based movement when the API is unavailable.\n */\nexport function viewMove(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackMove(state, direction, granularity);\n\t}\n\n\t// Save original DOM selection\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackMove(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('move', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tconst oldBlockId: BlockId = sel.anchor.blockId;\n\n\t\t// Validate isolating boundary crossing\n\t\tif (newBlockId !== oldBlockId && !canCrossBlockBoundary(state, oldBlockId, newBlockId)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newOffset: number = newSel.anchor.offset;\n\n\t\t// InlineNode boundary correction\n\t\tconst block = state.getBlock(newBlockId);\n\t\tif (block) {\n\t\t\tconst content = getContentAtOffset(block, newOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\t// Prefer landing after the InlineNode when moving forward\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewOffset = Math.min(newOffset + 1, getBlockLength(block));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Void block → NodeSelection\n\t\tif (isVoidBlock(state, newBlockId)) return nodeSelTx(state, newBlockId);\n\n\t\treturn moveTx(state, newBlockId, newOffset);\n\t} finally {\n\t\t// Restore original DOM selection (dispatch cycle will set the correct one)\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail if DOM changed\n\t\t}\n\t}\n}\n\n/**\n * Extends the selection using `Selection.modify('extend', direction, granularity)`.\n * Falls back to model-based extension when the API is unavailable.\n */\nexport function viewExtend(\n\tcontainer: HTMLElement,\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst domSel: globalThis.Selection | null = getSelection(container);\n\tif (!domSel?.modify) {\n\t\treturn fallbackExtend(state, direction, granularity);\n\t}\n\n\tconst origAnchor: Node | null = domSel.anchorNode;\n\tconst origAnchorOff: number = domSel.anchorOffset;\n\tconst origFocus: Node | null = domSel.focusNode;\n\tconst origFocusOff: number = domSel.focusOffset;\n\tif (!origAnchor || !origFocus) return fallbackExtend(state, direction, granularity);\n\n\ttry {\n\t\tdomSel.modify('extend', direction, granularity);\n\n\t\tconst newSel = readSelectionFromDOM(container);\n\t\tif (!newSel) return null;\n\n\t\t// Validate isolating boundary crossing for head\n\t\tconst oldHeadBlockId: BlockId = sel.head.blockId;\n\t\tconst newHeadBlockId: BlockId = newSel.head.blockId;\n\n\t\tif (\n\t\t\tnewHeadBlockId !== oldHeadBlockId &&\n\t\t\t!canCrossBlockBoundary(state, oldHeadBlockId, newHeadBlockId)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet newHeadOffset: number = newSel.head.offset;\n\n\t\t// InlineNode boundary correction for head position\n\t\tconst headBlock = state.getBlock(newSel.head.blockId);\n\t\tif (headBlock) {\n\t\t\tconst content = getContentAtOffset(headBlock, newHeadOffset);\n\t\t\tif (content?.kind === 'inline') {\n\t\t\t\tif (direction === 'forward') {\n\t\t\t\t\tnewHeadOffset = Math.min(newHeadOffset + 1, getBlockLength(headBlock));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn extendTx(\n\t\t\tstate,\n\t\t\tsel.anchor.blockId,\n\t\t\tsel.anchor.offset,\n\t\t\tnewSel.head.blockId,\n\t\t\tnewHeadOffset,\n\t\t);\n\t} finally {\n\t\ttry {\n\t\t\tdomSel.setBaseAndExtent(origAnchor, origAnchorOff, origFocus, origFocusOff);\n\t\t} catch {\n\t\t\t// Restore may fail\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fallback (model-based) when Selection.modify is unavailable\n// ---------------------------------------------------------------------------\n\nfunction fallbackMove(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tif (granularity === 'word') {\n\t\treturn fallbackWordMove(state, direction);\n\t}\n\tif (granularity === 'lineboundary') {\n\t\treturn direction === 'forward' ? moveToBlockEnd(state) : moveToBlockStart(state);\n\t}\n\t// granularity === 'line' → cross-block\n\tconst caretDir = direction === 'forward' ? 'down' : 'up';\n\treturn navigateAcrossBlocks(state, caretDir);\n}\n\nfunction fallbackExtend(\n\tstate: EditorState,\n\tdirection: Direction,\n\tgranularity: Granularity,\n): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst head = sel.head;\n\tconst block = state.getBlock(head.blockId);\n\tif (!block) return null;\n\n\tlet newOffset: number = head.offset;\n\n\tif (granularity === 'word') {\n\t\tnewOffset =\n\t\t\tdirection === 'forward'\n\t\t\t\t? findWordBoundaryForward(block, head.offset)\n\t\t\t\t: findWordBoundaryBackward(block, head.offset);\n\t} else if (granularity === 'lineboundary') {\n\t\tnewOffset = direction === 'forward' ? getBlockLength(block) : 0;\n\t} else {\n\t\t// line: extend to next block boundary\n\t\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\t\tconst idx: number = blockOrder.indexOf(head.blockId);\n\t\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 1;\n\n\t\tif (targetIdx < 0 || targetIdx >= blockOrder.length) return null;\n\n\t\tconst targetId: BlockId | undefined = blockOrder[targetIdx];\n\t\tif (!targetId) return null;\n\t\tif (!canCrossBlockBoundary(state, head.blockId, targetId)) return null;\n\n\t\tif (isVoidBlock(state, targetId)) {\n\t\t\t// Cannot extend a text selection into a void block\n\t\t\treturn null;\n\t\t}\n\n\t\tconst targetBlock = state.getBlock(targetId);\n\t\tif (!targetBlock) return null;\n\n\t\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\t\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, targetId, targetOffset);\n\t}\n\n\treturn extendTx(state, sel.anchor.blockId, sel.anchor.offset, head.blockId, newOffset);\n}\n\nfunction fallbackWordMove(state: EditorState, direction: Direction): Transaction | null {\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\n\tconst blockId: BlockId = isCollapsed(sel) ? sel.anchor.blockId : sel.head.blockId;\n\tconst offset: number = isCollapsed(sel) ? sel.anchor.offset : sel.head.offset;\n\tconst block = state.getBlock(blockId);\n\tif (!block) return null;\n\n\tconst newOffset: number =\n\t\tdirection === 'forward'\n\t\t\t? findWordBoundaryForward(block, offset)\n\t\t\t: findWordBoundaryBackward(block, offset);\n\n\tif (newOffset === offset) {\n\t\t// At block boundary — try crossing blocks\n\t\treturn fallbackCrossBlock(state, blockId, direction);\n\t}\n\n\treturn moveTx(state, blockId, newOffset);\n}\n\n/**\n * Crosses a block boundary from `blockId` in the given direction.\n * Checks `canCrossBlockBoundary` and handles void blocks (→ NodeSelection).\n */\nfunction fallbackCrossBlock(\n\tstate: EditorState,\n\tblockId: BlockId,\n\tdirection: Direction,\n): Transaction | null {\n\tconst blockOrder: readonly BlockId[] = state.getBlockOrder();\n\tconst idx: number = blockOrder.indexOf(blockId);\n\tconst targetIdx: number = direction === 'forward' ? idx + 1 : idx - 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\tif (!canCrossBlockBoundary(state, blockId, targetId)) return null;\n\n\tif (isVoidBlock(state, targetId)) return nodeSelTx(state, targetId);\n\n\tconst targetBlock = state.getBlock(targetId);\n\tif (!targetBlock) return null;\n\n\tconst targetOffset: number = direction === 'forward' ? 0 : getBlockLength(targetBlock);\n\treturn moveTx(state, targetId, targetOffset);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers\n// ---------------------------------------------------------------------------\n\n/** Moves the cursor one word forward. */\nexport function moveWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'word');\n}\n\n/** Moves the cursor one word backward. */\nexport function moveWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'word');\n}\n\n/** Moves the cursor to the start of the line (visual). */\nexport function moveToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'lineboundary');\n}\n\n/** Moves the cursor to the end of the line (visual). */\nexport function moveToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'lineboundary');\n}\n\n/** Moves the cursor one visual line up. */\nexport function moveLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'backward', 'line');\n}\n\n/** Moves the cursor one visual line down. */\nexport function moveLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewMove(container, state, 'forward', 'line');\n}\n\n/** Extends the selection one word forward. */\nexport function extendWordForward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'word');\n}\n\n/** Extends the selection one word backward. */\nexport function extendWordBackward(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'word');\n}\n\n/** Extends the selection to the start of the line. */\nexport function extendToLineStart(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'lineboundary');\n}\n\n/** Extends the selection to the end of the line. */\nexport function extendToLineEnd(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'lineboundary');\n}\n\n/** Extends the selection one visual line up. */\nexport function extendLineUp(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'backward', 'line');\n}\n\n/** Extends the selection one visual line down. */\nexport function extendLineDown(container: HTMLElement, state: EditorState): Transaction | null {\n\treturn viewExtend(container, state, 'forward', 'line');\n}\n","/**\n * Announcer: builds screen-reader announcement text for editor state changes.\n * Pure functions — no DOM mutation. The caller applies the result to a live region.\n */\n\nimport { isMarkActive } from '../commands/Commands.js';\nimport { markType } from '../model/TypeBrands.js';\nimport type { EditorState } from '../state/EditorState.js';\nimport type { Transaction } from '../state/Transaction.js';\n\nconst BLOCK_TYPE_LABELS: Record<string, string> = {\n\tparagraph: 'Paragraph',\n\theading: 'Heading',\n\tcode_block: 'Code Block',\n\tblockquote: 'Block Quote',\n\tlist_item: 'List Item',\n\thorizontal_rule: 'Horizontal Rule',\n\timage: 'Image',\n\ttable: 'Table',\n};\n\n/** Returns a human-readable label for a block type (used in screen reader announcements). */\nexport function getBlockTypeLabel(typeName: string, attrs?: Record<string, unknown>): string {\n\tif (typeName === 'heading' && attrs?.level) {\n\t\treturn `Heading ${attrs.level}`;\n\t}\n\treturn BLOCK_TYPE_LABELS[typeName] ?? typeName;\n}\n\n/**\n * Derives an announcement string from a state transition.\n * Returns `null` when no announcement is warranted.\n *\n * Priority order: undo/redo > block-type change > mark toggle.\n */\nexport function buildAnnouncement(\n\toldState: EditorState,\n\tnewState: EditorState,\n\ttr: Transaction,\n): string | null {\n\t// Priority 1: Undo/Redo\n\tif (tr.metadata.historyDirection === 'undo') return 'Undo';\n\tif (tr.metadata.historyDirection === 'redo') return 'Redo';\n\n\t// Priority 2: Block type changes\n\tfor (const step of tr.steps) {\n\t\tif (step.type === 'setBlockType') {\n\t\t\treturn getBlockTypeLabel(step.nodeType, step.attrs);\n\t\t}\n\t}\n\n\t// Priority 3: Mark changes\n\tfor (const mt of newState.schema.markTypes) {\n\t\tconst branded = markType(mt);\n\t\tconst wasActive: boolean = isMarkActive(oldState, branded);\n\t\tconst nowActive: boolean = isMarkActive(newState, branded);\n\t\tif (wasActive !== nowActive) {\n\t\t\treturn `${mt} ${nowActive ? 'on' : 'off'}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n","/**\n * CaretNavigationPlugin: registers platform-aware keymaps for all\n * programmatic movement commands (character, word, line, document).\n *\n * Movement priorities use 'navigation' to match GapCursorPlugin.\n * Also announces block-type changes to screen readers on cross-block navigation.\n */\n\nimport {\n\textendCharacterBackward,\n\textendCharacterForward,\n\textendToDocumentEnd,\n\textendToDocumentStart,\n\tmoveToDocumentEnd,\n\tmoveToDocumentStart,\n} from '../../commands/MovementCommands.js';\nimport { getBlockTypeLabel } from '../../editor/Announcer.js';\nimport { isCollapsed, isGapCursor, isNodeSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport { getTextDirection, isMac } from '../../view/Platform.js';\nimport {\n\textendLineDown,\n\textendLineUp,\n\textendToLineEnd,\n\textendToLineStart,\n\textendWordBackward,\n\textendWordForward,\n\tmoveLineDown,\n\tmoveLineUp,\n\tmoveToLineEnd,\n\tmoveToLineStart,\n\tmoveWordBackward,\n\tmoveWordForward,\n} from '../../view/ViewMovementCommands.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\n\ntype ViewCommandFn = (container: HTMLElement, state: EditorState) => Transaction | null;\ntype ViewCommandKey = `${'word' | 'lineboundary' | 'line'}:${'forward' | 'backward'}`;\ntype ViewCommandLookup = Readonly<Record<ViewCommandKey, ViewCommandFn>>;\n\nconst VIEW_MOVE_FNS: ViewCommandLookup = {\n\t'word:forward': moveWordForward,\n\t'word:backward': moveWordBackward,\n\t'lineboundary:forward': moveToLineEnd,\n\t'lineboundary:backward': moveToLineStart,\n\t'line:forward': moveLineDown,\n\t'line:backward': moveLineUp,\n};\n\nconst VIEW_EXTEND_FNS: ViewCommandLookup = {\n\t'word:forward': extendWordForward,\n\t'word:backward': extendWordBackward,\n\t'lineboundary:forward': extendToLineEnd,\n\t'lineboundary:backward': extendToLineStart,\n\t'line:forward': extendLineDown,\n\t'line:backward': extendLineUp,\n};\n\n/** Debounce delay for block-type announcements (ms). */\nconst ANNOUNCE_DEBOUNCE_MS = 150;\n\nexport class CaretNavigationPlugin implements Plugin {\n\treadonly id = 'caret-navigation';\n\treadonly name = 'Caret Navigation';\n\treadonly priority = 4;\n\n\tprivate context: PluginContext | null = null;\n\tprivate lastAnnouncedBlockId: BlockId | null = null;\n\tprivate announceTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate announce: ((text: string) => void) | null = null;\n\n\tinit(context: PluginContext): void {\n\t\tthis.context = context;\n\t\tthis.announce = (text: string) => context.announce(text);\n\t\tconst mac: boolean = isMac();\n\n\t\tconst keymap: Record<string, () => boolean> = {\n\t\t\t// --- Character movement (no modifier) ---\n\t\t\t// Handled natively by browser for basic cases, but Shift+Arrow\n\t\t\t// needs programmatic extend to correctly clear storedMarks.\n\n\t\t\t// --- Character extend ---\n\t\t\t'Shift-ArrowRight': () => this.extendCharacterVisual('right'),\n\t\t\t'Shift-ArrowLeft': () => this.extendCharacterVisual('left'),\n\n\t\t\t// --- Line extend ---\n\t\t\t'Shift-ArrowUp': () => this.viewExtendCommand('backward', 'line'),\n\t\t\t'Shift-ArrowDown': () => this.viewExtendCommand('forward', 'line'),\n\n\t\t\t// --- Word movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Alt-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Alt-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Shift-Alt-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'word'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'word'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Line boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowLeft': () => this.viewMoveVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-ArrowRight': () => this.viewMoveVisual('right', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowLeft': () => this.viewExtendVisual('left', 'lineboundary'),\n\t\t\t\t\t\t'Mod-Shift-ArrowRight': () => this.viewExtendVisual('right', 'lineboundary'),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tHome: () => this.viewMoveCommand('backward', 'lineboundary'),\n\t\t\t\t\t\tEnd: () => this.viewMoveCommand('forward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-Home': () => this.viewExtendCommand('backward', 'lineboundary'),\n\t\t\t\t\t\t'Shift-End': () => this.viewExtendCommand('forward', 'lineboundary'),\n\t\t\t\t\t}),\n\n\t\t\t// --- Document boundary movement (platform-aware) ---\n\t\t\t...(mac\n\t\t\t\t? {\n\t\t\t\t\t\t'Mod-ArrowUp': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-ArrowDown': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-ArrowUp': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-ArrowDown': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\t'Mod-Home': () => this.modelCommand(moveToDocumentStart),\n\t\t\t\t\t\t'Mod-End': () => this.modelCommand(moveToDocumentEnd),\n\t\t\t\t\t\t'Mod-Shift-Home': () => this.modelCommand(extendToDocumentStart),\n\t\t\t\t\t\t'Mod-Shift-End': () => this.modelCommand(extendToDocumentEnd),\n\t\t\t\t\t}),\n\t\t};\n\n\t\tcontext.registerKeymap(keymap, { priority: 'navigation' });\n\t}\n\n\t/** Announces block-type on cross-block cursor movement (debounced). */\n\tonStateChange(_oldState: EditorState, newState: EditorState, _tr: Transaction): void {\n\t\tconst newSel = newState.selection;\n\n\t\t// Only announce for collapsed text selections\n\t\tif (!isCollapsed(newSel) || isNodeSelection(newSel) || isGapCursor(newSel)) {\n\t\t\tif (this.announceTimer !== null) {\n\t\t\t\tclearTimeout(this.announceTimer);\n\t\t\t\tthis.announceTimer = null;\n\t\t\t}\n\t\t\tthis.lastAnnouncedBlockId = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst newBlockId: BlockId = newSel.anchor.blockId;\n\t\tif (newBlockId === this.lastAnnouncedBlockId) return;\n\n\t\t// First observed block (initial focus) — remember but don't announce\n\t\tif (this.lastAnnouncedBlockId === null) {\n\t\t\tthis.lastAnnouncedBlockId = newBlockId;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.lastAnnouncedBlockId = newBlockId;\n\n\t\t// Debounce rapid navigation\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = setTimeout(() => {\n\t\t\tthis.announceTimer = null;\n\t\t\t// Skip if another plugin already announced (e.g. CodeBlockPlugin's \"Left code block\")\n\t\t\tif (this.context?.hasAnnouncement()) return;\n\t\t\tconst block = newState.getBlock(newBlockId);\n\t\t\tif (!block) return;\n\t\t\tconst label: string = getBlockTypeLabel(\n\t\t\t\tblock.type,\n\t\t\t\tblock.attrs as Record<string, unknown> | undefined,\n\t\t\t);\n\t\t\tthis.announce?.(label);\n\t\t}, ANNOUNCE_DEBOUNCE_MS);\n\t}\n\n\tdestroy(): void {\n\t\tif (this.announceTimer !== null) clearTimeout(this.announceTimer);\n\t\tthis.announceTimer = null;\n\t\tthis.announce = null;\n\t\tthis.context = null;\n\t}\n\n\t/** Dispatches a model-based movement command (no DOM needed). */\n\tprivate modelCommand(fn: (state: EditorState) => Transaction | null): boolean {\n\t\tif (!this.context) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Extends selection by one character based on visual left/right intent. */\n\tprivate extendCharacterVisual(visual: 'left' | 'right'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst state: EditorState = this.context.getState();\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(state, visual);\n\t\tconst tr: Transaction | null =\n\t\t\tdir === 'forward' ? extendCharacterForward(state) : extendCharacterBackward(state);\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based movement command (uses container + Selection.modify). */\n\tprivate viewMoveCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_MOVE_FNS, direction, granularity);\n\t}\n\n\t/** Dispatches a view-based move command with visual direction mapping for RTL. */\n\tprivate viewMoveVisual(visual: 'left' | 'right', granularity: 'word' | 'lineboundary'): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewMoveCommand(dir, granularity);\n\t}\n\n\t/** Dispatches a view-based extend command. */\n\tprivate viewExtendCommand(\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\treturn this.dispatchViewCommand(VIEW_EXTEND_FNS, direction, granularity);\n\t}\n\n\tprivate dispatchViewCommand(\n\t\tlookup: ViewCommandLookup,\n\t\tdirection: 'forward' | 'backward',\n\t\tgranularity: 'word' | 'lineboundary' | 'line',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst fn: ViewCommandFn | undefined = lookup[`${granularity}:${direction}`];\n\t\tif (!fn) return false;\n\t\tconst tr: Transaction | null = fn(this.context.getContainer(), this.context.getState());\n\t\tif (tr) {\n\t\t\tthis.context.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/** Dispatches a view-based extend command with visual direction mapping for RTL. */\n\tprivate viewExtendVisual(\n\t\tvisual: 'left' | 'right',\n\t\tgranularity: 'word' | 'lineboundary',\n\t): boolean {\n\t\tif (!this.context) return false;\n\t\tconst dir: 'forward' | 'backward' = this.resolveLogicalHorizontalDirection(\n\t\t\tthis.context.getState(),\n\t\t\tvisual,\n\t\t);\n\t\treturn this.viewExtendCommand(dir, granularity);\n\t}\n\n\t/**\n\t * Resolves logical movement direction from visual left/right intent.\n\t * In RTL blocks, visual directions are mirrored in offset space.\n\t */\n\tprivate resolveLogicalHorizontalDirection(\n\t\tstate: EditorState,\n\t\tvisual: 'left' | 'right',\n\t): 'forward' | 'backward' {\n\t\tconst sel = state.selection;\n\t\tif (isNodeSelection(sel) || isGapCursor(sel)) {\n\t\t\treturn visual === 'left' ? 'backward' : 'forward';\n\t\t}\n\t\tconst container: HTMLElement | undefined = this.context?.getContainer();\n\t\tif (!container) return visual === 'left' ? 'backward' : 'forward';\n\t\tconst blockEl: Element | null = container.querySelector(\n\t\t\t`[data-block-id=\"${sel.head.blockId}\"]`,\n\t\t);\n\t\tconst isRtl: boolean = blockEl instanceof HTMLElement && getTextDirection(blockEl) === 'rtl';\n\t\tif (!isRtl) return visual === 'left' ? 'backward' : 'forward';\n\t\treturn visual === 'left' ? 'forward' : 'backward';\n\t}\n}\n"],"names":["SEGMENTER","Intl","Segmenter","granularity","nextGraphemeSize","text","offset","length","segment","index","prevGraphemeSize","lastSize","findWordBoundaryBackward","block","pos","content","getContentAtOffset","kind","test","char","findWordBoundaryForward","len","getBlockLength","resolveHead","state","sel","selection","isNodeSelection","isGapCursor","blockId","head","adjacentBlockId","currentBlockId","direction","blockOrder","getBlockOrder","idx","indexOf","targetIdx","moveCharacterForward","isCollapsed","moveTx","anchor","getBlock","step","getBlockText","blockOffsetToTextOffset","nextId","canCrossBlockBoundary","isVoidBlock","nodeSelTx","moveCharacterBackward","prevId","prevBlock","moveToBlockStart","moveToBlockEnd","blockLen","moveToDocumentStart","firstId","moveToDocumentEnd","lastId","lastBlock","extendCharacterForward","extendTx","extendCharacterBackward","extendToBlockStart","extendToBlockEnd","extendToDocumentStart","extendToDocumentEnd","lastBlockLen","viewMove","container","domSel","getSelection","modify","fallbackMove","origAnchor","anchorNode","origAnchorOff","anchorOffset","origFocus","focusNode","origFocusOff","focusOffset","newSel","readSelectionFromDOM","newBlockId","oldBlockId","newOffset","Math","min","setBaseAndExtent","viewExtend","fallbackExtend","oldHeadBlockId","newHeadBlockId","newHeadOffset","headBlock","targetId","targetBlock","targetOffset","navigateAcrossBlocks","moveWordForward","moveWordBackward","moveToLineStart","moveToLineEnd","moveLineUp","moveLineDown","extendWordForward","extendWordBackward","extendToLineStart","extendToLineEnd","extendLineUp","extendLineDown","BLOCK_TYPE_LABELS","paragraph","heading","code_block","blockquote","list_item","horizontal_rule","image","table","getBlockTypeLabel","typeName","attrs","level","buildAnnouncement","oldState","newState","tr","metadata","historyDirection","steps","type","nodeType","mt","schema","markTypes","branded","markType","wasActive","isMarkActive","nowActive","VIEW_MOVE_FNS","VIEW_EXTEND_FNS","CaretNavigationPlugin","id","name","priority","context","lastAnnouncedBlockId","announceTimer","announce","this","mac","isMac","keymap","extendCharacterVisual","viewExtendCommand","viewMoveVisual","viewExtendVisual","Home","viewMoveCommand","End","modelCommand","registerKeymap","_oldState","_tr","clearTimeout","setTimeout","hasAnnouncement","label","destroy","fn","getState","dispatch","visual","resolveLogicalHorizontalDirection","dispatchViewCommand","dir","lookup","getContainer","blockEl","querySelector","HTMLElement","getTextDirection"],"mappings":"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"}
|