overtype 1.2.1 → 1.2.3

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v1.2.1
2
+ * OverType v1.2.2
3
3
  * A lightweight markdown editor library with perfect WYSIWYG alignment
4
4
  * @license MIT
5
5
  * @author Demo User
@@ -234,11 +234,22 @@ var MarkdownParser = class {
234
234
  static parse(text, activeLine = -1, showActiveLineRaw = false) {
235
235
  this.resetLinkIndex();
236
236
  const lines = text.split("\n");
237
+ let inCodeBlock = false;
237
238
  const parsedLines = lines.map((line, index) => {
238
239
  if (showActiveLineRaw && index === activeLine) {
239
240
  const content = this.escapeHtml(line) || " ";
240
241
  return `<div class="raw-line">${content}</div>`;
241
242
  }
243
+ const codeFenceRegex = /^```[^`]*$/;
244
+ if (codeFenceRegex.test(line)) {
245
+ inCodeBlock = !inCodeBlock;
246
+ return this.parseLine(line);
247
+ }
248
+ if (inCodeBlock) {
249
+ const escaped = this.escapeHtml(line);
250
+ const indented = this.preserveIndentation(escaped, line);
251
+ return `<div>${indented || "&nbsp;"}</div>`;
252
+ }
242
253
  return this.parseLine(line);
243
254
  });
244
255
  const html = parsedLines.join("");
@@ -278,23 +289,22 @@ var MarkdownParser = class {
278
289
  if (lang) {
279
290
  codeElement.className = `language-${lang}`;
280
291
  }
281
- container.insertBefore(currentCodeBlock, child);
282
- child.remove();
292
+ container.insertBefore(currentCodeBlock, child.nextSibling);
293
+ currentCodeBlock._codeElement = codeElement;
283
294
  continue;
284
295
  } else {
285
296
  inCodeBlock = false;
286
297
  currentCodeBlock = null;
287
- child.remove();
288
298
  continue;
289
299
  }
290
300
  }
291
301
  }
292
302
  if (inCodeBlock && currentCodeBlock && child.tagName === "DIV" && !child.querySelector(".code-fence")) {
293
- const codeElement = currentCodeBlock.querySelector("code");
303
+ const codeElement = currentCodeBlock._codeElement || currentCodeBlock.querySelector("code");
294
304
  if (codeElement.textContent.length > 0) {
295
305
  codeElement.textContent += "\n";
296
306
  }
297
- const lineText = child.innerHTML.replace(/&nbsp;/g, " ").replace(/<[^>]*>/g, "");
307
+ const lineText = child.textContent.replace(/\u00A0/g, " ");
298
308
  codeElement.textContent += lineText;
299
309
  child.remove();
300
310
  continue;
@@ -347,21 +357,163 @@ var MarkdownParser = class {
347
357
  }
348
358
  return match;
349
359
  });
350
- const codeBlockRegex = /<div><span class="code-fence">```([^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">```<\/span><\/div>/gs;
351
- processed = processed.replace(codeBlockRegex, (match, lang, content) => {
360
+ const codeBlockRegex = /<div><span class="code-fence">(```[^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">(```)<\/span><\/div>/gs;
361
+ processed = processed.replace(codeBlockRegex, (match, openFence, content, closeFence) => {
352
362
  const lines = content.match(/<div>(.*?)<\/div>/gs) || [];
353
363
  const codeContent = lines.map((line) => {
354
- 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, "&");
364
+ const text = line.replace(/<div>(.*?)<\/div>/s, "$1").replace(/&nbsp;/g, " ");
355
365
  return text;
356
366
  }).join("\n");
357
- const langClass = lang ? ` class="language-${lang.trim()}"` : "";
358
- return `<pre class="code-block"><code${langClass}>${this.escapeHtml(codeContent)}</code></pre>`;
367
+ const lang = openFence.slice(3).trim();
368
+ const langClass = lang ? ` class="language-${lang}"` : "";
369
+ let result = `<div><span class="code-fence">${openFence}</span></div>`;
370
+ result += `<pre class="code-block"><code${langClass}>${codeContent}</code></pre>`;
371
+ result += `<div><span class="code-fence">${closeFence}</span></div>`;
372
+ return result;
359
373
  });
360
374
  return processed;
361
375
  }
