@notectl/core 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs → AttrRegistry-D9Jx_RVb.mjs} +2 -2
- package/dist/chunks/{AttrRegistry-Ci0qAvtS.mjs.map → AttrRegistry-D9Jx_RVb.mjs.map} +1 -1
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs +80 -0
- package/dist/chunks/AttributedMarkCommands-BtLea72z.mjs.map +1 -0
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs → CaretNavigation-7JYpIKbs.mjs} +3 -3
- package/dist/chunks/{CaretNavigation-qLfeL0So.mjs.map → CaretNavigation-7JYpIKbs.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-BBkN0AOb.mjs → ColorGrid--lBQ7jCK.mjs} +56 -55
- package/dist/chunks/ColorGrid--lBQ7jCK.mjs.map +1 -0
- package/dist/chunks/{ColorPickerPopup-D3ysvOH-.mjs → ColorPickerPopup-TpIRlXc8.mjs} +15 -15
- package/dist/chunks/ColorPickerPopup-TpIRlXc8.mjs.map +1 -0
- package/dist/chunks/{Document-DfZ1NOjs.mjs → Document-Brtw2jL1.mjs} +6 -6
- package/dist/chunks/Document-Brtw2jL1.mjs.map +1 -0
- package/dist/chunks/{MarkCommands-uybmkw9l.mjs → MarkCommands-6RUJ4L3r.mjs} +3 -3
- package/dist/chunks/MarkCommands-6RUJ4L3r.mjs.map +1 -0
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs → NodeResolver-C2XYkeOi.mjs} +2 -2
- package/dist/chunks/{NodeResolver-CzLmmrph.mjs.map → NodeResolver-C2XYkeOi.mjs.map} +1 -1
- package/dist/chunks/NodeSpec-NZVvcCuH.mjs.map +1 -1
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs +34 -0
- package/dist/chunks/PopupPositioning-CgjgAKjp.mjs.map +1 -0
- package/dist/chunks/RangeIterator-Bh4Vl_6D.mjs +16 -0
- package/dist/chunks/{RangeIterator-Bye0UtbY.mjs.map → RangeIterator-Bh4Vl_6D.mjs.map} +1 -1
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs +235 -0
- package/dist/chunks/StyleRuntime-CP4NCwKT.mjs.map +1 -0
- package/dist/chunks/ToolbarItem-BpZggUGo.mjs.map +1 -1
- package/dist/chunks/{index-Bt_d4IqB.mjs → index--7pBhSF4.mjs} +74 -73
- package/dist/chunks/index--7pBhSF4.mjs.map +1 -0
- package/dist/chunks/{index-DayTn6Re.mjs → index-BXCkjDax.mjs} +78 -83
- package/dist/chunks/index-BXCkjDax.mjs.map +1 -0
- package/dist/chunks/{index-DGKQhSAl.mjs → index-BXtw5PNN.mjs} +2 -2
- package/dist/chunks/{index-DGKQhSAl.mjs.map → index-BXtw5PNN.mjs.map} +1 -1
- package/dist/chunks/{ToolbarPlugin-B7oJRzpl.mjs → index-BcDRT4K2.mjs} +110 -121
- package/dist/chunks/index-BcDRT4K2.mjs.map +1 -0
- package/dist/chunks/{index-DoCvDkfj.mjs → index-BjFBWBOC.mjs} +23 -25
- package/dist/chunks/index-BjFBWBOC.mjs.map +1 -0
- package/dist/chunks/{index-CrKz_IEn.mjs → index-C1opJocO.mjs} +2 -2
- package/dist/chunks/{index-CrKz_IEn.mjs.map → index-C1opJocO.mjs.map} +1 -1
- package/dist/chunks/{index-CfyS_pgH.mjs → index-CCyqQ0Dz.mjs} +32 -31
- package/dist/chunks/index-CCyqQ0Dz.mjs.map +1 -0
- package/dist/chunks/{index-E7OgN7hM.mjs → index-Cd3uJLP3.mjs} +2 -2
- package/dist/chunks/{index-E7OgN7hM.mjs.map → index-Cd3uJLP3.mjs.map} +1 -1
- package/dist/chunks/{index-yhz2qeeh.mjs → index-DSAuGP4n.mjs} +131 -127
- package/dist/chunks/index-DSAuGP4n.mjs.map +1 -0
- package/dist/chunks/{index-CH1r7DL-.mjs → index-DtRfqsq5.mjs} +211 -210
- package/dist/chunks/index-DtRfqsq5.mjs.map +1 -0
- package/dist/chunks/{index-CFPP456m.mjs → index-DxbQirny.mjs} +8 -8
- package/dist/chunks/{index-CFPP456m.mjs.map → index-DxbQirny.mjs.map} +1 -1
- package/dist/chunks/{index-DuRNETFv.mjs → index-ErZ5_a1o.mjs} +3 -3
- package/dist/chunks/{index-DuRNETFv.mjs.map → index-ErZ5_a1o.mjs.map} +1 -1
- package/dist/chunks/{index-Deag_lVi.mjs → index-WpbK_s_H.mjs} +29 -31
- package/dist/chunks/index-WpbK_s_H.mjs.map +1 -0
- package/dist/chunks/{index-DIvHQtE2.mjs → index-WrqH1wG8.mjs} +15 -14
- package/dist/chunks/index-WrqH1wG8.mjs.map +1 -0
- package/dist/chunks/{index-Dy6SllXW.mjs → index-fsVJLutn.mjs} +3 -3
- package/dist/chunks/{index-Dy6SllXW.mjs.map → index-fsVJLutn.mjs.map} +1 -1
- package/dist/chunks/{index-Ckzqtn5D.mjs → index-fx3KIap3.mjs} +15 -14
- package/dist/chunks/index-fx3KIap3.mjs.map +1 -0
- package/dist/chunks/{index-DmuuFS5D.mjs → index-oOJo-ywB.mjs} +4 -4
- package/dist/chunks/{index-DmuuFS5D.mjs.map → index-oOJo-ywB.mjs.map} +1 -1
- package/dist/chunks/{index-CFJFD4lF.mjs → index-toj88jtL.mjs} +19 -18
- package/dist/chunks/index-toj88jtL.mjs.map +1 -0
- package/dist/commands/AttributedMarkCommands.d.ts.map +1 -1
- package/dist/commands/MarkCommands.d.ts +3 -3
- package/dist/commands/MarkCommands.d.ts.map +1 -1
- package/dist/editor/CSSClassCollector.d.ts +36 -0
- package/dist/editor/CSSClassCollector.d.ts.map +1 -0
- package/dist/editor/ContentHTMLTypes.d.ts +29 -0
- package/dist/editor/ContentHTMLTypes.d.ts.map +1 -0
- package/dist/editor/ContentStyleInjector.d.ts +69 -0
- package/dist/editor/ContentStyleInjector.d.ts.map +1 -0
- package/dist/editor/DocumentParser.d.ts +9 -1
- package/dist/editor/DocumentParser.d.ts.map +1 -1
- package/dist/editor/DocumentSerializer.d.ts +6 -0
- package/dist/editor/DocumentSerializer.d.ts.map +1 -1
- package/dist/editor/EditorThemeController.d.ts +3 -0
- package/dist/editor/EditorThemeController.d.ts.map +1 -1
- package/dist/editor/MarkSerializer.d.ts +7 -0
- package/dist/editor/MarkSerializer.d.ts.map +1 -1
- package/dist/editor/NotectlEditor.d.ts +13 -3
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/editor/PaperLayoutController.d.ts.map +1 -1
- package/dist/editor/styles/font-select.d.ts +1 -1
- package/dist/editor/styles/font-select.d.ts.map +1 -1
- package/dist/editor/styles/font-size-select.d.ts +1 -1
- package/dist/editor/styles/font-size-select.d.ts.map +1 -1
- package/dist/editor/styles/heading-select.d.ts +1 -1
- package/dist/editor/styles/heading-select.d.ts.map +1 -1
- package/dist/editor/styles/toolbar.d.ts +1 -1
- package/dist/editor/styles/toolbar.d.ts.map +1 -1
- package/dist/index.d.ts +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/input/LegacyHTMLNormalizer.d.ts.map +1 -1
- package/dist/model/Document.d.ts +0 -4
- package/dist/model/Document.d.ts.map +1 -1
- package/dist/model/NodeSpec.d.ts +19 -2
- package/dist/model/NodeSpec.d.ts.map +1 -1
- package/dist/notectl-core.mjs +613 -477
- package/dist/notectl-core.mjs.map +1 -1
- package/dist/notectl-core.umd.js +1571 -1225
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block/CodeBlockNodeView.d.ts.map +1 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts +0 -1
- package/dist/plugins/code-block/CodeBlockPlugin.d.ts.map +1 -1
- package/dist/plugins/code-block/index.d.ts +2 -1
- package/dist/plugins/code-block/index.d.ts.map +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font/FontPlugin.d.ts +0 -1
- package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePlugin.d.ts +0 -2
- package/dist/plugins/font-size/FontSizePlugin.d.ts.map +1 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts +3 -1
- package/dist/plugins/font-size/FontSizePopup.d.ts.map +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +6 -6
- package/dist/plugins/hard-break.mjs +1 -1
- package/dist/plugins/heading/HeadingBlockTypePicker.d.ts.map +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight/HighlightPlugin.d.ts.map +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image/ImageNodeView.d.ts.map +1 -1
- package/dist/plugins/image/ImagePlugin.d.ts.map +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list/ListPlugin.d.ts.map +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print/PrintContentPreparer.d.ts.map +1 -1
- package/dist/plugins/shared/ColorGrid.d.ts.map +1 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts +2 -1
- package/dist/plugins/shared/ColorPickerPopup.d.ts.map +1 -1
- package/dist/plugins/shared/PopupManager.d.ts +12 -2
- package/dist/plugins/shared/PopupManager.d.ts.map +1 -1
- package/dist/plugins/shared/PopupPositioning.d.ts.map +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table/TableContextMenu.d.ts.map +1 -1
- package/dist/plugins/table/TableControls.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsDOM.d.ts.map +1 -1
- package/dist/plugins/table/TableControlsLayout.d.ts.map +1 -1
- package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
- package/dist/plugins/table/TablePlugin.d.ts.map +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color/TextColorPlugin.d.ts.map +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar/ToolbarItem.d.ts +3 -2
- package/dist/plugins/toolbar/ToolbarItem.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarOverflowController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts +0 -3
- package/dist/plugins/toolbar/ToolbarPopupController.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarRenderers.d.ts.map +1 -1
- package/dist/plugins/toolbar/ToolbarTooltip.d.ts.map +1 -1
- package/dist/plugins/toolbar.mjs +2 -2
- package/dist/presets/PresetTypes.d.ts +1 -1
- package/dist/presets/PresetTypes.d.ts.map +1 -1
- package/dist/style/StyleRuntime.d.ts +34 -0
- package/dist/style/StyleRuntime.d.ts.map +1 -0
- package/dist/view/Reconciler.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs +0 -58
- package/dist/chunks/AttributedMarkCommands-BETuHXj8.mjs.map +0 -1
- package/dist/chunks/ColorGrid-BBkN0AOb.mjs.map +0 -1
- package/dist/chunks/ColorPickerPopup-D3ysvOH-.mjs.map +0 -1
- package/dist/chunks/Document-DfZ1NOjs.mjs.map +0 -1
- package/dist/chunks/MarkCommands-uybmkw9l.mjs.map +0 -1
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs +0 -30
- package/dist/chunks/PopupPositioning-Cn6L_mO2.mjs.map +0 -1
- package/dist/chunks/RangeIterator-Bye0UtbY.mjs +0 -16
- package/dist/chunks/ToolbarPlugin-B7oJRzpl.mjs.map +0 -1
- package/dist/chunks/index-Bt_d4IqB.mjs.map +0 -1
- package/dist/chunks/index-CFJFD4lF.mjs.map +0 -1
- package/dist/chunks/index-CH1r7DL-.mjs.map +0 -1
- package/dist/chunks/index-CfyS_pgH.mjs.map +0 -1
- package/dist/chunks/index-Ckzqtn5D.mjs.map +0 -1
- package/dist/chunks/index-DIvHQtE2.mjs.map +0 -1
- package/dist/chunks/index-DayTn6Re.mjs.map +0 -1
- package/dist/chunks/index-Deag_lVi.mjs.map +0 -1
- package/dist/chunks/index-DoCvDkfj.mjs.map +0 -1
- package/dist/chunks/index-yhz2qeeh.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Ckzqtn5D.mjs","sources":["../../src/plugins/text-color/TextColorLocale.ts","../../src/plugins/text-color/TextColorPlugin.ts"],"sourcesContent":["/** Locale interface and default English locale for the TextColorPlugin. */\n\n// --- Locale Interface ---\n\nexport interface TextColorLocale {\n\treadonly label: string;\n\treadonly tooltip: string;\n\treadonly resetLabel: string;\n\treadonly ariaLabelPrefix: string;\n}\n\n// --- Default English Locale ---\n\nexport const TEXT_COLOR_LOCALE_EN: TextColorLocale = {\n\tlabel: 'Text Color',\n\ttooltip: 'Text Color',\n\tresetLabel: 'Default',\n\tariaLabelPrefix: 'Text color',\n};\n\n// --- German Locale ---\n\nexport const TEXT_COLOR_LOCALE_DE: TextColorLocale = {\n\tlabel: 'Textfarbe',\n\ttooltip: 'Textfarbe',\n\tresetLabel: 'Standard',\n\tariaLabelPrefix: 'Textfarbe',\n};\n\n// --- Spanish Locale ---\n\nexport const TEXT_COLOR_LOCALE_ES: TextColorLocale = {\n\tlabel: 'Color de texto',\n\ttooltip: 'Color de texto',\n\tresetLabel: 'Predeterminado',\n\tariaLabelPrefix: 'Color de texto',\n};\n\n// --- French Locale ---\n\nexport const TEXT_COLOR_LOCALE_FR: TextColorLocale = {\n\tlabel: 'Couleur du texte',\n\ttooltip: 'Couleur du texte',\n\tresetLabel: 'Par défaut',\n\tariaLabelPrefix: 'Couleur du texte',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const TEXT_COLOR_LOCALE_ZH: TextColorLocale = {\n\tlabel: '文字颜色',\n\ttooltip: '文字颜色',\n\tresetLabel: '默认',\n\tariaLabelPrefix: '文字颜色',\n};\n\n// --- Russian Locale ---\n\nexport const TEXT_COLOR_LOCALE_RU: TextColorLocale = {\n\tlabel: 'Цвет текста',\n\ttooltip: 'Цвет текста',\n\tresetLabel: 'По умолчанию',\n\tariaLabelPrefix: 'Цвет текста',\n};\n\n// --- Arabic Locale ---\n\nexport const TEXT_COLOR_LOCALE_AR: TextColorLocale = {\n\tlabel: 'لون النص',\n\ttooltip: 'لون النص',\n\tresetLabel: 'افتراضي',\n\tariaLabelPrefix: 'لون النص',\n};\n\n// --- Hindi Locale ---\n\nexport const TEXT_COLOR_LOCALE_HI: TextColorLocale = {\n\tlabel: 'टेक्स्ट रंग',\n\ttooltip: 'टेक्स्ट रंग',\n\tresetLabel: 'डिफ़ॉल्ट',\n\tariaLabelPrefix: 'टेक्स्ट रंग',\n};\n\n// --- Locale Map ---\n\nexport const TEXT_COLOR_LOCALES: Record<string, TextColorLocale> = {\n\ten: TEXT_COLOR_LOCALE_EN,\n\tde: TEXT_COLOR_LOCALE_DE,\n\tes: TEXT_COLOR_LOCALE_ES,\n\tfr: TEXT_COLOR_LOCALE_FR,\n\tzh: TEXT_COLOR_LOCALE_ZH,\n\tru: TEXT_COLOR_LOCALE_RU,\n\tar: TEXT_COLOR_LOCALE_AR,\n\thi: TEXT_COLOR_LOCALE_HI,\n};\n","/**\n * TextColorPlugin: registers a text color mark with attrs,\n * toolbar button with a color picker popup, and removeTextColor command.\n */\n\nimport { COLOR_PICKER_CSS } from '../../editor/styles/color-picker.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isColorMarkActive, removeColorMark } from '../shared/ColorMarkOperations.js';\nimport { renderColorPickerPopup } from '../shared/ColorPickerPopup.js';\nimport { isValidCSSColor, resolveColors } from '../shared/ColorValidation.js';\nimport { TEXT_COLOR_LOCALES, type TextColorLocale } from './TextColorLocale.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface MarkAttrRegistry {\n\t\ttextColor: { color: string };\n\t}\n}\n\n// --- Configuration ---\n\nexport interface TextColorConfig {\n\t/**\n\t * Restricts the color picker to a specific set of hex colors.\n\t * Each value must be a valid hex color code (`#RGB` or `#RRGGBB`).\n\t * Duplicates are removed automatically (case-insensitive).\n\t * When omitted, the full default palette is shown.\n\t */\n\treadonly colors?: readonly string[];\n\t/** When true, a separator is rendered after the textColor toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: TextColorLocale;\n}\n\n// --- Color Palette (Google Docs style: 10 columns x 7 rows) ---\n\nconst COLOR_PALETTE: readonly string[] = [\n\t// Row 1 — dark\n\t'#000000',\n\t'#434343',\n\t'#666666',\n\t'#999999',\n\t'#b7b7b7',\n\t'#cccccc',\n\t'#d9d9d9',\n\t'#efefef',\n\t'#f3f3f3',\n\t'#ffffff',\n\t// Row 2 — vivid\n\t'#980000',\n\t'#ff0000',\n\t'#ff9900',\n\t'#ffff00',\n\t'#00ff00',\n\t'#00ffff',\n\t'#4a86e8',\n\t'#0000ff',\n\t'#9900ff',\n\t'#ff00ff',\n\t// Row 3 — light 3\n\t'#e6b8af',\n\t'#f4cccc',\n\t'#fce5cd',\n\t'#fff2cc',\n\t'#d9ead3',\n\t'#d0e0e3',\n\t'#c9daf8',\n\t'#cfe2f3',\n\t'#d9d2e9',\n\t'#ead1dc',\n\t// Row 4 — light 2\n\t'#dd7e6b',\n\t'#ea9999',\n\t'#f9cb9c',\n\t'#ffe599',\n\t'#b6d7a8',\n\t'#a2c4c9',\n\t'#a4c2f4',\n\t'#9fc5e8',\n\t'#b4a7d6',\n\t'#d5a6bd',\n\t// Row 5 — light 1\n\t'#cc4125',\n\t'#e06666',\n\t'#f6b26b',\n\t'#ffd966',\n\t'#93c47d',\n\t'#76a5af',\n\t'#6d9eeb',\n\t'#6fa8dc',\n\t'#8e7cc3',\n\t'#c27ba0',\n\t// Row 6 — dark 1\n\t'#a61c00',\n\t'#cc0000',\n\t'#e69138',\n\t'#f1c232',\n\t'#6aa84f',\n\t'#45818e',\n\t'#3c78d8',\n\t'#3d85c6',\n\t'#674ea7',\n\t'#a64d79',\n\t// Row 7 — dark 2\n\t'#85200c',\n\t'#990000',\n\t'#b45f06',\n\t'#bf9000',\n\t'#38761d',\n\t'#134f5c',\n\t'#1155cc',\n\t'#0b5394',\n\t'#351c75',\n\t'#741b47',\n];\n\n// --- Plugin ---\n\nexport class TextColorPlugin implements Plugin {\n\treadonly id = 'textColor';\n\treadonly name = 'Text Color';\n\treadonly priority = 23;\n\n\tprivate readonly config: TextColorConfig;\n\tprivate readonly colors: readonly string[];\n\tprivate locale!: TextColorLocale;\n\n\tconstructor(config?: Partial<TextColorConfig>) {\n\t\tthis.config = { ...config };\n\t\tthis.colors = resolveColors(config?.colors, COLOR_PALETTE, 'TextColorPlugin');\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(TEXT_COLOR_LOCALES, context, this.config.locale);\n\n\t\tcontext.registerStyleSheet(COLOR_PICKER_CSS);\n\t\tthis.registerMarkSpec(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerToolbarItem(context);\n\t}\n\n\tprivate registerMarkSpec(context: PluginContext): void {\n\t\tcontext.registerMarkSpec({\n\t\t\ttype: 'textColor',\n\t\t\trank: 5,\n\t\t\tattrs: {\n\t\t\t\tcolor: { default: '' },\n\t\t\t},\n\t\t\ttoDOM(mark) {\n\t\t\t\tconst span = document.createElement('span');\n\t\t\t\tconst color = mark.attrs?.color ?? '';\n\t\t\t\tspan.style.color = color;\n\t\t\t\treturn span;\n\t\t\t},\n\t\t\ttoHTMLString: (mark, content) => {\n\t\t\t\tconst color: string = String(mark.attrs?.color ?? '');\n\t\t\t\tif (!color || !isValidCSSColor(color)) return content;\n\t\t\t\treturn `<span style=\"color: ${escapeHTML(color)}\">${content}</span>`;\n\t\t\t},\n\t\t\ttoHTMLStyle: (mark) => {\n\t\t\t\tconst color: string = String(mark.attrs?.color ?? '');\n\t\t\t\tif (!color || !isValidCSSColor(color)) return null;\n\t\t\t\treturn `color: ${escapeHTML(color)}`;\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 color: string = el.style.color;\n\t\t\t\t\t\tif (!color || !isValidCSSColor(color)) return false;\n\t\t\t\t\t\treturn { color };\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\tprivate registerCommands(context: PluginContext): void {\n\t\tcontext.registerCommand('removeTextColor', () => {\n\t\t\treturn removeColorMark(context, context.getState(), 'textColor');\n\t\t});\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst pathD =\n\t\t\t'M11 3L5.5 17h2.25l1.12-3h6.25l1.12 3h2.25L13 3h-2z' + 'm-1.38 9L12 5.67 14.38 12H9.62z';\n\t\tconst icon: string = [\n\t\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\"',\n\t\t\t' viewBox=\"0 0 24 24\">',\n\t\t\t`<path d=\"${pathD}\"/>`,\n\t\t\t'<rect x=\"3\" y=\"19.5\" width=\"18\" height=\"3\"',\n\t\t\t' rx=\"0.5\" fill=\"#e53935\"/>',\n\t\t\t'</svg>',\n\t\t].join('');\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'textColor',\n\t\t\tgroup: 'format',\n\t\t\ticon,\n\t\t\tlabel: this.locale.label,\n\t\t\ttooltip: this.locale.tooltip,\n\t\t\tcommand: 'removeTextColor',\n\t\t\tpriority: 45,\n\t\t\tpopupType: 'custom',\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\trenderPopup: (container, ctx, onClose) => {\n\t\t\t\trenderColorPickerPopup(container, ctx, {\n\t\t\t\t\tmarkType: 'textColor',\n\t\t\t\t\tcolors: this.colors,\n\t\t\t\t\tcolumns: 10,\n\t\t\t\t\tresetLabel: this.locale.resetLabel,\n\t\t\t\t\tresetCommand: 'removeTextColor',\n\t\t\t\t\tariaLabelPrefix: this.locale.ariaLabelPrefix,\n\t\t\t\t\tonClose,\n\t\t\t\t});\n\t\t\t},\n\t\t\tisActive: (state: EditorState) => isColorMarkActive(state, 'textColor'),\n\t\t});\n\t}\n}\n"],"names":["TEXT_COLOR_LOCALE_EN","label","tooltip","resetLabel","ariaLabelPrefix","TEXT_COLOR_LOCALES","en","de","es","fr","zh","ru","ar","hi","COLOR_PALETTE","TextColorPlugin","id","name","priority","config","colors","locale","this","resolveColors","context","resolvePluginLocale","registerStyleSheet","COLOR_PICKER_CSS","registerMarkSpec","registerCommands","registerToolbarItem","type","rank","attrs","color","default","mark","span","document","createElement","style","toHTMLString","content","String","isValidCSSColor","escapeHTML","toHTMLStyle","parseHTML","tag","getAttrs","el","sanitize","tags","registerCommand","removeColorMark","getState","icon","join","group","command","popupType","separatorAfter","renderPopup","container","ctx","onClose","renderColorPickerPopup","markType","columns","resetCommand","isActive","state","isColorMarkActive"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DIvHQtE2.mjs","sources":["../../src/plugins/highlight/HighlightLocale.ts","../../src/plugins/highlight/HighlightPlugin.ts"],"sourcesContent":["/** Locale interface and default English locale for the HighlightPlugin. */\n\n// --- Locale Interface ---\n\nexport interface HighlightLocale {\n\treadonly label: string;\n\treadonly tooltip: string;\n\treadonly resetLabel: string;\n\treadonly ariaLabelPrefix: string;\n}\n\n// --- Default English Locale ---\n\nexport const HIGHLIGHT_LOCALE_EN: HighlightLocale = {\n\tlabel: 'Highlight',\n\ttooltip: 'Highlight Color',\n\tresetLabel: 'None',\n\tariaLabelPrefix: 'Highlight color',\n};\n\n// --- German Locale ---\n\nexport const HIGHLIGHT_LOCALE_DE: HighlightLocale = {\n\tlabel: 'Hervorhebung',\n\ttooltip: 'Hervorhebungsfarbe',\n\tresetLabel: 'Keine',\n\tariaLabelPrefix: 'Hervorhebungsfarbe',\n};\n\n// --- Spanish Locale ---\n\nexport const HIGHLIGHT_LOCALE_ES: HighlightLocale = {\n\tlabel: 'Resaltado',\n\ttooltip: 'Color de resaltado',\n\tresetLabel: 'Ninguno',\n\tariaLabelPrefix: 'Color de resaltado',\n};\n\n// --- French Locale ---\n\nexport const HIGHLIGHT_LOCALE_FR: HighlightLocale = {\n\tlabel: 'Surlignage',\n\ttooltip: 'Couleur de surlignage',\n\tresetLabel: 'Aucun',\n\tariaLabelPrefix: 'Couleur de surlignage',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const HIGHLIGHT_LOCALE_ZH: HighlightLocale = {\n\tlabel: '高亮',\n\ttooltip: '高亮颜色',\n\tresetLabel: '无',\n\tariaLabelPrefix: '高亮颜色',\n};\n\n// --- Russian Locale ---\n\nexport const HIGHLIGHT_LOCALE_RU: HighlightLocale = {\n\tlabel: 'Выделение',\n\ttooltip: 'Цвет выделения',\n\tresetLabel: 'Нет',\n\tariaLabelPrefix: 'Цвет выделения',\n};\n\n// --- Arabic Locale ---\n\nexport const HIGHLIGHT_LOCALE_AR: HighlightLocale = {\n\tlabel: 'تمييز',\n\ttooltip: 'لون التمييز',\n\tresetLabel: 'بدون',\n\tariaLabelPrefix: 'لون التمييز',\n};\n\n// --- Hindi Locale ---\n\nexport const HIGHLIGHT_LOCALE_HI: HighlightLocale = {\n\tlabel: 'हाइलाइट',\n\ttooltip: 'हाइलाइट रंग',\n\tresetLabel: 'कोई नहीं',\n\tariaLabelPrefix: 'हाइलाइट रंग',\n};\n\n// --- Locale Map ---\n\nexport const HIGHLIGHT_LOCALES: Record<string, HighlightLocale> = {\n\ten: HIGHLIGHT_LOCALE_EN,\n\tde: HIGHLIGHT_LOCALE_DE,\n\tes: HIGHLIGHT_LOCALE_ES,\n\tfr: HIGHLIGHT_LOCALE_FR,\n\tzh: HIGHLIGHT_LOCALE_ZH,\n\tru: HIGHLIGHT_LOCALE_RU,\n\tar: HIGHLIGHT_LOCALE_AR,\n\thi: HIGHLIGHT_LOCALE_HI,\n};\n","/**\n * HighlightPlugin: registers a highlight (background-color) mark with attrs,\n * toolbar button with a color picker popup, and removeHighlight command.\n */\n\nimport { COLOR_PICKER_CSS } from '../../editor/styles/color-picker.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isColorMarkActive, removeColorMark } from '../shared/ColorMarkOperations.js';\nimport { renderColorPickerPopup } from '../shared/ColorPickerPopup.js';\nimport { isValidCSSColor, resolveColors } from '../shared/ColorValidation.js';\nimport { HIGHLIGHT_LOCALES, type HighlightLocale } from './HighlightLocale.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface MarkAttrRegistry {\n\t\thighlight: { color: string };\n\t}\n}\n\n// --- Configuration ---\n\nexport interface HighlightConfig {\n\t/**\n\t * Restricts the color picker to a specific set of hex colors.\n\t * Each value must be a valid hex color code (`#RGB` or `#RRGGBB`).\n\t * Duplicates are removed automatically (case-insensitive).\n\t * When omitted, the full default palette is shown.\n\t */\n\treadonly colors?: readonly string[];\n\t/** When true, a separator is rendered after the highlight toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: HighlightLocale;\n}\n\n// --- Color Palette (Highlight-optimized: 10 columns x 5 rows) ---\n\nconst HIGHLIGHT_PALETTE: readonly string[] = [\n\t// Row 1 — Classic highlighter colors (bright, vivid)\n\t'#fff176',\n\t'#aed581',\n\t'#4dd0e1',\n\t'#64b5f6',\n\t'#ce93d8',\n\t'#f48fb1',\n\t'#ffab91',\n\t'#ff8a65',\n\t'#e6ee9c',\n\t'#80cbc4',\n\t// Row 2 — Light pastels\n\t'#fff9c4',\n\t'#dcedc8',\n\t'#e0f7fa',\n\t'#e3f2fd',\n\t'#f3e5f5',\n\t'#fce4ec',\n\t'#fff3e0',\n\t'#fbe9e7',\n\t'#f9fbe7',\n\t'#e0f2f1',\n\t// Row 3 — Medium pastels\n\t'#fff59d',\n\t'#c5e1a5',\n\t'#80deea',\n\t'#90caf9',\n\t'#e1bee7',\n\t'#f8bbd0',\n\t'#ffcc80',\n\t'#ffab91',\n\t'#e6ee9c',\n\t'#a5d6a7',\n\t// Row 4 — Bold pastels\n\t'#ffee58',\n\t'#9ccc65',\n\t'#26c6da',\n\t'#42a5f5',\n\t'#ab47bc',\n\t'#ec407a',\n\t'#ffa726',\n\t'#ff7043',\n\t'#d4e157',\n\t'#66bb6a',\n\t// Row 5 — Grays and neutral highlights\n\t'#ffffff',\n\t'#fafafa',\n\t'#f5f5f5',\n\t'#eeeeee',\n\t'#e0e0e0',\n\t'#bdbdbd',\n\t'#e8eaf6',\n\t'#efebe9',\n\t'#eceff1',\n\t'#fafafa',\n];\n\n// --- Plugin ---\n\nexport class HighlightPlugin implements Plugin {\n\treadonly id = 'highlight';\n\treadonly name = 'Highlight';\n\treadonly priority = 24;\n\n\tprivate readonly config: HighlightConfig;\n\tprivate readonly colors: readonly string[];\n\tprivate locale!: HighlightLocale;\n\n\tconstructor(config?: Partial<HighlightConfig>) {\n\t\tthis.config = { ...config };\n\t\tthis.colors = resolveColors(config?.colors, HIGHLIGHT_PALETTE, 'HighlightPlugin');\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(HIGHLIGHT_LOCALES, context, this.config.locale);\n\n\t\tcontext.registerStyleSheet(COLOR_PICKER_CSS);\n\t\tthis.registerMarkSpec(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerToolbarItem(context);\n\t}\n\n\tprivate registerMarkSpec(context: PluginContext): void {\n\t\tcontext.registerMarkSpec({\n\t\t\ttype: 'highlight',\n\t\t\trank: 4,\n\t\t\tattrs: {\n\t\t\t\tcolor: { default: '' },\n\t\t\t},\n\t\t\ttoDOM(mark) {\n\t\t\t\tconst span = document.createElement('span');\n\t\t\t\tconst color = mark.attrs?.color ?? '';\n\t\t\t\tspan.style.backgroundColor = color;\n\t\t\t\treturn span;\n\t\t\t},\n\t\t\ttoHTMLString: (mark, content) => {\n\t\t\t\tconst color: string = String(mark.attrs?.color ?? '');\n\t\t\t\tif (!color || !isValidCSSColor(color)) return content;\n\t\t\t\treturn `<span style=\"background-color: ${escapeHTML(color)}\">${content}</span>`;\n\t\t\t},\n\t\t\ttoHTMLStyle: (mark) => {\n\t\t\t\tconst color: string = String(mark.attrs?.color ?? '');\n\t\t\t\tif (!color || !isValidCSSColor(color)) return null;\n\t\t\t\treturn `background-color: ${escapeHTML(color)}`;\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 color: string = el.style.backgroundColor;\n\t\t\t\t\t\tif (!color || !isValidCSSColor(color)) return false;\n\t\t\t\t\t\treturn { color };\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\tprivate registerCommands(context: PluginContext): void {\n\t\tcontext.registerCommand('removeHighlight', () => {\n\t\t\treturn removeColorMark(context, context.getState(), 'highlight');\n\t\t});\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst pathD =\n\t\t\t'M11 3L5.5 17h2.25l1.12-3h6.25l1.12 3h2.25L13 3h-2z' + 'm-1.38 9L12 5.67 14.38 12H9.62z';\n\t\tconst icon: string = [\n\t\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\"',\n\t\t\t' viewBox=\"0 0 24 24\">',\n\t\t\t`<path d=\"${pathD}\"/>`,\n\t\t\t'<rect x=\"3\" y=\"17\" width=\"18\" height=\"6\"',\n\t\t\t' rx=\"0.5\" fill=\"#fff176\"/>',\n\t\t\t'</svg>',\n\t\t].join('');\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'highlight',\n\t\t\tgroup: 'format',\n\t\t\ticon,\n\t\t\tlabel: this.locale.label,\n\t\t\ttooltip: this.locale.tooltip,\n\t\t\tcommand: 'removeHighlight',\n\t\t\tpriority: 46,\n\t\t\tpopupType: 'custom',\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\trenderPopup: (container, ctx, onClose) => {\n\t\t\t\trenderColorPickerPopup(container, ctx, {\n\t\t\t\t\tmarkType: 'highlight',\n\t\t\t\t\tcolors: this.colors,\n\t\t\t\t\tcolumns: 10,\n\t\t\t\t\tresetLabel: this.locale.resetLabel,\n\t\t\t\t\tresetCommand: 'removeHighlight',\n\t\t\t\t\tariaLabelPrefix: this.locale.ariaLabelPrefix,\n\t\t\t\t\tonClose,\n\t\t\t\t});\n\t\t\t},\n\t\t\tisActive: (state: EditorState) => isColorMarkActive(state, 'highlight'),\n\t\t});\n\t}\n}\n"],"names":["HIGHLIGHT_LOCALE_EN","label","tooltip","resetLabel","ariaLabelPrefix","HIGHLIGHT_LOCALES","en","de","es","fr","zh","ru","ar","hi","HIGHLIGHT_PALETTE","HighlightPlugin","id","name","priority","config","colors","locale","this","resolveColors","context","resolvePluginLocale","registerStyleSheet","COLOR_PICKER_CSS","registerMarkSpec","registerCommands","registerToolbarItem","type","rank","attrs","color","default","mark","span","document","createElement","style","backgroundColor","toHTMLString","content","String","isValidCSSColor","escapeHTML","toHTMLStyle","parseHTML","tag","getAttrs","el","sanitize","tags","registerCommand","removeColorMark","getState","icon","join","group","command","popupType","separatorAfter","renderPopup","container","ctx","onClose","renderColorPickerPopup","markType","columns","resetCommand","isActive","state","isColorMarkActive"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DayTn6Re.mjs","sources":["../../src/editor/styles/font-size-select.ts","../../src/plugins/font-size/FontSizeLocale.ts","../../src/plugins/font-size/FontSizeOperations.ts","../../src/plugins/font-size/FontSizePopup.ts","../../src/plugins/font-size/FontSizePlugin.ts"],"sourcesContent":["/** Font size picker toolbar combobox + popup styles. */\nexport const FONT_SIZE_SELECT_CSS = `\n/* Font Size Select — Width overrides for combobox button */\n.notectl-toolbar-btn--fontSize {\n\tmin-width: 60px;\n\tmax-width: 80px;\n}\n\n/* Font Size Picker Popup */\n.notectl-font-size-picker {\n\tmin-width: 140px;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.notectl-font-size-picker__input-wrapper {\n\tpadding: 8px 8px 4px;\n}\n\n.notectl-font-size-picker__input {\n\twidth: 100%;\n\tpadding: 6px 8px;\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 4px;\n\tfont-size: 13px;\n\tfont-family: inherit;\n\tcolor: var(--notectl-fg);\n\tbackground: var(--notectl-bg);\n\toutline: none;\n\tbox-sizing: border-box;\n\t-moz-appearance: textfield;\n}\n\n.notectl-font-size-picker__input::-webkit-inner-spin-button,\n.notectl-font-size-picker__input::-webkit-outer-spin-button {\n\t-webkit-appearance: none;\n\tmargin: 0;\n}\n\n.notectl-font-size-picker__input:focus {\n\tborder-color: var(--notectl-border-focus);\n\tbox-shadow: 0 0 0 2px var(--notectl-focus-ring);\n}\n\n.notectl-font-size-picker__list {\n\tmax-height: 280px;\n\toverflow-y: auto;\n\tpadding: 4px 0;\n}\n\n.notectl-font-size-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\toutline: none;\n}\n\n.notectl-font-size-picker__item:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-font-size-picker__item--active {\n\tbackground: var(--notectl-active-bg);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-font-size-picker__item--active:hover {\n\tbackground: var(--notectl-active-bg);\n}\n\n.notectl-font-size-picker__item--focused {\n\toutline: 2px solid var(--notectl-primary);\n\toutline-offset: -2px;\n}\n\n.notectl-font-size-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-size-picker__label {\n\tflex: 1;\n}\n`;\n","/** Locale interface and default English locale for the FontSizePlugin. */\n\n// --- Locale Interface ---\n\nexport interface FontSizeLocale {\n\treadonly label: string;\n\treadonly tooltip: string;\n\treadonly customFontSizeAria: string;\n\treadonly fontSizesAria: string;\n}\n\n// --- Default English Locale ---\n\nexport const FONT_SIZE_LOCALE_EN: FontSizeLocale = {\n\tlabel: 'Font Size',\n\ttooltip: 'Font Size',\n\tcustomFontSizeAria: 'Custom font size',\n\tfontSizesAria: 'Font sizes',\n};\n\n// --- German Locale ---\n\nexport const FONT_SIZE_LOCALE_DE: FontSizeLocale = {\n\tlabel: 'Schriftgröße',\n\ttooltip: 'Schriftgröße',\n\tcustomFontSizeAria: 'Benutzerdefinierte Schriftgröße',\n\tfontSizesAria: 'Schriftgrößen',\n};\n\n// --- Spanish Locale ---\n\nexport const FONT_SIZE_LOCALE_ES: FontSizeLocale = {\n\tlabel: 'Tamaño de fuente',\n\ttooltip: 'Tamaño de fuente',\n\tcustomFontSizeAria: 'Tamaño de fuente personalizado',\n\tfontSizesAria: 'Tamaños de fuente',\n};\n\n// --- French Locale ---\n\nexport const FONT_SIZE_LOCALE_FR: FontSizeLocale = {\n\tlabel: 'Taille de police',\n\ttooltip: 'Taille de police',\n\tcustomFontSizeAria: 'Taille de police personnalisée',\n\tfontSizesAria: 'Tailles de police',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const FONT_SIZE_LOCALE_ZH: FontSizeLocale = {\n\tlabel: '字号',\n\ttooltip: '字号',\n\tcustomFontSizeAria: '自定义字号',\n\tfontSizesAria: '字号列表',\n};\n\n// --- Russian Locale ---\n\nexport const FONT_SIZE_LOCALE_RU: FontSizeLocale = {\n\tlabel: 'Размер шрифта',\n\ttooltip: 'Размер шрифта',\n\tcustomFontSizeAria: 'Произвольный размер шрифта',\n\tfontSizesAria: 'Размеры шрифта',\n};\n\n// --- Arabic Locale ---\n\nexport const FONT_SIZE_LOCALE_AR: FontSizeLocale = {\n\tlabel: 'حجم الخط',\n\ttooltip: 'حجم الخط',\n\tcustomFontSizeAria: 'حجم خط مخصص',\n\tfontSizesAria: 'أحجام الخط',\n};\n\n// --- Hindi Locale ---\n\nexport const FONT_SIZE_LOCALE_HI: FontSizeLocale = {\n\tlabel: 'फ़ॉन्ट आकार',\n\ttooltip: 'फ़ॉन्ट आकार',\n\tcustomFontSizeAria: 'कस्टम फ़ॉन्ट आकार',\n\tfontSizesAria: 'फ़ॉन्ट आकार सूची',\n};\n\n// --- Locale Map ---\n\nexport const FONT_SIZE_LOCALES: Record<string, FontSizeLocale> = {\n\ten: FONT_SIZE_LOCALE_EN,\n\tde: FONT_SIZE_LOCALE_DE,\n\tes: FONT_SIZE_LOCALE_ES,\n\tfr: FONT_SIZE_LOCALE_FR,\n\tzh: FONT_SIZE_LOCALE_ZH,\n\tru: FONT_SIZE_LOCALE_RU,\n\tar: FONT_SIZE_LOCALE_AR,\n\thi: FONT_SIZE_LOCALE_HI,\n};\n","/**\n * Pure business logic for font-size state queries and transaction builders.\n * All functions are DOM-free and operate on EditorState / PluginContext.\n *\n * Delegates to the generic AttributedMarkCommands helpers for the\n * collapsed-vs-range branching logic.\n */\n\nimport {\n\tapplyAttributedMark,\n\tgetMarkAttrAtSelection,\n\tisAttributedMarkActive,\n\tremoveAttributedMark,\n} from '../../commands/AttributedMarkCommands.js';\nimport { markType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\n\n// --- State Queries ---\n\n/** Returns the raw fontSize CSS value at the current selection, or null. */\nexport function getActiveSize(state: EditorState): string | null {\n\treturn getMarkAttrAtSelection(state, 'fontSize', (m) => m.attrs.size ?? null);\n}\n\n/** Returns the active font size as a number, falling back to defaultSize. */\nexport function getActiveSizeNumeric(state: EditorState, defaultSize: number): number {\n\tconst raw: string | null = getActiveSize(state);\n\tif (!raw) return defaultSize;\n\tconst parsed: number = Number.parseInt(raw, 10);\n\treturn Number.isNaN(parsed) ? defaultSize : parsed;\n}\n\n/** Returns true when the selection carries a fontSize mark. */\nexport function isFontSizeActive(state: EditorState): boolean {\n\treturn isAttributedMarkActive(state, 'fontSize');\n}\n\n// --- Commands ---\n\n/** Applies a fontSize mark with the given CSS size string to the selection. */\nexport function applyFontSize(context: PluginContext, state: EditorState, size: string): boolean {\n\tconst mark = { type: markType('fontSize'), attrs: { size } };\n\tconst tr = applyAttributedMark(state, mark);\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Removes the fontSize mark from the current selection. */\nexport function removeFontSize(context: PluginContext, state: EditorState): boolean {\n\tconst tr = removeAttributedMark(state, markType('fontSize'));\n\tif (!tr) return false;\n\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Steps the font size up or down through the preset list. */\nexport function stepFontSize(\n\tcontext: PluginContext,\n\tstate: EditorState,\n\tdirection: 'up' | 'down',\n\tsizes: readonly number[],\n\tdefaultSize: number,\n): boolean {\n\tconst current: number = getActiveSizeNumeric(state, defaultSize);\n\tconst next: number | null = getNextPresetSize(current, direction, sizes);\n\tif (next === null) return false;\n\n\tif (next === defaultSize) {\n\t\treturn removeFontSize(context, state);\n\t}\n\treturn applyFontSize(context, state, `${next}px`);\n}\n\n/**\n * Selects a specific font size: removes the mark when size equals the\n * default, otherwise applies the new size.\n */\nexport function selectSize(context: PluginContext, size: number, defaultSize: number): void {\n\tif (size === defaultSize) {\n\t\tcontext.executeCommand('removeFontSize');\n\t} else {\n\t\tapplyFontSize(context, context.getState(), `${size}px`);\n\t}\n}\n\n// --- Helpers ---\n\n/** Finds the next preset size in the given direction, or null at boundaries. */\nexport function getNextPresetSize(\n\tcurrent: number,\n\tdirection: 'up' | 'down',\n\tsizes: readonly number[],\n): number | null {\n\tif (direction === 'up') {\n\t\tfor (const size of sizes) {\n\t\t\tif (size > current) return size;\n\t\t}\n\t\treturn null;\n\t}\n\tfor (let i: number = sizes.length - 1; i >= 0; i--) {\n\t\tconst size: number | undefined = sizes[i];\n\t\tif (size !== undefined && size < current) return size;\n\t}\n\treturn null;\n}\n","/**\n * Renders the font-size picker popup with a custom numeric input\n * and an accessible preset list with keyboard navigation.\n */\n\nimport type { PluginContext } from '../Plugin.js';\nimport type { FontSizeLocale } from './FontSizeLocale.js';\nimport { getActiveSizeNumeric, selectSize } from './FontSizeOperations.js';\n\n// --- Constants ---\n\nconst MIN_CUSTOM_SIZE = 1;\nconst MAX_CUSTOM_SIZE = 400;\n\n// --- Config ---\n\nexport interface FontSizePopupConfig {\n\treadonly sizes: readonly number[];\n\treadonly defaultSize: number;\n\treadonly dismissPopup: () => void;\n\treadonly locale?: FontSizeLocale;\n}\n\n// --- Public Entry Point ---\n\n/** Builds the font-size picker DOM inside the given container. */\nexport function renderFontSizePopup(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\tconfig: FontSizePopupConfig,\n): void {\n\tcontainer.classList.add('notectl-font-size-picker');\n\n\tconst currentSize: number = getActiveSizeNumeric(context.getState(), config.defaultSize);\n\n\tconst input: HTMLInputElement = buildCustomInput(currentSize, config);\n\tappendInputWrapper(container, input);\n\n\tconst { list, items } = buildSizeList(config.sizes, currentSize, context, config);\n\tcontainer.appendChild(list);\n\n\tscrollActiveIntoView(items, config.sizes, currentSize, list);\n\tattachKeyboardNavigation(input, list, items, context, config);\n}\n\n// --- Custom Input ---\n\nfunction buildCustomInput(currentSize: number, config: FontSizePopupConfig): HTMLInputElement {\n\tconst input: HTMLInputElement = document.createElement('input');\n\tinput.type = 'number';\n\tinput.className = 'notectl-font-size-picker__input';\n\tinput.min = String(MIN_CUSTOM_SIZE);\n\tinput.max = String(MAX_CUSTOM_SIZE);\n\tinput.value = String(currentSize);\n\tinput.setAttribute('aria-label', config.locale?.customFontSizeAria ?? 'Custom font size');\n\treturn input;\n}\n\nfunction appendInputWrapper(container: HTMLElement, input: HTMLInputElement): void {\n\tconst wrapper: HTMLDivElement = document.createElement('div');\n\twrapper.className = 'notectl-font-size-picker__input-wrapper';\n\twrapper.appendChild(input);\n\tcontainer.appendChild(wrapper);\n}\n\n// --- Size List ---\n\ninterface SizeListResult {\n\treadonly list: HTMLDivElement;\n\treadonly items: HTMLButtonElement[];\n}\n\nfunction buildSizeList(\n\tsizes: readonly number[],\n\tcurrentSize: number,\n\tcontext: PluginContext,\n\tconfig: FontSizePopupConfig,\n): SizeListResult {\n\tconst list: HTMLDivElement = document.createElement('div');\n\tlist.className = 'notectl-font-size-picker__list';\n\tlist.setAttribute('role', 'listbox');\n\tlist.setAttribute('aria-label', config.locale?.fontSizesAria ?? 'Font sizes');\n\n\tconst items: HTMLButtonElement[] = [];\n\n\tfor (const size of sizes) {\n\t\tconst isActive: boolean = size === currentSize;\n\t\tconst item: HTMLButtonElement = buildSizeItem(size, isActive);\n\n\t\titem.addEventListener('mousedown', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tselectSize(context, size, config.defaultSize);\n\t\t\tconfig.dismissPopup();\n\t\t});\n\n\t\titems.push(item);\n\t\tlist.appendChild(item);\n\t}\n\n\treturn { list, items };\n}\n\nfunction buildSizeItem(size: number, isActive: boolean): HTMLButtonElement {\n\tconst item: HTMLButtonElement = document.createElement('button');\n\titem.type = 'button';\n\titem.id = `notectl-font-size-option-${size}`;\n\titem.className = 'notectl-font-size-picker__item';\n\titem.setAttribute('role', 'option');\n\titem.setAttribute('aria-selected', String(isActive));\n\n\tif (isActive) {\n\t\titem.classList.add('notectl-font-size-picker__item--active');\n\t}\n\n\tconst check: HTMLSpanElement = document.createElement('span');\n\tcheck.className = 'notectl-font-size-picker__check';\n\tcheck.textContent = isActive ? '\\u2713' : '';\n\titem.appendChild(check);\n\n\tconst label: HTMLSpanElement = document.createElement('span');\n\tlabel.className = 'notectl-font-size-picker__label';\n\tlabel.textContent = String(size);\n\titem.appendChild(label);\n\n\treturn item;\n}\n\n// --- Scroll ---\n\nfunction scrollActiveIntoView(\n\titems: HTMLButtonElement[],\n\tsizes: readonly number[],\n\tcurrentSize: number,\n\tlist: HTMLDivElement,\n): void {\n\tconst activeIdx: number = sizes.indexOf(currentSize);\n\tif (activeIdx < 0) return;\n\n\tconst activeItem: HTMLButtonElement | undefined = items[activeIdx];\n\tif (!activeItem) return;\n\n\trequestAnimationFrame(() => {\n\t\tactiveItem.scrollIntoView({ block: 'nearest' });\n\t});\n\tlist.setAttribute('aria-activedescendant', activeItem.id);\n}\n\n// --- Keyboard Navigation ---\n\nfunction attachKeyboardNavigation(\n\tinput: HTMLInputElement,\n\tlist: HTMLDivElement,\n\titems: HTMLButtonElement[],\n\tcontext: PluginContext,\n\tconfig: FontSizePopupConfig,\n): void {\n\tlet focusedIndex = -1;\n\n\tconst setFocused = (idx: number): void => {\n\t\tif (focusedIndex >= 0 && focusedIndex < items.length) {\n\t\t\titems[focusedIndex]?.classList.remove('notectl-font-size-picker__item--focused');\n\t\t}\n\t\tfocusedIndex = idx;\n\t\tconst el: HTMLButtonElement | undefined = items[focusedIndex];\n\t\tif (focusedIndex >= 0 && focusedIndex < items.length && el) {\n\t\t\tel.classList.add('notectl-font-size-picker__item--focused');\n\t\t\tel.scrollIntoView({ block: 'nearest' });\n\t\t\tlist.setAttribute('aria-activedescendant', el.id);\n\t\t}\n\t};\n\n\tinput.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'Enter') {\n\t\t\te.preventDefault();\n\t\t\tconst val: number = Number.parseInt(input.value, 10);\n\t\t\tif (!Number.isNaN(val) && val >= MIN_CUSTOM_SIZE && val <= MAX_CUSTOM_SIZE) {\n\t\t\t\tselectSize(context, val, config.defaultSize);\n\t\t\t\tconfig.dismissPopup();\n\t\t\t}\n\t\t} else if (e.key === 'ArrowDown') {\n\t\t\te.preventDefault();\n\t\t\tsetFocused(0);\n\t\t\titems[0]?.focus();\n\t\t} else if (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tconfig.dismissPopup();\n\t\t}\n\t});\n\n\tlist.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\tif (e.key === 'ArrowDown') {\n\t\t\te.preventDefault();\n\t\t\tif (focusedIndex < items.length - 1) {\n\t\t\t\tsetFocused(focusedIndex + 1);\n\t\t\t\titems[focusedIndex]?.focus();\n\t\t\t}\n\t\t} else if (e.key === 'ArrowUp') {\n\t\t\te.preventDefault();\n\t\t\tif (focusedIndex > 0) {\n\t\t\t\tsetFocused(focusedIndex - 1);\n\t\t\t\titems[focusedIndex]?.focus();\n\t\t\t} else {\n\t\t\t\tsetFocused(-1);\n\t\t\t\tinput.focus();\n\t\t\t}\n\t\t} else if (e.key === 'Enter') {\n\t\t\te.preventDefault();\n\t\t\tconst selectedSize: number | undefined = config.sizes[focusedIndex];\n\t\t\tif (focusedIndex >= 0 && focusedIndex < config.sizes.length && selectedSize !== undefined) {\n\t\t\t\tselectSize(context, selectedSize, config.defaultSize);\n\t\t\t\tconfig.dismissPopup();\n\t\t\t}\n\t\t} else if (e.key === 'Escape') {\n\t\t\te.preventDefault();\n\t\t\tconfig.dismissPopup();\n\t\t}\n\t});\n}\n","/**\n * FontSizePlugin: registers a fontSize mark with attrs, a combobox-style\n * toolbar selector with WCAG-accessible popup, and commands for\n * increasing / decreasing font size.\n */\n\nimport { FONT_SIZE_SELECT_CSS } from '../../editor/styles/font-size-select.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { isValidCSSFontSize } from '../shared/ColorValidation.js';\nimport { ToolbarServiceKey } from '../toolbar/ToolbarPlugin.js';\nimport { FONT_SIZE_LOCALES, type FontSizeLocale } from './FontSizeLocale.js';\nimport {\n\tgetActiveSizeNumeric,\n\tisFontSizeActive,\n\tremoveFontSize,\n\tstepFontSize,\n} from './FontSizeOperations.js';\nimport { renderFontSizePopup } from './FontSizePopup.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface MarkAttrRegistry {\n\t\tfontSize: { size: string };\n\t}\n}\n\n// --- Constants ---\n\n/** Default preset sizes shown in the font size dropdown. */\nexport const DEFAULT_FONT_SIZES: readonly number[] = [\n\t8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 48, 64, 72, 96,\n];\n\nconst DEFAULT_FONT_SIZE = 16;\n\n// --- Configuration ---\n\nexport interface FontSizeConfig {\n\t/**\n\t * Preset sizes shown in the font size dropdown.\n\t * Must contain positive integers. Values are sorted and deduplicated automatically.\n\t * Defaults to {@link DEFAULT_FONT_SIZES} when omitted or empty.\n\t */\n\treadonly sizes?: readonly number[];\n\t/**\n\t * The base font size that text has when no fontSize mark is applied.\n\t * Shown as the initial value in the toolbar combo and used as the\n\t * \"neutral\" size — selecting it removes the mark instead of applying one.\n\t * Defaults to 16.\n\t */\n\treadonly defaultSize?: number;\n\t/** When true, a separator is rendered after the fontSize toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: FontSizeLocale;\n}\n\n// --- Plugin ---\n\nexport class FontSizePlugin implements Plugin {\n\treadonly id = 'fontSize';\n\treadonly name = 'Font Size';\n\treadonly priority = 21;\n\n\tprivate readonly config: FontSizeConfig;\n\tprivate readonly sizes: readonly number[];\n\tprivate readonly defaultSize: number;\n\tprivate locale!: FontSizeLocale;\n\tprivate context: PluginContext | null = null;\n\n\tconstructor(config?: Partial<FontSizeConfig>) {\n\t\tthis.config = { ...config };\n\t\tthis.sizes = resolveSizes(config?.sizes);\n\t\tthis.defaultSize = resolveDefaultSize(config?.defaultSize);\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(FONT_SIZE_LOCALES, context, this.config.locale);\n\n\t\tcontext.registerStyleSheet(FONT_SIZE_SELECT_CSS);\n\t\tthis.context = context;\n\t\tthis.registerMarkSpec(context);\n\t\tthis.registerCommands(context);\n\t\tthis.registerKeymaps(context);\n\t\tthis.registerToolbarItem(context);\n\t\tthis.applyDefaultSizeToContainer(context);\n\t}\n\n\tdestroy(): void {\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: 'fontSize',\n\t\t\trank: 4,\n\t\t\tattrs: {\n\t\t\t\tsize: { 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 size: string = mark.attrs?.size ?? '';\n\t\t\t\tif (size) {\n\t\t\t\t\tspan.style.fontSize = size;\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 size: string = String(mark.attrs?.size ?? '');\n\t\t\t\tif (!size || !isValidCSSFontSize(size)) return content;\n\t\t\t\treturn `<span style=\"font-size: ${escapeHTML(size)}\">${content}</span>`;\n\t\t\t},\n\t\t\ttoHTMLStyle: (mark) => {\n\t\t\t\tconst size: string = String(mark.attrs?.size ?? '');\n\t\t\t\tif (!size || !isValidCSSFontSize(size)) return null;\n\t\t\t\treturn `font-size: ${escapeHTML(size)}`;\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 size: string = el.style.fontSize;\n\t\t\t\t\t\tif (!size) return false;\n\t\t\t\t\t\treturn { size };\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('removeFontSize', () => {\n\t\t\treturn removeFontSize(context, context.getState());\n\t\t});\n\n\t\tcontext.registerCommand('setFontSize', () => {\n\t\t\treturn false;\n\t\t});\n\n\t\tcontext.registerCommand('increaseFontSize', () => {\n\t\t\treturn stepFontSize(context, context.getState(), 'up', this.sizes, this.defaultSize);\n\t\t});\n\n\t\tcontext.registerCommand('decreaseFontSize', () => {\n\t\t\treturn stepFontSize(context, context.getState(), 'down', this.sizes, this.defaultSize);\n\t\t});\n\t}\n\n\t// --- Keymaps ---\n\n\tprivate registerKeymaps(context: PluginContext): void {\n\t\tcontext.registerKeymap({\n\t\t\t'Mod-Shift-+': () => {\n\t\t\t\treturn stepFontSize(context, context.getState(), 'up', this.sizes, this.defaultSize);\n\t\t\t},\n\t\t\t'Mod-Shift-_': () => {\n\t\t\t\treturn stepFontSize(context, context.getState(), 'down', this.sizes, this.defaultSize);\n\t\t\t},\n\t\t});\n\t}\n\n\t// --- Toolbar ---\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'fontSize',\n\t\t\tgroup: 'format',\n\t\t\tlabel: this.locale.label,\n\t\t\ttooltip: this.locale.tooltip,\n\t\t\tcommand: 'removeFontSize',\n\t\t\tpriority: 6,\n\t\t\tpopupType: 'combobox',\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\tgetLabel: (state: EditorState): string =>\n\t\t\t\tString(getActiveSizeNumeric(state, this.defaultSize)),\n\t\t\trenderPopup: (container, ctx) => {\n\t\t\t\trenderFontSizePopup(container, ctx, {\n\t\t\t\t\tsizes: this.sizes,\n\t\t\t\t\tdefaultSize: this.defaultSize,\n\t\t\t\t\tdismissPopup: () => this.dismissPopup(),\n\t\t\t\t\tlocale: this.locale,\n\t\t\t\t});\n\t\t\t},\n\t\t\tisActive: (state) => isFontSizeActive(state),\n\t\t});\n\t}\n\n\t/**\n\t * Sets the configured default font size on the editor content container\n\t * so that unformatted text renders at the correct size instead of the\n\t * browser default (16px).\n\t */\n\tprivate applyDefaultSizeToContainer(context: PluginContext): void {\n\t\tconst container: HTMLElement = context.getContainer();\n\t\tcontainer.style.fontSize = `${this.defaultSize}px`;\n\t}\n\n\tprivate dismissPopup(): void {\n\t\tconst toolbar = this.context?.getService(ToolbarServiceKey);\n\t\ttoolbar?.closePopup();\n\t}\n}\n\n// --- Helpers ---\n\nfunction resolveSizes(sizes: readonly number[] | undefined): readonly number[] {\n\tif (!sizes || sizes.length === 0) return DEFAULT_FONT_SIZES;\n\tconst unique: number[] = [...new Set(sizes)].filter((n) => Number.isInteger(n) && n > 0);\n\tunique.sort((a, b) => a - b);\n\treturn unique.length > 0 ? unique : DEFAULT_FONT_SIZES;\n}\n\nfunction resolveDefaultSize(size: number | undefined): number {\n\tif (size === undefined) return DEFAULT_FONT_SIZE;\n\treturn Number.isInteger(size) && size > 0 ? size : DEFAULT_FONT_SIZE;\n}\n"],"names":["FONT_SIZE_LOCALE_EN","label","tooltip","customFontSizeAria","fontSizesAria","FONT_SIZE_LOCALES","en","de","es","fr","zh","ru","ar","hi","getActiveSizeNumeric","state","defaultSize","raw","getMarkAttrAtSelection","m","attrs","size","parsed","Number","parseInt","isNaN","applyFontSize","context","mark","type","markType","tr","applyAttributedMark","dispatch","removeFontSize","removeAttributedMark","stepFontSize","direction","sizes","next","current","i","length","selectSize","executeCommand","getState","buildSizeItem","isActive","item","document","createElement","id","className","setAttribute","String","classList","add","check","textContent","appendChild","DEFAULT_FONT_SIZES","FontSizePlugin","name","priority","config","locale","this","unique","Set","filter","n","isInteger","sort","a","b","resolvePluginLocale","registerStyleSheet","registerMarkSpec","registerCommands","registerKeymaps","registerToolbarItem","applyDefaultSizeToContainer","destroy","rank","default","span","style","fontSize","toHTMLString","content","isValidCSSFontSize","escapeHTML","toHTMLStyle","parseHTML","tag","getAttrs","el","sanitize","tags","registerCommand","registerKeymap","group","command","popupType","separatorAfter","getLabel","renderPopup","container","ctx","currentSize","input","min","max","value","wrapper","list","items","addEventListener","e","preventDefault","stopPropagation","dismissPopup","push","activeIdx","indexOf","activeItem","requestAnimationFrame","scrollIntoView","block","focusedIndex","setFocused","idx","remove","key","val","focus","selectedSize","isAttributedMarkActive","getContainer","toolbar","getService","ToolbarServiceKey","closePopup"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DoCvDkfj.mjs","sources":["../../src/plugins/heading/HeadingBlockTypePicker.ts","../../src/plugins/heading/HeadingCommands.ts","../../src/plugins/heading/HeadingKeyboardHandlers.ts","../../src/plugins/heading/HeadingLocale.ts","../../src/plugins/heading/HeadingPlugin.ts","../../src/editor/styles/heading-select.ts","../../src/plugins/heading/HeadingInputRules.ts"],"sourcesContent":["/**\n * Block type picker entries and toolbar dropdown for heading block types.\n * Registers picker entries for paragraph, title, subtitle, and all heading levels,\n * plus a combobox-style toolbar item with a custom popup.\n */\n\nimport { isGapCursor, isNodeSelection } from '../../model/Selection.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { ToolbarServiceKey } from '../toolbar/ToolbarPlugin.js';\nimport type { PickerEntryStyle } from './BlockTypePickerEntry.js';\nimport type { HeadingLocale } from './HeadingLocale.js';\nimport type { HeadingConfig } from './HeadingPlugin.js';\nimport type { HeadingLevel } from './HeadingPlugin.js';\n\n// --- Picker Entry Registration ---\n\n/** Registers block type picker entries for paragraph, title, subtitle, and heading levels. */\nexport function registerHeadingPickerEntries(\n\tcontext: PluginContext,\n\tconfig: HeadingConfig,\n\tlocale: HeadingLocale,\n): void {\n\tcontext.registerBlockTypePickerEntry({\n\t\tid: 'paragraph',\n\t\tlabel: locale.paragraph,\n\t\tcommand: 'setParagraph',\n\t\tpriority: 10,\n\t\tisActive: (state) => {\n\t\t\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\t\t\tconst block = state.getBlock(state.selection.anchor.blockId);\n\t\t\treturn block?.type === 'paragraph';\n\t\t},\n\t});\n\n\tcontext.registerBlockTypePickerEntry({\n\t\tid: 'title',\n\t\tlabel: locale.title,\n\t\tcommand: 'setTitle',\n\t\tpriority: 20,\n\t\tstyle: { fontSize: '1.6em', fontWeight: '700' },\n\t\tisActive: (state) => {\n\t\t\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\t\t\tconst block = state.getBlock(state.selection.anchor.blockId);\n\t\t\treturn block?.type === 'title';\n\t\t},\n\t});\n\n\tcontext.registerBlockTypePickerEntry({\n\t\tid: 'subtitle',\n\t\tlabel: locale.subtitle,\n\t\tcommand: 'setSubtitle',\n\t\tpriority: 30,\n\t\tstyle: { fontSize: '1.3em', fontWeight: '500' },\n\t\tisActive: (state) => {\n\t\t\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\t\t\tconst block = state.getBlock(state.selection.anchor.blockId);\n\t\t\treturn block?.type === 'subtitle';\n\t\t},\n\t});\n\n\tfor (const level of config.levels) {\n\t\tcontext.registerBlockTypePickerEntry({\n\t\t\tid: `heading-${level}`,\n\t\t\tlabel: getHeadingLabel(locale, level),\n\t\t\tcommand: `setHeading${level}`,\n\t\t\tpriority: 100 + level,\n\t\t\tstyle: { fontSize: `${1.4 - level * 0.1}em`, fontWeight: '600' },\n\t\t\tisActive: (state) => {\n\t\t\t\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\t\t\t\tconst block = state.getBlock(state.selection.anchor.blockId);\n\t\t\t\treturn block?.type === 'heading' && block.attrs?.level === level;\n\t\t\t},\n\t\t});\n\t}\n}\n\n// --- Toolbar Item ---\n\n/** Registers the heading toolbar dropdown with a combobox picker popup. */\nexport function registerHeadingToolbarItem(\n\tcontext: PluginContext,\n\tconfig: HeadingConfig,\n\tlocale: HeadingLocale,\n): void {\n\tcontext.registerToolbarItem({\n\t\tid: 'heading',\n\t\tgroup: 'block',\n\t\tlabel: locale.blockTypeLabel,\n\t\ttooltip: locale.blockTypeLabel,\n\t\tcommand: 'setParagraph',\n\t\tpriority: 50,\n\t\tpopupType: 'combobox',\n\t\tseparatorAfter: config.separatorAfter,\n\t\tgetLabel: (state: EditorState): string => getActiveLabel(state, context, locale),\n\t\trenderPopup: (container, ctx) => {\n\t\t\trenderHeadingPopup(container, ctx, locale);\n\t\t},\n\t\tisActive: (state) => {\n\t\t\tconst entries = context.getBlockTypePickerRegistry().getBlockTypePickerEntries();\n\t\t\treturn entries.some((entry) => entry.id !== 'paragraph' && entry.isActive(state));\n\t\t},\n\t});\n}\n\n// --- Popup Rendering ---\n\nfunction renderHeadingPopup(\n\tcontainer: HTMLElement,\n\tcontext: PluginContext,\n\tlocale: HeadingLocale,\n): void {\n\tcontainer.classList.add('notectl-heading-picker');\n\n\tconst state: EditorState = context.getState();\n\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return;\n\n\tconst list: HTMLDivElement = document.createElement('div');\n\tlist.className = 'notectl-heading-picker__list';\n\tlist.setAttribute('role', 'listbox');\n\tlist.setAttribute('aria-label', locale.blockTypesAria);\n\n\tconst entries = context.getBlockTypePickerRegistry().getBlockTypePickerEntries();\n\tfor (const entry of entries) {\n\t\tconst active: boolean = entry.isActive(state);\n\t\tlist.appendChild(\n\t\t\tcreatePickerItem(\n\t\t\t\tentry.label,\n\t\t\t\tactive,\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\tcontext.executeCommand(entry.command);\n\t\t\t\t\tconst toolbar = context.getService(ToolbarServiceKey);\n\t\t\t\t\ttoolbar?.closePopup();\n\t\t\t\t},\n\t\t\t\tentry.style,\n\t\t\t),\n\t\t);\n\t}\n\n\tcontainer.appendChild(list);\n}\n\nfunction createPickerItem(\n\tlabel: string,\n\tisActive: boolean,\n\thandler: (e: MouseEvent) => void,\n\tstyle?: PickerEntryStyle,\n): HTMLButtonElement {\n\tconst item: HTMLButtonElement = document.createElement('button');\n\titem.type = 'button';\n\titem.className = 'notectl-heading-picker__item';\n\titem.setAttribute('role', 'option');\n\titem.setAttribute('aria-selected', String(isActive));\n\n\tif (isActive) {\n\t\titem.classList.add('notectl-heading-picker__item--active');\n\t}\n\n\tconst check: HTMLSpanElement = document.createElement('span');\n\tcheck.className = 'notectl-heading-picker__check';\n\tcheck.textContent = isActive ? '\\u2713' : '';\n\titem.appendChild(check);\n\n\tconst labelSpan: HTMLSpanElement = document.createElement('span');\n\tlabelSpan.className = 'notectl-heading-picker__label';\n\tlabelSpan.textContent = label;\n\tif (style) {\n\t\tlabelSpan.style.fontSize = style.fontSize;\n\t\tlabelSpan.style.fontWeight = style.fontWeight;\n\t}\n\titem.appendChild(labelSpan);\n\n\titem.addEventListener('mousedown', handler);\n\treturn item;\n}\n\n// --- Helpers ---\n\nfunction getHeadingLabel(locale: HeadingLocale, level: HeadingLevel): string {\n\tconst key = `heading${level}` as keyof HeadingLocale;\n\treturn locale[key] as string;\n}\n\nfunction getActiveLabel(state: EditorState, context: PluginContext, locale: HeadingLocale): string {\n\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return locale.paragraph;\n\tconst entries = context.getBlockTypePickerRegistry().getBlockTypePickerEntries();\n\tfor (const entry of entries) {\n\t\tif (entry.isActive(state)) return entry.label;\n\t}\n\treturn locale.paragraph;\n}\n","/**\n * Command registration and implementations for heading block types.\n * Handles toggle between heading/title/subtitle and paragraph,\n * plus excluded-mark stripping when switching block types.\n */\n\nimport type { BlockNode, Mark } from '../../model/Document.js';\nimport { getBlockLength, getInlineChildren, isTextNode } from '../../model/Document.js';\nimport { isGapCursor, isNodeSelection } from '../../model/Selection.js';\nimport { type NodeTypeName, nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { TransactionBuilder } from '../../state/Transaction.js';\nimport type { PluginContext } from '../Plugin.js';\nimport type { HeadingConfig, HeadingLevel } from './HeadingPlugin.js';\n\n/** Registers all heading commands (setTitle, setSubtitle, setHeadingN, toggleHeading, setParagraph). */\nexport function registerHeadingCommands(context: PluginContext, config: HeadingConfig): void {\n\tcontext.registerCommand('setTitle', () => toggleSpecialBlock(context, 'title'));\n\n\tcontext.registerCommand('setSubtitle', () => toggleSpecialBlock(context, 'subtitle'));\n\n\tfor (const level of config.levels) {\n\t\tcontext.registerCommand(`setHeading${level}`, () => toggleHeading(context, level));\n\t}\n\n\tcontext.registerCommand('toggleHeading', () => toggleHeading(context, 1));\n\n\tcontext.registerCommand('setParagraph', () => setBlockType(context, nodeType('paragraph')));\n}\n\n// --- Command Implementations ---\n\n/**\n * Toggles between a special block type (title/subtitle) and paragraph.\n * If the block is already that type, resets to paragraph.\n */\nfunction toggleSpecialBlock(context: PluginContext, type: string): boolean {\n\tconst state: EditorState = context.getState();\n\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(state.selection.anchor.blockId);\n\tif (!block) return false;\n\n\tif (block.type === type) {\n\t\treturn setBlockType(context, nodeType('paragraph'));\n\t}\n\n\treturn setBlockType(context, nodeType(type) as NodeTypeName);\n}\n\n/**\n * Toggles between heading (at given level) and paragraph.\n * If the block is already a heading at the same level, resets to paragraph.\n */\nfunction toggleHeading(context: PluginContext, level: HeadingLevel): boolean {\n\tconst state: EditorState = context.getState();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tif (block.type === 'heading' && block.attrs?.level === level) {\n\t\treturn setBlockType(context, nodeType('paragraph'));\n\t}\n\n\treturn setBlockType(context, nodeType('heading'), { level });\n}\n\nfunction setBlockType(\n\tcontext: PluginContext,\n\ttype: NodeTypeName,\n\tattrs?: Record<string, string | number | boolean>,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tconst sel = state.selection;\n\tif (isNodeSelection(sel) || isGapCursor(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tconst builder: TransactionBuilder = state.transaction('command');\n\n\tconst spec = context.getSchemaRegistry().getNodeSpec(type);\n\tif (spec?.excludeMarks && spec.excludeMarks.length > 0) {\n\t\tstripExcludedMarks(builder, block, spec.excludeMarks);\n\t\tclearExcludedStoredMarks(builder, state, spec.excludeMarks);\n\t}\n\n\tconst tr = builder.setBlockType(sel.anchor.blockId, type, attrs).setSelection(sel).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n// --- Mark Stripping Helpers ---\n\n/**\n * Adds removeMark steps for each excluded mark type found\n * on the block's inline text content.\n */\nfunction stripExcludedMarks(\n\tbuilder: TransactionBuilder,\n\tblock: BlockNode,\n\texcludeMarks: readonly string[],\n): void {\n\tconst blockLength: number = getBlockLength(block);\n\tif (blockLength === 0) return;\n\n\tconst excludeSet: Set<string> = new Set(excludeMarks);\n\tconst inlineChildren = getInlineChildren(block);\n\tlet offset = 0;\n\n\tfor (const child of inlineChildren) {\n\t\tif (isTextNode(child)) {\n\t\t\tif (child.text.length > 0) {\n\t\t\t\tfor (const mark of child.marks) {\n\t\t\t\t\tif (excludeSet.has(mark.type)) {\n\t\t\t\t\t\tbuilder.removeMark(block.id, offset, offset + child.text.length, mark);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset += child.text.length;\n\t\t} else {\n\t\t\toffset += 1;\n\t\t}\n\t}\n}\n\n/**\n * Clears excluded mark types from stored marks so that\n * subsequent typing does not reintroduce them.\n */\nfunction clearExcludedStoredMarks(\n\tbuilder: TransactionBuilder,\n\tstate: EditorState,\n\texcludeMarks: readonly string[],\n): void {\n\tif (!state.storedMarks) return;\n\n\tconst excludeSet: Set<string> = new Set(excludeMarks);\n\tconst filtered: readonly Mark[] = state.storedMarks.filter((m: Mark) => !excludeSet.has(m.type));\n\n\tif (filtered.length !== state.storedMarks.length) {\n\t\tbuilder.setStoredMarks(filtered.length > 0 ? filtered : null, state.storedMarks);\n\t}\n}\n","/**\n * Keyboard handlers for heading block types.\n * Handles Enter key behavior: empty heading → paragraph, cursor at end → split + paragraph.\n */\n\nimport type { BlockNode } from '../../model/Document.js';\nimport { generateBlockId, getBlockLength, getBlockText } from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport type { HeadingConfig } from './HeadingPlugin.js';\n\n// --- Context Guard ---\n\nconst HEADING_TYPES: ReadonlySet<string> = new Set(['heading', 'title', 'subtitle']);\n\ninterface HeadingContext {\n\treadonly state: EditorState;\n\treadonly block: BlockNode;\n\treadonly blockId: BlockId;\n\treadonly offset: number;\n}\n\n/**\n * Guards against NodeSelection, non-collapsed selections, and non-heading blocks.\n * Returns false (not handled) if the cursor is not inside a heading-type block.\n */\nfunction withHeadingContext(\n\tcontext: PluginContext,\n\thandler: (ctx: HeadingContext) => boolean,\n): boolean {\n\tconst state: EditorState = context.getState();\n\tif (isNodeSelection(state.selection) || isGapCursor(state.selection)) return false;\n\n\tconst sel = state.selection;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\tif (!block || !HEADING_TYPES.has(block.type)) return false;\n\n\treturn handler({\n\t\tstate,\n\t\tblock,\n\t\tblockId: sel.anchor.blockId,\n\t\toffset: sel.anchor.offset,\n\t});\n}\n\n/** Registers all heading keyboard handlers (Enter, Mod-Shift-N shortcuts). */\nexport function registerHeadingKeymaps(context: PluginContext, config: HeadingConfig): void {\n\tconst keymap: Record<string, () => boolean> = {\n\t\tEnter: () => handleEnter(context),\n\t};\n\n\tfor (const level of config.levels) {\n\t\tif (level <= 6) {\n\t\t\tkeymap[`Mod-Shift-${level}`] = () => context.executeCommand(`setHeading${level}`);\n\t\t}\n\t}\n\n\tcontext.registerKeymap(keymap);\n}\n\n// --- Enter Handler ---\n\n/**\n * Handles Enter inside a heading block.\n * Empty heading → convert to paragraph.\n * Cursor at end → split and convert new block to paragraph.\n * Cursor in middle → normal split (both stay heading).\n */\nfunction handleEnter(context: PluginContext): boolean {\n\treturn withHeadingContext(context, (ctx) => {\n\t\tconst text: string = getBlockText(ctx.block);\n\n\t\tif (text === '') {\n\t\t\tconst tr = ctx.state\n\t\t\t\t.transaction('input')\n\t\t\t\t.setBlockType(ctx.blockId, nodeType('paragraph'))\n\t\t\t\t.setSelection(ctx.state.selection)\n\t\t\t\t.build();\n\t\t\tcontext.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst blockLength: number = getBlockLength(ctx.block);\n\n\t\tif (ctx.offset >= blockLength) {\n\t\t\tconst newBlockId = generateBlockId();\n\t\t\tconst tr = ctx.state\n\t\t\t\t.transaction('input')\n\t\t\t\t.splitBlock(ctx.blockId, ctx.offset, newBlockId)\n\t\t\t\t.setBlockType(newBlockId, nodeType('paragraph'))\n\t\t\t\t.setSelection(createCollapsedSelection(newBlockId, 0))\n\t\t\t\t.build();\n\t\t\tcontext.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t});\n}\n","/**\n * Locale interface and default English locale for the HeadingPlugin.\n */\n\n// --- Locale Interface ---\n\nexport interface HeadingLocale {\n\treadonly paragraph: string;\n\treadonly title: string;\n\treadonly subtitle: string;\n\treadonly heading1: string;\n\treadonly heading2: string;\n\treadonly heading3: string;\n\treadonly heading4: string;\n\treadonly heading5: string;\n\treadonly heading6: string;\n\treadonly blockTypeLabel: string;\n\treadonly blockTypesAria: string;\n}\n\n// --- Default English Locale ---\n\nexport const HEADING_LOCALE_EN: HeadingLocale = {\n\tparagraph: 'Paragraph',\n\ttitle: 'Title',\n\tsubtitle: 'Subtitle',\n\theading1: 'Heading 1',\n\theading2: 'Heading 2',\n\theading3: 'Heading 3',\n\theading4: 'Heading 4',\n\theading5: 'Heading 5',\n\theading6: 'Heading 6',\n\tblockTypeLabel: 'Block Type',\n\tblockTypesAria: 'Block types',\n};\n\n// --- German Locale ---\n\nexport const HEADING_LOCALE_DE: HeadingLocale = {\n\tparagraph: 'Absatz',\n\ttitle: 'Titel',\n\tsubtitle: 'Untertitel',\n\theading1: 'Überschrift 1',\n\theading2: 'Überschrift 2',\n\theading3: 'Überschrift 3',\n\theading4: 'Überschrift 4',\n\theading5: 'Überschrift 5',\n\theading6: 'Überschrift 6',\n\tblockTypeLabel: 'Blocktyp',\n\tblockTypesAria: 'Blocktypen',\n};\n\n// --- Spanish Locale ---\n\nexport const HEADING_LOCALE_ES: HeadingLocale = {\n\tparagraph: 'Párrafo',\n\ttitle: 'Título',\n\tsubtitle: 'Subtítulo',\n\theading1: 'Encabezado 1',\n\theading2: 'Encabezado 2',\n\theading3: 'Encabezado 3',\n\theading4: 'Encabezado 4',\n\theading5: 'Encabezado 5',\n\theading6: 'Encabezado 6',\n\tblockTypeLabel: 'Tipo de bloque',\n\tblockTypesAria: 'Tipos de bloque',\n};\n\n// --- French Locale ---\n\nexport const HEADING_LOCALE_FR: HeadingLocale = {\n\tparagraph: 'Paragraphe',\n\ttitle: 'Titre',\n\tsubtitle: 'Sous-titre',\n\theading1: 'Titre 1',\n\theading2: 'Titre 2',\n\theading3: 'Titre 3',\n\theading4: 'Titre 4',\n\theading5: 'Titre 5',\n\theading6: 'Titre 6',\n\tblockTypeLabel: 'Type de bloc',\n\tblockTypesAria: 'Types de bloc',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const HEADING_LOCALE_ZH: HeadingLocale = {\n\tparagraph: '正文',\n\ttitle: '标题',\n\tsubtitle: '副标题',\n\theading1: '一级标题',\n\theading2: '二级标题',\n\theading3: '三级标题',\n\theading4: '四级标题',\n\theading5: '五级标题',\n\theading6: '六级标题',\n\tblockTypeLabel: '块类型',\n\tblockTypesAria: '块类型列表',\n};\n\n// --- Russian Locale ---\n\nexport const HEADING_LOCALE_RU: HeadingLocale = {\n\tparagraph: 'Абзац',\n\ttitle: 'Заголовок',\n\tsubtitle: 'Подзаголовок',\n\theading1: 'Заголовок 1',\n\theading2: 'Заголовок 2',\n\theading3: 'Заголовок 3',\n\theading4: 'Заголовок 4',\n\theading5: 'Заголовок 5',\n\theading6: 'Заголовок 6',\n\tblockTypeLabel: 'Тип блока',\n\tblockTypesAria: 'Типы блоков',\n};\n\n// --- Arabic Locale ---\n\nexport const HEADING_LOCALE_AR: HeadingLocale = {\n\tparagraph: 'فقرة',\n\ttitle: 'عنوان',\n\tsubtitle: 'عنوان فرعي',\n\theading1: 'عنوان 1',\n\theading2: 'عنوان 2',\n\theading3: 'عنوان 3',\n\theading4: 'عنوان 4',\n\theading5: 'عنوان 5',\n\theading6: 'عنوان 6',\n\tblockTypeLabel: 'نوع الكتلة',\n\tblockTypesAria: 'أنواع الكتل',\n};\n\n// --- Hindi Locale ---\n\nexport const HEADING_LOCALE_HI: HeadingLocale = {\n\tparagraph: 'अनुच्छेद',\n\ttitle: 'शीर्षक',\n\tsubtitle: 'उपशीर्षक',\n\theading1: 'शीर्षक 1',\n\theading2: 'शीर्षक 2',\n\theading3: 'शीर्षक 3',\n\theading4: 'शीर्षक 4',\n\theading5: 'शीर्षक 5',\n\theading6: 'शीर्षक 6',\n\tblockTypeLabel: 'ब्लॉक प्रकार',\n\tblockTypesAria: 'ब्लॉक प्रकार सूची',\n};\n\n// --- Locale Map ---\n\nexport const HEADING_LOCALES: Record<string, HeadingLocale> = {\n\ten: HEADING_LOCALE_EN,\n\tde: HEADING_LOCALE_DE,\n\tes: HEADING_LOCALE_ES,\n\tfr: HEADING_LOCALE_FR,\n\tzh: HEADING_LOCALE_ZH,\n\tru: HEADING_LOCALE_RU,\n\tar: HEADING_LOCALE_AR,\n\thi: HEADING_LOCALE_HI,\n};\n","/**\n * HeadingPlugin: registers Title, Subtitle, and H1–H6 heading block types\n * with NodeSpec, toggle commands, keyboard shortcuts, input rules, and a\n * combobox-style toolbar dropdown that reflects the current block type.\n *\n * This file is a thin orchestrator — command logic, input rules, keyboard\n * handlers, and picker/toolbar rendering are delegated to dedicated modules.\n */\n\nimport { HEADING_SELECT_CSS } from '../../editor/styles/heading-select.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport { createBlockElement } from '../../model/NodeSpec.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport type { BlockAlignment } from '../alignment/AlignmentPlugin.js';\nimport {\n\tregisterHeadingPickerEntries,\n\tregisterHeadingToolbarItem,\n} from './HeadingBlockTypePicker.js';\nimport { registerHeadingCommands } from './HeadingCommands.js';\nimport { registerHeadingInputRules } from './HeadingInputRules.js';\nimport { registerHeadingKeymaps } from './HeadingKeyboardHandlers.js';\nimport { HEADING_LOCALES, type HeadingLocale } from './HeadingLocale.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\theading: { level: HeadingLevel; align?: BlockAlignment };\n\t\ttitle: { align?: BlockAlignment };\n\t\tsubtitle: { align?: BlockAlignment };\n\t}\n}\n\n// --- Configuration ---\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport interface HeadingConfig {\n\t/** Which heading levels to enable. Defaults to [1, 2, 3, 4, 5, 6]. */\n\treadonly levels: readonly HeadingLevel[];\n\t/** When true, a separator is rendered after the heading toolbar item. */\n\treadonly separatorAfter?: boolean;\n\treadonly locale?: HeadingLocale;\n}\n\nconst DEFAULT_CONFIG: HeadingConfig = {\n\tlevels: [1, 2, 3, 4, 5, 6],\n};\n\n// --- Heading Tag Mapping ---\n\nconst HEADING_TAGS: Record<HeadingLevel, string> = {\n\t1: 'h1',\n\t2: 'h2',\n\t3: 'h3',\n\t4: 'h4',\n\t5: 'h5',\n\t6: 'h6',\n};\n\n// --- Plugin ---\n\nexport class HeadingPlugin implements Plugin {\n\treadonly id = 'heading';\n\treadonly name = 'Heading';\n\treadonly priority = 30;\n\n\tprivate readonly config: HeadingConfig;\n\tprivate locale!: HeadingLocale;\n\n\tconstructor(config?: Partial<HeadingConfig>) {\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(HEADING_LOCALES, context, this.config.locale);\n\t\tcontext.registerStyleSheet(HEADING_SELECT_CSS);\n\n\t\tthis.registerNodeSpecs(context);\n\t\tregisterHeadingCommands(context, this.config);\n\t\tregisterHeadingKeymaps(context, this.config);\n\t\tregisterHeadingInputRules(context, this.config);\n\t\tregisterHeadingPickerEntries(context, this.config, this.locale);\n\t\tregisterHeadingToolbarItem(context, this.config, this.locale);\n\t}\n\n\tprivate registerNodeSpecs(context: PluginContext): void {\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'title',\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['text'] },\n\t\t\texcludeMarks: ['fontSize'],\n\t\t\ttoDOM(node) {\n\t\t\t\tconst el = createBlockElement('h1', node.id);\n\t\t\t\tel.classList.add('notectl-title');\n\t\t\t\treturn el;\n\t\t\t},\n\t\t\ttoHTML(_node, content) {\n\t\t\t\treturn `<h1>${content || '<br>'}</h1>`;\n\t\t\t},\n\t\t\tsanitize: { tags: ['h1'] },\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'subtitle',\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['text'] },\n\t\t\texcludeMarks: ['fontSize'],\n\t\t\ttoDOM(node) {\n\t\t\t\tconst el = createBlockElement('h2', node.id);\n\t\t\t\tel.classList.add('notectl-subtitle');\n\t\t\t\treturn el;\n\t\t\t},\n\t\t\ttoHTML(_node, content) {\n\t\t\t\treturn `<h2>${content || '<br>'}</h2>`;\n\t\t\t},\n\t\t\tsanitize: { tags: ['h2'] },\n\t\t});\n\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'heading',\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['text'] },\n\t\t\texcludeMarks: ['fontSize'],\n\t\t\tattrs: {\n\t\t\t\tlevel: { default: 1 },\n\t\t\t},\n\t\t\ttoDOM(node) {\n\t\t\t\tconst level = node.attrs?.level ?? 1;\n\t\t\t\tconst tag = HEADING_TAGS[level] ?? 'h1';\n\t\t\t\treturn createBlockElement(tag, node.id);\n\t\t\t},\n\t\t\ttoHTML(node, content) {\n\t\t\t\tconst level = (node.attrs?.level ?? 1) as HeadingLevel;\n\t\t\t\tconst tag: string = HEADING_TAGS[level] ?? 'h1';\n\t\t\t\treturn `<${tag}>${content || '<br>'}</${tag}>`;\n\t\t\t},\n\t\t\tparseHTML: [\n\t\t\t\t{ tag: 'h1', getAttrs: () => ({ level: 1 }) },\n\t\t\t\t{ tag: 'h2', getAttrs: () => ({ level: 2 }) },\n\t\t\t\t{ tag: 'h3', getAttrs: () => ({ level: 3 }) },\n\t\t\t\t{ tag: 'h4', getAttrs: () => ({ level: 4 }) },\n\t\t\t\t{ tag: 'h5', getAttrs: () => ({ level: 5 }) },\n\t\t\t\t{ tag: 'h6', getAttrs: () => ({ level: 6 }) },\n\t\t\t],\n\t\t\tsanitize: { tags: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] },\n\t\t});\n\t}\n}\n","/** Heading picker toolbar combobox + popup styles. */\nexport const HEADING_SELECT_CSS = `\n/* Heading Select — Width overrides for combobox button */\n.notectl-toolbar-btn--heading {\n\tmin-width: 100px;\n\tmax-width: 160px;\n}\n\n/* Heading Picker Popup */\n.notectl-heading-picker {\n\tmin-width: 200px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.notectl-heading-picker__list {\n\tpadding: 4px 0;\n}\n\n.notectl-heading-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\ttext-align: left;\n\twhite-space: nowrap;\n\tfont-family: inherit;\n\ttransition: background 0.1s;\n}\n\n.notectl-heading-picker__item:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-heading-picker__item--active {\n\tbackground: var(--notectl-active-bg);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-heading-picker__item--active:hover {\n\tbackground: var(--notectl-active-bg);\n}\n\n.notectl-heading-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-heading-picker__label {\n\tflex: 1;\n}\n`;\n","/**\n * Input rule registration for heading block types.\n * Converts `# `, `## `, etc. at the start of a paragraph into headings.\n */\n\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../../model/Selection.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { PluginContext } from '../Plugin.js';\nimport type { HeadingConfig } from './HeadingPlugin.js';\n\n/** Registers markdown-style input rules (e.g. `# ` → H1) for configured heading levels. */\nexport function registerHeadingInputRules(context: PluginContext, config: HeadingConfig): void {\n\tfor (const level of config.levels) {\n\t\tconst hashes: string = '#'.repeat(level);\n\t\tconst pattern: RegExp = new RegExp(`^${hashes} $`);\n\n\t\tcontext.registerInputRule({\n\t\t\tpattern,\n\t\t\thandler(state, _match, start, _end) {\n\t\t\t\tconst sel = state.selection;\n\t\t\t\tif (isNodeSelection(sel) || isGapCursor(sel)) return null;\n\t\t\t\tif (!isCollapsed(sel)) return null;\n\n\t\t\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\t\t\tif (!block || block.type !== 'paragraph') return null;\n\n\t\t\t\treturn state\n\t\t\t\t\t.transaction('input')\n\t\t\t\t\t.deleteTextAt(sel.anchor.blockId, start, start + level + 1)\n\t\t\t\t\t.setBlockType(sel.anchor.blockId, nodeType('heading'), { level })\n\t\t\t\t\t.setSelection(createCollapsedSelection(sel.anchor.blockId, 0))\n\t\t\t\t\t.build();\n\t\t\t},\n\t\t});\n\t}\n}\n"],"names":["createPickerItem","label","isActive","handler","style","item","document","createElement","type","className","setAttribute","String","classList","add","check","textContent","appendChild","labelSpan","fontSize","fontWeight","addEventListener","getHeadingLabel","locale","level","toggleSpecialBlock","context","state","getState","isNodeSelection","selection","isGapCursor","block","getBlock","anchor","blockId","setBlockType","nodeType","toggleHeading","sel","attrs","builder","transaction","spec","getSchemaRegistry","getNodeSpec","excludeMarks","length","getBlockLength","excludeSet","Set","inlineChildren","getInlineChildren","offset","child","isTextNode","text","mark","marks","has","removeMark","id","storedMarks","filtered","filter","m","setStoredMarks","tr","setSelection","build","dispatch","HEADING_TYPES","HEADING_LOCALE_EN","paragraph","title","subtitle","heading1","heading2","heading3","heading4","heading5","heading6","blockTypeLabel","blockTypesAria","HEADING_LOCALES","en","de","es","fr","zh","ru","ar","hi","DEFAULT_CONFIG","levels","HEADING_TAGS","HeadingPlugin","name","priority","config","this","resolvePluginLocale","registerStyleSheet","registerNodeSpecs","registerCommand","keymap","Enter","isCollapsed","ctx","getBlockText","blockLength","newBlockId","generateBlockId","splitBlock","createCollapsedSelection","executeCommand","registerKeymap","hashes","repeat","pattern","RegExp","registerInputRule","_match","start","_end","deleteTextAt","registerBlockTypePickerEntry","command","registerToolbarItem","group","tooltip","popupType","separatorAfter","getLabel","entries","getBlockTypePickerRegistry","getBlockTypePickerEntries","entry","renderPopup","container","list","active","e","preventDefault","stopPropagation","toolbar","getService","ToolbarServiceKey","closePopup","some","registerNodeSpec","content","allow","node","el","createBlockElement","toHTML","_node","sanitize","tags","default","tag","parseHTML","getAttrs"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-yhz2qeeh.mjs","sources":["../../src/plugins/image/ImageCommands.ts","../../src/plugins/image/ImageLocale.ts","../../src/plugins/image/ImageNodeView.ts","../../src/plugins/image/ImageUpload.ts","../../src/plugins/image/ImagePlugin.ts","../../src/editor/styles/image.ts"],"sourcesContent":["/**\n * Image plugin commands: insert, remove, and attribute updates.\n * Supports both root-level and nested contexts (e.g. images inside table cells).\n */\n\nimport type { BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { createBlockNode, getBlockChildren, isBlockNode } from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tcreateNodeSelection,\n\tisGapCursor,\n\tisNodeSelection,\n} from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport type { ImageAttrs } from './ImageUpload.js';\n\n/**\n * Inserts an image block. When the cursor is inside a table cell,\n * the image is inserted as a child of the cell. Otherwise it is\n * inserted after the current block at document root.\n */\nexport function insertImage(\n\tcontext: PluginContext,\n\tattrs: Partial<ImageAttrs> & { readonly src: string },\n): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\n\tconst imageAttrs: BlockAttrs = {\n\t\tsrc: attrs.src,\n\t\talt: attrs.alt ?? '',\n\t\talign: attrs.align ?? 'center',\n\t\t...(attrs.width !== undefined ? { width: attrs.width } : {}),\n\t\t...(attrs.height !== undefined ? { height: attrs.height } : {}),\n\t};\n\n\tif (isGapCursor(sel)) return false;\n\n\tconst anchorBlockId: BlockId = isNodeSelection(sel) ? sel.nodeId : sel.anchor.blockId;\n\n\t// Check if we're inside a table cell\n\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\tif (cellId) {\n\t\treturn insertImageIntoCell(state, context, cellId, imageAttrs);\n\t}\n\n\treturn insertImageAtRoot(state, context, anchorBlockId, imageAttrs);\n}\n\n/** Finds a table_cell ancestor for the given block (or the block itself). */\nfunction findTableCellAncestor(state: EditorState, bid: BlockId): BlockId | undefined {\n\tconst block: BlockNode | undefined = state.getBlock(bid);\n\tif (block?.type === 'table_cell') return bid;\n\n\tconst path: BlockId[] | undefined = state.getNodePath(bid);\n\tif (!path) return undefined;\n\n\tfor (const id of path) {\n\t\tconst node: BlockNode | undefined = state.getBlock(id);\n\t\tif (node?.type === 'table_cell') return id;\n\t}\n\treturn undefined;\n}\n\n/** Inserts an image at document root after the anchor block. */\nfunction insertImageAtRoot(\n\tstate: EditorState,\n\tcontext: PluginContext,\n\tanchorBlockId: BlockId,\n\timageAttrs: BlockAttrs,\n): boolean {\n\tconst blockIndex: number = state.doc.children.findIndex((b) => b.id === anchorBlockId);\n\tif (blockIndex === -1) return false;\n\n\tconst imageBlock: BlockNode = createBlockNode(nodeType('image'), [], undefined, imageAttrs);\n\tconst newParagraph: BlockNode = createBlockNode(nodeType('paragraph'));\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.insertNode([], blockIndex + 1, imageBlock)\n\t\t.insertNode([], blockIndex + 2, newParagraph)\n\t\t.setSelection(createNodeSelection(imageBlock.id, []))\n\t\t.build();\n\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Inserts an image as a child of a table cell. */\nfunction insertImageIntoCell(\n\tstate: EditorState,\n\tcontext: PluginContext,\n\tcellId: BlockId,\n\timageAttrs: BlockAttrs,\n): boolean {\n\tconst cellPath: BlockId[] | undefined = state.getNodePath(cellId);\n\tif (!cellPath) return false;\n\n\tconst imageBlock: BlockNode = createBlockNode(nodeType('image'), [], undefined, imageAttrs);\n\n\t// Remove existing block children first (if any)\n\tconst cell: BlockNode | undefined = state.getBlock(cellId);\n\tif (!cell) return false;\n\n\tconst builder = state.transaction('command');\n\tconst blockChildren: readonly BlockNode[] = getBlockChildren(cell);\n\tfor (let i = blockChildren.length - 1; i >= 0; i--) {\n\t\t// Find the raw index in children array\n\t\tconst rawIndex: number = cell.children.findIndex(\n\t\t\t(c) => isBlockNode(c) && c.id === blockChildren[i]?.id,\n\t\t);\n\t\tif (rawIndex !== -1) {\n\t\t\tbuilder.removeNode(cellPath, rawIndex);\n\t\t}\n\t}\n\n\t// Insert image at index 0 (before any text nodes)\n\tbuilder.insertNode(cellPath, 0, imageBlock);\n\tbuilder.setSelection(createNodeSelection(imageBlock.id, [...cellPath, imageBlock.id]));\n\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\n/** Removes the currently selected image block (works for nested images too). */\nexport function removeImage(context: PluginContext): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\tif (!isNodeSelection(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\tif (!block || block.type !== 'image') return false;\n\n\tconst nodePath: BlockId[] | undefined = state.getNodePath(sel.nodeId);\n\tif (!nodePath || nodePath.length === 0) return false;\n\n\t// parentPath = all IDs except the last (the image itself)\n\tconst parentPath: BlockId[] = nodePath.slice(0, -1);\n\n\t// Find the index of the image in its parent's children\n\tlet parent: BlockNode | { readonly children: readonly BlockNode[] } | undefined;\n\tif (parentPath.length === 0) {\n\t\tparent = state.doc;\n\t} else {\n\t\tconst parentId: BlockId | undefined = parentPath[parentPath.length - 1];\n\t\tparent = parentId ? state.getBlock(parentId) : undefined;\n\t}\n\tif (!parent) return false;\n\n\tconst imageIndex: number = parent.children.findIndex(\n\t\t(c) => isBlockNode(c) && c.id === sel.nodeId,\n\t);\n\tif (imageIndex === -1) return false;\n\n\t// Determine where to place the cursor after removal\n\tconst selectionTarget: BlockId | undefined = findSelectionTargetAfterRemoval(\n\t\tstate,\n\t\tparentPath,\n\t\tsel.nodeId,\n\t);\n\tif (!selectionTarget) return false;\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.removeNode(parentPath, imageIndex)\n\t\t.setSelection(createCollapsedSelection(selectionTarget, 0))\n\t\t.build();\n\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Finds a suitable block to select after removing an image. */\nfunction findSelectionTargetAfterRemoval(\n\tstate: EditorState,\n\tparentPath: readonly BlockId[],\n\tremovedId: BlockId,\n): BlockId | undefined {\n\tif (parentPath.length === 0) {\n\t\t// Root level: select adjacent block\n\t\tconst rootChildren: readonly BlockNode[] = state.doc.children;\n\t\tconst idx: number = rootChildren.findIndex((b) => b.id === removedId);\n\t\tconst adjacent: BlockNode | undefined = rootChildren[idx + 1] ?? rootChildren[idx - 1];\n\t\treturn adjacent?.id;\n\t}\n\n\t// Nested (e.g. inside table cell): select the cell itself\n\tconst cellId: BlockId | undefined = parentPath[parentPath.length - 1];\n\treturn cellId;\n}\n\n/** Updates attributes on the currently selected image block. */\nexport function setImageAttr(context: PluginContext, attrs: Partial<ImageAttrs>): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\tif (!isNodeSelection(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\tif (!block || block.type !== 'image') return false;\n\n\tconst path: BlockId[] | undefined = state.getNodePath(sel.nodeId);\n\tif (!path) return false;\n\n\tconst merged: BlockAttrs = { ...(block.attrs ?? {}), ...attrs };\n\tconst tr = state.transaction('command').setNodeAttr(path, merged).build();\n\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Minimum image width in pixels (shared with NodeView). */\nexport const MIN_IMAGE_WIDTH = 50;\n\n/**\n * Resizes the currently selected image by a pixel delta, preserving aspect ratio.\n * Returns false if no image is selected or the image has no width/height attributes.\n */\nexport function resizeImageByDelta(\n\tcontext: PluginContext,\n\tdelta: number,\n\tmaxWidth: number,\n): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\tif (!isNodeSelection(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\tif (!block || block.type !== 'image') return false;\n\n\tconst currentWidth: number | undefined = block.attrs?.width as number | undefined;\n\tconst currentHeight: number | undefined = block.attrs?.height as number | undefined;\n\tif (currentWidth === undefined || currentHeight === undefined) return false;\n\n\tconst aspectRatio: number = currentHeight > 0 ? currentWidth / currentHeight : 1;\n\tconst clampedWidth: number = Math.max(MIN_IMAGE_WIDTH, Math.min(maxWidth, currentWidth + delta));\n\tconst newHeight: number = Math.round(clampedWidth / aspectRatio);\n\n\tif (clampedWidth === currentWidth && newHeight === currentHeight) return false;\n\n\treturn setImageAttr(context, { width: clampedWidth, height: newHeight });\n}\n\n/** Resets the currently selected image to its natural size by removing width/height. */\nexport function resetImageSize(context: PluginContext): boolean {\n\tconst state = context.getState();\n\tconst sel = state.selection;\n\tif (!isNodeSelection(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\tif (!block || block.type !== 'image') return false;\n\n\tconst path: BlockId[] | undefined = state.getNodePath(sel.nodeId);\n\tif (!path) return false;\n\n\tconst attrs: BlockAttrs = { ...(block.attrs ?? {}) };\n\tconst cleaned: BlockAttrs = Object.fromEntries(\n\t\tObject.entries(attrs).filter(([k]) => k !== 'width' && k !== 'height'),\n\t);\n\n\tconst tr = state.transaction('command').setNodeAttr(path, cleaned).build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n/** Registers all image commands on the plugin context. */\nexport function registerImageCommands(context: PluginContext): void {\n\tcontext.registerCommand('insertImage', () => {\n\t\treturn insertImage(context, { src: '' });\n\t});\n\n\tcontext.registerCommand('removeImage', () => {\n\t\treturn removeImage(context);\n\t});\n}\n","/** Locale interface and default English locale for the ImagePlugin. */\n\n// --- Locale Interface ---\n\nexport interface ImageLocale {\n\treadonly insertImage: string;\n\treadonly insertImageTooltip: string;\n\treadonly uploadFromComputer: string;\n\treadonly uploadAria: string;\n\treadonly separator: string;\n\treadonly urlPlaceholder: string;\n\treadonly urlAria: string;\n\treadonly insertButton: string;\n\treadonly insertAria: string;\n\treadonly imageAria: (alt: string, width?: number, height?: number) => string;\n\treadonly resetToNaturalSize: string;\n\treadonly uploadFailed: string;\n}\n\n// --- Default English Locale ---\n\nexport const IMAGE_LOCALE_EN: ImageLocale = {\n\tinsertImage: 'Insert Image',\n\tinsertImageTooltip: 'Insert Image',\n\tuploadFromComputer: 'Upload from computer',\n\tuploadAria: 'Upload image from computer',\n\tseparator: 'or',\n\turlPlaceholder: 'https://...',\n\turlAria: 'Image URL',\n\tinsertButton: 'Insert',\n\tinsertAria: 'Insert image',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'Image'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} by ${height} pixels`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'Image reset to natural size.',\n\tuploadFailed: 'Image upload failed.',\n};\n\n// --- German Locale ---\n\nexport const IMAGE_LOCALE_DE: ImageLocale = {\n\tinsertImage: 'Bild einfügen',\n\tinsertImageTooltip: 'Bild einfügen',\n\tuploadFromComputer: 'Vom Computer hochladen',\n\tuploadAria: 'Bild vom Computer hochladen',\n\tseparator: 'oder',\n\turlPlaceholder: 'https://...',\n\turlAria: 'Bild-URL',\n\tinsertButton: 'Einfügen',\n\tinsertAria: 'Bild einfügen',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'Bild'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} mal ${height} Pixel`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'Bild auf Originalgröße zurückgesetzt.',\n\tuploadFailed: 'Bild-Upload fehlgeschlagen.',\n};\n\n// --- Spanish Locale ---\n\nexport const IMAGE_LOCALE_ES: ImageLocale = {\n\tinsertImage: 'Insertar imagen',\n\tinsertImageTooltip: 'Insertar imagen',\n\tuploadFromComputer: 'Subir desde el ordenador',\n\tuploadAria: 'Subir imagen desde el ordenador',\n\tseparator: 'o',\n\turlPlaceholder: 'https://...',\n\turlAria: 'URL de la imagen',\n\tinsertButton: 'Insertar',\n\tinsertAria: 'Insertar imagen',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'Imagen'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} por ${height} píxeles`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'Imagen restablecida a tamaño natural.',\n\tuploadFailed: 'Error al subir la imagen.',\n};\n\n// --- French Locale ---\n\nexport const IMAGE_LOCALE_FR: ImageLocale = {\n\tinsertImage: 'Insérer une image',\n\tinsertImageTooltip: 'Insérer une image',\n\tuploadFromComputer: 'Télécharger depuis l\\u2019ordinateur',\n\tuploadAria: 'Télécharger une image depuis l\\u2019ordinateur',\n\tseparator: 'ou',\n\turlPlaceholder: 'https://...',\n\turlAria: 'URL de l\\u2019image',\n\tinsertButton: 'Insérer',\n\tinsertAria: 'Insérer une image',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'Image'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} par ${height} pixels`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'Image rétablie à sa taille d\\u2019origine.',\n\tuploadFailed: 'Échec du téléchargement de l\\u2019image.',\n};\n\n// --- Chinese (Simplified) Locale ---\n\nexport const IMAGE_LOCALE_ZH: ImageLocale = {\n\tinsertImage: '插入图片',\n\tinsertImageTooltip: '插入图片',\n\tuploadFromComputer: '从电脑上传',\n\tuploadAria: '从电脑上传图片',\n\tseparator: '或',\n\turlPlaceholder: 'https://...',\n\turlAria: '图片链接',\n\tinsertButton: '插入',\n\tinsertAria: '插入图片',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || '图片'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} x ${height} 像素`);\n\t\t}\n\t\treturn parts.join(',');\n\t},\n\tresetToNaturalSize: '图片已恢复原始大小。',\n\tuploadFailed: '图片上传失败。',\n};\n\n// --- Russian Locale ---\n\nexport const IMAGE_LOCALE_RU: ImageLocale = {\n\tinsertImage: 'Вставить изображение',\n\tinsertImageTooltip: 'Вставить изображение',\n\tuploadFromComputer: 'Загрузить с компьютера',\n\tuploadAria: 'Загрузить изображение с компьютера',\n\tseparator: 'или',\n\turlPlaceholder: 'https://...',\n\turlAria: 'URL изображения',\n\tinsertButton: 'Вставить',\n\tinsertAria: 'Вставить изображение',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'Изображение'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} на ${height} пикселей`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'Изображение сброшено до исходного размера.',\n\tuploadFailed: 'Ошибка загрузки изображения.',\n};\n\n// --- Arabic Locale ---\n\nexport const IMAGE_LOCALE_AR: ImageLocale = {\n\tinsertImage: 'إدراج صورة',\n\tinsertImageTooltip: 'إدراج صورة',\n\tuploadFromComputer: 'رفع من الحاسوب',\n\tuploadAria: 'رفع صورة من الحاسوب',\n\tseparator: 'أو',\n\turlPlaceholder: 'https://...',\n\turlAria: 'رابط الصورة',\n\tinsertButton: 'إدراج',\n\tinsertAria: 'إدراج صورة',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'صورة'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} × ${height} بكسل`);\n\t\t}\n\t\treturn parts.join('، ');\n\t},\n\tresetToNaturalSize: 'تمت إعادة الصورة إلى حجمها الطبيعي.',\n\tuploadFailed: 'فشل رفع الصورة.',\n};\n\n// --- Hindi Locale ---\n\nexport const IMAGE_LOCALE_HI: ImageLocale = {\n\tinsertImage: 'चित्र डालें',\n\tinsertImageTooltip: 'चित्र डालें',\n\tuploadFromComputer: 'कंप्यूटर से अपलोड करें',\n\tuploadAria: 'कंप्यूटर से चित्र अपलोड करें',\n\tseparator: 'या',\n\turlPlaceholder: 'https://...',\n\turlAria: 'चित्र URL',\n\tinsertButton: 'डालें',\n\tinsertAria: 'चित्र डालें',\n\timageAria: (alt: string, width?: number, height?: number) => {\n\t\tconst parts: string[] = [alt || 'चित्र'];\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`${width} x ${height} पिक्सेल`);\n\t\t}\n\t\treturn parts.join(', ');\n\t},\n\tresetToNaturalSize: 'चित्र मूल आकार में पुनर्स्थापित।',\n\tuploadFailed: 'चित्र अपलोड विफल।',\n};\n\n// --- Locale Map ---\n\nexport const IMAGE_LOCALES: Record<string, ImageLocale> = {\n\ten: IMAGE_LOCALE_EN,\n\tde: IMAGE_LOCALE_DE,\n\tes: IMAGE_LOCALE_ES,\n\tfr: IMAGE_LOCALE_FR,\n\tzh: IMAGE_LOCALE_ZH,\n\tru: IMAGE_LOCALE_RU,\n\tar: IMAGE_LOCALE_AR,\n\thi: IMAGE_LOCALE_HI,\n};\n","/**\n * NodeView factory for image blocks.\n * Renders <figure> with <img>, handles selection state, upload overlay,\n * resize with 4 corner handles + live size indicator, and a keyboard-hint\n * showing resize shortcuts when the image is selected.\n */\n\nimport type { BlockAttrs, BlockNode } from '../../model/Document.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport type { NodeView, NodeViewFactory } from '../../view/NodeView.js';\nimport { formatShortcut } from '../toolbar/ToolbarItem.js';\nimport type { ImageKeymap, ImagePluginConfig, UploadState } from './ImageUpload.js';\n\nconst ALIGNMENT_CLASSES: Record<string, string> = {\n\tleft: 'notectl-image--left',\n\tcenter: 'notectl-image--center',\n\tright: 'notectl-image--right',\n};\n\nconst MIN_IMAGE_WIDTH = 50;\n\ntype HandlePosition = 'nw' | 'ne' | 'sw' | 'se';\n\nconst HANDLE_CURSORS: Readonly<Record<HandlePosition, string>> = {\n\tnw: 'nwse-resize',\n\tne: 'nesw-resize',\n\tsw: 'nesw-resize',\n\tse: 'nwse-resize',\n};\n\n/** +1 means dragging right increases width; -1 means dragging left does. */\nconst HANDLE_X_SIGN: Readonly<Record<HandlePosition, 1 | -1>> = {\n\tnw: -1,\n\tne: 1,\n\tsw: -1,\n\tse: 1,\n};\n\nconst HANDLE_LABELS: Readonly<Record<HandlePosition, string>> = {\n\tnw: 'Resize top-left',\n\tne: 'Resize top-right',\n\tsw: 'Resize bottom-left',\n\tse: 'Resize bottom-right',\n};\n\n// --- Global cursor override during resize ---\n\nlet activeCursorStyle: HTMLStyleElement | null = null;\n\nfunction setGlobalResizeCursor(cursor: string): void {\n\tclearGlobalResizeCursor();\n\tactiveCursorStyle = document.createElement('style');\n\tactiveCursorStyle.textContent = `*{cursor:${cursor}!important;user-select:none!important}`;\n\tdocument.head.appendChild(activeCursorStyle);\n}\n\nfunction clearGlobalResizeCursor(): void {\n\tif (activeCursorStyle) {\n\t\tactiveCursorStyle.remove();\n\t\tactiveCursorStyle = null;\n\t}\n}\n\n/** Builds the keyboard-hint text from the resolved keymap. */\nfunction buildKeyboardHintText(\n\tresolvedKeymap: Readonly<Record<keyof ImageKeymap, string | null>>,\n): string {\n\tconst shrink: string | null = resolvedKeymap.shrinkWidth ?? null;\n\tconst grow: string | null = resolvedKeymap.growWidth ?? null;\n\tif (!shrink || !grow) return '';\n\treturn `${formatShortcut(shrink)} / ${formatShortcut(grow)} to resize`;\n}\n\n/** Creates a NodeViewFactory for image blocks. */\nexport function createImageNodeViewFactory(\n\tconfig: ImagePluginConfig,\n\tuploadStates: Map<BlockId, UploadState>,\n\tresolvedKeymap?: Readonly<Record<keyof ImageKeymap, string | null>>,\n): NodeViewFactory {\n\treturn (\n\t\tnode: BlockNode,\n\t\tgetState: () => EditorState,\n\t\tdispatch: (tr: Transaction) => void,\n\t): NodeView => {\n\t\t// --- DOM Construction ---\n\t\tconst figure: HTMLElement = document.createElement('figure');\n\t\tfigure.className = 'notectl-image';\n\t\tfigure.setAttribute('data-block-id', node.id);\n\t\tfigure.setAttribute('data-void', 'true');\n\t\tfigure.setAttribute('data-selectable', 'true');\n\n\t\tconst container: HTMLDivElement = document.createElement('div');\n\t\tcontainer.className = 'notectl-image__container';\n\n\t\tconst img: HTMLImageElement = document.createElement('img');\n\t\timg.className = 'notectl-image__img';\n\t\timg.draggable = false;\n\n\t\tconst overlay: HTMLDivElement = document.createElement('div');\n\t\toverlay.className = 'notectl-image__overlay';\n\n\t\tcontainer.appendChild(img);\n\t\tcontainer.appendChild(overlay);\n\t\tfigure.appendChild(container);\n\n\t\tlet currentNodeId: BlockId = node.id;\n\t\tlet resizeOverlay: HTMLDivElement | null = null;\n\n\t\t// --- ARIA Label ---\n\n\t\tfunction updateAriaLabel(n: BlockNode): void {\n\t\t\tconst alt: string = (n.attrs?.alt as string | undefined) ?? '';\n\t\t\tconst width: number | undefined = n.attrs?.width as number | undefined;\n\t\t\tconst height: number | undefined = n.attrs?.height as number | undefined;\n\n\t\t\tconst parts: string[] = [];\n\t\t\tif (alt) {\n\t\t\t\tparts.push(alt);\n\t\t\t} else {\n\t\t\t\tparts.push('Image');\n\t\t\t}\n\t\t\tif (width !== undefined && height !== undefined) {\n\t\t\t\tparts.push(`${width} by ${height} pixels`);\n\t\t\t}\n\t\t\tfigure.setAttribute('aria-label', parts.join(', '));\n\t\t}\n\n\t\t// --- Attribute Application ---\n\n\t\tfunction applyAttrs(n: BlockNode): void {\n\t\t\tconst src: string = (n.attrs?.src as string | undefined) ?? '';\n\t\t\tconst alt: string = (n.attrs?.alt as string | undefined) ?? '';\n\t\t\tconst width: number | undefined = n.attrs?.width as number | undefined;\n\t\t\tconst height: number | undefined = n.attrs?.height as number | undefined;\n\t\t\tconst align: string = (n.attrs?.align as string | undefined) ?? 'center';\n\n\t\t\t// Only update src when it changes to avoid re-triggering load events\n\t\t\tif (img.src !== src) img.src = src;\n\t\t\timg.alt = alt;\n\n\t\t\tif (width !== undefined) {\n\t\t\t\timg.style.width = `${width}px`;\n\t\t\t} else {\n\t\t\t\timg.style.width = '';\n\t\t\t}\n\t\t\tif (height !== undefined) {\n\t\t\t\timg.style.height = `${height}px`;\n\t\t\t} else {\n\t\t\t\timg.style.height = '';\n\t\t\t}\n\n\t\t\tfor (const cls of Object.values(ALIGNMENT_CLASSES)) {\n\t\t\t\tfigure.classList.remove(cls);\n\t\t\t}\n\t\t\tconst alignClass: string | undefined = ALIGNMENT_CLASSES[align];\n\t\t\tif (alignClass) {\n\t\t\t\tfigure.classList.add(alignClass);\n\t\t\t}\n\n\t\t\tconst uploadState: UploadState = uploadStates.get(n.id) ?? 'idle';\n\t\t\tconst isUploading: boolean = uploadState === 'uploading';\n\t\t\toverlay.classList.toggle('notectl-image__overlay--uploading', isUploading);\n\t\t\toverlay.classList.toggle('notectl-image__overlay--error', uploadState === 'error');\n\n\t\t\tif (isUploading) {\n\t\t\t\toverlay.textContent = 'Uploading...';\n\t\t\t} else if (uploadState === 'error') {\n\t\t\t\toverlay.textContent = 'Upload failed';\n\t\t\t} else {\n\t\t\t\toverlay.textContent = '';\n\t\t\t}\n\n\t\t\tif (isUploading) {\n\t\t\t\tfigure.setAttribute('aria-busy', 'true');\n\t\t\t} else {\n\t\t\t\tfigure.removeAttribute('aria-busy');\n\t\t\t}\n\n\t\t\tupdateAriaLabel(n);\n\t\t}\n\n\t\tapplyAttrs(node);\n\n\t\t// --- Commit natural dimensions on first load ---\n\n\t\tlet dimensionsCommitted = false;\n\n\t\timg.addEventListener('load', () => {\n\t\t\tif (dimensionsCommitted) return;\n\n\t\t\tconst state: EditorState = getState();\n\t\t\tconst block: BlockNode | undefined = state.getBlock(currentNodeId);\n\t\t\tif (!block || block.type !== 'image') return;\n\n\t\t\tconst hasWidth: boolean = block.attrs?.width !== undefined;\n\t\t\tconst hasHeight: boolean = block.attrs?.height !== undefined;\n\t\t\tif (hasWidth && hasHeight) {\n\t\t\t\tdimensionsCommitted = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst naturalW: number = img.naturalWidth;\n\t\t\tconst naturalH: number = img.naturalHeight;\n\t\t\tif (naturalW === 0 || naturalH === 0) return;\n\n\t\t\tconst clampedW: number = Math.min(naturalW, config.maxWidth);\n\t\t\tconst ratio: number = naturalW > 0 ? naturalH / naturalW : 1;\n\t\t\tconst clampedH: number = Math.round(clampedW * ratio);\n\n\t\t\tconst path: BlockId[] | undefined = state.getNodePath(currentNodeId);\n\t\t\tif (!path) return;\n\n\t\t\tconst merged: BlockAttrs = {\n\t\t\t\t...(block.attrs ?? {}),\n\t\t\t\twidth: clampedW,\n\t\t\t\theight: clampedH,\n\t\t\t};\n\n\t\t\tconst tr: Transaction = state.transaction('command').setNodeAttr(path, merged).build();\n\t\t\tdispatch(tr);\n\t\t\tdimensionsCommitted = true;\n\t\t});\n\n\t\t// --- Resize Logic ---\n\n\t\tfunction clampWidth(width: number): number {\n\t\t\treturn Math.max(MIN_IMAGE_WIDTH, Math.min(config.maxWidth, width));\n\t\t}\n\n\t\tfunction commitResize(nodeId: BlockId, width: number, height: number): void {\n\t\t\tconst state: EditorState = getState();\n\t\t\tconst block: BlockNode | undefined = state.getBlock(nodeId);\n\t\t\tif (!block) return;\n\n\t\t\tconst prevWidth: number | undefined = block.attrs?.width as number | undefined;\n\t\t\tconst prevHeight: number | undefined = block.attrs?.height as number | undefined;\n\t\t\tif (prevWidth === width && prevHeight === height) return;\n\n\t\t\tconst path: BlockId[] | undefined = state.getNodePath(nodeId);\n\t\t\tif (!path) return;\n\n\t\t\tconst merged: BlockAttrs = {\n\t\t\t\t...(block.attrs ?? {}),\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t};\n\n\t\t\tconst tr: Transaction = state.transaction('command').setNodeAttr(path, merged).build();\n\t\t\tdispatch(tr);\n\t\t}\n\n\t\tfunction attachHandleListeners(\n\t\t\thandle: HTMLDivElement,\n\t\t\tposition: HandlePosition,\n\t\t\tnodeId: BlockId,\n\t\t\tsizeIndicator: HTMLDivElement,\n\t\t): void {\n\t\t\tlet startX = 0;\n\t\t\tlet startWidth = 0;\n\t\t\tlet aspectRatio = 1;\n\n\t\t\tconst onPointerMove = (e: PointerEvent): void => {\n\t\t\t\tconst deltaX: number = e.clientX - startX;\n\t\t\t\tconst newWidth: number = clampWidth(startWidth + deltaX * HANDLE_X_SIGN[position]);\n\t\t\t\tconst newHeight: number = Math.round(newWidth / aspectRatio);\n\n\t\t\t\timg.style.width = `${newWidth}px`;\n\t\t\t\timg.style.height = `${newHeight}px`;\n\n\t\t\t\tsizeIndicator.textContent = `${Math.round(newWidth)} \\u00D7 ${newHeight}`;\n\t\t\t};\n\n\t\t\tconst onPointerUp = (e: PointerEvent): void => {\n\t\t\t\tdocument.removeEventListener('pointermove', onPointerMove);\n\t\t\t\tdocument.removeEventListener('pointerup', onPointerUp);\n\n\t\t\t\tfigure.classList.remove('notectl-image--resizing');\n\t\t\t\tsizeIndicator.classList.remove('notectl-image__size-indicator--visible');\n\t\t\t\tclearGlobalResizeCursor();\n\n\t\t\t\tconst finalWidth: number = Math.round(img.getBoundingClientRect().width);\n\t\t\t\tconst finalHeight: number = Math.round(finalWidth / aspectRatio);\n\n\t\t\t\tcommitResize(nodeId, finalWidth, finalHeight);\n\n\t\t\t\t(e.target as HTMLElement | null)?.releasePointerCapture?.(e.pointerId);\n\t\t\t};\n\n\t\t\thandle.addEventListener('pointerdown', (e: PointerEvent) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\n\t\t\t\tstartX = e.clientX;\n\t\t\t\tstartWidth = img.getBoundingClientRect().width;\n\t\t\t\tconst imgHeight: number = img.getBoundingClientRect().height;\n\t\t\t\taspectRatio = imgHeight > 0 ? startWidth / imgHeight : 1;\n\n\t\t\t\tfigure.classList.add('notectl-image--resizing');\n\t\t\t\tsizeIndicator.textContent = `${Math.round(startWidth)} \\u00D7 ${Math.round(imgHeight)}`;\n\t\t\t\tsizeIndicator.classList.add('notectl-image__size-indicator--visible');\n\t\t\t\tsetGlobalResizeCursor(HANDLE_CURSORS[position]);\n\n\t\t\t\t(e.target as HTMLElement).setPointerCapture(e.pointerId);\n\t\t\t\tdocument.addEventListener('pointermove', onPointerMove);\n\t\t\t\tdocument.addEventListener('pointerup', onPointerUp);\n\t\t\t});\n\t\t}\n\n\t\tfunction createResizeOverlay(nodeId: BlockId): void {\n\t\t\tif (!config.resizable || resizeOverlay) return;\n\n\t\t\tresizeOverlay = document.createElement('div');\n\t\t\tresizeOverlay.className = 'notectl-image__resize-overlay';\n\t\t\tresizeOverlay.setAttribute('data-notectl-no-print', '');\n\n\t\t\tconst sizeIndicator: HTMLDivElement = document.createElement('div');\n\t\t\tsizeIndicator.className = 'notectl-image__size-indicator';\n\t\t\tresizeOverlay.appendChild(sizeIndicator);\n\n\t\t\tconst positions: readonly HandlePosition[] = ['nw', 'ne', 'sw', 'se'];\n\t\t\tfor (const pos of positions) {\n\t\t\t\tconst handle: HTMLDivElement = document.createElement('div');\n\t\t\t\thandle.className = `notectl-image__resize-handle notectl-image__resize-handle--${pos}`;\n\t\t\t\thandle.setAttribute('role', 'separator');\n\t\t\t\thandle.setAttribute('aria-label', HANDLE_LABELS[pos]);\n\t\t\t\tattachHandleListeners(handle, pos, nodeId, sizeIndicator);\n\t\t\t\tresizeOverlay.appendChild(handle);\n\t\t\t}\n\n\t\t\t// Keyboard hint (hidden from screenreaders, visual only)\n\t\t\tif (resolvedKeymap) {\n\t\t\t\tconst hintText: string = buildKeyboardHintText(resolvedKeymap);\n\t\t\t\tif (hintText) {\n\t\t\t\t\tconst hint: HTMLDivElement = document.createElement('div');\n\t\t\t\t\thint.className = 'notectl-image__keyboard-hint';\n\t\t\t\t\thint.setAttribute('aria-hidden', 'true');\n\t\t\t\t\thint.textContent = hintText;\n\t\t\t\t\tresizeOverlay.appendChild(hint);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontainer.appendChild(resizeOverlay);\n\t\t}\n\n\t\tfunction removeResizeOverlay(): void {\n\t\t\tclearGlobalResizeCursor();\n\t\t\tif (resizeOverlay) {\n\t\t\t\tresizeOverlay.remove();\n\t\t\t\tresizeOverlay = null;\n\t\t\t}\n\t\t}\n\n\t\t// --- NodeView Interface ---\n\n\t\treturn {\n\t\t\tdom: figure,\n\t\t\tcontentDOM: null,\n\n\t\t\tupdate(updatedNode: BlockNode): boolean {\n\t\t\t\tif (updatedNode.type !== 'image') return false;\n\t\t\t\tcurrentNodeId = updatedNode.id;\n\t\t\t\tfigure.setAttribute('data-block-id', updatedNode.id);\n\t\t\t\tapplyAttrs(updatedNode);\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\tselectNode(): void {\n\t\t\t\tfigure.classList.add('notectl-image--selected');\n\t\t\t\tcreateResizeOverlay(currentNodeId);\n\t\t\t},\n\n\t\t\tdeselectNode(): void {\n\t\t\t\tfigure.classList.remove('notectl-image--selected');\n\t\t\t\tremoveResizeOverlay();\n\t\t\t},\n\n\t\t\tdestroy(): void {\n\t\t\t\tremoveResizeOverlay();\n\t\t\t\t// Do NOT revoke blob URLs here — the URL may still be referenced\n\t\t\t\t// by the clipboard (cut/paste) or undo history. Blob URLs are\n\t\t\t\t// automatically released when the document is unloaded.\n\t\t\t},\n\t\t};\n\t};\n}\n","/**\n * Image plugin types, configuration, and service key.\n */\n\nimport { ServiceKey } from '../Plugin.js';\nimport type { ImageLocale } from './ImageLocale.js';\n\n// --- Image Attributes ---\n\nexport interface ImageAttrs {\n\treadonly src: string;\n\treadonly alt: string;\n\treadonly width?: number;\n\treadonly height?: number;\n\treadonly align: 'left' | 'center' | 'right';\n}\n\n// --- Upload Types ---\n\nexport interface ImageUploadResult {\n\treadonly url: string;\n\treadonly width?: number;\n\treadonly height?: number;\n}\n\nexport interface ImageUploadService {\n\tupload(file: File): Promise<ImageUploadResult>;\n}\n\nexport const IMAGE_UPLOAD_SERVICE = new ServiceKey<ImageUploadService>('image:upload');\n\nexport type UploadState = 'idle' | 'uploading' | 'complete' | 'error';\n\n// --- Keyboard Bindings ---\n\n/**\n * Configurable keyboard bindings for image resize actions.\n * Omit a slot to use the default; set to `null` to disable the binding.\n *\n * Key descriptor format: `'Mod-Shift-ArrowRight'`, etc.\n * `Mod` resolves to Cmd on macOS, Ctrl on Windows/Linux.\n */\nexport interface ImageKeymap {\n\treadonly growWidth?: string | null;\n\treadonly shrinkWidth?: string | null;\n\treadonly growWidthLarge?: string | null;\n\treadonly shrinkWidthLarge?: string | null;\n\treadonly resetSize?: string | null;\n}\n\nexport const DEFAULT_IMAGE_KEYMAP: Readonly<Record<keyof ImageKeymap, string>> = {\n\tgrowWidth: 'Mod-Shift-ArrowRight',\n\tshrinkWidth: 'Mod-Shift-ArrowLeft',\n\tgrowWidthLarge: 'Mod-Shift-Alt-ArrowRight',\n\tshrinkWidthLarge: 'Mod-Shift-Alt-ArrowLeft',\n\tresetSize: 'Mod-Shift-0',\n};\n\n// --- Configuration ---\n\nexport interface ImagePluginConfig {\n\treadonly maxWidth: number;\n\treadonly maxFileSize: number;\n\treadonly acceptedTypes: readonly string[];\n\treadonly resizable: boolean;\n\treadonly separatorAfter?: boolean;\n\t/** Pixels to grow/shrink per small resize step. @default 10 */\n\treadonly resizeStep?: number;\n\t/** Pixels to grow/shrink per large resize step. @default 50 */\n\treadonly resizeStepLarge?: number;\n\t/** Customize keyboard bindings for image resize actions. */\n\treadonly keymap?: ImageKeymap;\n\t/** Locale override for user-facing strings. */\n\treadonly locale?: ImageLocale;\n}\n\nexport const DEFAULT_IMAGE_CONFIG: ImagePluginConfig = {\n\tmaxWidth: 800,\n\tmaxFileSize: 10 * 1024 * 1024,\n\tacceptedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml'],\n\tresizable: true,\n\tresizeStep: 10,\n\tresizeStepLarge: 50,\n};\n","/**\n * ImagePlugin: registers an image void block type with NodeSpec,\n * NodeView, commands, file handler, toolbar button, and accessible\n * keyboard resize with screenreader announcements.\n */\n\nimport { IMAGE_CSS } from '../../editor/styles/image.js';\nimport { resolvePluginLocale } from '../../i18n/resolvePluginLocale.js';\nimport type { BlockAttrs, BlockNode } from '../../model/Document.js';\nimport { escapeHTML } from '../../model/HTMLUtils.js';\nimport { createBlockElement } from '../../model/NodeSpec.js';\nimport { 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 type { Plugin, PluginContext } from '../Plugin.js';\nimport { formatShortcut } from '../toolbar/ToolbarItem.js';\nimport {\n\tinsertImage,\n\tregisterImageCommands,\n\tresetImageSize,\n\tresizeImageByDelta,\n} from './ImageCommands.js';\nimport { IMAGE_LOCALES, type ImageLocale } from './ImageLocale.js';\nimport { createImageNodeViewFactory } from './ImageNodeView.js';\nimport {\n\tDEFAULT_IMAGE_CONFIG,\n\tDEFAULT_IMAGE_KEYMAP,\n\tIMAGE_UPLOAD_SERVICE,\n\ttype ImageKeymap,\n\ttype ImagePluginConfig,\n\ttype UploadState,\n} from './ImageUpload.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\timage: {\n\t\t\tsrc: string;\n\t\t\talt: string;\n\t\t\twidth?: number;\n\t\t\theight?: number;\n\t\t\talign: 'left' | 'center' | 'right';\n\t\t};\n\t}\n}\n\n// --- Plugin ---\n\nexport class ImagePlugin implements Plugin {\n\treadonly id = 'image';\n\treadonly name = 'Image';\n\treadonly priority = 45;\n\n\tprivate readonly config: ImagePluginConfig;\n\tprivate readonly resolvedKeymap: Readonly<Record<keyof ImageKeymap, string | null>>;\n\tprivate readonly uploadStates = new Map<BlockId, UploadState>();\n\tprivate readonly blobUrls = new Set<string>();\n\tprivate context: PluginContext | null = null;\n\tprivate locale!: ImageLocale;\n\n\tconstructor(config?: Partial<ImagePluginConfig>) {\n\t\tthis.config = { ...DEFAULT_IMAGE_CONFIG, ...config };\n\t\tthis.resolvedKeymap = { ...DEFAULT_IMAGE_KEYMAP, ...config?.keymap };\n\t}\n\n\tinit(context: PluginContext): void {\n\t\tthis.locale = resolvePluginLocale(IMAGE_LOCALES, context, this.config.locale);\n\t\tcontext.registerStyleSheet(IMAGE_CSS);\n\t\tthis.context = context;\n\t\tthis.registerNodeSpec(context);\n\t\tthis.registerNodeView(context);\n\t\tregisterImageCommands(context);\n\t\tthis.registerResizeCommands(context);\n\t\tthis.registerResizeKeymaps(context);\n\t\tthis.registerFileHandler(context);\n\t\tthis.registerToolbarItem(context);\n\t}\n\n\tdestroy(): void {\n\t\tfor (const url of this.blobUrls) {\n\t\t\tURL.revokeObjectURL(url);\n\t\t}\n\t\tthis.blobUrls.clear();\n\t\tthis.uploadStates.clear();\n\t\tthis.context = null;\n\t}\n\n\tonStateChange(oldState: EditorState, newState: EditorState, _tr: Transaction): void {\n\t\tif (!this.context) return;\n\n\t\t// Clean up upload states for removed blocks (searches full tree)\n\t\tfor (const id of this.uploadStates.keys()) {\n\t\t\tif (!newState.getBlock(id)) {\n\t\t\t\tthis.uploadStates.delete(id);\n\t\t\t}\n\t\t}\n\n\t\t// Announce image selection for screenreaders\n\t\tconst oldIsImage: boolean = this.isImageSelected(oldState);\n\t\tconst nowIsImage: boolean = this.isImageSelected(newState);\n\n\t\tif (!oldIsImage && nowIsImage) {\n\t\t\tthis.announceImageSelection(newState);\n\t\t}\n\t}\n\n\tprivate registerNodeSpec(context: PluginContext): void {\n\t\tconst locale = this.locale;\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'image',\n\t\t\tgroup: 'block',\n\t\t\tisVoid: true,\n\t\t\tselectable: true,\n\t\t\tattrs: {\n\t\t\t\tsrc: { default: '' },\n\t\t\t\talt: { default: '' },\n\t\t\t\talign: { default: 'center' },\n\t\t\t},\n\t\t\ttoDOM(node) {\n\t\t\t\tconst figure = createBlockElement('figure', node.id);\n\t\t\t\tfigure.className = 'notectl-image';\n\t\t\t\tfigure.setAttribute('data-void', 'true');\n\t\t\t\tfigure.setAttribute('data-selectable', 'true');\n\n\t\t\t\tconst imgContainer: HTMLDivElement = document.createElement('div');\n\t\t\t\timgContainer.className = 'notectl-image__container';\n\n\t\t\t\tconst alt: string = (node.attrs?.alt as string | undefined) ?? '';\n\t\t\t\tconst width: number | undefined = node.attrs?.width as number | undefined;\n\t\t\t\tconst height: number | undefined = node.attrs?.height as number | undefined;\n\n\t\t\t\tconst img: HTMLImageElement = document.createElement('img');\n\t\t\t\timg.className = 'notectl-image__img';\n\t\t\t\timg.src = (node.attrs?.src as string | undefined) ?? '';\n\t\t\t\timg.alt = alt;\n\t\t\t\timg.draggable = false;\n\n\t\t\t\tif (width !== undefined) img.style.width = `${width}px`;\n\t\t\t\tif (height !== undefined) img.style.height = `${height}px`;\n\n\t\t\t\tconst align: string = (node.attrs?.align as string | undefined) ?? 'center';\n\t\t\t\tconst alignClass: string | undefined = {\n\t\t\t\t\tleft: 'notectl-image--left',\n\t\t\t\t\tcenter: 'notectl-image--center',\n\t\t\t\t\tright: 'notectl-image--right',\n\t\t\t\t}[align];\n\t\t\t\tif (alignClass) figure.classList.add(alignClass);\n\n\t\t\t\tfigure.setAttribute('aria-label', locale.imageAria(alt, width, height));\n\n\t\t\t\timgContainer.appendChild(img);\n\t\t\t\tfigure.appendChild(imgContainer);\n\t\t\t\treturn figure;\n\t\t\t},\n\t\t\ttoHTML(node) {\n\t\t\t\tconst src: string = escapeHTML((node.attrs?.src as string | undefined) ?? '');\n\t\t\t\tconst alt: string = escapeHTML((node.attrs?.alt as string | undefined) ?? '');\n\t\t\t\tconst width: number | undefined = node.attrs?.width as number | undefined;\n\t\t\t\tconst height: number | undefined = node.attrs?.height as number | undefined;\n\t\t\t\tconst align: string = (node.attrs?.align as string | undefined) ?? 'center';\n\n\t\t\t\tconst sizeAttrs: string =\n\t\t\t\t\t(width !== undefined ? ` width=\"${width}\"` : '') +\n\t\t\t\t\t(height !== undefined ? ` height=\"${height}\"` : '');\n\n\t\t\t\tconst alignStyle: string = align ? ` style=\"text-align: ${escapeHTML(align)}\"` : '';\n\n\t\t\t\treturn `<figure${alignStyle}><img src=\"${src}\" alt=\"${alt}\"${sizeAttrs}></figure>`;\n\t\t\t},\n\t\t\tparseHTML: [\n\t\t\t\t{\n\t\t\t\t\ttag: 'figure',\n\t\t\t\t\tgetAttrs(el) {\n\t\t\t\t\t\tconst img: HTMLImageElement | null = el.querySelector('img');\n\t\t\t\t\t\tif (!img) return false;\n\t\t\t\t\t\tconst attrs: Record<string, string | number | boolean> = {\n\t\t\t\t\t\t\tsrc: img.getAttribute('src') ?? '',\n\t\t\t\t\t\t\talt: img.getAttribute('alt') ?? '',\n\t\t\t\t\t\t\talign: 'center',\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst width: string | null = img.getAttribute('width');\n\t\t\t\t\t\tconst height: string | null = img.getAttribute('height');\n\t\t\t\t\t\tif (width) attrs.width = Number.parseInt(width, 10);\n\t\t\t\t\t\tif (height) attrs.height = Number.parseInt(height, 10);\n\n\t\t\t\t\t\t// Check inline style first (new format), then class names (legacy)\n\t\t\t\t\t\tconst textAlign: string = el.style?.textAlign ?? '';\n\t\t\t\t\t\tif (textAlign === 'left' || textAlign === 'right' || textAlign === 'center') {\n\t\t\t\t\t\t\tattrs.align = textAlign;\n\t\t\t\t\t\t} else if (el.classList.contains('notectl-image--left')) {\n\t\t\t\t\t\t\tattrs.align = 'left';\n\t\t\t\t\t\t} else if (el.classList.contains('notectl-image--right')) {\n\t\t\t\t\t\t\tattrs.align = 'right';\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn attrs;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttag: 'img',\n\t\t\t\t\tgetAttrs(el) {\n\t\t\t\t\t\tconst attrs: Record<string, string | number | boolean> = {\n\t\t\t\t\t\t\tsrc: el.getAttribute('src') ?? '',\n\t\t\t\t\t\t\talt: el.getAttribute('alt') ?? '',\n\t\t\t\t\t\t\talign: 'center',\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst width: string | null = el.getAttribute('width');\n\t\t\t\t\t\tconst height: string | null = el.getAttribute('height');\n\t\t\t\t\t\tif (width) attrs.width = Number.parseInt(width, 10);\n\t\t\t\t\t\tif (height) attrs.height = Number.parseInt(height, 10);\n\t\t\t\t\t\treturn attrs;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t],\n\t\t\tsanitize: {\n\t\t\t\ttags: ['figure', 'img'],\n\t\t\t\tattrs: ['src', 'alt', 'width', 'height', 'class', 'style'],\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate registerNodeView(context: PluginContext): void {\n\t\tcontext.registerNodeView(\n\t\t\t'image',\n\t\t\tcreateImageNodeViewFactory(this.config, this.uploadStates, this.resolvedKeymap),\n\t\t);\n\t}\n\n\tprivate registerResizeCommands(context: PluginContext): void {\n\t\tconst step: number = this.config.resizeStep ?? 10;\n\t\tconst stepLarge: number = this.config.resizeStepLarge ?? 50;\n\t\tconst maxWidth: number = this.config.maxWidth;\n\n\t\tcontext.registerCommand('resizeImageGrow', () => {\n\t\t\tconst result: boolean = resizeImageByDelta(context, step, maxWidth);\n\t\t\tif (result) this.announceCurrentSize(context);\n\t\t\treturn result;\n\t\t});\n\n\t\tcontext.registerCommand('resizeImageShrink', () => {\n\t\t\tconst result: boolean = resizeImageByDelta(context, -step, maxWidth);\n\t\t\tif (result) this.announceCurrentSize(context);\n\t\t\treturn result;\n\t\t});\n\n\t\tcontext.registerCommand('resizeImageGrowLarge', () => {\n\t\t\tconst result: boolean = resizeImageByDelta(context, stepLarge, maxWidth);\n\t\t\tif (result) this.announceCurrentSize(context);\n\t\t\treturn result;\n\t\t});\n\n\t\tcontext.registerCommand('resizeImageShrinkLarge', () => {\n\t\t\tconst result: boolean = resizeImageByDelta(context, -stepLarge, maxWidth);\n\t\t\tif (result) this.announceCurrentSize(context);\n\t\t\treturn result;\n\t\t});\n\n\t\tcontext.registerCommand('resetImageSize', () => {\n\t\t\tconst result: boolean = resetImageSize(context);\n\t\t\tif (result) context.announce(this.locale.resetToNaturalSize);\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tprivate registerResizeKeymaps(context: PluginContext): void {\n\t\tconst bindings: Record<string, () => boolean> = {};\n\t\tconst commands: Record<keyof ImageKeymap, string> = {\n\t\t\tgrowWidth: 'resizeImageGrow',\n\t\t\tshrinkWidth: 'resizeImageShrink',\n\t\t\tgrowWidthLarge: 'resizeImageGrowLarge',\n\t\t\tshrinkWidthLarge: 'resizeImageShrinkLarge',\n\t\t\tresetSize: 'resetImageSize',\n\t\t};\n\n\t\tfor (const [slot, commandName] of Object.entries(commands)) {\n\t\t\tconst binding: string | null = this.resolvedKeymap[slot as keyof ImageKeymap] ?? null;\n\t\t\tif (binding) {\n\t\t\t\tbindings[binding] = () => context.executeCommand(commandName);\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(bindings).length > 0) {\n\t\t\tcontext.registerKeymap(bindings);\n\t\t}\n\t}\n\n\tprivate registerFileHandler(context: PluginContext): void {\n\t\tcontext.registerFileHandler('image/*', async (file, _position) => {\n\t\t\tif (!this.isAcceptedType(file.type)) return false;\n\t\t\tthis.handleFileInsert(context, file);\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tprivate handleFileInsert(context: PluginContext, file: File): void {\n\t\tif (file.size > this.config.maxFileSize) return;\n\n\t\tconst blobUrl: string = URL.createObjectURL(file);\n\t\tthis.blobUrls.add(blobUrl);\n\n\t\tconst inserted: boolean = insertImage(context, { src: blobUrl });\n\t\tif (!inserted) {\n\t\t\tURL.revokeObjectURL(blobUrl);\n\t\t\tthis.blobUrls.delete(blobUrl);\n\t\t\treturn;\n\t\t}\n\n\t\t// Find the newly inserted image block\n\t\tconst state: EditorState = context.getState();\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return;\n\t\tconst imageBlockId: BlockId = sel.nodeId;\n\n\t\tthis.uploadStates.set(imageBlockId, 'uploading');\n\n\t\t// Upload if service is registered\n\t\tconst uploadService = context.getService(IMAGE_UPLOAD_SERVICE);\n\t\tif (uploadService) {\n\t\t\tthis.uploadFile(context, file, imageBlockId, blobUrl);\n\t\t} else {\n\t\t\tthis.uploadStates.set(imageBlockId, 'complete');\n\t\t}\n\t}\n\n\tprivate async uploadFile(\n\t\tcontext: PluginContext,\n\t\tfile: File,\n\t\timageBlockId: BlockId,\n\t\tblobUrl: string,\n\t): Promise<void> {\n\t\tconst uploadService = context.getService(IMAGE_UPLOAD_SERVICE);\n\t\tif (!uploadService) return;\n\n\t\ttry {\n\t\t\tconst result = await uploadService.upload(file);\n\t\t\tthis.uploadStates.set(imageBlockId, 'complete');\n\n\t\t\t// Replace blob URL with uploaded URL\n\t\t\tconst state: EditorState = context.getState();\n\t\t\tconst block: BlockNode | undefined = state.getBlock(imageBlockId);\n\t\t\tif (!block) return;\n\n\t\t\tconst path: BlockId[] | undefined = state.getNodePath(imageBlockId);\n\t\t\tif (!path) return;\n\n\t\t\tconst merged: BlockAttrs = {\n\t\t\t\t...(block.attrs ?? {}),\n\t\t\t\tsrc: result.url,\n\t\t\t\t...(result.width !== undefined ? { width: result.width } : {}),\n\t\t\t\t...(result.height !== undefined ? { height: result.height } : {}),\n\t\t\t};\n\n\t\t\tconst tr: Transaction = state.transaction('command').setNodeAttr(path, merged).build();\n\t\t\tcontext.dispatch(tr);\n\n\t\t\t// Clean up blob URL\n\t\t\tURL.revokeObjectURL(blobUrl);\n\t\t\tthis.blobUrls.delete(blobUrl);\n\t\t} catch {\n\t\t\tthis.uploadStates.set(imageBlockId, 'error');\n\t\t\tcontext.announce(this.locale.uploadFailed);\n\t\t}\n\t}\n\n\tprivate registerToolbarItem(context: PluginContext): void {\n\t\tconst icon =\n\t\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z\"/></svg>';\n\n\t\tcontext.registerToolbarItem({\n\t\t\tid: 'image',\n\t\t\tgroup: 'insert',\n\t\t\ticon,\n\t\t\tlabel: this.locale.insertImage,\n\t\t\ttooltip: this.locale.insertImageTooltip,\n\t\t\tcommand: 'insertImage',\n\t\t\tpriority: 50,\n\t\t\tpopupType: 'custom',\n\t\t\tseparatorAfter: this.config.separatorAfter,\n\t\t\trenderPopup: (container, ctx, onClose) => {\n\t\t\t\tthis.renderImagePopup(container, ctx, onClose);\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate renderImagePopup(\n\t\tcontainer: HTMLElement,\n\t\tcontext: PluginContext,\n\t\tonClose: () => void,\n\t): void {\n\t\tcontainer.style.padding = '8px';\n\t\tcontainer.style.minWidth = '240px';\n\n\t\t// --- File upload ---\n\t\tconst fileInput: HTMLInputElement = document.createElement('input');\n\t\tfileInput.type = 'file';\n\t\tfileInput.accept = this.config.acceptedTypes.join(',');\n\t\tfileInput.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;opacity:0;';\n\n\t\tconst uploadBtn: HTMLButtonElement = document.createElement('button');\n\t\tuploadBtn.type = 'button';\n\t\tuploadBtn.textContent = this.locale.uploadFromComputer;\n\t\tuploadBtn.setAttribute('aria-label', this.locale.uploadAria);\n\t\tuploadBtn.style.cssText =\n\t\t\t'display:block;width:100%;padding:8px 12px;cursor:pointer;' +\n\t\t\t'text-align:center;box-sizing:border-box;' +\n\t\t\t'border:1px solid var(--notectl-border);border-radius:4px;' +\n\t\t\t'background:var(--notectl-surface-raised);color:var(--notectl-fg);';\n\n\t\tuploadBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tfileInput.click();\n\t\t});\n\n\t\tfileInput.addEventListener('change', () => {\n\t\t\tconst file: File | undefined = fileInput.files?.[0];\n\t\t\tif (file) {\n\t\t\t\tthis.handleFileInsert(context, file);\n\t\t\t\tonClose();\n\t\t\t\tcontext.getContainer().focus();\n\t\t\t}\n\t\t});\n\n\t\tcontainer.appendChild(fileInput);\n\t\tcontainer.appendChild(uploadBtn);\n\n\t\t// --- Separator ---\n\t\tconst separator: HTMLDivElement = document.createElement('div');\n\t\tseparator.style.cssText =\n\t\t\t'display:flex;align-items:center;margin:8px 0;' +\n\t\t\t'color:var(--notectl-fg-muted);font-size:12px;';\n\t\tconst line1: HTMLSpanElement = document.createElement('span');\n\t\tline1.style.cssText = 'flex:1;height:1px;background:var(--notectl-border);';\n\t\tconst orText: HTMLSpanElement = document.createElement('span');\n\t\torText.textContent = this.locale.separator;\n\t\torText.style.cssText = 'padding:0 8px;';\n\t\tconst line2: HTMLSpanElement = document.createElement('span');\n\t\tline2.style.cssText = 'flex:1;height:1px;background:var(--notectl-border);';\n\t\tseparator.appendChild(line1);\n\t\tseparator.appendChild(orText);\n\t\tseparator.appendChild(line2);\n\t\tcontainer.appendChild(separator);\n\n\t\t// --- URL input ---\n\t\tconst urlInput: HTMLInputElement = document.createElement('input');\n\t\turlInput.type = 'url';\n\t\turlInput.placeholder = this.locale.urlPlaceholder;\n\t\turlInput.setAttribute('aria-label', this.locale.urlAria);\n\t\turlInput.style.cssText =\n\t\t\t'width:100%;padding:6px 8px;box-sizing:border-box;' +\n\t\t\t'border:1px solid var(--notectl-border);border-radius:4px;' +\n\t\t\t'background:var(--notectl-bg);color:var(--notectl-fg);';\n\n\t\tconst insertBtn: HTMLButtonElement = document.createElement('button');\n\t\tinsertBtn.type = 'button';\n\t\tinsertBtn.textContent = this.locale.insertButton;\n\t\tinsertBtn.setAttribute('aria-label', this.locale.insertAria);\n\t\tinsertBtn.style.cssText =\n\t\t\t'width:100%;padding:8px 12px;margin-top:4px;cursor:pointer;' +\n\t\t\t'border:1px solid var(--notectl-border);border-radius:4px;' +\n\t\t\t'background:var(--notectl-surface-raised);color:var(--notectl-fg);';\n\n\t\tconst applyUrl = (): void => {\n\t\t\tconst src: string = urlInput.value.trim();\n\t\t\tif (src) {\n\t\t\t\tinsertImage(context, { src });\n\t\t\t\tonClose();\n\t\t\t\tcontext.getContainer().focus();\n\t\t\t}\n\t\t};\n\n\t\tinsertBtn.addEventListener('mousedown', (e: MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tapplyUrl();\n\t\t});\n\n\t\turlInput.addEventListener('keydown', (e: KeyboardEvent) => {\n\t\t\tif (e.key === 'Enter') {\n\t\t\t\te.preventDefault();\n\t\t\t\tapplyUrl();\n\t\t\t}\n\t\t});\n\n\t\tcontainer.appendChild(urlInput);\n\t\tcontainer.appendChild(insertBtn);\n\n\t\trequestAnimationFrame(() => urlInput.focus());\n\t}\n\n\tprivate isAcceptedType(mimeType: string): boolean {\n\t\treturn this.config.acceptedTypes.some(\n\t\t\t(accepted) =>\n\t\t\t\taccepted === mimeType ||\n\t\t\t\t(accepted.endsWith('/*') && mimeType.startsWith(accepted.slice(0, -1))),\n\t\t);\n\t}\n\n\tprivate isImageSelected(state: EditorState): boolean {\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return false;\n\t\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\t\treturn block?.type === 'image';\n\t}\n\n\tprivate announceImageSelection(state: EditorState): void {\n\t\tif (!this.context) return;\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return;\n\n\t\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\t\tif (!block || block.type !== 'image') return;\n\n\t\tconst alt: string = (block.attrs?.alt as string | undefined) ?? '';\n\t\tconst width: number | undefined = block.attrs?.width as number | undefined;\n\t\tconst height: number | undefined = block.attrs?.height as number | undefined;\n\n\t\tconst parts: string[] = ['Image selected.'];\n\t\tif (alt) parts.push(`Alt text: ${alt}.`);\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tparts.push(`Size: ${width} by ${height} pixels.`);\n\t\t}\n\n\t\tconst shrinkKey: string | null = this.resolvedKeymap.shrinkWidth ?? null;\n\t\tconst growKey: string | null = this.resolvedKeymap.growWidth ?? null;\n\t\tif (shrinkKey && growKey) {\n\t\t\tparts.push(`${formatShortcut(shrinkKey)} / ${formatShortcut(growKey)} to resize.`);\n\t\t}\n\n\t\tthis.context.announce(parts.join(' '));\n\t}\n\n\tprivate announceCurrentSize(context: PluginContext): void {\n\t\tconst state = context.getState();\n\t\tconst sel = state.selection;\n\t\tif (!isNodeSelection(sel)) return;\n\n\t\tconst block: BlockNode | undefined = state.getBlock(sel.nodeId);\n\t\tif (!block || block.type !== 'image') return;\n\n\t\tconst width: number | undefined = block.attrs?.width as number | undefined;\n\t\tconst height: number | undefined = block.attrs?.height as number | undefined;\n\t\tif (width !== undefined && height !== undefined) {\n\t\t\tcontext.announce(`Image resized to ${width} by ${height} pixels.`);\n\t\t}\n\t}\n}\n","/** Image styles — display, alignment, resize handles, upload overlay. */\nexport const IMAGE_CSS = `\n/* Image */\n.notectl-image {\n\tmargin: 8px 0;\n\tuser-select: none;\n\tline-height: 0;\n}\n\n.notectl-image--center {\n\ttext-align: center;\n}\n\n.notectl-image--left {\n\ttext-align: left;\n}\n\n.notectl-image--right {\n\ttext-align: right;\n}\n\n.notectl-image__container {\n\tdisplay: inline-block;\n\tposition: relative;\n\tline-height: 0;\n}\n\n.notectl-image__img {\n\tdisplay: block;\n\tmax-width: 100%;\n\tborder-radius: 4px;\n}\n\n.notectl-image--selected .notectl-image__container {\n\toutline: 2px solid var(--notectl-primary);\n\toutline-offset: 2px;\n\tborder-radius: 6px;\n}\n\n/* Alignment toolbar (appears on image selection) */\n.notectl-image__align-toolbar {\n\tdisplay: flex;\n\tgap: 2px;\n\tposition: absolute;\n\ttop: -36px;\n\tleft: 50%;\n\ttransform: translateX(-50%);\n\tbackground: var(--notectl-surface-overlay);\n\tborder: 1px solid var(--notectl-border);\n\tborder-radius: 6px;\n\tpadding: 2px;\n\tbox-shadow: 0 2px 8px var(--notectl-shadow);\n\tz-index: 10;\n}\n\n.notectl-image__align-btn {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 28px;\n\theight: 28px;\n\tborder: none;\n\tbackground: transparent;\n\tborder-radius: 4px;\n\tcursor: pointer;\n\tcolor: var(--notectl-fg);\n}\n\n.notectl-image__align-btn:hover {\n\tbackground: var(--notectl-hover-bg);\n}\n\n.notectl-image__align-btn--active {\n\tbackground: var(--notectl-active-bg);\n\tcolor: var(--notectl-primary-fg);\n}\n\n.notectl-image__align-btn svg {\n\twidth: 16px;\n\theight: 16px;\n\tfill: currentColor;\n}\n\n/* Upload overlay */\n.notectl-image__overlay {\n\tposition: absolute;\n\tinset: 0;\n\tdisplay: none;\n\talign-items: center;\n\tjustify-content: center;\n\tbackground: rgba(255, 255, 255, 0.8);\n\tborder-radius: 4px;\n\tfont-size: 13px;\n\tcolor: var(--notectl-fg-muted);\n}\n\n.notectl-image__overlay--uploading {\n\tdisplay: flex;\n}\n\n.notectl-image__overlay--error {\n\tdisplay: flex;\n\tbackground: var(--notectl-danger-muted);\n\tcolor: var(--notectl-danger);\n}\n\n/* Resize overlay + handles */\n.notectl-image__resize-overlay {\n\tposition: absolute;\n\tinset: 0;\n\tpointer-events: none;\n}\n\n.notectl-image__resize-handle {\n\tposition: absolute;\n\twidth: 10px;\n\theight: 10px;\n\tbackground: var(--notectl-bg);\n\tborder: 2px solid var(--notectl-primary);\n\tborder-radius: 50%;\n\tpointer-events: all;\n\tz-index: 2;\n\ttransition: transform 0.1s, background 0.1s;\n}\n\n.notectl-image__resize-handle:hover {\n\tbackground: var(--notectl-primary);\n\ttransform: scale(1.3);\n}\n\n.notectl-image--resizing .notectl-image__resize-handle {\n\tbackground: var(--notectl-primary);\n}\n\n.notectl-image__resize-handle--nw {\n\ttop: -5px;\n\tleft: -5px;\n\tcursor: nwse-resize;\n}\n\n.notectl-image__resize-handle--ne {\n\ttop: -5px;\n\tright: -5px;\n\tcursor: nesw-resize;\n}\n\n.notectl-image__resize-handle--sw {\n\tbottom: -5px;\n\tleft: -5px;\n\tcursor: nesw-resize;\n}\n\n.notectl-image__resize-handle--se {\n\tbottom: -5px;\n\tright: -5px;\n\tcursor: nwse-resize;\n}\n\n/* Size indicator tooltip */\n.notectl-image__size-indicator {\n\tposition: absolute;\n\tbottom: -28px;\n\tleft: 50%;\n\ttransform: translateX(-50%);\n\tpadding: 2px 8px;\n\tbackground: var(--notectl-tooltip-bg);\n\tcolor: var(--notectl-tooltip-fg);\n\tfont-size: 11px;\n\tfont-weight: 500;\n\tborder-radius: 4px;\n\twhite-space: nowrap;\n\tpointer-events: none;\n\topacity: 0;\n\ttransition: opacity 0.15s;\n\tz-index: 3;\n}\n\n.notectl-image__size-indicator--visible {\n\topacity: 1;\n}\n\n/* Keyboard resize hint (shown on selection, hidden during drag) */\n.notectl-image__keyboard-hint {\n\tdisplay: none;\n\tposition: absolute;\n\tbottom: -28px;\n\tleft: 50%;\n\ttransform: translateX(-50%);\n\tpadding: 2px 8px;\n\tfont-size: 11px;\n\tcolor: var(--notectl-fg-muted);\n\twhite-space: nowrap;\n\tpointer-events: none;\n\tz-index: 3;\n}\n\n.notectl-image--selected .notectl-image__keyboard-hint {\n\tdisplay: block;\n}\n\n.notectl-image--resizing .notectl-image__keyboard-hint {\n\tdisplay: none;\n}\n\n/* Resizing feedback */\n.notectl-image--resizing .notectl-image__img {\n\topacity: 0.85;\n}\n`;\n"],"names":["insertImage","context","attrs","state","getState","sel","selection","imageAttrs","src","alt","align","width","height","isGapCursor","anchorBlockId","isNodeSelection","nodeId","anchor","blockId","cellId","bid","block","getBlock","type","path","getNodePath","id","node","cellPath","imageBlock","createBlockNode","nodeType","cell","builder","transaction","blockChildren","getBlockChildren","i","length","rawIndex","children","findIndex","c","isBlockNode","removeNode","insertNode","setSelection","createNodeSelection","dispatch","build","blockIndex","doc","b","newParagraph","tr","resizeImageByDelta","delta","maxWidth","currentWidth","currentHeight","aspectRatio","clampedWidth","Math","max","min","newHeight","round","merged","setNodeAttr","IMAGE_LOCALE_EN","insertImageTooltip","uploadFromComputer","uploadAria","separator","urlPlaceholder","urlAria","insertButton","insertAria","imageAria","parts","push","join","resetToNaturalSize","uploadFailed","IMAGE_LOCALES","en","de","es","fr","zh","ru","ar","hi","ALIGNMENT_CLASSES","left","center","right","HANDLE_CURSORS","nw","ne","sw","se","HANDLE_X_SIGN","HANDLE_LABELS","activeCursorStyle","clearGlobalResizeCursor","remove","createImageNodeViewFactory","config","uploadStates","resolvedKeymap","figure","document","createElement","className","setAttribute","container","img","draggable","overlay","appendChild","currentNodeId","resizeOverlay","applyAttrs","n","style","cls","Object","values","classList","alignClass","add","uploadState","get","isUploading","toggle","textContent","removeAttribute","dimensionsCommitted","attachHandleListeners","handle","position","sizeIndicator","startX","startWidth","onPointerMove","e","deltaX","clientX","newWidth","onPointerUp","removeEventListener","finalWidth","getBoundingClientRect","finalHeight","prevWidth","prevHeight","target","releasePointerCapture","pointerId","addEventListener","preventDefault","stopPropagation","imgHeight","cursor","head","setPointerCapture","removeResizeOverlay","hasWidth","hasHeight","naturalW","naturalWidth","naturalH","naturalHeight","clampedW","ratio","clampedH","dom","contentDOM","update","updatedNode","resizable","positions","pos","hintText","shrink","shrinkWidth","grow","growWidth","formatShortcut","hint","deselectNode","destroy","IMAGE_UPLOAD_SERVICE","ServiceKey","DEFAULT_IMAGE_KEYMAP","growWidthLarge","shrinkWidthLarge","resetSize","DEFAULT_IMAGE_CONFIG","maxFileSize","acceptedTypes","resizeStep","resizeStepLarge","ImagePlugin","name","priority","Map","blobUrls","Set","locale","this","keymap","resolvePluginLocale","registerStyleSheet","registerNodeSpec","registerNodeView","registerCommand","nodePath","parentPath","slice","parent","parentId","imageIndex","selectionTarget","removedId","rootChildren","idx","adjacent","createCollapsedSelection","registerResizeCommands","registerResizeKeymaps","registerFileHandler","registerToolbarItem","url","URL","revokeObjectURL","clear","oldState","newState","_tr","keys","delete","oldIsImage","isImageSelected","nowIsImage","announceImageSelection","group","isVoid","selectable","default","createBlockElement","imgContainer","escapeHTML","sizeAttrs","parseHTML","tag","el","querySelector","getAttribute","Number","parseInt","textAlign","contains","sanitize","tags","step","stepLarge","result","announceCurrentSize","cleaned","fromEntries","entries","filter","k","announce","bindings","commands","slot","commandName","binding","executeCommand","registerKeymap","async","file","_position","isAcceptedType","handleFileInsert","size","blobUrl","createObjectURL","imageBlockId","set","getService","uploadFile","uploadService","upload","icon","label","tooltip","command","popupType","separatorAfter","renderPopup","ctx","onClose","renderImagePopup","padding","minWidth","fileInput","accept","cssText","uploadBtn","click","files","getContainer","focus","line1","orText","line2","urlInput","placeholder","insertBtn","applyUrl","value","trim","key","requestAnimationFrame","mimeType","some","accepted","endsWith","startsWith","shrinkKey","growKey"],"mappings":"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"}
|