overtype 1.2.2 → 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.
- package/README.md +9 -4
- package/dist/overtype.cjs +243 -1
- package/dist/overtype.cjs.map +2 -2
- package/dist/overtype.esm.js +243 -1
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +243 -1
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +56 -49
- package/package.json +1 -1
- package/src/overtype.js +145 -1
- package/src/parser.js +162 -0
package/dist/overtype.esm.js
CHANGED
|
@@ -373,9 +373,147 @@ var MarkdownParser = class {
|
|
|
373
373
|
});
|
|
374
374
|
return processed;
|
|
375
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
|
+
}
|
|
376
506
|
};
|
|
377
507
|
// Track link index for anchor naming
|
|
378
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
|
+
});
|
|
379
517
|
|
|
380
518
|
// node_modules/markdown-actions/dist/markdown-actions.esm.js
|
|
381
519
|
var __defProp2 = Object.defineProperty;
|
|
@@ -2848,7 +2986,9 @@ var _OverType = class _OverType {
|
|
|
2848
2986
|
showActiveLineRaw: false,
|
|
2849
2987
|
showStats: false,
|
|
2850
2988
|
toolbar: false,
|
|
2851
|
-
statsFormatter: null
|
|
2989
|
+
statsFormatter: null,
|
|
2990
|
+
smartLists: true
|
|
2991
|
+
// Enable smart list continuation
|
|
2852
2992
|
};
|
|
2853
2993
|
const { theme, colors, ...cleanOptions } = options;
|
|
2854
2994
|
return {
|
|
@@ -3141,11 +3281,113 @@ var _OverType = class _OverType {
|
|
|
3141
3281
|
this.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3142
3282
|
return;
|
|
3143
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
|
+
}
|
|
3144
3290
|
const handled = this.shortcuts.handleKeydown(event);
|
|
3145
3291
|
if (!handled && this.options.onKeydown) {
|
|
3146
3292
|
this.options.onKeydown(event, this);
|
|
3147
3293
|
}
|
|
3148
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
|
+
}
|
|
3149
3391
|
/**
|
|
3150
3392
|
* Handle scroll events
|
|
3151
3393
|
* @private
|