376
+ /**
377
+ * Get list context at cursor position
378
+ * @param {string} text - Full text content
379
+ * @param {number} cursorPosition - Current cursor position
380
+ * @returns {Object} List context information
381
+ */
382
+ static getListContext(text, cursorPosition) {
383
+ const lines = text.split("\n");
384
+ let currentPos = 0;
385
+ let lineIndex = 0;
386
+ let lineStart = 0;
387
+ for (let i = 0; i < lines.length; i++) {
388
+ const lineLength = lines[i].length;
389
+ if (currentPos + lineLength >= cursorPosition) {
390
+ lineIndex = i;
391
+ lineStart = currentPos;
392
+ break;
393
+ }
394
+ currentPos += lineLength + 1;
395
+ }
396
+ const currentLine = lines[lineIndex];
397
+ const lineEnd = lineStart + currentLine.length;
398
+ const checkboxMatch = currentLine.match(this.LIST_PATTERNS.checkbox);
399
+ if (checkboxMatch) {
400
+ return {
401
+ inList: true,
402
+ listType: "checkbox",
403
+ indent: checkboxMatch[1],
404
+ marker: "-",
405
+ checked: checkboxMatch[2] === "x",
406
+ content: checkboxMatch[3],
407
+ lineStart,
408
+ lineEnd,
409
+ markerEndPos: lineStart + checkboxMatch[1].length + checkboxMatch[2].length + 5
410
+ // indent + "- [ ] "
411
+ };
412
+ }
413
+ const bulletMatch = currentLine.match(this.LIST_PATTERNS.bullet);
414
+ if (bulletMatch) {
415
+ return {
416
+ inList: true,
417
+ listType: "bullet",
418
+ indent: bulletMatch[1],
419
+ marker: bulletMatch[2],
420
+ content: bulletMatch[3],
421
+ lineStart,
422
+ lineEnd,
423
+ markerEndPos: lineStart + bulletMatch[1].length + bulletMatch[2].length + 1
424
+ // indent + marker + space
425
+ };
426
+ }
427
+ const numberedMatch = currentLine.match(this.LIST_PATTERNS.numbered);
428
+ if (numberedMatch) {
429
+ return {
430
+ inList: true,
431
+ listType: "numbered",
432
+ indent: numberedMatch[1],
433
+ marker: parseInt(numberedMatch[2]),
434
+ content: numberedMatch[3],
435
+ lineStart,
436
+ lineEnd,
437
+ markerEndPos: lineStart + numberedMatch[1].length + numberedMatch[2].length + 2
438
+ // indent + number + ". "
439
+ };
440
+ }
441
+ return {
442
+ inList: false,
443
+ listType: null,
444
+ indent: "",
445
+ marker: null,
446
+ content: currentLine,
447
+ lineStart,
448
+ lineEnd,
449
+ markerEndPos: lineStart
450
+ };
451
+ }
452
+ /**
453
+ * Create a new list item based on context
454
+ * @param {Object} context - List context from getListContext
455
+ * @returns {string} New list item text
456
+ */
457
+ static createNewListItem(context) {
458
+ switch (context.listType) {
459
+ case "bullet":
460
+ return `${context.indent}${context.marker} `;
461
+ case "numbered":
462
+ return `${context.indent}${context.marker + 1}. `;
463
+ case "checkbox":
464
+ return `${context.indent}- [ ] `;
465
+ default:
466
+ return "";
467
+ }
468
+ }
469
+ /**
470
+ * Renumber all numbered lists in text
471
+ * @param {string} text - Text containing numbered lists
472
+ * @returns {string} Text with renumbered lists
473
+ */
474
+ static renumberLists(text) {
475
+ const lines = text.split("\n");
476
+ const numbersByIndent = /* @__PURE__ */ new Map();
477
+ let inList = false;
478
+ const result = lines.map((line) => {
479
+ const match = line.match(this.LIST_PATTERNS.numbered);
480
+ if (match) {
481
+ const indent = match[1];
482
+ const indentLevel = indent.length;
483
+ const content = match[3];
484
+ if (!inList) {
485
+ numbersByIndent.clear();
486
+ }
487
+ const currentNumber = (numbersByIndent.get(indentLevel) || 0) + 1;
488
+ numbersByIndent.set(indentLevel, currentNumber);
489
+ for (const [level] of numbersByIndent) {
490
+ if (level > indentLevel) {
491
+ numbersByIndent.delete(level);
492
+ }
493
+ }
494
+ inList = true;
495
+ return `${indent}${currentNumber}. ${content}`;
496
+ } else {
497
+ if (line.trim() === "" || !line.match(/^\s/)) {
498
+ inList = false;
499
+ numbersByIndent.clear();
500
+ }
501
+ return line;
502
+ }
503
+ });
504
+ return result.join("\n");
505
+ }
362
506
  };
