overtype 2.3.9 → 2.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OverType
2
2
 
3
- A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~110KB minified with all features.
3
+ A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~111KB minified with all features.
4
4
 
5
5
  ## Live Examples
6
6
 
@@ -19,7 +19,7 @@ A lightweight markdown editor library with perfect WYSIWYG alignment using an in
19
19
  - ⌨️ **Keyboard shortcuts** - Common markdown shortcuts (Cmd/Ctrl+B for bold, etc.)
20
20
  - 📱 **Mobile optimized** - Responsive design with mobile-specific styles
21
21
  - 🔄 **DOM persistence aware** - Recovers from existing DOM (perfect for HyperClay and similar platforms)
22
- - 🚀 **Lightweight** - ~110KB minified
22
+ - 🚀 **Lightweight** - ~111KB minified
23
23
  - 🎯 **Optional toolbar** - Clean, minimal toolbar with all essential formatting
24
24
  - ✨ **Smart shortcuts** - Keyboard shortcuts with selection preservation
25
25
  - 📝 **Smart list continuation** - GitHub-style automatic list continuation on Enter
@@ -35,7 +35,7 @@ We overlap an invisible textarea on top of styled output, giving the illusion of
35
35
 
36
36
  | Feature | OverType | HyperMD | Milkdown | TUI Editor | EasyMDE |
37
37
  |---------|----------|---------|----------|------------|---------|
38
- | **Size** | ~110KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
38
+ | **Size** | ~111KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
39
39
  | **Dependencies** | Bundled | CodeMirror | ProseMirror + plugins | Multiple libs | CodeMirror |
40
40
  | **Setup** | Single file | Complex config | Build step required | Complex config | Moderate |
41
41
  | **Approach** | Invisible textarea | ContentEditable | ContentEditable | ContentEditable | CodeMirror |
