overtype 2.1.1 → 2.3.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 +36 -23
- package/dist/overtype-webcomponent.esm.js +1647 -120
- package/dist/overtype-webcomponent.esm.js.map +4 -4
- package/dist/overtype-webcomponent.js +1647 -120
- package/dist/overtype-webcomponent.js.map +4 -4
- package/dist/overtype-webcomponent.min.js +129 -111
- package/dist/overtype.cjs +1617 -117
- package/dist/overtype.cjs.map +4 -4
- package/dist/overtype.d.ts +33 -0
- package/dist/overtype.esm.js +1617 -117
- package/dist/overtype.esm.js.map +4 -4
- package/dist/overtype.js +1617 -117
- package/dist/overtype.js.map +4 -4
- package/dist/overtype.min.js +132 -114
- package/package.json +4 -4
- package/src/icons.js +6 -0
- package/src/link-tooltip.js +22 -67
- package/src/overtype-webcomponent.js +32 -3
- package/src/overtype.d.ts +33 -0
- package/src/overtype.js +272 -36
- package/src/parser.js +9 -3
- package/src/styles.js +62 -49
- package/src/themes.js +54 -3
- package/src/toolbar-buttons.js +23 -0
- package/src/toolbar.js +12 -0
package/src/overtype.js
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
import { MarkdownParser } from './parser.js';
|
|
8
8
|
import { ShortcutsManager } from './shortcuts.js';
|
|
9
9
|
import { generateStyles } from './styles.js';
|
|
10
|
-
import { getTheme, mergeTheme, solar, themeToCSSVars } from './themes.js';
|
|
10
|
+
import { getTheme, mergeTheme, solar, themeToCSSVars, resolveAutoTheme } from './themes.js';
|
|
11
11
|
import { Toolbar } from './toolbar.js';
|
|
12
12
|
import { LinkTooltip } from './link-tooltip.js';
|
|
13
|
-
import { defaultToolbarButtons } from './toolbar-buttons.js';
|
|
13
|
+
import { defaultToolbarButtons, toolbarButtons as builtinToolbarButtons } from './toolbar-buttons.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Build action map from toolbar button configurations
|
|
@@ -80,6 +80,11 @@ class OverType {
|
|
|
80
80
|
static stylesInjected = false;
|
|
81
81
|
static globalListenersInitialized = false;
|
|
82
82
|
static instanceCount = 0;
|
|
83
|
+
static _autoMediaQuery = null;
|
|
84
|
+
static _autoMediaListener = null;
|
|
85
|
+
static _autoInstances = new Set();
|
|
86
|
+
static _globalAutoTheme = false;
|
|
87
|
+
static _globalAutoCustomColors = null;
|
|
83
88
|
|
|
84
89
|
/**
|
|
85
90
|
* Constructor - Always returns an array of instances
|
|
@@ -156,6 +161,10 @@ class OverType {
|
|
|
156
161
|
this._buildFromScratch();
|
|
157
162
|
}
|
|
158
163
|
|
|
164
|
+
if (this.instanceTheme === 'auto') {
|
|
165
|
+
this.setTheme('auto');
|
|
166
|
+
}
|
|
167
|
+
|
|
159
168
|
// Setup shortcuts manager
|
|
160
169
|
this.shortcuts = new ShortcutsManager(this);
|
|
161
170
|
|
|
@@ -226,7 +235,8 @@ class OverType {
|
|
|
226
235
|
toolbarButtons: null, // Defaults to defaultToolbarButtons if toolbar: true
|
|
227
236
|
statsFormatter: null,
|
|
228
237
|
smartLists: true, // Enable smart list continuation
|
|
229
|
-
codeHighlighter: null // Per-instance code highlighter
|
|
238
|
+
codeHighlighter: null, // Per-instance code highlighter
|
|
239
|
+
spellcheck: false // Browser spellcheck (disabled by default)
|
|
230
240
|
};
|
|
231
241
|
|
|
232
242
|
// Remove theme and colors from options - these are now global
|
|
@@ -414,9 +424,16 @@ class OverType {
|
|
|
414
424
|
this.preview.className = 'overtype-preview';
|
|
415
425
|
this.preview.setAttribute('aria-hidden', 'true');
|
|
416
426
|
|
|
427
|
+
// Create placeholder shim
|
|
428
|
+
this.placeholderEl = document.createElement('div');
|
|
429
|
+
this.placeholderEl.className = 'overtype-placeholder';
|
|
430
|
+
this.placeholderEl.setAttribute('aria-hidden', 'true');
|
|
431
|
+
this.placeholderEl.textContent = this.options.placeholder;
|
|
432
|
+
|
|
417
433
|
// Assemble DOM
|
|
418
434
|
this.wrapper.appendChild(this.textarea);
|
|
419
435
|
this.wrapper.appendChild(this.preview);
|
|
436
|
+
this.wrapper.appendChild(this.placeholderEl);
|
|
420
437
|
|
|
421
438
|
// No need to prevent link clicks - pointer-events handles this
|
|
422
439
|
|
|
@@ -451,7 +468,7 @@ class OverType {
|
|
|
451
468
|
this.textarea.setAttribute('autocomplete', 'off');
|
|
452
469
|
this.textarea.setAttribute('autocorrect', 'off');
|
|
453
470
|
this.textarea.setAttribute('autocapitalize', 'off');
|
|
454
|
-
this.textarea.setAttribute('spellcheck',
|
|
471
|
+
this.textarea.setAttribute('spellcheck', String(this.options.spellcheck));
|
|
455
472
|
this.textarea.setAttribute('data-gramm', 'false');
|
|
456
473
|
this.textarea.setAttribute('data-gramm_editor', 'false');
|
|
457
474
|
this.textarea.setAttribute('data-enable-grammarly', 'false');
|
|
@@ -462,12 +479,22 @@ class OverType {
|
|
|
462
479
|
* @private
|
|
463
480
|
*/
|
|
464
481
|
_createToolbar() {
|
|
465
|
-
|
|
466
|
-
|
|
482
|
+
let toolbarButtons = this.options.toolbarButtons || defaultToolbarButtons;
|
|
483
|
+
|
|
484
|
+
if (this.options.fileUpload?.enabled && !toolbarButtons.some(b => b?.name === 'upload')) {
|
|
485
|
+
const viewModeIdx = toolbarButtons.findIndex(b => b?.name === 'viewMode');
|
|
486
|
+
if (viewModeIdx !== -1) {
|
|
487
|
+
toolbarButtons = [...toolbarButtons];
|
|
488
|
+
toolbarButtons.splice(viewModeIdx, 0, builtinToolbarButtons.separator, builtinToolbarButtons.upload);
|
|
489
|
+
} else {
|
|
490
|
+
toolbarButtons = [...toolbarButtons, builtinToolbarButtons.separator, builtinToolbarButtons.upload];
|
|
491
|
+
}
|
|
492
|
+
}
|
|
467
493
|
|
|
468
494
|
this.toolbar = new Toolbar(this, { toolbarButtons });
|
|
469
495
|
this.toolbar.create();
|
|
470
496
|
|
|
497
|
+
|
|
471
498
|
// Store listener references for cleanup
|
|
472
499
|
this._toolbarSelectionListener = () => {
|
|
473
500
|
if (this.toolbar) {
|
|
@@ -513,6 +540,11 @@ class OverType {
|
|
|
513
540
|
if (this.options.toolbarButtons) {
|
|
514
541
|
Object.assign(this.actionsById, buildActionsMap(this.options.toolbarButtons));
|
|
515
542
|
}
|
|
543
|
+
|
|
544
|
+
// Register upload action when file upload is enabled
|
|
545
|
+
if (this.options.fileUpload?.enabled) {
|
|
546
|
+
Object.assign(this.actionsById, buildActionsMap([builtinToolbarButtons.upload]));
|
|
547
|
+
}
|
|
516
548
|
}
|
|
517
549
|
|
|
518
550
|
/**
|
|
@@ -529,6 +561,8 @@ class OverType {
|
|
|
529
561
|
if (this.options.autoResize) {
|
|
530
562
|
if (!this.container.classList.contains('overtype-auto-resize')) {
|
|
531
563
|
this._setupAutoResize();
|
|
564
|
+
} else {
|
|
565
|
+
this._updateAutoHeight();
|
|
532
566
|
}
|
|
533
567
|
} else {
|
|
534
568
|
// Ensure auto-resize class is removed
|
|
@@ -546,10 +580,135 @@ class OverType {
|
|
|
546
580
|
this.toolbar = null;
|
|
547
581
|
}
|
|
548
582
|
|
|
583
|
+
// Update placeholder text
|
|
584
|
+
if (this.placeholderEl) {
|
|
585
|
+
this.placeholderEl.textContent = this.options.placeholder;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Setup or remove file upload
|
|
589
|
+
if (this.options.fileUpload && !this.fileUploadInitialized) {
|
|
590
|
+
this._initFileUpload();
|
|
591
|
+
} else if (!this.options.fileUpload && this.fileUploadInitialized) {
|
|
592
|
+
this._destroyFileUpload();
|
|
593
|
+
}
|
|
594
|
+
|
|
549
595
|
// Update preview with initial content
|
|
550
596
|
this.updatePreview();
|
|
551
597
|
}
|
|
552
598
|
|
|
599
|
+
_initFileUpload() {
|
|
600
|
+
const options = this.options.fileUpload;
|
|
601
|
+
if (!options || !options.enabled) return;
|
|
602
|
+
|
|
603
|
+
options.maxSize = options.maxSize || 10 * 1024 * 1024;
|
|
604
|
+
options.mimeTypes = options.mimeTypes || [];
|
|
605
|
+
options.batch = options.batch || false;
|
|
606
|
+
if (!options.onInsertFile || typeof options.onInsertFile !== 'function') {
|
|
607
|
+
console.warn('OverType: fileUpload.onInsertFile callback is required for file uploads.');
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this._fileUploadCounter = 0;
|
|
612
|
+
this._boundHandleFilePaste = this._handleFilePaste.bind(this);
|
|
613
|
+
this._boundHandleFileDrop = this._handleFileDrop.bind(this);
|
|
614
|
+
this._boundHandleDragOver = this._handleDragOver.bind(this);
|
|
615
|
+
|
|
616
|
+
this.textarea.addEventListener('paste', this._boundHandleFilePaste);
|
|
617
|
+
this.textarea.addEventListener('drop', this._boundHandleFileDrop);
|
|
618
|
+
this.textarea.addEventListener('dragover', this._boundHandleDragOver);
|
|
619
|
+
|
|
620
|
+
this.fileUploadInitialized = true;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
_handleFilePaste(e) {
|
|
624
|
+
if (!e?.clipboardData?.files?.length) return;
|
|
625
|
+
e.preventDefault();
|
|
626
|
+
this._handleDataTransfer(e.clipboardData);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
_handleFileDrop(e) {
|
|
630
|
+
if (!e?.dataTransfer?.files?.length) return;
|
|
631
|
+
e.preventDefault();
|
|
632
|
+
this._handleDataTransfer(e.dataTransfer);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
_handleDataTransfer(dataTransfer) {
|
|
636
|
+
const files = [];
|
|
637
|
+
for (const file of dataTransfer.files) {
|
|
638
|
+
if (file.size > this.options.fileUpload.maxSize) continue;
|
|
639
|
+
if (this.options.fileUpload.mimeTypes.length > 0
|
|
640
|
+
&& !this.options.fileUpload.mimeTypes.includes(file.type)) continue;
|
|
641
|
+
|
|
642
|
+
const id = ++this._fileUploadCounter;
|
|
643
|
+
const prefix = file.type.startsWith('image/') ? '!' : '';
|
|
644
|
+
const placeholder = `${prefix}[Uploading ${file.name} (#${id})...]()`;
|
|
645
|
+
this.insertAtCursor(`${placeholder}\n`);
|
|
646
|
+
|
|
647
|
+
if (this.options.fileUpload.batch) {
|
|
648
|
+
files.push({ file, placeholder });
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
this.options.fileUpload.onInsertFile(file).then((text) => {
|
|
653
|
+
this.textarea.value = this.textarea.value.replace(placeholder, text);
|
|
654
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
655
|
+
}, (error) => {
|
|
656
|
+
console.error('OverType: File upload failed', error);
|
|
657
|
+
this.textarea.value = this.textarea.value.replace(placeholder, '[Upload failed]()');
|
|
658
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (this.options.fileUpload.batch && files.length > 0) {
|
|
663
|
+
this.options.fileUpload.onInsertFile(files.map(f => f.file)).then((result) => {
|
|
664
|
+
const texts = Array.isArray(result) ? result : [result];
|
|
665
|
+
texts.forEach((text, index) => {
|
|
666
|
+
this.textarea.value = this.textarea.value.replace(files[index].placeholder, text);
|
|
667
|
+
});
|
|
668
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
669
|
+
}, (error) => {
|
|
670
|
+
console.error('OverType: File upload failed', error);
|
|
671
|
+
files.forEach(({ placeholder }) => {
|
|
672
|
+
this.textarea.value = this.textarea.value.replace(placeholder, '[Upload failed]()');
|
|
673
|
+
});
|
|
674
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
_handleDragOver(e) {
|
|
680
|
+
e.preventDefault();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
_destroyFileUpload() {
|
|
684
|
+
this.textarea.removeEventListener('paste', this._boundHandleFilePaste);
|
|
685
|
+
this.textarea.removeEventListener('drop', this._boundHandleFileDrop);
|
|
686
|
+
this.textarea.removeEventListener('dragover', this._boundHandleDragOver);
|
|
687
|
+
this._boundHandleFilePaste = null;
|
|
688
|
+
this._boundHandleFileDrop = null;
|
|
689
|
+
this._boundHandleDragOver = null;
|
|
690
|
+
this.fileUploadInitialized = false;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
insertAtCursor(text) {
|
|
694
|
+
const start = this.textarea.selectionStart;
|
|
695
|
+
const end = this.textarea.selectionEnd;
|
|
696
|
+
|
|
697
|
+
let inserted = false;
|
|
698
|
+
try {
|
|
699
|
+
inserted = document.execCommand('insertText', false, text);
|
|
700
|
+
} catch (_) {}
|
|
701
|
+
|
|
702
|
+
if (!inserted) {
|
|
703
|
+
const before = this.textarea.value.slice(0, start);
|
|
704
|
+
const after = this.textarea.value.slice(end);
|
|
705
|
+
this.textarea.value = before + text + after;
|
|
706
|
+
this.textarea.setSelectionRange(start + text.length, start + text.length);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
this.textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
710
|
+
}
|
|
711
|
+
|
|
553
712
|
/**
|
|
554
713
|
* Update preview with parsed markdown
|
|
555
714
|
*/
|
|
@@ -563,7 +722,12 @@ class OverType {
|
|
|
563
722
|
|
|
564
723
|
// Parse markdown
|
|
565
724
|
const html = MarkdownParser.parse(text, activeLine, this.options.showActiveLineRaw, this.options.codeHighlighter, isPreviewMode);
|
|
566
|
-
this.preview.innerHTML = html
|
|
725
|
+
this.preview.innerHTML = html;
|
|
726
|
+
|
|
727
|
+
// Show/hide placeholder shim
|
|
728
|
+
if (this.placeholderEl) {
|
|
729
|
+
this.placeholderEl.style.display = text ? 'none' : '';
|
|
730
|
+
}
|
|
567
731
|
|
|
568
732
|
// Apply code block backgrounds
|
|
569
733
|
this._applyCodeBlockBackgrounds();
|
|
@@ -1012,38 +1176,73 @@ class OverType {
|
|
|
1012
1176
|
this._createToolbar();
|
|
1013
1177
|
}
|
|
1014
1178
|
|
|
1179
|
+
if (this.fileUploadInitialized) {
|
|
1180
|
+
this._destroyFileUpload();
|
|
1181
|
+
}
|
|
1182
|
+
if (this.options.fileUpload) {
|
|
1183
|
+
this._initFileUpload();
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1015
1186
|
this._applyOptions();
|
|
1016
1187
|
this.updatePreview();
|
|
1017
1188
|
}
|
|
1018
1189
|
|
|
1190
|
+
showToolbar() {
|
|
1191
|
+
if (this.toolbar) {
|
|
1192
|
+
this.toolbar.show();
|
|
1193
|
+
} else {
|
|
1194
|
+
this._createToolbar();
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
hideToolbar() {
|
|
1199
|
+
if (this.toolbar) {
|
|
1200
|
+
this.toolbar.hide();
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1019
1204
|
/**
|
|
1020
1205
|
* Set theme for this instance
|
|
1021
1206
|
* @param {string|Object} theme - Theme name or custom theme object
|
|
1022
1207
|
* @returns {this} Returns this for chaining
|
|
1023
1208
|
*/
|
|
1024
1209
|
setTheme(theme) {
|
|
1025
|
-
|
|
1210
|
+
OverType._autoInstances.delete(this);
|
|
1026
1211
|
this.instanceTheme = theme;
|
|
1027
1212
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1213
|
+
if (theme === 'auto') {
|
|
1214
|
+
OverType._autoInstances.add(this);
|
|
1215
|
+
OverType._startAutoListener();
|
|
1216
|
+
this._applyResolvedTheme(resolveAutoTheme('auto'));
|
|
1217
|
+
} else {
|
|
1218
|
+
const themeObj = typeof theme === 'string' ? getTheme(theme) : theme;
|
|
1219
|
+
const themeName = typeof themeObj === 'string' ? themeObj : themeObj.name;
|
|
1031
1220
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1221
|
+
if (themeName) {
|
|
1222
|
+
this.container.setAttribute('data-theme', themeName);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (themeObj && themeObj.colors) {
|
|
1226
|
+
const cssVars = themeToCSSVars(themeObj.colors, themeObj.previewColors);
|
|
1227
|
+
this.container.style.cssText += cssVars;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
this.updatePreview();
|
|
1035
1231
|
}
|
|
1036
1232
|
|
|
1037
|
-
|
|
1233
|
+
OverType._stopAutoListener();
|
|
1234
|
+
return this;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
_applyResolvedTheme(themeName) {
|
|
1238
|
+
const themeObj = getTheme(themeName);
|
|
1239
|
+
this.container.setAttribute('data-theme', themeName);
|
|
1240
|
+
|
|
1038
1241
|
if (themeObj && themeObj.colors) {
|
|
1039
|
-
|
|
1040
|
-
this.container.style.cssText += cssVars;
|
|
1242
|
+
this.container.style.cssText = themeToCSSVars(themeObj.colors, themeObj.previewColors);
|
|
1041
1243
|
}
|
|
1042
1244
|
|
|
1043
|
-
// Update preview to reflect new theme
|
|
1044
1245
|
this.updatePreview();
|
|
1045
|
-
|
|
1046
|
-
return this;
|
|
1047
1246
|
}
|
|
1048
1247
|
|
|
1049
1248
|
/**
|
|
@@ -1251,6 +1450,13 @@ class OverType {
|
|
|
1251
1450
|
* Destroy the editor instance
|
|
1252
1451
|
*/
|
|
1253
1452
|
destroy() {
|
|
1453
|
+
OverType._autoInstances.delete(this);
|
|
1454
|
+
OverType._stopAutoListener();
|
|
1455
|
+
|
|
1456
|
+
if (this.fileUploadInitialized) {
|
|
1457
|
+
this._destroyFileUpload();
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1254
1460
|
// Remove instance reference
|
|
1255
1461
|
this.element.overTypeInstance = null;
|
|
1256
1462
|
OverType.instances.delete(this.element);
|
|
@@ -1307,7 +1513,7 @@ class OverType {
|
|
|
1307
1513
|
}
|
|
1308
1514
|
}
|
|
1309
1515
|
|
|
1310
|
-
return new OverType(el, options);
|
|
1516
|
+
return new OverType(el, options)[0];
|
|
1311
1517
|
});
|
|
1312
1518
|
}
|
|
1313
1519
|
|
|
@@ -1375,59 +1581,89 @@ class OverType {
|
|
|
1375
1581
|
* @param {Object} customColors - Optional color overrides
|
|
1376
1582
|
*/
|
|
1377
1583
|
static setTheme(theme, customColors = null) {
|
|
1378
|
-
|
|
1584
|
+
OverType._globalAutoTheme = false;
|
|
1585
|
+
OverType._globalAutoCustomColors = null;
|
|
1586
|
+
|
|
1587
|
+
if (theme === 'auto') {
|
|
1588
|
+
OverType._globalAutoTheme = true;
|
|
1589
|
+
OverType._globalAutoCustomColors = customColors;
|
|
1590
|
+
OverType._startAutoListener();
|
|
1591
|
+
OverType._applyGlobalTheme(resolveAutoTheme('auto'), customColors);
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
OverType._stopAutoListener();
|
|
1596
|
+
OverType._applyGlobalTheme(theme, customColors);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
static _applyGlobalTheme(theme, customColors = null) {
|
|
1379
1600
|
let themeObj = typeof theme === 'string' ? getTheme(theme) : theme;
|
|
1380
1601
|
|
|
1381
|
-
// Apply custom colors if provided
|
|
1382
1602
|
if (customColors) {
|
|
1383
1603
|
themeObj = mergeTheme(themeObj, customColors);
|
|
1384
1604
|
}
|
|
1385
1605
|
|
|
1386
|
-
// Store as current theme
|
|
1387
1606
|
OverType.currentTheme = themeObj;
|
|
1388
|
-
|
|
1389
|
-
// Re-inject styles with new theme
|
|
1390
1607
|
OverType.injectStyles(true);
|
|
1391
1608
|
|
|
1392
|
-
|
|
1609
|
+
const themeName = typeof themeObj === 'string' ? themeObj : themeObj.name;
|
|
1610
|
+
|
|
1393
1611
|
document.querySelectorAll('.overtype-container').forEach(container => {
|
|
1394
|
-
const themeName = typeof themeObj === 'string' ? themeObj : themeObj.name;
|
|
1395
1612
|
if (themeName) {
|
|
1396
1613
|
container.setAttribute('data-theme', themeName);
|
|
1397
1614
|
}
|
|
1398
1615
|
});
|
|
1399
1616
|
|
|
1400
|
-
// Also handle any old-style wrappers without containers
|
|
1401
1617
|
document.querySelectorAll('.overtype-wrapper').forEach(wrapper => {
|
|
1402
1618
|
if (!wrapper.closest('.overtype-container')) {
|
|
1403
|
-
const themeName = typeof themeObj === 'string' ? themeObj : themeObj.name;
|
|
1404
1619
|
if (themeName) {
|
|
1405
1620
|
wrapper.setAttribute('data-theme', themeName);
|
|
1406
1621
|
}
|
|
1407
1622
|
}
|
|
1408
1623
|
|
|
1409
|
-
// Trigger preview update for the instance
|
|
1410
1624
|
const instance = wrapper._instance;
|
|
1411
1625
|
if (instance) {
|
|
1412
1626
|
instance.updatePreview();
|
|
1413
1627
|
}
|
|
1414
1628
|
});
|
|
1415
1629
|
|
|
1416
|
-
// Update web components (shadow DOM instances)
|
|
1417
|
-
const themeName = typeof themeObj === 'string' ? themeObj : themeObj.name;
|
|
1418
1630
|
document.querySelectorAll('overtype-editor').forEach(webComponent => {
|
|
1419
|
-
// Set the theme attribute to update the theme name
|
|
1420
1631
|
if (themeName && typeof webComponent.setAttribute === 'function') {
|
|
1421
1632
|
webComponent.setAttribute('theme', themeName);
|
|
1422
1633
|
}
|
|
1423
|
-
// Also call refreshTheme() to handle cases where the theme name stays the same
|
|
1424
|
-
// but the theme object's properties have changed
|
|
1425
1634
|
if (typeof webComponent.refreshTheme === 'function') {
|
|
1426
1635
|
webComponent.refreshTheme();
|
|
1427
1636
|
}
|
|
1428
1637
|
});
|
|
1429
1638
|
}
|
|
1430
1639
|
|
|
1640
|
+
static _startAutoListener() {
|
|
1641
|
+
if (OverType._autoMediaQuery) return;
|
|
1642
|
+
if (!window.matchMedia) return;
|
|
1643
|
+
|
|
1644
|
+
OverType._autoMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
1645
|
+
OverType._autoMediaListener = (e) => {
|
|
1646
|
+
const resolved = e.matches ? 'cave' : 'solar';
|
|
1647
|
+
|
|
1648
|
+
if (OverType._globalAutoTheme) {
|
|
1649
|
+
OverType._applyGlobalTheme(resolved, OverType._globalAutoCustomColors);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
OverType._autoInstances.forEach(inst => inst._applyResolvedTheme(resolved));
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
OverType._autoMediaQuery.addEventListener('change', OverType._autoMediaListener);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
static _stopAutoListener() {
|
|
1659
|
+
if (OverType._autoInstances.size > 0 || OverType._globalAutoTheme) return;
|
|
1660
|
+
if (!OverType._autoMediaQuery) return;
|
|
1661
|
+
|
|
1662
|
+
OverType._autoMediaQuery.removeEventListener('change', OverType._autoMediaListener);
|
|
1663
|
+
OverType._autoMediaQuery = null;
|
|
1664
|
+
OverType._autoMediaListener = null;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1431
1667
|
/**
|
|
1432
1668
|
* Set global code highlighter for all OverType instances
|
|
1433
1669
|
* @param {Function|null} highlighter - Function that takes (code, language) and returns highlighted HTML
|
package/src/parser.js
CHANGED
|
@@ -87,6 +87,7 @@ export class MarkdownParser {
|
|
|
87
87
|
static parseHeader(html) {
|
|
88
88
|
return html.replace(/^(#{1,3})\s(.+)$/, (match, hashes, content) => {
|
|
89
89
|
const level = hashes.length;
|
|
90
|
+
content = this.parseInlineElements(content);
|
|
90
91
|
return `<h${level}><span class="syntax-marker">${hashes} </span>${content}</h${level}>`;
|
|
91
92
|
});
|
|
92
93
|
}
|
|
@@ -121,6 +122,7 @@ export class MarkdownParser {
|
|
|
121
122
|
*/
|
|
122
123
|
static parseBulletList(html) {
|
|
123
124
|
return html.replace(/^((?: )*)([-*+])\s(.+)$/, (match, indent, marker, content) => {
|
|
125
|
+
content = this.parseInlineElements(content);
|
|
124
126
|
return `${indent}<li class="bullet-list"><span class="syntax-marker">${marker} </span>${content}</li>`;
|
|
125
127
|
});
|
|
126
128
|
}
|
|
@@ -133,6 +135,7 @@ export class MarkdownParser {
|
|
|
133
135
|
*/
|
|
134
136
|
static parseTaskList(html, isPreviewMode = false) {
|
|
135
137
|
return html.replace(/^((?: )*)-\s+\[([ xX])\]\s+(.+)$/, (match, indent, checked, content) => {
|
|
138
|
+
content = this.parseInlineElements(content);
|
|
136
139
|
if (isPreviewMode) {
|
|
137
140
|
// Preview mode: render actual checkbox
|
|
138
141
|
const isChecked = checked.toLowerCase() === 'x';
|
|
@@ -151,6 +154,7 @@ export class MarkdownParser {
|
|
|
151
154
|
*/
|
|
152
155
|
static parseNumberedList(html) {
|
|
153
156
|
return html.replace(/^((?: )*)(\d+\.)\s(.+)$/, (match, indent, marker, content) => {
|
|
157
|
+
content = this.parseInlineElements(content);
|
|
154
158
|
return `${indent}<li class="ordered-list"><span class="syntax-marker">${marker} </span>${content}</li>`;
|
|
155
159
|
});
|
|
156
160
|
}
|
|
@@ -188,7 +192,7 @@ export class MarkdownParser {
|
|
|
188
192
|
*/
|
|
189
193
|
static parseItalic(html) {
|
|
190
194
|
// Single asterisk - must not be adjacent to other asterisks
|
|
191
|
-
//
|
|
195
|
+
// Must not be inside a syntax-marker span (avoid matching bullet list markers like ">* ")
|
|
192
196
|
html = html.replace(/(?<![\*>])\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
|
|
193
197
|
|
|
194
198
|
// Single underscore - must be at word boundaries to avoid matching inside words
|
|
@@ -464,8 +468,10 @@ export class MarkdownParser {
|
|
|
464
468
|
html = this.parseBulletList(html);
|
|
465
469
|
html = this.parseNumberedList(html);
|
|
466
470
|
|
|
467
|
-
// Parse inline elements
|
|
468
|
-
html
|
|
471
|
+
// Parse inline elements (skip for headers and list items — already parsed inside those functions)
|
|
472
|
+
if (!html.includes('<li') && !html.includes('<h')) {
|
|
473
|
+
html = this.parseInlineElements(html);
|
|
474
|
+
}
|
|
469
475
|
|
|
470
476
|
// Wrap in div to maintain line structure
|
|
471
477
|
if (html.trim() === '') {
|