363
507
  // Track link index for anchor naming
364
508
  __publicField(MarkdownParser, "linkIndex", 0);
509
+ /**
510
+ * List pattern definitions
511
+ */
512
+ __publicField(MarkdownParser, "LIST_PATTERNS", {
513
+ bullet: /^(\s*)([-*+])\s+(.*)$/,
514
+ numbered: /^(\s*)(\d+)\.\s+(.*)$/,
515
+ checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
516
+ });
365
517
 
366
518
  // node_modules/markdown-actions/dist/markdown-actions.esm.js
367
519
  var __defProp2 = Object.defineProperty;
@@ -1496,11 +1648,17 @@ function generateStyles(options = {}) {
1496
1648
  position: relative !important; /* Override reset - needed for absolute children */
1497
1649
  overflow: visible !important; /* Allow dropdown to overflow container */
1498
1650
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1651
+ text-align: left !important;
1499
1652
  ${themeVars ? `
1500
1653
  /* Theme Variables */
1501
1654
  ${themeVars}` : ""}
1502
1655
  }
1503
1656
 
1657
+ /* Force left alignment for all elements in the editor */
1658
+ .overtype-container .overtype-wrapper * {
1659
+ text-align: left !important;
1660
+ }
1661
+
1504
1662
  /* Auto-resize mode styles */
