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