@mrakomor/core 0.0.11 → 0.0.13

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.
Files changed (113) hide show
  1. package/dist/blockChrome.mjs +2 -2
  2. package/dist/blockInsertMenu.mjs +1 -1
  3. package/dist/chunk-2C6UDFD4.mjs +1 -0
  4. package/dist/chunk-4WTPDNPA.mjs +1 -0
  5. package/dist/chunk-5AD5MQXI.mjs +1 -0
  6. package/dist/{chunk-K2DMHAUD.mjs → chunk-7CJLTNHZ.mjs} +1 -1
  7. package/dist/{chunk-VYTSZ4S6.mjs → chunk-B2UFH244.mjs} +1 -1
  8. package/dist/chunk-BWK2S4UI.mjs +2 -0
  9. package/dist/chunk-D6K2L5O3.mjs +4 -0
  10. package/dist/{chunk-NR2BAMSW.mjs → chunk-DHFIX7FW.mjs} +2 -2
  11. package/dist/chunk-ERNLRJPB.mjs +1 -0
  12. package/dist/chunk-EXDY5TZH.mjs +1 -0
  13. package/dist/chunk-J4TTWQ6J.mjs +2 -0
  14. package/dist/{chunk-2VURZA2E.mjs → chunk-SG5PCGIV.mjs} +1 -1
  15. package/dist/chunk-ZHOZPQZH.mjs +1 -0
  16. package/dist/clipboard.cjs +3 -3
  17. package/dist/clipboard.mjs +3 -3
  18. package/dist/footer.cjs +3 -3
  19. package/dist/footer.mjs +2 -2
  20. package/dist/headline.cjs +2 -2
  21. package/dist/headline.mjs +1 -1
  22. package/dist/help.cjs +1 -1
  23. package/dist/help.mjs +2 -2
  24. package/dist/index.cjs +6 -6
  25. package/dist/index.mjs +4 -4
  26. package/dist/link.mjs +2 -2
  27. package/dist/list.cjs +3 -3
  28. package/dist/list.mjs +1 -1
  29. package/dist/paragraph.cjs +2 -2
  30. package/dist/paragraph.mjs +1 -1
  31. package/dist/slashCommand.cjs +2 -2
  32. package/dist/slashCommand.mjs +1 -1
  33. package/dist/tooltip.cjs +1 -1
  34. package/dist/tooltip.mjs +2 -2
  35. package/dist/tts.cjs +8 -8
  36. package/dist/tts.mjs +3 -3
  37. package/dist/types/Mrakomor.d.mts +230 -0
  38. package/dist/types/component/AbstractComponent.d.mts +69 -0
  39. package/dist/types/component/Editor.d.mts +8 -0
  40. package/dist/types/component/block/BlockComponent.d.mts +105 -0
  41. package/dist/types/component/block/BlockRegister.d.mts +29 -0
  42. package/dist/types/component/block/ExternalComponent.d.mts +42 -0
  43. package/dist/types/component/block/UnknownComponent.d.mts +3 -0
  44. package/dist/types/component/block/blockItemEquals.d.mts +6 -0
  45. package/dist/types/component/block/helpers.d.mts +319 -0
  46. package/dist/types/component/editorContext.d.mts +4 -0
  47. package/dist/types/component/entityRange/EntityRangeElement.d.mts +15 -0
  48. package/dist/types/component/inlineStyle/Bold.d.mts +4 -0
  49. package/dist/types/component/inlineStyle/InlineElement.d.mts +10 -0
  50. package/dist/types/component/inlineStyle/Italic.d.mts +4 -0
  51. package/dist/types/component/inlineStyle/Subscript.d.mts +4 -0
  52. package/dist/types/component/inlineStyle/Superscript.d.mts +4 -0
  53. package/dist/types/component/inlineStyle/Underline.d.mts +4 -0
  54. package/dist/types/component/inlineStyle/index.d.mts +5 -0
  55. package/dist/types/component/overlayMount.d.mts +46 -0
  56. package/dist/types/dom.d.mts +169 -0
  57. package/dist/types/emitter.d.mts +38 -0
  58. package/dist/types/index.d.mts +7 -0
  59. package/dist/types/namespaces.d.mts +17 -0
  60. package/dist/types/plugin/blockChrome/BlockChrome.d.mts +11 -0
  61. package/dist/types/plugin/blockChrome/index.d.mts +1 -0
  62. package/dist/types/plugin/blockInsertMenu/BlockInsertMenu.d.mts +6 -0
  63. package/dist/types/plugin/blockInsertMenu/index.d.mts +1 -0
  64. package/dist/types/plugin/clipboard/clipboardHelpers.d.mts +151 -0
  65. package/dist/types/plugin/clipboard/clipboardPlugin.d.mts +6 -0
  66. package/dist/types/plugin/clipboard/index.d.mts +1 -0
  67. package/dist/types/plugin/default/defaultMrakomorPlugin.d.mts +21 -0
  68. package/dist/types/plugin/dialog/Dialog.d.mts +23 -0
  69. package/dist/types/plugin/footer/footerPlugin.d.mts +7 -0
  70. package/dist/types/plugin/footer/index.d.mts +1 -0
  71. package/dist/types/plugin/headline/Headline.d.mts +29 -0
  72. package/dist/types/plugin/headline/index.d.mts +1 -0
  73. package/dist/types/plugin/help/HelpRegister.d.mts +15 -0
  74. package/dist/types/plugin/help/helpPlugin.d.mts +26 -0
  75. package/dist/types/plugin/help/helpSchema.d.mts +11 -0
  76. package/dist/types/plugin/help/index.d.mts +1 -0
  77. package/dist/types/plugin/linkEntity/LinkEntity.d.mts +37 -0
  78. package/dist/types/plugin/linkEntity/index.d.mts +1 -0
  79. package/dist/types/plugin/list/List.d.mts +61 -0
  80. package/dist/types/plugin/list/index.d.mts +1 -0
  81. package/dist/types/plugin/list/listHelpers.d.mts +166 -0
  82. package/dist/types/plugin/menu/Menu.d.mts +22 -0
  83. package/dist/types/plugin/menu/menuPlugin.d.mts +6 -0
  84. package/dist/types/plugin/paragraph/Paragraph.d.mts +16 -0
  85. package/dist/types/plugin/paragraph/index.d.mts +1 -0
  86. package/dist/types/plugin/slashCommand/SlashActionRegister.d.mts +12 -0
  87. package/dist/types/plugin/slashCommand/index.d.mts +1 -0
  88. package/dist/types/plugin/slashCommand/slashActionSchema.d.mts +29 -0
  89. package/dist/types/plugin/slashCommand/slashCommandHelpers.d.mts +77 -0
  90. package/dist/types/plugin/slashCommand/slashCommandPlugin.d.mts +11 -0
  91. package/dist/types/plugin/slashCommand/slashHelp.d.mts +12 -0
  92. package/dist/types/plugin/tooltip/Tooltip.d.mts +16 -0
  93. package/dist/types/plugin/tooltip/index.d.mts +1 -0
  94. package/dist/types/plugin/tts/TtsHeadline.d.mts +27 -0
  95. package/dist/types/plugin/tts/TtsList.d.mts +19 -0
  96. package/dist/types/plugin/tts/TtsParagraph.d.mts +9 -0
  97. package/dist/types/plugin/tts/TtsVoice.d.mts +52 -0
  98. package/dist/types/plugin/tts/index.d.mts +4 -0
  99. package/dist/types/register/Register.d.mts +50 -0
  100. package/dist/types/register/sortRegisterEntries.d.mts +7 -0
  101. package/dist/types/schema/blockContent.d.mts +34 -0
  102. package/dist/types/schema/blockRegister.d.mts +14 -0
  103. package/dist/types/schema/config.d.mts +6 -0
  104. package/dist/types/types.d.mts +636 -0
  105. package/package.json +36 -22
  106. package/dist/chunk-5VPYPKGA.mjs +0 -1
  107. package/dist/chunk-BCE45I7Q.mjs +0 -2
  108. package/dist/chunk-N4I2WZWF.mjs +0 -1
  109. package/dist/chunk-SCFD3XBH.mjs +0 -1
  110. package/dist/chunk-T7OO472O.mjs +0 -1
  111. package/dist/chunk-WQWE5UCN.mjs +0 -1
  112. package/dist/chunk-YO4ZA6PJ.mjs +0 -2
  113. package/dist/chunk-ZDMD7SRT.mjs +0 -4