1505
1663
  .overtype-container.overtype-auto-resize {
1506
1664
  height: auto !important;
@@ -2828,7 +2986,9 @@ var _OverType = class _OverType {
2828
2986
  showActiveLineRaw: false,
2829
2987
  showStats: false,
2830
2988
  toolbar: false,
2831
- statsFormatter: null
2989
+ statsFormatter: null,
2990
+ smartLists: true
2991
+ // Enable smart list continuation
2832
2992
  };
2833
2993
  const { theme, colors, ...cleanOptions } = options;
2834
2994
  return {
@@ -3055,17 +3215,6 @@ var _OverType = class _OverType {
3055
3215
  closeFence.style.display = "block";
3056
3216
  openParent.classList.add("code-block-line");
3057
3217
  closeParent.classList.add("code-block-line");
3058
- let currentDiv = openParent.nextElementSibling;
3059
- while (currentDiv && currentDiv !== closeParent) {
3060
- if (currentDiv.tagName === "DIV") {
3061
- currentDiv.classList.add("code-block-line");
3062
- const plainText = currentDiv.textContent;
3063
- currentDiv.textContent = plainText;
3064
- }
3065
- currentDiv = currentDiv.nextElementSibling;
3066
- if (!currentDiv)
3067
- break;
3068
- }
3069
3218
  }
3070
3219
  }
3071
3220
  /**
@@ -3132,11 +3281,113 @@ var _OverType = class _OverType {
3132
3281
  this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3133
3282
  return;
3134
3283
  }
3284
+ if (event.key === "Enter" && !event.shiftKey && !event.metaKey && !event.ctrlKey && this.options.smartLists) {
3285
+ if (this.handleSmartListContinuation()) {
3286
+ event.preventDefault();
3287
+ return;
3288
+ }
3289
+ }
3135
3290
  const handled = this.shortcuts.handleKeydown(event);
3136
3291
  if (!handled && this.options.onKeydown) {
3137
3292
  this.options.onKeydown(event, this);
3138
3293
  }
3139
3294
  }
3295
+ /**
3296
+ * Handle smart list continuation
3297
+ * @returns {boolean} Whether the event was handled
3298
+ */
3299
+ handleSmartListContinuation() {
3300
+ const textarea = this.textarea;
3301
+ const cursorPos = textarea.selectionStart;
3302
+ const context = MarkdownParser.getListContext(textarea.value, cursorPos);
3303
+ if (!context || !context.inList)
3304
+ return false;
3305
+ if (context.content.trim() === "" && cursorPos >= context.markerEndPos) {
3306
+ this.deleteListMarker(context);
3307
+ return true;
3308
+ }
3309
+ if (cursorPos > context.markerEndPos && cursorPos < context.lineEnd) {
3310
+ this.splitListItem(context, cursorPos);
3311
+ } else {
3312
+ this.insertNewListItem(context);
3313
+ }
3314
+ if (context.listType === "numbered") {
3315
+ this.scheduleNumberedListUpdate();
3316
+ }
3317
+ return true;
3318
+ }
3319
+ /**
3320
+ * Delete list marker and exit list
3321
+ * @private
3322
+ */
3323
+ deleteListMarker(context) {
3324
+ this.textarea.setSelectionRange(context.lineStart, context.markerEndPos);
3325
+ document.execCommand("delete");
3326
+ this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3327
+ }
3328
+ /**
3329
+ * Insert new list item
3330
+ * @private
3331
+ */
3332
+ insertNewListItem(context) {
3333
+ const newItem = MarkdownParser.createNewListItem(context);
3334
+ document.execCommand("insertText", false, "\n" + newItem);
3335
+ this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3336
+ }
3337
+ /**
3338
+ * Split list item at cursor position
3339
+ * @private
3340
+ */
3341
+ splitListItem(context, cursorPos) {
3342
+ const textAfterCursor = context.content.substring(cursorPos - context.markerEndPos);
3343
+ this.textarea.setSelectionRange(cursorPos, context.lineEnd);
3344
+ document.execCommand("delete");
3345
+ const newItem = MarkdownParser.createNewListItem(context);
3346
+ document.execCommand("insertText", false, "\n" + newItem + textAfterCursor);
3347
+ const newCursorPos = this.textarea.selectionStart - textAfterCursor.length;
3348
+ this.textarea.setSelectionRange(newCursorPos, newCursorPos);
3349
+ this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3350
+ }
3351
+ /**
3352
+ * Schedule numbered list renumbering
3353
+ * @private
3354
+ */
3355
+ scheduleNumberedListUpdate() {
3356
+ if (this.numberUpdateTimeout) {
3357
+ clearTimeout(this.numberUpdateTimeout);
3358
+ }
3359
+ this.numberUpdateTimeout = setTimeout(() => {
3360
+ this.updateNumberedLists();
3361
+ }, 10);
3362
+ }
3363
+ /**
3364
+ * Update/renumber all numbered lists
3365
+ * @private
3366
+ */
3367
+ updateNumberedLists() {
3368
+ const value = this.textarea.value;
3369
+ const cursorPos = this.textarea.selectionStart;
3370
+ const newValue = MarkdownParser.renumberLists(value);
3371
+ if (newValue !== value) {
3372
+ let offset = 0;
3373
+ const oldLines = value.split("\n");
3374
+ const newLines = newValue.split("\n");
3375
+ let charCount = 0;
3376
+ for (let i = 0; i < oldLines.length && charCount < cursorPos; i++) {
3377
+ if (oldLines[i] !== newLines[i]) {
3378
+ const diff = newLines[i].length - oldLines[i].length;
3379
+ if (charCount + oldLines[i].length < cursorPos) {
3380
+ offset += diff;
3381
+ }
3382
+ }
3383
+ charCount += oldLines[i].length + 1;
3384
+ }
3385
+ this.textarea.value = newValue;
3386
+ const newCursorPos = cursorPos + offset;
3387
+ this.textarea.setSelectionRange(newCursorPos, newCursorPos);
3388
+ this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3389
+ }
3390
+ }
3140
3391
  /**
3141
3392
  * Handle scroll events
3142
3393
  * @private