overtype 1.0.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.
@@ -0,0 +1,2599 @@
1
+ /**
2
+ * OverType v1.0.0
3
+ * A lightweight markdown editor library with perfect WYSIWYG alignment
4
+ * @license MIT
5
+ * @author Demo User
6
+ * https://github.com/demo/overtype
7
+ */
8
+ var OverType = (() => {
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var __publicField = (obj, key, value) => {
28
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
29
+ return value;
30
+ };
31
+
32
+ // src/overtype.js
33
+ var overtype_exports = {};
34
+ __export(overtype_exports, {
35
+ OverType: () => OverType,
36
+ default: () => overtype_default
37
+ });
38
+
39
+ // src/parser.js
40
+ var MarkdownParser = class {
41
+ /**
42
+ * Escape HTML special characters
43
+ * @param {string} text - Raw text to escape
44
+ * @returns {string} Escaped HTML-safe text
45
+ */
46
+ static escapeHtml(text) {
47
+ const map = {
48
+ "&": "&",
49
+ "<": "&lt;",
50
+ ">": "&gt;",
51
+ '"': "&quot;",
52
+ "'": "&#39;"
53
+ };
54
+ return text.replace(/[&<>"']/g, (m) => map[m]);
55
+ }
56
+ /**
57
+ * Preserve leading spaces as non-breaking spaces
58
+ * @param {string} html - HTML string
59
+ * @param {string} originalLine - Original line with spaces
60
+ * @returns {string} HTML with preserved indentation
61
+ */
62
+ static preserveIndentation(html, originalLine) {
63
+ const leadingSpaces = originalLine.match(/^(\s*)/)[1];
64
+ const indentation = leadingSpaces.replace(/ /g, "&nbsp;");
65
+ return html.replace(/^\s*/, indentation);
66
+ }
67
+ /**
68
+ * Parse headers (h1-h3 only)
69
+ * @param {string} html - HTML line to parse
70
+ * @returns {string} Parsed HTML with header styling
71
+ */
72
+ static parseHeader(html) {
73
+ return html.replace(/^(#{1,3})\s(.+)$/, (match, hashes, content) => {
74
+ const level = hashes.length;
75
+ const levelClasses = ["h1", "h2", "h3"];
76
+ return `<span class="header ${levelClasses[level - 1]}"><span class="syntax-marker">${hashes}</span> ${content}</span>`;
77
+ });
78
+ }
79
+ /**
80
+ * Parse horizontal rules
81
+ * @param {string} html - HTML line to parse
82
+ * @returns {string|null} Parsed horizontal rule or null
83
+ */
84
+ static parseHorizontalRule(html) {
85
+ if (html.match(/^(-{3,}|\*{3,}|_{3,})$/)) {
86
+ return `<div><span class="hr-marker">${html}</span></div>`;
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Parse blockquotes
92
+ * @param {string} html - HTML line to parse
93
+ * @returns {string} Parsed blockquote
94
+ */
95
+ static parseBlockquote(html) {
96
+ return html.replace(/^&gt; (.+)$/, (match, content) => {
97
+ return `<span class="blockquote"><span class="syntax-marker">&gt;</span> ${content}</span>`;
98
+ });
99
+ }
100
+ /**
101
+ * Parse bullet lists
102
+ * @param {string} html - HTML line to parse
103
+ * @returns {string} Parsed bullet list item
104
+ */
105
+ static parseBulletList(html) {
106
+ return html.replace(/^((?:&nbsp;)*)([-*])\s(.+)$/, (match, indent, marker, content) => {
107
+ return `${indent}<span class="syntax-marker">${marker}</span> ${content}`;
108
+ });
109
+ }
110
+ /**
111
+ * Parse numbered lists
112
+ * @param {string} html - HTML line to parse
113
+ * @returns {string} Parsed numbered list item
114
+ */
115
+ static parseNumberedList(html) {
116
+ return html.replace(/^((?:&nbsp;)*)(\d+\.)\s(.+)$/, (match, indent, marker, content) => {
117
+ return `${indent}<span class="syntax-marker">${marker}</span> ${content}`;
118
+ });
119
+ }
120
+ /**
121
+ * Parse code blocks (markers only)
122
+ * @param {string} html - HTML line to parse
123
+ * @returns {string|null} Parsed code fence or null
124
+ */
125
+ static parseCodeBlock(html) {
126
+ if (html.startsWith("```")) {
127
+ return `<div><span class="code-fence">${html}</span></div>`;
128
+ }
129
+ return null;
130
+ }
131
+ /**
132
+ * Parse bold text
133
+ * @param {string} html - HTML with potential bold markdown
134
+ * @returns {string} HTML with bold styling
135
+ */
136
+ static parseBold(html) {
137
+ html = html.replace(/\*\*(.+?)\*\*/g, '<strong><span class="syntax-marker">**</span>$1<span class="syntax-marker">**</span></strong>');
138
+ html = html.replace(/__(.+?)__/g, '<strong><span class="syntax-marker">__</span>$1<span class="syntax-marker">__</span></strong>');
139
+ return html;
140
+ }
141
+ /**
142
+ * Parse italic text
143
+ * Note: Uses lookbehind assertions - requires modern browsers
144
+ * @param {string} html - HTML with potential italic markdown
145
+ * @returns {string} HTML with italic styling
146
+ */
147
+ static parseItalic(html) {
148
+ html = html.replace(new RegExp("(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)", "g"), '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
149
+ html = html.replace(new RegExp("(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
150
+ return html;
151
+ }
152
+ /**
153
+ * Parse inline code
154
+ * @param {string} html - HTML with potential code markdown
155
+ * @returns {string} HTML with code styling
156
+ */
157
+ static parseInlineCode(html) {
158
+ return html.replace(/`(.+?)`/g, '<code><span class="syntax-marker">`</span>$1<span class="syntax-marker">`</span></code>');
159
+ }
160
+ /**
161
+ * Parse links
162
+ * @param {string} html - HTML with potential link markdown
163
+ * @returns {string} HTML with link styling
164
+ */
165
+ static parseLinks(html) {
166
+ return html.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2"><span class="syntax-marker">[</span>$1<span class="syntax-marker">](</span><span class="syntax-marker">$2</span><span class="syntax-marker">)</span></a>');
167
+ }
168
+ /**
169
+ * Parse all inline elements in correct order
170
+ * @param {string} text - Text with potential inline markdown
171
+ * @returns {string} HTML with all inline styling
172
+ */
173
+ static parseInlineElements(text) {
174
+ let html = text;
175
+ html = this.parseInlineCode(html);
176
+ html = this.parseLinks(html);
177
+ html = this.parseBold(html);
178
+ html = this.parseItalic(html);
179
+ return html;
180
+ }
181
+ /**
182
+ * Parse a single line of markdown
183
+ * @param {string} line - Raw markdown line
184
+ * @returns {string} Parsed HTML line
185
+ */
186
+ static parseLine(line) {
187
+ let html = this.escapeHtml(line);
188
+ html = this.preserveIndentation(html, line);
189
+ const horizontalRule = this.parseHorizontalRule(html);
190
+ if (horizontalRule)
191
+ return horizontalRule;
192
+ const codeBlock = this.parseCodeBlock(html);
193
+ if (codeBlock)
194
+ return codeBlock;
195
+ html = this.parseHeader(html);
196
+ html = this.parseBlockquote(html);
197
+ html = this.parseBulletList(html);
198
+ html = this.parseNumberedList(html);
199
+ html = this.parseInlineElements(html);
200
+ if (html.trim() === "") {
201
+ return "<div>&nbsp;</div>";
202
+ }
203
+ return `<div>${html}</div>`;
204
+ }
205
+ /**
206
+ * Parse full markdown text
207
+ * @param {string} text - Full markdown text
208
+ * @param {number} activeLine - Currently active line index (optional)
209
+ * @param {boolean} showActiveLineRaw - Show raw markdown on active line
210
+ * @returns {string} Parsed HTML
211
+ */
212
+ static parse(text, activeLine = -1, showActiveLineRaw = false) {
213
+ const lines = text.split("\n");
214
+ const parsedLines = lines.map((line, index) => {
215
+ if (showActiveLineRaw && index === activeLine) {
216
+ const content = this.escapeHtml(line) || "&nbsp;";
217
+ return `<div class="raw-line">${content}</div>`;
218
+ }
219
+ return this.parseLine(line);
220
+ });
221
+ return parsedLines.join("");
222
+ }
223
+ };
224
+
225
+ // node_modules/markdown-actions/dist/markdown-actions.esm.js
226
+ var __defProp2 = Object.defineProperty;
227
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
228
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
229
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
230
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
231
+ var __spreadValues = (a, b) => {
232
+ for (var prop in b || (b = {}))
233
+ if (__hasOwnProp2.call(b, prop))
234
+ __defNormalProp2(a, prop, b[prop]);
235
+ if (__getOwnPropSymbols)
236
+ for (var prop of __getOwnPropSymbols(b)) {
237
+ if (__propIsEnum.call(b, prop))
238
+ __defNormalProp2(a, prop, b[prop]);
239
+ }
240
+ return a;
241
+ };
242
+ var FORMATS = {
243
+ bold: {
244
+ prefix: "**",
245
+ suffix: "**",
246
+ trimFirst: true
247
+ },
248
+ italic: {
249
+ prefix: "_",
250
+ suffix: "_",
251
+ trimFirst: true
252
+ },
253
+ code: {
254
+ prefix: "`",
255
+ suffix: "`",
256
+ blockPrefix: "```",
257
+ blockSuffix: "```"
258
+ },
259
+ link: {
260
+ prefix: "[",
261
+ suffix: "](url)",
262
+ replaceNext: "url",
263
+ scanFor: "https?://"
264
+ },
265
+ bulletList: {
266
+ prefix: "- ",
267
+ multiline: true,
268
+ unorderedList: true
269
+ },
270
+ numberedList: {
271
+ prefix: "1. ",
272
+ multiline: true,
273
+ orderedList: true
274
+ },
275
+ quote: {
276
+ prefix: "> ",
277
+ multiline: true,
278
+ surroundWithNewlines: true
279
+ },
280
+ taskList: {
281
+ prefix: "- [ ] ",
282
+ multiline: true,
283
+ surroundWithNewlines: true
284
+ },
285
+ header1: { prefix: "# " },
286
+ header2: { prefix: "## " },
287
+ header3: { prefix: "### " },
288
+ header4: { prefix: "#### " },
289
+ header5: { prefix: "##### " },
290
+ header6: { prefix: "###### " }
291
+ };
292
+ function getDefaultStyle() {
293
+ return {
294
+ prefix: "",
295
+ suffix: "",
296
+ blockPrefix: "",
297
+ blockSuffix: "",
298
+ multiline: false,
299
+ replaceNext: "",
300
+ prefixSpace: false,
301
+ scanFor: "",
302
+ surroundWithNewlines: false,
303
+ orderedList: false,
304
+ unorderedList: false,
305
+ trimFirst: false
306
+ };
307
+ }
308
+ function mergeWithDefaults(format) {
309
+ return __spreadValues(__spreadValues({}, getDefaultStyle()), format);
310
+ }
311
+ var debugMode = false;
312
+ function getDebugMode() {
313
+ return debugMode;
314
+ }
315
+ function debugLog(funcName, message, data) {
316
+ if (!debugMode)
317
+ return;
318
+ console.group(`\u{1F50D} ${funcName}`);
319
+ console.log(message);
320
+ if (data) {
321
+ console.log("Data:", data);
322
+ }
323
+ console.groupEnd();
324
+ }
325
+ function debugSelection(textarea, label) {
326
+ if (!debugMode)
327
+ return;
328
+ const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
329
+ console.group(`\u{1F4CD} Selection: ${label}`);
330
+ console.log("Position:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
331
+ console.log("Selected text:", JSON.stringify(selected));
332
+ console.log("Length:", selected.length);
333
+ const before = textarea.value.slice(Math.max(0, textarea.selectionStart - 10), textarea.selectionStart);
334
+ const after = textarea.value.slice(textarea.selectionEnd, Math.min(textarea.value.length, textarea.selectionEnd + 10));
335
+ console.log("Context:", JSON.stringify(before) + "[SELECTION]" + JSON.stringify(after));
336
+ console.groupEnd();
337
+ }
338
+ function debugResult(result) {
339
+ if (!debugMode)
340
+ return;
341
+ console.group("\u{1F4DD} Result");
342
+ console.log("Text to insert:", JSON.stringify(result.text));
343
+ console.log("New selection:", `${result.selectionStart}-${result.selectionEnd}`);
344
+ console.groupEnd();
345
+ }
346
+ var canInsertText = null;
347
+ function insertText(textarea, { text, selectionStart, selectionEnd }) {
348
+ const debugMode2 = getDebugMode();
349
+ if (debugMode2) {
350
+ console.group("\u{1F527} insertText");
351
+ console.log("Current selection:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
352
+ console.log("Text to insert:", JSON.stringify(text));
353
+ console.log("New selection to set:", selectionStart, "-", selectionEnd);
354
+ }
355
+ textarea.focus();
356
+ const originalSelectionStart = textarea.selectionStart;
357
+ const originalSelectionEnd = textarea.selectionEnd;
358
+ const before = textarea.value.slice(0, originalSelectionStart);
359
+ const after = textarea.value.slice(originalSelectionEnd);
360
+ if (debugMode2) {
361
+ console.log("Before text (last 20):", JSON.stringify(before.slice(-20)));
362
+ console.log("After text (first 20):", JSON.stringify(after.slice(0, 20)));
363
+ console.log("Selected text being replaced:", JSON.stringify(textarea.value.slice(originalSelectionStart, originalSelectionEnd)));
364
+ }
365
+ const originalValue = textarea.value;
366
+ const hasSelection = originalSelectionStart !== originalSelectionEnd;
367
+ if (!hasSelection && (canInsertText === null || canInsertText === true)) {
368
+ textarea.contentEditable = "true";
369
+ try {
370
+ canInsertText = document.execCommand("insertText", false, text);
371
+ } catch (error) {
372
+ canInsertText = false;
373
+ }
374
+ textarea.contentEditable = "false";
375
+ } else if (hasSelection) {
376
+ if (debugMode2)
377
+ console.log("Has selection, skipping execCommand and using manual mode");
378
+ canInsertText = false;
379
+ }
380
+ if (debugMode2) {
381
+ console.log("canInsertText before:", canInsertText);
382
+ console.log("execCommand result:", canInsertText);
383
+ }
384
+ if (canInsertText) {
385
+ const expectedValue = before + text + after;
386
+ const actualValue = textarea.value;
387
+ if (debugMode2) {
388
+ console.log("Expected length:", expectedValue.length);
389
+ console.log("Actual length:", actualValue.length);
390
+ }
391
+ if (actualValue !== expectedValue) {
392
+ if (debugMode2) {
393
+ console.log("execCommand changed the value but not as expected");
394
+ console.log("Expected:", JSON.stringify(expectedValue.slice(0, 100)));
395
+ console.log("Actual:", JSON.stringify(actualValue.slice(0, 100)));
396
+ }
397
+ }
398
+ }
399
+ if (!canInsertText) {
400
+ if (debugMode2)
401
+ console.log("Using manual insertion");
402
+ if (textarea.value === originalValue) {
403
+ if (debugMode2)
404
+ console.log("Value unchanged, doing manual replacement");
405
+ try {
406
+ document.execCommand("ms-beginUndoUnit");
407
+ } catch (e) {
408
+ }
409
+ textarea.value = before + text + after;
410
+ try {
411
+ document.execCommand("ms-endUndoUnit");
412
+ } catch (e) {
413
+ }
414
+ textarea.dispatchEvent(new CustomEvent("input", { bubbles: true, cancelable: true }));
415
+ } else {
416
+ if (debugMode2)
417
+ console.log("Value was changed by execCommand, skipping manual insertion");
418
+ }
419
+ }
420
+ if (debugMode2)
421
+ console.log("Setting selection range:", selectionStart, selectionEnd);
422
+ if (selectionStart != null && selectionEnd != null) {
423
+ textarea.setSelectionRange(selectionStart, selectionEnd);
424
+ } else {
425
+ textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd);
426
+ }
427
+ if (debugMode2) {
428
+ console.log("Final value length:", textarea.value.length);
429
+ console.groupEnd();
430
+ }
431
+ }
432
+ function isMultipleLines(string) {
433
+ return string.trim().split("\n").length > 1;
434
+ }
435
+ function wordSelectionStart(text, i) {
436
+ let index = i;
437
+ while (text[index] && text[index - 1] != null && !text[index - 1].match(/\s/)) {
438
+ index--;
439
+ }
440
+ return index;
441
+ }
442
+ function wordSelectionEnd(text, i, multiline) {
443
+ let index = i;
444
+ const breakpoint = multiline ? /\n/ : /\s/;
445
+ while (text[index] && !text[index].match(breakpoint)) {
446
+ index++;
447
+ }
448
+ return index;
449
+ }
450
+ function expandSelectionToLine(textarea) {
451
+ const lines = textarea.value.split("\n");
452
+ let counter = 0;
453
+ for (let index = 0; index < lines.length; index++) {
454
+ const lineLength = lines[index].length + 1;
455
+ if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
456
+ textarea.selectionStart = counter;
457
+ }
458
+ if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
459
+ if (index === lines.length - 1) {
460
+ textarea.selectionEnd = Math.min(counter + lines[index].length, textarea.value.length);
461
+ } else {
462
+ textarea.selectionEnd = counter + lineLength - 1;
463
+ }
464
+ }
465
+ counter += lineLength;
466
+ }
467
+ }
468
+ function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {
469
+ if (textarea.selectionStart === textarea.selectionEnd) {
470
+ textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
471
+ textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline);
472
+ } else {
473
+ const expandedSelectionStart = textarea.selectionStart - prefixToUse.length;
474
+ const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length;
475
+ const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse;
476
+ const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse;
477
+ if (beginsWithPrefix && endsWithSuffix) {
478
+ textarea.selectionStart = expandedSelectionStart;
479
+ textarea.selectionEnd = expandedSelectionEnd;
480
+ }
481
+ }
482
+ return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
483
+ }
484
+ function newlinesToSurroundSelectedText(textarea) {
485
+ const beforeSelection = textarea.value.slice(0, textarea.selectionStart);
486
+ const afterSelection = textarea.value.slice(textarea.selectionEnd);
487
+ const breaksBefore = beforeSelection.match(/\n*$/);
488
+ const breaksAfter = afterSelection.match(/^\n*/);
489
+ const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0;
490
+ const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0;
491
+ let newlinesToAppend = "";
492
+ let newlinesToPrepend = "";
493
+ if (beforeSelection.match(/\S/) && newlinesBeforeSelection < 2) {
494
+ newlinesToAppend = "\n".repeat(2 - newlinesBeforeSelection);
495
+ }
496
+ if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
497
+ newlinesToPrepend = "\n".repeat(2 - newlinesAfterSelection);
498
+ }
499
+ return { newlinesToAppend, newlinesToPrepend };
500
+ }
501
+ function applyLineOperation(textarea, operation, options = {}) {
502
+ const originalStart = textarea.selectionStart;
503
+ const originalEnd = textarea.selectionEnd;
504
+ const noInitialSelection = originalStart === originalEnd;
505
+ const value = textarea.value;
506
+ let lineStart = originalStart;
507
+ while (lineStart > 0 && value[lineStart - 1] !== "\n") {
508
+ lineStart--;
509
+ }
510
+ if (noInitialSelection) {
511
+ let lineEnd = originalStart;
512
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
513
+ lineEnd++;
514
+ }
515
+ textarea.selectionStart = lineStart;
516
+ textarea.selectionEnd = lineEnd;
517
+ } else {
518
+ expandSelectionToLine(textarea);
519
+ }
520
+ const result = operation(textarea);
521
+ if (options.adjustSelection) {
522
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
523
+ const isRemoving = selectedText.startsWith(options.prefix);
524
+ const adjusted = options.adjustSelection(isRemoving, originalStart, originalEnd, lineStart);
525
+ result.selectionStart = adjusted.start;
526
+ result.selectionEnd = adjusted.end;
527
+ } else if (options.prefix) {
528
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
529
+ const isRemoving = selectedText.startsWith(options.prefix);
530
+ if (noInitialSelection) {
531
+ if (isRemoving) {
532
+ result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
533
+ result.selectionEnd = result.selectionStart;
534
+ } else {
535
+ result.selectionStart = originalStart + options.prefix.length;
536
+ result.selectionEnd = result.selectionStart;
537
+ }
538
+ } else {
539
+ if (isRemoving) {
540
+ result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
541
+ result.selectionEnd = Math.max(originalEnd - options.prefix.length, lineStart);
542
+ } else {
543
+ result.selectionStart = originalStart + options.prefix.length;
544
+ result.selectionEnd = originalEnd + options.prefix.length;
545
+ }
546
+ }
547
+ }
548
+ return result;
549
+ }
550
+ function blockStyle(textarea, style) {
551
+ let newlinesToAppend;
552
+ let newlinesToPrepend;
553
+ const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines, trimFirst } = style;
554
+ const originalSelectionStart = textarea.selectionStart;
555
+ const originalSelectionEnd = textarea.selectionEnd;
556
+ let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
557
+ let prefixToUse = isMultipleLines(selectedText) && blockPrefix && blockPrefix.length > 0 ? `${blockPrefix}
558
+ ` : prefix;
559
+ let suffixToUse = isMultipleLines(selectedText) && blockSuffix && blockSuffix.length > 0 ? `
560
+ ${blockSuffix}` : suffix;
561
+ if (prefixSpace) {
562
+ const beforeSelection = textarea.value[textarea.selectionStart - 1];
563
+ if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\s/)) {
564
+ prefixToUse = ` ${prefixToUse}`;
565
+ }
566
+ }
567
+ selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, style.multiline);
568
+ let selectionStart = textarea.selectionStart;
569
+ let selectionEnd = textarea.selectionEnd;
570
+ const hasReplaceNext = replaceNext && replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0;
571
+ if (surroundWithNewlines) {
572
+ const ref = newlinesToSurroundSelectedText(textarea);
573
+ newlinesToAppend = ref.newlinesToAppend;
574
+ newlinesToPrepend = ref.newlinesToPrepend;
575
+ prefixToUse = newlinesToAppend + prefix;
576
+ suffixToUse += newlinesToPrepend;
577
+ }
578
+ if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {
579
+ const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length);
580
+ if (originalSelectionStart === originalSelectionEnd) {
581
+ let position = originalSelectionStart - prefixToUse.length;
582
+ position = Math.max(position, selectionStart);
583
+ position = Math.min(position, selectionStart + replacementText.length);
584
+ selectionStart = selectionEnd = position;
585
+ } else {
586
+ selectionEnd = selectionStart + replacementText.length;
587
+ }
588
+ return { text: replacementText, selectionStart, selectionEnd };
589
+ } else if (!hasReplaceNext) {
590
+ let replacementText = prefixToUse + selectedText + suffixToUse;
591
+ selectionStart = originalSelectionStart + prefixToUse.length;
592
+ selectionEnd = originalSelectionEnd + prefixToUse.length;
593
+ const whitespaceEdges = selectedText.match(/^\s*|\s*$/g);
594
+ if (trimFirst && whitespaceEdges) {
595
+ const leadingWhitespace = whitespaceEdges[0] || "";
596
+ const trailingWhitespace = whitespaceEdges[1] || "";
597
+ replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace;
598
+ selectionStart += leadingWhitespace.length;
599
+ selectionEnd -= trailingWhitespace.length;
600
+ }
601
+ return { text: replacementText, selectionStart, selectionEnd };
602
+ } else if (scanFor && scanFor.length > 0 && selectedText.match(scanFor)) {
603
+ suffixToUse = suffixToUse.replace(replaceNext, selectedText);
604
+ const replacementText = prefixToUse + suffixToUse;
605
+ selectionStart = selectionEnd = selectionStart + prefixToUse.length;
606
+ return { text: replacementText, selectionStart, selectionEnd };
607
+ } else {
608
+ const replacementText = prefixToUse + selectedText + suffixToUse;
609
+ selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext);
610
+ selectionEnd = selectionStart + replaceNext.length;
611
+ return { text: replacementText, selectionStart, selectionEnd };
612
+ }
613
+ }
614
+ function multilineStyle(textarea, style) {
615
+ const { prefix, suffix, surroundWithNewlines } = style;
616
+ let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
617
+ let selectionStart = textarea.selectionStart;
618
+ let selectionEnd = textarea.selectionEnd;
619
+ const lines = text.split("\n");
620
+ const undoStyle = lines.every((line) => line.startsWith(prefix) && (!suffix || line.endsWith(suffix)));
621
+ if (undoStyle) {
622
+ text = lines.map((line) => {
623
+ let result = line.slice(prefix.length);
624
+ if (suffix) {
625
+ result = result.slice(0, result.length - suffix.length);
626
+ }
627
+ return result;
628
+ }).join("\n");
629
+ selectionEnd = selectionStart + text.length;
630
+ } else {
631
+ text = lines.map((line) => prefix + line + (suffix || "")).join("\n");
632
+ if (surroundWithNewlines) {
633
+ const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
634
+ selectionStart += newlinesToAppend.length;
635
+ selectionEnd = selectionStart + text.length;
636
+ text = newlinesToAppend + text + newlinesToPrepend;
637
+ }
638
+ }
639
+ return { text, selectionStart, selectionEnd };
640
+ }
641
+ function undoOrderedListStyle(text) {
642
+ const lines = text.split("\n");
643
+ const orderedListRegex = /^\d+\.\s+/;
644
+ const shouldUndoOrderedList = lines.every((line) => orderedListRegex.test(line));
645
+ let result = lines;
646
+ if (shouldUndoOrderedList) {
647
+ result = lines.map((line) => line.replace(orderedListRegex, ""));
648
+ }
649
+ return {
650
+ text: result.join("\n"),
651
+ processed: shouldUndoOrderedList
652
+ };
653
+ }
654
+ function undoUnorderedListStyle(text) {
655
+ const lines = text.split("\n");
656
+ const unorderedListPrefix = "- ";
657
+ const shouldUndoUnorderedList = lines.every((line) => line.startsWith(unorderedListPrefix));
658
+ let result = lines;
659
+ if (shouldUndoUnorderedList) {
660
+ result = lines.map((line) => line.slice(unorderedListPrefix.length));
661
+ }
662
+ return {
663
+ text: result.join("\n"),
664
+ processed: shouldUndoUnorderedList
665
+ };
666
+ }
667
+ function makePrefix(index, unorderedList) {
668
+ if (unorderedList) {
669
+ return "- ";
670
+ } else {
671
+ return `${index + 1}. `;
672
+ }
673
+ }
674
+ function clearExistingListStyle(style, selectedText) {
675
+ let undoResult;
676
+ let undoResultOppositeList;
677
+ let pristineText;
678
+ if (style.orderedList) {
679
+ undoResult = undoOrderedListStyle(selectedText);
680
+ undoResultOppositeList = undoUnorderedListStyle(undoResult.text);
681
+ pristineText = undoResultOppositeList.text;
682
+ } else {
683
+ undoResult = undoUnorderedListStyle(selectedText);
684
+ undoResultOppositeList = undoOrderedListStyle(undoResult.text);
685
+ pristineText = undoResultOppositeList.text;
686
+ }
687
+ return [undoResult, undoResultOppositeList, pristineText];
688
+ }
689
+ function listStyle(textarea, style) {
690
+ const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
691
+ let selectionStart = textarea.selectionStart;
692
+ let selectionEnd = textarea.selectionEnd;
693
+ expandSelectionToLine(textarea);
694
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
695
+ const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
696
+ const prefixedLines = pristineText.split("\n").map((value, index) => {
697
+ return `${makePrefix(index, style.unorderedList)}${value}`;
698
+ });
699
+ const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
700
+ return previousValue + makePrefix(currentIndex, style.unorderedList).length;
701
+ }, 0);
702
+ const totalPrefixLengthOppositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
703
+ return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
704
+ }, 0);
705
+ if (undoResult.processed) {
706
+ if (noInitialSelection) {
707
+ selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
708
+ selectionEnd = selectionStart;
709
+ } else {
710
+ selectionStart = textarea.selectionStart;
711
+ selectionEnd = textarea.selectionEnd - totalPrefixLength;
712
+ }
713
+ return { text: pristineText, selectionStart, selectionEnd };
714
+ }
715
+ const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
716
+ const text = newlinesToAppend + prefixedLines.join("\n") + newlinesToPrepend;
717
+ if (noInitialSelection) {
718
+ selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
719
+ selectionEnd = selectionStart;
720
+ } else {
721
+ if (undoResultOppositeList.processed) {
722
+ selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
723
+ selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOppositeList;
724
+ } else {
725
+ selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
726
+ selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
727
+ }
728
+ }
729
+ return { text, selectionStart, selectionEnd };
730
+ }
731
+ function applyListStyle(textarea, style) {
732
+ const result = applyLineOperation(
733
+ textarea,
734
+ (ta) => listStyle(ta, style),
735
+ {
736
+ // Custom selection adjustment for lists
737
+ adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
738
+ const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
739
+ const orderedListRegex = /^\d+\.\s+/;
740
+ const unorderedListRegex = /^- /;
741
+ const hasOrderedList = orderedListRegex.test(currentLine);
742
+ const hasUnorderedList = unorderedListRegex.test(currentLine);
743
+ const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
744
+ if (selStart === selEnd) {
745
+ if (isRemovingCurrent) {
746
+ const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
747
+ const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
748
+ return {
749
+ start: Math.max(selStart - prefixLength, lineStart),
750
+ end: Math.max(selStart - prefixLength, lineStart)
751
+ };
752
+ } else if (hasOrderedList || hasUnorderedList) {
753
+ const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
754
+ const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
755
+ const newPrefixLength = style.unorderedList ? 2 : 3;
756
+ const adjustment = newPrefixLength - oldPrefixLength;
757
+ return {
758
+ start: selStart + adjustment,
759
+ end: selStart + adjustment
760
+ };
761
+ } else {
762
+ const prefixLength = style.unorderedList ? 2 : 3;
763
+ return {
764
+ start: selStart + prefixLength,
765
+ end: selStart + prefixLength
766
+ };
767
+ }
768
+ } else {
769
+ if (isRemovingCurrent) {
770
+ const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
771
+ const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
772
+ return {
773
+ start: Math.max(selStart - prefixLength, lineStart),
774
+ end: Math.max(selEnd - prefixLength, lineStart)
775
+ };
776
+ } else if (hasOrderedList || hasUnorderedList) {
777
+ const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
778
+ const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
779
+ const newPrefixLength = style.unorderedList ? 2 : 3;
780
+ const adjustment = newPrefixLength - oldPrefixLength;
781
+ return {
782
+ start: selStart + adjustment,
783
+ end: selEnd + adjustment
784
+ };
785
+ } else {
786
+ const prefixLength = style.unorderedList ? 2 : 3;
787
+ return {
788
+ start: selStart + prefixLength,
789
+ end: selEnd + prefixLength
790
+ };
791
+ }
792
+ }
793
+ }
794
+ }
795
+ );
796
+ insertText(textarea, result);
797
+ }
798
+ function getActiveFormats(textarea) {
799
+ if (!textarea)
800
+ return [];
801
+ const formats = [];
802
+ const { selectionStart, selectionEnd, value } = textarea;
803
+ const lines = value.split("\n");
804
+ let lineStart = 0;
805
+ let currentLine = "";
806
+ for (const line of lines) {
807
+ if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {
808
+ currentLine = line;
809
+ break;
810
+ }
811
+ lineStart += line.length + 1;
812
+ }
813
+ if (currentLine.startsWith("- ")) {
814
+ if (currentLine.startsWith("- [ ] ") || currentLine.startsWith("- [x] ")) {
815
+ formats.push("task-list");
816
+ } else {
817
+ formats.push("bullet-list");
818
+ }
819
+ }
820
+ if (/^\d+\.\s/.test(currentLine)) {
821
+ formats.push("numbered-list");
822
+ }
823
+ if (currentLine.startsWith("> ")) {
824
+ formats.push("quote");
825
+ }
826
+ if (currentLine.startsWith("# "))
827
+ formats.push("header");
828
+ if (currentLine.startsWith("## "))
829
+ formats.push("header-2");
830
+ if (currentLine.startsWith("### "))
831
+ formats.push("header-3");
832
+ const lookBehind = Math.max(0, selectionStart - 10);
833
+ const lookAhead = Math.min(value.length, selectionEnd + 10);
834
+ const surrounding = value.slice(lookBehind, lookAhead);
835
+ if (surrounding.includes("**")) {
836
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
837
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
838
+ const lastOpenBold = beforeCursor.lastIndexOf("**");
839
+ const nextCloseBold = afterCursor.indexOf("**");
840
+ if (lastOpenBold !== -1 && nextCloseBold !== -1) {
841
+ formats.push("bold");
842
+ }
843
+ }
844
+ if (surrounding.includes("_")) {
845
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
846
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
847
+ const lastOpenItalic = beforeCursor.lastIndexOf("_");
848
+ const nextCloseItalic = afterCursor.indexOf("_");
849
+ if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {
850
+ formats.push("italic");
851
+ }
852
+ }
853
+ if (surrounding.includes("`")) {
854
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
855
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
856
+ if (beforeCursor.includes("`") && afterCursor.includes("`")) {
857
+ formats.push("code");
858
+ }
859
+ }
860
+ if (surrounding.includes("[") && surrounding.includes("]")) {
861
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
862
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
863
+ const lastOpenBracket = beforeCursor.lastIndexOf("[");
864
+ const nextCloseBracket = afterCursor.indexOf("]");
865
+ if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
866
+ const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
867
+ if (afterBracket.startsWith("(")) {
868
+ formats.push("link");
869
+ }
870
+ }
871
+ }
872
+ return formats;
873
+ }
874
+ function toggleBold(textarea) {
875
+ if (!textarea || textarea.disabled || textarea.readOnly)
876
+ return;
877
+ debugLog("toggleBold", "Starting");
878
+ debugSelection(textarea, "Before");
879
+ const style = mergeWithDefaults(FORMATS.bold);
880
+ const result = blockStyle(textarea, style);
881
+ debugResult(result);
882
+ insertText(textarea, result);
883
+ debugSelection(textarea, "After");
884
+ }
885
+ function toggleItalic(textarea) {
886
+ if (!textarea || textarea.disabled || textarea.readOnly)
887
+ return;
888
+ const style = mergeWithDefaults(FORMATS.italic);
889
+ const result = blockStyle(textarea, style);
890
+ insertText(textarea, result);
891
+ }
892
+ function toggleCode(textarea) {
893
+ if (!textarea || textarea.disabled || textarea.readOnly)
894
+ return;
895
+ const style = mergeWithDefaults(FORMATS.code);
896
+ const result = blockStyle(textarea, style);
897
+ insertText(textarea, result);
898
+ }
899
+ function insertLink(textarea, options = {}) {
900
+ if (!textarea || textarea.disabled || textarea.readOnly)
901
+ return;
902
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
903
+ let style = mergeWithDefaults(FORMATS.link);
904
+ const isURL = selectedText && selectedText.match(/^https?:\/\//);
905
+ if (isURL && !options.url) {
906
+ style.suffix = `](${selectedText})`;
907
+ style.replaceNext = "";
908
+ } else if (options.url) {
909
+ style.suffix = `](${options.url})`;
910
+ style.replaceNext = "";
911
+ }
912
+ if (options.text && !selectedText) {
913
+ const pos = textarea.selectionStart;
914
+ textarea.value = textarea.value.slice(0, pos) + options.text + textarea.value.slice(pos);
915
+ textarea.selectionStart = pos;
916
+ textarea.selectionEnd = pos + options.text.length;
917
+ }
918
+ const result = blockStyle(textarea, style);
919
+ insertText(textarea, result);
920
+ }
921
+ function toggleBulletList(textarea) {
922
+ if (!textarea || textarea.disabled || textarea.readOnly)
923
+ return;
924
+ const style = mergeWithDefaults(FORMATS.bulletList);
925
+ applyListStyle(textarea, style);
926
+ }
927
+ function toggleNumberedList(textarea) {
928
+ if (!textarea || textarea.disabled || textarea.readOnly)
929
+ return;
930
+ const style = mergeWithDefaults(FORMATS.numberedList);
931
+ applyListStyle(textarea, style);
932
+ }
933
+ function toggleQuote(textarea) {
934
+ if (!textarea || textarea.disabled || textarea.readOnly)
935
+ return;
936
+ debugLog("toggleQuote", "Starting");
937
+ debugSelection(textarea, "Initial");
938
+ const style = mergeWithDefaults(FORMATS.quote);
939
+ const result = applyLineOperation(
940
+ textarea,
941
+ (ta) => multilineStyle(ta, style),
942
+ { prefix: style.prefix }
943
+ );
944
+ debugResult(result);
945
+ insertText(textarea, result);
946
+ debugSelection(textarea, "Final");
947
+ }
948
+ function toggleTaskList(textarea) {
949
+ if (!textarea || textarea.disabled || textarea.readOnly)
950
+ return;
951
+ const style = mergeWithDefaults(FORMATS.taskList);
952
+ const result = applyLineOperation(
953
+ textarea,
954
+ (ta) => multilineStyle(ta, style),
955
+ { prefix: style.prefix }
956
+ );
957
+ insertText(textarea, result);
958
+ }
959
+ function insertHeader(textarea, level = 1, toggle = false) {
960
+ if (!textarea || textarea.disabled || textarea.readOnly)
961
+ return;
962
+ if (level < 1 || level > 6)
963
+ level = 1;
964
+ debugLog("insertHeader", `============ START ============`);
965
+ debugLog("insertHeader", `Level: ${level}, Toggle: ${toggle}`);
966
+ debugLog("insertHeader", `Initial cursor: ${textarea.selectionStart}-${textarea.selectionEnd}`);
967
+ const headerKey = `header${level === 1 ? "1" : level}`;
968
+ const style = mergeWithDefaults(FORMATS[headerKey] || FORMATS.header1);
969
+ debugLog("insertHeader", `Style prefix: "${style.prefix}"`);
970
+ const value = textarea.value;
971
+ const originalStart = textarea.selectionStart;
972
+ const originalEnd = textarea.selectionEnd;
973
+ let lineStart = originalStart;
974
+ while (lineStart > 0 && value[lineStart - 1] !== "\n") {
975
+ lineStart--;
976
+ }
977
+ let lineEnd = originalEnd;
978
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
979
+ lineEnd++;
980
+ }
981
+ const currentLineContent = value.slice(lineStart, lineEnd);
982
+ debugLog("insertHeader", `Current line (before): "${currentLineContent}"`);
983
+ const existingHeaderMatch = currentLineContent.match(/^(#{1,6})\s*/);
984
+ const existingLevel = existingHeaderMatch ? existingHeaderMatch[1].length : 0;
985
+ const existingPrefixLength = existingHeaderMatch ? existingHeaderMatch[0].length : 0;
986
+ debugLog("insertHeader", `Existing header check:`);
987
+ debugLog("insertHeader", ` - Match: ${existingHeaderMatch ? `"${existingHeaderMatch[0]}"` : "none"}`);
988
+ debugLog("insertHeader", ` - Existing level: ${existingLevel}`);
989
+ debugLog("insertHeader", ` - Existing prefix length: ${existingPrefixLength}`);
990
+ debugLog("insertHeader", ` - Target level: ${level}`);
991
+ const shouldToggleOff = toggle && existingLevel === level;
992
+ debugLog("insertHeader", `Should toggle OFF: ${shouldToggleOff} (toggle=${toggle}, existingLevel=${existingLevel}, level=${level})`);
993
+ const result = applyLineOperation(
994
+ textarea,
995
+ (ta) => {
996
+ const currentLine = ta.value.slice(ta.selectionStart, ta.selectionEnd);
997
+ debugLog("insertHeader", `Line in operation: "${currentLine}"`);
998
+ const cleanedLine = currentLine.replace(/^#{1,6}\s*/, "");
999
+ debugLog("insertHeader", `Cleaned line: "${cleanedLine}"`);
1000
+ let newLine;
1001
+ if (shouldToggleOff) {
1002
+ debugLog("insertHeader", "ACTION: Toggling OFF - removing header");
1003
+ newLine = cleanedLine;
1004
+ } else if (existingLevel > 0) {
1005
+ debugLog("insertHeader", `ACTION: Replacing H${existingLevel} with H${level}`);
1006
+ newLine = style.prefix + cleanedLine;
1007
+ } else {
1008
+ debugLog("insertHeader", "ACTION: Adding new header");
1009
+ newLine = style.prefix + cleanedLine;
1010
+ }
1011
+ debugLog("insertHeader", `New line: "${newLine}"`);
1012
+ return {
1013
+ text: newLine,
1014
+ selectionStart: ta.selectionStart,
1015
+ selectionEnd: ta.selectionEnd
1016
+ };
1017
+ },
1018
+ {
1019
+ prefix: style.prefix,
1020
+ // Custom selection adjustment for headers
1021
+ adjustSelection: (isRemoving, selStart, selEnd, lineStartPos) => {
1022
+ debugLog("insertHeader", `Adjusting selection:`);
1023
+ debugLog("insertHeader", ` - isRemoving param: ${isRemoving}`);
1024
+ debugLog("insertHeader", ` - shouldToggleOff: ${shouldToggleOff}`);
1025
+ debugLog("insertHeader", ` - selStart: ${selStart}, selEnd: ${selEnd}`);
1026
+ debugLog("insertHeader", ` - lineStartPos: ${lineStartPos}`);
1027
+ if (shouldToggleOff) {
1028
+ const adjustment = Math.max(selStart - existingPrefixLength, lineStartPos);
1029
+ debugLog("insertHeader", ` - Removing header, adjusting by -${existingPrefixLength}`);
1030
+ return {
1031
+ start: adjustment,
1032
+ end: selStart === selEnd ? adjustment : Math.max(selEnd - existingPrefixLength, lineStartPos)
1033
+ };
1034
+ } else if (existingPrefixLength > 0) {
1035
+ const prefixDiff = style.prefix.length - existingPrefixLength;
1036
+ debugLog("insertHeader", ` - Replacing header, adjusting by ${prefixDiff}`);
1037
+ return {
1038
+ start: selStart + prefixDiff,
1039
+ end: selEnd + prefixDiff
1040
+ };
1041
+ } else {
1042
+ debugLog("insertHeader", ` - Adding header, adjusting by +${style.prefix.length}`);
1043
+ return {
1044
+ start: selStart + style.prefix.length,
1045
+ end: selEnd + style.prefix.length
1046
+ };
1047
+ }
1048
+ }
1049
+ }
1050
+ );
1051
+ debugLog("insertHeader", `Final result: text="${result.text}", cursor=${result.selectionStart}-${result.selectionEnd}`);
1052
+ debugLog("insertHeader", `============ END ============`);
1053
+ insertText(textarea, result);
1054
+ }
1055
+ function toggleH1(textarea) {
1056
+ insertHeader(textarea, 1, true);
1057
+ }
1058
+ function toggleH2(textarea) {
1059
+ insertHeader(textarea, 2, true);
1060
+ }
1061
+ function toggleH3(textarea) {
1062
+ insertHeader(textarea, 3, true);
1063
+ }
1064
+ function getActiveFormats2(textarea) {
1065
+ return getActiveFormats(textarea);
1066
+ }
1067
+
1068
+ // src/shortcuts.js
1069
+ var ShortcutsManager = class {
1070
+ constructor(editor) {
1071
+ this.editor = editor;
1072
+ this.textarea = editor.textarea;
1073
+ }
1074
+ /**
1075
+ * Handle keydown events - called by OverType
1076
+ * @param {KeyboardEvent} event - The keyboard event
1077
+ * @returns {boolean} Whether the event was handled
1078
+ */
1079
+ handleKeydown(event) {
1080
+ const isMac = navigator.platform.toLowerCase().includes("mac");
1081
+ const modKey = isMac ? event.metaKey : event.ctrlKey;
1082
+ if (!modKey)
1083
+ return false;
1084
+ let action = null;
1085
+ switch (event.key.toLowerCase()) {
1086
+ case "b":
1087
+ if (!event.shiftKey) {
1088
+ action = "toggleBold";
1089
+ }
1090
+ break;
1091
+ case "i":
1092
+ if (!event.shiftKey) {
1093
+ action = "toggleItalic";
1094
+ }
1095
+ break;
1096
+ case "k":
1097
+ if (!event.shiftKey) {
1098
+ action = "insertLink";
1099
+ }
1100
+ break;
1101
+ case "7":
1102
+ if (event.shiftKey) {
1103
+ action = "toggleNumberedList";
1104
+ }
1105
+ break;
1106
+ case "8":
1107
+ if (event.shiftKey) {
1108
+ action = "toggleBulletList";
1109
+ }
1110
+ break;
1111
+ }
1112
+ if (action) {
1113
+ event.preventDefault();
1114
+ if (this.editor.toolbar) {
1115
+ this.editor.toolbar.handleAction(action);
1116
+ } else {
1117
+ this.handleAction(action);
1118
+ }
1119
+ return true;
1120
+ }
1121
+ return false;
1122
+ }
1123
+ /**
1124
+ * Handle action - fallback when no toolbar exists
1125
+ * This duplicates toolbar.handleAction for consistency
1126
+ */
1127
+ async handleAction(action) {
1128
+ const textarea = this.textarea;
1129
+ if (!textarea)
1130
+ return;
1131
+ textarea.focus();
1132
+ try {
1133
+ switch (action) {
1134
+ case "toggleBold":
1135
+ toggleBold(textarea);
1136
+ break;
1137
+ case "toggleItalic":
1138
+ toggleItalic(textarea);
1139
+ break;
1140
+ case "insertLink":
1141
+ insertLink(textarea);
1142
+ break;
1143
+ case "toggleBulletList":
1144
+ toggleBulletList(textarea);
1145
+ break;
1146
+ case "toggleNumberedList":
1147
+ toggleNumberedList(textarea);
1148
+ break;
1149
+ }
1150
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
1151
+ } catch (error) {
1152
+ console.error("Error in markdown action:", error);
1153
+ }
1154
+ }
1155
+ /**
1156
+ * Cleanup
1157
+ */
1158
+ destroy() {
1159
+ }
1160
+ };
1161
+
1162
+ // src/themes.js
1163
+ var solar = {
1164
+ name: "solar",
1165
+ colors: {
1166
+ bgPrimary: "#faf0ca",
1167
+ // Lemon Chiffon - main background
1168
+ bgSecondary: "#ffffff",
1169
+ // White - editor background
1170
+ text: "#0d3b66",
1171
+ // Yale Blue - main text
1172
+ h1: "#f95738",
1173
+ // Tomato - h1 headers
1174
+ h2: "#ee964b",
1175
+ // Sandy Brown - h2 headers
1176
+ h3: "#3d8a51",
1177
+ // Forest green - h3 headers
1178
+ strong: "#ee964b",
1179
+ // Sandy Brown - bold text
1180
+ em: "#f95738",
1181
+ // Tomato - italic text
1182
+ link: "#0d3b66",
1183
+ // Yale Blue - links
1184
+ code: "#0d3b66",
1185
+ // Yale Blue - inline code
1186
+ codeBg: "rgba(244, 211, 94, 0.4)",
1187
+ // Naples Yellow with transparency
1188
+ blockquote: "#5a7a9b",
1189
+ // Muted blue - blockquotes
1190
+ hr: "#5a7a9b",
1191
+ // Muted blue - horizontal rules
1192
+ syntaxMarker: "rgba(13, 59, 102, 0.52)",
1193
+ // Yale Blue with transparency
1194
+ cursor: "#f95738",
1195
+ // Tomato - cursor
1196
+ selection: "rgba(244, 211, 94, 0.4)",
1197
+ // Naples Yellow with transparency
1198
+ listMarker: "#ee964b",
1199
+ // Sandy Brown - list markers
1200
+ // Toolbar colors
1201
+ toolbarBg: "#ffffff",
1202
+ // White - toolbar background
1203
+ toolbarBorder: "rgba(13, 59, 102, 0.15)",
1204
+ // Yale Blue border
1205
+ toolbarIcon: "#0d3b66",
1206
+ // Yale Blue - icon color
1207
+ toolbarHover: "#f5f5f5",
1208
+ // Light gray - hover background
1209
+ toolbarActive: "#faf0ca"
1210
+ // Lemon Chiffon - active button background
1211
+ }
1212
+ };
1213
+ var cave = {
1214
+ name: "cave",
1215
+ colors: {
1216
+ bgPrimary: "#141E26",
1217
+ // Deep ocean - main background
1218
+ bgSecondary: "#1D2D3E",
1219
+ // Darker charcoal - editor background
1220
+ text: "#c5dde8",
1221
+ // Light blue-gray - main text
1222
+ h1: "#d4a5ff",
1223
+ // Rich lavender - h1 headers
1224
+ h2: "#f6ae2d",
1225
+ // Hunyadi Yellow - h2 headers
1226
+ h3: "#9fcfec",
1227
+ // Brighter blue - h3 headers
1228
+ strong: "#f6ae2d",
1229
+ // Hunyadi Yellow - bold text
1230
+ em: "#9fcfec",
1231
+ // Brighter blue - italic text
1232
+ link: "#9fcfec",
1233
+ // Brighter blue - links
1234
+ code: "#c5dde8",
1235
+ // Light blue-gray - inline code
1236
+ codeBg: "#1a232b",
1237
+ // Very dark blue - code background
1238
+ blockquote: "#9fcfec",
1239
+ // Brighter blue - same as italic
1240
+ hr: "#c5dde8",
1241
+ // Light blue-gray - horizontal rules
1242
+ syntaxMarker: "rgba(159, 207, 236, 0.73)",
1243
+ // Brighter blue semi-transparent
1244
+ cursor: "#f26419",
1245
+ // Orange Pantone - cursor
1246
+ selection: "rgba(51, 101, 138, 0.4)",
1247
+ // Lapis Lazuli with transparency
1248
+ listMarker: "#f6ae2d",
1249
+ // Hunyadi Yellow - list markers
1250
+ // Toolbar colors for dark theme
1251
+ toolbarBg: "#1D2D3E",
1252
+ // Darker charcoal - toolbar background
1253
+ toolbarBorder: "rgba(197, 221, 232, 0.1)",
1254
+ // Light blue-gray border
1255
+ toolbarIcon: "#c5dde8",
1256
+ // Light blue-gray - icon color
1257
+ toolbarHover: "#243546",
1258
+ // Slightly lighter charcoal - hover background
1259
+ toolbarActive: "#2a3f52"
1260
+ // Even lighter - active button background
1261
+ }
1262
+ };
1263
+ var themes = {
1264
+ solar,
1265
+ cave,
1266
+ // Aliases for backward compatibility
1267
+ light: solar,
1268
+ dark: cave
1269
+ };
1270
+ function getTheme(theme) {
1271
+ if (typeof theme === "string") {
1272
+ const themeObj = themes[theme] || themes.solar;
1273
+ return { ...themeObj, name: theme };
1274
+ }
1275
+ return theme;
1276
+ }
1277
+ function themeToCSSVars(colors) {
1278
+ const vars = [];
1279
+ for (const [key, value] of Object.entries(colors)) {
1280
+ const varName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
1281
+ vars.push(`--${varName}: ${value};`);
1282
+ }
1283
+ return vars.join("\n");
1284
+ }
1285
+ function mergeTheme(baseTheme, customColors = {}) {
1286
+ return {
1287
+ ...baseTheme,
1288
+ colors: {
1289
+ ...baseTheme.colors,
1290
+ ...customColors
1291
+ }
1292
+ };
1293
+ }
1294
+
1295
+ // src/styles.js
1296
+ function generateStyles(options = {}) {
1297
+ const {
1298
+ fontSize = "14px",
1299
+ lineHeight = 1.6,
1300
+ fontFamily = "'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace",
1301
+ padding = "20px",
1302
+ theme = null,
1303
+ mobile = {}
1304
+ } = options;
1305
+ const mobileStyles = Object.keys(mobile).length > 0 ? `
1306
+ @media (max-width: 640px) {
1307
+ .overtype-wrapper .overtype-input,
1308
+ .overtype-wrapper .overtype-preview {
1309
+ ${Object.entries(mobile).map(([prop, val]) => {
1310
+ const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
1311
+ return `${cssProp}: ${val} !important;`;
1312
+ }).join("\n ")}
1313
+ }
1314
+ }
1315
+ ` : "";
1316
+ const themeVars = theme && theme.colors ? themeToCSSVars(theme.colors) : "";
1317
+ return `
1318
+ /* OverType Editor Styles */
1319
+ .overtype-container {
1320
+ position: relative !important;
1321
+ width: 100% !important;
1322
+ height: 100% !important;
1323
+ ${themeVars ? `
1324
+ /* Theme Variables */
1325
+ ${themeVars}` : ""}
1326
+ }
1327
+
1328
+ .overtype-wrapper {
1329
+ position: relative !important;
1330
+ width: 100% !important;
1331
+ height: 100% !important;
1332
+ overflow: hidden !important;
1333
+ background: var(--bg-secondary, #ffffff) !important;
1334
+ }
1335
+
1336
+ /* Critical alignment styles - must be identical for both layers */
1337
+ .overtype-wrapper .overtype-input,
1338
+ .overtype-wrapper .overtype-preview {
1339
+ /* Positioning - must be identical */
1340
+ position: absolute !important;
1341
+ top: 0 !important;
1342
+ left: 0 !important;
1343
+ width: 100% !important;
1344
+ height: 100% !important;
1345
+
1346
+ /* Font properties - any difference breaks alignment */
1347
+ font-family: ${fontFamily} !important;
1348
+ font-size: var(--instance-font-size, ${fontSize}) !important;
1349
+ line-height: var(--instance-line-height, ${lineHeight}) !important;
1350
+ font-weight: normal !important;
1351
+ font-style: normal !important;
1352
+ font-variant: normal !important;
1353
+ font-stretch: normal !important;
1354
+ font-kerning: none !important;
1355
+ font-feature-settings: normal !important;
1356
+
1357
+ /* Box model - must match exactly */
1358
+ padding: var(--instance-padding, ${padding}) !important;
1359
+ margin: 0 !important;
1360
+ border: none !important;
1361
+ outline: none !important;
1362
+ box-sizing: border-box !important;
1363
+
1364
+ /* Text layout - critical for character positioning */
1365
+ white-space: pre-wrap !important;
1366
+ word-wrap: break-word !important;
1367
+ word-break: normal !important;
1368
+ overflow-wrap: break-word !important;
1369
+ tab-size: 2 !important;
1370
+ -moz-tab-size: 2 !important;
1371
+ text-align: left !important;
1372
+ text-indent: 0 !important;
1373
+ letter-spacing: normal !important;
1374
+ word-spacing: normal !important;
1375
+
1376
+ /* Text rendering */
1377
+ text-transform: none !important;
1378
+ text-rendering: auto !important;
1379
+ -webkit-font-smoothing: auto !important;
1380
+ -webkit-text-size-adjust: 100% !important;
1381
+
1382
+ /* Direction and writing */
1383
+ direction: ltr !important;
1384
+ writing-mode: horizontal-tb !important;
1385
+ unicode-bidi: normal !important;
1386
+ text-orientation: mixed !important;
1387
+
1388
+ /* Visual effects that could shift perception */
1389
+ text-shadow: none !important;
1390
+ filter: none !important;
1391
+ transform: none !important;
1392
+ zoom: 1 !important;
1393
+
1394
+ /* Vertical alignment */
1395
+ vertical-align: baseline !important;
1396
+
1397
+ /* Size constraints */
1398
+ min-width: 0 !important;
1399
+ min-height: 0 !important;
1400
+ max-width: none !important;
1401
+ max-height: none !important;
1402
+
1403
+ /* Overflow */
1404
+ overflow-y: auto !important;
1405
+ overflow-x: auto !important;
1406
+ scrollbar-width: auto !important;
1407
+ scrollbar-gutter: auto !important;
1408
+
1409
+ /* Animation/transition - disabled to prevent movement */
1410
+ animation: none !important;
1411
+ transition: none !important;
1412
+ }
1413
+
1414
+ /* Input layer styles */
1415
+ .overtype-wrapper .overtype-input {
1416
+ /* Layer positioning */
1417
+ z-index: 1 !important;
1418
+
1419
+ /* Text visibility */
1420
+ color: transparent !important;
1421
+ caret-color: var(--cursor, #f95738) !important;
1422
+ background-color: transparent !important;
1423
+
1424
+ /* Textarea-specific */
1425
+ resize: none !important;
1426
+ appearance: none !important;
1427
+ -webkit-appearance: none !important;
1428
+ -moz-appearance: none !important;
1429
+
1430
+ /* Prevent mobile zoom on focus */
1431
+ touch-action: manipulation !important;
1432
+
1433
+ /* Disable autofill and spellcheck */
1434
+ autocomplete: off !important;
1435
+ autocorrect: off !important;
1436
+ autocapitalize: off !important;
1437
+ spellcheck: false !important;
1438
+ }
1439
+
1440
+ .overtype-wrapper .overtype-input::selection {
1441
+ background-color: var(--selection, rgba(244, 211, 94, 0.4));
1442
+ }
1443
+
1444
+ /* Preview layer styles */
1445
+ .overtype-wrapper .overtype-preview {
1446
+ /* Layer positioning */
1447
+ z-index: 0 !important;
1448
+ pointer-events: none !important;
1449
+ color: var(--text, #0d3b66) !important;
1450
+ background-color: transparent !important;
1451
+
1452
+ /* Prevent text selection */
1453
+ user-select: none !important;
1454
+ -webkit-user-select: none !important;
1455
+ -moz-user-select: none !important;
1456
+ -ms-user-select: none !important;
1457
+ }
1458
+
1459
+ /* Defensive styles for preview child divs */
1460
+ .overtype-wrapper .overtype-preview div {
1461
+ /* Reset any inherited styles */
1462
+ margin: 0 !important;
1463
+ padding: 0 !important;
1464
+ border: none !important;
1465
+ text-align: left !important;
1466
+ text-indent: 0 !important;
1467
+ display: block !important;
1468
+ position: static !important;
1469
+ transform: none !important;
1470
+ min-height: 0 !important;
1471
+ max-height: none !important;
1472
+ line-height: inherit !important;
1473
+ font-size: inherit !important;
1474
+ font-family: inherit !important;
1475
+ }
1476
+
1477
+ /* Markdown element styling - NO SIZE CHANGES */
1478
+ .overtype-wrapper .overtype-preview .header {
1479
+ font-weight: bold !important;
1480
+ }
1481
+
1482
+ /* Header colors */
1483
+ .overtype-wrapper .overtype-preview .h1 {
1484
+ color: var(--h1, #f95738) !important;
1485
+ }
1486
+ .overtype-wrapper .overtype-preview .h2 {
1487
+ color: var(--h2, #ee964b) !important;
1488
+ }
1489
+ .overtype-wrapper .overtype-preview .h3 {
1490
+ color: var(--h3, #3d8a51) !important;
1491
+ }
1492
+
1493
+ /* Bold text */
1494
+ .overtype-wrapper .overtype-preview strong {
1495
+ color: var(--strong, #ee964b) !important;
1496
+ font-weight: bold !important;
1497
+ }
1498
+
1499
+ /* Italic text */
1500
+ .overtype-wrapper .overtype-preview em {
1501
+ color: var(--em, #f95738) !important;
1502
+ text-decoration-color: var(--em, #f95738) !important;
1503
+ text-decoration-thickness: 1px !important;
1504
+ font-style: italic !important;
1505
+ }
1506
+
1507
+ /* Inline code */
1508
+ .overtype-wrapper .overtype-preview code {
1509
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1510
+ color: var(--code, #0d3b66) !important;
1511
+ padding: 0 !important;
1512
+ border-radius: 2px !important;
1513
+ font-family: inherit !important;
1514
+ font-weight: normal !important;
1515
+ }
1516
+
1517
+ /* Code blocks */
1518
+ .overtype-wrapper .overtype-preview pre {
1519
+ background: #1e1e1e !important;
1520
+ padding: 0 !important;
1521
+ margin: 0 !important;
1522
+ border-radius: 4px !important;
1523
+ overflow-x: auto !important;
1524
+ }
1525
+
1526
+ .overtype-wrapper .overtype-preview pre code {
1527
+ background: none !important;
1528
+ }
1529
+
1530
+ /* Blockquotes */
1531
+ .overtype-wrapper .overtype-preview .blockquote {
1532
+ color: var(--blockquote, #5a7a9b) !important;
1533
+ padding: 0 !important;
1534
+ margin: 0 !important;
1535
+ border: none !important;
1536
+ }
1537
+
1538
+ /* Links */
1539
+ .overtype-wrapper .overtype-preview a {
1540
+ color: var(--link, #0d3b66) !important;
1541
+ text-decoration: underline !important;
1542
+ font-weight: normal !important;
1543
+ }
1544
+
1545
+ .overtype-wrapper .overtype-preview a:hover {
1546
+ text-decoration: underline !important;
1547
+ color: var(--link, #0d3b66) !important;
1548
+ }
1549
+
1550
+ /* Lists - no list styling */
1551
+ .overtype-wrapper .overtype-preview ul,
1552
+ .overtype-wrapper .overtype-preview ol {
1553
+ list-style: none !important;
1554
+ margin: 0 !important;
1555
+ padding: 0 !important;
1556
+ }
1557
+
1558
+ .overtype-wrapper .overtype-preview li {
1559
+ margin: 0 !important;
1560
+ padding: 0 !important;
1561
+ list-style: none !important;
1562
+ }
1563
+
1564
+ /* Horizontal rules */
1565
+ .overtype-wrapper .overtype-preview hr {
1566
+ border: none !important;
1567
+ color: var(--hr, #5a7a9b) !important;
1568
+ margin: 0 !important;
1569
+ padding: 0 !important;
1570
+ }
1571
+
1572
+ .overtype-wrapper .overtype-preview .hr-marker {
1573
+ color: var(--hr, #5a7a9b) !important;
1574
+ opacity: 0.6 !important;
1575
+ }
1576
+
1577
+ /* Code fence markers - with background when not in code block */
1578
+ .overtype-wrapper .overtype-preview .code-fence {
1579
+ color: var(--code, #0d3b66) !important;
1580
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1581
+ }
1582
+
1583
+ /* Code block lines - background for entire code block */
1584
+ .overtype-wrapper .overtype-preview .code-block-line {
1585
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1586
+ }
1587
+
1588
+ /* Remove background from code fence when inside code block line */
1589
+ .overtype-wrapper .overtype-preview .code-block-line .code-fence {
1590
+ background: transparent !important;
1591
+ }
1592
+
1593
+ /* Raw markdown line */
1594
+ .overtype-wrapper .overtype-preview .raw-line {
1595
+ color: var(--raw-line, #5a7a9b) !important;
1596
+ font-style: normal !important;
1597
+ font-weight: normal !important;
1598
+ }
1599
+
1600
+ /* Syntax markers */
1601
+ .overtype-wrapper .overtype-preview .syntax-marker {
1602
+ color: var(--syntax-marker, rgba(13, 59, 102, 0.52)) !important;
1603
+ opacity: 0.7 !important;
1604
+ }
1605
+
1606
+ /* List markers */
1607
+ .overtype-wrapper .overtype-preview .list-marker {
1608
+ color: var(--list-marker, #ee964b) !important;
1609
+ }
1610
+
1611
+ /* Stats bar */
1612
+ .overtype-wrapper.with-stats {
1613
+ padding-bottom: 40px !important;
1614
+ }
1615
+
1616
+ .overtype-wrapper .overtype-stats {
1617
+ position: absolute !important;
1618
+ bottom: 0 !important;
1619
+ left: 0 !important;
1620
+ right: 0 !important;
1621
+ height: 40px !important;
1622
+ padding: 0 20px !important;
1623
+ background: #f8f9fa !important;
1624
+ border-top: 1px solid #e0e0e0 !important;
1625
+ display: flex !important;
1626
+ justify-content: space-between !important;
1627
+ align-items: center !important;
1628
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
1629
+ font-size: 0.85rem !important;
1630
+ color: #666 !important;
1631
+ z-index: 2 !important;
1632
+ }
1633
+
1634
+ /* Dark theme stats bar */
1635
+ .overtype-wrapper[data-theme="cave"] .overtype-stats {
1636
+ background: var(--bg-secondary, #1D2D3E) !important;
1637
+ border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
1638
+ color: var(--text, #c5dde8) !important;
1639
+ }
1640
+
1641
+ .overtype-wrapper .overtype-stats .overtype-stat {
1642
+ display: flex !important;
1643
+ align-items: center !important;
1644
+ gap: 5px !important;
1645
+ white-space: nowrap !important;
1646
+ }
1647
+
1648
+ .overtype-wrapper .overtype-stats .live-dot {
1649
+ width: 8px !important;
1650
+ height: 8px !important;
1651
+ background: #4caf50 !important;
1652
+ border-radius: 50% !important;
1653
+ animation: pulse 2s infinite !important;
1654
+ }
1655
+
1656
+ @keyframes pulse {
1657
+ 0%, 100% { opacity: 1; transform: scale(1); }
1658
+ 50% { opacity: 0.6; transform: scale(1.2); }
1659
+ }
1660
+
1661
+ /* Adjust textarea and preview for stats bar */
1662
+ .overtype-wrapper.with-stats .overtype-input,
1663
+ .overtype-wrapper.with-stats .overtype-preview {
1664
+ height: calc(100% - 40px) !important;
1665
+ }
1666
+
1667
+ /* Toolbar Styles */
1668
+ .overtype-toolbar {
1669
+ display: flex;
1670
+ align-items: center;
1671
+ gap: 4px;
1672
+ padding: 8px;
1673
+ background: var(--toolbar-bg, var(--bg-primary, #f8f9fa));
1674
+ border: 1px solid var(--toolbar-border, var(--border, #e0e0e0));
1675
+ border-bottom: none;
1676
+ border-radius: 8px 8px 0 0;
1677
+ overflow-x: auto;
1678
+ -webkit-overflow-scrolling: touch;
1679
+ }
1680
+
1681
+ .overtype-toolbar-button {
1682
+ display: flex;
1683
+ align-items: center;
1684
+ justify-content: center;
1685
+ width: 32px;
1686
+ height: 32px;
1687
+ padding: 0;
1688
+ border: none;
1689
+ border-radius: 6px;
1690
+ background: transparent;
1691
+ color: var(--toolbar-icon, var(--text-secondary, #666));
1692
+ cursor: pointer;
1693
+ transition: all 0.2s ease;
1694
+ flex-shrink: 0;
1695
+ }
1696
+
1697
+ .overtype-toolbar-button svg {
1698
+ width: 20px;
1699
+ height: 20px;
1700
+ fill: currentColor;
1701
+ }
1702
+
1703
+ /* Special sizing for code block icon */
1704
+ .overtype-toolbar-button[data-action="insertCodeBlock"] svg {
1705
+ width: 22px;
1706
+ height: 18px;
1707
+ fill: transparent !important;
1708
+ }
1709
+
1710
+ .overtype-toolbar-button:hover {
1711
+ background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
1712
+ color: var(--toolbar-icon, var(--text-primary, #333));
1713
+ }
1714
+
1715
+ .overtype-toolbar-button:active {
1716
+ transform: scale(0.95);
1717
+ }
1718
+
1719
+ .overtype-toolbar-button.active {
1720
+ background: var(--toolbar-active, var(--primary, #007bff));
1721
+ color: var(--toolbar-icon, var(--text-primary, #333));
1722
+ }
1723
+
1724
+ .overtype-toolbar-button:disabled {
1725
+ opacity: 0.5;
1726
+ cursor: not-allowed;
1727
+ }
1728
+
1729
+ .overtype-toolbar-separator {
1730
+ width: 1px;
1731
+ height: 24px;
1732
+ background: var(--border, #e0e0e0);
1733
+ margin: 0 4px;
1734
+ flex-shrink: 0;
1735
+ }
1736
+
1737
+ /* Adjust wrapper when toolbar is present */
1738
+ .overtype-container .overtype-toolbar + .overtype-wrapper {
1739
+ border-radius: 0 0 8px 8px;
1740
+ border-top: none;
1741
+ }
1742
+
1743
+ /* Mobile toolbar adjustments */
1744
+ @media (max-width: 640px) {
1745
+ .overtype-toolbar {
1746
+ padding: 6px;
1747
+ gap: 2px;
1748
+ }
1749
+
1750
+ .overtype-toolbar-button {
1751
+ width: 36px;
1752
+ height: 36px;
1753
+ }
1754
+
1755
+ .overtype-toolbar-separator {
1756
+ margin: 0 2px;
1757
+ }
1758
+ }
1759
+
1760
+ ${mobileStyles}
1761
+ `;
1762
+ }
1763
+
1764
+ // src/icons.js
1765
+ var boldIcon = `<svg viewBox="0 0 18 18">
1766
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5,4H9.5A2.5,2.5,0,0,1,12,6.5v0A2.5,2.5,0,0,1,9.5,9H5A0,0,0,0,1,5,9V4A0,0,0,0,1,5,4Z"></path>
1767
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5,9h5.5A2.5,2.5,0,0,1,13,11.5v0A2.5,2.5,0,0,1,10.5,14H5a0,0,0,0,1,0,0V9A0,0,0,0,1,5,9Z"></path>
1768
+ </svg>`;
1769
+ var italicIcon = `<svg viewBox="0 0 18 18">
1770
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="13" y1="4" y2="4"></line>
1771
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="5" x2="11" y1="14" y2="14"></line>
1772
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="10" y1="14" y2="4"></line>
1773
+ </svg>`;
1774
+ var h1Icon = `<svg viewBox="0 0 18 18">
1775
+ <path fill="currentColor" d="M10,4V14a1,1,0,0,1-2,0V10H3v4a1,1,0,0,1-2,0V4A1,1,0,0,1,3,4V8H8V4a1,1,0,0,1,2,0Zm6.06787,9.209H14.98975V7.59863a.54085.54085,0,0,0-.605-.60547h-.62744a1.01119,1.01119,0,0,0-.748.29688L11.645,8.56641a.5435.5435,0,0,0-.022.8584l.28613.30762a.53861.53861,0,0,0,.84717.0332l.09912-.08789a1.2137,1.2137,0,0,0,.2417-.35254h.02246s-.01123.30859-.01123.60547V13.209H12.041a.54085.54085,0,0,0-.605.60547v.43945a.54085.54085,0,0,0,.605.60547h4.02686a.54085.54085,0,0,0,.605-.60547v-.43945A.54085.54085,0,0,0,16.06787,13.209Z"></path>
1776
+ </svg>`;
1777
+ var h2Icon = `<svg viewBox="0 0 18 18">
1778
+ <path fill="currentColor" d="M16.73975,13.81445v.43945a.54085.54085,0,0,1-.605.60547H11.855a.58392.58392,0,0,1-.64893-.60547V14.0127c0-2.90527,3.39941-3.42187,3.39941-4.55469a.77675.77675,0,0,0-.84717-.78125,1.17684,1.17684,0,0,0-.83594.38477c-.2749.26367-.561.374-.85791.13184l-.4292-.34082c-.30811-.24219-.38525-.51758-.1543-.81445a2.97155,2.97155,0,0,1,2.45361-1.17676,2.45393,2.45393,0,0,1,2.68408,2.40918c0,2.45312-3.1792,2.92676-3.27832,3.93848h2.79443A.54085.54085,0,0,1,16.73975,13.81445ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z"></path>
1779
+ </svg>`;
1780
+ var h3Icon = `<svg viewBox="0 0 18 18">
1781
+ <path fill="currentColor" d="M16.65186,12.30664a2.6742,2.6742,0,0,1-2.915,2.68457,3.96592,3.96592,0,0,1-2.25537-.6709.56007.56007,0,0,1-.13232-.83594L11.64648,13c.209-.34082.48389-.36328.82471-.1543a2.32654,2.32654,0,0,0,1.12256.33008c.71484,0,1.12207-.35156,1.12207-.78125,0-.61523-.61621-.86816-1.46338-.86816H13.2085a.65159.65159,0,0,1-.68213-.41895l-.05518-.10937a.67114.67114,0,0,1,.14307-.78125l.71533-.86914a8.55289,8.55289,0,0,1,.68213-.7373V8.58887a3.93913,3.93913,0,0,1-.748.05469H11.9873a.54085.54085,0,0,1-.605-.60547V7.59863a.54085.54085,0,0,1,.605-.60547h3.75146a.53773.53773,0,0,1,.60547.59375v.17676a1.03723,1.03723,0,0,1-.27539.748L14.74854,10.0293A2.31132,2.31132,0,0,1,16.65186,12.30664ZM9,3A.99974.99974,0,0,0,8,4V8H3V4A1,1,0,0,0,1,4V14a1,1,0,0,0,2,0V10H8v4a1,1,0,0,0,2,0V4A.99974.99974,0,0,0,9,3Z"></path>
1782
+ </svg>`;
1783
+ var linkIcon = `<svg viewBox="0 0 18 18">
1784
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="11" y1="7" y2="11"></line>
1785
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.9,4.577a3.476,3.476,0,0,1,.36,4.679A3.476,3.476,0,0,1,4.577,8.9C3.185,7.5,2.035,6.4,4.217,4.217S7.5,3.185,8.9,4.577Z"></path>
1786
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.423,9.1a3.476,3.476,0,0,0-4.679-.36,3.476,3.476,0,0,0,.36,4.679c1.392,1.392,2.5,2.542,4.679.36S14.815,10.5,13.423,9.1Z"></path>
1787
+ </svg>`;
1788
+ var codeIcon = `<svg viewBox="0 0 18 18">
1789
+ <polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="5 7 3 9 5 11"></polyline>
1790
+ <polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="13 7 15 9 13 11"></polyline>
1791
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="10" x2="8" y1="5" y2="13"></line>
1792
+ </svg>`;
1793
+ var codeBlockIcon = `<svg viewBox="0 0 46 33" fill="transparent" xmlns="http://www.w3.org/2000/svg">
1794
+ <path d="M35 8h3a5 5 0 0 1 5 5v12a5 5 0 0 1-5 5H18a5 5 0 0 1-5-5v-2" stroke="currentColor" stroke-width="4" stroke-linecap="round"></path>
1795
+ <path d="m9 2.5-6 6L9 14M20 2.5l6 6-6 5.5" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
1796
+ </svg>`;
1797
+ var bulletListIcon = `<svg viewBox="0 0 18 18">
1798
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="4" y2="4"></line>
1799
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="9" y2="9"></line>
1800
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="14" y2="14"></line>
1801
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="4" y2="4"></line>
1802
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="9" y2="9"></line>
1803
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="14" y2="14"></line>
1804
+ </svg>`;
1805
+ var orderedListIcon = `<svg viewBox="0 0 18 18">
1806
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="4" y2="4"></line>
1807
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="9" y2="9"></line>
1808
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="14" y2="14"></line>
1809
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" x1="2.5" x2="4.5" y1="5.5" y2="5.5"></line>
1810
+ <path fill="currentColor" d="M3.5,6A0.5,0.5,0,0,1,3,5.5V3.085l-0.276.138A0.5,0.5,0,0,1,2.053,3c-0.124-.247-0.023-0.324.224-0.447l1-.5A0.5,0.5,0,0,1,4,2.5v3A0.5,0.5,0,0,1,3.5,6Z"></path>
1811
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M4.5,10.5h-2c0-.234,1.85-1.076,1.85-2.234A0.959,0.959,0,0,0,2.5,8.156"></path>
1812
+ <path stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M2.5,14.846a0.959,0.959,0,0,0,1.85-.109A0.7,0.7,0,0,0,3.75,14a0.688,0.688,0,0,0,.6-0.736,0.959,0.959,0,0,0-1.85-.109"></path>
1813
+ </svg>`;
1814
+ var quoteIcon = `<svg viewBox="2 2 20 20">
1815
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 10.8182L9 10.8182C8.80222 10.8182 8.60888 10.7649 8.44443 10.665C8.27998 10.5651 8.15181 10.4231 8.07612 10.257C8.00043 10.0909 7.98063 9.90808 8.01922 9.73174C8.0578 9.55539 8.15304 9.39341 8.29289 9.26627C8.43275 9.13913 8.61093 9.05255 8.80491 9.01747C8.99889 8.98239 9.19996 9.00039 9.38268 9.0692C9.56541 9.13801 9.72159 9.25453 9.83147 9.40403C9.94135 9.55353 10 9.72929 10 9.90909L10 12.1818C10 12.664 9.78929 13.1265 9.41421 13.4675C9.03914 13.8084 8.53043 14 8 14"></path>
1816
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 10.8182L15 10.8182C14.8022 10.8182 14.6089 10.7649 14.4444 10.665C14.28 10.5651 14.1518 10.4231 14.0761 10.257C14.0004 10.0909 13.9806 9.90808 14.0192 9.73174C14.0578 9.55539 14.153 9.39341 14.2929 9.26627C14.4327 9.13913 14.6109 9.05255 14.8049 9.01747C14.9989 8.98239 15.2 9.00039 15.3827 9.0692C15.5654 9.13801 15.7216 9.25453 15.8315 9.40403C15.9414 9.55353 16 9.72929 16 9.90909L16 12.1818C16 12.664 15.7893 13.1265 15.4142 13.4675C15.0391 13.8084 14.5304 14 14 14"></path>
1817
+ </svg>`;
1818
+ var taskListIcon = `<svg viewBox="0 0 18 18">
1819
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="4" y2="4"></line>
1820
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="9" y2="9"></line>
1821
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="14" y2="14"></line>
1822
+ <rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="3" width="3" height="3" rx="0.5"></rect>
1823
+ <rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="13" width="3" height="3" rx="0.5"></rect>
1824
+ <polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" points="2.65 9.5 3.5 10.5 5 8.5"></polyline>
1825
+ </svg>`;
1826
+
1827
+ // src/toolbar.js
1828
+ var Toolbar = class {
1829
+ constructor(editor) {
1830
+ this.editor = editor;
1831
+ this.container = null;
1832
+ this.buttons = {};
1833
+ }
1834
+ /**
1835
+ * Create and attach toolbar to editor
1836
+ */
1837
+ create() {
1838
+ this.container = document.createElement("div");
1839
+ this.container.className = "overtype-toolbar";
1840
+ this.container.setAttribute("role", "toolbar");
1841
+ this.container.setAttribute("aria-label", "Text formatting");
1842
+ const buttonConfig = [
1843
+ { name: "bold", icon: boldIcon, title: "Bold (Ctrl+B)", action: "toggleBold" },
1844
+ { name: "italic", icon: italicIcon, title: "Italic (Ctrl+I)", action: "toggleItalic" },
1845
+ { separator: true },
1846
+ { name: "h1", icon: h1Icon, title: "Heading 1", action: "insertH1" },
1847
+ { name: "h2", icon: h2Icon, title: "Heading 2", action: "insertH2" },
1848
+ { name: "h3", icon: h3Icon, title: "Heading 3", action: "insertH3" },
1849
+ { separator: true },
1850
+ { name: "link", icon: linkIcon, title: "Insert Link (Ctrl+K)", action: "insertLink" },
1851
+ { name: "code", icon: codeIcon, title: "Inline Code", action: "toggleCode" },
1852
+ { name: "codeBlock", icon: codeBlockIcon, title: "Code Block", action: "insertCodeBlock" },
1853
+ { separator: true },
1854
+ { name: "quote", icon: quoteIcon, title: "Quote", action: "toggleQuote" },
1855
+ { separator: true },
1856
+ { name: "bulletList", icon: bulletListIcon, title: "Bullet List", action: "toggleBulletList" },
1857
+ { name: "orderedList", icon: orderedListIcon, title: "Numbered List", action: "toggleNumberedList" },
1858
+ { name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" }
1859
+ ];
1860
+ buttonConfig.forEach((config) => {
1861
+ if (config.separator) {
1862
+ const separator = document.createElement("div");
1863
+ separator.className = "overtype-toolbar-separator";
1864
+ separator.setAttribute("role", "separator");
1865
+ this.container.appendChild(separator);
1866
+ } else {
1867
+ const button = this.createButton(config);
1868
+ this.buttons[config.name] = button;
1869
+ this.container.appendChild(button);
1870
+ }
1871
+ });
1872
+ const container = this.editor.element.querySelector(".overtype-container");
1873
+ const wrapper = this.editor.element.querySelector(".overtype-wrapper");
1874
+ if (container && wrapper) {
1875
+ container.insertBefore(this.container, wrapper);
1876
+ }
1877
+ return this.container;
1878
+ }
1879
+ /**
1880
+ * Create individual toolbar button
1881
+ */
1882
+ createButton(config) {
1883
+ const button = document.createElement("button");
1884
+ button.className = "overtype-toolbar-button";
1885
+ button.type = "button";
1886
+ button.title = config.title;
1887
+ button.setAttribute("aria-label", config.title);
1888
+ button.setAttribute("data-action", config.action);
1889
+ button.innerHTML = config.icon;
1890
+ button.addEventListener("click", (e) => {
1891
+ e.preventDefault();
1892
+ this.handleAction(config.action);
1893
+ });
1894
+ return button;
1895
+ }
1896
+ /**
1897
+ * Handle toolbar button actions
1898
+ */
1899
+ async handleAction(action) {
1900
+ const textarea = this.editor.textarea;
1901
+ if (!textarea)
1902
+ return;
1903
+ textarea.focus();
1904
+ try {
1905
+ switch (action) {
1906
+ case "toggleBold":
1907
+ toggleBold(textarea);
1908
+ break;
1909
+ case "toggleItalic":
1910
+ toggleItalic(textarea);
1911
+ break;
1912
+ case "insertH1":
1913
+ toggleH1(textarea);
1914
+ break;
1915
+ case "insertH2":
1916
+ toggleH2(textarea);
1917
+ break;
1918
+ case "insertH3":
1919
+ toggleH3(textarea);
1920
+ break;
1921
+ case "insertLink":
1922
+ insertLink(textarea);
1923
+ break;
1924
+ case "toggleCode":
1925
+ toggleCode(textarea);
1926
+ break;
1927
+ case "insertCodeBlock":
1928
+ const start = textarea.selectionStart;
1929
+ const end = textarea.selectionEnd;
1930
+ const selectedText = textarea.value.slice(start, end);
1931
+ const codeBlock = "```\n" + selectedText + "\n```";
1932
+ textarea.setRangeText(codeBlock, start, end, "end");
1933
+ break;
1934
+ case "toggleBulletList":
1935
+ toggleBulletList(textarea);
1936
+ break;
1937
+ case "toggleNumberedList":
1938
+ toggleNumberedList(textarea);
1939
+ break;
1940
+ case "toggleQuote":
1941
+ toggleQuote(textarea);
1942
+ break;
1943
+ case "toggleTaskList":
1944
+ toggleTaskList(textarea);
1945
+ break;
1946
+ }
1947
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
1948
+ } catch (error) {
1949
+ console.error("Error loading markdown-actions:", error);
1950
+ }
1951
+ }
1952
+ /**
1953
+ * Update toolbar button states based on current selection
1954
+ */
1955
+ async updateButtonStates() {
1956
+ const textarea = this.editor.textarea;
1957
+ if (!textarea)
1958
+ return;
1959
+ try {
1960
+ const activeFormats = getActiveFormats2(textarea);
1961
+ Object.entries(this.buttons).forEach(([name, button]) => {
1962
+ let isActive = false;
1963
+ switch (name) {
1964
+ case "bold":
1965
+ isActive = activeFormats.includes("bold");
1966
+ break;
1967
+ case "italic":
1968
+ isActive = activeFormats.includes("italic");
1969
+ break;
1970
+ case "code":
1971
+ isActive = false;
1972
+ break;
1973
+ case "bulletList":
1974
+ isActive = activeFormats.includes("bullet-list");
1975
+ break;
1976
+ case "orderedList":
1977
+ isActive = activeFormats.includes("numbered-list");
1978
+ break;
1979
+ case "quote":
1980
+ isActive = activeFormats.includes("quote");
1981
+ break;
1982
+ case "taskList":
1983
+ isActive = activeFormats.includes("task-list");
1984
+ break;
1985
+ case "h1":
1986
+ isActive = activeFormats.includes("header");
1987
+ break;
1988
+ case "h2":
1989
+ isActive = activeFormats.includes("header-2");
1990
+ break;
1991
+ case "h3":
1992
+ isActive = activeFormats.includes("header-3");
1993
+ break;
1994
+ }
1995
+ button.classList.toggle("active", isActive);
1996
+ button.setAttribute("aria-pressed", isActive.toString());
1997
+ });
1998
+ } catch (error) {
1999
+ }
2000
+ }
2001
+ /**
2002
+ * Destroy toolbar
2003
+ */
2004
+ destroy() {
2005
+ if (this.container) {
2006
+ this.container.remove();
2007
+ this.container = null;
2008
+ this.buttons = {};
2009
+ }
2010
+ }
2011
+ };
2012
+
2013
+ // src/overtype.js
2014
+ var _OverType = class _OverType {
2015
+ /**
2016
+ * Constructor - Always returns an array of instances
2017
+ * @param {string|Element|NodeList|Array} target - Target element(s)
2018
+ * @param {Object} options - Configuration options
2019
+ * @returns {Array} Array of OverType instances
2020
+ */
2021
+ constructor(target, options = {}) {
2022
+ let elements;
2023
+ if (typeof target === "string") {
2024
+ elements = document.querySelectorAll(target);
2025
+ if (elements.length === 0) {
2026
+ throw new Error(`No elements found for selector: ${target}`);
2027
+ }
2028
+ elements = Array.from(elements);
2029
+ } else if (target instanceof Element) {
2030
+ elements = [target];
2031
+ } else if (target instanceof NodeList) {
2032
+ elements = Array.from(target);
2033
+ } else if (Array.isArray(target)) {
2034
+ elements = target;
2035
+ } else {
2036
+ throw new Error("Invalid target: must be selector string, Element, NodeList, or Array");
2037
+ }
2038
+ const instances = elements.map((element) => {
2039
+ if (element.overTypeInstance) {
2040
+ element.overTypeInstance.reinit(options);
2041
+ return element.overTypeInstance;
2042
+ }
2043
+ const instance = Object.create(_OverType.prototype);
2044
+ instance._init(element, options);
2045
+ element.overTypeInstance = instance;
2046
+ _OverType.instances.set(element, instance);
2047
+ return instance;
2048
+ });
2049
+ return instances;
2050
+ }
2051
+ /**
2052
+ * Internal initialization
2053
+ * @private
2054
+ */
2055
+ _init(element, options = {}) {
2056
+ this.element = element;
2057
+ this.options = this._mergeOptions(options);
2058
+ this.instanceId = ++_OverType.instanceCount;
2059
+ this.initialized = false;
2060
+ _OverType.injectStyles();
2061
+ _OverType.initGlobalListeners();
2062
+ const container = element.querySelector(".overtype-container");
2063
+ const wrapper = element.querySelector(".overtype-wrapper");
2064
+ if (container || wrapper) {
2065
+ this._recoverFromDOM(container, wrapper);
2066
+ } else {
2067
+ this._buildFromScratch();
2068
+ }
2069
+ this.shortcuts = new ShortcutsManager(this);
2070
+ if (this.options.toolbar) {
2071
+ this.toolbar = new Toolbar(this);
2072
+ this.toolbar.create();
2073
+ this.textarea.addEventListener("selectionchange", () => {
2074
+ this.toolbar.updateButtonStates();
2075
+ });
2076
+ this.textarea.addEventListener("input", () => {
2077
+ this.toolbar.updateButtonStates();
2078
+ });
2079
+ }
2080
+ this.initialized = true;
2081
+ if (this.options.onChange) {
2082
+ this.options.onChange(this.getValue(), this);
2083
+ }
2084
+ }
2085
+ /**
2086
+ * Merge user options with defaults
2087
+ * @private
2088
+ */
2089
+ _mergeOptions(options) {
2090
+ const defaults = {
2091
+ // Typography
2092
+ fontSize: "14px",
2093
+ lineHeight: 1.6,
2094
+ fontFamily: "'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace",
2095
+ padding: "16px",
2096
+ // Mobile styles
2097
+ mobile: {
2098
+ fontSize: "16px",
2099
+ // Prevent zoom on iOS
2100
+ padding: "12px",
2101
+ lineHeight: 1.5
2102
+ },
2103
+ // Behavior
2104
+ autofocus: false,
2105
+ placeholder: "Start typing...",
2106
+ value: "",
2107
+ // Callbacks
2108
+ onChange: null,
2109
+ onKeydown: null,
2110
+ // Features
2111
+ showActiveLineRaw: false,
2112
+ showStats: false,
2113
+ toolbar: false,
2114
+ statsFormatter: null
2115
+ };
2116
+ const { theme, colors, ...cleanOptions } = options;
2117
+ return {
2118
+ ...defaults,
2119
+ ...cleanOptions
2120
+ };
2121
+ }
2122
+ /**
2123
+ * Recover from existing DOM structure
2124
+ * @private
2125
+ */
2126
+ _recoverFromDOM(container, wrapper) {
2127
+ if (container && container.classList.contains("overtype-container")) {
2128
+ this.container = container;
2129
+ this.wrapper = container.querySelector(".overtype-wrapper");
2130
+ } else if (wrapper) {
2131
+ this.wrapper = wrapper;
2132
+ this.container = document.createElement("div");
2133
+ this.container.className = "overtype-container";
2134
+ const currentTheme = _OverType.currentTheme || solar;
2135
+ const themeName = typeof currentTheme === "string" ? currentTheme : currentTheme.name;
2136
+ if (themeName) {
2137
+ this.container.setAttribute("data-theme", themeName);
2138
+ }
2139
+ wrapper.parentNode.insertBefore(this.container, wrapper);
2140
+ this.container.appendChild(wrapper);
2141
+ }
2142
+ if (!this.wrapper) {
2143
+ if (container)
2144
+ container.remove();
2145
+ if (wrapper)
2146
+ wrapper.remove();
2147
+ this._buildFromScratch();
2148
+ return;
2149
+ }
2150
+ this.textarea = this.wrapper.querySelector(".overtype-input");
2151
+ this.preview = this.wrapper.querySelector(".overtype-preview");
2152
+ if (!this.textarea || !this.preview) {
2153
+ this.container.remove();
2154
+ this._buildFromScratch();
2155
+ return;
2156
+ }
2157
+ this.wrapper._instance = this;
2158
+ if (this.options.fontSize) {
2159
+ this.wrapper.style.setProperty("--instance-font-size", this.options.fontSize);
2160
+ }
2161
+ if (this.options.lineHeight) {
2162
+ this.wrapper.style.setProperty("--instance-line-height", String(this.options.lineHeight));
2163
+ }
2164
+ if (this.options.padding) {
2165
+ this.wrapper.style.setProperty("--instance-padding", this.options.padding);
2166
+ }
2167
+ this._configureTextarea();
2168
+ this._applyOptions();
2169
+ }
2170
+ /**
2171
+ * Build editor from scratch
2172
+ * @private
2173
+ */
2174
+ _buildFromScratch() {
2175
+ const content = this._extractContent();
2176
+ this.element.innerHTML = "";
2177
+ this._createDOM();
2178
+ if (content || this.options.value) {
2179
+ this.setValue(content || this.options.value);
2180
+ }
2181
+ this._applyOptions();
2182
+ }
2183
+ /**
2184
+ * Extract content from element
2185
+ * @private
2186
+ */
2187
+ _extractContent() {
2188
+ const textarea = this.element.querySelector(".overtype-input");
2189
+ if (textarea)
2190
+ return textarea.value;
2191
+ return this.element.textContent || "";
2192
+ }
2193
+ /**
2194
+ * Create DOM structure
2195
+ * @private
2196
+ */
2197
+ _createDOM() {
2198
+ this.container = document.createElement("div");
2199
+ this.container.className = "overtype-container";
2200
+ const currentTheme = _OverType.currentTheme || solar;
2201
+ const themeName = typeof currentTheme === "string" ? currentTheme : currentTheme.name;
2202
+ if (themeName) {
2203
+ this.container.setAttribute("data-theme", themeName);
2204
+ }
2205
+ this.wrapper = document.createElement("div");
2206
+ this.wrapper.className = "overtype-wrapper";
2207
+ if (this.options.showStats) {
2208
+ this.wrapper.classList.add("with-stats");
2209
+ }
2210
+ if (this.options.fontSize) {
2211
+ this.wrapper.style.setProperty("--instance-font-size", this.options.fontSize);
2212
+ }
2213
+ if (this.options.lineHeight) {
2214
+ this.wrapper.style.setProperty("--instance-line-height", String(this.options.lineHeight));
2215
+ }
2216
+ if (this.options.padding) {
2217
+ this.wrapper.style.setProperty("--instance-padding", this.options.padding);
2218
+ }
2219
+ this.wrapper._instance = this;
2220
+ this.textarea = document.createElement("textarea");
2221
+ this.textarea.className = "overtype-input";
2222
+ this.textarea.placeholder = this.options.placeholder;
2223
+ this._configureTextarea();
2224
+ this.preview = document.createElement("div");
2225
+ this.preview.className = "overtype-preview";
2226
+ this.preview.setAttribute("aria-hidden", "true");
2227
+ this.wrapper.appendChild(this.textarea);
2228
+ this.wrapper.appendChild(this.preview);
2229
+ if (this.options.showStats) {
2230
+ this.statsBar = document.createElement("div");
2231
+ this.statsBar.className = "overtype-stats";
2232
+ this.wrapper.appendChild(this.statsBar);
2233
+ this._updateStats();
2234
+ }
2235
+ this.container.appendChild(this.wrapper);
2236
+ this.element.appendChild(this.container);
2237
+ }
2238
+ /**
2239
+ * Configure textarea attributes
2240
+ * @private
2241
+ */
2242
+ _configureTextarea() {
2243
+ this.textarea.setAttribute("autocomplete", "off");
2244
+ this.textarea.setAttribute("autocorrect", "off");
2245
+ this.textarea.setAttribute("autocapitalize", "off");
2246
+ this.textarea.setAttribute("spellcheck", "false");
2247
+ this.textarea.setAttribute("data-gramm", "false");
2248
+ this.textarea.setAttribute("data-gramm_editor", "false");
2249
+ this.textarea.setAttribute("data-enable-grammarly", "false");
2250
+ }
2251
+ /**
2252
+ * Apply options to the editor
2253
+ * @private
2254
+ */
2255
+ _applyOptions() {
2256
+ if (this.options.autofocus) {
2257
+ this.textarea.focus();
2258
+ }
2259
+ this.updatePreview();
2260
+ }
2261
+ /**
2262
+ * Update preview with parsed markdown
2263
+ */
2264
+ updatePreview() {
2265
+ const text = this.textarea.value;
2266
+ const cursorPos = this.textarea.selectionStart;
2267
+ const activeLine = this._getCurrentLine(text, cursorPos);
2268
+ const html = MarkdownParser.parse(text, activeLine, this.options.showActiveLineRaw);
2269
+ this.preview.innerHTML = html || '<span style="color: #808080;">Start typing...</span>';
2270
+ this._applyCodeBlockBackgrounds();
2271
+ if (this.options.showStats && this.statsBar) {
2272
+ this._updateStats();
2273
+ }
2274
+ if (this.options.onChange && this.initialized) {
2275
+ this.options.onChange(text, this);
2276
+ }
2277
+ }
2278
+ /**
2279
+ * Apply background styling to code blocks
2280
+ * @private
2281
+ */
2282
+ _applyCodeBlockBackgrounds() {
2283
+ const codeFences = this.preview.querySelectorAll(".code-fence");
2284
+ for (let i = 0; i < codeFences.length - 1; i += 2) {
2285
+ const openFence = codeFences[i];
2286
+ const closeFence = codeFences[i + 1];
2287
+ const openParent = openFence.parentElement;
2288
+ const closeParent = closeFence.parentElement;
2289
+ if (!openParent || !closeParent)
2290
+ continue;
2291
+ openFence.style.display = "block";
2292
+ closeFence.style.display = "block";
2293
+ openParent.classList.add("code-block-line");
2294
+ closeParent.classList.add("code-block-line");
2295
+ let currentDiv = openParent.nextElementSibling;
2296
+ while (currentDiv && currentDiv !== closeParent) {
2297
+ if (currentDiv.tagName === "DIV") {
2298
+ currentDiv.classList.add("code-block-line");
2299
+ }
2300
+ currentDiv = currentDiv.nextElementSibling;
2301
+ if (!currentDiv)
2302
+ break;
2303
+ }
2304
+ }
2305
+ }
2306
+ /**
2307
+ * Get current line number from cursor position
2308
+ * @private
2309
+ */
2310
+ _getCurrentLine(text, cursorPos) {
2311
+ const lines = text.substring(0, cursorPos).split("\n");
2312
+ return lines.length - 1;
2313
+ }
2314
+ /**
2315
+ * Handle input events
2316
+ * @private
2317
+ */
2318
+ handleInput(event) {
2319
+ this.updatePreview();
2320
+ }
2321
+ /**
2322
+ * Handle keydown events
2323
+ * @private
2324
+ */
2325
+ handleKeydown(event) {
2326
+ const handled = this.shortcuts.handleKeydown(event);
2327
+ if (!handled && this.options.onKeydown) {
2328
+ this.options.onKeydown(event, this);
2329
+ }
2330
+ }
2331
+ /**
2332
+ * Handle scroll events
2333
+ * @private
2334
+ */
2335
+ handleScroll(event) {
2336
+ this.preview.scrollTop = this.textarea.scrollTop;
2337
+ this.preview.scrollLeft = this.textarea.scrollLeft;
2338
+ }
2339
+ /**
2340
+ * Get editor content
2341
+ * @returns {string} Current markdown content
2342
+ */
2343
+ getValue() {
2344
+ return this.textarea.value;
2345
+ }
2346
+ /**
2347
+ * Set editor content
2348
+ * @param {string} value - Markdown content to set
2349
+ */
2350
+ setValue(value) {
2351
+ this.textarea.value = value;
2352
+ this.updatePreview();
2353
+ }
2354
+ /**
2355
+ * Focus the editor
2356
+ */
2357
+ focus() {
2358
+ this.textarea.focus();
2359
+ }
2360
+ /**
2361
+ * Blur the editor
2362
+ */
2363
+ blur() {
2364
+ this.textarea.blur();
2365
+ }
2366
+ /**
2367
+ * Check if editor is initialized
2368
+ * @returns {boolean}
2369
+ */
2370
+ isInitialized() {
2371
+ return this.initialized;
2372
+ }
2373
+ /**
2374
+ * Re-initialize with new options
2375
+ * @param {Object} options - New options to apply
2376
+ */
2377
+ reinit(options = {}) {
2378
+ this.options = this._mergeOptions({ ...this.options, ...options });
2379
+ this._applyOptions();
2380
+ this.updatePreview();
2381
+ }
2382
+ /**
2383
+ * Update stats bar
2384
+ * @private
2385
+ */
2386
+ _updateStats() {
2387
+ if (!this.statsBar)
2388
+ return;
2389
+ const value = this.textarea.value;
2390
+ const lines = value.split("\n");
2391
+ const chars = value.length;
2392
+ const words = value.split(/\s+/).filter((w) => w.length > 0).length;
2393
+ const selectionStart = this.textarea.selectionStart;
2394
+ const beforeCursor = value.substring(0, selectionStart);
2395
+ const linesBeforeCursor = beforeCursor.split("\n");
2396
+ const currentLine = linesBeforeCursor.length;
2397
+ const currentColumn = linesBeforeCursor[linesBeforeCursor.length - 1].length + 1;
2398
+ if (this.options.statsFormatter) {
2399
+ this.statsBar.innerHTML = this.options.statsFormatter({
2400
+ chars,
2401
+ words,
2402
+ lines: lines.length,
2403
+ line: currentLine,
2404
+ column: currentColumn
2405
+ });
2406
+ } else {
2407
+ this.statsBar.innerHTML = `
2408
+ <div class="overtype-stat">
2409
+ <span class="live-dot"></span>
2410
+ <span>${chars} chars, ${words} words, ${lines.length} lines</span>
2411
+ </div>
2412
+ <div class="overtype-stat">Line ${currentLine}, Col ${currentColumn}</div>
2413
+ `;
2414
+ }
2415
+ }
2416
+ /**
2417
+ * Show or hide stats bar
2418
+ * @param {boolean} show - Whether to show stats
2419
+ */
2420
+ showStats(show) {
2421
+ this.options.showStats = show;
2422
+ if (show && !this.statsBar) {
2423
+ this.statsBar = document.createElement("div");
2424
+ this.statsBar.className = "overtype-stats";
2425
+ this.wrapper.appendChild(this.statsBar);
2426
+ this.wrapper.classList.add("with-stats");
2427
+ this._updateStats();
2428
+ } else if (!show && this.statsBar) {
2429
+ this.statsBar.remove();
2430
+ this.statsBar = null;
2431
+ this.wrapper.classList.remove("with-stats");
2432
+ }
2433
+ }
2434
+ /**
2435
+ * Destroy the editor instance
2436
+ */
2437
+ destroy() {
2438
+ this.element.overTypeInstance = null;
2439
+ _OverType.instances.delete(this.element);
2440
+ if (this.shortcuts) {
2441
+ this.shortcuts.destroy();
2442
+ }
2443
+ if (this.wrapper) {
2444
+ const content = this.getValue();
2445
+ this.wrapper.remove();
2446
+ this.element.textContent = content;
2447
+ }
2448
+ this.initialized = false;
2449
+ }
2450
+ // ===== Static Methods =====
2451
+ /**
2452
+ * Initialize multiple editors (static convenience method)
2453
+ * @param {string|Element|NodeList|Array} target - Target element(s)
2454
+ * @param {Object} options - Configuration options
2455
+ * @returns {Array} Array of OverType instances
2456
+ */
2457
+ static init(target, options = {}) {
2458
+ return new _OverType(target, options);
2459
+ }
2460
+ /**
2461
+ * Get instance from element
2462
+ * @param {Element} element - DOM element
2463
+ * @returns {OverType|null} OverType instance or null
2464
+ */
2465
+ static getInstance(element) {
2466
+ return element.overTypeInstance || _OverType.instances.get(element) || null;
2467
+ }
2468
+ /**
2469
+ * Destroy all instances
2470
+ */
2471
+ static destroyAll() {
2472
+ const elements = document.querySelectorAll("[data-overtype-instance]");
2473
+ elements.forEach((element) => {
2474
+ const instance = _OverType.getInstance(element);
2475
+ if (instance) {
2476
+ instance.destroy();
2477
+ }
2478
+ });
2479
+ }
2480
+ /**
2481
+ * Inject styles into the document
2482
+ * @param {boolean} force - Force re-injection
2483
+ */
2484
+ static injectStyles(force = false) {
2485
+ if (_OverType.stylesInjected && !force)
2486
+ return;
2487
+ const existing = document.querySelector("style.overtype-styles");
2488
+ if (existing) {
2489
+ existing.remove();
2490
+ }
2491
+ const theme = _OverType.currentTheme || solar;
2492
+ const styles = generateStyles({ theme });
2493
+ const styleEl = document.createElement("style");
2494
+ styleEl.className = "overtype-styles";
2495
+ styleEl.textContent = styles;
2496
+ document.head.appendChild(styleEl);
2497
+ _OverType.stylesInjected = true;
2498
+ }
2499
+ /**
2500
+ * Set global theme for all OverType instances
2501
+ * @param {string|Object} theme - Theme name or custom theme object
2502
+ * @param {Object} customColors - Optional color overrides
2503
+ */
2504
+ static setTheme(theme, customColors = null) {
2505
+ let themeObj = typeof theme === "string" ? getTheme(theme) : theme;
2506
+ if (customColors) {
2507
+ themeObj = mergeTheme(themeObj, customColors);
2508
+ }
2509
+ _OverType.currentTheme = themeObj;
2510
+ _OverType.injectStyles(true);
2511
+ document.querySelectorAll(".overtype-container").forEach((container) => {
2512
+ const themeName = typeof themeObj === "string" ? themeObj : themeObj.name;
2513
+ if (themeName) {
2514
+ container.setAttribute("data-theme", themeName);
2515
+ }
2516
+ });
2517
+ document.querySelectorAll(".overtype-wrapper").forEach((wrapper) => {
2518
+ if (!wrapper.closest(".overtype-container")) {
2519
+ const themeName = typeof themeObj === "string" ? themeObj : themeObj.name;
2520
+ if (themeName) {
2521
+ wrapper.setAttribute("data-theme", themeName);
2522
+ }
2523
+ }
2524
+ const instance = wrapper._instance;
2525
+ if (instance) {
2526
+ instance.updatePreview();
2527
+ }
2528
+ });
2529
+ }
2530
+ /**
2531
+ * Initialize global event listeners
2532
+ */
2533
+ static initGlobalListeners() {
2534
+ if (_OverType.globalListenersInitialized)
2535
+ return;
2536
+ document.addEventListener("input", (e) => {
2537
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
2538
+ const wrapper = e.target.closest(".overtype-wrapper");
2539
+ const instance = wrapper == null ? void 0 : wrapper._instance;
2540
+ if (instance)
2541
+ instance.handleInput(e);
2542
+ }
2543
+ });
2544
+ document.addEventListener("keydown", (e) => {
2545
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
2546
+ const wrapper = e.target.closest(".overtype-wrapper");
2547
+ const instance = wrapper == null ? void 0 : wrapper._instance;
2548
+ if (instance)
2549
+ instance.handleKeydown(e);
2550
+ }
2551
+ });
2552
+ document.addEventListener("scroll", (e) => {
2553
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
2554
+ const wrapper = e.target.closest(".overtype-wrapper");
2555
+ const instance = wrapper == null ? void 0 : wrapper._instance;
2556
+ if (instance)
2557
+ instance.handleScroll(e);
2558
+ }
2559
+ }, true);
2560
+ document.addEventListener("selectionchange", (e) => {
2561
+ const activeElement = document.activeElement;
2562
+ if (activeElement && activeElement.classList.contains("overtype-input")) {
2563
+ const wrapper = activeElement.closest(".overtype-wrapper");
2564
+ const instance = wrapper == null ? void 0 : wrapper._instance;
2565
+ if (instance) {
2566
+ if (instance.options.showStats && instance.statsBar) {
2567
+ instance._updateStats();
2568
+ }
2569
+ clearTimeout(instance._selectionTimeout);
2570
+ instance._selectionTimeout = setTimeout(() => {
2571
+ instance.updatePreview();
2572
+ }, 50);
2573
+ }
2574
+ }
2575
+ });
2576
+ _OverType.globalListenersInitialized = true;
2577
+ }
2578
+ };
2579
+ // Static properties
2580
+ __publicField(_OverType, "instances", /* @__PURE__ */ new WeakMap());
2581
+ __publicField(_OverType, "stylesInjected", false);
2582
+ __publicField(_OverType, "globalListenersInitialized", false);
2583
+ __publicField(_OverType, "instanceCount", 0);
2584
+ var OverType = _OverType;
2585
+ OverType.MarkdownParser = MarkdownParser;
2586
+ OverType.ShortcutsManager = ShortcutsManager;
2587
+ OverType.themes = { solar, cave: getTheme("cave") };
2588
+ OverType.getTheme = getTheme;
2589
+ OverType.currentTheme = solar;
2590
+ var overtype_default = OverType;
2591
+ return __toCommonJS(overtype_exports);
2592
+ })();
2593
+ /**
2594
+ * OverType - A lightweight markdown editor library with perfect WYSIWYG alignment
2595
+ * @version 1.0.0
2596
+ * @license MIT
2597
+ */
2598
+ window.OverType = OverType.OverType || OverType.default || OverType;
2599
+ //# sourceMappingURL=overtype.js.map