overtype 2.0.6 → 2.1.0

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. ~93KB minified with all features.
3
+ A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay technique. Includes optional toolbar. ~94KB 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** - ~93KB minified
22
+ - 🚀 **Lightweight** - ~94KB 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** | ~93KB | 364.02 KB | 344.51 KB | 560.99 KB | 323.69 KB |
38
+ | **Size** | ~94KB | 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 |
@@ -537,11 +537,22 @@ OverType.setTheme('solar', { h1: '#custom' }) // Override specific colors
537
537
  OverType.setCodeHighlighter((code, lang) => highlightedHTML)
538
538
  OverType.setCodeHighlighter(null) // Disable global highlighting
539
539
 
540
+ // Extend parsing with custom syntax (footnotes, directives, etc.)
541
+ // IMPORTANT: You must maintain 1-to-1 character alignment - wrap text, don't change it
542
+ // See: https://panphora.github.io/overtype/examples/custom-syntax.html
543
+ OverType.setCustomSyntax((html) => {
544
+ return html.replace(/\[\^(\w+)\]/g, '<span class="footnote">$&</span>');
545
+ })
546
+
540
547
  // Note: Instance methods override global settings
541
548
 
542
549
  // Initialize multiple editors (same as constructor)
543
550
  OverType.init(target, options)
544
551
 
552
+ // Initialize with per-element config via data-ot-* attributes
553
+ // Uses kebab-case: data-ot-show-stats="true" → showStats: true
554
+ OverType.initFromData('.editor', { /* defaults */ })
555
+
545
556
  // Get instance from element
546
557
  OverType.getInstance(element)
547
558
 
@@ -731,6 +742,26 @@ OverType uses a unique invisible textarea overlay approach:
731
742
  - Textarea content drives everything
732
743
  - One-way data flow: textarea → parser → preview
733
744
 
745
+ ## Data Attribute Configuration
746
+
747
+ Use `OverType.initFromData()` to configure multiple editors via HTML data attributes:
748
+
749
+ ```html
750
+ <div class="editor" data-ot-toolbar="true" data-ot-theme="cave"></div>
751
+ <div class="editor" data-ot-auto-resize="true" data-ot-min-height="200px"></div>
752
+ <div class="editor" data-ot-show-stats="true" data-ot-placeholder="Write here..."></div>
753
+
754
+ <script>
755
+ OverType.initFromData('.editor', { fontSize: '14px' }); // defaults
756
+ </script>
757
+ ```
758
+
759
+ Uses kebab-case attributes that convert to camelCase options (e.g., `data-ot-show-stats` → `showStats`).
760
+
761
+ **Supported:** `toolbar`, `theme`, `value`, `placeholder`, `autofocus`, `auto-resize`, `min-height`, `max-height`, `font-size`, `line-height`, `show-stats`, `smart-lists`, `show-active-line-raw`
762
+
763
+ **Not supported (use JS):** `toolbarButtons`, `textareaProps`, `onChange`, `onKeydown`, `statsFormatter`, `codeHighlighter`, `colors`, `mobile`
764
+
734
765
  ## Contributors
735
766
 
736
767
  Special thanks to:
@@ -27,6 +27,24 @@ var MarkdownParser = class {
27
27
  static setCodeHighlighter(highlighter) {
28
28
  this.codeHighlighter = highlighter;
29
29
  }
30
+ /**
31
+ * Set custom syntax processor function
32
+ * @param {Function|null} processor - Function that takes (html) and returns modified HTML
33
+ */
34
+ static setCustomSyntax(processor) {
35
+ this.customSyntax = processor;
36
+ }
37
+ /**
38
+ * Apply custom syntax processor to parsed HTML
39
+ * @param {string} html - Parsed HTML line
40
+ * @returns {string} HTML with custom syntax applied
41
+ */
42
+ static applyCustomSyntax(html) {
43
+ if (this.customSyntax) {
44
+ return this.customSyntax(html);
45
+ }
46
+ return html;
47
+ }
30
48
  /**
31
49
  * Escape HTML special characters
32
50
  * @param {string} text - Raw text to escape
@@ -365,14 +383,14 @@ var MarkdownParser = class {
365
383
  const codeFenceRegex = /^```[^`]*$/;
366
384
  if (codeFenceRegex.test(line)) {
367
385
  inCodeBlock = !inCodeBlock;
368
- return this.parseLine(line, isPreviewMode);
386
+ return this.applyCustomSyntax(this.parseLine(line, isPreviewMode));
369
387
  }
370
388
  if (inCodeBlock) {
371
389
  const escaped = this.escapeHtml(line);
372
390
  const indented = this.preserveIndentation(escaped, line);
373
391
  return `<div>${indented || "&nbsp;"}</div>`;
374
392
  }
375
- return this.parseLine(line, isPreviewMode);
393
+ return this.applyCustomSyntax(this.parseLine(line, isPreviewMode));
376
394
  });
377
395
  const html = parsedLines.join("");
378
396
  return this.postProcessHTML(html, instanceHighlighter);
@@ -704,6 +722,8 @@ var MarkdownParser = class {
704
722
  __publicField(MarkdownParser, "linkIndex", 0);
705
723
  // Global code highlighter function
706
724
  __publicField(MarkdownParser, "codeHighlighter", null);
725
+ // Custom syntax processor function
726
+ __publicField(MarkdownParser, "customSyntax", null);
707
727
  /**
708
728
  * List pattern definitions
709
729
  */
@@ -4084,6 +4104,8 @@ var _OverType = class _OverType {
4084
4104
  this.statsBar.className = "overtype-stats";
4085
4105
  this.container.appendChild(this.statsBar);
4086
4106
  this._updateStats();
4107
+ } else if (show && this.statsBar) {
4108
+ this._updateStats();
4087
4109
  } else if (!show && this.statsBar) {
4088
4110
  this.statsBar.remove();
4089
4111
  this.statsBar = null;
@@ -4152,6 +4174,44 @@ var _OverType = class _OverType {
4152
4174
  static init(target, options = {}) {
4153
4175
  return new _OverType(target, options);
4154
4176
  }
4177
+ /**
4178
+ * Initialize editors with options from data-ot-* attributes
4179
+ * @param {string} selector - CSS selector for target elements
4180
+ * @param {Object} defaults - Default options (data attrs override these)
4181
+ * @returns {Array<OverType>} Array of OverType instances
4182
+ * @example
4183
+ * // HTML: <div class="editor" data-ot-toolbar="true" data-ot-theme="cave"></div>
4184
+ * OverType.initFromData('.editor', { fontSize: '14px' });
4185
+ */
4186
+ static initFromData(selector, defaults = {}) {
4187
+ const elements = document.querySelectorAll(selector);
4188
+ return Array.from(elements).map((el) => {
4189
+ const options = { ...defaults };
4190
+ for (const attr of el.attributes) {
4191
+ if (attr.name.startsWith("data-ot-")) {
4192
+ const kebab = attr.name.slice(8);
4193
+ const key = kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
4194
+ options[key] = _OverType._parseDataValue(attr.value);
4195
+ }
4196
+ }
4197
+ return new _OverType(el, options);
4198
+ });
4199
+ }
4200
+ /**
4201
+ * Parse a data attribute value to the appropriate type
4202
+ * @private
4203
+ */
4204
+ static _parseDataValue(value) {
4205
+ if (value === "true")
4206
+ return true;
4207
+ if (value === "false")
4208
+ return false;
4209
+ if (value === "null")
4210
+ return null;
4211
+ if (value !== "" && !isNaN(Number(value)))
4212
+ return Number(value);
4213
+ return value;
4214
+ }
4155
4215
  /**
4156
4216
  * Get instance from element
4157
4217
  * @param {Element} element - DOM element
@@ -4252,6 +4312,32 @@ var _OverType = class _OverType {
4252
4312
  }
4253
4313
  });
4254
4314
  }
4315
+ /**
4316
+ * Set custom syntax processor for extending markdown parsing
4317
+ * @param {Function|null} processor - Function that takes (html) and returns modified HTML
4318
+ * @example
4319
+ * OverType.setCustomSyntax((html) => {
4320
+ * // Highlight footnote references [^1]
4321
+ * return html.replace(/\[\^(\w+)\]/g, '<span class="footnote-ref">$&</span>');
4322
+ * });
4323
+ */
4324
+ static setCustomSyntax(processor) {
4325
+ MarkdownParser.setCustomSyntax(processor);
4326
+ document.querySelectorAll(".overtype-wrapper").forEach((wrapper) => {
4327
+ const instance = wrapper._instance;
4328
+ if (instance && instance.updatePreview) {
4329
+ instance.updatePreview();
4330
+ }
4331
+ });
4332
+ document.querySelectorAll("overtype-editor").forEach((webComponent) => {
4333
+ if (typeof webComponent.getEditor === "function") {
4334
+ const instance = webComponent.getEditor();
4335
+ if (instance && instance.updatePreview) {
4336
+ instance.updatePreview();
4337
+ }
4338
+ }
4339
+ });
4340
+ }
4255
4341
  /**
4256
4342
  * Initialize global event listeners
4257
4343
  */