editable.ts 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +571 -0
  3. package/dist/editable.umd.cjs +2 -0
  4. package/dist/editable.umd.cjs.map +1 -0
  5. package/lib/block.d.ts +13 -0
  6. package/lib/block.d.ts.map +1 -0
  7. package/lib/block.js +58 -0
  8. package/lib/block.js.map +1 -0
  9. package/lib/clipboard.d.ts +22 -0
  10. package/lib/clipboard.d.ts.map +1 -0
  11. package/lib/clipboard.js +154 -0
  12. package/lib/clipboard.js.map +1 -0
  13. package/lib/config.d.ts +37 -0
  14. package/lib/config.d.ts.map +1 -0
  15. package/lib/config.js +64 -0
  16. package/lib/config.js.map +1 -0
  17. package/lib/content.d.ts +37 -0
  18. package/lib/content.d.ts.map +1 -0
  19. package/lib/content.js +526 -0
  20. package/lib/content.js.map +1 -0
  21. package/lib/core.d.ts +97 -0
  22. package/lib/core.d.ts.map +1 -0
  23. package/lib/core.js +261 -0
  24. package/lib/core.js.map +1 -0
  25. package/lib/create-default-behavior.d.ts +31 -0
  26. package/lib/create-default-behavior.d.ts.map +1 -0
  27. package/lib/create-default-behavior.js +178 -0
  28. package/lib/create-default-behavior.js.map +1 -0
  29. package/lib/create-default-events.d.ts +152 -0
  30. package/lib/create-default-events.d.ts.map +1 -0
  31. package/lib/create-default-events.js +183 -0
  32. package/lib/create-default-events.js.map +1 -0
  33. package/lib/cursor.d.ts +68 -0
  34. package/lib/cursor.d.ts.map +1 -0
  35. package/lib/cursor.js +354 -0
  36. package/lib/cursor.js.map +1 -0
  37. package/lib/dispatcher.d.ts +78 -0
  38. package/lib/dispatcher.d.ts.map +1 -0
  39. package/lib/dispatcher.js +416 -0
  40. package/lib/dispatcher.js.map +1 -0
  41. package/lib/eventable.d.ts +2 -0
  42. package/lib/eventable.d.ts.map +1 -0
  43. package/lib/eventable.js +104 -0
  44. package/lib/eventable.js.map +1 -0
  45. package/lib/feature-detection.d.ts +12 -0
  46. package/lib/feature-detection.d.ts.map +1 -0
  47. package/lib/feature-detection.js +42 -0
  48. package/lib/feature-detection.js.map +1 -0
  49. package/lib/highlight-support.d.ts +24 -0
  50. package/lib/highlight-support.d.ts.map +1 -0
  51. package/lib/highlight-support.js +172 -0
  52. package/lib/highlight-support.js.map +1 -0
  53. package/lib/highlight-text.d.ts +21 -0
  54. package/lib/highlight-text.d.ts.map +1 -0
  55. package/lib/highlight-text.js +147 -0
  56. package/lib/highlight-text.js.map +1 -0
  57. package/lib/keyboard.d.ts +33 -0
  58. package/lib/keyboard.d.ts.map +1 -0
  59. package/lib/keyboard.js +189 -0
  60. package/lib/keyboard.js.map +1 -0
  61. package/lib/monitored-highlighting.d.ts +28 -0
  62. package/lib/monitored-highlighting.d.ts.map +1 -0
  63. package/lib/monitored-highlighting.js +194 -0
  64. package/lib/monitored-highlighting.js.map +1 -0
  65. package/lib/node-iterator.d.ts +16 -0
  66. package/lib/node-iterator.d.ts.map +1 -0
  67. package/lib/node-iterator.js +97 -0
  68. package/lib/node-iterator.js.map +1 -0
  69. package/lib/node-type.d.ts +13 -0
  70. package/lib/node-type.d.ts.map +1 -0
  71. package/lib/node-type.js +15 -0
  72. package/lib/node-type.js.map +1 -0
  73. package/lib/parser.d.ts +89 -0
  74. package/lib/parser.d.ts.map +1 -0
  75. package/lib/parser.js +251 -0
  76. package/lib/parser.js.map +1 -0
  77. package/lib/plugins/highlighting/match-collection.d.ts +7 -0
  78. package/lib/plugins/highlighting/match-collection.d.ts.map +1 -0
  79. package/lib/plugins/highlighting/match-collection.js +62 -0
  80. package/lib/plugins/highlighting/match-collection.js.map +1 -0
  81. package/lib/plugins/highlighting/spellcheck-service.d.ts +12 -0
  82. package/lib/plugins/highlighting/spellcheck-service.d.ts.map +1 -0
  83. package/lib/plugins/highlighting/spellcheck-service.js +24 -0
  84. package/lib/plugins/highlighting/spellcheck-service.js.map +1 -0
  85. package/lib/plugins/highlighting/text-search.d.ts +10 -0
  86. package/lib/plugins/highlighting/text-search.d.ts.map +1 -0
  87. package/lib/plugins/highlighting/text-search.js +92 -0
  88. package/lib/plugins/highlighting/text-search.js.map +1 -0
  89. package/lib/plugins/highlighting/whitespace-highlighting.d.ts +14 -0
  90. package/lib/plugins/highlighting/whitespace-highlighting.d.ts.map +1 -0
  91. package/lib/plugins/highlighting/whitespace-highlighting.js +52 -0
  92. package/lib/plugins/highlighting/whitespace-highlighting.js.map +1 -0
  93. package/lib/quotes.d.ts +8 -0
  94. package/lib/quotes.d.ts.map +1 -0
  95. package/lib/quotes.js +170 -0
  96. package/lib/quotes.js.map +1 -0
  97. package/lib/range-container.d.ts +22 -0
  98. package/lib/range-container.d.ts.map +1 -0
  99. package/lib/range-container.js +52 -0
  100. package/lib/range-container.js.map +1 -0
  101. package/lib/range-save-restore.d.ts +13 -0
  102. package/lib/range-save-restore.d.ts.map +1 -0
  103. package/lib/range-save-restore.js +153 -0
  104. package/lib/range-save-restore.js.map +1 -0
  105. package/lib/selection-watcher.d.ts +55 -0
  106. package/lib/selection-watcher.d.ts.map +1 -0
  107. package/lib/selection-watcher.js +126 -0
  108. package/lib/selection-watcher.js.map +1 -0
  109. package/lib/selection.d.ts +74 -0
  110. package/lib/selection.d.ts.map +1 -0
  111. package/lib/selection.js +341 -0
  112. package/lib/selection.js.map +1 -0
  113. package/lib/smartQuotes.d.ts +16 -0
  114. package/lib/smartQuotes.d.ts.map +1 -0
  115. package/lib/smartQuotes.js +92 -0
  116. package/lib/smartQuotes.js.map +1 -0
  117. package/lib/util/binary_search.d.ts +23 -0
  118. package/lib/util/binary_search.d.ts.map +1 -0
  119. package/lib/util/binary_search.js +137 -0
  120. package/lib/util/binary_search.js.map +1 -0
  121. package/lib/util/clone-deep.d.ts +8 -0
  122. package/lib/util/clone-deep.d.ts.map +1 -0
  123. package/lib/util/clone-deep.js +10 -0
  124. package/lib/util/clone-deep.js.map +1 -0
  125. package/lib/util/dom.d.ts +43 -0
  126. package/lib/util/dom.d.ts.map +1 -0
  127. package/lib/util/dom.js +272 -0
  128. package/lib/util/dom.js.map +1 -0
  129. package/lib/util/element.d.ts +10 -0
  130. package/lib/util/element.d.ts.map +1 -0
  131. package/lib/util/element.js +29 -0
  132. package/lib/util/element.js.map +1 -0
  133. package/lib/util/error.d.ts +2 -0
  134. package/lib/util/error.d.ts.map +1 -0
  135. package/lib/util/error.js +16 -0
  136. package/lib/util/error.js.map +1 -0
  137. package/lib/util/log.d.ts +2 -0
  138. package/lib/util/log.d.ts.map +1 -0
  139. package/lib/util/log.js +18 -0
  140. package/lib/util/log.js.map +1 -0
  141. package/lib/util/merge.d.ts +6 -0
  142. package/lib/util/merge.d.ts.map +1 -0
  143. package/lib/util/merge.js +31 -0
  144. package/lib/util/merge.js.map +1 -0
  145. package/lib/util/string.d.ts +25 -0
  146. package/lib/util/string.d.ts.map +1 -0
  147. package/lib/util/string.js +68 -0
  148. package/lib/util/string.js.map +1 -0
  149. package/lib/util/viewport.d.ts +6 -0
  150. package/lib/util/viewport.d.ts.map +1 -0
  151. package/lib/util/viewport.js +8 -0
  152. package/lib/util/viewport.js.map +1 -0
  153. package/package.json +86 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Upfront GmbH, 2015 - 2018 Livingdocs AG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,571 @@
