overtype 1.1.8 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3541 @@
1
+ /**
2
+ * OverType v1.2.1
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 __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
26
+ var __publicField = (obj, key, value) => {
27
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
28
+ return value;
29
+ };
30
+
31
+ // src/overtype.js
32
+ var overtype_exports = {};
33
+ __export(overtype_exports, {
34
+ OverType: () => OverType,
35
+ default: () => overtype_default
36
+ });
37
+ module.exports = __toCommonJS(overtype_exports);
38
+
39
+ // src/parser.js
40
+ var MarkdownParser = class {
41
+ /**
42
+ * Reset link index (call before parsing a new document)
43
+ */
44
+ static resetLinkIndex() {
45
+ this.linkIndex = 0;
46
+ }
47
+ /**
48
+ * Escape HTML special characters
49
+ * @param {string} text - Raw text to escape
50
+ * @returns {string} Escaped HTML-safe text
51
+ */
52
+ static escapeHtml(text) {
53
+ const map = {
54
+ "&": "&",
55
+ "<": "&lt;",
56
+ ">": "&gt;",
57
+ '"': "&quot;",
58
+ "'": "&#39;"
59
+ };
60
+ return text.replace(/[&<>"']/g, (m) => map[m]);
61
+ }
62
+ /**
63
+ * Preserve leading spaces as non-breaking spaces
64
+ * @param {string} html - HTML string
65
+ * @param {string} originalLine - Original line with spaces
66
+ * @returns {string} HTML with preserved indentation
67
+ */
68
+ static preserveIndentation(html, originalLine) {
69
+ const leadingSpaces = originalLine.match(/^(\s*)/)[1];
70
+ const indentation = leadingSpaces.replace(/ /g, "&nbsp;");
71
+ return html.replace(/^\s*/, indentation);
72
+ }
73
+ /**
74
+ * Parse headers (h1-h3 only)
75
+ * @param {string} html - HTML line to parse
76
+ * @returns {string} Parsed HTML with header styling
77
+ */
78
+ static parseHeader(html) {
79
+ return html.replace(/^(#{1,3})\s(.+)$/, (match, hashes, content) => {
80
+ const level = hashes.length;
81
+ return `<h${level}><span class="syntax-marker">${hashes} </span>${content}</h${level}>`;
82
+ });
83
+ }
84
+ /**
85
+ * Parse horizontal rules
86
+ * @param {string} html - HTML line to parse
87
+ * @returns {string|null} Parsed horizontal rule or null
88
+ */
89
+ static parseHorizontalRule(html) {
90
+ if (html.match(/^(-{3,}|\*{3,}|_{3,})$/)) {
91
+ return `<div><span class="hr-marker">${html}</span></div>`;
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * Parse blockquotes
97
+ * @param {string} html - HTML line to parse
98
+ * @returns {string} Parsed blockquote
99
+ */
100
+ static parseBlockquote(html) {
101
+ return html.replace(/^&gt; (.+)$/, (match, content) => {
102
+ return `<span class="blockquote"><span class="syntax-marker">&gt;</span> ${content}</span>`;
103
+ });
104
+ }
105
+ /**
106
+ * Parse bullet lists
107
+ * @param {string} html - HTML line to parse
108
+ * @returns {string} Parsed bullet list item
109
+ */
110
+ static parseBulletList(html) {
111
+ return html.replace(/^((?:&nbsp;)*)([-*])\s(.+)$/, (match, indent, marker, content) => {
112
+ return `${indent}<li class="bullet-list"><span class="syntax-marker">${marker} </span>${content}</li>`;
113
+ });
114
+ }
115
+ /**
116
+ * Parse numbered lists
117
+ * @param {string} html - HTML line to parse
118
+ * @returns {string} Parsed numbered list item
119
+ */
120
+ static parseNumberedList(html) {
121
+ return html.replace(/^((?:&nbsp;)*)(\d+\.)\s(.+)$/, (match, indent, marker, content) => {
122
+ return `${indent}<li class="ordered-list"><span class="syntax-marker">${marker} </span>${content}</li>`;
123
+ });
124
+ }
125
+ /**
126
+ * Parse code blocks (markers only)
127
+ * @param {string} html - HTML line to parse
128
+ * @returns {string|null} Parsed code fence or null
129
+ */
130
+ static parseCodeBlock(html) {
131
+ const codeFenceRegex = /^`{3}[^`]*$/;
132
+ if (codeFenceRegex.test(html)) {
133
+ return `<div><span class="code-fence">${html}</span></div>`;
134
+ }
135
+ return null;
136
+ }
137
+ /**
138
+ * Parse bold text
139
+ * @param {string} html - HTML with potential bold markdown
140
+ * @returns {string} HTML with bold styling
141
+ */
142
+ static parseBold(html) {
143
+ html = html.replace(/\*\*(.+?)\*\*/g, '<strong><span class="syntax-marker">**</span>$1<span class="syntax-marker">**</span></strong>');
144
+ html = html.replace(/__(.+?)__/g, '<strong><span class="syntax-marker">__</span>$1<span class="syntax-marker">__</span></strong>');
145
+ return html;
146
+ }
147
+ /**
148
+ * Parse italic text
149
+ * Note: Uses lookbehind assertions - requires modern browsers
150
+ * @param {string} html - HTML with potential italic markdown
151
+ * @returns {string} HTML with italic styling
152
+ */
153
+ static parseItalic(html) {
154
+ html = html.replace(new RegExp("(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)", "g"), '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
155
+ html = html.replace(new RegExp("(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", "g"), '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
156
+ return html;
157
+ }
158
+ /**
159
+ * Parse inline code
160
+ * @param {string} html - HTML with potential code markdown
161
+ * @returns {string} HTML with code styling
162
+ */
163
+ static parseInlineCode(html) {
164
+ return html.replace(new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)", "g"), '<code><span class="syntax-marker">$1</span>$2<span class="syntax-marker">$3</span></code>');
165
+ }
166
+ /**
167
+ * Sanitize URL to prevent XSS attacks
168
+ * @param {string} url - URL to sanitize
169
+ * @returns {string} Safe URL or '#' if dangerous
170
+ */
171
+ static sanitizeUrl(url) {
172
+ const trimmed = url.trim();
173
+ const lower = trimmed.toLowerCase();
174
+ const safeProtocols = [
175
+ "http://",
176
+ "https://",
177
+ "mailto:",
178
+ "ftp://",
179
+ "ftps://"
180
+ ];
181
+ const hasSafeProtocol = safeProtocols.some((protocol) => lower.startsWith(protocol));
182
+ const isRelative = trimmed.startsWith("/") || trimmed.startsWith("#") || trimmed.startsWith("?") || trimmed.startsWith(".") || !trimmed.includes(":") && !trimmed.includes("//");
183
+ if (hasSafeProtocol || isRelative) {
184
+ return url;
185
+ }
186
+ return "#";
187
+ }
188
+ /**
189
+ * Parse links
190
+ * @param {string} html - HTML with potential link markdown
191
+ * @returns {string} HTML with link styling
192
+ */
193
+ static parseLinks(html) {
194
+ return html.replace(/\[(.+?)\]\((.+?)\)/g, (match, text, url) => {
195
+ const anchorName = `--link-${this.linkIndex++}`;
196
+ const safeUrl = this.sanitizeUrl(url);
197
+ return `<a href="${safeUrl}" style="anchor-name: ${anchorName}"><span class="syntax-marker">[</span>${text}<span class="syntax-marker url-part">](${url})</span></a>`;
198
+ });
199
+ }
200
+ /**
201
+ * Parse all inline elements in correct order
202
+ * @param {string} text - Text with potential inline markdown
203
+ * @returns {string} HTML with all inline styling
204
+ */
205
+ static parseInlineElements(text) {
206
+ let html = text;
207
+ html = this.parseInlineCode(html);
208
+ const sanctuaries = /* @__PURE__ */ new Map();
209
+ html = html.replace(/(<code>.*?<\/code>)/g, (match) => {
210
+ const placeholder = `\uE000${sanctuaries.size}\uE001`;
211
+ sanctuaries.set(placeholder, match);
212
+ return placeholder;
213
+ });
214
+ html = this.parseLinks(html);
215
+ html = html.replace(/(<a[^>]*>.*?<\/a>)/g, (match) => {
216
+ const placeholder = `\uE000${sanctuaries.size}\uE001`;
217
+ sanctuaries.set(placeholder, match);
218
+ return placeholder;
219
+ });
220
+ html = this.parseBold(html);
221
+ html = this.parseItalic(html);
222
+ sanctuaries.forEach((content, placeholder) => {
223
+ html = html.replace(placeholder, content);
224
+ });
225
+ return html;
226
+ }
227
+ /**
228
+ * Parse a single line of markdown
229
+ * @param {string} line - Raw markdown line
230
+ * @returns {string} Parsed HTML line
231
+ */
232
+ static parseLine(line) {
233
+ let html = this.escapeHtml(line);
234
+ html = this.preserveIndentation(html, line);
235
+ const horizontalRule = this.parseHorizontalRule(html);
236
+ if (horizontalRule)
237
+ return horizontalRule;
238
+ const codeBlock = this.parseCodeBlock(html);
239
+ if (codeBlock)
240
+ return codeBlock;
241
+ html = this.parseHeader(html);
242
+ html = this.parseBlockquote(html);
243
+ html = this.parseBulletList(html);
244
+ html = this.parseNumberedList(html);
245
+ html = this.parseInlineElements(html);
246
+ if (html.trim() === "") {
247
+ return "<div>&nbsp;</div>";
248
+ }
249
+ return `<div>${html}</div>`;
250
+ }
251
+ /**
252
+ * Parse full markdown text
253
+ * @param {string} text - Full markdown text
254
+ * @param {number} activeLine - Currently active line index (optional)
255
+ * @param {boolean} showActiveLineRaw - Show raw markdown on active line
256
+ * @returns {string} Parsed HTML
257
+ */
258
+ static parse(text, activeLine = -1, showActiveLineRaw = false) {
259
+ this.resetLinkIndex();
260
+ const lines = text.split("\n");
261
+ const parsedLines = lines.map((line, index) => {
262
+ if (showActiveLineRaw && index === activeLine) {
263
+ const content = this.escapeHtml(line) || "&nbsp;";
264
+ return `<div class="raw-line">${content}</div>`;
265
+ }
266
+ return this.parseLine(line);
267
+ });
268
+ const html = parsedLines.join("");
269
+ return this.postProcessHTML(html);
270
+ }
271
+ /**
272
+ * Post-process HTML to consolidate lists and code blocks
273
+ * @param {string} html - HTML to post-process
274
+ * @returns {string} Post-processed HTML with consolidated lists and code blocks
275
+ */
276
+ static postProcessHTML(html) {
277
+ if (typeof document === "undefined" || !document) {
278
+ return this.postProcessHTMLManual(html);
279
+ }
280
+ const container = document.createElement("div");
281
+ container.innerHTML = html;
282
+ let currentList = null;
283
+ let listType = null;
284
+ let currentCodeBlock = null;
285
+ let inCodeBlock = false;
286
+ const children = Array.from(container.children);
287
+ for (let i = 0; i < children.length; i++) {
288
+ const child = children[i];
289
+ if (!child.parentNode)
290
+ continue;
291
+ const codeFence = child.querySelector(".code-fence");
292
+ if (codeFence) {
293
+ const fenceText = codeFence.textContent;
294
+ if (fenceText.startsWith("```")) {
295
+ if (!inCodeBlock) {
296
+ inCodeBlock = true;
297
+ currentCodeBlock = document.createElement("pre");
298
+ const codeElement = document.createElement("code");
299
+ currentCodeBlock.appendChild(codeElement);
300
+ currentCodeBlock.className = "code-block";
301
+ const lang = fenceText.slice(3).trim();
302
+ if (lang) {
303
+ codeElement.className = `language-${lang}`;
304
+ }
305
+ container.insertBefore(currentCodeBlock, child);
306
+ child.remove();
307
+ continue;
308
+ } else {
309
+ inCodeBlock = false;
310
+ currentCodeBlock = null;
311
+ child.remove();
312
+ continue;
313
+ }
314
+ }
315
+ }
316
+ if (inCodeBlock && currentCodeBlock && child.tagName === "DIV" && !child.querySelector(".code-fence")) {
317
+ const codeElement = currentCodeBlock.querySelector("code");
318
+ if (codeElement.textContent.length > 0) {
319
+ codeElement.textContent += "\n";
320
+ }
321
+ const lineText = child.innerHTML.replace(/&nbsp;/g, " ").replace(/<[^>]*>/g, "");
322
+ codeElement.textContent += lineText;
323
+ child.remove();
324
+ continue;
325
+ }
326
+ let listItem = null;
327
+ if (child.tagName === "DIV") {
328
+ listItem = child.querySelector("li");
329
+ }
330
+ if (listItem) {
331
+ const isBullet = listItem.classList.contains("bullet-list");
332
+ const isOrdered = listItem.classList.contains("ordered-list");
333
+ if (!isBullet && !isOrdered) {
334
+ currentList = null;
335
+ listType = null;
336
+ continue;
337
+ }
338
+ const newType = isBullet ? "ul" : "ol";
339
+ if (!currentList || listType !== newType) {
340
+ currentList = document.createElement(newType);
341
+ container.insertBefore(currentList, child);
342
+ listType = newType;
343
+ }
344
+ currentList.appendChild(listItem);
345
+ child.remove();
346
+ } else {
347
+ currentList = null;
348
+ listType = null;
349
+ }
350
+ }
351
+ return container.innerHTML;
352
+ }
353
+ /**
354
+ * Manual post-processing for Node.js environments (without DOM)
355
+ * @param {string} html - HTML to post-process
356
+ * @returns {string} Post-processed HTML
357
+ */
358
+ static postProcessHTMLManual(html) {
359
+ let processed = html;
360
+ processed = processed.replace(/((?:<div>(?:&nbsp;)*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
361
+ const items = match.match(/<li class="bullet-list">.*?<\/li>/gs) || [];
362
+ if (items.length > 0) {
363
+ return "<ul>" + items.join("") + "</ul>";
364
+ }
365
+ return match;
366
+ });
367
+ processed = processed.replace(/((?:<div>(?:&nbsp;)*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs, (match) => {
368
+ const items = match.match(/<li class="ordered-list">.*?<\/li>/gs) || [];
369
+ if (items.length > 0) {
370
+ return "<ol>" + items.join("") + "</ol>";
371
+ }
372
+ return match;
373
+ });
374
+ const codeBlockRegex = /<div><span class="code-fence">```([^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">```<\/span><\/div>/gs;
375
+ processed = processed.replace(codeBlockRegex, (match, lang, content) => {
376
+ const lines = content.match(/<div>(.*?)<\/div>/gs) || [];
377
+ const codeContent = lines.map((line) => {
378
+ const text = line.replace(/<div>(.*?)<\/div>/s, "$1").replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&amp;/g, "&");
379
+ return text;
380
+ }).join("\n");
381
+ const langClass = lang ? ` class="language-${lang.trim()}"` : "";
382
+ return `<pre class="code-block"><code${langClass}>${this.escapeHtml(codeContent)}</code></pre>`;
383
+ });
384
+ return processed;
385
+ }
386
+ };
387
+ // Track link index for anchor naming
388
+ __publicField(MarkdownParser, "linkIndex", 0);
389
+
390
+ // node_modules/markdown-actions/dist/markdown-actions.esm.js
391
+ var __defProp2 = Object.defineProperty;
392
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
393
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
394
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
395
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
396
+ var __spreadValues = (a, b) => {
397
+ for (var prop in b || (b = {}))
398
+ if (__hasOwnProp2.call(b, prop))
399
+ __defNormalProp2(a, prop, b[prop]);
400
+ if (__getOwnPropSymbols)
401
+ for (var prop of __getOwnPropSymbols(b)) {
402
+ if (__propIsEnum.call(b, prop))
403
+ __defNormalProp2(a, prop, b[prop]);
404
+ }
405
+ return a;
406
+ };
407
+ var FORMATS = {
408
+ bold: {
409
+ prefix: "**",
410
+ suffix: "**",
411
+ trimFirst: true
412
+ },
413
+ italic: {
414
+ prefix: "_",
415
+ suffix: "_",
416
+ trimFirst: true
417
+ },
418
+ code: {
419
+ prefix: "`",
420
+ suffix: "`",
421
+ blockPrefix: "```",
422
+ blockSuffix: "```"
423
+ },
424
+ link: {
425
+ prefix: "[",
426
+ suffix: "](url)",
427
+ replaceNext: "url",
428
+ scanFor: "https?://"
429
+ },
430
+ bulletList: {
431
+ prefix: "- ",
432
+ multiline: true,
433
+ unorderedList: true
434
+ },
435
+ numberedList: {
436
+ prefix: "1. ",
437
+ multiline: true,
438
+ orderedList: true
439
+ },
440
+ quote: {
441
+ prefix: "> ",
442
+ multiline: true,
443
+ surroundWithNewlines: true
444
+ },
445
+ taskList: {
446
+ prefix: "- [ ] ",
447
+ multiline: true,
448
+ surroundWithNewlines: true
449
+ },
450
+ header1: { prefix: "# " },
451
+ header2: { prefix: "## " },
452
+ header3: { prefix: "### " },
453
+ header4: { prefix: "#### " },
454
+ header5: { prefix: "##### " },
455
+ header6: { prefix: "###### " }
456
+ };
457
+ function getDefaultStyle() {
458
+ return {
459
+ prefix: "",
460
+ suffix: "",
461
+ blockPrefix: "",
462
+ blockSuffix: "",
463
+ multiline: false,
464
+ replaceNext: "",
465
+ prefixSpace: false,
466
+ scanFor: "",
467
+ surroundWithNewlines: false,
468
+ orderedList: false,
469
+ unorderedList: false,
470
+ trimFirst: false
471
+ };
472
+ }
473
+ function mergeWithDefaults(format) {
474
+ return __spreadValues(__spreadValues({}, getDefaultStyle()), format);
475
+ }
476
+ var debugMode = false;
477
+ function getDebugMode() {
478
+ return debugMode;
479
+ }
480
+ function debugLog(funcName, message, data) {
481
+ if (!debugMode)
482
+ return;
483
+ console.group(`\u{1F50D} ${funcName}`);
484
+ console.log(message);
485
+ if (data) {
486
+ console.log("Data:", data);
487
+ }
488
+ console.groupEnd();
489
+ }
490
+ function debugSelection(textarea, label) {
491
+ if (!debugMode)
492
+ return;
493
+ const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
494
+ console.group(`\u{1F4CD} Selection: ${label}`);
495
+ console.log("Position:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
496
+ console.log("Selected text:", JSON.stringify(selected));
497
+ console.log("Length:", selected.length);
498
+ const before = textarea.value.slice(Math.max(0, textarea.selectionStart - 10), textarea.selectionStart);
499
+ const after = textarea.value.slice(textarea.selectionEnd, Math.min(textarea.value.length, textarea.selectionEnd + 10));
500
+ console.log("Context:", JSON.stringify(before) + "[SELECTION]" + JSON.stringify(after));
501
+ console.groupEnd();
502
+ }
503
+ function debugResult(result) {
504
+ if (!debugMode)
505
+ return;
506
+ console.group("\u{1F4DD} Result");
507
+ console.log("Text to insert:", JSON.stringify(result.text));
508
+ console.log("New selection:", `${result.selectionStart}-${result.selectionEnd}`);
509
+ console.groupEnd();
510
+ }
511
+ var canInsertText = null;
512
+ function insertText(textarea, { text, selectionStart, selectionEnd }) {
513
+ const debugMode2 = getDebugMode();
514
+ if (debugMode2) {
515
+ console.group("\u{1F527} insertText");
516
+ console.log("Current selection:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
517
+ console.log("Text to insert:", JSON.stringify(text));
518
+ console.log("New selection to set:", selectionStart, "-", selectionEnd);
519
+ }
520
+ textarea.focus();
521
+ const originalSelectionStart = textarea.selectionStart;
522
+ const originalSelectionEnd = textarea.selectionEnd;
523
+ const before = textarea.value.slice(0, originalSelectionStart);
524
+ const after = textarea.value.slice(originalSelectionEnd);
525
+ if (debugMode2) {
526
+ console.log("Before text (last 20):", JSON.stringify(before.slice(-20)));
527
+ console.log("After text (first 20):", JSON.stringify(after.slice(0, 20)));
528
+ console.log("Selected text being replaced:", JSON.stringify(textarea.value.slice(originalSelectionStart, originalSelectionEnd)));
529
+ }
530
+ const originalValue = textarea.value;
531
+ const hasSelection = originalSelectionStart !== originalSelectionEnd;
532
+ if (canInsertText === null || canInsertText === true) {
533
+ textarea.contentEditable = "true";
534
+ try {
535
+ canInsertText = document.execCommand("insertText", false, text);
536
+ if (debugMode2)
537
+ console.log("execCommand returned:", canInsertText, "for text with", text.split("\n").length, "lines");
538
+ } catch (error) {
539
+ canInsertText = false;
540
+ if (debugMode2)
541
+ console.log("execCommand threw error:", error);
542
+ }
543
+ textarea.contentEditable = "false";
544
+ }
545
+ if (debugMode2) {
546
+ console.log("canInsertText before:", canInsertText);
547
+ console.log("execCommand result:", canInsertText);
548
+ }
549
+ if (canInsertText) {
550
+ const expectedValue = before + text + after;
551
+ const actualValue = textarea.value;
552
+ if (debugMode2) {
553
+ console.log("Expected length:", expectedValue.length);
554
+ console.log("Actual length:", actualValue.length);
555
+ }
556
+ if (actualValue !== expectedValue) {
557
+ if (debugMode2) {
558
+ console.log("execCommand changed the value but not as expected");
559
+ console.log("Expected:", JSON.stringify(expectedValue.slice(0, 100)));
560
+ console.log("Actual:", JSON.stringify(actualValue.slice(0, 100)));
561
+ }
562
+ }
563
+ }
564
+ if (!canInsertText) {
565
+ if (debugMode2)
566
+ console.log("Using manual insertion");
567
+ if (textarea.value === originalValue) {
568
+ if (debugMode2)
569
+ console.log("Value unchanged, doing manual replacement");
570
+ try {
571
+ document.execCommand("ms-beginUndoUnit");
572
+ } catch (e) {
573
+ }
574
+ textarea.value = before + text + after;
575
+ try {
576
+ document.execCommand("ms-endUndoUnit");
577
+ } catch (e) {
578
+ }
579
+ textarea.dispatchEvent(new CustomEvent("input", { bubbles: true, cancelable: true }));
580
+ } else {
581
+ if (debugMode2)
582
+ console.log("Value was changed by execCommand, skipping manual insertion");
583
+ }
584
+ }
585
+ if (debugMode2)
586
+ console.log("Setting selection range:", selectionStart, selectionEnd);
587
+ if (selectionStart != null && selectionEnd != null) {
588
+ textarea.setSelectionRange(selectionStart, selectionEnd);
589
+ } else {
590
+ textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd);
591
+ }
592
+ if (debugMode2) {
593
+ console.log("Final value length:", textarea.value.length);
594
+ console.groupEnd();
595
+ }
596
+ }
597
+ function isMultipleLines(string) {
598
+ return string.trim().split("\n").length > 1;
599
+ }
600
+ function wordSelectionStart(text, i) {
601
+ let index = i;
602
+ while (text[index] && text[index - 1] != null && !text[index - 1].match(/\s/)) {
603
+ index--;
604
+ }
605
+ return index;
606
+ }
607
+ function wordSelectionEnd(text, i, multiline) {
608
+ let index = i;
609
+ const breakpoint = multiline ? /\n/ : /\s/;
610
+ while (text[index] && !text[index].match(breakpoint)) {
611
+ index++;
612
+ }
613
+ return index;
614
+ }
615
+ function expandSelectionToLine(textarea) {
616
+ const lines = textarea.value.split("\n");
617
+ let counter = 0;
618
+ for (let index = 0; index < lines.length; index++) {
619
+ const lineLength = lines[index].length + 1;
620
+ if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
621
+ textarea.selectionStart = counter;
622
+ }
623
+ if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
624
+ if (index === lines.length - 1) {
625
+ textarea.selectionEnd = Math.min(counter + lines[index].length, textarea.value.length);
626
+ } else {
627
+ textarea.selectionEnd = counter + lineLength - 1;
628
+ }
629
+ }
630
+ counter += lineLength;
631
+ }
632
+ }
633
+ function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {
634
+ if (textarea.selectionStart === textarea.selectionEnd) {
635
+ textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
636
+ textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline);
637
+ } else {
638
+ const expandedSelectionStart = textarea.selectionStart - prefixToUse.length;
639
+ const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length;
640
+ const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse;
641
+ const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse;
642
+ if (beginsWithPrefix && endsWithSuffix) {
643
+ textarea.selectionStart = expandedSelectionStart;
644
+ textarea.selectionEnd = expandedSelectionEnd;
645
+ }
646
+ }
647
+ return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
648
+ }
649
+ function newlinesToSurroundSelectedText(textarea) {
650
+ const beforeSelection = textarea.value.slice(0, textarea.selectionStart);
651
+ const afterSelection = textarea.value.slice(textarea.selectionEnd);
652
+ const breaksBefore = beforeSelection.match(/\n*$/);
653
+ const breaksAfter = afterSelection.match(/^\n*/);
654
+ const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0;
655
+ const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0;
656
+ let newlinesToAppend = "";
657
+ let newlinesToPrepend = "";
658
+ if (beforeSelection.match(/\S/) && newlinesBeforeSelection < 2) {
659
+ newlinesToAppend = "\n".repeat(2 - newlinesBeforeSelection);
660
+ }
661
+ if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
662
+ newlinesToPrepend = "\n".repeat(2 - newlinesAfterSelection);
663
+ }
664
+ return { newlinesToAppend, newlinesToPrepend };
665
+ }
666
+ function applyLineOperation(textarea, operation, options = {}) {
667
+ const originalStart = textarea.selectionStart;
668
+ const originalEnd = textarea.selectionEnd;
669
+ const noInitialSelection = originalStart === originalEnd;
670
+ const value = textarea.value;
671
+ let lineStart = originalStart;
672
+ while (lineStart > 0 && value[lineStart - 1] !== "\n") {
673
+ lineStart--;
674
+ }
675
+ if (noInitialSelection) {
676
+ let lineEnd = originalStart;
677
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
678
+ lineEnd++;
679
+ }
680
+ textarea.selectionStart = lineStart;
681
+ textarea.selectionEnd = lineEnd;
682
+ } else {
683
+ expandSelectionToLine(textarea);
684
+ }
685
+ const result = operation(textarea);
686
+ if (options.adjustSelection) {
687
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
688
+ const isRemoving = selectedText.startsWith(options.prefix);
689
+ const adjusted = options.adjustSelection(isRemoving, originalStart, originalEnd, lineStart);
690
+ result.selectionStart = adjusted.start;
691
+ result.selectionEnd = adjusted.end;
692
+ } else if (options.prefix) {
693
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
694
+ const isRemoving = selectedText.startsWith(options.prefix);
695
+ if (noInitialSelection) {
696
+ if (isRemoving) {
697
+ result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
698
+ result.selectionEnd = result.selectionStart;
699
+ } else {
700
+ result.selectionStart = originalStart + options.prefix.length;
701
+ result.selectionEnd = result.selectionStart;
702
+ }
703
+ } else {
704
+ if (isRemoving) {
705
+ result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
706
+ result.selectionEnd = Math.max(originalEnd - options.prefix.length, lineStart);
707
+ } else {
708
+ result.selectionStart = originalStart + options.prefix.length;
709
+ result.selectionEnd = originalEnd + options.prefix.length;
710
+ }
711
+ }
712
+ }
713
+ return result;
714
+ }
715
+ function blockStyle(textarea, style) {
716
+ let newlinesToAppend;
717
+ let newlinesToPrepend;
718
+ const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines, trimFirst } = style;
719
+ const originalSelectionStart = textarea.selectionStart;
720
+ const originalSelectionEnd = textarea.selectionEnd;
721
+ let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
722
+ let prefixToUse = isMultipleLines(selectedText) && blockPrefix && blockPrefix.length > 0 ? `${blockPrefix}
723
+ ` : prefix;
724
+ let suffixToUse = isMultipleLines(selectedText) && blockSuffix && blockSuffix.length > 0 ? `
725
+ ${blockSuffix}` : suffix;
726
+ if (prefixSpace) {
727
+ const beforeSelection = textarea.value[textarea.selectionStart - 1];
728
+ if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\s/)) {
729
+ prefixToUse = ` ${prefixToUse}`;
730
+ }
731
+ }
732
+ selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, style.multiline);
733
+ let selectionStart = textarea.selectionStart;
734
+ let selectionEnd = textarea.selectionEnd;
735
+ const hasReplaceNext = replaceNext && replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0;
736
+ if (surroundWithNewlines) {
737
+ const ref = newlinesToSurroundSelectedText(textarea);
738
+ newlinesToAppend = ref.newlinesToAppend;
739
+ newlinesToPrepend = ref.newlinesToPrepend;
740
+ prefixToUse = newlinesToAppend + prefix;
741
+ suffixToUse += newlinesToPrepend;
742
+ }
743
+ if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {
744
+ const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length);
745
+ if (originalSelectionStart === originalSelectionEnd) {
746
+ let position = originalSelectionStart - prefixToUse.length;
747
+ position = Math.max(position, selectionStart);
748
+ position = Math.min(position, selectionStart + replacementText.length);
749
+ selectionStart = selectionEnd = position;
750
+ } else {
751
+ selectionEnd = selectionStart + replacementText.length;
752
+ }
753
+ return { text: replacementText, selectionStart, selectionEnd };
754
+ } else if (!hasReplaceNext) {
755
+ let replacementText = prefixToUse + selectedText + suffixToUse;
756
+ selectionStart = originalSelectionStart + prefixToUse.length;
757
+ selectionEnd = originalSelectionEnd + prefixToUse.length;
758
+ const whitespaceEdges = selectedText.match(/^\s*|\s*$/g);
759
+ if (trimFirst && whitespaceEdges) {
760
+ const leadingWhitespace = whitespaceEdges[0] || "";
761
+ const trailingWhitespace = whitespaceEdges[1] || "";
762
+ replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace;
763
+ selectionStart += leadingWhitespace.length;
764
+ selectionEnd -= trailingWhitespace.length;
765
+ }
766
+ return { text: replacementText, selectionStart, selectionEnd };
767
+ } else if (scanFor && scanFor.length > 0 && selectedText.match(scanFor)) {
768
+ suffixToUse = suffixToUse.replace(replaceNext, selectedText);
769
+ const replacementText = prefixToUse + suffixToUse;
770
+ selectionStart = selectionEnd = selectionStart + prefixToUse.length;
771
+ return { text: replacementText, selectionStart, selectionEnd };
772
+ } else {
773
+ const replacementText = prefixToUse + selectedText + suffixToUse;
774
+ selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext);
775
+ selectionEnd = selectionStart + replaceNext.length;
776
+ return { text: replacementText, selectionStart, selectionEnd };
777
+ }
778
+ }
779
+ function multilineStyle(textarea, style) {
780
+ const { prefix, suffix, surroundWithNewlines } = style;
781
+ let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
782
+ let selectionStart = textarea.selectionStart;
783
+ let selectionEnd = textarea.selectionEnd;
784
+ const lines = text.split("\n");
785
+ const undoStyle = lines.every((line) => line.startsWith(prefix) && (!suffix || line.endsWith(suffix)));
786
+ if (undoStyle) {
787
+ text = lines.map((line) => {
788
+ let result = line.slice(prefix.length);
789
+ if (suffix) {
790
+ result = result.slice(0, result.length - suffix.length);
791
+ }
792
+ return result;
793
+ }).join("\n");
794
+ selectionEnd = selectionStart + text.length;
795
+ } else {
796
+ text = lines.map((line) => prefix + line + (suffix || "")).join("\n");
797
+ if (surroundWithNewlines) {
798
+ const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
799
+ selectionStart += newlinesToAppend.length;
800
+ selectionEnd = selectionStart + text.length;
801
+ text = newlinesToAppend + text + newlinesToPrepend;
802
+ }
803
+ }
804
+ return { text, selectionStart, selectionEnd };
805
+ }
806
+ function undoOrderedListStyle(text) {
807
+ const lines = text.split("\n");
808
+ const orderedListRegex = /^\d+\.\s+/;
809
+ const shouldUndoOrderedList = lines.every((line) => orderedListRegex.test(line));
810
+ let result = lines;
811
+ if (shouldUndoOrderedList) {
812
+ result = lines.map((line) => line.replace(orderedListRegex, ""));
813
+ }
814
+ return {
815
+ text: result.join("\n"),
816
+ processed: shouldUndoOrderedList
817
+ };
818
+ }
819
+ function undoUnorderedListStyle(text) {
820
+ const lines = text.split("\n");
821
+ const unorderedListPrefix = "- ";
822
+ const shouldUndoUnorderedList = lines.every((line) => line.startsWith(unorderedListPrefix));
823
+ let result = lines;
824
+ if (shouldUndoUnorderedList) {
825
+ result = lines.map((line) => line.slice(unorderedListPrefix.length));
826
+ }
827
+ return {
828
+ text: result.join("\n"),
829
+ processed: shouldUndoUnorderedList
830
+ };
831
+ }
832
+ function makePrefix(index, unorderedList) {
833
+ if (unorderedList) {
834
+ return "- ";
835
+ } else {
836
+ return `${index + 1}. `;
837
+ }
838
+ }
839
+ function clearExistingListStyle(style, selectedText) {
840
+ let undoResult;
841
+ let undoResultOppositeList;
842
+ let pristineText;
843
+ if (style.orderedList) {
844
+ undoResult = undoOrderedListStyle(selectedText);
845
+ undoResultOppositeList = undoUnorderedListStyle(undoResult.text);
846
+ pristineText = undoResultOppositeList.text;
847
+ } else {
848
+ undoResult = undoUnorderedListStyle(selectedText);
849
+ undoResultOppositeList = undoOrderedListStyle(undoResult.text);
850
+ pristineText = undoResultOppositeList.text;
851
+ }
852
+ return [undoResult, undoResultOppositeList, pristineText];
853
+ }
854
+ function listStyle(textarea, style) {
855
+ const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
856
+ let selectionStart = textarea.selectionStart;
857
+ let selectionEnd = textarea.selectionEnd;
858
+ expandSelectionToLine(textarea);
859
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
860
+ const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
861
+ const prefixedLines = pristineText.split("\n").map((value, index) => {
862
+ return `${makePrefix(index, style.unorderedList)}${value}`;
863
+ });
864
+ const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
865
+ return previousValue + makePrefix(currentIndex, style.unorderedList).length;
866
+ }, 0);
867
+ const totalPrefixLengthOppositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
868
+ return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
869
+ }, 0);
870
+ if (undoResult.processed) {
871
+ if (noInitialSelection) {
872
+ selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
873
+ selectionEnd = selectionStart;
874
+ } else {
875
+ selectionStart = textarea.selectionStart;
876
+ selectionEnd = textarea.selectionEnd - totalPrefixLength;
877
+ }
878
+ return { text: pristineText, selectionStart, selectionEnd };
879
+ }
880
+ const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
881
+ const text = newlinesToAppend + prefixedLines.join("\n") + newlinesToPrepend;
882
+ if (noInitialSelection) {
883
+ selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
884
+ selectionEnd = selectionStart;
885
+ } else {
886
+ if (undoResultOppositeList.processed) {
887
+ selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
888
+ selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOppositeList;
889
+ } else {
890
+ selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
891
+ selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
892
+ }
893
+ }
894
+ return { text, selectionStart, selectionEnd };
895
+ }
896
+ function applyListStyle(textarea, style) {
897
+ const result = applyLineOperation(
898
+ textarea,
899
+ (ta) => listStyle(ta, style),
900
+ {
901
+ // Custom selection adjustment for lists
902
+ adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
903
+ const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
904
+ const orderedListRegex = /^\d+\.\s+/;
905
+ const unorderedListRegex = /^- /;
906
+ const hasOrderedList = orderedListRegex.test(currentLine);
907
+ const hasUnorderedList = unorderedListRegex.test(currentLine);
908
+ const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
909
+ if (selStart === selEnd) {
910
+ if (isRemovingCurrent) {
911
+ const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
912
+ const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
913
+ return {
914
+ start: Math.max(selStart - prefixLength, lineStart),
915
+ end: Math.max(selStart - prefixLength, lineStart)
916
+ };
917
+ } else if (hasOrderedList || hasUnorderedList) {
918
+ const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
919
+ const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
920
+ const newPrefixLength = style.unorderedList ? 2 : 3;
921
+ const adjustment = newPrefixLength - oldPrefixLength;
922
+ return {
923
+ start: selStart + adjustment,
924
+ end: selStart + adjustment
925
+ };
926
+ } else {
927
+ const prefixLength = style.unorderedList ? 2 : 3;
928
+ return {
929
+ start: selStart + prefixLength,
930
+ end: selStart + prefixLength
931
+ };
932
+ }
933
+ } else {
934
+ if (isRemovingCurrent) {
935
+ const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
936
+ const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
937
+ return {
938
+ start: Math.max(selStart - prefixLength, lineStart),
939
+ end: Math.max(selEnd - prefixLength, lineStart)
940
+ };
941
+ } else if (hasOrderedList || hasUnorderedList) {
942
+ const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
943
+ const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
944
+ const newPrefixLength = style.unorderedList ? 2 : 3;
945
+ const adjustment = newPrefixLength - oldPrefixLength;
946
+ return {
947
+ start: selStart + adjustment,
948
+ end: selEnd + adjustment
949
+ };
950
+ } else {
951
+ const prefixLength = style.unorderedList ? 2 : 3;
952
+ return {
953
+ start: selStart + prefixLength,
954
+ end: selEnd + prefixLength
955
+ };
956
+ }
957
+ }
958
+ }
959
+ }
960
+ );
961
+ insertText(textarea, result);
962
+ }
963
+ function getActiveFormats(textarea) {
964
+ if (!textarea)
965
+ return [];
966
+ const formats = [];
967
+ const { selectionStart, selectionEnd, value } = textarea;
968
+ const lines = value.split("\n");
969
+ let lineStart = 0;
970
+ let currentLine = "";
971
+ for (const line of lines) {
972
+ if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {
973
+ currentLine = line;
974
+ break;
975
+ }
976
+ lineStart += line.length + 1;
977
+ }
978
+ if (currentLine.startsWith("- ")) {
979
+ if (currentLine.startsWith("- [ ] ") || currentLine.startsWith("- [x] ")) {
980
+ formats.push("task-list");
981
+ } else {
982
+ formats.push("bullet-list");
983
+ }
984
+ }
985
+ if (/^\d+\.\s/.test(currentLine)) {
986
+ formats.push("numbered-list");
987
+ }
988
+ if (currentLine.startsWith("> ")) {
989
+ formats.push("quote");
990
+ }
991
+ if (currentLine.startsWith("# "))
992
+ formats.push("header");
993
+ if (currentLine.startsWith("## "))
994
+ formats.push("header-2");
995
+ if (currentLine.startsWith("### "))
996
+ formats.push("header-3");
997
+ const lookBehind = Math.max(0, selectionStart - 10);
998
+ const lookAhead = Math.min(value.length, selectionEnd + 10);
999
+ const surrounding = value.slice(lookBehind, lookAhead);
1000
+ if (surrounding.includes("**")) {
1001
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
1002
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
1003
+ const lastOpenBold = beforeCursor.lastIndexOf("**");
1004
+ const nextCloseBold = afterCursor.indexOf("**");
1005
+ if (lastOpenBold !== -1 && nextCloseBold !== -1) {
1006
+ formats.push("bold");
1007
+ }
1008
+ }
1009
+ if (surrounding.includes("_")) {
1010
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
1011
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
1012
+ const lastOpenItalic = beforeCursor.lastIndexOf("_");
1013
+ const nextCloseItalic = afterCursor.indexOf("_");
1014
+ if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {
1015
+ formats.push("italic");
1016
+ }
1017
+ }
1018
+ if (surrounding.includes("`")) {
1019
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
1020
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
1021
+ if (beforeCursor.includes("`") && afterCursor.includes("`")) {
1022
+ formats.push("code");
1023
+ }
1024
+ }
1025
+ if (surrounding.includes("[") && surrounding.includes("]")) {
1026
+ const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
1027
+ const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
1028
+ const lastOpenBracket = beforeCursor.lastIndexOf("[");
1029
+ const nextCloseBracket = afterCursor.indexOf("]");
1030
+ if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
1031
+ const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
1032
+ if (afterBracket.startsWith("(")) {
1033
+ formats.push("link");
1034
+ }
1035
+ }
1036
+ }
1037
+ return formats;
1038
+ }
1039
+ function toggleBold(textarea) {
1040
+ if (!textarea || textarea.disabled || textarea.readOnly)
1041
+ return;
1042
+ debugLog("toggleBold", "Starting");
1043
+ debugSelection(textarea, "Before");
1044
+ const style = mergeWithDefaults(FORMATS.bold);
1045
+ const result = blockStyle(textarea, style);
1046
+ debugResult(result);
1047
+ insertText(textarea, result);
1048
+ debugSelection(textarea, "After");
1049
+ }
1050
+ function toggleItalic(textarea) {
1051
+ if (!textarea || textarea.disabled || textarea.readOnly)
1052
+ return;
1053
+ const style = mergeWithDefaults(FORMATS.italic);
1054
+ const result = blockStyle(textarea, style);
1055
+ insertText(textarea, result);
1056
+ }
1057
+ function toggleCode(textarea) {
1058
+ if (!textarea || textarea.disabled || textarea.readOnly)
1059
+ return;
1060
+ const style = mergeWithDefaults(FORMATS.code);
1061
+ const result = blockStyle(textarea, style);
1062
+ insertText(textarea, result);
1063
+ }
1064
+ function insertLink(textarea, options = {}) {
1065
+ if (!textarea || textarea.disabled || textarea.readOnly)
1066
+ return;
1067
+ const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
1068
+ let style = mergeWithDefaults(FORMATS.link);
1069
+ const isURL = selectedText && selectedText.match(/^https?:\/\//);
1070
+ if (isURL && !options.url) {
1071
+ style.suffix = `](${selectedText})`;
1072
+ style.replaceNext = "";
1073
+ } else if (options.url) {
1074
+ style.suffix = `](${options.url})`;
1075
+ style.replaceNext = "";
1076
+ }
1077
+ if (options.text && !selectedText) {
1078
+ const pos = textarea.selectionStart;
1079
+ textarea.value = textarea.value.slice(0, pos) + options.text + textarea.value.slice(pos);
1080
+ textarea.selectionStart = pos;
1081
+ textarea.selectionEnd = pos + options.text.length;
1082
+ }
1083
+ const result = blockStyle(textarea, style);
1084
+ insertText(textarea, result);
1085
+ }
1086
+ function toggleBulletList(textarea) {
1087
+ if (!textarea || textarea.disabled || textarea.readOnly)
1088
+ return;
1089
+ const style = mergeWithDefaults(FORMATS.bulletList);
1090
+ applyListStyle(textarea, style);
1091
+ }
1092
+ function toggleNumberedList(textarea) {
1093
+ if (!textarea || textarea.disabled || textarea.readOnly)
1094
+ return;
1095
+ const style = mergeWithDefaults(FORMATS.numberedList);
1096
+ applyListStyle(textarea, style);
1097
+ }
1098
+ function toggleQuote(textarea) {
1099
+ if (!textarea || textarea.disabled || textarea.readOnly)
1100
+ return;
1101
+ debugLog("toggleQuote", "Starting");
1102
+ debugSelection(textarea, "Initial");
1103
+ const style = mergeWithDefaults(FORMATS.quote);
1104
+ const result = applyLineOperation(
1105
+ textarea,
1106
+ (ta) => multilineStyle(ta, style),
1107
+ { prefix: style.prefix }
1108
+ );
1109
+ debugResult(result);
1110
+ insertText(textarea, result);
1111
+ debugSelection(textarea, "Final");
1112
+ }
1113
+ function toggleTaskList(textarea) {
1114
+ if (!textarea || textarea.disabled || textarea.readOnly)
1115
+ return;
1116
+ const style = mergeWithDefaults(FORMATS.taskList);
1117
+ const result = applyLineOperation(
1118
+ textarea,
1119
+ (ta) => multilineStyle(ta, style),
1120
+ { prefix: style.prefix }
1121
+ );
1122
+ insertText(textarea, result);
1123
+ }
1124
+ function insertHeader(textarea, level = 1, toggle = false) {
1125
+ if (!textarea || textarea.disabled || textarea.readOnly)
1126
+ return;
1127
+ if (level < 1 || level > 6)
1128
+ level = 1;
1129
+ debugLog("insertHeader", `============ START ============`);
1130
+ debugLog("insertHeader", `Level: ${level}, Toggle: ${toggle}`);
1131
+ debugLog("insertHeader", `Initial cursor: ${textarea.selectionStart}-${textarea.selectionEnd}`);
1132
+ const headerKey = `header${level === 1 ? "1" : level}`;
1133
+ const style = mergeWithDefaults(FORMATS[headerKey] || FORMATS.header1);
1134
+ debugLog("insertHeader", `Style prefix: "${style.prefix}"`);
1135
+ const value = textarea.value;
1136
+ const originalStart = textarea.selectionStart;
1137
+ const originalEnd = textarea.selectionEnd;
1138
+ let lineStart = originalStart;
1139
+ while (lineStart > 0 && value[lineStart - 1] !== "\n") {
1140
+ lineStart--;
1141
+ }
1142
+ let lineEnd = originalEnd;
1143
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
1144
+ lineEnd++;
1145
+ }
1146
+ const currentLineContent = value.slice(lineStart, lineEnd);
1147
+ debugLog("insertHeader", `Current line (before): "${currentLineContent}"`);
1148
+ const existingHeaderMatch = currentLineContent.match(/^(#{1,6})\s*/);
1149
+ const existingLevel = existingHeaderMatch ? existingHeaderMatch[1].length : 0;
1150
+ const existingPrefixLength = existingHeaderMatch ? existingHeaderMatch[0].length : 0;
1151
+ debugLog("insertHeader", `Existing header check:`);
1152
+ debugLog("insertHeader", ` - Match: ${existingHeaderMatch ? `"${existingHeaderMatch[0]}"` : "none"}`);
1153
+ debugLog("insertHeader", ` - Existing level: ${existingLevel}`);
1154
+ debugLog("insertHeader", ` - Existing prefix length: ${existingPrefixLength}`);
1155
+ debugLog("insertHeader", ` - Target level: ${level}`);
1156
+ const shouldToggleOff = toggle && existingLevel === level;
1157
+ debugLog("insertHeader", `Should toggle OFF: ${shouldToggleOff} (toggle=${toggle}, existingLevel=${existingLevel}, level=${level})`);
1158
+ const result = applyLineOperation(
1159
+ textarea,
1160
+ (ta) => {
1161
+ const currentLine = ta.value.slice(ta.selectionStart, ta.selectionEnd);
1162
+ debugLog("insertHeader", `Line in operation: "${currentLine}"`);
1163
+ const cleanedLine = currentLine.replace(/^#{1,6}\s*/, "");
1164
+ debugLog("insertHeader", `Cleaned line: "${cleanedLine}"`);
1165
+ let newLine;
1166
+ if (shouldToggleOff) {
1167
+ debugLog("insertHeader", "ACTION: Toggling OFF - removing header");
1168
+ newLine = cleanedLine;
1169
+ } else if (existingLevel > 0) {
1170
+ debugLog("insertHeader", `ACTION: Replacing H${existingLevel} with H${level}`);
1171
+ newLine = style.prefix + cleanedLine;
1172
+ } else {
1173
+ debugLog("insertHeader", "ACTION: Adding new header");
1174
+ newLine = style.prefix + cleanedLine;
1175
+ }
1176
+ debugLog("insertHeader", `New line: "${newLine}"`);
1177
+ return {
1178
+ text: newLine,
1179
+ selectionStart: ta.selectionStart,
1180
+ selectionEnd: ta.selectionEnd
1181
+ };
1182
+ },
1183
+ {
1184
+ prefix: style.prefix,
1185
+ // Custom selection adjustment for headers
1186
+ adjustSelection: (isRemoving, selStart, selEnd, lineStartPos) => {
1187
+ debugLog("insertHeader", `Adjusting selection:`);
1188
+ debugLog("insertHeader", ` - isRemoving param: ${isRemoving}`);
1189
+ debugLog("insertHeader", ` - shouldToggleOff: ${shouldToggleOff}`);
1190
+ debugLog("insertHeader", ` - selStart: ${selStart}, selEnd: ${selEnd}`);
1191
+ debugLog("insertHeader", ` - lineStartPos: ${lineStartPos}`);
1192
+ if (shouldToggleOff) {
1193
+ const adjustment = Math.max(selStart - existingPrefixLength, lineStartPos);
1194
+ debugLog("insertHeader", ` - Removing header, adjusting by -${existingPrefixLength}`);
1195
+ return {
1196
+ start: adjustment,
1197
+ end: selStart === selEnd ? adjustment : Math.max(selEnd - existingPrefixLength, lineStartPos)
1198
+ };
1199
+ } else if (existingPrefixLength > 0) {
1200
+ const prefixDiff = style.prefix.length - existingPrefixLength;
1201
+ debugLog("insertHeader", ` - Replacing header, adjusting by ${prefixDiff}`);
1202
+ return {
1203
+ start: selStart + prefixDiff,
1204
+ end: selEnd + prefixDiff
1205
+ };
1206
+ } else {
1207
+ debugLog("insertHeader", ` - Adding header, adjusting by +${style.prefix.length}`);
1208
+ return {
1209
+ start: selStart + style.prefix.length,
1210
+ end: selEnd + style.prefix.length
1211
+ };
1212
+ }
1213
+ }
1214
+ }
1215
+ );
1216
+ debugLog("insertHeader", `Final result: text="${result.text}", cursor=${result.selectionStart}-${result.selectionEnd}`);
1217
+ debugLog("insertHeader", `============ END ============`);
1218
+ insertText(textarea, result);
1219
+ }
1220
+ function toggleH1(textarea) {
1221
+ insertHeader(textarea, 1, true);
1222
+ }
1223
+ function toggleH2(textarea) {
1224
+ insertHeader(textarea, 2, true);
1225
+ }
1226
+ function toggleH3(textarea) {
1227
+ insertHeader(textarea, 3, true);
1228
+ }
1229
+ function getActiveFormats2(textarea) {
1230
+ return getActiveFormats(textarea);
1231
+ }
1232
+
1233
+ // src/shortcuts.js
1234
+ var ShortcutsManager = class {
1235
+ constructor(editor) {
1236
+ this.editor = editor;
1237
+ this.textarea = editor.textarea;
1238
+ }
1239
+ /**
1240
+ * Handle keydown events - called by OverType
1241
+ * @param {KeyboardEvent} event - The keyboard event
1242
+ * @returns {boolean} Whether the event was handled
1243
+ */
1244
+ handleKeydown(event) {
1245
+ const isMac = navigator.platform.toLowerCase().includes("mac");
1246
+ const modKey = isMac ? event.metaKey : event.ctrlKey;
1247
+ if (!modKey)
1248
+ return false;
1249
+ let action = null;
1250
+ switch (event.key.toLowerCase()) {
1251
+ case "b":
1252
+ if (!event.shiftKey) {
1253
+ action = "toggleBold";
1254
+ }
1255
+ break;
1256
+ case "i":
1257
+ if (!event.shiftKey) {
1258
+ action = "toggleItalic";
1259
+ }
1260
+ break;
1261
+ case "k":
1262
+ if (!event.shiftKey) {
1263
+ action = "insertLink";
1264
+ }
1265
+ break;
1266
+ case "7":
1267
+ if (event.shiftKey) {
1268
+ action = "toggleNumberedList";
1269
+ }
1270
+ break;
1271
+ case "8":
1272
+ if (event.shiftKey) {
1273
+ action = "toggleBulletList";
1274
+ }
1275
+ break;
1276
+ }
1277
+ if (action) {
1278
+ event.preventDefault();
1279
+ if (this.editor.toolbar) {
1280
+ this.editor.toolbar.handleAction(action);
1281
+ } else {
1282
+ this.handleAction(action);
1283
+ }
1284
+ return true;
1285
+ }
1286
+ return false;
1287
+ }
1288
+ /**
1289
+ * Handle action - fallback when no toolbar exists
1290
+ * This duplicates toolbar.handleAction for consistency
1291
+ */
1292
+ async handleAction(action) {
1293
+ const textarea = this.textarea;
1294
+ if (!textarea)
1295
+ return;
1296
+ textarea.focus();
1297
+ try {
1298
+ switch (action) {
1299
+ case "toggleBold":
1300
+ toggleBold(textarea);
1301
+ break;
1302
+ case "toggleItalic":
1303
+ toggleItalic(textarea);
1304
+ break;
1305
+ case "insertLink":
1306
+ insertLink(textarea);
1307
+ break;
1308
+ case "toggleBulletList":
1309
+ toggleBulletList(textarea);
1310
+ break;
1311
+ case "toggleNumberedList":
1312
+ toggleNumberedList(textarea);
1313
+ break;
1314
+ }
1315
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
1316
+ } catch (error) {
1317
+ console.error("Error in markdown action:", error);
1318
+ }
1319
+ }
1320
+ /**
1321
+ * Cleanup
1322
+ */
1323
+ destroy() {
1324
+ }
1325
+ };
1326
+
1327
+ // src/themes.js
1328
+ var solar = {
1329
+ name: "solar",
1330
+ colors: {
1331
+ bgPrimary: "#faf0ca",
1332
+ // Lemon Chiffon - main background
1333
+ bgSecondary: "#ffffff",
1334
+ // White - editor background
1335
+ text: "#0d3b66",
1336
+ // Yale Blue - main text
1337
+ h1: "#f95738",
1338
+ // Tomato - h1 headers
1339
+ h2: "#ee964b",
1340
+ // Sandy Brown - h2 headers
1341
+ h3: "#3d8a51",
1342
+ // Forest green - h3 headers
1343
+ strong: "#ee964b",
1344
+ // Sandy Brown - bold text
1345
+ em: "#f95738",
1346
+ // Tomato - italic text
1347
+ link: "#0d3b66",
1348
+ // Yale Blue - links
1349
+ code: "#0d3b66",
1350
+ // Yale Blue - inline code
1351
+ codeBg: "rgba(244, 211, 94, 0.4)",
1352
+ // Naples Yellow with transparency
1353
+ blockquote: "#5a7a9b",
1354
+ // Muted blue - blockquotes
1355
+ hr: "#5a7a9b",
1356
+ // Muted blue - horizontal rules
1357
+ syntaxMarker: "rgba(13, 59, 102, 0.52)",
1358
+ // Yale Blue with transparency
1359
+ cursor: "#f95738",
1360
+ // Tomato - cursor
1361
+ selection: "rgba(244, 211, 94, 0.4)",
1362
+ // Naples Yellow with transparency
1363
+ listMarker: "#ee964b",
1364
+ // Sandy Brown - list markers
1365
+ // Toolbar colors
1366
+ toolbarBg: "#ffffff",
1367
+ // White - toolbar background
1368
+ toolbarBorder: "rgba(13, 59, 102, 0.15)",
1369
+ // Yale Blue border
1370
+ toolbarIcon: "#0d3b66",
1371
+ // Yale Blue - icon color
1372
+ toolbarHover: "#f5f5f5",
1373
+ // Light gray - hover background
1374
+ toolbarActive: "#faf0ca"
1375
+ // Lemon Chiffon - active button background
1376
+ }
1377
+ };
1378
+ var cave = {
1379
+ name: "cave",
1380
+ colors: {
1381
+ bgPrimary: "#141E26",
1382
+ // Deep ocean - main background
1383
+ bgSecondary: "#1D2D3E",
1384
+ // Darker charcoal - editor background
1385
+ text: "#c5dde8",
1386
+ // Light blue-gray - main text
1387
+ h1: "#d4a5ff",
1388
+ // Rich lavender - h1 headers
1389
+ h2: "#f6ae2d",
1390
+ // Hunyadi Yellow - h2 headers
1391
+ h3: "#9fcfec",
1392
+ // Brighter blue - h3 headers
1393
+ strong: "#f6ae2d",
1394
+ // Hunyadi Yellow - bold text
1395
+ em: "#9fcfec",
1396
+ // Brighter blue - italic text
1397
+ link: "#9fcfec",
1398
+ // Brighter blue - links
1399
+ code: "#c5dde8",
1400
+ // Light blue-gray - inline code
1401
+ codeBg: "#1a232b",
1402
+ // Very dark blue - code background
1403
+ blockquote: "#9fcfec",
1404
+ // Brighter blue - same as italic
1405
+ hr: "#c5dde8",
1406
+ // Light blue-gray - horizontal rules
1407
+ syntaxMarker: "rgba(159, 207, 236, 0.73)",
1408
+ // Brighter blue semi-transparent
1409
+ cursor: "#f26419",
1410
+ // Orange Pantone - cursor
1411
+ selection: "rgba(51, 101, 138, 0.4)",
1412
+ // Lapis Lazuli with transparency
1413
+ listMarker: "#f6ae2d",
1414
+ // Hunyadi Yellow - list markers
1415
+ // Toolbar colors for dark theme
1416
+ toolbarBg: "#1D2D3E",
1417
+ // Darker charcoal - toolbar background
1418
+ toolbarBorder: "rgba(197, 221, 232, 0.1)",
1419
+ // Light blue-gray border
1420
+ toolbarIcon: "#c5dde8",
1421
+ // Light blue-gray - icon color
1422
+ toolbarHover: "#243546",
1423
+ // Slightly lighter charcoal - hover background
1424
+ toolbarActive: "#2a3f52"
1425
+ // Even lighter - active button background
1426
+ }
1427
+ };
1428
+ var themes = {
1429
+ solar,
1430
+ cave,
1431
+ // Aliases for backward compatibility
1432
+ light: solar,
1433
+ dark: cave
1434
+ };
1435
+ function getTheme(theme) {
1436
+ if (typeof theme === "string") {
1437
+ const themeObj = themes[theme] || themes.solar;
1438
+ return { ...themeObj, name: theme };
1439
+ }
1440
+ return theme;
1441
+ }
1442
+ function themeToCSSVars(colors) {
1443
+ const vars = [];
1444
+ for (const [key, value] of Object.entries(colors)) {
1445
+ const varName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
1446
+ vars.push(`--${varName}: ${value};`);
1447
+ }
1448
+ return vars.join("\n");
1449
+ }
1450
+ function mergeTheme(baseTheme, customColors = {}) {
1451
+ return {
1452
+ ...baseTheme,
1453
+ colors: {
1454
+ ...baseTheme.colors,
1455
+ ...customColors
1456
+ }
1457
+ };
1458
+ }
1459
+
1460
+ // src/styles.js
1461
+ function generateStyles(options = {}) {
1462
+ const {
1463
+ fontSize = "14px",
1464
+ lineHeight = 1.6,
1465
+ /* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
1466
+ fontFamily = '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
1467
+ padding = "20px",
1468
+ theme = null,
1469
+ mobile = {}
1470
+ } = options;
1471
+ const mobileStyles = Object.keys(mobile).length > 0 ? `
1472
+ @media (max-width: 640px) {
1473
+ .overtype-wrapper .overtype-input,
1474
+ .overtype-wrapper .overtype-preview {
1475
+ ${Object.entries(mobile).map(([prop, val]) => {
1476
+ const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
1477
+ return `${cssProp}: ${val} !important;`;
1478
+ }).join("\n ")}
1479
+ }
1480
+ }
1481
+ ` : "";
1482
+ const themeVars = theme && theme.colors ? themeToCSSVars(theme.colors) : "";
1483
+ return `
1484
+ /* OverType Editor Styles */
1485
+
1486
+ /* Middle-ground CSS Reset - Prevent parent styles from leaking in */
1487
+ .overtype-container * {
1488
+ /* Box model - these commonly leak */
1489
+ margin: 0 !important;
1490
+ padding: 0 !important;
1491
+ border: 0 !important;
1492
+
1493
+ /* Layout - these can break our layout */
1494
+ /* Don't reset position - it breaks dropdowns */
1495
+ float: none !important;
1496
+ clear: none !important;
1497
+
1498
+ /* Typography - only reset decorative aspects */
1499
+ text-decoration: none !important;
1500
+ text-transform: none !important;
1501
+ letter-spacing: normal !important;
1502
+
1503
+ /* Visual effects that can interfere */
1504
+ box-shadow: none !important;
1505
+ text-shadow: none !important;
1506
+
1507
+ /* Ensure box-sizing is consistent */
1508
+ box-sizing: border-box !important;
1509
+
1510
+ /* Keep inheritance for these */
1511
+ /* font-family, color, line-height, font-size - inherit */
1512
+ }
1513
+
1514
+ /* Container base styles after reset */
1515
+ .overtype-container {
1516
+ display: grid !important;
1517
+ grid-template-rows: auto 1fr auto !important;
1518
+ width: 100% !important;
1519
+ height: 100% !important;
1520
+ position: relative !important; /* Override reset - needed for absolute children */
1521
+ overflow: visible !important; /* Allow dropdown to overflow container */
1522
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1523
+ ${themeVars ? `
1524
+ /* Theme Variables */
1525
+ ${themeVars}` : ""}
1526
+ }
1527
+
1528
+ /* Auto-resize mode styles */
1529
+ .overtype-container.overtype-auto-resize {
1530
+ height: auto !important;
1531
+ grid-template-rows: auto auto auto !important;
1532
+ }
1533
+
1534
+ .overtype-container.overtype-auto-resize .overtype-wrapper {
1535
+ height: auto !important;
1536
+ min-height: 60px !important;
1537
+ overflow: visible !important;
1538
+ }
1539
+
1540
+ .overtype-wrapper {
1541
+ position: relative !important; /* Override reset - needed for absolute children */
1542
+ width: 100% !important;
1543
+ height: 100% !important; /* Take full height of grid cell */
1544
+ min-height: 60px !important; /* Minimum usable height */
1545
+ overflow: hidden !important;
1546
+ background: var(--bg-secondary, #ffffff) !important;
1547
+ grid-row: 2 !important; /* Always second row in grid */
1548
+ z-index: 1; /* Below toolbar and dropdown */
1549
+ }
1550
+
1551
+ /* Critical alignment styles - must be identical for both layers */
1552
+ .overtype-wrapper .overtype-input,
1553
+ .overtype-wrapper .overtype-preview {
1554
+ /* Positioning - must be identical */
1555
+ position: absolute !important; /* Override reset - required for overlay */
1556
+ top: 0 !important;
1557
+ left: 0 !important;
1558
+ width: 100% !important;
1559
+ height: 100% !important;
1560
+
1561
+ /* Font properties - any difference breaks alignment */
1562
+ font-family: ${fontFamily} !important;
1563
+ font-variant-ligatures: none !important; /* keep metrics stable for code */
1564
+ font-size: var(--instance-font-size, ${fontSize}) !important;
1565
+ line-height: var(--instance-line-height, ${lineHeight}) !important;
1566
+ font-weight: normal !important;
1567
+ font-style: normal !important;
1568
+ font-variant: normal !important;
1569
+ font-stretch: normal !important;
1570
+ font-kerning: none !important;
1571
+ font-feature-settings: normal !important;
1572
+
1573
+ /* Box model - must match exactly */
1574
+ padding: var(--instance-padding, ${padding}) !important;
1575
+ margin: 0 !important;
1576
+ border: none !important;
1577
+ outline: none !important;
1578
+ box-sizing: border-box !important;
1579
+
1580
+ /* Text layout - critical for character positioning */
1581
+ white-space: pre-wrap !important;
1582
+ word-wrap: break-word !important;
1583
+ word-break: normal !important;
1584
+ overflow-wrap: break-word !important;
1585
+ tab-size: 2 !important;
1586
+ -moz-tab-size: 2 !important;
1587
+ text-align: left !important;
1588
+ text-indent: 0 !important;
1589
+ letter-spacing: normal !important;
1590
+ word-spacing: normal !important;
1591
+
1592
+ /* Text rendering */
1593
+ text-transform: none !important;
1594
+ text-rendering: auto !important;
1595
+ -webkit-font-smoothing: auto !important;
1596
+ -webkit-text-size-adjust: 100% !important;
1597
+
1598
+ /* Direction and writing */
1599
+ direction: ltr !important;
1600
+ writing-mode: horizontal-tb !important;
1601
+ unicode-bidi: normal !important;
1602
+ text-orientation: mixed !important;
1603
+
1604
+ /* Visual effects that could shift perception */
1605
+ text-shadow: none !important;
1606
+ filter: none !important;
1607
+ transform: none !important;
1608
+ zoom: 1 !important;
1609
+
1610
+ /* Vertical alignment */
1611
+ vertical-align: baseline !important;
1612
+
1613
+ /* Size constraints */
1614
+ min-width: 0 !important;
1615
+ min-height: 0 !important;
1616
+ max-width: none !important;
1617
+ max-height: none !important;
1618
+
1619
+ /* Overflow */
1620
+ overflow-y: auto !important;
1621
+ overflow-x: auto !important;
1622
+ /* overscroll-behavior removed to allow scroll-through to parent */
1623
+ scrollbar-width: auto !important;
1624
+ scrollbar-gutter: auto !important;
1625
+
1626
+ /* Animation/transition - disabled to prevent movement */
1627
+ animation: none !important;
1628
+ transition: none !important;
1629
+ }
1630
+
1631
+ /* Input layer styles */
1632
+ .overtype-wrapper .overtype-input {
1633
+ /* Layer positioning */
1634
+ z-index: 1 !important;
1635
+
1636
+ /* Text visibility */
1637
+ color: transparent !important;
1638
+ caret-color: var(--cursor, #f95738) !important;
1639
+ background-color: transparent !important;
1640
+
1641
+ /* Textarea-specific */
1642
+ resize: none !important;
1643
+ appearance: none !important;
1644
+ -webkit-appearance: none !important;
1645
+ -moz-appearance: none !important;
1646
+
1647
+ /* Prevent mobile zoom on focus */
1648
+ touch-action: manipulation !important;
1649
+
1650
+ /* Disable autofill and spellcheck */
1651
+ autocomplete: off !important;
1652
+ autocorrect: off !important;
1653
+ autocapitalize: off !important;
1654
+ spellcheck: false !important;
1655
+ }
1656
+
1657
+ .overtype-wrapper .overtype-input::selection {
1658
+ background-color: var(--selection, rgba(244, 211, 94, 0.4));
1659
+ }
1660
+
1661
+ /* Preview layer styles */
1662
+ .overtype-wrapper .overtype-preview {
1663
+ /* Layer positioning */
1664
+ z-index: 0 !important;
1665
+ pointer-events: none !important;
1666
+ color: var(--text, #0d3b66) !important;
1667
+ background-color: transparent !important;
1668
+
1669
+ /* Prevent text selection */
1670
+ user-select: none !important;
1671
+ -webkit-user-select: none !important;
1672
+ -moz-user-select: none !important;
1673
+ -ms-user-select: none !important;
1674
+ }
1675
+
1676
+ /* Defensive styles for preview child divs */
1677
+ .overtype-wrapper .overtype-preview div {
1678
+ /* Reset any inherited styles */
1679
+ margin: 0 !important;
1680
+ padding: 0 !important;
1681
+ border: none !important;
1682
+ text-align: left !important;
1683
+ text-indent: 0 !important;
1684
+ display: block !important;
1685
+ position: static !important;
1686
+ transform: none !important;
1687
+ min-height: 0 !important;
1688
+ max-height: none !important;
1689
+ line-height: inherit !important;
1690
+ font-size: inherit !important;
1691
+ font-family: inherit !important;
1692
+ }
1693
+
1694
+ /* Markdown element styling - NO SIZE CHANGES */
1695
+ .overtype-wrapper .overtype-preview .header {
1696
+ font-weight: bold !important;
1697
+ }
1698
+
1699
+ /* Header colors */
1700
+ .overtype-wrapper .overtype-preview .h1 {
1701
+ color: var(--h1, #f95738) !important;
1702
+ }
1703
+ .overtype-wrapper .overtype-preview .h2 {
1704
+ color: var(--h2, #ee964b) !important;
1705
+ }
1706
+ .overtype-wrapper .overtype-preview .h3 {
1707
+ color: var(--h3, #3d8a51) !important;
1708
+ }
1709
+
1710
+ /* Semantic headers - flatten in edit mode */
1711
+ .overtype-wrapper .overtype-preview h1,
1712
+ .overtype-wrapper .overtype-preview h2,
1713
+ .overtype-wrapper .overtype-preview h3 {
1714
+ font-size: inherit !important;
1715
+ font-weight: bold !important;
1716
+ margin: 0 !important;
1717
+ padding: 0 !important;
1718
+ display: inline !important;
1719
+ line-height: inherit !important;
1720
+ }
1721
+
1722
+ /* Header colors for semantic headers */
1723
+ .overtype-wrapper .overtype-preview h1 {
1724
+ color: var(--h1, #f95738) !important;
1725
+ }
1726
+ .overtype-wrapper .overtype-preview h2 {
1727
+ color: var(--h2, #ee964b) !important;
1728
+ }
1729
+ .overtype-wrapper .overtype-preview h3 {
1730
+ color: var(--h3, #3d8a51) !important;
1731
+ }
1732
+
1733
+ /* Lists - remove styling in edit mode */
1734
+ .overtype-wrapper .overtype-preview ul,
1735
+ .overtype-wrapper .overtype-preview ol {
1736
+ list-style: none !important;
1737
+ margin: 0 !important;
1738
+ padding: 0 !important;
1739
+ display: block !important; /* Lists need to be block for line breaks */
1740
+ }
1741
+
1742
+ .overtype-wrapper .overtype-preview li {
1743
+ display: block !important; /* Each item on its own line */
1744
+ margin: 0 !important;
1745
+ padding: 0 !important;
1746
+ /* Don't set list-style here - let ul/ol control it */
1747
+ }
1748
+
1749
+ /* Bold text */
1750
+ .overtype-wrapper .overtype-preview strong {
1751
+ color: var(--strong, #ee964b) !important;
1752
+ font-weight: bold !important;
1753
+ }
1754
+
1755
+ /* Italic text */
1756
+ .overtype-wrapper .overtype-preview em {
1757
+ color: var(--em, #f95738) !important;
1758
+ text-decoration-color: var(--em, #f95738) !important;
1759
+ text-decoration-thickness: 1px !important;
1760
+ font-style: italic !important;
1761
+ }
1762
+
1763
+ /* Inline code */
1764
+ .overtype-wrapper .overtype-preview code {
1765
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1766
+ color: var(--code, #0d3b66) !important;
1767
+ padding: 0 !important;
1768
+ border-radius: 2px !important;
1769
+ font-family: inherit !important;
1770
+ font-size: inherit !important;
1771
+ line-height: inherit !important;
1772
+ font-weight: normal !important;
1773
+ }
1774
+
1775
+ /* Code blocks - consolidated pre blocks */
1776
+ .overtype-wrapper .overtype-preview pre {
1777
+ padding: 0 !important;
1778
+ margin: 0 !important;
1779
+ border-radius: 4px !important;
1780
+ overflow-x: auto !important;
1781
+ }
1782
+
1783
+ /* Code block styling in normal mode - yellow background */
1784
+ .overtype-wrapper .overtype-preview pre.code-block {
1785
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1786
+ }
1787
+
1788
+ /* Code inside pre blocks - remove background */
1789
+ .overtype-wrapper .overtype-preview pre code {
1790
+ background: transparent !important;
1791
+ color: var(--code, #0d3b66) !important;
1792
+ }
1793
+
1794
+ /* Blockquotes */
1795
+ .overtype-wrapper .overtype-preview .blockquote {
1796
+ color: var(--blockquote, #5a7a9b) !important;
1797
+ padding: 0 !important;
1798
+ margin: 0 !important;
1799
+ border: none !important;
1800
+ }
1801
+
1802
+ /* Links */
1803
+ .overtype-wrapper .overtype-preview a {
1804
+ color: var(--link, #0d3b66) !important;
1805
+ text-decoration: underline !important;
1806
+ font-weight: normal !important;
1807
+ }
1808
+
1809
+ .overtype-wrapper .overtype-preview a:hover {
1810
+ text-decoration: underline !important;
1811
+ color: var(--link, #0d3b66) !important;
1812
+ }
1813
+
1814
+ /* Lists - no list styling */
1815
+ .overtype-wrapper .overtype-preview ul,
1816
+ .overtype-wrapper .overtype-preview ol {
1817
+ list-style: none !important;
1818
+ margin: 0 !important;
1819
+ padding: 0 !important;
1820
+ }
1821
+
1822
+
1823
+ /* Horizontal rules */
1824
+ .overtype-wrapper .overtype-preview hr {
1825
+ border: none !important;
1826
+ color: var(--hr, #5a7a9b) !important;
1827
+ margin: 0 !important;
1828
+ padding: 0 !important;
1829
+ }
1830
+
1831
+ .overtype-wrapper .overtype-preview .hr-marker {
1832
+ color: var(--hr, #5a7a9b) !important;
1833
+ opacity: 0.6 !important;
1834
+ }
1835
+
1836
+ /* Code fence markers - with background when not in code block */
1837
+ .overtype-wrapper .overtype-preview .code-fence {
1838
+ color: var(--code, #0d3b66) !important;
1839
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1840
+ }
1841
+
1842
+ /* Code block lines - background for entire code block */
1843
+ .overtype-wrapper .overtype-preview .code-block-line {
1844
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1845
+ }
1846
+
1847
+ /* Remove background from code fence when inside code block line */
1848
+ .overtype-wrapper .overtype-preview .code-block-line .code-fence {
1849
+ background: transparent !important;
1850
+ }
1851
+
1852
+ /* Raw markdown line */
1853
+ .overtype-wrapper .overtype-preview .raw-line {
1854
+ color: var(--raw-line, #5a7a9b) !important;
1855
+ font-style: normal !important;
1856
+ font-weight: normal !important;
1857
+ }
1858
+
1859
+ /* Syntax markers */
1860
+ .overtype-wrapper .overtype-preview .syntax-marker {
1861
+ color: var(--syntax-marker, rgba(13, 59, 102, 0.52)) !important;
1862
+ opacity: 0.7 !important;
1863
+ }
1864
+
1865
+ /* List markers */
1866
+ .overtype-wrapper .overtype-preview .list-marker {
1867
+ color: var(--list-marker, #ee964b) !important;
1868
+ }
1869
+
1870
+ /* Stats bar */
1871
+
1872
+ /* Stats bar - positioned by grid, not absolute */
1873
+ .overtype-stats {
1874
+ height: 40px !important;
1875
+ padding: 0 20px !important;
1876
+ background: #f8f9fa !important;
1877
+ border-top: 1px solid #e0e0e0 !important;
1878
+ display: flex !important;
1879
+ justify-content: space-between !important;
1880
+ align-items: center !important;
1881
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
1882
+ font-size: 0.85rem !important;
1883
+ color: #666 !important;
1884
+ grid-row: 3 !important; /* Always third row in grid */
1885
+ }
1886
+
1887
+ /* Dark theme stats bar */
1888
+ .overtype-container[data-theme="cave"] .overtype-stats {
1889
+ background: var(--bg-secondary, #1D2D3E) !important;
1890
+ border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
1891
+ color: var(--text, #c5dde8) !important;
1892
+ }
1893
+
1894
+ .overtype-stats .overtype-stat {
1895
+ display: flex !important;
1896
+ align-items: center !important;
1897
+ gap: 5px !important;
1898
+ white-space: nowrap !important;
1899
+ }
1900
+
1901
+ .overtype-stats .live-dot {
1902
+ width: 8px !important;
1903
+ height: 8px !important;
1904
+ background: #4caf50 !important;
1905
+ border-radius: 50% !important;
1906
+ animation: pulse 2s infinite !important;
1907
+ }
1908
+
1909
+ @keyframes pulse {
1910
+ 0%, 100% { opacity: 1; transform: scale(1); }
1911
+ 50% { opacity: 0.6; transform: scale(1.2); }
1912
+ }
1913
+
1914
+
1915
+ /* Toolbar Styles */
1916
+ .overtype-toolbar {
1917
+ display: flex;
1918
+ align-items: center;
1919
+ gap: 4px;
1920
+ padding: 8px !important; /* Override reset */
1921
+ background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
1922
+ overflow-x: auto !important; /* Allow horizontal scrolling */
1923
+ overflow-y: hidden !important; /* Hide vertical overflow */
1924
+ -webkit-overflow-scrolling: touch;
1925
+ flex-shrink: 0;
1926
+ height: auto !important;
1927
+ grid-row: 1 !important; /* Always first row in grid */
1928
+ position: relative !important; /* Override reset */
1929
+ z-index: 100; /* Ensure toolbar is above wrapper */
1930
+ scrollbar-width: thin; /* Thin scrollbar on Firefox */
1931
+ }
1932
+
1933
+ /* Thin scrollbar styling */
1934
+ .overtype-toolbar::-webkit-scrollbar {
1935
+ height: 4px;
1936
+ }
1937
+
1938
+ .overtype-toolbar::-webkit-scrollbar-track {
1939
+ background: transparent;
1940
+ }
1941
+
1942
+ .overtype-toolbar::-webkit-scrollbar-thumb {
1943
+ background: rgba(0, 0, 0, 0.2);
1944
+ border-radius: 2px;
1945
+ }
1946
+
1947
+ .overtype-toolbar-button {
1948
+ display: flex;
1949
+ align-items: center;
1950
+ justify-content: center;
1951
+ width: 32px;
1952
+ height: 32px;
1953
+ padding: 0;
1954
+ border: none;
1955
+ border-radius: 6px;
1956
+ background: transparent;
1957
+ color: var(--toolbar-icon, var(--text-secondary, #666));
1958
+ cursor: pointer;
1959
+ transition: all 0.2s ease;
1960
+ flex-shrink: 0;
1961
+ }
1962
+
1963
+ .overtype-toolbar-button svg {
1964
+ width: 20px;
1965
+ height: 20px;
1966
+ fill: currentColor;
1967
+ }
1968
+
1969
+ .overtype-toolbar-button:hover {
1970
+ background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
1971
+ color: var(--toolbar-icon, var(--text-primary, #333));
1972
+ }
1973
+
1974
+ .overtype-toolbar-button:active {
1975
+ transform: scale(0.95);
1976
+ }
1977
+
1978
+ .overtype-toolbar-button.active {
1979
+ background: var(--toolbar-active, var(--primary, #007bff));
1980
+ color: var(--toolbar-icon, var(--text-primary, #333));
1981
+ }
1982
+
1983
+ .overtype-toolbar-button:disabled {
1984
+ opacity: 0.5;
1985
+ cursor: not-allowed;
1986
+ }
1987
+
1988
+ .overtype-toolbar-separator {
1989
+ width: 1px;
1990
+ height: 24px;
1991
+ background: var(--border, #e0e0e0);
1992
+ margin: 0 4px;
1993
+ flex-shrink: 0;
1994
+ }
1995
+
1996
+ /* Adjust wrapper when toolbar is present */
1997
+ .overtype-container .overtype-toolbar + .overtype-wrapper {
1998
+ }
1999
+
2000
+ /* Mobile toolbar adjustments */
2001
+ @media (max-width: 640px) {
2002
+ .overtype-toolbar {
2003
+ padding: 6px;
2004
+ gap: 2px;
2005
+ }
2006
+
2007
+ .overtype-toolbar-button {
2008
+ width: 36px;
2009
+ height: 36px;
2010
+ }
2011
+
2012
+ .overtype-toolbar-separator {
2013
+ margin: 0 2px;
2014
+ }
2015
+ }
2016
+
2017
+ /* Plain mode - hide preview and show textarea text */
2018
+ .overtype-container.plain-mode .overtype-preview {
2019
+ display: none !important;
2020
+ }
2021
+
2022
+ .overtype-container.plain-mode .overtype-input {
2023
+ color: var(--text, #0d3b66) !important;
2024
+ /* Use system font stack for better plain text readability */
2025
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
2026
+ "Helvetica Neue", Arial, sans-serif !important;
2027
+ }
2028
+
2029
+ /* Ensure textarea remains transparent in overlay mode */
2030
+ .overtype-container:not(.plain-mode) .overtype-input {
2031
+ color: transparent !important;
2032
+ }
2033
+
2034
+ /* Dropdown menu styles */
2035
+ .overtype-toolbar-button {
2036
+ position: relative !important; /* Override reset - needed for dropdown */
2037
+ }
2038
+
2039
+ .overtype-toolbar-button.dropdown-active {
2040
+ background: var(--toolbar-active, var(--hover-bg, #f0f0f0));
2041
+ }
2042
+
2043
+ .overtype-dropdown-menu {
2044
+ position: fixed !important; /* Fixed positioning relative to viewport */
2045
+ background: var(--bg-secondary, white) !important; /* Override reset */
2046
+ border: 1px solid var(--border, #e0e0e0) !important; /* Override reset */
2047
+ border-radius: 6px;
2048
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important; /* Override reset */
2049
+ z-index: 10000; /* Very high z-index to ensure visibility */
2050
+ min-width: 150px;
2051
+ padding: 4px 0 !important; /* Override reset */
2052
+ /* Position will be set via JavaScript based on button position */
2053
+ }
2054
+
2055
+ .overtype-dropdown-item {
2056
+ display: flex;
2057
+ align-items: center;
2058
+ width: 100%;
2059
+ padding: 8px 12px;
2060
+ border: none;
2061
+ background: none;
2062
+ text-align: left;
2063
+ cursor: pointer;
2064
+ font-size: 14px;
2065
+ color: var(--text, #333);
2066
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
2067
+ }
2068
+
2069
+ .overtype-dropdown-item:hover {
2070
+ background: var(--hover-bg, #f0f0f0);
2071
+ }
2072
+
2073
+ .overtype-dropdown-item.active {
2074
+ font-weight: 600;
2075
+ }
2076
+
2077
+ .overtype-dropdown-check {
2078
+ width: 16px;
2079
+ margin-right: 8px;
2080
+ color: var(--h1, #007bff);
2081
+ }
2082
+
2083
+ /* Preview mode styles */
2084
+ .overtype-container.preview-mode .overtype-input {
2085
+ display: none !important;
2086
+ }
2087
+
2088
+ .overtype-container.preview-mode .overtype-preview {
2089
+ pointer-events: auto !important;
2090
+ user-select: text !important;
2091
+ cursor: text !important;
2092
+ }
2093
+
2094
+ /* Hide syntax markers in preview mode */
2095
+ .overtype-container.preview-mode .syntax-marker {
2096
+ display: none !important;
2097
+ }
2098
+
2099
+ /* Hide URL part of links in preview mode - extra specificity */
2100
+ .overtype-container.preview-mode .syntax-marker.url-part,
2101
+ .overtype-container.preview-mode .url-part {
2102
+ display: none !important;
2103
+ }
2104
+
2105
+ /* Hide all syntax markers inside links too */
2106
+ .overtype-container.preview-mode a .syntax-marker {
2107
+ display: none !important;
2108
+ }
2109
+
2110
+ /* Headers - restore proper sizing in preview mode */
2111
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1,
2112
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2,
2113
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
2114
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
2115
+ font-weight: 600 !important;
2116
+ margin: 0 !important;
2117
+ display: block !important;
2118
+ color: inherit !important; /* Use parent text color */
2119
+ line-height: 1 !important; /* Tight line height for headings */
2120
+ }
2121
+
2122
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1 {
2123
+ font-size: 2em !important;
2124
+ }
2125
+
2126
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2 {
2127
+ font-size: 1.5em !important;
2128
+ }
2129
+
2130
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
2131
+ font-size: 1.17em !important;
2132
+ }
2133
+
2134
+ /* Lists - restore list styling in preview mode */
2135
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview ul {
2136
+ display: block !important;
2137
+ list-style: disc !important;
2138
+ padding-left: 2em !important;
2139
+ margin: 1em 0 !important;
2140
+ }
2141
+
2142
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview ol {
2143
+ display: block !important;
2144
+ list-style: decimal !important;
2145
+ padding-left: 2em !important;
2146
+ margin: 1em 0 !important;
2147
+ }
2148
+
2149
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview li {
2150
+ display: list-item !important;
2151
+ margin: 0 !important;
2152
+ padding: 0 !important;
2153
+ }
2154
+
2155
+ /* Links - make clickable in preview mode */
2156
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview a {
2157
+ pointer-events: auto !important;
2158
+ cursor: pointer !important;
2159
+ color: var(--link, #0066cc) !important;
2160
+ text-decoration: underline !important;
2161
+ }
2162
+
2163
+ /* Code blocks - proper pre/code styling in preview mode */
2164
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block {
2165
+ background: #2d2d2d !important;
2166
+ color: #f8f8f2 !important;
2167
+ padding: 1.2em !important;
2168
+ border-radius: 3px !important;
2169
+ overflow-x: auto !important;
2170
+ margin: 0 !important;
2171
+ display: block !important;
2172
+ }
2173
+
2174
+ /* Cave theme code block background in preview mode */
2175
+ .overtype-container[data-theme="cave"].preview-mode .overtype-wrapper .overtype-preview pre.code-block {
2176
+ background: #11171F !important;
2177
+ }
2178
+
2179
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block code {
2180
+ background: transparent !important;
2181
+ color: inherit !important;
2182
+ padding: 0 !important;
2183
+ font-family: ${fontFamily} !important;
2184
+ font-size: 0.9em !important;
2185
+ line-height: 1.4 !important;
2186
+ }
2187
+
2188
+ /* Hide old code block lines and fences in preview mode */
2189
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-block-line {
2190
+ display: none !important;
2191
+ }
2192
+
2193
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-fence {
2194
+ display: none !important;
2195
+ }
2196
+
2197
+ /* Blockquotes - enhanced styling in preview mode */
2198
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .blockquote {
2199
+ display: block !important;
2200
+ border-left: 4px solid var(--blockquote, #ddd) !important;
2201
+ padding-left: 1em !important;
2202
+ margin: 1em 0 !important;
2203
+ font-style: italic !important;
2204
+ }
2205
+
2206
+ /* Typography improvements in preview mode */
2207
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview {
2208
+ font-family: Georgia, 'Times New Roman', serif !important;
2209
+ font-size: 16px !important;
2210
+ line-height: 1.8 !important;
2211
+ color: var(--text, #333) !important; /* Consistent text color */
2212
+ }
2213
+
2214
+ /* Inline code in preview mode - keep monospace */
2215
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview code {
2216
+ font-family: ${fontFamily} !important;
2217
+ font-size: 0.9em !important;
2218
+ background: rgba(135, 131, 120, 0.15) !important;
2219
+ padding: 0.2em 0.4em !important;
2220
+ border-radius: 3px !important;
2221
+ }
2222
+
2223
+ /* Strong and em elements in preview mode */
2224
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview strong {
2225
+ font-weight: 700 !important;
2226
+ color: inherit !important; /* Use parent text color */
2227
+ }
2228
+
2229
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview em {
2230
+ font-style: italic !important;
2231
+ color: inherit !important; /* Use parent text color */
2232
+ }
2233
+
2234
+ /* HR in preview mode */
2235
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .hr-marker {
2236
+ display: block !important;
2237
+ border-top: 2px solid var(--hr, #ddd) !important;
2238
+ text-indent: -9999px !important;
2239
+ height: 2px !important;
2240
+ }
2241
+
2242
+ ${mobileStyles}
2243
+ `;
2244
+ }
2245
+
2246
+ // src/icons.js
2247
+ var boldIcon = `<svg viewBox="0 0 18 18">
2248
+ <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>
2249
+ <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>
2250
+ </svg>`;
2251
+ var italicIcon = `<svg viewBox="0 0 18 18">
2252
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="13" y1="4" y2="4"></line>
2253
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="5" x2="11" y1="14" y2="14"></line>
2254
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="10" y1="14" y2="4"></line>
2255
+ </svg>`;
2256
+ var h1Icon = `<svg viewBox="0 0 18 18">
2257
+ <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>
2258
+ </svg>`;
2259
+ var h2Icon = `<svg viewBox="0 0 18 18">
2260
+ <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>
2261
+ </svg>`;
2262
+ var h3Icon = `<svg viewBox="0 0 18 18">
2263
+ <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>
2264
+ </svg>`;
2265
+ var linkIcon = `<svg viewBox="0 0 18 18">
2266
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="11" y1="7" y2="11"></line>
2267
+ <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>
2268
+ <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>
2269
+ </svg>`;
2270
+ var codeIcon = `<svg viewBox="0 0 18 18">
2271
+ <polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="5 7 3 9 5 11"></polyline>
2272
+ <polyline stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" points="13 7 15 9 13 11"></polyline>
2273
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="10" x2="8" y1="5" y2="13"></line>
2274
+ </svg>`;
2275
+ var bulletListIcon = `<svg viewBox="0 0 18 18">
2276
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="4" y2="4"></line>
2277
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="9" y2="9"></line>
2278
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="6" x2="15" y1="14" y2="14"></line>
2279
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="4" y2="4"></line>
2280
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="9" y2="9"></line>
2281
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="3" x2="3" y1="14" y2="14"></line>
2282
+ </svg>`;
2283
+ var orderedListIcon = `<svg viewBox="0 0 18 18">
2284
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="4" y2="4"></line>
2285
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="9" y2="9"></line>
2286
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="7" x2="15" y1="14" y2="14"></line>
2287
+ <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>
2288
+ <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>
2289
+ <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>
2290
+ <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>
2291
+ </svg>`;
2292
+ var quoteIcon = `<svg viewBox="2 2 20 20">
2293
+ <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>
2294
+ <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>
2295
+ </svg>`;
2296
+ var taskListIcon = `<svg viewBox="0 0 18 18">
2297
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="4" y2="4"></line>
2298
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="9" y2="9"></line>
2299
+ <line stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8" x2="16" y1="14" y2="14"></line>
2300
+ <rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="3" width="3" height="3" rx="0.5"></rect>
2301
+ <rect stroke="currentColor" fill="none" stroke-width="1.5" x="2" y="13" width="3" height="3" rx="0.5"></rect>
2302
+ <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>
2303
+ </svg>`;
2304
+ var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2305
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" fill="none"></path>
2306
+ <circle cx="12" cy="12" r="3" fill="none"></circle>
2307
+ </svg>`;
2308
+
2309
+ // src/toolbar.js
2310
+ var Toolbar = class {
2311
+ constructor(editor) {
2312
+ this.editor = editor;
2313
+ this.container = null;
2314
+ this.buttons = {};
2315
+ }
2316
+ /**
2317
+ * Create and attach toolbar to editor
2318
+ */
2319
+ create() {
2320
+ this.container = document.createElement("div");
2321
+ this.container.className = "overtype-toolbar";
2322
+ this.container.setAttribute("role", "toolbar");
2323
+ this.container.setAttribute("aria-label", "Text formatting");
2324
+ const buttonConfig = [
2325
+ { name: "bold", icon: boldIcon, title: "Bold (Ctrl+B)", action: "toggleBold" },
2326
+ { name: "italic", icon: italicIcon, title: "Italic (Ctrl+I)", action: "toggleItalic" },
2327
+ { separator: true },
2328
+ { name: "h1", icon: h1Icon, title: "Heading 1", action: "insertH1" },
2329
+ { name: "h2", icon: h2Icon, title: "Heading 2", action: "insertH2" },
2330
+ { name: "h3", icon: h3Icon, title: "Heading 3", action: "insertH3" },
2331
+ { separator: true },
2332
+ { name: "link", icon: linkIcon, title: "Insert Link (Ctrl+K)", action: "insertLink" },
2333
+ { name: "code", icon: codeIcon, title: "Code (Ctrl+`)", action: "toggleCode" },
2334
+ { separator: true },
2335
+ { name: "quote", icon: quoteIcon, title: "Quote", action: "toggleQuote" },
2336
+ { separator: true },
2337
+ { name: "bulletList", icon: bulletListIcon, title: "Bullet List", action: "toggleBulletList" },
2338
+ { name: "orderedList", icon: orderedListIcon, title: "Numbered List", action: "toggleNumberedList" },
2339
+ { name: "taskList", icon: taskListIcon, title: "Task List", action: "toggleTaskList" },
2340
+ { separator: true },
2341
+ { name: "viewMode", icon: eyeIcon, title: "View mode", action: "toggle-view-menu", hasDropdown: true }
2342
+ ];
2343
+ buttonConfig.forEach((config) => {
2344
+ if (config.separator) {
2345
+ const separator = document.createElement("div");
2346
+ separator.className = "overtype-toolbar-separator";
2347
+ separator.setAttribute("role", "separator");
2348
+ this.container.appendChild(separator);
2349
+ } else {
2350
+ const button = this.createButton(config);
2351
+ this.buttons[config.name] = button;
2352
+ this.container.appendChild(button);
2353
+ }
2354
+ });
2355
+ const container = this.editor.element.querySelector(".overtype-container");
2356
+ const wrapper = this.editor.element.querySelector(".overtype-wrapper");
2357
+ if (container && wrapper) {
2358
+ container.insertBefore(this.container, wrapper);
2359
+ }
2360
+ return this.container;
2361
+ }
2362
+ /**
2363
+ * Create individual toolbar button
2364
+ */
2365
+ createButton(config) {
2366
+ const button = document.createElement("button");
2367
+ button.className = "overtype-toolbar-button";
2368
+ button.type = "button";
2369
+ button.title = config.title;
2370
+ button.setAttribute("aria-label", config.title);
2371
+ button.setAttribute("data-action", config.action);
2372
+ button.innerHTML = config.icon;
2373
+ if (config.hasDropdown) {
2374
+ button.classList.add("has-dropdown");
2375
+ if (config.name === "viewMode") {
2376
+ this.viewModeButton = button;
2377
+ }
2378
+ }
2379
+ button.addEventListener("click", (e) => {
2380
+ e.preventDefault();
2381
+ this.handleAction(config.action, button);
2382
+ });
2383
+ return button;
2384
+ }
2385
+ /**
2386
+ * Handle toolbar button actions
2387
+ */
2388
+ async handleAction(action, button) {
2389
+ const textarea = this.editor.textarea;
2390
+ if (!textarea)
2391
+ return;
2392
+ if (action === "toggle-view-menu") {
2393
+ this.toggleViewDropdown(button);
2394
+ return;
2395
+ }
2396
+ textarea.focus();
2397
+ try {
2398
+ switch (action) {
2399
+ case "toggleBold":
2400
+ toggleBold(textarea);
2401
+ break;
2402
+ case "toggleItalic":
2403
+ toggleItalic(textarea);
2404
+ break;
2405
+ case "insertH1":
2406
+ toggleH1(textarea);
2407
+ break;
2408
+ case "insertH2":
2409
+ toggleH2(textarea);
2410
+ break;
2411
+ case "insertH3":
2412
+ toggleH3(textarea);
2413
+ break;
2414
+ case "insertLink":
2415
+ insertLink(textarea);
2416
+ break;
2417
+ case "toggleCode":
2418
+ toggleCode(textarea);
2419
+ break;
2420
+ case "toggleBulletList":
2421
+ toggleBulletList(textarea);
2422
+ break;
2423
+ case "toggleNumberedList":
2424
+ toggleNumberedList(textarea);
2425
+ break;
2426
+ case "toggleQuote":
2427
+ toggleQuote(textarea);
2428
+ break;
2429
+ case "toggleTaskList":
2430
+ toggleTaskList(textarea);
2431
+ break;
2432
+ case "toggle-plain":
2433
+ const isPlain = this.editor.container.classList.contains("plain-mode");
2434
+ this.editor.showPlainTextarea(!isPlain);
2435
+ break;
2436
+ }
2437
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
2438
+ } catch (error) {
2439
+ console.error("Error loading markdown-actions:", error);
2440
+ }
2441
+ }
2442
+ /**
2443
+ * Update toolbar button states based on current selection
2444
+ */
2445
+ async updateButtonStates() {
2446
+ const textarea = this.editor.textarea;
2447
+ if (!textarea)
2448
+ return;
2449
+ try {
2450
+ const activeFormats = getActiveFormats2(textarea);
2451
+ Object.entries(this.buttons).forEach(([name, button]) => {
2452
+ let isActive = false;
2453
+ switch (name) {
2454
+ case "bold":
2455
+ isActive = activeFormats.includes("bold");
2456
+ break;
2457
+ case "italic":
2458
+ isActive = activeFormats.includes("italic");
2459
+ break;
2460
+ case "code":
2461
+ isActive = false;
2462
+ break;
2463
+ case "bulletList":
2464
+ isActive = activeFormats.includes("bullet-list");
2465
+ break;
2466
+ case "orderedList":
2467
+ isActive = activeFormats.includes("numbered-list");
2468
+ break;
2469
+ case "quote":
2470
+ isActive = activeFormats.includes("quote");
2471
+ break;
2472
+ case "taskList":
2473
+ isActive = activeFormats.includes("task-list");
2474
+ break;
2475
+ case "h1":
2476
+ isActive = activeFormats.includes("header");
2477
+ break;
2478
+ case "h2":
2479
+ isActive = activeFormats.includes("header-2");
2480
+ break;
2481
+ case "h3":
2482
+ isActive = activeFormats.includes("header-3");
2483
+ break;
2484
+ case "togglePlain":
2485
+ isActive = !this.editor.container.classList.contains("plain-mode");
2486
+ break;
2487
+ }
2488
+ button.classList.toggle("active", isActive);
2489
+ button.setAttribute("aria-pressed", isActive.toString());
2490
+ });
2491
+ } catch (error) {
2492
+ }
2493
+ }
2494
+ /**
2495
+ * Toggle view mode dropdown menu
2496
+ */
2497
+ toggleViewDropdown(button) {
2498
+ const existingDropdown = document.querySelector(".overtype-dropdown-menu");
2499
+ if (existingDropdown) {
2500
+ existingDropdown.remove();
2501
+ button.classList.remove("dropdown-active");
2502
+ document.removeEventListener("click", this.handleDocumentClick);
2503
+ return;
2504
+ }
2505
+ const dropdown = this.createViewDropdown();
2506
+ const rect = button.getBoundingClientRect();
2507
+ dropdown.style.top = `${rect.bottom + 4}px`;
2508
+ dropdown.style.left = `${rect.left}px`;
2509
+ document.body.appendChild(dropdown);
2510
+ button.classList.add("dropdown-active");
2511
+ this.handleDocumentClick = (e) => {
2512
+ if (!button.contains(e.target) && !dropdown.contains(e.target)) {
2513
+ dropdown.remove();
2514
+ button.classList.remove("dropdown-active");
2515
+ document.removeEventListener("click", this.handleDocumentClick);
2516
+ }
2517
+ };
2518
+ setTimeout(() => {
2519
+ document.addEventListener("click", this.handleDocumentClick);
2520
+ }, 0);
2521
+ }
2522
+ /**
2523
+ * Create view mode dropdown menu
2524
+ */
2525
+ createViewDropdown() {
2526
+ const dropdown = document.createElement("div");
2527
+ dropdown.className = "overtype-dropdown-menu";
2528
+ const isPlain = this.editor.container.classList.contains("plain-mode");
2529
+ const isPreview = this.editor.container.classList.contains("preview-mode");
2530
+ const currentMode = isPreview ? "preview" : isPlain ? "plain" : "normal";
2531
+ const modes = [
2532
+ { id: "normal", label: "Normal Edit", icon: "\u2713" },
2533
+ { id: "plain", label: "Plain Textarea", icon: "\u2713" },
2534
+ { id: "preview", label: "Preview Mode", icon: "\u2713" }
2535
+ ];
2536
+ modes.forEach((mode) => {
2537
+ const item = document.createElement("button");
2538
+ item.className = "overtype-dropdown-item";
2539
+ item.type = "button";
2540
+ const check = document.createElement("span");
2541
+ check.className = "overtype-dropdown-check";
2542
+ check.textContent = currentMode === mode.id ? mode.icon : "";
2543
+ const label = document.createElement("span");
2544
+ label.textContent = mode.label;
2545
+ item.appendChild(check);
2546
+ item.appendChild(label);
2547
+ if (currentMode === mode.id) {
2548
+ item.classList.add("active");
2549
+ }
2550
+ item.addEventListener("click", (e) => {
2551
+ e.stopPropagation();
2552
+ this.setViewMode(mode.id);
2553
+ dropdown.remove();
2554
+ this.viewModeButton.classList.remove("dropdown-active");
2555
+ document.removeEventListener("click", this.handleDocumentClick);
2556
+ });
2557
+ dropdown.appendChild(item);
2558
+ });
2559
+ return dropdown;
2560
+ }
2561
+ /**
2562
+ * Set view mode
2563
+ */
2564
+ setViewMode(mode) {
2565
+ this.editor.container.classList.remove("plain-mode", "preview-mode");
2566
+ switch (mode) {
2567
+ case "plain":
2568
+ this.editor.showPlainTextarea(true);
2569
+ break;
2570
+ case "preview":
2571
+ this.editor.showPreviewMode(true);
2572
+ break;
2573
+ case "normal":
2574
+ default:
2575
+ this.editor.showPlainTextarea(false);
2576
+ if (typeof this.editor.showPreviewMode === "function") {
2577
+ this.editor.showPreviewMode(false);
2578
+ }
2579
+ break;
2580
+ }
2581
+ }
2582
+ /**
2583
+ * Destroy toolbar
2584
+ */
2585
+ destroy() {
2586
+ if (this.container) {
2587
+ if (this.handleDocumentClick) {
2588
+ document.removeEventListener("click", this.handleDocumentClick);
2589
+ }
2590
+ this.container.remove();
2591
+ this.container = null;
2592
+ this.buttons = {};
2593
+ }
2594
+ }
2595
+ };
2596
+
2597
+ // src/link-tooltip.js
2598
+ var LinkTooltip = class {
2599
+ constructor(editor) {
2600
+ this.editor = editor;
2601
+ this.tooltip = null;
2602
+ this.currentLink = null;
2603
+ this.hideTimeout = null;
2604
+ this.init();
2605
+ }
2606
+ init() {
2607
+ const supportsAnchor = CSS.supports("position-anchor: --x") && CSS.supports("position-area: center");
2608
+ if (!supportsAnchor) {
2609
+ return;
2610
+ }
2611
+ this.createTooltip();
2612
+ this.editor.textarea.addEventListener("selectionchange", () => this.checkCursorPosition());
2613
+ this.editor.textarea.addEventListener("keyup", (e) => {
2614
+ if (e.key.includes("Arrow") || e.key === "Home" || e.key === "End") {
2615
+ this.checkCursorPosition();
2616
+ }
2617
+ });
2618
+ this.editor.textarea.addEventListener("input", () => this.hide());
2619
+ this.editor.textarea.addEventListener("scroll", () => this.hide());
2620
+ this.tooltip.addEventListener("mouseenter", () => this.cancelHide());
2621
+ this.tooltip.addEventListener("mouseleave", () => this.scheduleHide());
2622
+ }
2623
+ createTooltip() {
2624
+ this.tooltip = document.createElement("div");
2625
+ this.tooltip.className = "overtype-link-tooltip";
2626
+ const tooltipStyles = document.createElement("style");
2627
+ tooltipStyles.textContent = `
2628
+ @supports (position-anchor: --x) and (position-area: center) {
2629
+ .overtype-link-tooltip {
2630
+ position: absolute;
2631
+ position-anchor: var(--target-anchor, --link-0);
2632
+ position-area: block-end center;
2633
+ margin-top: 8px;
2634
+
2635
+ background: #333;
2636
+ color: white;
2637
+ padding: 6px 10px;
2638
+ border-radius: 16px;
2639
+ font-size: 12px;
2640
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
2641
+ display: none;
2642
+ z-index: 10000;
2643
+ cursor: pointer;
2644
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
2645
+ max-width: 300px;
2646
+ white-space: nowrap;
2647
+ overflow: hidden;
2648
+ text-overflow: ellipsis;
2649
+
2650
+ position-try: most-width block-end inline-end, flip-inline, block-start center;
2651
+ position-visibility: anchors-visible;
2652
+ }
2653
+
2654
+ .overtype-link-tooltip.visible {
2655
+ display: flex;
2656
+ }
2657
+ }
2658
+ `;
2659
+ document.head.appendChild(tooltipStyles);
2660
+ this.tooltip.innerHTML = `
2661
+ <span style="display: flex; align-items: center; gap: 6px;">
2662
+ <svg width="12" height="12" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink: 0;">
2663
+ <path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path>
2664
+ <path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"></path>
2665
+ </svg>
2666
+ <span class="overtype-link-tooltip-url"></span>
2667
+ </span>
2668
+ `;
2669
+ this.tooltip.addEventListener("click", (e) => {
2670
+ e.preventDefault();
2671
+ e.stopPropagation();
2672
+ if (this.currentLink) {
2673
+ window.open(this.currentLink.url, "_blank");
2674
+ this.hide();
2675
+ }
2676
+ });
2677
+ this.editor.container.appendChild(this.tooltip);
2678
+ }
2679
+ checkCursorPosition() {
2680
+ const cursorPos = this.editor.textarea.selectionStart;
2681
+ const text = this.editor.textarea.value;
2682
+ const linkInfo = this.findLinkAtPosition(text, cursorPos);
2683
+ if (linkInfo) {
2684
+ if (!this.currentLink || this.currentLink.url !== linkInfo.url || this.currentLink.index !== linkInfo.index) {
2685
+ this.show(linkInfo);
2686
+ }
2687
+ } else {
2688
+ this.scheduleHide();
2689
+ }
2690
+ }
2691
+ findLinkAtPosition(text, position) {
2692
+ const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
2693
+ let match;
2694
+ let linkIndex = 0;
2695
+ while ((match = linkRegex.exec(text)) !== null) {
2696
+ const start = match.index;
2697
+ const end = match.index + match[0].length;
2698
+ if (position >= start && position <= end) {
2699
+ return {
2700
+ text: match[1],
2701
+ url: match[2],
2702
+ index: linkIndex,
2703
+ start,
2704
+ end
2705
+ };
2706
+ }
2707
+ linkIndex++;
2708
+ }
2709
+ return null;
2710
+ }
2711
+ show(linkInfo) {
2712
+ this.currentLink = linkInfo;
2713
+ this.cancelHide();
2714
+ const urlSpan = this.tooltip.querySelector(".overtype-link-tooltip-url");
2715
+ urlSpan.textContent = linkInfo.url;
2716
+ this.tooltip.style.setProperty("--target-anchor", `--link-${linkInfo.index}`);
2717
+ this.tooltip.classList.add("visible");
2718
+ }
2719
+ hide() {
2720
+ this.tooltip.classList.remove("visible");
2721
+ this.currentLink = null;
2722
+ }
2723
+ scheduleHide() {
2724
+ this.cancelHide();
2725
+ this.hideTimeout = setTimeout(() => this.hide(), 300);
2726
+ }
2727
+ cancelHide() {
2728
+ if (this.hideTimeout) {
2729
+ clearTimeout(this.hideTimeout);
2730
+ this.hideTimeout = null;
2731
+ }
2732
+ }
2733
+ destroy() {
2734
+ this.cancelHide();
2735
+ if (this.tooltip && this.tooltip.parentNode) {
2736
+ this.tooltip.parentNode.removeChild(this.tooltip);
2737
+ }
2738
+ this.tooltip = null;
2739
+ this.currentLink = null;
2740
+ }
2741
+ };
2742
+
2743
+ // src/overtype.js
2744
+ var _OverType = class _OverType {
2745
+ /**
2746
+ * Constructor - Always returns an array of instances
2747
+ * @param {string|Element|NodeList|Array} target - Target element(s)
2748
+ * @param {Object} options - Configuration options
2749
+ * @returns {Array} Array of OverType instances
2750
+ */
2751
+ constructor(target, options = {}) {
2752
+ let elements;
2753
+ if (typeof target === "string") {
2754
+ elements = document.querySelectorAll(target);
2755
+ if (elements.length === 0) {
2756
+ throw new Error(`No elements found for selector: ${target}`);
2757
+ }
2758
+ elements = Array.from(elements);
2759
+ } else if (target instanceof Element) {
2760
+ elements = [target];
2761
+ } else if (target instanceof NodeList) {
2762
+ elements = Array.from(target);
2763
+ } else if (Array.isArray(target)) {
2764
+ elements = target;
2765
+ } else {
2766
+ throw new Error("Invalid target: must be selector string, Element, NodeList, or Array");
2767
+ }
2768
+ const instances = elements.map((element) => {
2769
+ if (element.overTypeInstance) {
2770
+ element.overTypeInstance.reinit(options);
2771
+ return element.overTypeInstance;
2772
+ }
2773
+ const instance = Object.create(_OverType.prototype);
2774
+ instance._init(element, options);
2775
+ element.overTypeInstance = instance;
2776
+ _OverType.instances.set(element, instance);
2777
+ return instance;
2778
+ });
2779
+ return instances;
2780
+ }
2781
+ /**
2782
+ * Internal initialization
2783
+ * @private
2784
+ */
2785
+ _init(element, options = {}) {
2786
+ this.element = element;
2787
+ this.instanceTheme = options.theme || null;
2788
+ this.options = this._mergeOptions(options);
2789
+ this.instanceId = ++_OverType.instanceCount;
2790
+ this.initialized = false;
2791
+ _OverType.injectStyles();
2792
+ _OverType.initGlobalListeners();
2793
+ const container = element.querySelector(".overtype-container");
2794
+ const wrapper = element.querySelector(".overtype-wrapper");
2795
+ if (container || wrapper) {
2796
+ this._recoverFromDOM(container, wrapper);
2797
+ } else {
2798
+ this._buildFromScratch();
2799
+ }
2800
+ this.shortcuts = new ShortcutsManager(this);
2801
+ this.linkTooltip = new LinkTooltip(this);
2802
+ if (this.options.toolbar) {
2803
+ this.toolbar = new Toolbar(this);
2804
+ this.toolbar.create();
2805
+ this.textarea.addEventListener("selectionchange", () => {
2806
+ this.toolbar.updateButtonStates();
2807
+ });
2808
+ this.textarea.addEventListener("input", () => {
2809
+ this.toolbar.updateButtonStates();
2810
+ });
2811
+ }
2812
+ this.initialized = true;
2813
+ if (this.options.onChange) {
2814
+ this.options.onChange(this.getValue(), this);
2815
+ }
2816
+ }
2817
+ /**
2818
+ * Merge user options with defaults
2819
+ * @private
2820
+ */
2821
+ _mergeOptions(options) {
2822
+ const defaults = {
2823
+ // Typography
2824
+ fontSize: "14px",
2825
+ lineHeight: 1.6,
2826
+ /* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
2827
+ fontFamily: '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
2828
+ padding: "16px",
2829
+ // Mobile styles
2830
+ mobile: {
2831
+ fontSize: "16px",
2832
+ // Prevent zoom on iOS
2833
+ padding: "12px",
2834
+ lineHeight: 1.5
2835
+ },
2836
+ // Native textarea properties
2837
+ textareaProps: {},
2838
+ // Behavior
2839
+ autofocus: false,
2840
+ autoResize: false,
2841
+ // Auto-expand height with content
2842
+ minHeight: "100px",
2843
+ // Minimum height for autoResize mode
2844
+ maxHeight: null,
2845
+ // Maximum height for autoResize mode (null = unlimited)
2846
+ placeholder: "Start typing...",
2847
+ value: "",
2848
+ // Callbacks
2849
+ onChange: null,
2850
+ onKeydown: null,
2851
+ // Features
2852
+ showActiveLineRaw: false,
2853
+ showStats: false,
2854
+ toolbar: false,
2855
+ statsFormatter: null
2856
+ };
2857
+ const { theme, colors, ...cleanOptions } = options;
2858
+ return {
2859
+ ...defaults,
2860
+ ...cleanOptions
2861
+ };
2862
+ }
2863
+ /**
2864
+ * Recover from existing DOM structure
2865
+ * @private
2866
+ */
2867
+ _recoverFromDOM(container, wrapper) {
2868
+ if (container && container.classList.contains("overtype-container")) {
2869
+ this.container = container;
2870
+ this.wrapper = container.querySelector(".overtype-wrapper");
2871
+ } else if (wrapper) {
2872
+ this.wrapper = wrapper;
2873
+ this.container = document.createElement("div");
2874
+ this.container.className = "overtype-container";
2875
+ const themeToUse = this.instanceTheme || _OverType.currentTheme || solar;
2876
+ const themeName = typeof themeToUse === "string" ? themeToUse : themeToUse.name;
2877
+ if (themeName) {
2878
+ this.container.setAttribute("data-theme", themeName);
2879
+ }
2880
+ if (this.instanceTheme) {
2881
+ const themeObj = typeof this.instanceTheme === "string" ? getTheme(this.instanceTheme) : this.instanceTheme;
2882
+ if (themeObj && themeObj.colors) {
2883
+ const cssVars = themeToCSSVars(themeObj.colors);
2884
+ this.container.style.cssText += cssVars;
2885
+ }
2886
+ }
2887
+ wrapper.parentNode.insertBefore(this.container, wrapper);
2888
+ this.container.appendChild(wrapper);
2889
+ }
2890
+ if (!this.wrapper) {
2891
+ if (container)
2892
+ container.remove();
2893
+ if (wrapper)
2894
+ wrapper.remove();
2895
+ this._buildFromScratch();
2896
+ return;
2897
+ }
2898
+ this.textarea = this.wrapper.querySelector(".overtype-input");
2899
+ this.preview = this.wrapper.querySelector(".overtype-preview");
2900
+ if (!this.textarea || !this.preview) {
2901
+ this.container.remove();
2902
+ this._buildFromScratch();
2903
+ return;
2904
+ }
2905
+ this.wrapper._instance = this;
2906
+ if (this.options.fontSize) {
2907
+ this.wrapper.style.setProperty("--instance-font-size", this.options.fontSize);
2908
+ }
2909
+ if (this.options.lineHeight) {
2910
+ this.wrapper.style.setProperty("--instance-line-height", String(this.options.lineHeight));
2911
+ }
2912
+ if (this.options.padding) {
2913
+ this.wrapper.style.setProperty("--instance-padding", this.options.padding);
2914
+ }
2915
+ this._configureTextarea();
2916
+ this._applyOptions();
2917
+ }
2918
+ /**
2919
+ * Build editor from scratch
2920
+ * @private
2921
+ */
2922
+ _buildFromScratch() {
2923
+ const content = this._extractContent();
2924
+ this.element.innerHTML = "";
2925
+ this._createDOM();
2926
+ if (content || this.options.value) {
2927
+ this.setValue(content || this.options.value);
2928
+ }
2929
+ this._applyOptions();
2930
+ }
2931
+ /**
2932
+ * Extract content from element
2933
+ * @private
2934
+ */
2935
+ _extractContent() {
2936
+ const textarea = this.element.querySelector(".overtype-input");
2937
+ if (textarea)
2938
+ return textarea.value;
2939
+ return this.element.textContent || "";
2940
+ }
2941
+ /**
2942
+ * Create DOM structure
2943
+ * @private
2944
+ */
2945
+ _createDOM() {
2946
+ this.container = document.createElement("div");
2947
+ this.container.className = "overtype-container";
2948
+ const themeToUse = this.instanceTheme || _OverType.currentTheme || solar;
2949
+ const themeName = typeof themeToUse === "string" ? themeToUse : themeToUse.name;
2950
+ if (themeName) {
2951
+ this.container.setAttribute("data-theme", themeName);
2952
+ }
2953
+ if (this.instanceTheme) {
2954
+ const themeObj = typeof this.instanceTheme === "string" ? getTheme(this.instanceTheme) : this.instanceTheme;
2955
+ if (themeObj && themeObj.colors) {
2956
+ const cssVars = themeToCSSVars(themeObj.colors);
2957
+ this.container.style.cssText += cssVars;
2958
+ }
2959
+ }
2960
+ this.wrapper = document.createElement("div");
2961
+ this.wrapper.className = "overtype-wrapper";
2962
+ if (this.options.fontSize) {
2963
+ this.wrapper.style.setProperty("--instance-font-size", this.options.fontSize);
2964
+ }
2965
+ if (this.options.lineHeight) {
2966
+ this.wrapper.style.setProperty("--instance-line-height", String(this.options.lineHeight));
2967
+ }
2968
+ if (this.options.padding) {
2969
+ this.wrapper.style.setProperty("--instance-padding", this.options.padding);
2970
+ }
2971
+ this.wrapper._instance = this;
2972
+ this.textarea = document.createElement("textarea");
2973
+ this.textarea.className = "overtype-input";
2974
+ this.textarea.placeholder = this.options.placeholder;
2975
+ this._configureTextarea();
2976
+ if (this.options.textareaProps) {
2977
+ Object.entries(this.options.textareaProps).forEach(([key, value]) => {
2978
+ if (key === "className" || key === "class") {
2979
+ this.textarea.className += " " + value;
2980
+ } else if (key === "style" && typeof value === "object") {
2981
+ Object.assign(this.textarea.style, value);
2982
+ } else {
2983
+ this.textarea.setAttribute(key, value);
2984
+ }
2985
+ });
2986
+ }
2987
+ this.preview = document.createElement("div");
2988
+ this.preview.className = "overtype-preview";
2989
+ this.preview.setAttribute("aria-hidden", "true");
2990
+ this.wrapper.appendChild(this.textarea);
2991
+ this.wrapper.appendChild(this.preview);
2992
+ this.container.appendChild(this.wrapper);
2993
+ if (this.options.showStats) {
2994
+ this.statsBar = document.createElement("div");
2995
+ this.statsBar.className = "overtype-stats";
2996
+ this.container.appendChild(this.statsBar);
2997
+ this._updateStats();
2998
+ }
2999
+ this.element.appendChild(this.container);
3000
+ if (window.location.pathname.includes("demo.html")) {
3001
+ console.log("_createDOM completed:", {
3002
+ elementId: this.element.id,
3003
+ autoResize: this.options.autoResize,
3004
+ containerClasses: this.container.className,
3005
+ hasStats: !!this.statsBar,
3006
+ hasToolbar: this.options.toolbar
3007
+ });
3008
+ }
3009
+ if (this.options.autoResize) {
3010
+ this._setupAutoResize();
3011
+ } else {
3012
+ this.container.classList.remove("overtype-auto-resize");
3013
+ if (window.location.pathname.includes("demo.html")) {
3014
+ console.log("Removed auto-resize class from:", this.element.id);
3015
+ }
3016
+ }
3017
+ }
3018
+ /**
3019
+ * Configure textarea attributes
3020
+ * @private
3021
+ */
3022
+ _configureTextarea() {
3023
+ this.textarea.setAttribute("autocomplete", "off");
3024
+ this.textarea.setAttribute("autocorrect", "off");
3025
+ this.textarea.setAttribute("autocapitalize", "off");
3026
+ this.textarea.setAttribute("spellcheck", "false");
3027
+ this.textarea.setAttribute("data-gramm", "false");
3028
+ this.textarea.setAttribute("data-gramm_editor", "false");
3029
+ this.textarea.setAttribute("data-enable-grammarly", "false");
3030
+ }
3031
+ /**
3032
+ * Apply options to the editor
3033
+ * @private
3034
+ */
3035
+ _applyOptions() {
3036
+ if (this.options.autofocus) {
3037
+ this.textarea.focus();
3038
+ }
3039
+ if (this.options.autoResize) {
3040
+ if (!this.container.classList.contains("overtype-auto-resize")) {
3041
+ this._setupAutoResize();
3042
+ }
3043
+ } else {
3044
+ this.container.classList.remove("overtype-auto-resize");
3045
+ }
3046
+ this.updatePreview();
3047
+ }
3048
+ /**
3049
+ * Update preview with parsed markdown
3050
+ */
3051
+ updatePreview() {
3052
+ const text = this.textarea.value;
3053
+ const cursorPos = this.textarea.selectionStart;
3054
+ const activeLine = this._getCurrentLine(text, cursorPos);
3055
+ const html = MarkdownParser.parse(text, activeLine, this.options.showActiveLineRaw);
3056
+ this.preview.innerHTML = html || '<span style="color: #808080;">Start typing...</span>';
3057
+ this._applyCodeBlockBackgrounds();
3058
+ if (this.options.showStats && this.statsBar) {
3059
+ this._updateStats();
3060
+ }
3061
+ if (this.options.onChange && this.initialized) {
3062
+ this.options.onChange(text, this);
3063
+ }
3064
+ }
3065
+ /**
3066
+ * Apply background styling to code blocks
3067
+ * @private
3068
+ */
3069
+ _applyCodeBlockBackgrounds() {
3070
+ const codeFences = this.preview.querySelectorAll(".code-fence");
3071
+ for (let i = 0; i < codeFences.length - 1; i += 2) {
3072
+ const openFence = codeFences[i];
3073
+ const closeFence = codeFences[i + 1];
3074
+ const openParent = openFence.parentElement;
3075
+ const closeParent = closeFence.parentElement;
3076
+ if (!openParent || !closeParent)
3077
+ continue;
3078
+ openFence.style.display = "block";
3079
+ closeFence.style.display = "block";
3080
+ openParent.classList.add("code-block-line");
3081
+ closeParent.classList.add("code-block-line");
3082
+ let currentDiv = openParent.nextElementSibling;
3083
+ while (currentDiv && currentDiv !== closeParent) {
3084
+ if (currentDiv.tagName === "DIV") {
3085
+ currentDiv.classList.add("code-block-line");
3086
+ const plainText = currentDiv.textContent;
3087
+ currentDiv.textContent = plainText;
3088
+ }
3089
+ currentDiv = currentDiv.nextElementSibling;
3090
+ if (!currentDiv)
3091
+ break;
3092
+ }
3093
+ }
3094
+ }
3095
+ /**
3096
+ * Get current line number from cursor position
3097
+ * @private
3098
+ */
3099
+ _getCurrentLine(text, cursorPos) {
3100
+ const lines = text.substring(0, cursorPos).split("\n");
3101
+ return lines.length - 1;
3102
+ }
3103
+ /**
3104
+ * Handle input events
3105
+ * @private
3106
+ */
3107
+ handleInput(event) {
3108
+ this.updatePreview();
3109
+ }
3110
+ /**
3111
+ * Handle keydown events
3112
+ * @private
3113
+ */
3114
+ handleKeydown(event) {
3115
+ if (event.key === "Tab") {
3116
+ event.preventDefault();
3117
+ const start = this.textarea.selectionStart;
3118
+ const end = this.textarea.selectionEnd;
3119
+ const value = this.textarea.value;
3120
+ if (start !== end && event.shiftKey) {
3121
+ const before = value.substring(0, start);
3122
+ const selection = value.substring(start, end);
3123
+ const after = value.substring(end);
3124
+ const lines = selection.split("\n");
3125
+ const outdented = lines.map((line) => line.replace(/^ /, "")).join("\n");
3126
+ if (document.execCommand) {
3127
+ this.textarea.setSelectionRange(start, end);
3128
+ document.execCommand("insertText", false, outdented);
3129
+ } else {
3130
+ this.textarea.value = before + outdented + after;
3131
+ this.textarea.selectionStart = start;
3132
+ this.textarea.selectionEnd = start + outdented.length;
3133
+ }
3134
+ } else if (start !== end) {
3135
+ const before = value.substring(0, start);
3136
+ const selection = value.substring(start, end);
3137
+ const after = value.substring(end);
3138
+ const lines = selection.split("\n");
3139
+ const indented = lines.map((line) => " " + line).join("\n");
3140
+ if (document.execCommand) {
3141
+ this.textarea.setSelectionRange(start, end);
3142
+ document.execCommand("insertText", false, indented);
3143
+ } else {
3144
+ this.textarea.value = before + indented + after;
3145
+ this.textarea.selectionStart = start;
3146
+ this.textarea.selectionEnd = start + indented.length;
3147
+ }
3148
+ } else {
3149
+ if (document.execCommand) {
3150
+ document.execCommand("insertText", false, " ");
3151
+ } else {
3152
+ this.textarea.value = value.substring(0, start) + " " + value.substring(end);
3153
+ this.textarea.selectionStart = this.textarea.selectionEnd = start + 2;
3154
+ }
3155
+ }
3156
+ this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3157
+ return;
3158
+ }
3159
+ const handled = this.shortcuts.handleKeydown(event);
3160
+ if (!handled && this.options.onKeydown) {
3161
+ this.options.onKeydown(event, this);
3162
+ }
3163
+ }
3164
+ /**
3165
+ * Handle scroll events
3166
+ * @private
3167
+ */
3168
+ handleScroll(event) {
3169
+ this.preview.scrollTop = this.textarea.scrollTop;
3170
+ this.preview.scrollLeft = this.textarea.scrollLeft;
3171
+ }
3172
+ /**
3173
+ * Get editor content
3174
+ * @returns {string} Current markdown content
3175
+ */
3176
+ getValue() {
3177
+ return this.textarea.value;
3178
+ }
3179
+ /**
3180
+ * Set editor content
3181
+ * @param {string} value - Markdown content to set
3182
+ */
3183
+ setValue(value) {
3184
+ this.textarea.value = value;
3185
+ this.updatePreview();
3186
+ if (this.options.autoResize) {
3187
+ this._updateAutoHeight();
3188
+ }
3189
+ }
3190
+ /**
3191
+ * Get the rendered HTML of the current content
3192
+ * @param {boolean} processForPreview - If true, post-processes HTML for preview mode (consolidates lists/code blocks)
3193
+ * @returns {string} Rendered HTML
3194
+ */
3195
+ getRenderedHTML(processForPreview = false) {
3196
+ const markdown = this.getValue();
3197
+ let html = MarkdownParser.parse(markdown);
3198
+ if (processForPreview) {
3199
+ html = MarkdownParser.postProcessHTML(html);
3200
+ }
3201
+ return html;
3202
+ }
3203
+ /**
3204
+ * Get the current preview element's HTML
3205
+ * @returns {string} Current preview HTML (as displayed)
3206
+ */
3207
+ getPreviewHTML() {
3208
+ return this.preview.innerHTML;
3209
+ }
3210
+ /**
3211
+ * Focus the editor
3212
+ */
3213
+ focus() {
3214
+ this.textarea.focus();
3215
+ }
3216
+ /**
3217
+ * Blur the editor
3218
+ */
3219
+ blur() {
3220
+ this.textarea.blur();
3221
+ }
3222
+ /**
3223
+ * Check if editor is initialized
3224
+ * @returns {boolean}
3225
+ */
3226
+ isInitialized() {
3227
+ return this.initialized;
3228
+ }
3229
+ /**
3230
+ * Re-initialize with new options
3231
+ * @param {Object} options - New options to apply
3232
+ */
3233
+ reinit(options = {}) {
3234
+ this.options = this._mergeOptions({ ...this.options, ...options });
3235
+ this._applyOptions();
3236
+ this.updatePreview();
3237
+ }
3238
+ /**
3239
+ * Update stats bar
3240
+ * @private
3241
+ */
3242
+ _updateStats() {
3243
+ if (!this.statsBar)
3244
+ return;
3245
+ const value = this.textarea.value;
3246
+ const lines = value.split("\n");
3247
+ const chars = value.length;
3248
+ const words = value.split(/\s+/).filter((w) => w.length > 0).length;
3249
+ const selectionStart = this.textarea.selectionStart;
3250
+ const beforeCursor = value.substring(0, selectionStart);
3251
+ const linesBeforeCursor = beforeCursor.split("\n");
3252
+ const currentLine = linesBeforeCursor.length;
3253
+ const currentColumn = linesBeforeCursor[linesBeforeCursor.length - 1].length + 1;
3254
+ if (this.options.statsFormatter) {
3255
+ this.statsBar.innerHTML = this.options.statsFormatter({
3256
+ chars,
3257
+ words,
3258
+ lines: lines.length,
3259
+ line: currentLine,
3260
+ column: currentColumn
3261
+ });
3262
+ } else {
3263
+ this.statsBar.innerHTML = `
3264
+ <div class="overtype-stat">
3265
+ <span class="live-dot"></span>
3266
+ <span>${chars} chars, ${words} words, ${lines.length} lines</span>
3267
+ </div>
3268
+ <div class="overtype-stat">Line ${currentLine}, Col ${currentColumn}</div>
3269
+ `;
3270
+ }
3271
+ }
3272
+ /**
3273
+ * Setup auto-resize functionality
3274
+ * @private
3275
+ */
3276
+ _setupAutoResize() {
3277
+ this.container.classList.add("overtype-auto-resize");
3278
+ this.previousHeight = null;
3279
+ this._updateAutoHeight();
3280
+ this.textarea.addEventListener("input", () => this._updateAutoHeight());
3281
+ window.addEventListener("resize", () => this._updateAutoHeight());
3282
+ }
3283
+ /**
3284
+ * Update height based on scrollHeight
3285
+ * @private
3286
+ */
3287
+ _updateAutoHeight() {
3288
+ if (!this.options.autoResize)
3289
+ return;
3290
+ const textarea = this.textarea;
3291
+ const preview = this.preview;
3292
+ const wrapper = this.wrapper;
3293
+ const computed = window.getComputedStyle(textarea);
3294
+ const paddingTop = parseFloat(computed.paddingTop);
3295
+ const paddingBottom = parseFloat(computed.paddingBottom);
3296
+ const scrollTop = textarea.scrollTop;
3297
+ textarea.style.setProperty("height", "auto", "important");
3298
+ let newHeight = textarea.scrollHeight;
3299
+ if (this.options.minHeight) {
3300
+ const minHeight = parseInt(this.options.minHeight);
3301
+ newHeight = Math.max(newHeight, minHeight);
3302
+ }
3303
+ let overflow = "hidden";
3304
+ if (this.options.maxHeight) {
3305
+ const maxHeight = parseInt(this.options.maxHeight);
3306
+ if (newHeight > maxHeight) {
3307
+ newHeight = maxHeight;
3308
+ overflow = "auto";
3309
+ }
3310
+ }
3311
+ const heightPx = newHeight + "px";
3312
+ textarea.style.setProperty("height", heightPx, "important");
3313
+ textarea.style.setProperty("overflow-y", overflow, "important");
3314
+ preview.style.setProperty("height", heightPx, "important");
3315
+ preview.style.setProperty("overflow-y", overflow, "important");
3316
+ wrapper.style.setProperty("height", heightPx, "important");
3317
+ textarea.scrollTop = scrollTop;
3318
+ preview.scrollTop = scrollTop;
3319
+ if (this.previousHeight !== newHeight) {
3320
+ this.previousHeight = newHeight;
3321
+ }
3322
+ }
3323
+ /**
3324
+ * Show or hide stats bar
3325
+ * @param {boolean} show - Whether to show stats
3326
+ */
3327
+ showStats(show) {
3328
+ this.options.showStats = show;
3329
+ if (show && !this.statsBar) {
3330
+ this.statsBar = document.createElement("div");
3331
+ this.statsBar.className = "overtype-stats";
3332
+ this.container.appendChild(this.statsBar);
3333
+ this._updateStats();
3334
+ } else if (!show && this.statsBar) {
3335
+ this.statsBar.remove();
3336
+ this.statsBar = null;
3337
+ }
3338
+ }
3339
+ /**
3340
+ * Show or hide the plain textarea (toggle overlay visibility)
3341
+ * @param {boolean} show - true to show plain textarea (hide overlay), false to show overlay
3342
+ * @returns {boolean} Current plain textarea state
3343
+ */
3344
+ showPlainTextarea(show) {
3345
+ if (show) {
3346
+ this.container.classList.add("plain-mode");
3347
+ } else {
3348
+ this.container.classList.remove("plain-mode");
3349
+ }
3350
+ if (this.toolbar) {
3351
+ const toggleBtn = this.container.querySelector('[data-action="toggle-plain"]');
3352
+ if (toggleBtn) {
3353
+ toggleBtn.classList.toggle("active", !show);
3354
+ toggleBtn.title = show ? "Show markdown preview" : "Show plain textarea";
3355
+ }
3356
+ }
3357
+ return show;
3358
+ }
3359
+ /**
3360
+ * Show/hide preview mode
3361
+ * @param {boolean} show - Show preview mode if true, edit mode if false
3362
+ * @returns {boolean} Current preview mode state
3363
+ */
3364
+ showPreviewMode(show) {
3365
+ if (show) {
3366
+ this.container.classList.add("preview-mode");
3367
+ } else {
3368
+ this.container.classList.remove("preview-mode");
3369
+ }
3370
+ return show;
3371
+ }
3372
+ /**
3373
+ * Destroy the editor instance
3374
+ */
3375
+ destroy() {
3376
+ this.element.overTypeInstance = null;
3377
+ _OverType.instances.delete(this.element);
3378
+ if (this.shortcuts) {
3379
+ this.shortcuts.destroy();
3380
+ }
3381
+ if (this.wrapper) {
3382
+ const content = this.getValue();
3383
+ this.wrapper.remove();
3384
+ this.element.textContent = content;
3385
+ }
3386
+ this.initialized = false;
3387
+ }
3388
+ // ===== Static Methods =====
3389
+ /**
3390
+ * Initialize multiple editors (static convenience method)
3391
+ * @param {string|Element|NodeList|Array} target - Target element(s)
3392
+ * @param {Object} options - Configuration options
3393
+ * @returns {Array} Array of OverType instances
3394
+ */
3395
+ static init(target, options = {}) {
3396
+ return new _OverType(target, options);
3397
+ }
3398
+ /**
3399
+ * Get instance from element
3400
+ * @param {Element} element - DOM element
3401
+ * @returns {OverType|null} OverType instance or null
3402
+ */
3403
+ static getInstance(element) {
3404
+ return element.overTypeInstance || _OverType.instances.get(element) || null;
3405
+ }
3406
+ /**
3407
+ * Destroy all instances
3408
+ */
3409
+ static destroyAll() {
3410
+ const elements = document.querySelectorAll("[data-overtype-instance]");
3411
+ elements.forEach((element) => {
3412
+ const instance = _OverType.getInstance(element);
3413
+ if (instance) {
3414
+ instance.destroy();
3415
+ }
3416
+ });
3417
+ }
3418
+ /**
3419
+ * Inject styles into the document
3420
+ * @param {boolean} force - Force re-injection
3421
+ */
3422
+ static injectStyles(force = false) {
3423
+ if (_OverType.stylesInjected && !force)
3424
+ return;
3425
+ const existing = document.querySelector("style.overtype-styles");
3426
+ if (existing) {
3427
+ existing.remove();
3428
+ }
3429
+ const theme = _OverType.currentTheme || solar;
3430
+ const styles = generateStyles({ theme });
3431
+ const styleEl = document.createElement("style");
3432
+ styleEl.className = "overtype-styles";
3433
+ styleEl.textContent = styles;
3434
+ document.head.appendChild(styleEl);
3435
+ _OverType.stylesInjected = true;
3436
+ }
3437
+ /**
3438
+ * Set global theme for all OverType instances
3439
+ * @param {string|Object} theme - Theme name or custom theme object
3440
+ * @param {Object} customColors - Optional color overrides
3441
+ */
3442
+ static setTheme(theme, customColors = null) {
3443
+ let themeObj = typeof theme === "string" ? getTheme(theme) : theme;
3444
+ if (customColors) {
3445
+ themeObj = mergeTheme(themeObj, customColors);
3446
+ }
3447
+ _OverType.currentTheme = themeObj;
3448
+ _OverType.injectStyles(true);
3449
+ document.querySelectorAll(".overtype-container").forEach((container) => {
3450
+ const themeName = typeof themeObj === "string" ? themeObj : themeObj.name;
3451
+ if (themeName) {
3452
+ container.setAttribute("data-theme", themeName);
3453
+ }
3454
+ });
3455
+ document.querySelectorAll(".overtype-wrapper").forEach((wrapper) => {
3456
+ if (!wrapper.closest(".overtype-container")) {
3457
+ const themeName = typeof themeObj === "string" ? themeObj : themeObj.name;
3458
+ if (themeName) {
3459
+ wrapper.setAttribute("data-theme", themeName);
3460
+ }
3461
+ }
3462
+ const instance = wrapper._instance;
3463
+ if (instance) {
3464
+ instance.updatePreview();
3465
+ }
3466
+ });
3467
+ }
3468
+ /**
3469
+ * Initialize global event listeners
3470
+ */
3471
+ static initGlobalListeners() {
3472
+ if (_OverType.globalListenersInitialized)
3473
+ return;
3474
+ document.addEventListener("input", (e) => {
3475
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
3476
+ const wrapper = e.target.closest(".overtype-wrapper");
3477
+ const instance = wrapper == null ? void 0 : wrapper._instance;
3478
+ if (instance)
3479
+ instance.handleInput(e);
3480
+ }
3481
+ });
3482
+ document.addEventListener("keydown", (e) => {
3483
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
3484
+ const wrapper = e.target.closest(".overtype-wrapper");
3485
+ const instance = wrapper == null ? void 0 : wrapper._instance;
3486
+ if (instance)
3487
+ instance.handleKeydown(e);
3488
+ }
3489
+ });
3490
+ document.addEventListener("scroll", (e) => {
3491
+ if (e.target && e.target.classList && e.target.classList.contains("overtype-input")) {
3492
+ const wrapper = e.target.closest(".overtype-wrapper");
3493
+ const instance = wrapper == null ? void 0 : wrapper._instance;
3494
+ if (instance)
3495
+ instance.handleScroll(e);
3496
+ }
3497
+ }, true);
3498
+ document.addEventListener("selectionchange", (e) => {
3499
+ const activeElement = document.activeElement;
3500
+ if (activeElement && activeElement.classList.contains("overtype-input")) {
3501
+ const wrapper = activeElement.closest(".overtype-wrapper");
3502
+ const instance = wrapper == null ? void 0 : wrapper._instance;
3503
+ if (instance) {
3504
+ if (instance.options.showStats && instance.statsBar) {
3505
+ instance._updateStats();
3506
+ }
3507
+ clearTimeout(instance._selectionTimeout);
3508
+ instance._selectionTimeout = setTimeout(() => {
3509
+ instance.updatePreview();
3510
+ }, 50);
3511
+ }
3512
+ }
3513
+ });
3514
+ _OverType.globalListenersInitialized = true;
3515
+ }
3516
+ };
3517
+ // Static properties
3518
+ __publicField(_OverType, "instances", /* @__PURE__ */ new WeakMap());
3519
+ __publicField(_OverType, "stylesInjected", false);
3520
+ __publicField(_OverType, "globalListenersInitialized", false);
3521
+ __publicField(_OverType, "instanceCount", 0);
3522
+ var OverType = _OverType;
3523
+ OverType.MarkdownParser = MarkdownParser;
3524
+ OverType.ShortcutsManager = ShortcutsManager;
3525
+ OverType.themes = { solar, cave: getTheme("cave") };
3526
+ OverType.getTheme = getTheme;
3527
+ OverType.currentTheme = solar;
3528
+ if (typeof window !== "undefined" && typeof window.document !== "undefined") {
3529
+ window.OverType = OverType;
3530
+ }
3531
+ var overtype_default = OverType;
3532
+ // Annotate the CommonJS export names for ESM import in node:
3533
+ 0 && (module.exports = {
3534
+ OverType
3535
+ });
3536
+ /**
3537
+ * OverType - A lightweight markdown editor library with perfect WYSIWYG alignment
3538
+ * @version 1.0.0
3539
+ * @license MIT
3540
+ */
3541
+ //# sourceMappingURL=overtype.cjs.map