@@ -145,6 +145,24 @@ const [editor] = new OverType('#editor', {
145
145
  // orderedList, taskList, quote, separator, viewMode
146
146
  ```
147
147
 
148
+ **Driving formatting from your own UI:**
149
+
150
+ OverType re-exports the bundled `markdown-actions` library so you can build a fully custom toolbar (or any UI) without installing or bundling `markdown-actions` separately:
151
+
152
+ ```javascript
153
+ import OverType, { markdownActions } from 'overtype';
154
+
155
+ const [editor] = new OverType('#editor');
156
+
157
+ // Apply formatting to the editor's textarea directly
158
+ document.querySelector('#bold-btn').addEventListener('click', () => {
159
+ markdownActions.toggleBold(editor.textarea);
160
+ editor.textarea.focus();
161
+ });
162
+ ```
163
+
164
+ Available actions include `toggleBold`, `toggleItalic`, `toggleCode`, `insertLink`, `toggleBulletList`, `toggleNumberedList`, `toggleQuote`, `toggleTaskList`, `insertHeader`, `toggleH1`/`H2`/`H3`, `getActiveFormats`, `hasFormat`, `expandSelection`, and `applyCustomFormat`. The same namespace is available as `window.markdownActions` and `OverType.markdownActions` in script-tag builds.
165
+
148
166
  See [examples/custom-toolbar.html](examples/custom-toolbar.html) for complete examples.
149
167
 
150
168
  ### View Modes
@@ -236,6 +254,38 @@ document.querySelector('form').addEventListener('submit', (e) => {
236
254
  });
237
255
  ```
238
256
 
257
+ ### File Uploads
258
+
259
+ OverType handles paste and drop of files when `fileUpload` is configured. You upload to your own backend in `onInsertFile` and return the markdown to insert. When that markdown link is later removed from the editor, `onRemoveFile` fires so you can clean up the backend file.
260
+
261
+ ```javascript
262
+ const [editor] = new OverType('#editor', {
263
+ fileUpload: {
264
+ enabled: true,
265
+ maxSize: 10 * 1024 * 1024, // 10MB
266
+ mimeTypes: ['image/png', 'image/jpeg'], // optional whitelist; empty = accept all
267
+ batch: false, // true = one onInsertFile call per drop
268
+
269
+ // Upload to your backend, return the markdown link to insert
270
+ onInsertFile: async (file) => {
271
+ const { url } = await uploadToBackend(file);
272
+ const isImage = file.type.startsWith('image/');
273
+ return isImage ? `![${file.name}](${url})` : `[${file.name}](${url})`;
274
+ },
275
+
276
+ // Optional: fires when an inserted link is removed from the editor.
277
+ // Useful for deleting orphaned files from storage.
278
+ onRemoveFile: ({ url, filename, file }) => {
279
+ fetch(`/api/files/${encodeURIComponent(url)}`, { method: 'DELETE' });
280
+ }
281
+ }
282
+ });
283
+ ```
284
+
285
+ `onRemoveFile` only fires for URLs that OverType originally inserted via `onInsertFile`. URL edits, manual paste of an existing link, or programmatic edits to non-tracked URLs do not trigger it.
286
+
287
+ See [examples/file-upload.html](website/examples/file-upload.html) for a complete working demo.
288
+
239
289
  ### Custom Theme
240
290
 
241
291
  ```javascript
@@ -476,7 +526,19 @@ new OverType(target, options)
476
526
 
477
527
  // Callbacks
478
528
  onChange: (value, instance) => {},
479
- onKeydown: (event, instance) => {}
529
+ onKeydown: (event, instance) => {},
530
+ onFocus: (event, instance) => {},
531
+ onBlur: (event, instance) => {},
532
+
533
+ // File upload (paste/drop) — see "File Uploads" section below
534
+ fileUpload: {
535
+ enabled: true,
536
+ maxSize: 10 * 1024 * 1024, // bytes (default 10MB)
537
+ mimeTypes: [], // empty = accept all
538
+ batch: false, // single onInsertFile call per drop
539
+ onInsertFile: async (file) => `[${file.name}](url)`, // required: return inserted markdown
540
+ onRemoveFile: ({ url, filename, file }) => {} // fires when an inserted link is removed
541
+ }
480
542
  }
481
543
  ```
482
544
 
@@ -554,10 +616,12 @@ OverType.init(target, options)
554
616
 
555
617
  // Initialize with per-element config via data-ot-* attributes
556
618
  // Uses kebab-case: data-ot-show-stats="true" → showStats: true
619
+ // Accepts a selector, Element, NodeList, or Array of elements
557
620
  OverType.initFromData('.editor', { /* defaults */ })
558
621
 
559
- // Get instance from element
560
- OverType.getInstance(element)
622
+ // Get instance from a selector, Element, NodeList, or Array
623
+ // Returns the instance for the first matching element
624
+ OverType.getInstance(target)
561
625
 
562
626
  // Destroy all instances
563
627
  OverType.destroyAll()
@@ -763,7 +827,7 @@ Uses kebab-case attributes that convert to camelCase options (e.g., `data-ot-sho
763
827
 
764
828
  **Supported:** `toolbar`, `theme`, `value`, `placeholder`, `autofocus`, `auto-resize`, `min-height`, `max-height`, `font-size`, `line-height`, `show-stats`, `smart-lists`, `show-active-line-raw`
765
829
 
766
- **Not supported (use JS):** `toolbarButtons`, `textareaProps`, `onChange`, `onKeydown`, `statsFormatter`, `codeHighlighter`, `colors`, `mobile`
830
+ **Not supported (use JS):** `toolbarButtons`, `textareaProps`, `onChange`, `onKeydown`, `onFocus`, `onBlur`, `statsFormatter`, `codeHighlighter`, `colors`, `mobile`
767
831
 
768
832
  ## Contributors
769
833
 
@@ -776,7 +840,7 @@ Special thanks to:
776
840
  - [Lyric Wai](https://github.com/lyricat) - Fixed double-escaping of links ([#64](https://github.com/panphora/overtype/pull/64)), shared code block alignment fix ([#65](https://github.com/panphora/overtype/issues/65))
777
841
  - [kozi](https://github.com/kozi) - Reported Firefox link tooltip bug ([#68](https://github.com/panphora/overtype/issues/68)), toolbar positioning ([#69](https://github.com/panphora/overtype/issues/69)), theme CSS variable issues ([#70](https://github.com/panphora/overtype/issues/70), [#71](https://github.com/panphora/overtype/issues/71))
778
842
  - [1951FDG](https://github.com/1951FDG) - Reported unordered list rendering bug ([#74](https://github.com/panphora/overtype/issues/74)), suggested showStats() API improvement ([#77](https://github.com/panphora/overtype/issues/77)), reported placeholder CSS regression ([#102](https://github.com/panphora/overtype/issues/102))
779
- - [nodesocket](https://github.com/nodesocket) - Reported toolbarButtons export issues ([#73](https://github.com/panphora/overtype/issues/73), [#78](https://github.com/panphora/overtype/issues/78)), suggested image toolbar button ([#89](https://github.com/panphora/overtype/issues/89)), reported custom theme stats bar styling ([#101](https://github.com/panphora/overtype/issues/101))
843
+ - [nodesocket](https://github.com/nodesocket) - Reported toolbarButtons export issues ([#73](https://github.com/panphora/overtype/issues/73), [#78](https://github.com/panphora/overtype/issues/78)), suggested image toolbar button ([#89](https://github.com/panphora/overtype/issues/89)), reported custom theme stats bar styling ([#101](https://github.com/panphora/overtype/issues/101)), suggested onRemoveFile callback ([#104](https://github.com/panphora/overtype/issues/104))
780
844
  - [Travis Bell](https://github.com/travisbell) - Reported keyboard shortcuts bug in ESM build ([#80](https://github.com/panphora/overtype/issues/80))
781
845
  - [fab2713](https://github.com/fab2713) - Reported italic rendering in lists ([#81](https://github.com/panphora/overtype/issues/81)), reinit maxHeight ([#82](https://github.com/panphora/overtype/issues/82)), placeholder visibility ([#83](https://github.com/panphora/overtype/issues/83)), suggested auto theme ([#84](https://github.com/panphora/overtype/issues/84)), relative URL prefix ([#85](https://github.com/panphora/overtype/issues/85)), minification improvements ([#94](https://github.com/panphora/overtype/issues/94))
782
846
  - [oooo-ps](https://github.com/oooo-ps) - Reported remote script loading issue ([#86](https://github.com/panphora/overtype/issues/86))
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v2.3.9
2
+ * OverType v2.3.10
3
3
  * A lightweight markdown editor library with perfect WYSIWYG alignment
4
4
  * @license MIT
5
5
  * @author David Miranda
@@ -5172,6 +5172,7 @@ var _OverType = class _OverType {
5172
5172
  return;
5173
5173
  }
5174
5174
  this._fileUploadCounter = 0;
5175
+ this._uploadedFiles = /* @__PURE__ */ new Map();
5175
5176
  this._boundHandleFilePaste = this._handleFilePaste.bind(this);
5176
5177
  this._boundHandleFileDrop = this._handleFileDrop.bind(this);
5177
5178
  this._boundHandleDragOver = this._handleDragOver.bind(this);
@@ -5180,6 +5181,53 @@ var _OverType = class _OverType {
5180
5181
  this.textarea.addEventListener("dragover", this._boundHandleDragOver);
5181
5182
  this.fileUploadInitialized = true;
5182
5183
  }
5184
+ /**
5185
+ * Extract URLs from markdown link syntax: [text](url) or ![text](url).
5186
+ * @private
5187
+ */
5188
+ _extractMarkdownUrls(text) {
5189
+ const urls = [];
5190
+ const re = /!?\[[^\]]*\]\(([^)\s]+)/g;
5191
+ let m;
5192
+ while ((m = re.exec(text)) !== null)
5193
+ urls.push(m[1]);
5194
+ return urls;
5195
+ }
5196
+ /**
5197
+ * Track URLs that were just inserted, pairing each with the source File.
5198
+ * If multiple URLs appear in one inserted block, all get associated with
5199
+ * the same file (rare; happens if onInsertFile returns several links).
5200
+ * @private
5201
+ */
5202
+ _trackInsertedUrls(insertedText, file) {
5203
+ if (!this._uploadedFiles || !file || !insertedText)
5204
+ return;
5205
+ for (const url of this._extractMarkdownUrls(insertedText)) {
5206
+ this._uploadedFiles.set(url, { filename: file.name, file });
5207
+ }
5208
+ }
5209
+ /**
5210
+ * Diff the tracked-URL set against the current value and fire
5211
+ * fileUpload.onRemoveFile for any URL no longer present.
5212
+ * @private
5213
+ */
5214
+ _checkForRemovedUploads() {
5215
+ var _a;
5216
+ if (!this._uploadedFiles || this._uploadedFiles.size === 0)
5217
+ return;
5218
+ const cb = (_a = this.options.fileUpload) == null ? void 0 : _a.onRemoveFile;
5219
+ const value = this.textarea.value;
5220
+ const removed = [];
5221
+ for (const [url, info] of this._uploadedFiles) {
5222
+ if (!value.includes(url))
5223
+ removed.push({ url, info });
5224
+ }
5225
+ for (const { url, info } of removed) {
5226
+ this._uploadedFiles.delete(url);
5227
+ if (cb)
5228
+ cb({ url, filename: info.filename, file: info.file });
5229
+ }
5230
+ }
5183
5231
  _handleFilePaste(e) {
5184
5232
  var _a, _b;
5185
5233
  if (!((_b = (_a = e == null ? void 0 : e.clipboardData) == null ? void 0 : _a.files) == null ? void 0 : _b.length))
@@ -5212,6 +5260,7 @@ var _OverType = class _OverType {
5212
5260
  }
5213
5261
  this.options.fileUpload.onInsertFile(file).then((text) => {
5214
5262
  this.textarea.value = this.textarea.value.replace(placeholder, text);
5263
+ this._trackInsertedUrls(text, file);
5215
5264
  this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
5216
5265
  }, (error) => {
5217
5266
  console.error("OverType: File upload failed", error);
@@ -5224,6 +5273,7 @@ var _OverType = class _OverType {
5224
5273
  const texts = Array.isArray(result) ? result : [result];
5225
5274
  texts.forEach((text, index) => {
5226
5275
  this.textarea.value = this.textarea.value.replace(files[index].placeholder, text);
5276
+ this._trackInsertedUrls(text, files[index].file);
5227
5277
  });
5228
5278
  this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
5229
5279
  }, (error) => {
@@ -5245,6 +5295,7 @@ var _OverType = class _OverType {
5245
5295
  this._boundHandleFilePaste = null;
5246
5296
  this._boundHandleFileDrop = null;
5247
5297
  this._boundHandleDragOver = null;
5298
+ this._uploadedFiles = null;
5248
5299
  this.fileUploadInitialized = false;
5249
5300
  }
5250
5301
  insertAtCursor(text) {
@@ -5289,9 +5340,12 @@ var _OverType = class _OverType {
5289
5340
  * @private
5290
5341
  */
5291
5342
  _notifyChange() {
5292
- if (!this.options.onChange || !this.initialized)
5343
+ if (!this.initialized)
5293
5344
  return;
5294
- this.options.onChange(this.textarea.value, this);
5345
+ this._checkForRemovedUploads();
5346
+ if (this.options.onChange) {
5347
+ this.options.onChange(this.textarea.value, this);
5348
+ }
5295
5349
  }
5296
5350
  /**
5297
5351
  * Apply background styling to code blocks