overtype 2.0.6 → 2.1.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.
- package/README.md +39 -3
- package/dist/overtype-webcomponent.esm.js +1435 -1283
- package/dist/overtype-webcomponent.esm.js.map +3 -3
- package/dist/overtype-webcomponent.js +1433 -1281
- package/dist/overtype-webcomponent.js.map +3 -3
- package/dist/overtype-webcomponent.min.js +50 -50
- package/dist/overtype.cjs +1435 -1283
- package/dist/overtype.cjs.map +3 -3
- package/dist/overtype.esm.js +1435 -1283
- package/dist/overtype.esm.js.map +3 -3
- package/dist/overtype.js +1668 -1515
- package/dist/overtype.js.map +3 -3
- package/dist/overtype.min.js +39 -38
- package/package.json +5 -3
- package/src/link-tooltip.js +18 -4
- package/src/overtype.js +204 -3
- package/src/parser.js +25 -2
- package/src/shortcuts.js +11 -76
- package/src/toolbar-buttons.js +25 -12
- package/src/toolbar.js +27 -48
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v2.0
|
|
2
|
+
* OverType v2.1.0
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author David Miranda
|
|
@@ -50,6 +50,24 @@ var OverTypeEditor = (() => {
|
|
|
50
50
|
static setCodeHighlighter(highlighter) {
|
|
51
51
|
this.codeHighlighter = highlighter;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Set custom syntax processor function
|
|
55
|
+
* @param {Function|null} processor - Function that takes (html) and returns modified HTML
|
|
56
|
+
*/
|
|
57
|
+
static setCustomSyntax(processor) {
|
|
58
|
+
this.customSyntax = processor;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Apply custom syntax processor to parsed HTML
|
|
62
|
+
* @param {string} html - Parsed HTML line
|
|
63
|
+
* @returns {string} HTML with custom syntax applied
|
|
64
|
+
*/
|
|
65
|
+
static applyCustomSyntax(html) {
|
|
66
|
+
if (this.customSyntax) {
|
|
67
|
+
return this.customSyntax(html);
|
|
68
|
+
}
|
|
69
|
+
return html;
|
|
70
|
+
}
|
|
53
71
|
/**
|
|
54
72
|
* Escape HTML special characters
|
|
55
73
|
* @param {string} text - Raw text to escape
|
|
@@ -388,14 +406,14 @@ var OverTypeEditor = (() => {
|
|
|
388
406
|
const codeFenceRegex = /^```[^`]*$/;
|
|
389
407
|
if (codeFenceRegex.test(line)) {
|
|
390
408
|
inCodeBlock = !inCodeBlock;
|
|
391
|
-
return this.parseLine(line, isPreviewMode);
|
|
409
|
+
return this.applyCustomSyntax(this.parseLine(line, isPreviewMode));
|
|
392
410
|
}
|
|
393
411
|
if (inCodeBlock) {
|
|
394
412
|
const escaped = this.escapeHtml(line);
|
|
395
413
|
const indented = this.preserveIndentation(escaped, line);
|
|
396
414
|
return `<div>${indented || " "}</div>`;
|
|
397
415
|
}
|
|
398
|
-
return this.parseLine(line, isPreviewMode);
|
|
416
|
+
return this.applyCustomSyntax(this.parseLine(line, isPreviewMode));
|
|
399
417
|
});
|
|
400
418
|
const html = parsedLines.join("");
|
|
401
419
|
return this.postProcessHTML(html, instanceHighlighter);
|
|
@@ -727,6 +745,8 @@ var OverTypeEditor = (() => {
|
|
|
727
745
|
__publicField(MarkdownParser, "linkIndex", 0);
|
|
728
746
|
// Global code highlighter function
|
|
729
747
|
__publicField(MarkdownParser, "codeHighlighter", null);
|
|
748
|
+
// Custom syntax processor function
|
|
749
|
+
__publicField(MarkdownParser, "customSyntax", null);
|
|
730
750
|
/**
|
|
731
751
|
* List pattern definitions
|
|
732
752
|
*/
|
|
@@ -736,940 +756,55 @@ var OverTypeEditor = (() => {
|
|
|
736
756
|
checkbox: /^(\s*)-\s+\[([ x])\]\s+(.*)$/
|
|
737
757
|
});
|
|
738
758
|
|
|
739
|
-
//
|
|
740
|
-
var
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
744
|
-
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
745
|
-
var __spreadValues = (a, b) => {
|
|
746
|
-
for (var prop in b || (b = {}))
|
|
747
|
-
if (__hasOwnProp2.call(b, prop))
|
|
748
|
-
__defNormalProp2(a, prop, b[prop]);
|
|
749
|
-
if (__getOwnPropSymbols)
|
|
750
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
751
|
-
if (__propIsEnum.call(b, prop))
|
|
752
|
-
__defNormalProp2(a, prop, b[prop]);
|
|
753
|
-
}
|
|
754
|
-
return a;
|
|
755
|
-
};
|
|
756
|
-
var FORMATS = {
|
|
757
|
-
bold: {
|
|
758
|
-
prefix: "**",
|
|
759
|
-
suffix: "**",
|
|
760
|
-
trimFirst: true
|
|
761
|
-
},
|
|
762
|
-
italic: {
|
|
763
|
-
prefix: "_",
|
|
764
|
-
suffix: "_",
|
|
765
|
-
trimFirst: true
|
|
766
|
-
},
|
|
767
|
-
code: {
|
|
768
|
-
prefix: "`",
|
|
769
|
-
suffix: "`",
|
|
770
|
-
blockPrefix: "```",
|
|
771
|
-
blockSuffix: "```"
|
|
772
|
-
},
|
|
773
|
-
link: {
|
|
774
|
-
prefix: "[",
|
|
775
|
-
suffix: "](url)",
|
|
776
|
-
replaceNext: "url",
|
|
777
|
-
scanFor: "https?://"
|
|
778
|
-
},
|
|
779
|
-
bulletList: {
|
|
780
|
-
prefix: "- ",
|
|
781
|
-
multiline: true,
|
|
782
|
-
unorderedList: true
|
|
783
|
-
},
|
|
784
|
-
numberedList: {
|
|
785
|
-
prefix: "1. ",
|
|
786
|
-
multiline: true,
|
|
787
|
-
orderedList: true
|
|
788
|
-
},
|
|
789
|
-
quote: {
|
|
790
|
-
prefix: "> ",
|
|
791
|
-
multiline: true,
|
|
792
|
-
surroundWithNewlines: true
|
|
793
|
-
},
|
|
794
|
-
taskList: {
|
|
795
|
-
prefix: "- [ ] ",
|
|
796
|
-
multiline: true,
|
|
797
|
-
surroundWithNewlines: true
|
|
798
|
-
},
|
|
799
|
-
header1: { prefix: "# " },
|
|
800
|
-
header2: { prefix: "## " },
|
|
801
|
-
header3: { prefix: "### " },
|
|
802
|
-
header4: { prefix: "#### " },
|
|
803
|
-
header5: { prefix: "##### " },
|
|
804
|
-
header6: { prefix: "###### " }
|
|
805
|
-
};
|
|
806
|
-
function getDefaultStyle() {
|
|
807
|
-
return {
|
|
808
|
-
prefix: "",
|
|
809
|
-
suffix: "",
|
|
810
|
-
blockPrefix: "",
|
|
811
|
-
blockSuffix: "",
|
|
812
|
-
multiline: false,
|
|
813
|
-
replaceNext: "",
|
|
814
|
-
prefixSpace: false,
|
|
815
|
-
scanFor: "",
|
|
816
|
-
surroundWithNewlines: false,
|
|
817
|
-
orderedList: false,
|
|
818
|
-
unorderedList: false,
|
|
819
|
-
trimFirst: false
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
function mergeWithDefaults(format) {
|
|
823
|
-
return __spreadValues(__spreadValues({}, getDefaultStyle()), format);
|
|
824
|
-
}
|
|
825
|
-
var debugMode = false;
|
|
826
|
-
function getDebugMode() {
|
|
827
|
-
return debugMode;
|
|
828
|
-
}
|
|
829
|
-
function debugLog(funcName, message, data) {
|
|
830
|
-
if (!debugMode)
|
|
831
|
-
return;
|
|
832
|
-
console.group(`\u{1F50D} ${funcName}`);
|
|
833
|
-
console.log(message);
|
|
834
|
-
if (data) {
|
|
835
|
-
console.log("Data:", data);
|
|
836
|
-
}
|
|
837
|
-
console.groupEnd();
|
|
838
|
-
}
|
|
839
|
-
function debugSelection(textarea, label) {
|
|
840
|
-
if (!debugMode)
|
|
841
|
-
return;
|
|
842
|
-
const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
843
|
-
console.group(`\u{1F4CD} Selection: ${label}`);
|
|
844
|
-
console.log("Position:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
845
|
-
console.log("Selected text:", JSON.stringify(selected));
|
|
846
|
-
console.log("Length:", selected.length);
|
|
847
|
-
const before = textarea.value.slice(Math.max(0, textarea.selectionStart - 10), textarea.selectionStart);
|
|
848
|
-
const after = textarea.value.slice(textarea.selectionEnd, Math.min(textarea.value.length, textarea.selectionEnd + 10));
|
|
849
|
-
console.log("Context:", JSON.stringify(before) + "[SELECTION]" + JSON.stringify(after));
|
|
850
|
-
console.groupEnd();
|
|
851
|
-
}
|
|
852
|
-
function debugResult(result) {
|
|
853
|
-
if (!debugMode)
|
|
854
|
-
return;
|
|
855
|
-
console.group("\u{1F4DD} Result");
|
|
856
|
-
console.log("Text to insert:", JSON.stringify(result.text));
|
|
857
|
-
console.log("New selection:", `${result.selectionStart}-${result.selectionEnd}`);
|
|
858
|
-
console.groupEnd();
|
|
859
|
-
}
|
|
860
|
-
var canInsertText = null;
|
|
861
|
-
function insertText(textarea, { text, selectionStart, selectionEnd }) {
|
|
862
|
-
const debugMode2 = getDebugMode();
|
|
863
|
-
if (debugMode2) {
|
|
864
|
-
console.group("\u{1F527} insertText");
|
|
865
|
-
console.log("Current selection:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
866
|
-
console.log("Text to insert:", JSON.stringify(text));
|
|
867
|
-
console.log("New selection to set:", selectionStart, "-", selectionEnd);
|
|
868
|
-
}
|
|
869
|
-
textarea.focus();
|
|
870
|
-
const originalSelectionStart = textarea.selectionStart;
|
|
871
|
-
const originalSelectionEnd = textarea.selectionEnd;
|
|
872
|
-
const before = textarea.value.slice(0, originalSelectionStart);
|
|
873
|
-
const after = textarea.value.slice(originalSelectionEnd);
|
|
874
|
-
if (debugMode2) {
|
|
875
|
-
console.log("Before text (last 20):", JSON.stringify(before.slice(-20)));
|
|
876
|
-
console.log("After text (first 20):", JSON.stringify(after.slice(0, 20)));
|
|
877
|
-
console.log("Selected text being replaced:", JSON.stringify(textarea.value.slice(originalSelectionStart, originalSelectionEnd)));
|
|
878
|
-
}
|
|
879
|
-
const originalValue = textarea.value;
|
|
880
|
-
const hasSelection = originalSelectionStart !== originalSelectionEnd;
|
|
881
|
-
if (canInsertText === null || canInsertText === true) {
|
|
882
|
-
textarea.contentEditable = "true";
|
|
883
|
-
try {
|
|
884
|
-
canInsertText = document.execCommand("insertText", false, text);
|
|
885
|
-
if (debugMode2)
|
|
886
|
-
console.log("execCommand returned:", canInsertText, "for text with", text.split("\n").length, "lines");
|
|
887
|
-
} catch (error) {
|
|
888
|
-
canInsertText = false;
|
|
889
|
-
if (debugMode2)
|
|
890
|
-
console.log("execCommand threw error:", error);
|
|
891
|
-
}
|
|
892
|
-
textarea.contentEditable = "false";
|
|
893
|
-
}
|
|
894
|
-
if (debugMode2) {
|
|
895
|
-
console.log("canInsertText before:", canInsertText);
|
|
896
|
-
console.log("execCommand result:", canInsertText);
|
|
759
|
+
// src/shortcuts.js
|
|
760
|
+
var ShortcutsManager = class {
|
|
761
|
+
constructor(editor) {
|
|
762
|
+
this.editor = editor;
|
|
897
763
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
764
|
+
/**
|
|
765
|
+
* Handle keydown events - called by OverType
|
|
766
|
+
* @param {KeyboardEvent} event - The keyboard event
|
|
767
|
+
* @returns {boolean} Whether the event was handled
|
|
768
|
+
*/
|
|
769
|
+
handleKeydown(event) {
|
|
770
|
+
const isMac = navigator.platform.toLowerCase().includes("mac");
|
|
771
|
+
const modKey = isMac ? event.metaKey : event.ctrlKey;
|
|
772
|
+
if (!modKey)
|
|
773
|
+
return false;
|
|
774
|
+
let actionId = null;
|
|
775
|
+
switch (event.key.toLowerCase()) {
|
|
776
|
+
case "b":
|
|
777
|
+
if (!event.shiftKey)
|
|
778
|
+
actionId = "toggleBold";
|
|
779
|
+
break;
|
|
780
|
+
case "i":
|
|
781
|
+
if (!event.shiftKey)
|
|
782
|
+
actionId = "toggleItalic";
|
|
783
|
+
break;
|
|
784
|
+
case "k":
|
|
785
|
+
if (!event.shiftKey)
|
|
786
|
+
actionId = "insertLink";
|
|
787
|
+
break;
|
|
788
|
+
case "7":
|
|
789
|
+
if (event.shiftKey)
|
|
790
|
+
actionId = "toggleNumberedList";
|
|
791
|
+
break;
|
|
792
|
+
case "8":
|
|
793
|
+
if (event.shiftKey)
|
|
794
|
+
actionId = "toggleBulletList";
|
|
795
|
+
break;
|
|
911
796
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
if (textarea.value === originalValue) {
|
|
917
|
-
if (debugMode2)
|
|
918
|
-
console.log("Value unchanged, doing manual replacement");
|
|
919
|
-
try {
|
|
920
|
-
document.execCommand("ms-beginUndoUnit");
|
|
921
|
-
} catch (e) {
|
|
922
|
-
}
|
|
923
|
-
textarea.value = before + text + after;
|
|
924
|
-
try {
|
|
925
|
-
document.execCommand("ms-endUndoUnit");
|
|
926
|
-
} catch (e) {
|
|
927
|
-
}
|
|
928
|
-
textarea.dispatchEvent(new CustomEvent("input", { bubbles: true, cancelable: true }));
|
|
929
|
-
} else {
|
|
930
|
-
if (debugMode2)
|
|
931
|
-
console.log("Value was changed by execCommand, skipping manual insertion");
|
|
797
|
+
if (actionId) {
|
|
798
|
+
event.preventDefault();
|
|
799
|
+
this.editor.performAction(actionId, event);
|
|
800
|
+
return true;
|
|
932
801
|
}
|
|
802
|
+
return false;
|
|
933
803
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
} else {
|
|
939
|
-
textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd);
|
|
940
|
-
}
|
|
941
|
-
if (debugMode2) {
|
|
942
|
-
console.log("Final value length:", textarea.value.length);
|
|
943
|
-
console.groupEnd();
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
function isMultipleLines(string) {
|
|
947
|
-
return string.trim().split("\n").length > 1;
|
|
948
|
-
}
|
|
949
|
-
function wordSelectionStart(text, i) {
|
|
950
|
-
let index = i;
|
|
951
|
-
while (text[index] && text[index - 1] != null && !text[index - 1].match(/\s/)) {
|
|
952
|
-
index--;
|
|
953
|
-
}
|
|
954
|
-
return index;
|
|
955
|
-
}
|
|
956
|
-
function wordSelectionEnd(text, i, multiline) {
|
|
957
|
-
let index = i;
|
|
958
|
-
const breakpoint = multiline ? /\n/ : /\s/;
|
|
959
|
-
while (text[index] && !text[index].match(breakpoint)) {
|
|
960
|
-
index++;
|
|
961
|
-
}
|
|
962
|
-
return index;
|
|
963
|
-
}
|
|
964
|
-
function expandSelectionToLine(textarea) {
|
|
965
|
-
const lines = textarea.value.split("\n");
|
|
966
|
-
let counter = 0;
|
|
967
|
-
for (let index = 0; index < lines.length; index++) {
|
|
968
|
-
const lineLength = lines[index].length + 1;
|
|
969
|
-
if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
|
|
970
|
-
textarea.selectionStart = counter;
|
|
971
|
-
}
|
|
972
|
-
if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
|
|
973
|
-
if (index === lines.length - 1) {
|
|
974
|
-
textarea.selectionEnd = Math.min(counter + lines[index].length, textarea.value.length);
|
|
975
|
-
} else {
|
|
976
|
-
textarea.selectionEnd = counter + lineLength - 1;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
counter += lineLength;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {
|
|
983
|
-
if (textarea.selectionStart === textarea.selectionEnd) {
|
|
984
|
-
textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
|
|
985
|
-
textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline);
|
|
986
|
-
} else {
|
|
987
|
-
const expandedSelectionStart = textarea.selectionStart - prefixToUse.length;
|
|
988
|
-
const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length;
|
|
989
|
-
const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse;
|
|
990
|
-
const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse;
|
|
991
|
-
if (beginsWithPrefix && endsWithSuffix) {
|
|
992
|
-
textarea.selectionStart = expandedSelectionStart;
|
|
993
|
-
textarea.selectionEnd = expandedSelectionEnd;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
997
|
-
}
|
|
998
|
-
function newlinesToSurroundSelectedText(textarea) {
|
|
999
|
-
const beforeSelection = textarea.value.slice(0, textarea.selectionStart);
|
|
1000
|
-
const afterSelection = textarea.value.slice(textarea.selectionEnd);
|
|
1001
|
-
const breaksBefore = beforeSelection.match(/\n*$/);
|
|
1002
|
-
const breaksAfter = afterSelection.match(/^\n*/);
|
|
1003
|
-
const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0;
|
|
1004
|
-
const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0;
|
|
1005
|
-
let newlinesToAppend = "";
|
|
1006
|
-
let newlinesToPrepend = "";
|
|
1007
|
-
if (beforeSelection.match(/\S/) && newlinesBeforeSelection < 2) {
|
|
1008
|
-
newlinesToAppend = "\n".repeat(2 - newlinesBeforeSelection);
|
|
1009
|
-
}
|
|
1010
|
-
if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
|
|
1011
|
-
newlinesToPrepend = "\n".repeat(2 - newlinesAfterSelection);
|
|
1012
|
-
}
|
|
1013
|
-
return { newlinesToAppend, newlinesToPrepend };
|
|
1014
|
-
}
|
|
1015
|
-
function applyLineOperation(textarea, operation, options = {}) {
|
|
1016
|
-
const originalStart = textarea.selectionStart;
|
|
1017
|
-
const originalEnd = textarea.selectionEnd;
|
|
1018
|
-
const noInitialSelection = originalStart === originalEnd;
|
|
1019
|
-
const value = textarea.value;
|
|
1020
|
-
let lineStart = originalStart;
|
|
1021
|
-
while (lineStart > 0 && value[lineStart - 1] !== "\n") {
|
|
1022
|
-
lineStart--;
|
|
1023
|
-
}
|
|
1024
|
-
if (noInitialSelection) {
|
|
1025
|
-
let lineEnd = originalStart;
|
|
1026
|
-
while (lineEnd < value.length && value[lineEnd] !== "\n") {
|
|
1027
|
-
lineEnd++;
|
|
1028
|
-
}
|
|
1029
|
-
textarea.selectionStart = lineStart;
|
|
1030
|
-
textarea.selectionEnd = lineEnd;
|
|
1031
|
-
} else {
|
|
1032
|
-
expandSelectionToLine(textarea);
|
|
1033
|
-
}
|
|
1034
|
-
const result = operation(textarea);
|
|
1035
|
-
if (options.adjustSelection) {
|
|
1036
|
-
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1037
|
-
const isRemoving = selectedText.startsWith(options.prefix);
|
|
1038
|
-
const adjusted = options.adjustSelection(isRemoving, originalStart, originalEnd, lineStart);
|
|
1039
|
-
result.selectionStart = adjusted.start;
|
|
1040
|
-
result.selectionEnd = adjusted.end;
|
|
1041
|
-
} else if (options.prefix) {
|
|
1042
|
-
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1043
|
-
const isRemoving = selectedText.startsWith(options.prefix);
|
|
1044
|
-
if (noInitialSelection) {
|
|
1045
|
-
if (isRemoving) {
|
|
1046
|
-
result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
|
|
1047
|
-
result.selectionEnd = result.selectionStart;
|
|
1048
|
-
} else {
|
|
1049
|
-
result.selectionStart = originalStart + options.prefix.length;
|
|
1050
|
-
result.selectionEnd = result.selectionStart;
|
|
1051
|
-
}
|
|
1052
|
-
} else {
|
|
1053
|
-
if (isRemoving) {
|
|
1054
|
-
result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
|
|
1055
|
-
result.selectionEnd = Math.max(originalEnd - options.prefix.length, lineStart);
|
|
1056
|
-
} else {
|
|
1057
|
-
result.selectionStart = originalStart + options.prefix.length;
|
|
1058
|
-
result.selectionEnd = originalEnd + options.prefix.length;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
return result;
|
|
1063
|
-
}
|
|
1064
|
-
function blockStyle(textarea, style) {
|
|
1065
|
-
let newlinesToAppend;
|
|
1066
|
-
let newlinesToPrepend;
|
|
1067
|
-
const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines, trimFirst } = style;
|
|
1068
|
-
const originalSelectionStart = textarea.selectionStart;
|
|
1069
|
-
const originalSelectionEnd = textarea.selectionEnd;
|
|
1070
|
-
let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1071
|
-
let prefixToUse = isMultipleLines(selectedText) && blockPrefix && blockPrefix.length > 0 ? `${blockPrefix}
|
|
1072
|
-
` : prefix;
|
|
1073
|
-
let suffixToUse = isMultipleLines(selectedText) && blockSuffix && blockSuffix.length > 0 ? `
|
|
1074
|
-
${blockSuffix}` : suffix;
|
|
1075
|
-
if (prefixSpace) {
|
|
1076
|
-
const beforeSelection = textarea.value[textarea.selectionStart - 1];
|
|
1077
|
-
if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\s/)) {
|
|
1078
|
-
prefixToUse = ` ${prefixToUse}`;
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, style.multiline);
|
|
1082
|
-
let selectionStart = textarea.selectionStart;
|
|
1083
|
-
let selectionEnd = textarea.selectionEnd;
|
|
1084
|
-
const hasReplaceNext = replaceNext && replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0;
|
|
1085
|
-
if (surroundWithNewlines) {
|
|
1086
|
-
const ref = newlinesToSurroundSelectedText(textarea);
|
|
1087
|
-
newlinesToAppend = ref.newlinesToAppend;
|
|
1088
|
-
newlinesToPrepend = ref.newlinesToPrepend;
|
|
1089
|
-
prefixToUse = newlinesToAppend + prefix;
|
|
1090
|
-
suffixToUse += newlinesToPrepend;
|
|
1091
|
-
}
|
|
1092
|
-
if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {
|
|
1093
|
-
const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length);
|
|
1094
|
-
if (originalSelectionStart === originalSelectionEnd) {
|
|
1095
|
-
let position = originalSelectionStart - prefixToUse.length;
|
|
1096
|
-
position = Math.max(position, selectionStart);
|
|
1097
|
-
position = Math.min(position, selectionStart + replacementText.length);
|
|
1098
|
-
selectionStart = selectionEnd = position;
|
|
1099
|
-
} else {
|
|
1100
|
-
selectionEnd = selectionStart + replacementText.length;
|
|
1101
|
-
}
|
|
1102
|
-
return { text: replacementText, selectionStart, selectionEnd };
|
|
1103
|
-
} else if (!hasReplaceNext) {
|
|
1104
|
-
let replacementText = prefixToUse + selectedText + suffixToUse;
|
|
1105
|
-
selectionStart = originalSelectionStart + prefixToUse.length;
|
|
1106
|
-
selectionEnd = originalSelectionEnd + prefixToUse.length;
|
|
1107
|
-
const whitespaceEdges = selectedText.match(/^\s*|\s*$/g);
|
|
1108
|
-
if (trimFirst && whitespaceEdges) {
|
|
1109
|
-
const leadingWhitespace = whitespaceEdges[0] || "";
|
|
1110
|
-
const trailingWhitespace = whitespaceEdges[1] || "";
|
|
1111
|
-
replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace;
|
|
1112
|
-
selectionStart += leadingWhitespace.length;
|
|
1113
|
-
selectionEnd -= trailingWhitespace.length;
|
|
1114
|
-
}
|
|
1115
|
-
return { text: replacementText, selectionStart, selectionEnd };
|
|
1116
|
-
} else if (scanFor && scanFor.length > 0 && selectedText.match(scanFor)) {
|
|
1117
|
-
suffixToUse = suffixToUse.replace(replaceNext, selectedText);
|
|
1118
|
-
const replacementText = prefixToUse + suffixToUse;
|
|
1119
|
-
selectionStart = selectionEnd = selectionStart + prefixToUse.length;
|
|
1120
|
-
return { text: replacementText, selectionStart, selectionEnd };
|
|
1121
|
-
} else {
|
|
1122
|
-
const replacementText = prefixToUse + selectedText + suffixToUse;
|
|
1123
|
-
selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext);
|
|
1124
|
-
selectionEnd = selectionStart + replaceNext.length;
|
|
1125
|
-
return { text: replacementText, selectionStart, selectionEnd };
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
function multilineStyle(textarea, style) {
|
|
1129
|
-
const { prefix, suffix, surroundWithNewlines } = style;
|
|
1130
|
-
let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1131
|
-
let selectionStart = textarea.selectionStart;
|
|
1132
|
-
let selectionEnd = textarea.selectionEnd;
|
|
1133
|
-
const lines = text.split("\n");
|
|
1134
|
-
const undoStyle = lines.every((line) => line.startsWith(prefix) && (!suffix || line.endsWith(suffix)));
|
|
1135
|
-
if (undoStyle) {
|
|
1136
|
-
text = lines.map((line) => {
|
|
1137
|
-
let result = line.slice(prefix.length);
|
|
1138
|
-
if (suffix) {
|
|
1139
|
-
result = result.slice(0, result.length - suffix.length);
|
|
1140
|
-
}
|
|
1141
|
-
return result;
|
|
1142
|
-
}).join("\n");
|
|
1143
|
-
selectionEnd = selectionStart + text.length;
|
|
1144
|
-
} else {
|
|
1145
|
-
text = lines.map((line) => prefix + line + (suffix || "")).join("\n");
|
|
1146
|
-
if (surroundWithNewlines) {
|
|
1147
|
-
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
|
1148
|
-
selectionStart += newlinesToAppend.length;
|
|
1149
|
-
selectionEnd = selectionStart + text.length;
|
|
1150
|
-
text = newlinesToAppend + text + newlinesToPrepend;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
return { text, selectionStart, selectionEnd };
|
|
1154
|
-
}
|
|
1155
|
-
function undoOrderedListStyle(text) {
|
|
1156
|
-
const lines = text.split("\n");
|
|
1157
|
-
const orderedListRegex = /^\d+\.\s+/;
|
|
1158
|
-
const shouldUndoOrderedList = lines.every((line) => orderedListRegex.test(line));
|
|
1159
|
-
let result = lines;
|
|
1160
|
-
if (shouldUndoOrderedList) {
|
|
1161
|
-
result = lines.map((line) => line.replace(orderedListRegex, ""));
|
|
1162
|
-
}
|
|
1163
|
-
return {
|
|
1164
|
-
text: result.join("\n"),
|
|
1165
|
-
processed: shouldUndoOrderedList
|
|
1166
|
-
};
|
|
1167
|
-
}
|
|
1168
|
-
function undoUnorderedListStyle(text) {
|
|
1169
|
-
const lines = text.split("\n");
|
|
1170
|
-
const unorderedListPrefix = "- ";
|
|
1171
|
-
const shouldUndoUnorderedList = lines.every((line) => line.startsWith(unorderedListPrefix));
|
|
1172
|
-
let result = lines;
|
|
1173
|
-
if (shouldUndoUnorderedList) {
|
|
1174
|
-
result = lines.map((line) => line.slice(unorderedListPrefix.length));
|
|
1175
|
-
}
|
|
1176
|
-
return {
|
|
1177
|
-
text: result.join("\n"),
|
|
1178
|
-
processed: shouldUndoUnorderedList
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
function makePrefix(index, unorderedList) {
|
|
1182
|
-
if (unorderedList) {
|
|
1183
|
-
return "- ";
|
|
1184
|
-
} else {
|
|
1185
|
-
return `${index + 1}. `;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
function clearExistingListStyle(style, selectedText) {
|
|
1189
|
-
let undoResult;
|
|
1190
|
-
let undoResultOppositeList;
|
|
1191
|
-
let pristineText;
|
|
1192
|
-
if (style.orderedList) {
|
|
1193
|
-
undoResult = undoOrderedListStyle(selectedText);
|
|
1194
|
-
undoResultOppositeList = undoUnorderedListStyle(undoResult.text);
|
|
1195
|
-
pristineText = undoResultOppositeList.text;
|
|
1196
|
-
} else {
|
|
1197
|
-
undoResult = undoUnorderedListStyle(selectedText);
|
|
1198
|
-
undoResultOppositeList = undoOrderedListStyle(undoResult.text);
|
|
1199
|
-
pristineText = undoResultOppositeList.text;
|
|
1200
|
-
}
|
|
1201
|
-
return [undoResult, undoResultOppositeList, pristineText];
|
|
1202
|
-
}
|
|
1203
|
-
function listStyle(textarea, style) {
|
|
1204
|
-
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
|
|
1205
|
-
let selectionStart = textarea.selectionStart;
|
|
1206
|
-
let selectionEnd = textarea.selectionEnd;
|
|
1207
|
-
expandSelectionToLine(textarea);
|
|
1208
|
-
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1209
|
-
const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
|
|
1210
|
-
const prefixedLines = pristineText.split("\n").map((value, index) => {
|
|
1211
|
-
return `${makePrefix(index, style.unorderedList)}${value}`;
|
|
1212
|
-
});
|
|
1213
|
-
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
1214
|
-
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
|
|
1215
|
-
}, 0);
|
|
1216
|
-
const totalPrefixLengthOppositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
1217
|
-
return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
|
|
1218
|
-
}, 0);
|
|
1219
|
-
if (undoResult.processed) {
|
|
1220
|
-
if (noInitialSelection) {
|
|
1221
|
-
selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
|
|
1222
|
-
selectionEnd = selectionStart;
|
|
1223
|
-
} else {
|
|
1224
|
-
selectionStart = textarea.selectionStart;
|
|
1225
|
-
selectionEnd = textarea.selectionEnd - totalPrefixLength;
|
|
1226
|
-
}
|
|
1227
|
-
return { text: pristineText, selectionStart, selectionEnd };
|
|
1228
|
-
}
|
|
1229
|
-
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
|
1230
|
-
const text = newlinesToAppend + prefixedLines.join("\n") + newlinesToPrepend;
|
|
1231
|
-
if (noInitialSelection) {
|
|
1232
|
-
selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
|
|
1233
|
-
selectionEnd = selectionStart;
|
|
1234
|
-
} else {
|
|
1235
|
-
if (undoResultOppositeList.processed) {
|
|
1236
|
-
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
|
1237
|
-
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOppositeList;
|
|
1238
|
-
} else {
|
|
1239
|
-
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
|
1240
|
-
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
return { text, selectionStart, selectionEnd };
|
|
1244
|
-
}
|
|
1245
|
-
function applyListStyle(textarea, style) {
|
|
1246
|
-
const result = applyLineOperation(
|
|
1247
|
-
textarea,
|
|
1248
|
-
(ta) => listStyle(ta, style),
|
|
1249
|
-
{
|
|
1250
|
-
// Custom selection adjustment for lists
|
|
1251
|
-
adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
|
|
1252
|
-
const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
|
|
1253
|
-
const orderedListRegex = /^\d+\.\s+/;
|
|
1254
|
-
const unorderedListRegex = /^- /;
|
|
1255
|
-
const hasOrderedList = orderedListRegex.test(currentLine);
|
|
1256
|
-
const hasUnorderedList = unorderedListRegex.test(currentLine);
|
|
1257
|
-
const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
|
|
1258
|
-
if (selStart === selEnd) {
|
|
1259
|
-
if (isRemovingCurrent) {
|
|
1260
|
-
const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
|
|
1261
|
-
const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
|
|
1262
|
-
return {
|
|
1263
|
-
start: Math.max(selStart - prefixLength, lineStart),
|
|
1264
|
-
end: Math.max(selStart - prefixLength, lineStart)
|
|
1265
|
-
};
|
|
1266
|
-
} else if (hasOrderedList || hasUnorderedList) {
|
|
1267
|
-
const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
|
|
1268
|
-
const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
|
|
1269
|
-
const newPrefixLength = style.unorderedList ? 2 : 3;
|
|
1270
|
-
const adjustment = newPrefixLength - oldPrefixLength;
|
|
1271
|
-
return {
|
|
1272
|
-
start: selStart + adjustment,
|
|
1273
|
-
end: selStart + adjustment
|
|
1274
|
-
};
|
|
1275
|
-
} else {
|
|
1276
|
-
const prefixLength = style.unorderedList ? 2 : 3;
|
|
1277
|
-
return {
|
|
1278
|
-
start: selStart + prefixLength,
|
|
1279
|
-
end: selStart + prefixLength
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
} else {
|
|
1283
|
-
if (isRemovingCurrent) {
|
|
1284
|
-
const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
|
|
1285
|
-
const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
|
|
1286
|
-
return {
|
|
1287
|
-
start: Math.max(selStart - prefixLength, lineStart),
|
|
1288
|
-
end: Math.max(selEnd - prefixLength, lineStart)
|
|
1289
|
-
};
|
|
1290
|
-
} else if (hasOrderedList || hasUnorderedList) {
|
|
1291
|
-
const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
|
|
1292
|
-
const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
|
|
1293
|
-
const newPrefixLength = style.unorderedList ? 2 : 3;
|
|
1294
|
-
const adjustment = newPrefixLength - oldPrefixLength;
|
|
1295
|
-
return {
|
|
1296
|
-
start: selStart + adjustment,
|
|
1297
|
-
end: selEnd + adjustment
|
|
1298
|
-
};
|
|
1299
|
-
} else {
|
|
1300
|
-
const prefixLength = style.unorderedList ? 2 : 3;
|
|
1301
|
-
return {
|
|
1302
|
-
start: selStart + prefixLength,
|
|
1303
|
-
end: selEnd + prefixLength
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
);
|
|
1310
|
-
insertText(textarea, result);
|
|
1311
|
-
}
|
|
1312
|
-
function getActiveFormats(textarea) {
|
|
1313
|
-
if (!textarea)
|
|
1314
|
-
return [];
|
|
1315
|
-
const formats = [];
|
|
1316
|
-
const { selectionStart, selectionEnd, value } = textarea;
|
|
1317
|
-
const lines = value.split("\n");
|
|
1318
|
-
let lineStart = 0;
|
|
1319
|
-
let currentLine = "";
|
|
1320
|
-
for (const line of lines) {
|
|
1321
|
-
if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {
|
|
1322
|
-
currentLine = line;
|
|
1323
|
-
break;
|
|
1324
|
-
}
|
|
1325
|
-
lineStart += line.length + 1;
|
|
1326
|
-
}
|
|
1327
|
-
if (currentLine.startsWith("- ")) {
|
|
1328
|
-
if (currentLine.startsWith("- [ ] ") || currentLine.startsWith("- [x] ")) {
|
|
1329
|
-
formats.push("task-list");
|
|
1330
|
-
} else {
|
|
1331
|
-
formats.push("bullet-list");
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
if (/^\d+\.\s/.test(currentLine)) {
|
|
1335
|
-
formats.push("numbered-list");
|
|
1336
|
-
}
|
|
1337
|
-
if (currentLine.startsWith("> ")) {
|
|
1338
|
-
formats.push("quote");
|
|
1339
|
-
}
|
|
1340
|
-
if (currentLine.startsWith("# "))
|
|
1341
|
-
formats.push("header");
|
|
1342
|
-
if (currentLine.startsWith("## "))
|
|
1343
|
-
formats.push("header-2");
|
|
1344
|
-
if (currentLine.startsWith("### "))
|
|
1345
|
-
formats.push("header-3");
|
|
1346
|
-
const lookBehind = Math.max(0, selectionStart - 10);
|
|
1347
|
-
const lookAhead = Math.min(value.length, selectionEnd + 10);
|
|
1348
|
-
const surrounding = value.slice(lookBehind, lookAhead);
|
|
1349
|
-
if (surrounding.includes("**")) {
|
|
1350
|
-
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
1351
|
-
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
1352
|
-
const lastOpenBold = beforeCursor.lastIndexOf("**");
|
|
1353
|
-
const nextCloseBold = afterCursor.indexOf("**");
|
|
1354
|
-
if (lastOpenBold !== -1 && nextCloseBold !== -1) {
|
|
1355
|
-
formats.push("bold");
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
if (surrounding.includes("_")) {
|
|
1359
|
-
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
1360
|
-
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
1361
|
-
const lastOpenItalic = beforeCursor.lastIndexOf("_");
|
|
1362
|
-
const nextCloseItalic = afterCursor.indexOf("_");
|
|
1363
|
-
if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {
|
|
1364
|
-
formats.push("italic");
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
if (surrounding.includes("`")) {
|
|
1368
|
-
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
1369
|
-
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
1370
|
-
if (beforeCursor.includes("`") && afterCursor.includes("`")) {
|
|
1371
|
-
formats.push("code");
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
if (surrounding.includes("[") && surrounding.includes("]")) {
|
|
1375
|
-
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
1376
|
-
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
1377
|
-
const lastOpenBracket = beforeCursor.lastIndexOf("[");
|
|
1378
|
-
const nextCloseBracket = afterCursor.indexOf("]");
|
|
1379
|
-
if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
|
|
1380
|
-
const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
|
|
1381
|
-
if (afterBracket.startsWith("(")) {
|
|
1382
|
-
formats.push("link");
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
return formats;
|
|
1387
|
-
}
|
|
1388
|
-
function toggleBold(textarea) {
|
|
1389
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1390
|
-
return;
|
|
1391
|
-
debugLog("toggleBold", "Starting");
|
|
1392
|
-
debugSelection(textarea, "Before");
|
|
1393
|
-
const style = mergeWithDefaults(FORMATS.bold);
|
|
1394
|
-
const result = blockStyle(textarea, style);
|
|
1395
|
-
debugResult(result);
|
|
1396
|
-
insertText(textarea, result);
|
|
1397
|
-
debugSelection(textarea, "After");
|
|
1398
|
-
}
|
|
1399
|
-
function toggleItalic(textarea) {
|
|
1400
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1401
|
-
return;
|
|
1402
|
-
const style = mergeWithDefaults(FORMATS.italic);
|
|
1403
|
-
const result = blockStyle(textarea, style);
|
|
1404
|
-
insertText(textarea, result);
|
|
1405
|
-
}
|
|
1406
|
-
function toggleCode(textarea) {
|
|
1407
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1408
|
-
return;
|
|
1409
|
-
const style = mergeWithDefaults(FORMATS.code);
|
|
1410
|
-
const result = blockStyle(textarea, style);
|
|
1411
|
-
insertText(textarea, result);
|
|
1412
|
-
}
|
|
1413
|
-
function insertLink(textarea, options = {}) {
|
|
1414
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1415
|
-
return;
|
|
1416
|
-
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1417
|
-
let style = mergeWithDefaults(FORMATS.link);
|
|
1418
|
-
const isURL = selectedText && selectedText.match(/^https?:\/\//);
|
|
1419
|
-
if (isURL && !options.url) {
|
|
1420
|
-
style.suffix = `](${selectedText})`;
|
|
1421
|
-
style.replaceNext = "";
|
|
1422
|
-
} else if (options.url) {
|
|
1423
|
-
style.suffix = `](${options.url})`;
|
|
1424
|
-
style.replaceNext = "";
|
|
1425
|
-
}
|
|
1426
|
-
if (options.text && !selectedText) {
|
|
1427
|
-
const pos = textarea.selectionStart;
|
|
1428
|
-
textarea.value = textarea.value.slice(0, pos) + options.text + textarea.value.slice(pos);
|
|
1429
|
-
textarea.selectionStart = pos;
|
|
1430
|
-
textarea.selectionEnd = pos + options.text.length;
|
|
1431
|
-
}
|
|
1432
|
-
const result = blockStyle(textarea, style);
|
|
1433
|
-
insertText(textarea, result);
|
|
1434
|
-
}
|
|
1435
|
-
function toggleBulletList(textarea) {
|
|
1436
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1437
|
-
return;
|
|
1438
|
-
const style = mergeWithDefaults(FORMATS.bulletList);
|
|
1439
|
-
applyListStyle(textarea, style);
|
|
1440
|
-
}
|
|
1441
|
-
function toggleNumberedList(textarea) {
|
|
1442
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1443
|
-
return;
|
|
1444
|
-
const style = mergeWithDefaults(FORMATS.numberedList);
|
|
1445
|
-
applyListStyle(textarea, style);
|
|
1446
|
-
}
|
|
1447
|
-
function toggleQuote(textarea) {
|
|
1448
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1449
|
-
return;
|
|
1450
|
-
debugLog("toggleQuote", "Starting");
|
|
1451
|
-
debugSelection(textarea, "Initial");
|
|
1452
|
-
const style = mergeWithDefaults(FORMATS.quote);
|
|
1453
|
-
const result = applyLineOperation(
|
|
1454
|
-
textarea,
|
|
1455
|
-
(ta) => multilineStyle(ta, style),
|
|
1456
|
-
{ prefix: style.prefix }
|
|
1457
|
-
);
|
|
1458
|
-
debugResult(result);
|
|
1459
|
-
insertText(textarea, result);
|
|
1460
|
-
debugSelection(textarea, "Final");
|
|
1461
|
-
}
|
|
1462
|
-
function toggleTaskList(textarea) {
|
|
1463
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1464
|
-
return;
|
|
1465
|
-
const style = mergeWithDefaults(FORMATS.taskList);
|
|
1466
|
-
const result = applyLineOperation(
|
|
1467
|
-
textarea,
|
|
1468
|
-
(ta) => multilineStyle(ta, style),
|
|
1469
|
-
{ prefix: style.prefix }
|
|
1470
|
-
);
|
|
1471
|
-
insertText(textarea, result);
|
|
1472
|
-
}
|
|
1473
|
-
function insertHeader(textarea, level = 1, toggle = false) {
|
|
1474
|
-
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
1475
|
-
return;
|
|
1476
|
-
if (level < 1 || level > 6)
|
|
1477
|
-
level = 1;
|
|
1478
|
-
debugLog("insertHeader", `============ START ============`);
|
|
1479
|
-
debugLog("insertHeader", `Level: ${level}, Toggle: ${toggle}`);
|
|
1480
|
-
debugLog("insertHeader", `Initial cursor: ${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
1481
|
-
const headerKey = `header${level === 1 ? "1" : level}`;
|
|
1482
|
-
const style = mergeWithDefaults(FORMATS[headerKey] || FORMATS.header1);
|
|
1483
|
-
debugLog("insertHeader", `Style prefix: "${style.prefix}"`);
|
|
1484
|
-
const value = textarea.value;
|
|
1485
|
-
const originalStart = textarea.selectionStart;
|
|
1486
|
-
const originalEnd = textarea.selectionEnd;
|
|
1487
|
-
let lineStart = originalStart;
|
|
1488
|
-
while (lineStart > 0 && value[lineStart - 1] !== "\n") {
|
|
1489
|
-
lineStart--;
|
|
1490
|
-
}
|
|
1491
|
-
let lineEnd = originalEnd;
|
|
1492
|
-
while (lineEnd < value.length && value[lineEnd] !== "\n") {
|
|
1493
|
-
lineEnd++;
|
|
1494
|
-
}
|
|
1495
|
-
const currentLineContent = value.slice(lineStart, lineEnd);
|
|
1496
|
-
debugLog("insertHeader", `Current line (before): "${currentLineContent}"`);
|
|
1497
|
-
const existingHeaderMatch = currentLineContent.match(/^(#{1,6})\s*/);
|
|
1498
|
-
const existingLevel = existingHeaderMatch ? existingHeaderMatch[1].length : 0;
|
|
1499
|
-
const existingPrefixLength = existingHeaderMatch ? existingHeaderMatch[0].length : 0;
|
|
1500
|
-
debugLog("insertHeader", `Existing header check:`);
|
|
1501
|
-
debugLog("insertHeader", ` - Match: ${existingHeaderMatch ? `"${existingHeaderMatch[0]}"` : "none"}`);
|
|
1502
|
-
debugLog("insertHeader", ` - Existing level: ${existingLevel}`);
|
|
1503
|
-
debugLog("insertHeader", ` - Existing prefix length: ${existingPrefixLength}`);
|
|
1504
|
-
debugLog("insertHeader", ` - Target level: ${level}`);
|
|
1505
|
-
const shouldToggleOff = toggle && existingLevel === level;
|
|
1506
|
-
debugLog("insertHeader", `Should toggle OFF: ${shouldToggleOff} (toggle=${toggle}, existingLevel=${existingLevel}, level=${level})`);
|
|
1507
|
-
const result = applyLineOperation(
|
|
1508
|
-
textarea,
|
|
1509
|
-
(ta) => {
|
|
1510
|
-
const currentLine = ta.value.slice(ta.selectionStart, ta.selectionEnd);
|
|
1511
|
-
debugLog("insertHeader", `Line in operation: "${currentLine}"`);
|
|
1512
|
-
const cleanedLine = currentLine.replace(/^#{1,6}\s*/, "");
|
|
1513
|
-
debugLog("insertHeader", `Cleaned line: "${cleanedLine}"`);
|
|
1514
|
-
let newLine;
|
|
1515
|
-
if (shouldToggleOff) {
|
|
1516
|
-
debugLog("insertHeader", "ACTION: Toggling OFF - removing header");
|
|
1517
|
-
newLine = cleanedLine;
|
|
1518
|
-
} else if (existingLevel > 0) {
|
|
1519
|
-
debugLog("insertHeader", `ACTION: Replacing H${existingLevel} with H${level}`);
|
|
1520
|
-
newLine = style.prefix + cleanedLine;
|
|
1521
|
-
} else {
|
|
1522
|
-
debugLog("insertHeader", "ACTION: Adding new header");
|
|
1523
|
-
newLine = style.prefix + cleanedLine;
|
|
1524
|
-
}
|
|
1525
|
-
debugLog("insertHeader", `New line: "${newLine}"`);
|
|
1526
|
-
return {
|
|
1527
|
-
text: newLine,
|
|
1528
|
-
selectionStart: ta.selectionStart,
|
|
1529
|
-
selectionEnd: ta.selectionEnd
|
|
1530
|
-
};
|
|
1531
|
-
},
|
|
1532
|
-
{
|
|
1533
|
-
prefix: style.prefix,
|
|
1534
|
-
// Custom selection adjustment for headers
|
|
1535
|
-
adjustSelection: (isRemoving, selStart, selEnd, lineStartPos) => {
|
|
1536
|
-
debugLog("insertHeader", `Adjusting selection:`);
|
|
1537
|
-
debugLog("insertHeader", ` - isRemoving param: ${isRemoving}`);
|
|
1538
|
-
debugLog("insertHeader", ` - shouldToggleOff: ${shouldToggleOff}`);
|
|
1539
|
-
debugLog("insertHeader", ` - selStart: ${selStart}, selEnd: ${selEnd}`);
|
|
1540
|
-
debugLog("insertHeader", ` - lineStartPos: ${lineStartPos}`);
|
|
1541
|
-
if (shouldToggleOff) {
|
|
1542
|
-
const adjustment = Math.max(selStart - existingPrefixLength, lineStartPos);
|
|
1543
|
-
debugLog("insertHeader", ` - Removing header, adjusting by -${existingPrefixLength}`);
|
|
1544
|
-
return {
|
|
1545
|
-
start: adjustment,
|
|
1546
|
-
end: selStart === selEnd ? adjustment : Math.max(selEnd - existingPrefixLength, lineStartPos)
|
|
1547
|
-
};
|
|
1548
|
-
} else if (existingPrefixLength > 0) {
|
|
1549
|
-
const prefixDiff = style.prefix.length - existingPrefixLength;
|
|
1550
|
-
debugLog("insertHeader", ` - Replacing header, adjusting by ${prefixDiff}`);
|
|
1551
|
-
return {
|
|
1552
|
-
start: selStart + prefixDiff,
|
|
1553
|
-
end: selEnd + prefixDiff
|
|
1554
|
-
};
|
|
1555
|
-
} else {
|
|
1556
|
-
debugLog("insertHeader", ` - Adding header, adjusting by +${style.prefix.length}`);
|
|
1557
|
-
return {
|
|
1558
|
-
start: selStart + style.prefix.length,
|
|
1559
|
-
end: selEnd + style.prefix.length
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
1564
|
-
);
|
|
1565
|
-
debugLog("insertHeader", `Final result: text="${result.text}", cursor=${result.selectionStart}-${result.selectionEnd}`);
|
|
1566
|
-
debugLog("insertHeader", `============ END ============`);
|
|
1567
|
-
insertText(textarea, result);
|
|
1568
|
-
}
|
|
1569
|
-
function toggleH1(textarea) {
|
|
1570
|
-
insertHeader(textarea, 1, true);
|
|
1571
|
-
}
|
|
1572
|
-
function toggleH2(textarea) {
|
|
1573
|
-
insertHeader(textarea, 2, true);
|
|
1574
|
-
}
|
|
1575
|
-
function toggleH3(textarea) {
|
|
1576
|
-
insertHeader(textarea, 3, true);
|
|
1577
|
-
}
|
|
1578
|
-
function getActiveFormats2(textarea) {
|
|
1579
|
-
return getActiveFormats(textarea);
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
// src/shortcuts.js
|
|
1583
|
-
var ShortcutsManager = class {
|
|
1584
|
-
constructor(editor) {
|
|
1585
|
-
this.editor = editor;
|
|
1586
|
-
this.textarea = editor.textarea;
|
|
1587
|
-
}
|
|
1588
|
-
/**
|
|
1589
|
-
* Handle keydown events - called by OverType
|
|
1590
|
-
* @param {KeyboardEvent} event - The keyboard event
|
|
1591
|
-
* @returns {boolean} Whether the event was handled
|
|
1592
|
-
*/
|
|
1593
|
-
handleKeydown(event) {
|
|
1594
|
-
const isMac = navigator.platform.toLowerCase().includes("mac");
|
|
1595
|
-
const modKey = isMac ? event.metaKey : event.ctrlKey;
|
|
1596
|
-
if (!modKey)
|
|
1597
|
-
return false;
|
|
1598
|
-
let action = null;
|
|
1599
|
-
switch (event.key.toLowerCase()) {
|
|
1600
|
-
case "b":
|
|
1601
|
-
if (!event.shiftKey) {
|
|
1602
|
-
action = "toggleBold";
|
|
1603
|
-
}
|
|
1604
|
-
break;
|
|
1605
|
-
case "i":
|
|
1606
|
-
if (!event.shiftKey) {
|
|
1607
|
-
action = "toggleItalic";
|
|
1608
|
-
}
|
|
1609
|
-
break;
|
|
1610
|
-
case "k":
|
|
1611
|
-
if (!event.shiftKey) {
|
|
1612
|
-
action = "insertLink";
|
|
1613
|
-
}
|
|
1614
|
-
break;
|
|
1615
|
-
case "7":
|
|
1616
|
-
if (event.shiftKey) {
|
|
1617
|
-
action = "toggleNumberedList";
|
|
1618
|
-
}
|
|
1619
|
-
break;
|
|
1620
|
-
case "8":
|
|
1621
|
-
if (event.shiftKey) {
|
|
1622
|
-
action = "toggleBulletList";
|
|
1623
|
-
}
|
|
1624
|
-
break;
|
|
1625
|
-
}
|
|
1626
|
-
if (action) {
|
|
1627
|
-
event.preventDefault();
|
|
1628
|
-
if (this.editor.toolbar) {
|
|
1629
|
-
this.editor.toolbar.handleAction(action);
|
|
1630
|
-
} else {
|
|
1631
|
-
this.handleAction(action);
|
|
1632
|
-
}
|
|
1633
|
-
return true;
|
|
1634
|
-
}
|
|
1635
|
-
return false;
|
|
1636
|
-
}
|
|
1637
|
-
/**
|
|
1638
|
-
* Handle action - fallback when no toolbar exists
|
|
1639
|
-
* This duplicates toolbar.handleAction for consistency
|
|
1640
|
-
*/
|
|
1641
|
-
async handleAction(action) {
|
|
1642
|
-
const textarea = this.textarea;
|
|
1643
|
-
if (!textarea)
|
|
1644
|
-
return;
|
|
1645
|
-
textarea.focus();
|
|
1646
|
-
try {
|
|
1647
|
-
switch (action) {
|
|
1648
|
-
case "toggleBold":
|
|
1649
|
-
toggleBold(textarea);
|
|
1650
|
-
break;
|
|
1651
|
-
case "toggleItalic":
|
|
1652
|
-
toggleItalic(textarea);
|
|
1653
|
-
break;
|
|
1654
|
-
case "insertLink":
|
|
1655
|
-
insertLink(textarea);
|
|
1656
|
-
break;
|
|
1657
|
-
case "toggleBulletList":
|
|
1658
|
-
toggleBulletList(textarea);
|
|
1659
|
-
break;
|
|
1660
|
-
case "toggleNumberedList":
|
|
1661
|
-
toggleNumberedList(textarea);
|
|
1662
|
-
break;
|
|
1663
|
-
}
|
|
1664
|
-
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1665
|
-
} catch (error) {
|
|
1666
|
-
console.error("Error in markdown action:", error);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
/**
|
|
1670
|
-
* Cleanup
|
|
1671
|
-
*/
|
|
1672
|
-
destroy() {
|
|
804
|
+
/**
|
|
805
|
+
* Cleanup
|
|
806
|
+
*/
|
|
807
|
+
destroy() {
|
|
1673
808
|
}
|
|
1674
809
|
};
|
|
1675
810
|
|
|
@@ -2338,366 +1473,1209 @@ ${blockSuffix}` : suffix;
|
|
|
2338
1473
|
border-radius: 2px;
|
|
2339
1474
|
}
|
|
2340
1475
|
|
|
2341
|
-
.overtype-toolbar-button {
|
|
2342
|
-
display: flex;
|
|
2343
|
-
align-items: center;
|
|
2344
|
-
justify-content: center;
|
|
2345
|
-
width: 32px;
|
|
2346
|
-
height: 32px;
|
|
2347
|
-
padding: 0;
|
|
2348
|
-
border: none;
|
|
2349
|
-
border-radius: 6px;
|
|
2350
|
-
background: transparent;
|
|
2351
|
-
color: var(--toolbar-icon, var(--text-secondary, #666));
|
|
2352
|
-
cursor: pointer;
|
|
2353
|
-
transition: all 0.2s ease;
|
|
2354
|
-
flex-shrink: 0;
|
|
1476
|
+
.overtype-toolbar-button {
|
|
1477
|
+
display: flex;
|
|
1478
|
+
align-items: center;
|
|
1479
|
+
justify-content: center;
|
|
1480
|
+
width: 32px;
|
|
1481
|
+
height: 32px;
|
|
1482
|
+
padding: 0;
|
|
1483
|
+
border: none;
|
|
1484
|
+
border-radius: 6px;
|
|
1485
|
+
background: transparent;
|
|
1486
|
+
color: var(--toolbar-icon, var(--text-secondary, #666));
|
|
1487
|
+
cursor: pointer;
|
|
1488
|
+
transition: all 0.2s ease;
|
|
1489
|
+
flex-shrink: 0;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
.overtype-toolbar-button svg {
|
|
1493
|
+
width: 20px;
|
|
1494
|
+
height: 20px;
|
|
1495
|
+
fill: currentColor;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
.overtype-toolbar-button:hover {
|
|
1499
|
+
background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
|
|
1500
|
+
color: var(--toolbar-icon, var(--text-primary, #333));
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
.overtype-toolbar-button:active {
|
|
1504
|
+
transform: scale(0.95);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
.overtype-toolbar-button.active {
|
|
1508
|
+
background: var(--toolbar-active, var(--primary, #007bff));
|
|
1509
|
+
color: var(--toolbar-icon, var(--text-primary, #333));
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
.overtype-toolbar-button:disabled {
|
|
1513
|
+
opacity: 0.5;
|
|
1514
|
+
cursor: not-allowed;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
.overtype-toolbar-separator {
|
|
1518
|
+
width: 1px;
|
|
1519
|
+
height: 24px;
|
|
1520
|
+
background: var(--border, #e0e0e0);
|
|
1521
|
+
margin: 0 4px;
|
|
1522
|
+
flex-shrink: 0;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/* Adjust wrapper when toolbar is present */
|
|
1526
|
+
/* Mobile toolbar adjustments */
|
|
1527
|
+
@media (max-width: 640px) {
|
|
1528
|
+
.overtype-toolbar {
|
|
1529
|
+
padding: 6px;
|
|
1530
|
+
gap: 2px;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
.overtype-toolbar-button {
|
|
1534
|
+
width: 36px;
|
|
1535
|
+
height: 36px;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.overtype-toolbar-separator {
|
|
1539
|
+
margin: 0 2px;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/* Plain mode - hide preview and show textarea text */
|
|
1544
|
+
.overtype-container[data-mode="plain"] .overtype-preview {
|
|
1545
|
+
display: none !important;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
.overtype-container[data-mode="plain"] .overtype-input {
|
|
1549
|
+
color: var(--text, #0d3b66) !important;
|
|
1550
|
+
/* Use system font stack for better plain text readability */
|
|
1551
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
1552
|
+
"Helvetica Neue", Arial, sans-serif !important;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/* Ensure textarea remains transparent in overlay mode */
|
|
1556
|
+
.overtype-container:not([data-mode="plain"]) .overtype-input {
|
|
1557
|
+
color: transparent !important;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/* Dropdown menu styles */
|
|
1561
|
+
.overtype-toolbar-button {
|
|
1562
|
+
position: relative !important; /* Override reset - needed for dropdown */
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
.overtype-toolbar-button.dropdown-active {
|
|
1566
|
+
background: var(--toolbar-active, var(--hover-bg, #f0f0f0));
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
.overtype-dropdown-menu {
|
|
1570
|
+
position: fixed !important; /* Fixed positioning relative to viewport */
|
|
1571
|
+
background: var(--bg-secondary, white) !important; /* Override reset */
|
|
1572
|
+
border: 1px solid var(--border, #e0e0e0) !important; /* Override reset */
|
|
1573
|
+
border-radius: 6px;
|
|
1574
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important; /* Override reset */
|
|
1575
|
+
z-index: 10000; /* Very high z-index to ensure visibility */
|
|
1576
|
+
min-width: 150px;
|
|
1577
|
+
padding: 4px 0 !important; /* Override reset */
|
|
1578
|
+
/* Position will be set via JavaScript based on button position */
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
.overtype-dropdown-item {
|
|
1582
|
+
display: flex;
|
|
1583
|
+
align-items: center;
|
|
1584
|
+
width: 100%;
|
|
1585
|
+
padding: 8px 12px;
|
|
1586
|
+
border: none;
|
|
1587
|
+
background: none;
|
|
1588
|
+
text-align: left;
|
|
1589
|
+
cursor: pointer;
|
|
1590
|
+
font-size: 14px;
|
|
1591
|
+
color: var(--text, #333);
|
|
1592
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.overtype-dropdown-item:hover {
|
|
1596
|
+
background: var(--hover-bg, #f0f0f0);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
.overtype-dropdown-item.active {
|
|
1600
|
+
font-weight: 600;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
.overtype-dropdown-check {
|
|
1604
|
+
width: 16px;
|
|
1605
|
+
margin-right: 8px;
|
|
1606
|
+
color: var(--h1, #007bff);
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
.overtype-dropdown-icon {
|
|
1610
|
+
width: 20px;
|
|
1611
|
+
margin-right: 8px;
|
|
1612
|
+
text-align: center;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/* Preview mode styles */
|
|
1616
|
+
.overtype-container[data-mode="preview"] .overtype-input {
|
|
1617
|
+
display: none !important;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.overtype-container[data-mode="preview"] .overtype-preview {
|
|
1621
|
+
pointer-events: auto !important;
|
|
1622
|
+
user-select: text !important;
|
|
1623
|
+
cursor: text !important;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/* Hide syntax markers in preview mode */
|
|
1627
|
+
.overtype-container[data-mode="preview"] .syntax-marker {
|
|
1628
|
+
display: none !important;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/* Hide URL part of links in preview mode - extra specificity */
|
|
1632
|
+
.overtype-container[data-mode="preview"] .syntax-marker.url-part,
|
|
1633
|
+
.overtype-container[data-mode="preview"] .url-part {
|
|
1634
|
+
display: none !important;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/* Hide all syntax markers inside links too */
|
|
1638
|
+
.overtype-container[data-mode="preview"] a .syntax-marker {
|
|
1639
|
+
display: none !important;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
/* Headers - restore proper sizing in preview mode */
|
|
1643
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1,
|
|
1644
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2,
|
|
1645
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
|
|
1646
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
1647
|
+
font-weight: 600 !important;
|
|
1648
|
+
margin: 0 !important;
|
|
1649
|
+
display: block !important;
|
|
1650
|
+
color: inherit !important; /* Use parent text color */
|
|
1651
|
+
line-height: 1 !important; /* Tight line height for headings */
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1 {
|
|
1655
|
+
font-size: 2em !important;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2 {
|
|
1659
|
+
font-size: 1.5em !important;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
|
|
1663
|
+
font-size: 1.17em !important;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/* Lists - restore list styling in preview mode */
|
|
1667
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ul {
|
|
1668
|
+
display: block !important;
|
|
1669
|
+
list-style: disc !important;
|
|
1670
|
+
padding-left: 2em !important;
|
|
1671
|
+
margin: 1em 0 !important;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ol {
|
|
1675
|
+
display: block !important;
|
|
1676
|
+
list-style: decimal !important;
|
|
1677
|
+
padding-left: 2em !important;
|
|
1678
|
+
margin: 1em 0 !important;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li {
|
|
1682
|
+
display: list-item !important;
|
|
1683
|
+
margin: 0 !important;
|
|
1684
|
+
padding: 0 !important;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/* Task list checkboxes - only in preview mode */
|
|
1688
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list {
|
|
1689
|
+
list-style: none !important;
|
|
1690
|
+
position: relative !important;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list input[type="checkbox"] {
|
|
1694
|
+
margin-right: 0.5em !important;
|
|
1695
|
+
cursor: default !important;
|
|
1696
|
+
vertical-align: middle !important;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
/* Task list in normal mode - keep syntax visible */
|
|
1700
|
+
.overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list {
|
|
1701
|
+
list-style: none !important;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list .syntax-marker {
|
|
1705
|
+
color: var(--syntax, #999999) !important;
|
|
1706
|
+
font-weight: normal !important;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/* Links - make clickable in preview mode */
|
|
1710
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview a {
|
|
1711
|
+
pointer-events: auto !important;
|
|
1712
|
+
cursor: pointer !important;
|
|
1713
|
+
color: var(--link, #0066cc) !important;
|
|
1714
|
+
text-decoration: underline !important;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/* Code blocks - proper pre/code styling in preview mode */
|
|
1718
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
|
|
1719
|
+
background: #2d2d2d !important;
|
|
1720
|
+
color: #f8f8f2 !important;
|
|
1721
|
+
padding: 1.2em !important;
|
|
1722
|
+
border-radius: 3px !important;
|
|
1723
|
+
overflow-x: auto !important;
|
|
1724
|
+
margin: 0 !important;
|
|
1725
|
+
display: block !important;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
/* Cave theme code block background in preview mode */
|
|
1729
|
+
.overtype-container[data-theme="cave"][data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
|
|
1730
|
+
background: #11171F !important;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block code {
|
|
1734
|
+
background: transparent !important;
|
|
1735
|
+
color: inherit !important;
|
|
1736
|
+
padding: 0 !important;
|
|
1737
|
+
font-family: ${fontFamily} !important;
|
|
1738
|
+
font-size: 0.9em !important;
|
|
1739
|
+
line-height: 1.4 !important;
|
|
2355
1740
|
}
|
|
2356
1741
|
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
fill: currentColor;
|
|
1742
|
+
/* Hide old code block lines and fences in preview mode */
|
|
1743
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-block-line {
|
|
1744
|
+
display: none !important;
|
|
2361
1745
|
}
|
|
2362
1746
|
|
|
2363
|
-
.overtype-
|
|
2364
|
-
|
|
2365
|
-
color: var(--toolbar-icon, var(--text-primary, #333));
|
|
1747
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-fence {
|
|
1748
|
+
display: none !important;
|
|
2366
1749
|
}
|
|
2367
1750
|
|
|
2368
|
-
|
|
2369
|
-
|
|
1751
|
+
/* Blockquotes - enhanced styling in preview mode */
|
|
1752
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .blockquote {
|
|
1753
|
+
display: block !important;
|
|
1754
|
+
border-left: 4px solid var(--blockquote, #ddd) !important;
|
|
1755
|
+
padding-left: 1em !important;
|
|
1756
|
+
margin: 1em 0 !important;
|
|
1757
|
+
font-style: italic !important;
|
|
2370
1758
|
}
|
|
2371
1759
|
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
1760
|
+
/* Typography improvements in preview mode */
|
|
1761
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview {
|
|
1762
|
+
font-family: Georgia, 'Times New Roman', serif !important;
|
|
1763
|
+
font-size: 16px !important;
|
|
1764
|
+
line-height: 1.8 !important;
|
|
1765
|
+
color: var(--text, #333) !important; /* Consistent text color */
|
|
2375
1766
|
}
|
|
2376
1767
|
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
1768
|
+
/* Inline code in preview mode - keep monospace */
|
|
1769
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview code {
|
|
1770
|
+
font-family: ${fontFamily} !important;
|
|
1771
|
+
font-size: 0.9em !important;
|
|
1772
|
+
background: rgba(135, 131, 120, 0.15) !important;
|
|
1773
|
+
padding: 0.2em 0.4em !important;
|
|
1774
|
+
border-radius: 3px !important;
|
|
2380
1775
|
}
|
|
2381
1776
|
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
margin: 0 4px;
|
|
2387
|
-
flex-shrink: 0;
|
|
1777
|
+
/* Strong and em elements in preview mode */
|
|
1778
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview strong {
|
|
1779
|
+
font-weight: 700 !important;
|
|
1780
|
+
color: inherit !important; /* Use parent text color */
|
|
2388
1781
|
}
|
|
2389
1782
|
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
padding: 6px;
|
|
2395
|
-
gap: 2px;
|
|
2396
|
-
}
|
|
2397
|
-
|
|
2398
|
-
.overtype-toolbar-button {
|
|
2399
|
-
width: 36px;
|
|
2400
|
-
height: 36px;
|
|
2401
|
-
}
|
|
1783
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview em {
|
|
1784
|
+
font-style: italic !important;
|
|
1785
|
+
color: inherit !important; /* Use parent text color */
|
|
1786
|
+
}
|
|
2402
1787
|
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
1788
|
+
/* HR in preview mode */
|
|
1789
|
+
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .hr-marker {
|
|
1790
|
+
display: block !important;
|
|
1791
|
+
border-top: 2px solid var(--hr, #ddd) !important;
|
|
1792
|
+
text-indent: -9999px !important;
|
|
1793
|
+
height: 2px !important;
|
|
2406
1794
|
}
|
|
2407
|
-
|
|
2408
|
-
/*
|
|
2409
|
-
.overtype-
|
|
1795
|
+
|
|
1796
|
+
/* Link Tooltip - Base styles (all browsers) */
|
|
1797
|
+
.overtype-link-tooltip {
|
|
1798
|
+
/* Visual styles that work for both positioning methods */
|
|
1799
|
+
background: #333 !important;
|
|
1800
|
+
color: white !important;
|
|
1801
|
+
padding: 6px 10px !important;
|
|
1802
|
+
border-radius: 16px !important;
|
|
1803
|
+
font-size: 12px !important;
|
|
1804
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
2410
1805
|
display: none !important;
|
|
1806
|
+
z-index: 10000 !important;
|
|
1807
|
+
cursor: pointer !important;
|
|
1808
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
|
1809
|
+
max-width: 300px !important;
|
|
1810
|
+
white-space: nowrap !important;
|
|
1811
|
+
overflow: hidden !important;
|
|
1812
|
+
text-overflow: ellipsis !important;
|
|
1813
|
+
|
|
1814
|
+
/* Base positioning for Floating UI fallback */
|
|
1815
|
+
position: absolute;
|
|
2411
1816
|
}
|
|
2412
|
-
|
|
2413
|
-
.overtype-
|
|
2414
|
-
|
|
2415
|
-
/* Use system font stack for better plain text readability */
|
|
2416
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
2417
|
-
"Helvetica Neue", Arial, sans-serif !important;
|
|
1817
|
+
|
|
1818
|
+
.overtype-link-tooltip.visible {
|
|
1819
|
+
display: flex !important;
|
|
2418
1820
|
}
|
|
2419
|
-
|
|
2420
|
-
/*
|
|
2421
|
-
|
|
2422
|
-
|
|
1821
|
+
|
|
1822
|
+
/* CSS Anchor Positioning (modern browsers only) */
|
|
1823
|
+
@supports (position-anchor: --x) and (position-area: center) {
|
|
1824
|
+
.overtype-link-tooltip {
|
|
1825
|
+
/* Only anchor positioning specific properties */
|
|
1826
|
+
position-anchor: var(--target-anchor, --link-0);
|
|
1827
|
+
position-area: block-end center;
|
|
1828
|
+
margin-top: 8px !important;
|
|
1829
|
+
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
1830
|
+
position-visibility: anchors-visible;
|
|
1831
|
+
}
|
|
2423
1832
|
}
|
|
2424
1833
|
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
1834
|
+
${mobileStyles}
|
|
1835
|
+
`;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
// node_modules/markdown-actions/dist/markdown-actions.esm.js
|
|
1839
|
+
var __defProp2 = Object.defineProperty;
|
|
1840
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
1841
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
1842
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
1843
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1844
|
+
var __spreadValues = (a, b) => {
|
|
1845
|
+
for (var prop in b || (b = {}))
|
|
1846
|
+
if (__hasOwnProp2.call(b, prop))
|
|
1847
|
+
__defNormalProp2(a, prop, b[prop]);
|
|
1848
|
+
if (__getOwnPropSymbols)
|
|
1849
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
1850
|
+
if (__propIsEnum.call(b, prop))
|
|
1851
|
+
__defNormalProp2(a, prop, b[prop]);
|
|
1852
|
+
}
|
|
1853
|
+
return a;
|
|
1854
|
+
};
|
|
1855
|
+
var FORMATS = {
|
|
1856
|
+
bold: {
|
|
1857
|
+
prefix: "**",
|
|
1858
|
+
suffix: "**",
|
|
1859
|
+
trimFirst: true
|
|
1860
|
+
},
|
|
1861
|
+
italic: {
|
|
1862
|
+
prefix: "_",
|
|
1863
|
+
suffix: "_",
|
|
1864
|
+
trimFirst: true
|
|
1865
|
+
},
|
|
1866
|
+
code: {
|
|
1867
|
+
prefix: "`",
|
|
1868
|
+
suffix: "`",
|
|
1869
|
+
blockPrefix: "```",
|
|
1870
|
+
blockSuffix: "```"
|
|
1871
|
+
},
|
|
1872
|
+
link: {
|
|
1873
|
+
prefix: "[",
|
|
1874
|
+
suffix: "](url)",
|
|
1875
|
+
replaceNext: "url",
|
|
1876
|
+
scanFor: "https?://"
|
|
1877
|
+
},
|
|
1878
|
+
bulletList: {
|
|
1879
|
+
prefix: "- ",
|
|
1880
|
+
multiline: true,
|
|
1881
|
+
unorderedList: true
|
|
1882
|
+
},
|
|
1883
|
+
numberedList: {
|
|
1884
|
+
prefix: "1. ",
|
|
1885
|
+
multiline: true,
|
|
1886
|
+
orderedList: true
|
|
1887
|
+
},
|
|
1888
|
+
quote: {
|
|
1889
|
+
prefix: "> ",
|
|
1890
|
+
multiline: true,
|
|
1891
|
+
surroundWithNewlines: true
|
|
1892
|
+
},
|
|
1893
|
+
taskList: {
|
|
1894
|
+
prefix: "- [ ] ",
|
|
1895
|
+
multiline: true,
|
|
1896
|
+
surroundWithNewlines: true
|
|
1897
|
+
},
|
|
1898
|
+
header1: { prefix: "# " },
|
|
1899
|
+
header2: { prefix: "## " },
|
|
1900
|
+
header3: { prefix: "### " },
|
|
1901
|
+
header4: { prefix: "#### " },
|
|
1902
|
+
header5: { prefix: "##### " },
|
|
1903
|
+
header6: { prefix: "###### " }
|
|
1904
|
+
};
|
|
1905
|
+
function getDefaultStyle() {
|
|
1906
|
+
return {
|
|
1907
|
+
prefix: "",
|
|
1908
|
+
suffix: "",
|
|
1909
|
+
blockPrefix: "",
|
|
1910
|
+
blockSuffix: "",
|
|
1911
|
+
multiline: false,
|
|
1912
|
+
replaceNext: "",
|
|
1913
|
+
prefixSpace: false,
|
|
1914
|
+
scanFor: "",
|
|
1915
|
+
surroundWithNewlines: false,
|
|
1916
|
+
orderedList: false,
|
|
1917
|
+
unorderedList: false,
|
|
1918
|
+
trimFirst: false
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
function mergeWithDefaults(format) {
|
|
1922
|
+
return __spreadValues(__spreadValues({}, getDefaultStyle()), format);
|
|
1923
|
+
}
|
|
1924
|
+
var debugMode = false;
|
|
1925
|
+
function getDebugMode() {
|
|
1926
|
+
return debugMode;
|
|
1927
|
+
}
|
|
1928
|
+
function debugLog(funcName, message, data) {
|
|
1929
|
+
if (!debugMode)
|
|
1930
|
+
return;
|
|
1931
|
+
console.group(`\u{1F50D} ${funcName}`);
|
|
1932
|
+
console.log(message);
|
|
1933
|
+
if (data) {
|
|
1934
|
+
console.log("Data:", data);
|
|
1935
|
+
}
|
|
1936
|
+
console.groupEnd();
|
|
1937
|
+
}
|
|
1938
|
+
function debugSelection(textarea, label) {
|
|
1939
|
+
if (!debugMode)
|
|
1940
|
+
return;
|
|
1941
|
+
const selected = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
1942
|
+
console.group(`\u{1F4CD} Selection: ${label}`);
|
|
1943
|
+
console.log("Position:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
1944
|
+
console.log("Selected text:", JSON.stringify(selected));
|
|
1945
|
+
console.log("Length:", selected.length);
|
|
1946
|
+
const before = textarea.value.slice(Math.max(0, textarea.selectionStart - 10), textarea.selectionStart);
|
|
1947
|
+
const after = textarea.value.slice(textarea.selectionEnd, Math.min(textarea.value.length, textarea.selectionEnd + 10));
|
|
1948
|
+
console.log("Context:", JSON.stringify(before) + "[SELECTION]" + JSON.stringify(after));
|
|
1949
|
+
console.groupEnd();
|
|
1950
|
+
}
|
|
1951
|
+
function debugResult(result) {
|
|
1952
|
+
if (!debugMode)
|
|
1953
|
+
return;
|
|
1954
|
+
console.group("\u{1F4DD} Result");
|
|
1955
|
+
console.log("Text to insert:", JSON.stringify(result.text));
|
|
1956
|
+
console.log("New selection:", `${result.selectionStart}-${result.selectionEnd}`);
|
|
1957
|
+
console.groupEnd();
|
|
1958
|
+
}
|
|
1959
|
+
var canInsertText = null;
|
|
1960
|
+
function insertText(textarea, { text, selectionStart, selectionEnd }) {
|
|
1961
|
+
const debugMode2 = getDebugMode();
|
|
1962
|
+
if (debugMode2) {
|
|
1963
|
+
console.group("\u{1F527} insertText");
|
|
1964
|
+
console.log("Current selection:", `${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
1965
|
+
console.log("Text to insert:", JSON.stringify(text));
|
|
1966
|
+
console.log("New selection to set:", selectionStart, "-", selectionEnd);
|
|
2428
1967
|
}
|
|
2429
|
-
|
|
2430
|
-
.
|
|
2431
|
-
|
|
1968
|
+
textarea.focus();
|
|
1969
|
+
const originalSelectionStart = textarea.selectionStart;
|
|
1970
|
+
const originalSelectionEnd = textarea.selectionEnd;
|
|
1971
|
+
const before = textarea.value.slice(0, originalSelectionStart);
|
|
1972
|
+
const after = textarea.value.slice(originalSelectionEnd);
|
|
1973
|
+
if (debugMode2) {
|
|
1974
|
+
console.log("Before text (last 20):", JSON.stringify(before.slice(-20)));
|
|
1975
|
+
console.log("After text (first 20):", JSON.stringify(after.slice(0, 20)));
|
|
1976
|
+
console.log("Selected text being replaced:", JSON.stringify(textarea.value.slice(originalSelectionStart, originalSelectionEnd)));
|
|
2432
1977
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
1978
|
+
const originalValue = textarea.value;
|
|
1979
|
+
const hasSelection = originalSelectionStart !== originalSelectionEnd;
|
|
1980
|
+
if (canInsertText === null || canInsertText === true) {
|
|
1981
|
+
textarea.contentEditable = "true";
|
|
1982
|
+
try {
|
|
1983
|
+
canInsertText = document.execCommand("insertText", false, text);
|
|
1984
|
+
if (debugMode2)
|
|
1985
|
+
console.log("execCommand returned:", canInsertText, "for text with", text.split("\n").length, "lines");
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
canInsertText = false;
|
|
1988
|
+
if (debugMode2)
|
|
1989
|
+
console.log("execCommand threw error:", error);
|
|
1990
|
+
}
|
|
1991
|
+
textarea.contentEditable = "false";
|
|
2444
1992
|
}
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
align-items: center;
|
|
2449
|
-
width: 100%;
|
|
2450
|
-
padding: 8px 12px;
|
|
2451
|
-
border: none;
|
|
2452
|
-
background: none;
|
|
2453
|
-
text-align: left;
|
|
2454
|
-
cursor: pointer;
|
|
2455
|
-
font-size: 14px;
|
|
2456
|
-
color: var(--text, #333);
|
|
2457
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
1993
|
+
if (debugMode2) {
|
|
1994
|
+
console.log("canInsertText before:", canInsertText);
|
|
1995
|
+
console.log("execCommand result:", canInsertText);
|
|
2458
1996
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
1997
|
+
if (canInsertText) {
|
|
1998
|
+
const expectedValue = before + text + after;
|
|
1999
|
+
const actualValue = textarea.value;
|
|
2000
|
+
if (debugMode2) {
|
|
2001
|
+
console.log("Expected length:", expectedValue.length);
|
|
2002
|
+
console.log("Actual length:", actualValue.length);
|
|
2003
|
+
}
|
|
2004
|
+
if (actualValue !== expectedValue) {
|
|
2005
|
+
if (debugMode2) {
|
|
2006
|
+
console.log("execCommand changed the value but not as expected");
|
|
2007
|
+
console.log("Expected:", JSON.stringify(expectedValue.slice(0, 100)));
|
|
2008
|
+
console.log("Actual:", JSON.stringify(actualValue.slice(0, 100)));
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2462
2011
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2012
|
+
if (!canInsertText) {
|
|
2013
|
+
if (debugMode2)
|
|
2014
|
+
console.log("Using manual insertion");
|
|
2015
|
+
if (textarea.value === originalValue) {
|
|
2016
|
+
if (debugMode2)
|
|
2017
|
+
console.log("Value unchanged, doing manual replacement");
|
|
2018
|
+
try {
|
|
2019
|
+
document.execCommand("ms-beginUndoUnit");
|
|
2020
|
+
} catch (e) {
|
|
2021
|
+
}
|
|
2022
|
+
textarea.value = before + text + after;
|
|
2023
|
+
try {
|
|
2024
|
+
document.execCommand("ms-endUndoUnit");
|
|
2025
|
+
} catch (e) {
|
|
2026
|
+
}
|
|
2027
|
+
textarea.dispatchEvent(new CustomEvent("input", { bubbles: true, cancelable: true }));
|
|
2028
|
+
} else {
|
|
2029
|
+
if (debugMode2)
|
|
2030
|
+
console.log("Value was changed by execCommand, skipping manual insertion");
|
|
2031
|
+
}
|
|
2466
2032
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2033
|
+
if (debugMode2)
|
|
2034
|
+
console.log("Setting selection range:", selectionStart, selectionEnd);
|
|
2035
|
+
if (selectionStart != null && selectionEnd != null) {
|
|
2036
|
+
textarea.setSelectionRange(selectionStart, selectionEnd);
|
|
2037
|
+
} else {
|
|
2038
|
+
textarea.setSelectionRange(originalSelectionStart, textarea.selectionEnd);
|
|
2472
2039
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
margin-right: 8px;
|
|
2477
|
-
text-align: center;
|
|
2040
|
+
if (debugMode2) {
|
|
2041
|
+
console.log("Final value length:", textarea.value.length);
|
|
2042
|
+
console.groupEnd();
|
|
2478
2043
|
}
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
.
|
|
2482
|
-
|
|
2044
|
+
}
|
|
2045
|
+
function isMultipleLines(string) {
|
|
2046
|
+
return string.trim().split("\n").length > 1;
|
|
2047
|
+
}
|
|
2048
|
+
function wordSelectionStart(text, i) {
|
|
2049
|
+
let index = i;
|
|
2050
|
+
while (text[index] && text[index - 1] != null && !text[index - 1].match(/\s/)) {
|
|
2051
|
+
index--;
|
|
2483
2052
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2053
|
+
return index;
|
|
2054
|
+
}
|
|
2055
|
+
function wordSelectionEnd(text, i, multiline) {
|
|
2056
|
+
let index = i;
|
|
2057
|
+
const breakpoint = multiline ? /\n/ : /\s/;
|
|
2058
|
+
while (text[index] && !text[index].match(breakpoint)) {
|
|
2059
|
+
index++;
|
|
2489
2060
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2061
|
+
return index;
|
|
2062
|
+
}
|
|
2063
|
+
function expandSelectionToLine(textarea) {
|
|
2064
|
+
const lines = textarea.value.split("\n");
|
|
2065
|
+
let counter = 0;
|
|
2066
|
+
for (let index = 0; index < lines.length; index++) {
|
|
2067
|
+
const lineLength = lines[index].length + 1;
|
|
2068
|
+
if (textarea.selectionStart >= counter && textarea.selectionStart < counter + lineLength) {
|
|
2069
|
+
textarea.selectionStart = counter;
|
|
2070
|
+
}
|
|
2071
|
+
if (textarea.selectionEnd >= counter && textarea.selectionEnd < counter + lineLength) {
|
|
2072
|
+
if (index === lines.length - 1) {
|
|
2073
|
+
textarea.selectionEnd = Math.min(counter + lines[index].length, textarea.value.length);
|
|
2074
|
+
} else {
|
|
2075
|
+
textarea.selectionEnd = counter + lineLength - 1;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
counter += lineLength;
|
|
2494
2079
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
.
|
|
2498
|
-
|
|
2499
|
-
|
|
2080
|
+
}
|
|
2081
|
+
function expandSelectedText(textarea, prefixToUse, suffixToUse, multiline = false) {
|
|
2082
|
+
if (textarea.selectionStart === textarea.selectionEnd) {
|
|
2083
|
+
textarea.selectionStart = wordSelectionStart(textarea.value, textarea.selectionStart);
|
|
2084
|
+
textarea.selectionEnd = wordSelectionEnd(textarea.value, textarea.selectionEnd, multiline);
|
|
2085
|
+
} else {
|
|
2086
|
+
const expandedSelectionStart = textarea.selectionStart - prefixToUse.length;
|
|
2087
|
+
const expandedSelectionEnd = textarea.selectionEnd + suffixToUse.length;
|
|
2088
|
+
const beginsWithPrefix = textarea.value.slice(expandedSelectionStart, textarea.selectionStart) === prefixToUse;
|
|
2089
|
+
const endsWithSuffix = textarea.value.slice(textarea.selectionEnd, expandedSelectionEnd) === suffixToUse;
|
|
2090
|
+
if (beginsWithPrefix && endsWithSuffix) {
|
|
2091
|
+
textarea.selectionStart = expandedSelectionStart;
|
|
2092
|
+
textarea.selectionEnd = expandedSelectionEnd;
|
|
2093
|
+
}
|
|
2500
2094
|
}
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2095
|
+
return textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2096
|
+
}
|
|
2097
|
+
function newlinesToSurroundSelectedText(textarea) {
|
|
2098
|
+
const beforeSelection = textarea.value.slice(0, textarea.selectionStart);
|
|
2099
|
+
const afterSelection = textarea.value.slice(textarea.selectionEnd);
|
|
2100
|
+
const breaksBefore = beforeSelection.match(/\n*$/);
|
|
2101
|
+
const breaksAfter = afterSelection.match(/^\n*/);
|
|
2102
|
+
const newlinesBeforeSelection = breaksBefore ? breaksBefore[0].length : 0;
|
|
2103
|
+
const newlinesAfterSelection = breaksAfter ? breaksAfter[0].length : 0;
|
|
2104
|
+
let newlinesToAppend = "";
|
|
2105
|
+
let newlinesToPrepend = "";
|
|
2106
|
+
if (beforeSelection.match(/\S/) && newlinesBeforeSelection < 2) {
|
|
2107
|
+
newlinesToAppend = "\n".repeat(2 - newlinesBeforeSelection);
|
|
2505
2108
|
}
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1,
|
|
2509
|
-
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2,
|
|
2510
|
-
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
|
|
2511
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
|
2512
|
-
font-weight: 600 !important;
|
|
2513
|
-
margin: 0 !important;
|
|
2514
|
-
display: block !important;
|
|
2515
|
-
color: inherit !important; /* Use parent text color */
|
|
2516
|
-
line-height: 1 !important; /* Tight line height for headings */
|
|
2109
|
+
if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
|
|
2110
|
+
newlinesToPrepend = "\n".repeat(2 - newlinesAfterSelection);
|
|
2517
2111
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2112
|
+
return { newlinesToAppend, newlinesToPrepend };
|
|
2113
|
+
}
|
|
2114
|
+
function applyLineOperation(textarea, operation, options = {}) {
|
|
2115
|
+
const originalStart = textarea.selectionStart;
|
|
2116
|
+
const originalEnd = textarea.selectionEnd;
|
|
2117
|
+
const noInitialSelection = originalStart === originalEnd;
|
|
2118
|
+
const value = textarea.value;
|
|
2119
|
+
let lineStart = originalStart;
|
|
2120
|
+
while (lineStart > 0 && value[lineStart - 1] !== "\n") {
|
|
2121
|
+
lineStart--;
|
|
2521
2122
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2123
|
+
if (noInitialSelection) {
|
|
2124
|
+
let lineEnd = originalStart;
|
|
2125
|
+
while (lineEnd < value.length && value[lineEnd] !== "\n") {
|
|
2126
|
+
lineEnd++;
|
|
2127
|
+
}
|
|
2128
|
+
textarea.selectionStart = lineStart;
|
|
2129
|
+
textarea.selectionEnd = lineEnd;
|
|
2130
|
+
} else {
|
|
2131
|
+
expandSelectionToLine(textarea);
|
|
2525
2132
|
}
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2133
|
+
const result = operation(textarea);
|
|
2134
|
+
if (options.adjustSelection) {
|
|
2135
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2136
|
+
const isRemoving = selectedText.startsWith(options.prefix);
|
|
2137
|
+
const adjusted = options.adjustSelection(isRemoving, originalStart, originalEnd, lineStart);
|
|
2138
|
+
result.selectionStart = adjusted.start;
|
|
2139
|
+
result.selectionEnd = adjusted.end;
|
|
2140
|
+
} else if (options.prefix) {
|
|
2141
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2142
|
+
const isRemoving = selectedText.startsWith(options.prefix);
|
|
2143
|
+
if (noInitialSelection) {
|
|
2144
|
+
if (isRemoving) {
|
|
2145
|
+
result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
|
|
2146
|
+
result.selectionEnd = result.selectionStart;
|
|
2147
|
+
} else {
|
|
2148
|
+
result.selectionStart = originalStart + options.prefix.length;
|
|
2149
|
+
result.selectionEnd = result.selectionStart;
|
|
2150
|
+
}
|
|
2151
|
+
} else {
|
|
2152
|
+
if (isRemoving) {
|
|
2153
|
+
result.selectionStart = Math.max(originalStart - options.prefix.length, lineStart);
|
|
2154
|
+
result.selectionEnd = Math.max(originalEnd - options.prefix.length, lineStart);
|
|
2155
|
+
} else {
|
|
2156
|
+
result.selectionStart = originalStart + options.prefix.length;
|
|
2157
|
+
result.selectionEnd = originalEnd + options.prefix.length;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2529
2160
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2161
|
+
return result;
|
|
2162
|
+
}
|
|
2163
|
+
function blockStyle(textarea, style) {
|
|
2164
|
+
let newlinesToAppend;
|
|
2165
|
+
let newlinesToPrepend;
|
|
2166
|
+
const { prefix, suffix, blockPrefix, blockSuffix, replaceNext, prefixSpace, scanFor, surroundWithNewlines, trimFirst } = style;
|
|
2167
|
+
const originalSelectionStart = textarea.selectionStart;
|
|
2168
|
+
const originalSelectionEnd = textarea.selectionEnd;
|
|
2169
|
+
let selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2170
|
+
let prefixToUse = isMultipleLines(selectedText) && blockPrefix && blockPrefix.length > 0 ? `${blockPrefix}
|
|
2171
|
+
` : prefix;
|
|
2172
|
+
let suffixToUse = isMultipleLines(selectedText) && blockSuffix && blockSuffix.length > 0 ? `
|
|
2173
|
+
${blockSuffix}` : suffix;
|
|
2174
|
+
if (prefixSpace) {
|
|
2175
|
+
const beforeSelection = textarea.value[textarea.selectionStart - 1];
|
|
2176
|
+
if (textarea.selectionStart !== 0 && beforeSelection != null && !beforeSelection.match(/\s/)) {
|
|
2177
|
+
prefixToUse = ` ${prefixToUse}`;
|
|
2178
|
+
}
|
|
2537
2179
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2180
|
+
selectedText = expandSelectedText(textarea, prefixToUse, suffixToUse, style.multiline);
|
|
2181
|
+
let selectionStart = textarea.selectionStart;
|
|
2182
|
+
let selectionEnd = textarea.selectionEnd;
|
|
2183
|
+
const hasReplaceNext = replaceNext && replaceNext.length > 0 && suffixToUse.indexOf(replaceNext) > -1 && selectedText.length > 0;
|
|
2184
|
+
if (surroundWithNewlines) {
|
|
2185
|
+
const ref = newlinesToSurroundSelectedText(textarea);
|
|
2186
|
+
newlinesToAppend = ref.newlinesToAppend;
|
|
2187
|
+
newlinesToPrepend = ref.newlinesToPrepend;
|
|
2188
|
+
prefixToUse = newlinesToAppend + prefix;
|
|
2189
|
+
suffixToUse += newlinesToPrepend;
|
|
2544
2190
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2191
|
+
if (selectedText.startsWith(prefixToUse) && selectedText.endsWith(suffixToUse)) {
|
|
2192
|
+
const replacementText = selectedText.slice(prefixToUse.length, selectedText.length - suffixToUse.length);
|
|
2193
|
+
if (originalSelectionStart === originalSelectionEnd) {
|
|
2194
|
+
let position = originalSelectionStart - prefixToUse.length;
|
|
2195
|
+
position = Math.max(position, selectionStart);
|
|
2196
|
+
position = Math.min(position, selectionStart + replacementText.length);
|
|
2197
|
+
selectionStart = selectionEnd = position;
|
|
2198
|
+
} else {
|
|
2199
|
+
selectionEnd = selectionStart + replacementText.length;
|
|
2200
|
+
}
|
|
2201
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
|
2202
|
+
} else if (!hasReplaceNext) {
|
|
2203
|
+
let replacementText = prefixToUse + selectedText + suffixToUse;
|
|
2204
|
+
selectionStart = originalSelectionStart + prefixToUse.length;
|
|
2205
|
+
selectionEnd = originalSelectionEnd + prefixToUse.length;
|
|
2206
|
+
const whitespaceEdges = selectedText.match(/^\s*|\s*$/g);
|
|
2207
|
+
if (trimFirst && whitespaceEdges) {
|
|
2208
|
+
const leadingWhitespace = whitespaceEdges[0] || "";
|
|
2209
|
+
const trailingWhitespace = whitespaceEdges[1] || "";
|
|
2210
|
+
replacementText = leadingWhitespace + prefixToUse + selectedText.trim() + suffixToUse + trailingWhitespace;
|
|
2211
|
+
selectionStart += leadingWhitespace.length;
|
|
2212
|
+
selectionEnd -= trailingWhitespace.length;
|
|
2213
|
+
}
|
|
2214
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
|
2215
|
+
} else if (scanFor && scanFor.length > 0 && selectedText.match(scanFor)) {
|
|
2216
|
+
suffixToUse = suffixToUse.replace(replaceNext, selectedText);
|
|
2217
|
+
const replacementText = prefixToUse + suffixToUse;
|
|
2218
|
+
selectionStart = selectionEnd = selectionStart + prefixToUse.length;
|
|
2219
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
|
2220
|
+
} else {
|
|
2221
|
+
const replacementText = prefixToUse + selectedText + suffixToUse;
|
|
2222
|
+
selectionStart = selectionStart + prefixToUse.length + selectedText.length + suffixToUse.indexOf(replaceNext);
|
|
2223
|
+
selectionEnd = selectionStart + replaceNext.length;
|
|
2224
|
+
return { text: replacementText, selectionStart, selectionEnd };
|
|
2550
2225
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2226
|
+
}
|
|
2227
|
+
function multilineStyle(textarea, style) {
|
|
2228
|
+
const { prefix, suffix, surroundWithNewlines } = style;
|
|
2229
|
+
let text = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2230
|
+
let selectionStart = textarea.selectionStart;
|
|
2231
|
+
let selectionEnd = textarea.selectionEnd;
|
|
2232
|
+
const lines = text.split("\n");
|
|
2233
|
+
const undoStyle = lines.every((line) => line.startsWith(prefix) && (!suffix || line.endsWith(suffix)));
|
|
2234
|
+
if (undoStyle) {
|
|
2235
|
+
text = lines.map((line) => {
|
|
2236
|
+
let result = line.slice(prefix.length);
|
|
2237
|
+
if (suffix) {
|
|
2238
|
+
result = result.slice(0, result.length - suffix.length);
|
|
2239
|
+
}
|
|
2240
|
+
return result;
|
|
2241
|
+
}).join("\n");
|
|
2242
|
+
selectionEnd = selectionStart + text.length;
|
|
2243
|
+
} else {
|
|
2244
|
+
text = lines.map((line) => prefix + line + (suffix || "")).join("\n");
|
|
2245
|
+
if (surroundWithNewlines) {
|
|
2246
|
+
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
|
2247
|
+
selectionStart += newlinesToAppend.length;
|
|
2248
|
+
selectionEnd = selectionStart + text.length;
|
|
2249
|
+
text = newlinesToAppend + text + newlinesToPrepend;
|
|
2250
|
+
}
|
|
2556
2251
|
}
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2252
|
+
return { text, selectionStart, selectionEnd };
|
|
2253
|
+
}
|
|
2254
|
+
function undoOrderedListStyle(text) {
|
|
2255
|
+
const lines = text.split("\n");
|
|
2256
|
+
const orderedListRegex = /^\d+\.\s+/;
|
|
2257
|
+
const shouldUndoOrderedList = lines.every((line) => orderedListRegex.test(line));
|
|
2258
|
+
let result = lines;
|
|
2259
|
+
if (shouldUndoOrderedList) {
|
|
2260
|
+
result = lines.map((line) => line.replace(orderedListRegex, ""));
|
|
2562
2261
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2262
|
+
return {
|
|
2263
|
+
text: result.join("\n"),
|
|
2264
|
+
processed: shouldUndoOrderedList
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
function undoUnorderedListStyle(text) {
|
|
2268
|
+
const lines = text.split("\n");
|
|
2269
|
+
const unorderedListPrefix = "- ";
|
|
2270
|
+
const shouldUndoUnorderedList = lines.every((line) => line.startsWith(unorderedListPrefix));
|
|
2271
|
+
let result = lines;
|
|
2272
|
+
if (shouldUndoUnorderedList) {
|
|
2273
|
+
result = lines.map((line) => line.slice(unorderedListPrefix.length));
|
|
2567
2274
|
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2275
|
+
return {
|
|
2276
|
+
text: result.join("\n"),
|
|
2277
|
+
processed: shouldUndoUnorderedList
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function makePrefix(index, unorderedList) {
|
|
2281
|
+
if (unorderedList) {
|
|
2282
|
+
return "- ";
|
|
2283
|
+
} else {
|
|
2284
|
+
return `${index + 1}. `;
|
|
2572
2285
|
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2286
|
+
}
|
|
2287
|
+
function clearExistingListStyle(style, selectedText) {
|
|
2288
|
+
let undoResult;
|
|
2289
|
+
let undoResultOppositeList;
|
|
2290
|
+
let pristineText;
|
|
2291
|
+
if (style.orderedList) {
|
|
2292
|
+
undoResult = undoOrderedListStyle(selectedText);
|
|
2293
|
+
undoResultOppositeList = undoUnorderedListStyle(undoResult.text);
|
|
2294
|
+
pristineText = undoResultOppositeList.text;
|
|
2295
|
+
} else {
|
|
2296
|
+
undoResult = undoUnorderedListStyle(selectedText);
|
|
2297
|
+
undoResultOppositeList = undoOrderedListStyle(undoResult.text);
|
|
2298
|
+
pristineText = undoResultOppositeList.text;
|
|
2580
2299
|
}
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2300
|
+
return [undoResult, undoResultOppositeList, pristineText];
|
|
2301
|
+
}
|
|
2302
|
+
function listStyle(textarea, style) {
|
|
2303
|
+
const noInitialSelection = textarea.selectionStart === textarea.selectionEnd;
|
|
2304
|
+
let selectionStart = textarea.selectionStart;
|
|
2305
|
+
let selectionEnd = textarea.selectionEnd;
|
|
2306
|
+
expandSelectionToLine(textarea);
|
|
2307
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2308
|
+
const [undoResult, undoResultOppositeList, pristineText] = clearExistingListStyle(style, selectedText);
|
|
2309
|
+
const prefixedLines = pristineText.split("\n").map((value, index) => {
|
|
2310
|
+
return `${makePrefix(index, style.unorderedList)}${value}`;
|
|
2311
|
+
});
|
|
2312
|
+
const totalPrefixLength = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
2313
|
+
return previousValue + makePrefix(currentIndex, style.unorderedList).length;
|
|
2314
|
+
}, 0);
|
|
2315
|
+
const totalPrefixLengthOppositeList = prefixedLines.reduce((previousValue, _currentValue, currentIndex) => {
|
|
2316
|
+
return previousValue + makePrefix(currentIndex, !style.unorderedList).length;
|
|
2317
|
+
}, 0);
|
|
2318
|
+
if (undoResult.processed) {
|
|
2319
|
+
if (noInitialSelection) {
|
|
2320
|
+
selectionStart = Math.max(selectionStart - makePrefix(0, style.unorderedList).length, 0);
|
|
2321
|
+
selectionEnd = selectionStart;
|
|
2322
|
+
} else {
|
|
2323
|
+
selectionStart = textarea.selectionStart;
|
|
2324
|
+
selectionEnd = textarea.selectionEnd - totalPrefixLength;
|
|
2325
|
+
}
|
|
2326
|
+
return { text: pristineText, selectionStart, selectionEnd };
|
|
2591
2327
|
}
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2328
|
+
const { newlinesToAppend, newlinesToPrepend } = newlinesToSurroundSelectedText(textarea);
|
|
2329
|
+
const text = newlinesToAppend + prefixedLines.join("\n") + newlinesToPrepend;
|
|
2330
|
+
if (noInitialSelection) {
|
|
2331
|
+
selectionStart = Math.max(selectionStart + makePrefix(0, style.unorderedList).length + newlinesToAppend.length, 0);
|
|
2332
|
+
selectionEnd = selectionStart;
|
|
2333
|
+
} else {
|
|
2334
|
+
if (undoResultOppositeList.processed) {
|
|
2335
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
|
2336
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength - totalPrefixLengthOppositeList;
|
|
2337
|
+
} else {
|
|
2338
|
+
selectionStart = Math.max(textarea.selectionStart + newlinesToAppend.length, 0);
|
|
2339
|
+
selectionEnd = textarea.selectionEnd + newlinesToAppend.length + totalPrefixLength;
|
|
2340
|
+
}
|
|
2596
2341
|
}
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2342
|
+
return { text, selectionStart, selectionEnd };
|
|
2343
|
+
}
|
|
2344
|
+
function applyListStyle(textarea, style) {
|
|
2345
|
+
const result = applyLineOperation(
|
|
2346
|
+
textarea,
|
|
2347
|
+
(ta) => listStyle(ta, style),
|
|
2348
|
+
{
|
|
2349
|
+
// Custom selection adjustment for lists
|
|
2350
|
+
adjustSelection: (isRemoving, selStart, selEnd, lineStart) => {
|
|
2351
|
+
const currentLine = textarea.value.slice(lineStart, textarea.selectionEnd);
|
|
2352
|
+
const orderedListRegex = /^\d+\.\s+/;
|
|
2353
|
+
const unorderedListRegex = /^- /;
|
|
2354
|
+
const hasOrderedList = orderedListRegex.test(currentLine);
|
|
2355
|
+
const hasUnorderedList = unorderedListRegex.test(currentLine);
|
|
2356
|
+
const isRemovingCurrent = style.orderedList && hasOrderedList || style.unorderedList && hasUnorderedList;
|
|
2357
|
+
if (selStart === selEnd) {
|
|
2358
|
+
if (isRemovingCurrent) {
|
|
2359
|
+
const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
|
|
2360
|
+
const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
|
|
2361
|
+
return {
|
|
2362
|
+
start: Math.max(selStart - prefixLength, lineStart),
|
|
2363
|
+
end: Math.max(selStart - prefixLength, lineStart)
|
|
2364
|
+
};
|
|
2365
|
+
} else if (hasOrderedList || hasUnorderedList) {
|
|
2366
|
+
const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
|
|
2367
|
+
const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
|
|
2368
|
+
const newPrefixLength = style.unorderedList ? 2 : 3;
|
|
2369
|
+
const adjustment = newPrefixLength - oldPrefixLength;
|
|
2370
|
+
return {
|
|
2371
|
+
start: selStart + adjustment,
|
|
2372
|
+
end: selStart + adjustment
|
|
2373
|
+
};
|
|
2374
|
+
} else {
|
|
2375
|
+
const prefixLength = style.unorderedList ? 2 : 3;
|
|
2376
|
+
return {
|
|
2377
|
+
start: selStart + prefixLength,
|
|
2378
|
+
end: selStart + prefixLength
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
} else {
|
|
2382
|
+
if (isRemovingCurrent) {
|
|
2383
|
+
const prefixMatch = currentLine.match(style.orderedList ? orderedListRegex : unorderedListRegex);
|
|
2384
|
+
const prefixLength = prefixMatch ? prefixMatch[0].length : 0;
|
|
2385
|
+
return {
|
|
2386
|
+
start: Math.max(selStart - prefixLength, lineStart),
|
|
2387
|
+
end: Math.max(selEnd - prefixLength, lineStart)
|
|
2388
|
+
};
|
|
2389
|
+
} else if (hasOrderedList || hasUnorderedList) {
|
|
2390
|
+
const oldPrefixMatch = currentLine.match(hasOrderedList ? orderedListRegex : unorderedListRegex);
|
|
2391
|
+
const oldPrefixLength = oldPrefixMatch ? oldPrefixMatch[0].length : 0;
|
|
2392
|
+
const newPrefixLength = style.unorderedList ? 2 : 3;
|
|
2393
|
+
const adjustment = newPrefixLength - oldPrefixLength;
|
|
2394
|
+
return {
|
|
2395
|
+
start: selStart + adjustment,
|
|
2396
|
+
end: selEnd + adjustment
|
|
2397
|
+
};
|
|
2398
|
+
} else {
|
|
2399
|
+
const prefixLength = style.unorderedList ? 2 : 3;
|
|
2400
|
+
return {
|
|
2401
|
+
start: selStart + prefixLength,
|
|
2402
|
+
end: selEnd + prefixLength
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
);
|
|
2409
|
+
insertText(textarea, result);
|
|
2410
|
+
}
|
|
2411
|
+
function getActiveFormats(textarea) {
|
|
2412
|
+
if (!textarea)
|
|
2413
|
+
return [];
|
|
2414
|
+
const formats = [];
|
|
2415
|
+
const { selectionStart, selectionEnd, value } = textarea;
|
|
2416
|
+
const lines = value.split("\n");
|
|
2417
|
+
let lineStart = 0;
|
|
2418
|
+
let currentLine = "";
|
|
2419
|
+
for (const line of lines) {
|
|
2420
|
+
if (selectionStart >= lineStart && selectionStart <= lineStart + line.length) {
|
|
2421
|
+
currentLine = line;
|
|
2422
|
+
break;
|
|
2423
|
+
}
|
|
2424
|
+
lineStart += line.length + 1;
|
|
2605
2425
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2426
|
+
if (currentLine.startsWith("- ")) {
|
|
2427
|
+
if (currentLine.startsWith("- [ ] ") || currentLine.startsWith("- [x] ")) {
|
|
2428
|
+
formats.push("task-list");
|
|
2429
|
+
} else {
|
|
2430
|
+
formats.push("bullet-list");
|
|
2431
|
+
}
|
|
2610
2432
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
display: none !important;
|
|
2433
|
+
if (/^\d+\.\s/.test(currentLine)) {
|
|
2434
|
+
formats.push("numbered-list");
|
|
2614
2435
|
}
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
.overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .blockquote {
|
|
2618
|
-
display: block !important;
|
|
2619
|
-
border-left: 4px solid var(--blockquote, #ddd) !important;
|
|
2620
|
-
padding-left: 1em !important;
|
|
2621
|
-
margin: 1em 0 !important;
|
|
2622
|
-
font-style: italic !important;
|
|
2436
|
+
if (currentLine.startsWith("> ")) {
|
|
2437
|
+
formats.push("quote");
|
|
2623
2438
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
.
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2439
|
+
if (currentLine.startsWith("# "))
|
|
2440
|
+
formats.push("header");
|
|
2441
|
+
if (currentLine.startsWith("## "))
|
|
2442
|
+
formats.push("header-2");
|
|
2443
|
+
if (currentLine.startsWith("### "))
|
|
2444
|
+
formats.push("header-3");
|
|
2445
|
+
const lookBehind = Math.max(0, selectionStart - 10);
|
|
2446
|
+
const lookAhead = Math.min(value.length, selectionEnd + 10);
|
|
2447
|
+
const surrounding = value.slice(lookBehind, lookAhead);
|
|
2448
|
+
if (surrounding.includes("**")) {
|
|
2449
|
+
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
2450
|
+
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
2451
|
+
const lastOpenBold = beforeCursor.lastIndexOf("**");
|
|
2452
|
+
const nextCloseBold = afterCursor.indexOf("**");
|
|
2453
|
+
if (lastOpenBold !== -1 && nextCloseBold !== -1) {
|
|
2454
|
+
formats.push("bold");
|
|
2455
|
+
}
|
|
2631
2456
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2457
|
+
if (surrounding.includes("_")) {
|
|
2458
|
+
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
2459
|
+
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
2460
|
+
const lastOpenItalic = beforeCursor.lastIndexOf("_");
|
|
2461
|
+
const nextCloseItalic = afterCursor.indexOf("_");
|
|
2462
|
+
if (lastOpenItalic !== -1 && nextCloseItalic !== -1) {
|
|
2463
|
+
formats.push("italic");
|
|
2464
|
+
}
|
|
2640
2465
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2466
|
+
if (surrounding.includes("`")) {
|
|
2467
|
+
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
2468
|
+
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
2469
|
+
if (beforeCursor.includes("`") && afterCursor.includes("`")) {
|
|
2470
|
+
formats.push("code");
|
|
2471
|
+
}
|
|
2646
2472
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2473
|
+
if (surrounding.includes("[") && surrounding.includes("]")) {
|
|
2474
|
+
const beforeCursor = value.slice(Math.max(0, selectionStart - 100), selectionStart);
|
|
2475
|
+
const afterCursor = value.slice(selectionEnd, Math.min(value.length, selectionEnd + 100));
|
|
2476
|
+
const lastOpenBracket = beforeCursor.lastIndexOf("[");
|
|
2477
|
+
const nextCloseBracket = afterCursor.indexOf("]");
|
|
2478
|
+
if (lastOpenBracket !== -1 && nextCloseBracket !== -1) {
|
|
2479
|
+
const afterBracket = value.slice(selectionEnd + nextCloseBracket + 1, selectionEnd + nextCloseBracket + 10);
|
|
2480
|
+
if (afterBracket.startsWith("(")) {
|
|
2481
|
+
formats.push("link");
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2651
2484
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2485
|
+
return formats;
|
|
2486
|
+
}
|
|
2487
|
+
function toggleBold(textarea) {
|
|
2488
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2489
|
+
return;
|
|
2490
|
+
debugLog("toggleBold", "Starting");
|
|
2491
|
+
debugSelection(textarea, "Before");
|
|
2492
|
+
const style = mergeWithDefaults(FORMATS.bold);
|
|
2493
|
+
const result = blockStyle(textarea, style);
|
|
2494
|
+
debugResult(result);
|
|
2495
|
+
insertText(textarea, result);
|
|
2496
|
+
debugSelection(textarea, "After");
|
|
2497
|
+
}
|
|
2498
|
+
function toggleItalic(textarea) {
|
|
2499
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2500
|
+
return;
|
|
2501
|
+
const style = mergeWithDefaults(FORMATS.italic);
|
|
2502
|
+
const result = blockStyle(textarea, style);
|
|
2503
|
+
insertText(textarea, result);
|
|
2504
|
+
}
|
|
2505
|
+
function toggleCode(textarea) {
|
|
2506
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2507
|
+
return;
|
|
2508
|
+
const style = mergeWithDefaults(FORMATS.code);
|
|
2509
|
+
const result = blockStyle(textarea, style);
|
|
2510
|
+
insertText(textarea, result);
|
|
2511
|
+
}
|
|
2512
|
+
function insertLink(textarea, options = {}) {
|
|
2513
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2514
|
+
return;
|
|
2515
|
+
const selectedText = textarea.value.slice(textarea.selectionStart, textarea.selectionEnd);
|
|
2516
|
+
let style = mergeWithDefaults(FORMATS.link);
|
|
2517
|
+
const isURL = selectedText && selectedText.match(/^https?:\/\//);
|
|
2518
|
+
if (isURL && !options.url) {
|
|
2519
|
+
style.suffix = `](${selectedText})`;
|
|
2520
|
+
style.replaceNext = "";
|
|
2521
|
+
} else if (options.url) {
|
|
2522
|
+
style.suffix = `](${options.url})`;
|
|
2523
|
+
style.replaceNext = "";
|
|
2659
2524
|
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
color: white !important;
|
|
2666
|
-
padding: 6px 10px !important;
|
|
2667
|
-
border-radius: 16px !important;
|
|
2668
|
-
font-size: 12px !important;
|
|
2669
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
|
|
2670
|
-
display: none !important;
|
|
2671
|
-
z-index: 10000 !important;
|
|
2672
|
-
cursor: pointer !important;
|
|
2673
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important;
|
|
2674
|
-
max-width: 300px !important;
|
|
2675
|
-
white-space: nowrap !important;
|
|
2676
|
-
overflow: hidden !important;
|
|
2677
|
-
text-overflow: ellipsis !important;
|
|
2678
|
-
|
|
2679
|
-
/* Base positioning for Floating UI fallback */
|
|
2680
|
-
position: absolute;
|
|
2525
|
+
if (options.text && !selectedText) {
|
|
2526
|
+
const pos = textarea.selectionStart;
|
|
2527
|
+
textarea.value = textarea.value.slice(0, pos) + options.text + textarea.value.slice(pos);
|
|
2528
|
+
textarea.selectionStart = pos;
|
|
2529
|
+
textarea.selectionEnd = pos + options.text.length;
|
|
2681
2530
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2531
|
+
const result = blockStyle(textarea, style);
|
|
2532
|
+
insertText(textarea, result);
|
|
2533
|
+
}
|
|
2534
|
+
function toggleBulletList(textarea) {
|
|
2535
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2536
|
+
return;
|
|
2537
|
+
const style = mergeWithDefaults(FORMATS.bulletList);
|
|
2538
|
+
applyListStyle(textarea, style);
|
|
2539
|
+
}
|
|
2540
|
+
function toggleNumberedList(textarea) {
|
|
2541
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2542
|
+
return;
|
|
2543
|
+
const style = mergeWithDefaults(FORMATS.numberedList);
|
|
2544
|
+
applyListStyle(textarea, style);
|
|
2545
|
+
}
|
|
2546
|
+
function toggleQuote(textarea) {
|
|
2547
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2548
|
+
return;
|
|
2549
|
+
debugLog("toggleQuote", "Starting");
|
|
2550
|
+
debugSelection(textarea, "Initial");
|
|
2551
|
+
const style = mergeWithDefaults(FORMATS.quote);
|
|
2552
|
+
const result = applyLineOperation(
|
|
2553
|
+
textarea,
|
|
2554
|
+
(ta) => multilineStyle(ta, style),
|
|
2555
|
+
{ prefix: style.prefix }
|
|
2556
|
+
);
|
|
2557
|
+
debugResult(result);
|
|
2558
|
+
insertText(textarea, result);
|
|
2559
|
+
debugSelection(textarea, "Final");
|
|
2560
|
+
}
|
|
2561
|
+
function toggleTaskList(textarea) {
|
|
2562
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2563
|
+
return;
|
|
2564
|
+
const style = mergeWithDefaults(FORMATS.taskList);
|
|
2565
|
+
const result = applyLineOperation(
|
|
2566
|
+
textarea,
|
|
2567
|
+
(ta) => multilineStyle(ta, style),
|
|
2568
|
+
{ prefix: style.prefix }
|
|
2569
|
+
);
|
|
2570
|
+
insertText(textarea, result);
|
|
2571
|
+
}
|
|
2572
|
+
function insertHeader(textarea, level = 1, toggle = false) {
|
|
2573
|
+
if (!textarea || textarea.disabled || textarea.readOnly)
|
|
2574
|
+
return;
|
|
2575
|
+
if (level < 1 || level > 6)
|
|
2576
|
+
level = 1;
|
|
2577
|
+
debugLog("insertHeader", `============ START ============`);
|
|
2578
|
+
debugLog("insertHeader", `Level: ${level}, Toggle: ${toggle}`);
|
|
2579
|
+
debugLog("insertHeader", `Initial cursor: ${textarea.selectionStart}-${textarea.selectionEnd}`);
|
|
2580
|
+
const headerKey = `header${level === 1 ? "1" : level}`;
|
|
2581
|
+
const style = mergeWithDefaults(FORMATS[headerKey] || FORMATS.header1);
|
|
2582
|
+
debugLog("insertHeader", `Style prefix: "${style.prefix}"`);
|
|
2583
|
+
const value = textarea.value;
|
|
2584
|
+
const originalStart = textarea.selectionStart;
|
|
2585
|
+
const originalEnd = textarea.selectionEnd;
|
|
2586
|
+
let lineStart = originalStart;
|
|
2587
|
+
while (lineStart > 0 && value[lineStart - 1] !== "\n") {
|
|
2588
|
+
lineStart--;
|
|
2685
2589
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
.overtype-link-tooltip {
|
|
2690
|
-
/* Only anchor positioning specific properties */
|
|
2691
|
-
position-anchor: var(--target-anchor, --link-0);
|
|
2692
|
-
position-area: block-end center;
|
|
2693
|
-
margin-top: 8px !important;
|
|
2694
|
-
position-try: most-width block-end inline-end, flip-inline, block-start center;
|
|
2695
|
-
position-visibility: anchors-visible;
|
|
2696
|
-
}
|
|
2590
|
+
let lineEnd = originalEnd;
|
|
2591
|
+
while (lineEnd < value.length && value[lineEnd] !== "\n") {
|
|
2592
|
+
lineEnd++;
|
|
2697
2593
|
}
|
|
2698
|
-
|
|
2699
|
-
${
|
|
2700
|
-
|
|
2594
|
+
const currentLineContent = value.slice(lineStart, lineEnd);
|
|
2595
|
+
debugLog("insertHeader", `Current line (before): "${currentLineContent}"`);
|
|
2596
|
+
const existingHeaderMatch = currentLineContent.match(/^(#{1,6})\s*/);
|
|
2597
|
+
const existingLevel = existingHeaderMatch ? existingHeaderMatch[1].length : 0;
|
|
2598
|
+
const existingPrefixLength = existingHeaderMatch ? existingHeaderMatch[0].length : 0;
|
|
2599
|
+
debugLog("insertHeader", `Existing header check:`);
|
|
2600
|
+
debugLog("insertHeader", ` - Match: ${existingHeaderMatch ? `"${existingHeaderMatch[0]}"` : "none"}`);
|
|
2601
|
+
debugLog("insertHeader", ` - Existing level: ${existingLevel}`);
|
|
2602
|
+
debugLog("insertHeader", ` - Existing prefix length: ${existingPrefixLength}`);
|
|
2603
|
+
debugLog("insertHeader", ` - Target level: ${level}`);
|
|
2604
|
+
const shouldToggleOff = toggle && existingLevel === level;
|
|
2605
|
+
debugLog("insertHeader", `Should toggle OFF: ${shouldToggleOff} (toggle=${toggle}, existingLevel=${existingLevel}, level=${level})`);
|
|
2606
|
+
const result = applyLineOperation(
|
|
2607
|
+
textarea,
|
|
2608
|
+
(ta) => {
|
|
2609
|
+
const currentLine = ta.value.slice(ta.selectionStart, ta.selectionEnd);
|
|
2610
|
+
debugLog("insertHeader", `Line in operation: "${currentLine}"`);
|
|
2611
|
+
const cleanedLine = currentLine.replace(/^#{1,6}\s*/, "");
|
|
2612
|
+
debugLog("insertHeader", `Cleaned line: "${cleanedLine}"`);
|
|
2613
|
+
let newLine;
|
|
2614
|
+
if (shouldToggleOff) {
|
|
2615
|
+
debugLog("insertHeader", "ACTION: Toggling OFF - removing header");
|
|
2616
|
+
newLine = cleanedLine;
|
|
2617
|
+
} else if (existingLevel > 0) {
|
|
2618
|
+
debugLog("insertHeader", `ACTION: Replacing H${existingLevel} with H${level}`);
|
|
2619
|
+
newLine = style.prefix + cleanedLine;
|
|
2620
|
+
} else {
|
|
2621
|
+
debugLog("insertHeader", "ACTION: Adding new header");
|
|
2622
|
+
newLine = style.prefix + cleanedLine;
|
|
2623
|
+
}
|
|
2624
|
+
debugLog("insertHeader", `New line: "${newLine}"`);
|
|
2625
|
+
return {
|
|
2626
|
+
text: newLine,
|
|
2627
|
+
selectionStart: ta.selectionStart,
|
|
2628
|
+
selectionEnd: ta.selectionEnd
|
|
2629
|
+
};
|
|
2630
|
+
},
|
|
2631
|
+
{
|
|
2632
|
+
prefix: style.prefix,
|
|
2633
|
+
// Custom selection adjustment for headers
|
|
2634
|
+
adjustSelection: (isRemoving, selStart, selEnd, lineStartPos) => {
|
|
2635
|
+
debugLog("insertHeader", `Adjusting selection:`);
|
|
2636
|
+
debugLog("insertHeader", ` - isRemoving param: ${isRemoving}`);
|
|
2637
|
+
debugLog("insertHeader", ` - shouldToggleOff: ${shouldToggleOff}`);
|
|
2638
|
+
debugLog("insertHeader", ` - selStart: ${selStart}, selEnd: ${selEnd}`);
|
|
2639
|
+
debugLog("insertHeader", ` - lineStartPos: ${lineStartPos}`);
|
|
2640
|
+
if (shouldToggleOff) {
|
|
2641
|
+
const adjustment = Math.max(selStart - existingPrefixLength, lineStartPos);
|
|
2642
|
+
debugLog("insertHeader", ` - Removing header, adjusting by -${existingPrefixLength}`);
|
|
2643
|
+
return {
|
|
2644
|
+
start: adjustment,
|
|
2645
|
+
end: selStart === selEnd ? adjustment : Math.max(selEnd - existingPrefixLength, lineStartPos)
|
|
2646
|
+
};
|
|
2647
|
+
} else if (existingPrefixLength > 0) {
|
|
2648
|
+
const prefixDiff = style.prefix.length - existingPrefixLength;
|
|
2649
|
+
debugLog("insertHeader", ` - Replacing header, adjusting by ${prefixDiff}`);
|
|
2650
|
+
return {
|
|
2651
|
+
start: selStart + prefixDiff,
|
|
2652
|
+
end: selEnd + prefixDiff
|
|
2653
|
+
};
|
|
2654
|
+
} else {
|
|
2655
|
+
debugLog("insertHeader", ` - Adding header, adjusting by +${style.prefix.length}`);
|
|
2656
|
+
return {
|
|
2657
|
+
start: selStart + style.prefix.length,
|
|
2658
|
+
end: selEnd + style.prefix.length
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
);
|
|
2664
|
+
debugLog("insertHeader", `Final result: text="${result.text}", cursor=${result.selectionStart}-${result.selectionEnd}`);
|
|
2665
|
+
debugLog("insertHeader", `============ END ============`);
|
|
2666
|
+
insertText(textarea, result);
|
|
2667
|
+
}
|
|
2668
|
+
function toggleH1(textarea) {
|
|
2669
|
+
insertHeader(textarea, 1, true);
|
|
2670
|
+
}
|
|
2671
|
+
function toggleH2(textarea) {
|
|
2672
|
+
insertHeader(textarea, 2, true);
|
|
2673
|
+
}
|
|
2674
|
+
function toggleH3(textarea) {
|
|
2675
|
+
insertHeader(textarea, 3, true);
|
|
2676
|
+
}
|
|
2677
|
+
function getActiveFormats2(textarea) {
|
|
2678
|
+
return getActiveFormats(textarea);
|
|
2701
2679
|
}
|
|
2702
2680
|
|
|
2703
2681
|
// src/toolbar.js
|
|
@@ -2757,55 +2735,43 @@ ${blockSuffix}` : suffix;
|
|
|
2757
2735
|
});
|
|
2758
2736
|
return button;
|
|
2759
2737
|
}
|
|
2760
|
-
button._clickHandler =
|
|
2738
|
+
button._clickHandler = (e) => {
|
|
2761
2739
|
e.preventDefault();
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
if (buttonConfig.action) {
|
|
2765
|
-
await buttonConfig.action({
|
|
2766
|
-
editor: this.editor,
|
|
2767
|
-
getValue: () => this.editor.getValue(),
|
|
2768
|
-
setValue: (value) => this.editor.setValue(value),
|
|
2769
|
-
event: e
|
|
2770
|
-
});
|
|
2771
|
-
}
|
|
2772
|
-
} catch (error) {
|
|
2773
|
-
console.error(`Button "${buttonConfig.name}" error:`, error);
|
|
2774
|
-
this.editor.wrapper.dispatchEvent(new CustomEvent("button-error", {
|
|
2775
|
-
detail: { buttonName: buttonConfig.name, error }
|
|
2776
|
-
}));
|
|
2777
|
-
button.classList.add("button-error");
|
|
2778
|
-
button.style.animation = "buttonError 0.3s";
|
|
2779
|
-
setTimeout(() => {
|
|
2780
|
-
button.classList.remove("button-error");
|
|
2781
|
-
button.style.animation = "";
|
|
2782
|
-
}, 300);
|
|
2783
|
-
}
|
|
2740
|
+
const actionId = buttonConfig.actionId || buttonConfig.name;
|
|
2741
|
+
this.editor.performAction(actionId, e);
|
|
2784
2742
|
};
|
|
2785
2743
|
button.addEventListener("click", button._clickHandler);
|
|
2786
2744
|
return button;
|
|
2787
2745
|
}
|
|
2788
2746
|
/**
|
|
2789
|
-
* Handle button action programmatically
|
|
2790
|
-
*
|
|
2747
|
+
* Handle button action programmatically
|
|
2748
|
+
* Accepts either an actionId string or a buttonConfig object (backwards compatible)
|
|
2749
|
+
* @param {string|Object} actionIdOrConfig - Action identifier string or button config object
|
|
2750
|
+
* @returns {Promise<boolean>} Whether the action was executed
|
|
2791
2751
|
*/
|
|
2792
|
-
async handleAction(
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
await
|
|
2752
|
+
async handleAction(actionIdOrConfig) {
|
|
2753
|
+
if (actionIdOrConfig && typeof actionIdOrConfig === "object" && typeof actionIdOrConfig.action === "function") {
|
|
2754
|
+
this.editor.textarea.focus();
|
|
2755
|
+
try {
|
|
2756
|
+
await actionIdOrConfig.action({
|
|
2797
2757
|
editor: this.editor,
|
|
2798
2758
|
getValue: () => this.editor.getValue(),
|
|
2799
2759
|
setValue: (value) => this.editor.setValue(value),
|
|
2800
2760
|
event: null
|
|
2801
2761
|
});
|
|
2762
|
+
return true;
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
console.error(`Action "${actionIdOrConfig.name}" error:`, error);
|
|
2765
|
+
this.editor.wrapper.dispatchEvent(new CustomEvent("button-error", {
|
|
2766
|
+
detail: { buttonName: actionIdOrConfig.name, error }
|
|
2767
|
+
}));
|
|
2768
|
+
return false;
|
|
2802
2769
|
}
|
|
2803
|
-
} catch (error) {
|
|
2804
|
-
console.error(`Action "${buttonConfig.name}" error:`, error);
|
|
2805
|
-
this.editor.wrapper.dispatchEvent(new CustomEvent("button-error", {
|
|
2806
|
-
detail: { buttonName: buttonConfig.name, error }
|
|
2807
|
-
}));
|
|
2808
2770
|
}
|
|
2771
|
+
if (typeof actionIdOrConfig === "string") {
|
|
2772
|
+
return this.editor.performAction(actionIdOrConfig, null);
|
|
2773
|
+
}
|
|
2774
|
+
return false;
|
|
2809
2775
|
}
|
|
2810
2776
|
/**
|
|
2811
2777
|
* Sanitize SVG to prevent XSS
|
|
@@ -2975,6 +2941,7 @@ ${blockSuffix}` : suffix;
|
|
|
2975
2941
|
this.visibilityChangeHandler = null;
|
|
2976
2942
|
this.useFloatingUI = false;
|
|
2977
2943
|
this.floatingUI = null;
|
|
2944
|
+
this.isTooltipHovered = false;
|
|
2978
2945
|
this.init();
|
|
2979
2946
|
}
|
|
2980
2947
|
async init() {
|
|
@@ -3008,14 +2975,25 @@ ${blockSuffix}` : suffix;
|
|
|
3008
2975
|
this.hide();
|
|
3009
2976
|
}
|
|
3010
2977
|
});
|
|
3011
|
-
this.editor.textarea.addEventListener("blur", () =>
|
|
2978
|
+
this.editor.textarea.addEventListener("blur", () => {
|
|
2979
|
+
if (!this.isTooltipHovered) {
|
|
2980
|
+
this.hide();
|
|
2981
|
+
}
|
|
2982
|
+
});
|
|
3012
2983
|
this.visibilityChangeHandler = () => {
|
|
3013
2984
|
if (document.hidden) {
|
|
3014
2985
|
this.hide();
|
|
3015
2986
|
}
|
|
3016
2987
|
};
|
|
3017
2988
|
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
3018
|
-
this.tooltip.addEventListener("mouseenter", () =>
|
|
2989
|
+
this.tooltip.addEventListener("mouseenter", () => {
|
|
2990
|
+
this.isTooltipHovered = true;
|
|
2991
|
+
this.cancelHide();
|
|
2992
|
+
});
|
|
2993
|
+
this.tooltip.addEventListener("mouseleave", () => {
|
|
2994
|
+
this.isTooltipHovered = false;
|
|
2995
|
+
this.scheduleHide();
|
|
2996
|
+
});
|
|
3019
2997
|
}
|
|
3020
2998
|
createTooltip() {
|
|
3021
2999
|
this.tooltip = document.createElement("div");
|
|
@@ -3125,6 +3103,7 @@ ${blockSuffix}` : suffix;
|
|
|
3125
3103
|
hide() {
|
|
3126
3104
|
this.tooltip.classList.remove("visible");
|
|
3127
3105
|
this.currentLink = null;
|
|
3106
|
+
this.isTooltipHovered = false;
|
|
3128
3107
|
}
|
|
3129
3108
|
scheduleHide() {
|
|
3130
3109
|
this.cancelHide();
|
|
@@ -3149,6 +3128,7 @@ ${blockSuffix}` : suffix;
|
|
|
3149
3128
|
this.currentLink = null;
|
|
3150
3129
|
this.floatingUI = null;
|
|
3151
3130
|
this.useFloatingUI = false;
|
|
3131
|
+
this.isTooltipHovered = false;
|
|
3152
3132
|
}
|
|
3153
3133
|
};
|
|
3154
3134
|
|
|
@@ -3219,27 +3199,30 @@ ${blockSuffix}` : suffix;
|
|
|
3219
3199
|
var toolbarButtons = {
|
|
3220
3200
|
bold: {
|
|
3221
3201
|
name: "bold",
|
|
3202
|
+
actionId: "toggleBold",
|
|
3222
3203
|
icon: boldIcon,
|
|
3223
3204
|
title: "Bold (Ctrl+B)",
|
|
3224
|
-
action: ({ editor
|
|
3205
|
+
action: ({ editor }) => {
|
|
3225
3206
|
toggleBold(editor.textarea);
|
|
3226
3207
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3227
3208
|
}
|
|
3228
3209
|
},
|
|
3229
3210
|
italic: {
|
|
3230
3211
|
name: "italic",
|
|
3212
|
+
actionId: "toggleItalic",
|
|
3231
3213
|
icon: italicIcon,
|
|
3232
3214
|
title: "Italic (Ctrl+I)",
|
|
3233
|
-
action: ({ editor
|
|
3215
|
+
action: ({ editor }) => {
|
|
3234
3216
|
toggleItalic(editor.textarea);
|
|
3235
3217
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3236
3218
|
}
|
|
3237
3219
|
},
|
|
3238
3220
|
code: {
|
|
3239
3221
|
name: "code",
|
|
3222
|
+
actionId: "toggleCode",
|
|
3240
3223
|
icon: codeIcon,
|
|
3241
3224
|
title: "Inline Code",
|
|
3242
|
-
action: ({ editor
|
|
3225
|
+
action: ({ editor }) => {
|
|
3243
3226
|
toggleCode(editor.textarea);
|
|
3244
3227
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3245
3228
|
}
|
|
@@ -3250,63 +3233,70 @@ ${blockSuffix}` : suffix;
|
|
|
3250
3233
|
},
|
|
3251
3234
|
link: {
|
|
3252
3235
|
name: "link",
|
|
3236
|
+
actionId: "insertLink",
|
|
3253
3237
|
icon: linkIcon,
|
|
3254
3238
|
title: "Insert Link",
|
|
3255
|
-
action: ({ editor
|
|
3239
|
+
action: ({ editor }) => {
|
|
3256
3240
|
insertLink(editor.textarea);
|
|
3257
3241
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3258
3242
|
}
|
|
3259
3243
|
},
|
|
3260
3244
|
h1: {
|
|
3261
3245
|
name: "h1",
|
|
3246
|
+
actionId: "toggleH1",
|
|
3262
3247
|
icon: h1Icon,
|
|
3263
3248
|
title: "Heading 1",
|
|
3264
|
-
action: ({ editor
|
|
3249
|
+
action: ({ editor }) => {
|
|
3265
3250
|
toggleH1(editor.textarea);
|
|
3266
3251
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3267
3252
|
}
|
|
3268
3253
|
},
|
|
3269
3254
|
h2: {
|
|
3270
3255
|
name: "h2",
|
|
3256
|
+
actionId: "toggleH2",
|
|
3271
3257
|
icon: h2Icon,
|
|
3272
3258
|
title: "Heading 2",
|
|
3273
|
-
action: ({ editor
|
|
3259
|
+
action: ({ editor }) => {
|
|
3274
3260
|
toggleH2(editor.textarea);
|
|
3275
3261
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3276
3262
|
}
|
|
3277
3263
|
},
|
|
3278
3264
|
h3: {
|
|
3279
3265
|
name: "h3",
|
|
3266
|
+
actionId: "toggleH3",
|
|
3280
3267
|
icon: h3Icon,
|
|
3281
3268
|
title: "Heading 3",
|
|
3282
|
-
action: ({ editor
|
|
3269
|
+
action: ({ editor }) => {
|
|
3283
3270
|
toggleH3(editor.textarea);
|
|
3284
3271
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3285
3272
|
}
|
|
3286
3273
|
},
|
|
3287
3274
|
bulletList: {
|
|
3288
3275
|
name: "bulletList",
|
|
3276
|
+
actionId: "toggleBulletList",
|
|
3289
3277
|
icon: bulletListIcon,
|
|
3290
3278
|
title: "Bullet List",
|
|
3291
|
-
action: ({ editor
|
|
3279
|
+
action: ({ editor }) => {
|
|
3292
3280
|
toggleBulletList(editor.textarea);
|
|
3293
3281
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3294
3282
|
}
|
|
3295
3283
|
},
|
|
3296
3284
|
orderedList: {
|
|
3297
3285
|
name: "orderedList",
|
|
3286
|
+
actionId: "toggleNumberedList",
|
|
3298
3287
|
icon: orderedListIcon,
|
|
3299
3288
|
title: "Numbered List",
|
|
3300
|
-
action: ({ editor
|
|
3289
|
+
action: ({ editor }) => {
|
|
3301
3290
|
toggleNumberedList(editor.textarea);
|
|
3302
3291
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3303
3292
|
}
|
|
3304
3293
|
},
|
|
3305
3294
|
taskList: {
|
|
3306
3295
|
name: "taskList",
|
|
3296
|
+
actionId: "toggleTaskList",
|
|
3307
3297
|
icon: taskListIcon,
|
|
3308
3298
|
title: "Task List",
|
|
3309
|
-
action: ({ editor
|
|
3299
|
+
action: ({ editor }) => {
|
|
3310
3300
|
if (toggleTaskList) {
|
|
3311
3301
|
toggleTaskList(editor.textarea);
|
|
3312
3302
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
@@ -3315,9 +3305,10 @@ ${blockSuffix}` : suffix;
|
|
|
3315
3305
|
},
|
|
3316
3306
|
quote: {
|
|
3317
3307
|
name: "quote",
|
|
3308
|
+
actionId: "toggleQuote",
|
|
3318
3309
|
icon: quoteIcon,
|
|
3319
3310
|
title: "Quote",
|
|
3320
|
-
action: ({ editor
|
|
3311
|
+
action: ({ editor }) => {
|
|
3321
3312
|
toggleQuote(editor.textarea);
|
|
3322
3313
|
editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3323
3314
|
}
|
|
@@ -3351,6 +3342,45 @@ ${blockSuffix}` : suffix;
|
|
|
3351
3342
|
];
|
|
3352
3343
|
|
|
3353
3344
|
// src/overtype.js
|
|
3345
|
+
function buildActionsMap(buttons) {
|
|
3346
|
+
const map = {};
|
|
3347
|
+
(buttons || []).forEach((btn) => {
|
|
3348
|
+
if (!btn || btn.name === "separator")
|
|
3349
|
+
return;
|
|
3350
|
+
const id = btn.actionId || btn.name;
|
|
3351
|
+
if (btn.action) {
|
|
3352
|
+
map[id] = btn.action;
|
|
3353
|
+
}
|
|
3354
|
+
});
|
|
3355
|
+
return map;
|
|
3356
|
+
}
|
|
3357
|
+
function normalizeButtons(buttons) {
|
|
3358
|
+
const list = buttons || defaultToolbarButtons;
|
|
3359
|
+
if (!Array.isArray(list))
|
|
3360
|
+
return null;
|
|
3361
|
+
return list.map((btn) => ({
|
|
3362
|
+
name: (btn == null ? void 0 : btn.name) || null,
|
|
3363
|
+
actionId: (btn == null ? void 0 : btn.actionId) || (btn == null ? void 0 : btn.name) || null,
|
|
3364
|
+
icon: (btn == null ? void 0 : btn.icon) || null,
|
|
3365
|
+
title: (btn == null ? void 0 : btn.title) || null
|
|
3366
|
+
}));
|
|
3367
|
+
}
|
|
3368
|
+
function toolbarButtonsChanged(prevButtons, nextButtons) {
|
|
3369
|
+
const prev = normalizeButtons(prevButtons);
|
|
3370
|
+
const next = normalizeButtons(nextButtons);
|
|
3371
|
+
if (prev === null || next === null)
|
|
3372
|
+
return prev !== next;
|
|
3373
|
+
if (prev.length !== next.length)
|
|
3374
|
+
return true;
|
|
3375
|
+
for (let i = 0; i < prev.length; i++) {
|
|
3376
|
+
const a = prev[i];
|
|
3377
|
+
const b = next[i];
|
|
3378
|
+
if (a.name !== b.name || a.actionId !== b.actionId || a.icon !== b.icon || a.title !== b.title) {
|
|
3379
|
+
return true;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
return false;
|
|
3383
|
+
}
|
|
3354
3384
|
var _OverType = class _OverType {
|
|
3355
3385
|
/**
|
|
3356
3386
|
* Constructor - Always returns an array of instances
|
|
@@ -3408,6 +3438,7 @@ ${blockSuffix}` : suffix;
|
|
|
3408
3438
|
this._buildFromScratch();
|
|
3409
3439
|
}
|
|
3410
3440
|
this.shortcuts = new ShortcutsManager(this);
|
|
3441
|
+
this._rebuildActionsMap();
|
|
3411
3442
|
this.linkTooltip = new LinkTooltip(this);
|
|
3412
3443
|
requestAnimationFrame(() => {
|
|
3413
3444
|
requestAnimationFrame(() => {
|
|
@@ -3663,6 +3694,17 @@ ${blockSuffix}` : suffix;
|
|
|
3663
3694
|
this._toolbarInputListener = null;
|
|
3664
3695
|
}
|
|
3665
3696
|
}
|
|
3697
|
+
/**
|
|
3698
|
+
* Rebuild the action map from current toolbar button configuration
|
|
3699
|
+
* Called during init and reinit to keep shortcuts in sync with toolbar buttons
|
|
3700
|
+
* @private
|
|
3701
|
+
*/
|
|
3702
|
+
_rebuildActionsMap() {
|
|
3703
|
+
this.actionsById = buildActionsMap(defaultToolbarButtons);
|
|
3704
|
+
if (this.options.toolbarButtons) {
|
|
3705
|
+
Object.assign(this.actionsById, buildActionsMap(this.options.toolbarButtons));
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3666
3708
|
/**
|
|
3667
3709
|
* Apply options to the editor
|
|
3668
3710
|
* @private
|
|
@@ -3924,6 +3966,40 @@ ${blockSuffix}` : suffix;
|
|
|
3924
3966
|
this._updateAutoHeight();
|
|
3925
3967
|
}
|
|
3926
3968
|
}
|
|
3969
|
+
/**
|
|
3970
|
+
* Execute an action by ID
|
|
3971
|
+
* Central dispatcher used by toolbar clicks, keyboard shortcuts, and programmatic calls
|
|
3972
|
+
* @param {string} actionId - The action identifier (e.g., 'toggleBold', 'insertLink')
|
|
3973
|
+
* @param {Event|null} event - Optional event that triggered the action
|
|
3974
|
+
* @returns {Promise<boolean>} Whether the action was executed successfully
|
|
3975
|
+
*/
|
|
3976
|
+
async performAction(actionId, event = null) {
|
|
3977
|
+
var _a;
|
|
3978
|
+
const textarea = this.textarea;
|
|
3979
|
+
if (!textarea)
|
|
3980
|
+
return false;
|
|
3981
|
+
const action = (_a = this.actionsById) == null ? void 0 : _a[actionId];
|
|
3982
|
+
if (!action) {
|
|
3983
|
+
console.warn(`OverType: Unknown action "${actionId}"`);
|
|
3984
|
+
return false;
|
|
3985
|
+
}
|
|
3986
|
+
textarea.focus();
|
|
3987
|
+
try {
|
|
3988
|
+
await action({
|
|
3989
|
+
editor: this,
|
|
3990
|
+
getValue: () => this.getValue(),
|
|
3991
|
+
setValue: (value) => this.setValue(value),
|
|
3992
|
+
event
|
|
3993
|
+
});
|
|
3994
|
+
return true;
|
|
3995
|
+
} catch (error) {
|
|
3996
|
+
console.error(`OverType: Action "${actionId}" error:`, error);
|
|
3997
|
+
this.wrapper.dispatchEvent(new CustomEvent("button-error", {
|
|
3998
|
+
detail: { actionId, error }
|
|
3999
|
+
}));
|
|
4000
|
+
return false;
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
3927
4003
|
/**
|
|
3928
4004
|
* Get the rendered HTML of the current content
|
|
3929
4005
|
* @param {Object} options - Rendering options
|
|
@@ -3980,7 +4056,17 @@ ${blockSuffix}` : suffix;
|
|
|
3980
4056
|
* @param {Object} options - New options to apply
|
|
3981
4057
|
*/
|
|
3982
4058
|
reinit(options = {}) {
|
|
4059
|
+
var _a;
|
|
4060
|
+
const prevToolbarButtons = (_a = this.options) == null ? void 0 : _a.toolbarButtons;
|
|
3983
4061
|
this.options = this._mergeOptions({ ...this.options, ...options });
|
|
4062
|
+
const toolbarNeedsRebuild = this.toolbar && this.options.toolbar && toolbarButtonsChanged(prevToolbarButtons, this.options.toolbarButtons);
|
|
4063
|
+
this._rebuildActionsMap();
|
|
4064
|
+
if (toolbarNeedsRebuild) {
|
|
4065
|
+
this._cleanupToolbarListeners();
|
|
4066
|
+
this.toolbar.destroy();
|
|
4067
|
+
this.toolbar = null;
|
|
4068
|
+
this._createToolbar();
|
|
4069
|
+
}
|
|
3984
4070
|
this._applyOptions();
|
|
3985
4071
|
this.updatePreview();
|
|
3986
4072
|
}
|
|
@@ -4107,6 +4193,8 @@ ${blockSuffix}` : suffix;
|
|
|
4107
4193
|
this.statsBar.className = "overtype-stats";
|
|
4108
4194
|
this.container.appendChild(this.statsBar);
|
|
4109
4195
|
this._updateStats();
|
|
4196
|
+
} else if (show && this.statsBar) {
|
|
4197
|
+
this._updateStats();
|
|
4110
4198
|
} else if (!show && this.statsBar) {
|
|
4111
4199
|
this.statsBar.remove();
|
|
4112
4200
|
this.statsBar = null;
|
|
@@ -4175,6 +4263,44 @@ ${blockSuffix}` : suffix;
|
|
|
4175
4263
|
static init(target, options = {}) {
|
|
4176
4264
|
return new _OverType(target, options);
|
|
4177
4265
|
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Initialize editors with options from data-ot-* attributes
|
|
4268
|
+
* @param {string} selector - CSS selector for target elements
|
|
4269
|
+
* @param {Object} defaults - Default options (data attrs override these)
|
|
4270
|
+
* @returns {Array<OverType>} Array of OverType instances
|
|
4271
|
+
* @example
|
|
4272
|
+
* // HTML: <div class="editor" data-ot-toolbar="true" data-ot-theme="cave"></div>
|
|
4273
|
+
* OverType.initFromData('.editor', { fontSize: '14px' });
|
|
4274
|
+
*/
|
|
4275
|
+
static initFromData(selector, defaults = {}) {
|
|
4276
|
+
const elements = document.querySelectorAll(selector);
|
|
4277
|
+
return Array.from(elements).map((el) => {
|
|
4278
|
+
const options = { ...defaults };
|
|
4279
|
+
for (const attr of el.attributes) {
|
|
4280
|
+
if (attr.name.startsWith("data-ot-")) {
|
|
4281
|
+
const kebab = attr.name.slice(8);
|
|
4282
|
+
const key = kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
4283
|
+
options[key] = _OverType._parseDataValue(attr.value);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
return new _OverType(el, options);
|
|
4287
|
+
});
|
|
4288
|
+
}
|
|
4289
|
+
/**
|
|
4290
|
+
* Parse a data attribute value to the appropriate type
|
|
4291
|
+
* @private
|
|
4292
|
+
*/
|
|
4293
|
+
static _parseDataValue(value) {
|
|
4294
|
+
if (value === "true")
|
|
4295
|
+
return true;
|
|
4296
|
+
if (value === "false")
|
|
4297
|
+
return false;
|
|
4298
|
+
if (value === "null")
|
|
4299
|
+
return null;
|
|
4300
|
+
if (value !== "" && !isNaN(Number(value)))
|
|
4301
|
+
return Number(value);
|
|
4302
|
+
return value;
|
|
4303
|
+
}
|
|
4178
4304
|
/**
|
|
4179
4305
|
* Get instance from element
|
|
4180
4306
|
* @param {Element} element - DOM element
|
|
@@ -4275,6 +4401,32 @@ ${blockSuffix}` : suffix;
|
|
|
4275
4401
|
}
|
|
4276
4402
|
});
|
|
4277
4403
|
}
|
|
4404
|
+
/**
|
|
4405
|
+
* Set custom syntax processor for extending markdown parsing
|
|
4406
|
+
* @param {Function|null} processor - Function that takes (html) and returns modified HTML
|
|
4407
|
+
* @example
|
|
4408
|
+
* OverType.setCustomSyntax((html) => {
|
|
4409
|
+
* // Highlight footnote references [^1]
|
|
4410
|
+
* return html.replace(/\[\^(\w+)\]/g, '<span class="footnote-ref">$&</span>');
|
|
4411
|
+
* });
|
|
4412
|
+
*/
|
|
4413
|
+
static setCustomSyntax(processor) {
|
|
4414
|
+
MarkdownParser.setCustomSyntax(processor);
|
|
4415
|
+
document.querySelectorAll(".overtype-wrapper").forEach((wrapper) => {
|
|
4416
|
+
const instance = wrapper._instance;
|
|
4417
|
+
if (instance && instance.updatePreview) {
|
|
4418
|
+
instance.updatePreview();
|
|
4419
|
+
}
|
|
4420
|
+
});
|
|
4421
|
+
document.querySelectorAll("overtype-editor").forEach((webComponent) => {
|
|
4422
|
+
if (typeof webComponent.getEditor === "function") {
|
|
4423
|
+
const instance = webComponent.getEditor();
|
|
4424
|
+
if (instance && instance.updatePreview) {
|
|
4425
|
+
instance.updatePreview();
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
});
|
|
4429
|
+
}
|
|
4278
4430
|
/**
|
|
4279
4431
|
* Initialize global event listeners
|
|
4280
4432
|
*/
|