@@ -0,0 +1,69 @@
1
+ export class AbstractComponent extends Component {
2
+ static get NAMESPACE(): string;
3
+ static getStateSchema(): import("@esmj/schema").SchemaInterface<{
4
+ component: string;
5
+ properties: {
6
+ [x: string]: unknown;
7
+ };
8
+ settings: {
9
+ [x: string]: unknown;
10
+ };
11
+ }, {
12
+ component: string;
13
+ properties: {
14
+ [x: string]: unknown;
15
+ };
16
+ settings: {
17
+ [x: string]: unknown;
18
+ };
19
+ }>;
20
+ static getPropsSchema(): import("@esmj/schema").SchemaInterface<{
21
+ state: {
22
+ component: string;
23
+ properties: {
24
+ [x: string]: unknown;
25
+ };
26
+ settings: {
27
+ [x: string]: unknown;
28
+ };
29
+ };
30
+ observeDOMChanges: boolean;
31
+ }, {
32
+ state: {
33
+ component: string;
34
+ properties: {
35
+ [x: string]: unknown;
36
+ };
37
+ settings: {
38
+ [x: string]: unknown;
39
+ };
40
+ };
41
+ observeDOMChanges: boolean;
42
+ }>;
43
+ constructor(props: any);
44
+ /** @type {import('#src/types.mjs').Signal<import('#src/types.mjs').BlockState>} */
45
+ state: import("#src/types.mjs").Signal<import("#src/types.mjs").BlockState>;
46
+ /** @type {Element | null} */
47
+ element: Element | null;
48
+ /** @type {import('#src/types.mjs').Mrakomor} */
49
+ mrakomor: import("#src/types.mjs").Mrakomor;
50
+ _stale: boolean;
51
+ _observer: MutationObserver | null;
52
+ /** @type {(() => void) | null} */
53
+ _offInputStale: (() => void) | null;
54
+ /** @type {((event: import('#src/types.mjs').EmitterEvent) => void) | null} */
55
+ _onInputMarkStale: ((event: import("#src/types.mjs").EmitterEvent) => void) | null;
56
+ _observeDOMChanges: any;
57
+ parseFromHTML(node: any): any;
58
+ getState(): any;
59
+ /**
60
+ * Whether live DOM may differ from the item signal (`getState()` parses until re-render).
61
+ * @returns {boolean}
62
+ */
63
+ isStale(): boolean;
64
+ onMount(): void;
65
+ onUnmount(): void;
66
+ #private;
67
+ }
68
+ export { AbstractComponent as SAbstractComponent };
69
+ import { Component } from '@esmj/dom';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Editor shell: tools overlay + keyed block list (`Each` by `componentId`).
3
+ * @param {{ mrakomor: import('#src/types.mjs').Signal<import('#src/types.mjs').Mrakomor> }} props
4
+ * @returns {*}
5
+ */
6
+ export function Editor({ mrakomor: mrakomorSignal }: {
7
+ mrakomor: import("#src/types.mjs").Signal<import("#src/types.mjs").Mrakomor>;
8
+ }): any;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Base class for content blocks (paragraph, headline, list, figure, …).
3
+ * Subclasses override `onKeydown` for block-owned keys and may override
4
+ * `setCursor` / `isAtomic` for neighbor Backspace behavior.
5
+ */
6
+ export class BlockComponent extends AbstractComponent {
7
+ /**
8
+ * Visual label for insert menus / tooltip toolbar (string or renderable).
9
+ * @returns {string | Function}
10
+ */
11
+ static get LABEL(): string | Function;
12
+ /**
13
+ * Extra slash-filter tokens (label and namespace leaf are added when empty).
14
+ * @returns {string[]}
15
+ */
16
+ static get KEYWORDS(): string[];
17
+ /**
18
+ * Menu group for insert / slash menus (`register` row `group`).
19
+ * @returns {string}
20
+ */
21
+ static get GROUP(): string;
22
+ /**
23
+ * Convert this block type's own state into the generic cross-component
24
+ * exchange shape: `properties.items` with exactly one entry (this block
25
+ * type has no per-item DOM identity). Records this block type's own
26
+ * `NAMESPACE` as `originComponent` for provenance (authoritative — not
27
+ * read from `state.component`, which a caller could omit or get wrong).
28
+ * Normalizes the item's text via `normalizeBlockText` so the empty-
29
+ * contenteditable `'\n'`/`'\r\n'` DOM sentinel never leaks into the
30
+ * exchange state as a fake 1-character text. `inlineStyleRanges`/
31
+ * `entityRanges` are included on the item ONLY when the real
32
+ * `state.properties` actually has that key — never fabricated to `[]` —
33
+ * so generic callers (`resolveActiveExchangeItem`/insert-range handlers)
34
+ * can tell "supports this range kind" (key present, even if empty) apart
35
+ * from "doesn't support it at all" (key absent), e.g. a plain Headline
36
+ * has neither key, `TtsHeadline` has `entityRanges` only. Override for
37
+ * blocks whose properties should not leak into the exchange state (e.g.
38
+ * atomic blocks), or that have more than one item (e.g. List).
39
+ * @param {import('#src/types.mjs').BlockState} state
40
+ * @returns {import('#src/types.mjs').ExchangeBlockState}
41
+ */
42
+ static toExchangeState(state: import("#src/types.mjs").BlockState): import("#src/types.mjs").ExchangeBlockState;
43
+ /**
44
+ * Convert a generic cross-component exchange state into this block type's
45
+ * own `properties`/`settings` fragment: unwraps `properties.items[0]` back
46
+ * into a flat `{ text, inlineStyleRanges, entityRanges }` (this block type
47
+ * has no per-item identity, so any additional incoming items are dropped —
48
+ * matches `unwrapListItem`'s per-item transform semantics, transform
49
+ * always targets ONE item). Override to reshape it (e.g. List wraps every
50
+ * item into its own row). Callers always finish with
51
+ * `Constructor.getStateSchema().parse(...)` for final validation/defaults.
52
+ * @param {import('#src/types.mjs').ExchangeBlockState} exchangeState
53
+ * @returns {import('#src/types.mjs').BlockStateInput}
54
+ */
55
+ static fromExchangeState(exchangeState: import("#src/types.mjs").ExchangeBlockState): import("#src/types.mjs").BlockStateInput;
56
+ /**
57
+ * Append an incoming exchange state onto this block's own state (Backspace
58
+ * merge join, previous block keeps its own type). Default text-concats via
59
+ * `deepMergeBlockState`, which keeps `state`'s own `component`/`componentId`
60
+ * and merges `properties`/`settings`. The incoming exchange state's items
61
+ * are joined into one flat item first (`joinExchangeItems`, `'\n'` between
62
+ * items) since this block type can only hold flat text — matters when the
63
+ * removed block is multi-item (e.g. a List being merged into a preceding
64
+ * Paragraph). Override to reshape the join (e.g. List appends the incoming
65
+ * text as a new item) or return `null` to opt out of merging entirely
66
+ * (e.g. atomic external blocks — see `ExternalComponent.appendExchangeState`).
67
+ * @param {import('#src/types.mjs').BlockState} state
68
+ * @param {import('#src/types.mjs').ExchangeBlockState} exchangeState
69
+ * @returns {{ state: import('#src/types.mjs').BlockState, cursor: number } | null}
70
+ */
71
+ static appendExchangeState(state: import("#src/types.mjs").BlockState, exchangeState: import("#src/types.mjs").ExchangeBlockState): {
72
+ state: import("#src/types.mjs").BlockState;
73
+ cursor: number;
74
+ } | null;
75
+ /**
76
+ * Whether Backspace on an empty neighbor should remove this block.
77
+ * Non-atomic blocks receive the cursor via `setCursor` instead.
78
+ * @returns {boolean}
79
+ */
80
+ isAtomic(): boolean;
81
+ /**
82
+ * Place the cursor in this block’s selectable root.
83
+ * Omitting `cursor` places it at the end of `properties.text`.
84
+ * Restores immediately and again in `afterRender`.
85
+ * @param {{ cursor?: number, itemIndex?: number }} [options] `itemIndex`
86
+ * targets a specific item for multi-item blocks (e.g. `List.setCursor`);
87
+ * this flat/single-item base has only one item, so it accepts
88
+ * `itemIndex` for a consistent cross-block `setCursor` contract (generic
89
+ * callers like `restoreCaretAfterMerge` forward it blindly) but ignores it.
90
+ * @returns {void}
91
+ */
92
+ setCursor({ cursor }?: {
93
+ cursor?: number;
94
+ itemIndex?: number;
95
+ }): void;
96
+ /**
97
+ * Handle keydown while this block is focused. Override in subclasses.
98
+ * Call `event.preventDefault()` when the block owns the key so default
99
+ * Backspace merge / insert is skipped (`unlessPrevented`).
100
+ * @param {import('#src/types.mjs').KeydownEvent} event
101
+ * @returns {void}
102
+ */
103
+ onKeydown(_event: any): void;
104
+ }
105
+ import { AbstractComponent } from '../AbstractComponent.mjs';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Block insert/slash register with namespace → constructor lookup.
3
+ */
4
+ export class BlockRegister extends Register {
5
+ /**
6
+ * NAMESPACE → Constructor from block register rows (first registration wins).
7
+ * @param {import('#src/types.mjs').BlockComponentRegistration[]} registrations
8
+ * @returns {Map<string, import('#src/types.mjs').BlockConstructor>}
9
+ */
10
+ static buildNamespaceMap(registrations: import("#src/types.mjs").BlockComponentRegistration[]): Map<string, import("#src/types.mjs").BlockConstructor>;
11
+ /**
12
+ * @param {import('#src/types.mjs').Mrakomor} mrakomor
13
+ */
14
+ constructor(mrakomor: import("#src/types.mjs").Mrakomor);
15
+ /**
16
+ * @param {*} raw
17
+ * @param {number} index
18
+ * @returns {import('#src/types.mjs').BlockComponentRegistration | null}
19
+ */
20
+ normalize(raw: any, index: number): import("#src/types.mjs").BlockComponentRegistration | null;
21
+ /**
22
+ * Resolve block component by dotted namespace (first register row wins).
23
+ * @param {string} namespace
24
+ * @returns {import('#src/types.mjs').BlockConstructor | undefined}
25
+ */
26
+ getByNamespace(namespace: string): import("#src/types.mjs").BlockConstructor | undefined;
27
+ #private;
28
+ }
29
+ import Register from '#src/register/Register.mjs';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Non-text embed block (CMS external widget). Hosts opaque content in
3
+ * `this.root` and a thin contenteditable strip so the block can take focus.
4
+ * Atomic: empty neighbor Backspace removes this block.
5
+ */
6
+ export class ExternalComponent extends BlockComponent {
7
+ /**
8
+ * Schema for external block state (`component`, `componentId`, empty props/settings).
9
+ * @param {Element | null | undefined} node
10
+ * @returns {*}
11
+ */
12
+ static getStateSchema(node: Element | null | undefined): any;
13
+ /**
14
+ * Atomic blocks never merge with a neighbor (Backspace removes them via
15
+ * `isAtomic` instead of appending text into them).
16
+ * @returns {null}
17
+ */
18
+ static appendExchangeState(): null;
19
+ /**
20
+ * @param {object} props
21
+ */
22
+ constructor(props: object);
23
+ /** @type {Element | null} host for embed content */
24
+ root: Element | null;
25
+ /**
26
+ * Current signal value (external blocks do not re-parse from DOM).
27
+ * @returns {import('#src/types.mjs').BlockState}
28
+ */
29
+ getState(): import("#src/types.mjs").BlockState;
30
+ /**
31
+ * Opaque container plus a focusable strip for selection / Enter.
32
+ * @returns {*}
33
+ */
34
+ render(): any;
35
+ /**
36
+ * Suppress most typing; Enter inserts an empty paragraph below.
37
+ * @param {import('#src/types.mjs').KeydownEvent} event
38
+ * @returns {void}
39
+ */
40
+ onKeydown(event: import("#src/types.mjs").KeydownEvent): void;
41
+ }
42
+ import { BlockComponent } from './BlockComponent.mjs';
@@ -0,0 +1,3 @@
1
+ export class UnknownComponent extends ExternalComponent {
2
+ }
3
+ import { ExternalComponent } from './ExternalComponent.mjs';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `Each` item equals for `#content` blocks: `deepEqual`, unless the live instance is stale.
3
+ * @param {import('#src/types.mjs').Mrakomor} mrakomor
4
+ * @returns {(prev: import('#src/types.mjs').BlockState, next: import('#src/types.mjs').BlockState) => boolean}
5
+ */
6
+ export function createBlockItemEquals(mrakomor: import("#src/types.mjs").Mrakomor): (prev: import("#src/types.mjs").BlockState, next: import("#src/types.mjs").BlockState) => boolean;
@@ -0,0 +1,319 @@
1
+ /**
2
+ * @typedef {import('#src/types.mjs').BlockState} BlockState
3
+ * @typedef {import('#src/types.mjs').RichTextState} RichTextState
4
+ * @typedef {import('#src/types.mjs').InlineStyleRange} InlineStyleRange
5
+ * @typedef {import('#src/types.mjs').EntityRange} EntityRange
6
+ * @typedef {import('#src/types.mjs').RichTextComponents} RichTextComponents
7
+ * @typedef {import('#src/types.mjs').SplitBlockResult} SplitBlockResult
8
+ * @typedef {import('#src/types.mjs').ContentMutation} ContentMutation
9
+ * @typedef {import('#src/types.mjs').ClipboardSelection} ClipboardSelection
10
+ * @typedef {import('#src/types.mjs').RichTextBlockInstance} RichTextBlockInstance
11
+ * @typedef {import('#src/types.mjs').RangeMeta} RangeMeta
12
+ */
13
+ /**
14
+ * Empty contenteditable often serializes as a lone newline; treat that as `''`.
15
+ * @param {string | null | undefined} text
16
+ * @returns {string}
17
+ */
18
+ export function normalizeBlockText(text: string | null | undefined): string;
19
+ /**
20
+ * Build a generic cross-component exchange state (see
21
+ * `BlockComponent.toExchangeState` / `fromExchangeState`). The only
22
+ * text-shaped field is `properties.items` — always an array. Pass
23
+ * `properties.items` directly to preserve it as-is (real multi-item
24
+ * producers like `List.toExchangeState`, including an intentionally empty
25
+ * array); otherwise (`items` absent) `properties.text`/`inlineStyleRanges`/
26
+ * `entityRanges` (or their absence) are wrapped into a single-item array —
27
+ * this is the convenience path most callers use to build a flat exchange
28
+ * state. `component`/`originComponent` record the target/source namespace.
29
+ * @param {Partial<import('#src/types.mjs').ExchangeBlockState> & { properties?: { items?: import('#src/types.mjs').ExchangeItem[] } & Record<string, *> }} [overrides]
30
+ * @returns {import('#src/types.mjs').ExchangeBlockState}
31
+ */
32
+ export function createExchangeState({ component, originComponent, properties, settings, }?: Partial<import("#src/types.mjs").ExchangeBlockState> & {
33
+ properties?: {
34
+ items?: import("#src/types.mjs").ExchangeItem[];
35
+ } & Record<string, any>;
36
+ }): import("#src/types.mjs").ExchangeBlockState;
37
+ /**
38
+ * Collapse multiple exchange items into one flat text-shaped item:
39
+ * concatenates each item's text (`'\n'` between items) and shifts each
40
+ * item's inline/entity ranges by the joined text's length so far. Used by
41
+ * `BlockComponent`'s default `appendExchangeState` to merge a possibly
42
+ * multi-item incoming exchange state (e.g. a List being merged into a flat
43
+ * text block) down to the single flat shape a text block can hold. A
44
+ * single-item input (the common case — merging two flat blocks) passes
45
+ * through unchanged (no separator, no shift).
46
+ * @param {import('#src/types.mjs').ExchangeItem[] | null | undefined} items
47
+ * @returns {{ text: string, inlineStyleRanges: InlineStyleRange[], entityRanges: EntityRange[] }}
48
+ */
49
+ export function joinExchangeItems(items: import("#src/types.mjs").ExchangeItem[] | null | undefined): {
50
+ text: string;
51
+ inlineStyleRanges: InlineStyleRange[];
52
+ entityRanges: EntityRange[];
53
+ };
54
+ /**
55
+ * Wrap a single item's `text`/`inlineStyleRanges`/`entityRanges` (e.g. one
56
+ * List row) as the flat rich-text shape accepted by
57
+ * `splitStateByCursor`/`deepMergeBlockState`'s per-item helpers. Distinct
58
+ * from `createExchangeState`, which builds a full `properties.items[]`
59
+ * `ExchangeBlockState` — this is the single-item building block used to
60
+ * construct one entry of that array, or to feed the flat-shape line-split
61
+ * helpers with one item's data (e.g. splitting one list item at the caret).
62
+ * @param {Partial<import('#src/types.mjs').ExchangeItem> | null | undefined} item
63
+ * @returns {RichTextState}
64
+ */
65
+ export function wrapExchangeItem(item: Partial<import("#src/types.mjs").ExchangeItem> | null | undefined): RichTextState;
66
+ /**
67
+ * Write a patched item's ranges back into a block's own persisted state by
68
+ * round-tripping through its own `toExchangeState`/`fromExchangeState` pair
69
+ * (same-type, in-place patch — NOT a cross-type conversion). Shared by the
70
+ * generic INSERT_INLINE_STYLE_RANGE/INSERT_ENTITY_RANGE handlers so every
71
+ * block type (flat text blocks and List) writes through one path instead of
72
+ * each implementing its own mutation. Relies on `Constructor.toExchangeState`
73
+ * including a stable per-item `componentId` for blocks with per-item DOM
74
+ * identity (e.g. List's `<li>`s), so `fromExchangeState` doesn't mint fresh
75
+ * ids on every keystroke. Returns `state` unchanged when `itemIndex` is out
76
+ * of range.
77
+ * @param {import('#src/types.mjs').BlockConstructor} Constructor
78
+ * @param {import('#src/types.mjs').BlockState} state
79
+ * @param {number} itemIndex
80
+ * @param {Partial<import('#src/types.mjs').ExchangeItem>} patch
81
+ * @returns {import('#src/types.mjs').BlockState}
82
+ */
83
+ export function patchBlockExchangeItem(Constructor: import("#src/types.mjs").BlockConstructor, state: import("#src/types.mjs").BlockState, itemIndex: number, patch: Partial<import("#src/types.mjs").ExchangeItem>): import("#src/types.mjs").BlockState;
84
+ /**
85
+ * Resolve the active exchange item (and its element/index/Constructor) for
86
+ * the block at `blockElement`/`range`, via the generic `toExchangeState` +
87
+ * `getSelectableItemForRange` contract. Works for ANY block type without a
88
+ * block-specific special case: a flat text block (Paragraph/Headline)
89
+ * always resolves item 0; a List resolves the active `<li>`'s own index.
90
+ * Falls back to item 0 when `range` doesn't resolve to a marked item at all
91
+ * (e.g. a stale/lost selection) — a best-effort default appropriate for
92
+ * single-item flat blocks; List's own key handlers use
93
+ * `getSelectableItemForRange` directly instead and bail (no-op) on the same
94
+ * failure, since guessing item 0 there would silently apply an action to
95
+ * the wrong `<li>`. Shared by the default plugin's insert-range/
96
+ * formatting-query handlers AND `Tooltip` (called directly, not through an
97
+ * emitter round trip — this is a pure read/compute with no plugin ever
98
+ * needing to intercept it; a block that needs different resolution
99
+ * overrides `toExchangeState` instead, the existing per-block extension
100
+ * point). See `patchBlockExchangeItem` for the matching write-back half of
101
+ * this contract.
102
+ * @param {import('#src/types.mjs').Mrakomor} mrakomor
103
+ * @param {Element} blockElement
104
+ * @param {Range | null} range
105
+ * @returns {{ Constructor: import('#src/types.mjs').BlockConstructor, current: import('#src/types.mjs').BlockState, element: Element, itemIndex: number, item: import('#src/types.mjs').ExchangeItem } | null}
106
+ */
107
+ export function resolveActiveExchangeItem(mrakomor: import("#src/types.mjs").Mrakomor, blockElement: Element, range: Range | null): {
108
+ Constructor: import("#src/types.mjs").BlockConstructor;
109
+ current: import("#src/types.mjs").BlockState;
110
+ element: Element;
111
+ itemIndex: number;
112
+ item: import("#src/types.mjs").ExchangeItem;
113
+ } | null;
114
+ /**
115
+ * Restore caret after a block merge (`afterRender` is a macrotask). Goes
116
+ * through the block instance's own `setCursor` (e.g. `List.setCursor`
117
+ * targets a specific `<li>`) instead of `getBlockContentRoot` +
118
+ * `setCursorPosition` directly — a multi-item block's content root (e.g.
119
+ * List's `<ul>/<ol>`) counts characters from the START of the whole
120
+ * concatenated text, landing in the wrong item otherwise. Shared by
121
+ * `defaultMrakomorPlugin`'s `onMergeBlockElement` and the list plugin's own
122
+ * adjacent-list re-merge Backspace handler.
123
+ * @param {{
124
+ * mrakomor: import('#src/types.mjs').Mrakomor,
125
+ * resultIndex: number,
126
+ * cursor?: number,
127
+ * fallbackBlockElement?: Element | null,
128
+ * itemIndex?: number,
129
+ * }} options `resultIndex` indexes the merged block in the (already
130
+ * committed) content array; `cursor` is the caret offset in the merged
131
+ * block at the join; `fallbackBlockElement` is used before `afterRender`
132
+ * has synced `#blockElements`, or if `resultIndex` cannot resolve one;
133
+ * `itemIndex` is for multi-item blocks (e.g. List) — the index of the
134
+ * item the join landed in, passed through as `setCursor`'s `itemIndex`
135
+ * field (part of `BlockComponent.setCursor`'s shared contract, ignored by
136
+ * single-item blocks) so a merge that also appended items after the
137
+ * joined one still targets the right item instead of the block's
138
+ * default (e.g. last item)
139
+ * @returns {void}
140
+ */
141
+ export function restoreCaretAfterMerge({ mrakomor, resultIndex, cursor, fallbackBlockElement, itemIndex, }: {
142
+ mrakomor: import("#src/types.mjs").Mrakomor;
143
+ resultIndex: number;
144
+ cursor?: number;
145
+ fallbackBlockElement?: Element | null;
146
+ itemIndex?: number;
147
+ }): void;
148
+ /**
149
+ * Paragraph or headline — blocks with `properties.text`.
150
+ * @param {BlockState} state
151
+ * @returns {boolean}
152
+ */
153
+ export function isTextBlock(state: BlockState): boolean;
154
+ /**
155
+ * List block — `properties.items` is an array.
156
+ * @param {BlockState} state
157
+ * @returns {boolean}
158
+ */
159
+ export function isListBlock(state: BlockState): boolean;
160
+ /**
161
+ * Two text blocks of the same component can merge on paste/delete.
162
+ * @param {BlockState} left
163
+ * @param {BlockState} right
164
+ * @returns {boolean}
165
+ */
166
+ export function canMergeBlocks(left: BlockState, right: BlockState): boolean;
167
+ /**
168
+ * Nested vDOM for one text segment with all active inline styles and entity
169
+ * ranges applied (entity wraps inline).
170
+ *
171
+ * @param {string} substring
172
+ * @param {InlineStyleRange[]} inlineRanges
173
+ * @param {EntityRange[]} entityRanges
174
+ * @param {RichTextComponents} components
175
+ * @returns {string | Node | object}
176
+ */
177
+ export function buildRichTextSegment(substring: string, inlineRanges: InlineStyleRange[], entityRanges: EntityRange[], { inlineStyleComponents, entityRangeComponents }: RichTextComponents): string | Node | object;
178
+ /**
179
+ * Render paragraph rich text without duplicating overlapping ranges.
180
+ *
181
+ * @param {RichTextState} state block state with `properties.text` / ranges
182
+ * @param {RichTextComponents} components
183
+ * @returns {DocumentFragment}
184
+ */
185
+ export function buildRichTextFragment(state: RichTextState, { inlineStyleComponents, entityRangeComponents }: RichTextComponents): DocumentFragment;
186
+ /**
187
+ * Walk block child nodes and collect inline/entity ranges with correct offsets.
188
+ * Recurses into matched inline/entity elements so nested markup round-trips.
189
+ *
190
+ * @param {NodeList | Node[]} childNodes
191
+ * @param {RichTextState} state mutated; ranges are pushed onto `properties`
192
+ * @param {number} offset starting character index
193
+ * @param {RichTextComponents} components
194
+ * @returns {number} character index after the last consumed node
195
+ */
196
+ export function parseRichTextChildNodes(childNodes: NodeList | Node[], state: RichTextState, offset: number, { inlineStyleComponents, entityRangeComponents }: RichTextComponents): number;
197
+ /**
198
+ * Concatenate two text-block states: `original` text then `newData` text,
199
+ * shifting `newData` ranges by the original length.
200
+ *
201
+ * @param {RichTextState} original
202
+ * @param {RichTextState} newData
203
+ * @returns {{ state: RichTextState, cursor: number }} merged state and caret at the join
204
+ */
205
+ export function deepMergeBlockState(original: RichTextState, newData: RichTextState): {
206
+ state: RichTextState;
207
+ cursor: number;
208
+ };
209
+ /**
210
+ * Trim selection across blocks and merge remnants into one block.
211
+ * Keeps the start block's identity; removes indexes startIndex..endIndex.
212
+ *
213
+ * @param {BlockState[]} content
214
+ * @param {ClipboardSelection} selection
215
+ * @returns {ContentMutation}
216
+ */
217
+ export function mergeBlocksAcrossSelection(content: BlockState[], { startIndex, endIndex, startOffset, endOffset }: ClipboardSelection): ContentMutation;
218
+ /**
219
+ * Split a text block at `cursor`: before keeps the original `componentId`,
220
+ * after gets a new one. Inline and entity ranges are clipped/shifted.
221
+ *
222
+ * @param {RichTextState} state
223
+ * @param {number} cursor
224
+ * @returns {SplitBlockResult}
225
+ */
226
+ export function splitStateByCursor(state: RichTextState, cursor: number): SplitBlockResult;
227
+ /**
228
+ * Split a text block at the caret and insert the remainder as a new block.
229
+ * Used by Paragraph and Headline on Enter — not a BlockComponent protocol.
230
+ *
231
+ * @param {RichTextBlockInstance} block
232
+ * @param {import('#src/types.mjs').KeydownEvent} event
233
+ * @param {{ namespace?: string }} [options]
234
+ * @returns {boolean} whether Enter was handled
235
+ */
236
+ export function splitAtCursorAndInsert(block: RichTextBlockInstance, event: import("#src/types.mjs").KeydownEvent, { namespace }?: {
237
+ namespace?: string;
238
+ }): boolean;
239
+ /**
240
+ * Insert an empty paragraph below the current selection (non-splitting blocks).
241
+ * @param {{ mrakomor: import('#src/types.mjs').Mrakomor }} block
242
+ * @param {import('#src/types.mjs').KeydownEvent} event
243
+ * @returns {boolean} whether Enter was handled
244
+ */
245
+ export function insertEmptyParagraphBelow(block: {
246
+ mrakomor: import("#src/types.mjs").Mrakomor;
247
+ }, event: import("#src/types.mjs").KeydownEvent): boolean;
248
+ /**
249
+ * Parse a rich-text block from DOM: schema defaults plus inline/entity ranges
250
+ * from child nodes.
251
+ *
252
+ * @param {RichTextBlockInstance} block
253
+ * @param {Element | null | undefined} node
254
+ * @returns {RichTextState}
255
+ */
256
+ export function parseRichTextBlock(block: RichTextBlockInstance, node: Element | null | undefined): RichTextState;
257
+ /**
258
+ * Computes text-range metadata (`offset` and `length`) for a DOM `Range`
259
+ * relative to the concatenated text content of a root element.
260
+ *
261
+ * @function getEntityRangeMeta
262
+ * @param {Element} element - Root element used as the text traversal boundary.
263
+ * @param {Range} range - DOM Range whose start position is used to compute the offset.
264
+ * @returns {RangeMeta} Range metadata:
265
+ * - `offset`: zero-based character index from the start of `element` text content to `range.startContainer + range.startOffset`
266
+ * - `length`: selected character count (`range.endOffset - range.startOffset`)
267
+ *
268
+ */
269
+ export function getEntityRangeMeta(element: Element, range: Range): RangeMeta;
270
+ /**
271
+ * Toggle an inline style over a selection: remove a covering same-style range,
272
+ * otherwise append a new range.
273
+ * @param {InlineStyleRange[]} ranges
274
+ * @param {InlineStyleRange} next
275
+ * @returns {InlineStyleRange[]}
276
+ */
277
+ export function toggleInlineStyleRange(ranges: InlineStyleRange[], { style, offset, length }: InlineStyleRange): InlineStyleRange[];
278
+ /**
279
+ * Toggle or apply an entity range exclusively: exact same-span+same-type removes
280
+ * that range; otherwise carve overlapping ranges then append. Adjacent spans are
281
+ * allowed. Returns `null` when length is 0.
282
+ * @param {EntityRange[]} ranges
283
+ * @param {EntityRange} entityRangeState
284
+ * @returns {EntityRange[] | null}
285
+ */
286
+ export function toggleEntityRange(ranges: EntityRange[], entityRangeState: EntityRange): EntityRange[] | null;
287
+ /**
288
+ * Toggle an inline style range for a selection against `element`. Returns the
289
+ * next `inlineStyleRanges` array, or `null` when the selection is collapsed.
290
+ * @param {InlineStyleRange[] | undefined} ranges
291
+ * @param {Element} element - root used to compute the range's text offset/length
292
+ * @param {Range} range
293
+ * @param {string} style
294
+ * @returns {InlineStyleRange[] | null}
295
+ */
296
+ export function applyInlineStyleRange(ranges: InlineStyleRange[] | undefined, element: Element, range: Range, style: string): InlineStyleRange[] | null;
297
+ /**
298
+ * Toggle an entity range for a selection against `element`. Returns the next
299
+ * `entityRanges` array, or `null` when the selection is collapsed.
300
+ * @param {EntityRange[] | undefined} ranges
301
+ * @param {Element} element - root used to compute the range's text offset/length
302
+ * @param {Range} range
303
+ * @param {{ namespace: string, entityData?: object }} options
304
+ * @returns {EntityRange[] | null}
305
+ */
306
+ export function applyEntityRange(ranges: EntityRange[] | undefined, element: Element, range: Range, { namespace, entityData }: {
307
+ namespace: string;
308
+ entityData?: object;
309
+ }): EntityRange[] | null;
310
+ export type BlockState = import("#src/types.mjs").BlockState;
311
+ export type RichTextState = import("#src/types.mjs").RichTextState;
312
+ export type InlineStyleRange = import("#src/types.mjs").InlineStyleRange;
313
+ export type EntityRange = import("#src/types.mjs").EntityRange;
314
+ export type RichTextComponents = import("#src/types.mjs").RichTextComponents;
315
+ export type SplitBlockResult = import("#src/types.mjs").SplitBlockResult;
316
+ export type ContentMutation = import("#src/types.mjs").ContentMutation;
317
+ export type ClipboardSelection = import("#src/types.mjs").ClipboardSelection;
318
+ export type RichTextBlockInstance = import("#src/types.mjs").RichTextBlockInstance;
319
+ export type RangeMeta = import("#src/types.mjs").RangeMeta;
@@ -0,0 +1,4 @@
1
+ export const EditorContext: {
2
+ id: symbol;
3
+ defaultValue: any;
4
+ };
@@ -0,0 +1,15 @@
1
+ export class EntityRangeElement extends Component {
2
+ static get nodeName(): string;
3
+ static get NAMESPACE(): string;
4
+ static match(node: any): boolean;
5
+ static getStateSchema(_node: any): import("@esmj/schema").SchemaInterface<unknown, unknown>;
6
+ constructor(props: any);
7
+ mrakomor: any;
8
+ element: {
9
+ get: () => null;
10
+ set: (element: any) => null;
11
+ [Symbol.toPrimitive](): string;
12
+ };
13
+ render(): any;
14
+ }
15
+ import { Component } from '@esmj/dom';
@@ -0,0 +1,4 @@
1
+ export class Bold extends InlineElement {
2
+ static get LABEL(): any;
3
+ }
4
+ import { InlineElement } from './InlineElement.mjs';
@@ -0,0 +1,10 @@
1
+ export class InlineElement extends Component {
2
+ static get nodeName(): string;
3
+ static get style(): string;
4
+ static match(node: any): boolean;
5
+ getState(): {
6
+ style: any;
7
+ };
8
+ render(): any;
9
+ }
10
+ import { Component } from '@esmj/dom';
@@ -0,0 +1,4 @@
1
+ export class Italic extends InlineElement {
2
+ static get LABEL(): any;
3
+ }
4
+ import { InlineElement } from './InlineElement.mjs';
@@ -0,0 +1,4 @@
1
+ export class Subscript extends InlineElement {
2
+ static get LABEL(): any;
3
+ }
4
+ import { InlineElement } from './InlineElement.mjs';
@@ -0,0 +1,4 @@
1
+ export class Superscript extends InlineElement {
2
+ static get LABEL(): any;
3
+ }
4
+ import { InlineElement } from './InlineElement.mjs';
@@ -0,0 +1,4 @@
1
+ export class Underline extends InlineElement {
2
+ static get LABEL(): any;
3
+ }
4
+ import { InlineElement } from './InlineElement.mjs';
@@ -0,0 +1,5 @@
1
+ export { Bold } from "./Bold.mjs";
2
+ export { Italic } from "./Italic.mjs";
3
+ export { Subscript } from "./Subscript.mjs";
4
+ export { Superscript } from "./Superscript.mjs";
5
+ export { Underline } from "./Underline.mjs";