1
+ # editable.ts
2
+
3
+ A TypeScript library that provides a friendly and browser-consistent API for `contenteditable` elements. Built for block-level rich text editing with a clean, event-driven architecture. It is a fork of [https://github.com/livingdocsIO/editable.js](https://github.com/livingdocsIO/editable.js) but written in typescript.
4
+
5
+ ## Summary
6
+
7
+ **editable.ts** is a modern TypeScript rewrite of editable.js, offering a robust abstraction layer over the browser's native `contenteditable` API. It handles cross-browser inconsistencies, provides a structured event system, and enables building rich text editors with minimal boilerplate.
8
+
9
+ ### Key Features
10
+
11
+ - **Cross-browser compatibility** - Abstracts away browser differences in Selection and Range APIs
12
+ - **Event-driven architecture** - Clean pub/sub system for handling user interactions
13
+ - **Block-based editing** - Optimized for block-level elements (paragraphs, headings, blockquotes)
14
+ - **Selection & Cursor management** - Powerful APIs for manipulating text selections and cursor positions
15
+ - **Highlighting system** - Built-in support for text highlighting, spellcheck, and custom markers
16
+ - **Default behaviors** - Sensible defaults for common operations (split, merge, insert blocks)
17
+ - **TypeScript support** - Full type definitions and modern TypeScript implementation
18
+ - **Extensible** - Easy to customize and extend with custom event handlers
19
+
20
+ ### Use Cases
21
+
22
+ - Rich text editors
23
+ - Content management systems
24
+ - Comment and annotation systems
25
+ - Collaborative editing interfaces
26
+ - Inline editing components
27
+
28
+ Check out the [original editable.js live demo](https://livingdocsio.github.io/editable.js/) for a reference implementation (note: this is the JavaScript version, not this TypeScript fork).
29
+
30
+ ## What is it about?
31
+
32
+ A JavaScript API that defines a friendly and browser-consistent content editable interface.
33
+
34
+ Editable is built for block level elements containing only phrasing content. This normally means `p`, `h1`-`h6`, `blockquote` etc. elements. This allows editable to be lean and mean since it is only concerned with formatting and not with layouting.
35
+
36
+ We made editable.ts to support our vision of online document editing. Have a look at [livingdocs.io](http://livingdocs.io/).
37
+
38
+ ## Architecture Overview
39
+
40
+ editable.ts follows a layered architecture that separates concerns and provides clear extension points.
41
+
42
+ ### High-Level Architecture
43
+
44
+ ```mermaid
45
+ graph TB
46
+ subgraph PublicAPI["Public API Layer"]
47
+ Editable[Editable Class]
48
+ end
49
+
50
+ subgraph EventSystem["Event System Layer"]
51
+ Dispatcher[Dispatcher]
52
+ Eventable[Eventable Mixin]
53
+ SelectionWatcher[SelectionWatcher]
54
+ Keyboard[Keyboard Handler]
55
+ end
56
+
57
+ subgraph CoreComponents["Core Components"]
58
+ Block[Block Management]
59
+ Content[Content Management]
60
+ Parser[Parser]
61
+ Clipboard[Clipboard Handler]
62
+ end
63
+
64
+ subgraph SelectionSystem["Selection & Cursor"]
65
+ Cursor[Cursor]
66
+ Selection[Selection]
67
+ RangeContainer[Range Container]
68
+ end
69
+
70
+ subgraph Highlighting["Highlighting System"]
71
+ HighlightSupport[Highlight Support]
72
+ MonitoredHighlighting[Monitored Highlighting]
73
+ Plugins[Highlighting Plugins]
74
+ end
75
+
76
+ subgraph DOMAbstraction["DOM Abstraction Layer"]
77
+ DOMUtils[DOM Utilities]
78
+ ElementUtils[Element Utilities]
79
+ StringUtils[String Utilities]
80
+ end
81
+
82
+ Editable --> Dispatcher
83
+ Editable --> Block
84
+ Editable --> Content
85
+ Editable --> HighlightSupport
86
+
87
+ Dispatcher --> Eventable
88
+ Dispatcher --> SelectionWatcher
89
+ Dispatcher --> Keyboard
90
+
91
+ SelectionWatcher --> Cursor
92
+ SelectionWatcher --> Selection
93
+ SelectionWatcher --> RangeContainer
94
+
95
+ Cursor --> Content
96
+ Cursor --> Parser
97
+ Selection --> Cursor
98
+
99
+ HighlightSupport --> MonitoredHighlighting
100
+ MonitoredHighlighting --> Plugins
101
+
102
+ Content --> Parser
103
+ Content --> DOMUtils
104
+ Parser --> ElementUtils
105
+ Block --> DOMUtils
106
+ ```
107
+
108
+ ### Core Components
109
+
110
+ #### 1. Editable Class (`core.ts`)
111
+
112
+ The main entry point and public API. Provides a clean, chainable interface for all operations.
113
+
114
+ **Key Responsibilities:**
115
+ - Exposes the public API for end users
116
+ - Manages instance-specific configuration
117
+ - Delegates to specialized modules
118
+ - Provides cursor/selection creation utilities
119
+ - Handles highlighting operations
120
+
121
+ **Key Methods:**
122
+ - `add()` / `remove()` - Enable/disable editable functionality
123
+ - `enable()` / `disable()` - Control editable state
124
+ - `on()` / `off()` - Event subscription
125
+ - `getSelection()` - Get current selection/cursor
126
+ - `highlight()` - Text highlighting functionality
127
+ - `getContent()` - Extract clean content
128
+
129
+ #### 2. Dispatcher (`dispatcher.ts`)
130
+
131
+ Central event coordination hub that bridges native DOM events to the internal event system.
132
+
133
+ **Event Flow:**
134
+ ```
135
+ Native DOM Event
136
+
137
+ Dispatcher (setupDocumentListener)
138
+
139
+ Event Handler (filter by editable block)
140
+
141
+ SelectionWatcher (get current selection/cursor)
142
+
143
+ Dispatcher.notify() (emit internal event)
144
+
145
+ Event Handlers (user-defined callbacks)
146
+ ```
147
+
148
+ #### 3. Event System (`eventable.ts`)
149
+
150
+ Lightweight publish/subscribe mixin implementing the Observer pattern.
151
+
152
+ **API:**
153
+ - `on(event, handler)` - Subscribe to events
154
+ - `off(event, handler)` - Unsubscribe from events
155
+ - `notify(event, ...args)` - Publish events
156
+
157
+ #### 4. Selection & Cursor System
158
+
159
+ **SelectionWatcher** - Monitors browser Selection API and converts to internal Cursor/Selection objects
160
+
161
+ **Cursor** - Represents a collapsed selection (cursor position) with capabilities for:
162
+ - Position querying (beginning, end, line detection)
163
+ - Content insertion/manipulation
164
+ - Tag detection (bold, italic, links, etc.)
165
+ - Coordinate calculations
166
+
167
+ **Selection** - Extends Cursor, represents a non-collapsed selection with additional capabilities:
168
+ - Text/HTML extraction
169
+ - Selection wrapping (links, formatting)
170
+ - Range validation
171
+ - Multiple rect support
172
+
173
+ #### 5. Block Management (`block.ts`)
174
+
175
+ Manages the lifecycle and state of individual editable block elements.
176
+
177
+ #### 6. Content Management (`content.ts`)
178
+
179
+ Handles all content manipulation, extraction, and normalization:
180
+ - HTML normalization
181
+ - Content extraction (removes internal markers)
182
+ - Fragment creation
183
+ - Tag wrapping/unwrapping
184
+
185
+ #### 7. Highlighting System
186
+
187
+ Comprehensive highlighting support including:
188
+ - Spellcheck integration
189
+ - Text search highlighting
190
+ - Range-based highlighting
191
+ - Highlight persistence during editing
192
+ - Custom highlight types
193
+
194
+ ### Data Flow Examples
195
+
196
+ #### User Types Enter Key
197
+
198
+ ```mermaid
199
+ sequenceDiagram
200
+ participant User
201
+ participant Browser
202
+ participant Dispatcher
203
+ participant Keyboard
204
+ participant SelectionWatcher
205
+ participant DefaultBehavior
206
+
207
+ User->>Browser: Presses Enter
208
+ Browser->>Dispatcher: keydown event
209
+ Dispatcher->>Keyboard: dispatchKeyEvent()
210
+ Keyboard->>Dispatcher: 'enter' event
211
+ Dispatcher->>SelectionWatcher: getFreshRange()
212
+ SelectionWatcher-->>Dispatcher: Cursor object
213
+ Dispatcher->>DefaultBehavior: notify('split'/'insert')
214
+ DefaultBehavior->>Browser: DOM updated
215
+ Browser-->>User: Cursor positioned
216
+ ```
217
+
218
+ #### User Selects Text
219
+
220
+ ```mermaid
221
+ sequenceDiagram
222
+ participant User
223
+ participant Browser
224
+ participant Dispatcher
225
+ participant SelectionWatcher
226
+
227
+ User->>Browser: Selects text
228
+ Browser->>Dispatcher: selectionchange event
229
+ Dispatcher->>SelectionWatcher: selectionChanged()
230
+ SelectionWatcher->>SelectionWatcher: getFreshSelection()
231
+ SelectionWatcher-->>Dispatcher: Selection object
232
+ Dispatcher->>Dispatcher: notify('selection')
233
+ Dispatcher-->>User: User handlers execute
234
+ ```
235
+
236
+ For a detailed technical deep-dive, see [ARCHITECTURE.md](docs/ARCHITECTURE.md).
237
+
238
+ ## Installation
239
+
240
+ Via npm:
241
+
242
+ ```shell
243
+ npm install --save editable.ts
244
+ ```
245
+
246
+ You can either `import` the module or find a prebuilt file in the npm bundle `dist/editable.umd.cjs`.
247
+
248
+ ```typescript
249
+ import { Editable } from 'editable.ts'
250
+ ```
251
+
252
+ ## Quick Start
253
+
254
+ ### Basic Usage
255
+
256
+ To make an element editable:
257
+
258
+ ```typescript
259
+ import { Editable } from 'editable.ts'
260
+
261
+ // Create an instance
262
+ const editable = new Editable()
263
+
264
+ // Make an element editable
265
+ const element = document.querySelector('.my-editable')
266
+ editable.add(element)
267
+ ```
268
+
269
+ ### TypeScript Example
270
+
271
+ ```typescript
272
+ import { Editable } from 'editable.ts'
273
+
274
+ const editable = new Editable({
275
+ defaultBehavior: true,
276
+ browserSpellcheck: true
277
+ })
278
+
279
+ // Add editable functionality to elements
280
+ editable.add(document.querySelectorAll('.editable-block'))
281
+ ```
282
+
283
+ ## Examples
284
+
285
+ ### Selection Changes with Toolbar
286
+
287
+ In a `selection` event you get the editable element that triggered the event as well as a selection object. Through the selection object you can get information about the selection like coordinates or the text it contains and you can manipulate the selection.
288
+
289
+ In the following example we show a toolbar on top of the selection whenever the user has selected something inside of an editable element.
290
+
291
+ ```typescript
292
+ editable.on('selection', (editableElement: HTMLElement, selection: Selection | null) => {
293
+ if (!selection) {
294
+ toolbar.hide()
295
+ return
296
+ }
297
+
298
+ // Get coordinates relative to the document (suited for absolutely positioned elements)
299
+ const coords = selection.getCoordinates()
300
+
301
+ // Position toolbar
302
+ const top = coords.top - toolbar.outerHeight()
303
+ const left = coords.left + (coords.width / 2) - (toolbar.outerWidth() / 2)
304
+ toolbar.css({top, left}).show()
305
+ })
306
+ ```
307
+
308
+ ### Cursor Manipulation
309
+
310
+ Create and manipulate cursors programmatically:
311
+
312
+ ```typescript
313
+ // Get current cursor/selection
314
+ const cursor = editable.getSelection()
315
+
316
+ if (cursor && cursor.isCursor) {
317
+ // Check if cursor is at beginning of block
318
+ if (cursor.isAtBeginning()) {
319
+ console.log('Cursor is at the beginning')
320
+ }
321
+
322
+ // Insert text at cursor position
323
+ cursor.insert('Hello, World!')
324
+
325
+ // Create cursor at specific position
326
+ const newCursor = editable.createCursor(element, 'end')
327
+ newCursor?.insertAfter('<strong>Bold text</strong>')
328
+ }
329
+ ```
330
+
331
+ ### Content Extraction
332
+
333
+ Extract clean content from editable elements:
334
+
335
+ ```typescript
336
+ // Get clean HTML content (removes internal markers)
337
+ const content = editable.getContent(element)
338
+ console.log(content) // Clean HTML string
339
+
340
+ // Get selection text
341
+ const selection = editable.getSelection(element)
342
+ if (selection && selection.isSelection) {
343
+ const selectedText = selection.text()
344
+ const selectedHtml = selection.html()
345
+ console.log('Selected text:', selectedText)
346
+ console.log('Selected HTML:', selectedHtml)
347
+ }
348
+ ```
349
+
350
+ ### Event Handling
351
+
352
+ Handle multiple events with a clean API:
353
+
354
+ ```typescript
355
+ // Handle focus events
356
+ editable.on('focus', (element: HTMLElement) => {
357
+ console.log('Element focused:', element)
358
+ })
359
+
360
+ // Handle content changes
361
+ editable.on('change', (element: HTMLElement) => {
362
+ console.log('Content changed in:', element)
363
+ // Auto-save, validation, etc.
364
+ })
365
+
366
+ // Handle block splits (Enter key in middle of block)
367
+ editable.on('split', (element: HTMLElement, cursor: Cursor) => {
368
+ console.log('Block split at:', cursor)
369
+ // Custom split behavior
370
+ })
371
+
372
+ // Handle block merges (Backspace/Delete at boundaries)
373
+ editable.on('merge', (element: HTMLElement, cursor: Cursor) => {
374
+ console.log('Blocks merged at:', cursor)
375
+ // Custom merge behavior
376
+ })
377
+ ```
378
+
379
+ ### Highlighting
380
+
381
+ Add text highlighting and spellcheck:
382
+
383
+ ```typescript
384
+ // Highlight specific text
385
+ const highlightId = editable.highlight({
386
+ editableHost: element,
387
+ text: 'search term',
388
+ highlightId: 'search-1',
389
+ type: 'search'
390
+ })
391
+
392
+ // Highlight specific range
393
+ editable.highlight({
394
+ editableHost: element,
395
+ text: 'important',
396
+ highlightId: 'important-1',
397
+ textRange: { start: 10, end: 18 },
398
+ type: 'comment'
399
+ })
400
+
401
+ // Setup spellcheck
402
+ editable.setupSpellcheck({
403
+ throttle: 300,
404
+ spellcheckService: (text: string) => {
405
+ // Your spellcheck service
406
+ return checkSpelling(text)
407
+ }
408
+ })
409
+
410
+ // Remove highlight
411
+ editable.removeHighlight({
412
+ editableHost: element,
413
+ highlightId: 'search-1'
414
+ })
415
+ ```
416
+
417
+ ### Custom Event Handlers
418
+
419
+ Override default behaviors:
420
+
421
+ ```typescript
422
+ // Disable default behavior and implement custom
423
+ const editable = new Editable({
424
+ defaultBehavior: false
425
+ })
426
+
427
+ // Custom Enter key handling
428
+ editable.on('keydown', (element: HTMLElement, event: KeyboardEvent) => {
429
+ if (event.key === 'Enter') {
430
+ event.preventDefault()
431
+ // Custom Enter behavior
432
+ insertCustomBlock(element)
433
+ }
434
+ })
435
+ ```
436
+
437
+ ## Events Overview
438
+
439
+ editable.ts emits a comprehensive set of events for all user interactions:
440
+
441
+ ### Core Events
442
+
443
+ - **focus**
444
+ Fired when an editable element gets focus.
445
+
446
+ - **blur**
447
+ Fired when an editable element loses focus.
448
+
449
+ - **selection**
450
+ Fired when the user selects some text inside an editable element.
451
+
452
+ - **cursor**
453
+ Fired when the cursor position changes.
454
+
455
+ - **change**
456
+ Fired when the user has made a change.
457
+
458
+ - **input**
459
+ Fired on user input.
460
+
461
+ ### Content Modification Events
462
+
463
+ - **insert**
464
+ Fired when the user presses `ENTER` at the beginning or end of an editable (For example you can insert a new paragraph after the element if this happens).
465
+
466
+ - **split**
467
+ Fired when the user presses `ENTER` in the middle of an element.
468
+
469
+ - **merge**
470
+ Fired when the user pressed `FORWARD DELETE` at the end or `BACKSPACE` at the beginning of an element.
471
+
472
+ - **newline**
473
+ Fired when the user presses `SHIFT+ENTER` to insert a newline.
474
+
475
+ - **switch**
476
+ Fired when the user pressed an `ARROW KEY` at the top or bottom so that you may want to set the cursor into the preceding or following element.
477
+
478
+ ### Clipboard Events
479
+
480
+ - **clipboard**
481
+ Fired for `copy`, `cut` and `paste` events.
482
+
483
+ - **paste**
484
+ Fired specifically on paste operations.
485
+
486
+ ### Highlighting Events
487
+
488
+ - **spellcheckUpdated**
489
+ Fired when the spellcheckService has updated the spellcheck highlights.
490
+
491
+ ## API Reference
492
+
493
+ For detailed API documentation, see the source files:
494
+
495
+ - **[core.ts](src/core.ts)** - Main Editable class and public API
496
+ - **[cursor.ts](src/cursor.ts)** - Cursor manipulation API
497
+ - **[selection.ts](src/selection.ts)** - Selection manipulation API
498
+ - **[dispatcher.ts](src/dispatcher.ts)** - Event system internals
499
+ - **[create-default-behavior.ts](src/create-default-behavior.ts)** - Default behavior implementation
500
+
501
+ ### Type Definitions
502
+
503
+ ```typescript
504
+ interface EditableConfig {
505
+ window?: Window
506
+ defaultBehavior?: boolean
507
+ mouseMoveSelectionChanges?: boolean
508
+ browserSpellcheck?: boolean
509
+ }
510
+
511
+ interface HighlightOptions {
512
+ editableHost: HTMLElement
513
+ text: string
514
+ highlightId: string
515
+ textRange?: { start: number; end: number }
516
+ raiseEvents?: boolean
517
+ type?: string
518
+ }
519
+ ```
520
+
521
+ ## Development
522
+
523
+ ### Setup
524
+
525
+ ```bash
526
+ # Install node dependencies
527
+ npm install
528
+ ```
529
+
530
+ ### Development Tasks
531
+
532
+ ```bash
533
+ # Development server with demo app (Vite dev server)
534
+ npm start
535
+
536
+ # Run tests (Vitest)
537
+ npm test
538
+
539
+ # Run tests in watch mode
540
+ npm run test:watch
541
+
542
+ # Run tests with coverage
543
+ npm run test:coverage
544
+
545
+ # Run tests with interactive UI
546
+ npm run test:ui
547
+
548
+ # TypeScript/JavaScript linting
549
+ npm run lint
550
+
551
+ # Build editable.ts (TypeScript → lib/, then bundle → dist/)
552
+ npm run build
553
+
554
+ # Build TypeScript only
555
+ npm run build:ts
556
+
557
+ # Build library bundle only
558
+ npm run build:dist
559
+
560
+ # Build examples only
561
+ npm run build:docs
562
+ ```
563
+
564
+ ### Requirements
565
+
566
+ - Node.js >= 22
567
+ - npm >= 11
568
+
569
+ ## License
570
+
571
+ editable.ts is licensed under the [MIT License](LICENSE).