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/dist/overtype.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OverType v2.0.6
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,1968 +759,1926 @@ __publicField(MarkdownParser, "LIST_PATTERNS", {
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);
762
+ // src/shortcuts.js
763
+ var ShortcutsManager = class {
764
+ constructor(editor) {
765
+ this.editor = editor;
920
766
  }
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);
767
+ /**
768
+ * Handle keydown events - called by OverType
769
+ * @param {KeyboardEvent} event - The keyboard event
770
+ * @returns {boolean} Whether the event was handled
771
+ */
772
+ handleKeydown(event) {
773
+ const isMac = navigator.platform.toLowerCase().includes("mac");
774
+ const modKey = isMac ? event.metaKey : event.ctrlKey;
775
+ if (!modKey)
776
+ return false;
777
+ let actionId = null;
778
+ switch (event.key.toLowerCase()) {
779
+ case "b":
780
+ if (!event.shiftKey)
781
+ actionId = "toggleBold";
782
+ break;
783
+ case "i":
784
+ if (!event.shiftKey)
785
+ actionId = "toggleItalic";
786
+ break;
787
+ case "k":
788
+ if (!event.shiftKey)
789
+ actionId = "insertLink";
790
+ break;
791
+ case "7":
792
+ if (event.shiftKey)
793
+ actionId = "toggleNumberedList";
794
+ break;
795
+ case "8":
796
+ if (event.shiftKey)
797
+ actionId = "toggleBulletList";
798
+ break;
927
799
  }
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
- }
800
+ if (actionId) {
801
+ event.preventDefault();
802
+ this.editor.performAction(actionId, event);
803
+ return true;
934
804
  }
805
+ return false;
935
806
  }
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
- }
807
+ /**
808
+ * Cleanup
809
+ */
810
+ destroy() {
956
811
  }
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);
812
+ };
813
+
814
+ // src/themes.js
815
+ var solar = {
816
+ name: "solar",
817
+ colors: {
818
+ bgPrimary: "#faf0ca",
819
+ // Lemon Chiffon - main background
820
+ bgSecondary: "#ffffff",
821
+ // White - editor background
822
+ text: "#0d3b66",
823
+ // Yale Blue - main text
824
+ textPrimary: "#0d3b66",
825
+ // Yale Blue - primary text (same as text)
826
+ textSecondary: "#5a7a9b",
827
+ // Muted blue - secondary text
828
+ h1: "#f95738",
829
+ // Tomato - h1 headers
830
+ h2: "#ee964b",
831
+ // Sandy Brown - h2 headers
832
+ h3: "#3d8a51",
833
+ // Forest green - h3 headers
834
+ strong: "#ee964b",
835
+ // Sandy Brown - bold text
836
+ em: "#f95738",
837
+ // Tomato - italic text
838
+ del: "#ee964b",
839
+ // Sandy Brown - deleted text (same as strong)
840
+ link: "#0d3b66",
841
+ // Yale Blue - links
842
+ code: "#0d3b66",
843
+ // Yale Blue - inline code
844
+ codeBg: "rgba(244, 211, 94, 0.4)",
845
+ // Naples Yellow with transparency
846
+ blockquote: "#5a7a9b",
847
+ // Muted blue - blockquotes
848
+ hr: "#5a7a9b",
849
+ // Muted blue - horizontal rules
850
+ syntaxMarker: "rgba(13, 59, 102, 0.52)",
851
+ // Yale Blue with transparency
852
+ syntax: "#999999",
853
+ // Gray - syntax highlighting fallback
854
+ cursor: "#f95738",
855
+ // Tomato - cursor
856
+ selection: "rgba(244, 211, 94, 0.4)",
857
+ // Naples Yellow with transparency
858
+ listMarker: "#ee964b",
859
+ // Sandy Brown - list markers
860
+ rawLine: "#5a7a9b",
861
+ // Muted blue - raw line indicators
862
+ border: "#e0e0e0",
863
+ // Light gray - borders
864
+ hoverBg: "#f0f0f0",
865
+ // Very light gray - hover backgrounds
866
+ primary: "#0d3b66",
867
+ // Yale Blue - primary accent
868
+ // Toolbar colors
869
+ toolbarBg: "#ffffff",
870
+ // White - toolbar background
871
+ toolbarIcon: "#0d3b66",
872
+ // Yale Blue - icon color
873
+ toolbarHover: "#f5f5f5",
874
+ // Light gray - hover background
875
+ toolbarActive: "#faf0ca"
876
+ // Lemon Chiffon - active button background
963
877
  }
964
- if (debugMode2) {
965
- console.log("Final value length:", textarea.value.length);
966
- console.groupEnd();
878
+ };
879
+ var cave = {
880
+ name: "cave",
881
+ colors: {
882
+ bgPrimary: "#141E26",
883
+ // Deep ocean - main background
884
+ bgSecondary: "#1D2D3E",
885
+ // Darker charcoal - editor background
886
+ text: "#c5dde8",
887
+ // Light blue-gray - main text
888
+ textPrimary: "#c5dde8",
889
+ // Light blue-gray - primary text (same as text)
890
+ textSecondary: "#9fcfec",
891
+ // Brighter blue - secondary text
892
+ h1: "#d4a5ff",
893
+ // Rich lavender - h1 headers
894
+ h2: "#f6ae2d",
895
+ // Hunyadi Yellow - h2 headers
896
+ h3: "#9fcfec",
897
+ // Brighter blue - h3 headers
898
+ strong: "#f6ae2d",
899
+ // Hunyadi Yellow - bold text
900
+ em: "#9fcfec",
901
+ // Brighter blue - italic text
902
+ del: "#f6ae2d",
903
+ // Hunyadi Yellow - deleted text (same as strong)
904
+ link: "#9fcfec",
905
+ // Brighter blue - links
906
+ code: "#c5dde8",
907
+ // Light blue-gray - inline code
908
+ codeBg: "#1a232b",
909
+ // Very dark blue - code background
910
+ blockquote: "#9fcfec",
911
+ // Brighter blue - same as italic
912
+ hr: "#c5dde8",
913
+ // Light blue-gray - horizontal rules
914
+ syntaxMarker: "rgba(159, 207, 236, 0.73)",
915
+ // Brighter blue semi-transparent
916
+ syntax: "#7a8c98",
917
+ // Muted gray-blue - syntax highlighting fallback
918
+ cursor: "#f26419",
919
+ // Orange Pantone - cursor
920
+ selection: "rgba(51, 101, 138, 0.4)",
921
+ // Lapis Lazuli with transparency
922
+ listMarker: "#f6ae2d",
923
+ // Hunyadi Yellow - list markers
924
+ rawLine: "#9fcfec",
925
+ // Brighter blue - raw line indicators
926
+ border: "#2a3f52",
927
+ // Dark blue-gray - borders
928
+ hoverBg: "#243546",
929
+ // Slightly lighter charcoal - hover backgrounds
930
+ primary: "#9fcfec",
931
+ // Brighter blue - primary accent
932
+ // Toolbar colors for dark theme
933
+ toolbarBg: "#1D2D3E",
934
+ // Darker charcoal - toolbar background
935
+ toolbarIcon: "#c5dde8",
936
+ // Light blue-gray - icon color
937
+ toolbarHover: "#243546",
938
+ // Slightly lighter charcoal - hover background
939
+ toolbarActive: "#2a3f52"
940
+ // Even lighter - active button background
967
941
  }
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--;
942
+ };
943
+ var themes = {
944
+ solar,
945
+ cave,
946
+ // Aliases for backward compatibility
947
+ light: solar,
948
+ dark: cave
949
+ };
950
+ function getTheme(theme) {
951
+ if (typeof theme === "string") {
952
+ const themeObj = themes[theme] || themes.solar;
953
+ return { ...themeObj, name: theme };
976
954
  }
977
- return index;
955
+ return theme;
978
956
  }
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++;
957
+ function themeToCSSVars(colors) {
958
+ const vars = [];
959
+ for (const [key, value] of Object.entries(colors)) {
960
+ const varName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
961
+ vars.push(`--${varName}: ${value};`);
984
962
  }
985
- return index;
963
+ return vars.join("\n");
986
964
  }
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;
965
+ function mergeTheme(baseTheme, customColors = {}) {
966
+ return {
967
+ ...baseTheme,
968
+ colors: {
969
+ ...baseTheme.colors,
970
+ ...customColors
994
971
  }
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;
972
+ };
973
+ }
974
+
975
+ // src/styles.js
976
+ function generateStyles(options = {}) {
977
+ const {
978
+ fontSize = "14px",
979
+ lineHeight = 1.6,
980
+ /* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
981
+ fontFamily = '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
982
+ padding = "20px",
983
+ theme = null,
984
+ mobile = {}
985
+ } = options;
986
+ const mobileStyles = Object.keys(mobile).length > 0 ? `
987
+ @media (max-width: 640px) {
988
+ .overtype-wrapper .overtype-input,
989
+ .overtype-wrapper .overtype-preview {
990
+ ${Object.entries(mobile).map(([prop, val]) => {
991
+ const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
992
+ return `${cssProp}: ${val} !important;`;
993
+ }).join("\n ")}
1000
994
  }
1001
995
  }
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;
996
+ ` : "";
997
+ const themeVars = theme && theme.colors ? themeToCSSVars(theme.colors) : "";
998
+ return `
999
+ /* OverType Editor Styles */
1000
+
1001
+ /* Middle-ground CSS Reset - Prevent parent styles from leaking in */
1002
+ .overtype-container * {
1003
+ /* Box model - these commonly leak */
1004
+ margin: 0 !important;
1005
+ padding: 0 !important;
1006
+ border: 0 !important;
1007
+
1008
+ /* Layout - these can break our layout */
1009
+ /* Don't reset position - it breaks dropdowns */
1010
+ float: none !important;
1011
+ clear: none !important;
1012
+
1013
+ /* Typography - only reset decorative aspects */
1014
+ text-decoration: none !important;
1015
+ text-transform: none !important;
1016
+ letter-spacing: normal !important;
1017
+
1018
+ /* Visual effects that can interfere */
1019
+ box-shadow: none !important;
1020
+ text-shadow: none !important;
1021
+
1022
+ /* Ensure box-sizing is consistent */
1023
+ box-sizing: border-box !important;
1024
+
1025
+ /* Keep inheritance for these */
1026
+ /* font-family, color, line-height, font-size - inherit */
1027
+ }
1028
+
1029
+ /* Container base styles after reset */
1030
+ .overtype-container {
1031
+ display: flex !important;
1032
+ flex-direction: column !important;
1033
+ width: 100% !important;
1034
+ height: 100% !important;
1035
+ position: relative !important; /* Override reset - needed for absolute children */
1036
+ overflow: visible !important; /* Allow dropdown to overflow container */
1037
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1038
+ text-align: left !important;
1039
+ ${themeVars ? `
1040
+ /* Theme Variables */
1041
+ ${themeVars}` : ""}
1042
+ }
1043
+
1044
+ /* Force left alignment for all elements in the editor */
1045
+ .overtype-container .overtype-wrapper * {
1046
+ text-align: left !important;
1047
+ }
1048
+
1049
+ /* Auto-resize mode styles */
1050
+ .overtype-container.overtype-auto-resize {
1051
+ height: auto !important;
1052
+ }
1053
+
1054
+ .overtype-container.overtype-auto-resize .overtype-wrapper {
1055
+ flex: 0 0 auto !important; /* Don't grow/shrink, use explicit height */
1056
+ height: auto !important;
1057
+ min-height: 60px !important;
1058
+ overflow: visible !important;
1059
+ }
1060
+
1061
+ .overtype-wrapper {
1062
+ position: relative !important; /* Override reset - needed for absolute children */
1063
+ width: 100% !important;
1064
+ flex: 1 1 0 !important; /* Grow to fill remaining space, with flex-basis: 0 */
1065
+ min-height: 60px !important; /* Minimum usable height */
1066
+ overflow: hidden !important;
1067
+ background: var(--bg-secondary, #ffffff) !important;
1068
+ z-index: 1; /* Below toolbar and dropdown */
1069
+ }
1070
+
1071
+ /* Critical alignment styles - must be identical for both layers */
1072
+ .overtype-wrapper .overtype-input,
1073
+ .overtype-wrapper .overtype-preview {
1074
+ /* Positioning - must be identical */
1075
+ position: absolute !important; /* Override reset - required for overlay */
1076
+ top: 0 !important;
1077
+ left: 0 !important;
1078
+ width: 100% !important;
1079
+ height: 100% !important;
1080
+
1081
+ /* Font properties - any difference breaks alignment */
1082
+ font-family: ${fontFamily} !important;
1083
+ font-variant-ligatures: none !important; /* keep metrics stable for code */
1084
+ font-size: var(--instance-font-size, ${fontSize}) !important;
1085
+ line-height: var(--instance-line-height, ${lineHeight}) !important;
1086
+ font-weight: normal !important;
1087
+ font-style: normal !important;
1088
+ font-variant: normal !important;
1089
+ font-stretch: normal !important;
1090
+ font-kerning: none !important;
1091
+ font-feature-settings: normal !important;
1092
+
1093
+ /* Box model - must match exactly */
1094
+ padding: var(--instance-padding, ${padding}) !important;
1095
+ margin: 0 !important;
1096
+ border: none !important;
1097
+ outline: none !important;
1098
+ box-sizing: border-box !important;
1099
+
1100
+ /* Text layout - critical for character positioning */
1101
+ white-space: pre-wrap !important;
1102
+ word-wrap: break-word !important;
1103
+ word-break: normal !important;
1104
+ overflow-wrap: break-word !important;
1105
+ tab-size: 2 !important;
1106
+ -moz-tab-size: 2 !important;
1107
+ text-align: left !important;
1108
+ text-indent: 0 !important;
1109
+ letter-spacing: normal !important;
1110
+ word-spacing: normal !important;
1111
+
1112
+ /* Text rendering */
1113
+ text-transform: none !important;
1114
+ text-rendering: auto !important;
1115
+ -webkit-font-smoothing: auto !important;
1116
+ -webkit-text-size-adjust: 100% !important;
1117
+
1118
+ /* Direction and writing */
1119
+ direction: ltr !important;
1120
+ writing-mode: horizontal-tb !important;
1121
+ unicode-bidi: normal !important;
1122
+ text-orientation: mixed !important;
1123
+
1124
+ /* Visual effects that could shift perception */
1125
+ text-shadow: none !important;
1126
+ filter: none !important;
1127
+ transform: none !important;
1128
+ zoom: 1 !important;
1129
+
1130
+ /* Vertical alignment */
1131
+ vertical-align: baseline !important;
1132
+
1133
+ /* Size constraints */
1134
+ min-width: 0 !important;
1135
+ min-height: 0 !important;
1136
+ max-width: none !important;
1137
+ max-height: none !important;
1138
+
1139
+ /* Overflow */
1140
+ overflow-y: auto !important;
1141
+ overflow-x: auto !important;
1142
+ /* overscroll-behavior removed to allow scroll-through to parent */
1143
+ scrollbar-width: auto !important;
1144
+ scrollbar-gutter: auto !important;
1145
+
1146
+ /* Animation/transition - disabled to prevent movement */
1147
+ animation: none !important;
1148
+ transition: none !important;
1017
1149
  }
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++;
1150
+
1151
+ /* Input layer styles */
1152
+ .overtype-wrapper .overtype-input {
1153
+ /* Layer positioning */
1154
+ z-index: 1 !important;
1155
+
1156
+ /* Text visibility */
1157
+ color: transparent !important;
1158
+ caret-color: var(--cursor, #f95738) !important;
1159
+ background-color: transparent !important;
1160
+
1161
+ /* Textarea-specific */
1162
+ resize: none !important;
1163
+ appearance: none !important;
1164
+ -webkit-appearance: none !important;
1165
+ -moz-appearance: none !important;
1166
+
1167
+ /* Prevent mobile zoom on focus */
1168
+ touch-action: manipulation !important;
1169
+
1170
+ /* Disable autofill and spellcheck */
1171
+ autocomplete: off !important;
1172
+ autocorrect: off !important;
1173
+ autocapitalize: off !important;
1174
+ spellcheck: false !important;
1051
1175
  }
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
- }
1176
+
1177
+ .overtype-wrapper .overtype-input::selection {
1178
+ background-color: var(--selection, rgba(244, 211, 94, 0.4));
1083
1179
  }
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}`;
1180
+
1181
+ /* Preview layer styles */
1182
+ .overtype-wrapper .overtype-preview {
1183
+ /* Layer positioning */
1184
+ z-index: 0 !important;
1185
+ pointer-events: none !important;
1186
+ color: var(--text, #0d3b66) !important;
1187
+ background-color: transparent !important;
1188
+
1189
+ /* Prevent text selection */
1190
+ user-select: none !important;
1191
+ -webkit-user-select: none !important;
1192
+ -moz-user-select: none !important;
1193
+ -ms-user-select: none !important;
1102
1194
  }
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;
1195
+
1196
+ /* Defensive styles for preview child divs */
1197
+ .overtype-wrapper .overtype-preview div {
1198
+ /* Reset any inherited styles */
1199
+ margin: 0 !important;
1200
+ padding: 0 !important;
1201
+ border: none !important;
1202
+ text-align: left !important;
1203
+ text-indent: 0 !important;
1204
+ display: block !important;
1205
+ position: static !important;
1206
+ transform: none !important;
1207
+ min-height: 0 !important;
1208
+ max-height: none !important;
1209
+ line-height: inherit !important;
1210
+ font-size: inherit !important;
1211
+ font-family: inherit !important;
1124
1212
  }
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;
1213
+
1214
+ /* Markdown element styling - NO SIZE CHANGES */
1215
+ .overtype-wrapper .overtype-preview .header {
1216
+ font-weight: bold !important;
1137
1217
  }
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;
1218
+
1219
+ /* Header colors */
1220
+ .overtype-wrapper .overtype-preview .h1 {
1221
+ color: var(--h1, #f95738) !important;
1174
1222
  }
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;
1223
+ .overtype-wrapper .overtype-preview .h2 {
1224
+ color: var(--h2, #ee964b) !important;
1249
1225
  }
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;
1226
+ .overtype-wrapper .overtype-preview .h3 {
1227
+ color: var(--h3, #3d8a51) !important;
1264
1228
  }
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
- }
1229
+
1230
+ /* Semantic headers - flatten in edit mode */
1231
+ .overtype-wrapper .overtype-preview h1,
1232
+ .overtype-wrapper .overtype-preview h2,
1233
+ .overtype-wrapper .overtype-preview h3 {
1234
+ font-size: inherit !important;
1235
+ font-weight: bold !important;
1236
+ margin: 0 !important;
1237
+ padding: 0 !important;
1238
+ display: inline !important;
1239
+ line-height: inherit !important;
1331
1240
  }
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;
1241
+
1242
+ /* Header colors for semantic headers */
1243
+ .overtype-wrapper .overtype-preview h1 {
1244
+ color: var(--h1, #f95738) !important;
1347
1245
  }
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");
1246
+ .overtype-wrapper .overtype-preview h2 {
1247
+ color: var(--h2, #ee964b) !important;
1355
1248
  }
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");
1249
+ .overtype-wrapper .overtype-preview h3 {
1250
+ color: var(--h3, #3d8a51) !important;
1379
1251
  }
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");
1252
+
1253
+ /* Lists - remove styling in edit mode */
1254
+ .overtype-wrapper .overtype-preview ul,
1255
+ .overtype-wrapper .overtype-preview ol {
1256
+ list-style: none !important;
1257
+ margin: 0 !important;
1258
+ padding: 0 !important;
1259
+ display: block !important; /* Lists need to be block for line breaks */
1388
1260
  }
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");
1261
+
1262
+ .overtype-wrapper .overtype-preview li {
1263
+ display: block !important; /* Each item on its own line */
1264
+ margin: 0 !important;
1265
+ padding: 0 !important;
1266
+ /* Don't set list-style here - let ul/ol control it */
1395
1267
  }
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
- }
1268
+
1269
+ /* Bold text */
1270
+ .overtype-wrapper .overtype-preview strong {
1271
+ color: var(--strong, #ee964b) !important;
1272
+ font-weight: bold !important;
1407
1273
  }
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
- }
1274
+
1275
+ /* Italic text */
1276
+ .overtype-wrapper .overtype-preview em {
1277
+ color: var(--em, #f95738) !important;
1278
+ text-decoration-color: var(--em, #f95738) !important;
1279
+ text-decoration-thickness: 1px !important;
1280
+ font-style: italic !important;
1281
+ }
1282
+
1283
+ /* Strikethrough text */
1284
+ .overtype-wrapper .overtype-preview del {
1285
+ color: var(--del, #ee964b) !important;
1286
+ text-decoration: line-through !important;
1287
+ text-decoration-color: var(--del, #ee964b) !important;
1288
+ text-decoration-thickness: 1px !important;
1586
1289
  }
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
1290
 
1605
- // src/shortcuts.js
1606
- var ShortcutsManager = class {
1607
- constructor(editor) {
1608
- this.editor = editor;
1609
- this.textarea = editor.textarea;
1610
- }
1611
- /**
1612
- * Handle keydown events - called by OverType
1613
- * @param {KeyboardEvent} event - The keyboard event
1614
- * @returns {boolean} Whether the event was handled
1615
- */
1616
- handleKeydown(event) {
1617
- const isMac = navigator.platform.toLowerCase().includes("mac");
1618
- const modKey = isMac ? event.metaKey : event.ctrlKey;
1619
- if (!modKey)
1620
- return false;
1621
- let action = null;
1622
- switch (event.key.toLowerCase()) {
1623
- case "b":
1624
- if (!event.shiftKey) {
1625
- action = "toggleBold";
1626
- }
1627
- break;
1628
- case "i":
1629
- if (!event.shiftKey) {
1630
- action = "toggleItalic";
1631
- }
1632
- break;
1633
- case "k":
1634
- if (!event.shiftKey) {
1635
- action = "insertLink";
1636
- }
1637
- break;
1638
- case "7":
1639
- if (event.shiftKey) {
1640
- action = "toggleNumberedList";
1641
- }
1642
- break;
1643
- case "8":
1644
- if (event.shiftKey) {
1645
- action = "toggleBulletList";
1646
- }
1647
- break;
1291
+ /* Inline code */
1292
+ .overtype-wrapper .overtype-preview code {
1293
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1294
+ color: var(--code, #0d3b66) !important;
1295
+ padding: 0 !important;
1296
+ border-radius: 2px !important;
1297
+ font-family: inherit !important;
1298
+ font-size: inherit !important;
1299
+ line-height: inherit !important;
1300
+ font-weight: normal !important;
1648
1301
  }
1649
- if (action) {
1650
- event.preventDefault();
1651
- if (this.editor.toolbar) {
1652
- this.editor.toolbar.handleAction(action);
1653
- } else {
1654
- this.handleAction(action);
1655
- }
1656
- return true;
1302
+
1303
+ /* Code blocks - consolidated pre blocks */
1304
+ .overtype-wrapper .overtype-preview pre {
1305
+ padding: 0 !important;
1306
+ margin: 0 !important;
1307
+ border-radius: 4px !important;
1308
+ overflow-x: auto !important;
1657
1309
  }
1658
- return false;
1659
- }
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);
1310
+
1311
+ /* Code block styling in normal mode - yellow background */
1312
+ .overtype-wrapper .overtype-preview pre.code-block {
1313
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1314
+ white-space: break-spaces !important; /* Prevent horizontal scrollbar that breaks alignment */
1690
1315
  }
1691
- }
1692
- /**
1693
- * Cleanup
1694
- */
1695
- destroy() {
1696
- }
1697
- };
1698
1316
 
1699
- // src/themes.js
1700
- var solar = {
1701
- name: "solar",
1702
- colors: {
1703
- bgPrimary: "#faf0ca",
1704
- // Lemon Chiffon - main background
1705
- bgSecondary: "#ffffff",
1706
- // White - editor background
1707
- text: "#0d3b66",
1708
- // Yale Blue - main text
1709
- textPrimary: "#0d3b66",
1710
- // Yale Blue - primary text (same as text)
1711
- textSecondary: "#5a7a9b",
1712
- // Muted blue - secondary text
1713
- h1: "#f95738",
1714
- // Tomato - h1 headers
1715
- h2: "#ee964b",
1716
- // Sandy Brown - h2 headers
1717
- h3: "#3d8a51",
1718
- // Forest green - h3 headers
1719
- strong: "#ee964b",
1720
- // Sandy Brown - bold text
1721
- em: "#f95738",
1722
- // Tomato - italic text
1723
- del: "#ee964b",
1724
- // Sandy Brown - deleted text (same as strong)
1725
- link: "#0d3b66",
1726
- // Yale Blue - links
1727
- code: "#0d3b66",
1728
- // Yale Blue - inline code
1729
- codeBg: "rgba(244, 211, 94, 0.4)",
1730
- // Naples Yellow with transparency
1731
- blockquote: "#5a7a9b",
1732
- // Muted blue - blockquotes
1733
- hr: "#5a7a9b",
1734
- // Muted blue - horizontal rules
1735
- syntaxMarker: "rgba(13, 59, 102, 0.52)",
1736
- // Yale Blue with transparency
1737
- syntax: "#999999",
1738
- // Gray - syntax highlighting fallback
1739
- cursor: "#f95738",
1740
- // Tomato - cursor
1741
- selection: "rgba(244, 211, 94, 0.4)",
1742
- // Naples Yellow with transparency
1743
- listMarker: "#ee964b",
1744
- // Sandy Brown - list markers
1745
- rawLine: "#5a7a9b",
1746
- // Muted blue - raw line indicators
1747
- border: "#e0e0e0",
1748
- // Light gray - borders
1749
- hoverBg: "#f0f0f0",
1750
- // Very light gray - hover backgrounds
1751
- primary: "#0d3b66",
1752
- // Yale Blue - primary accent
1753
- // Toolbar colors
1754
- toolbarBg: "#ffffff",
1755
- // White - toolbar background
1756
- toolbarIcon: "#0d3b66",
1757
- // Yale Blue - icon color
1758
- toolbarHover: "#f5f5f5",
1759
- // Light gray - hover background
1760
- toolbarActive: "#faf0ca"
1761
- // Lemon Chiffon - active button background
1762
- }
1763
- };
1764
- var cave = {
1765
- name: "cave",
1766
- colors: {
1767
- bgPrimary: "#141E26",
1768
- // Deep ocean - main background
1769
- bgSecondary: "#1D2D3E",
1770
- // Darker charcoal - editor background
1771
- text: "#c5dde8",
1772
- // Light blue-gray - main text
1773
- textPrimary: "#c5dde8",
1774
- // Light blue-gray - primary text (same as text)
1775
- textSecondary: "#9fcfec",
1776
- // Brighter blue - secondary text
1777
- h1: "#d4a5ff",
1778
- // Rich lavender - h1 headers
1779
- h2: "#f6ae2d",
1780
- // Hunyadi Yellow - h2 headers
1781
- h3: "#9fcfec",
1782
- // Brighter blue - h3 headers
1783
- strong: "#f6ae2d",
1784
- // Hunyadi Yellow - bold text
1785
- em: "#9fcfec",
1786
- // Brighter blue - italic text
1787
- del: "#f6ae2d",
1788
- // Hunyadi Yellow - deleted text (same as strong)
1789
- link: "#9fcfec",
1790
- // Brighter blue - links
1791
- code: "#c5dde8",
1792
- // Light blue-gray - inline code
1793
- codeBg: "#1a232b",
1794
- // Very dark blue - code background
1795
- blockquote: "#9fcfec",
1796
- // Brighter blue - same as italic
1797
- hr: "#c5dde8",
1798
- // Light blue-gray - horizontal rules
1799
- syntaxMarker: "rgba(159, 207, 236, 0.73)",
1800
- // Brighter blue semi-transparent
1801
- syntax: "#7a8c98",
1802
- // Muted gray-blue - syntax highlighting fallback
1803
- cursor: "#f26419",
1804
- // Orange Pantone - cursor
1805
- selection: "rgba(51, 101, 138, 0.4)",
1806
- // Lapis Lazuli with transparency
1807
- listMarker: "#f6ae2d",
1808
- // Hunyadi Yellow - list markers
1809
- rawLine: "#9fcfec",
1810
- // Brighter blue - raw line indicators
1811
- border: "#2a3f52",
1812
- // Dark blue-gray - borders
1813
- hoverBg: "#243546",
1814
- // Slightly lighter charcoal - hover backgrounds
1815
- primary: "#9fcfec",
1816
- // Brighter blue - primary accent
1817
- // Toolbar colors for dark theme
1818
- toolbarBg: "#1D2D3E",
1819
- // Darker charcoal - toolbar background
1820
- toolbarIcon: "#c5dde8",
1821
- // Light blue-gray - icon color
1822
- toolbarHover: "#243546",
1823
- // Slightly lighter charcoal - hover background
1824
- toolbarActive: "#2a3f52"
1825
- // Even lighter - active button background
1826
- }
1827
- };
1828
- var themes = {
1829
- solar,
1830
- cave,
1831
- // Aliases for backward compatibility
1832
- light: solar,
1833
- dark: cave
1834
- };
1835
- function getTheme(theme) {
1836
- if (typeof theme === "string") {
1837
- const themeObj = themes[theme] || themes.solar;
1838
- return { ...themeObj, name: theme };
1839
- }
1840
- return theme;
1841
- }
1842
- function themeToCSSVars(colors) {
1843
- const vars = [];
1844
- for (const [key, value] of Object.entries(colors)) {
1845
- const varName = key.replace(/([A-Z])/g, "-$1").toLowerCase();
1846
- vars.push(`--${varName}: ${value};`);
1847
- }
1848
- return vars.join("\n");
1849
- }
1850
- function mergeTheme(baseTheme, customColors = {}) {
1851
- return {
1852
- ...baseTheme,
1853
- colors: {
1854
- ...baseTheme.colors,
1855
- ...customColors
1317
+ /* Code inside pre blocks - remove background */
1318
+ .overtype-wrapper .overtype-preview pre code {
1319
+ background: transparent !important;
1320
+ color: var(--code, #0d3b66) !important;
1321
+ font-family: ${fontFamily} !important; /* Match textarea font exactly for alignment */
1856
1322
  }
1857
- };
1858
- }
1859
1323
 
1860
- // src/styles.js
1861
- function generateStyles(options = {}) {
1862
- const {
1863
- fontSize = "14px",
1864
- lineHeight = 1.6,
1865
- /* System-first, guaranteed monospaced; avoids Android 'ui-monospace' pitfalls */
1866
- fontFamily = '"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',
1867
- padding = "20px",
1868
- theme = null,
1869
- mobile = {}
1870
- } = options;
1871
- const mobileStyles = Object.keys(mobile).length > 0 ? `
1872
- @media (max-width: 640px) {
1873
- .overtype-wrapper .overtype-input,
1874
- .overtype-wrapper .overtype-preview {
1875
- ${Object.entries(mobile).map(([prop, val]) => {
1876
- const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
1877
- return `${cssProp}: ${val} !important;`;
1878
- }).join("\n ")}
1879
- }
1324
+ /* Blockquotes */
1325
+ .overtype-wrapper .overtype-preview .blockquote {
1326
+ color: var(--blockquote, #5a7a9b) !important;
1327
+ padding: 0 !important;
1328
+ margin: 0 !important;
1329
+ border: none !important;
1880
1330
  }
1881
- ` : "";
1882
- const themeVars = theme && theme.colors ? themeToCSSVars(theme.colors) : "";
1883
- return `
1884
- /* OverType Editor Styles */
1885
-
1886
- /* Middle-ground CSS Reset - Prevent parent styles from leaking in */
1887
- .overtype-container * {
1888
- /* Box model - these commonly leak */
1331
+
1332
+ /* Links */
1333
+ .overtype-wrapper .overtype-preview a {
1334
+ color: var(--link, #0d3b66) !important;
1335
+ text-decoration: underline !important;
1336
+ font-weight: normal !important;
1337
+ }
1338
+
1339
+ .overtype-wrapper .overtype-preview a:hover {
1340
+ text-decoration: underline !important;
1341
+ color: var(--link, #0d3b66) !important;
1342
+ }
1343
+
1344
+ /* Lists - no list styling */
1345
+ .overtype-wrapper .overtype-preview ul,
1346
+ .overtype-wrapper .overtype-preview ol {
1347
+ list-style: none !important;
1889
1348
  margin: 0 !important;
1890
1349
  padding: 0 !important;
1891
- border: 0 !important;
1892
-
1893
- /* Layout - these can break our layout */
1894
- /* Don't reset position - it breaks dropdowns */
1895
- float: none !important;
1896
- clear: none !important;
1897
-
1898
- /* Typography - only reset decorative aspects */
1899
- text-decoration: none !important;
1900
- text-transform: none !important;
1901
- letter-spacing: normal !important;
1902
-
1903
- /* Visual effects that can interfere */
1904
- box-shadow: none !important;
1905
- text-shadow: none !important;
1906
-
1907
- /* Ensure box-sizing is consistent */
1908
- box-sizing: border-box !important;
1909
-
1910
- /* Keep inheritance for these */
1911
- /* font-family, color, line-height, font-size - inherit */
1912
1350
  }
1913
-
1914
- /* Container base styles after reset */
1915
- .overtype-container {
1916
- display: flex !important;
1917
- flex-direction: column !important;
1918
- width: 100% !important;
1919
- height: 100% !important;
1920
- position: relative !important; /* Override reset - needed for absolute children */
1921
- overflow: visible !important; /* Allow dropdown to overflow container */
1922
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
1923
- text-align: left !important;
1924
- ${themeVars ? `
1925
- /* Theme Variables */
1926
- ${themeVars}` : ""}
1351
+
1352
+
1353
+ /* Horizontal rules */
1354
+ .overtype-wrapper .overtype-preview hr {
1355
+ border: none !important;
1356
+ color: var(--hr, #5a7a9b) !important;
1357
+ margin: 0 !important;
1358
+ padding: 0 !important;
1359
+ }
1360
+
1361
+ .overtype-wrapper .overtype-preview .hr-marker {
1362
+ color: var(--hr, #5a7a9b) !important;
1363
+ opacity: 0.6 !important;
1364
+ }
1365
+
1366
+ /* Code fence markers - with background when not in code block */
1367
+ .overtype-wrapper .overtype-preview .code-fence {
1368
+ color: var(--code, #0d3b66) !important;
1369
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1927
1370
  }
1928
1371
 
1929
- /* Force left alignment for all elements in the editor */
1930
- .overtype-container .overtype-wrapper * {
1931
- text-align: left !important;
1372
+ /* Code block lines - background for entire code block */
1373
+ .overtype-wrapper .overtype-preview .code-block-line {
1374
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
1932
1375
  }
1933
1376
 
1934
- /* Auto-resize mode styles */
1935
- .overtype-container.overtype-auto-resize {
1936
- height: auto !important;
1377
+ /* Remove background from code fence when inside code block line */
1378
+ .overtype-wrapper .overtype-preview .code-block-line .code-fence {
1379
+ background: transparent !important;
1937
1380
  }
1938
1381
 
1939
- .overtype-container.overtype-auto-resize .overtype-wrapper {
1940
- flex: 0 0 auto !important; /* Don't grow/shrink, use explicit height */
1941
- height: auto !important;
1942
- min-height: 60px !important;
1943
- overflow: visible !important;
1382
+ /* Raw markdown line */
1383
+ .overtype-wrapper .overtype-preview .raw-line {
1384
+ color: var(--raw-line, #5a7a9b) !important;
1385
+ font-style: normal !important;
1386
+ font-weight: normal !important;
1944
1387
  }
1945
-
1946
- .overtype-wrapper {
1947
- position: relative !important; /* Override reset - needed for absolute children */
1948
- width: 100% !important;
1949
- flex: 1 1 0 !important; /* Grow to fill remaining space, with flex-basis: 0 */
1950
- min-height: 60px !important; /* Minimum usable height */
1951
- overflow: hidden !important;
1952
- background: var(--bg-secondary, #ffffff) !important;
1953
- z-index: 1; /* Below toolbar and dropdown */
1388
+
1389
+ /* Syntax markers */
1390
+ .overtype-wrapper .overtype-preview .syntax-marker {
1391
+ color: var(--syntax-marker, rgba(13, 59, 102, 0.52)) !important;
1392
+ opacity: 0.7 !important;
1954
1393
  }
1955
1394
 
1956
- /* Critical alignment styles - must be identical for both layers */
1957
- .overtype-wrapper .overtype-input,
1958
- .overtype-wrapper .overtype-preview {
1959
- /* Positioning - must be identical */
1960
- position: absolute !important; /* Override reset - required for overlay */
1961
- top: 0 !important;
1962
- left: 0 !important;
1963
- width: 100% !important;
1964
- height: 100% !important;
1965
-
1966
- /* Font properties - any difference breaks alignment */
1967
- font-family: ${fontFamily} !important;
1968
- font-variant-ligatures: none !important; /* keep metrics stable for code */
1969
- font-size: var(--instance-font-size, ${fontSize}) !important;
1970
- line-height: var(--instance-line-height, ${lineHeight}) !important;
1971
- font-weight: normal !important;
1972
- font-style: normal !important;
1973
- font-variant: normal !important;
1974
- font-stretch: normal !important;
1975
- font-kerning: none !important;
1976
- font-feature-settings: normal !important;
1977
-
1978
- /* Box model - must match exactly */
1979
- padding: var(--instance-padding, ${padding}) !important;
1980
- margin: 0 !important;
1981
- border: none !important;
1982
- outline: none !important;
1983
- box-sizing: border-box !important;
1984
-
1985
- /* Text layout - critical for character positioning */
1986
- white-space: pre-wrap !important;
1987
- word-wrap: break-word !important;
1988
- word-break: normal !important;
1989
- overflow-wrap: break-word !important;
1990
- tab-size: 2 !important;
1991
- -moz-tab-size: 2 !important;
1992
- text-align: left !important;
1993
- text-indent: 0 !important;
1994
- letter-spacing: normal !important;
1995
- word-spacing: normal !important;
1996
-
1997
- /* Text rendering */
1998
- text-transform: none !important;
1999
- text-rendering: auto !important;
2000
- -webkit-font-smoothing: auto !important;
2001
- -webkit-text-size-adjust: 100% !important;
2002
-
2003
- /* Direction and writing */
2004
- direction: ltr !important;
2005
- writing-mode: horizontal-tb !important;
2006
- unicode-bidi: normal !important;
2007
- text-orientation: mixed !important;
2008
-
2009
- /* Visual effects that could shift perception */
2010
- text-shadow: none !important;
2011
- filter: none !important;
2012
- transform: none !important;
2013
- zoom: 1 !important;
2014
-
2015
- /* Vertical alignment */
2016
- vertical-align: baseline !important;
2017
-
2018
- /* Size constraints */
2019
- min-width: 0 !important;
2020
- min-height: 0 !important;
2021
- max-width: none !important;
2022
- max-height: none !important;
2023
-
2024
- /* Overflow */
2025
- overflow-y: auto !important;
2026
- overflow-x: auto !important;
2027
- /* overscroll-behavior removed to allow scroll-through to parent */
2028
- scrollbar-width: auto !important;
2029
- scrollbar-gutter: auto !important;
2030
-
2031
- /* Animation/transition - disabled to prevent movement */
2032
- animation: none !important;
2033
- transition: none !important;
1395
+ /* List markers */
1396
+ .overtype-wrapper .overtype-preview .list-marker {
1397
+ color: var(--list-marker, #ee964b) !important;
1398
+ }
1399
+
1400
+ /* Stats bar */
1401
+
1402
+ /* Stats bar - positioned by flexbox */
1403
+ .overtype-stats {
1404
+ height: 40px !important;
1405
+ padding: 0 20px !important;
1406
+ background: #f8f9fa !important;
1407
+ border-top: 1px solid #e0e0e0 !important;
1408
+ display: flex !important;
1409
+ justify-content: space-between !important;
1410
+ align-items: center !important;
1411
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
1412
+ font-size: 0.85rem !important;
1413
+ color: #666 !important;
1414
+ flex-shrink: 0 !important; /* Don't shrink */
1415
+ z-index: 10001 !important; /* Above link tooltip */
1416
+ position: relative !important; /* Enable z-index */
1417
+ }
1418
+
1419
+ /* Dark theme stats bar */
1420
+ .overtype-container[data-theme="cave"] .overtype-stats {
1421
+ background: var(--bg-secondary, #1D2D3E) !important;
1422
+ border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
1423
+ color: var(--text, #c5dde8) !important;
1424
+ }
1425
+
1426
+ .overtype-stats .overtype-stat {
1427
+ display: flex !important;
1428
+ align-items: center !important;
1429
+ gap: 5px !important;
1430
+ white-space: nowrap !important;
1431
+ }
1432
+
1433
+ .overtype-stats .live-dot {
1434
+ width: 8px !important;
1435
+ height: 8px !important;
1436
+ background: #4caf50 !important;
1437
+ border-radius: 50% !important;
1438
+ animation: overtype-pulse 2s infinite !important;
1439
+ }
1440
+
1441
+ @keyframes overtype-pulse {
1442
+ 0%, 100% { opacity: 1; transform: scale(1); }
1443
+ 50% { opacity: 0.6; transform: scale(1.2); }
2034
1444
  }
1445
+
2035
1446
 
2036
- /* Input layer styles */
2037
- .overtype-wrapper .overtype-input {
2038
- /* Layer positioning */
2039
- z-index: 1 !important;
2040
-
2041
- /* Text visibility */
2042
- color: transparent !important;
2043
- caret-color: var(--cursor, #f95738) !important;
2044
- background-color: transparent !important;
2045
-
2046
- /* Textarea-specific */
2047
- resize: none !important;
2048
- appearance: none !important;
2049
- -webkit-appearance: none !important;
2050
- -moz-appearance: none !important;
2051
-
2052
- /* Prevent mobile zoom on focus */
2053
- touch-action: manipulation !important;
2054
-
2055
- /* Disable autofill and spellcheck */
2056
- autocomplete: off !important;
2057
- autocorrect: off !important;
2058
- autocapitalize: off !important;
2059
- spellcheck: false !important;
1447
+ /* Toolbar Styles */
1448
+ .overtype-toolbar {
1449
+ display: flex !important;
1450
+ align-items: center !important;
1451
+ gap: 4px !important;
1452
+ padding: 8px !important; /* Override reset */
1453
+ background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
1454
+ border-bottom: 1px solid var(--toolbar-border, transparent) !important; /* Override reset */
1455
+ overflow-x: auto !important; /* Allow horizontal scrolling */
1456
+ overflow-y: hidden !important; /* Hide vertical overflow */
1457
+ -webkit-overflow-scrolling: touch !important;
1458
+ flex-shrink: 0 !important;
1459
+ height: auto !important;
1460
+ position: relative !important; /* Override reset */
1461
+ z-index: 100 !important; /* Ensure toolbar is above wrapper */
1462
+ scrollbar-width: thin; /* Thin scrollbar on Firefox */
1463
+ }
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;
2060
1477
  }
2061
1478
 
2062
- .overtype-wrapper .overtype-input::selection {
2063
- background-color: var(--selection, rgba(244, 211, 94, 0.4));
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;
2064
1493
  }
2065
1494
 
2066
- /* Preview layer styles */
2067
- .overtype-wrapper .overtype-preview {
2068
- /* Layer positioning */
2069
- z-index: 0 !important;
2070
- pointer-events: none !important;
2071
- color: var(--text, #0d3b66) !important;
2072
- background-color: transparent !important;
2073
-
2074
- /* Prevent text selection */
2075
- user-select: none !important;
2076
- -webkit-user-select: none !important;
2077
- -moz-user-select: none !important;
2078
- -ms-user-select: none !important;
1495
+ .overtype-toolbar-button svg {
1496
+ width: 20px;
1497
+ height: 20px;
1498
+ fill: currentColor;
2079
1499
  }
2080
1500
 
2081
- /* Defensive styles for preview child divs */
2082
- .overtype-wrapper .overtype-preview div {
2083
- /* Reset any inherited styles */
2084
- margin: 0 !important;
2085
- padding: 0 !important;
2086
- border: none !important;
2087
- text-align: left !important;
2088
- text-indent: 0 !important;
2089
- display: block !important;
2090
- position: static !important;
2091
- transform: none !important;
2092
- min-height: 0 !important;
2093
- max-height: none !important;
2094
- line-height: inherit !important;
2095
- font-size: inherit !important;
2096
- font-family: inherit !important;
1501
+ .overtype-toolbar-button:hover {
1502
+ background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
1503
+ color: var(--toolbar-icon, var(--text-primary, #333));
2097
1504
  }
2098
1505
 
2099
- /* Markdown element styling - NO SIZE CHANGES */
2100
- .overtype-wrapper .overtype-preview .header {
2101
- font-weight: bold !important;
1506
+ .overtype-toolbar-button:active {
1507
+ transform: scale(0.95);
2102
1508
  }
2103
1509
 
2104
- /* Header colors */
2105
- .overtype-wrapper .overtype-preview .h1 {
2106
- color: var(--h1, #f95738) !important;
1510
+ .overtype-toolbar-button.active {
1511
+ background: var(--toolbar-active, var(--primary, #007bff));
1512
+ color: var(--toolbar-icon, var(--text-primary, #333));
2107
1513
  }
2108
- .overtype-wrapper .overtype-preview .h2 {
2109
- color: var(--h2, #ee964b) !important;
1514
+
1515
+ .overtype-toolbar-button:disabled {
1516
+ opacity: 0.5;
1517
+ cursor: not-allowed;
2110
1518
  }
2111
- .overtype-wrapper .overtype-preview .h3 {
2112
- color: var(--h3, #3d8a51) !important;
1519
+
1520
+ .overtype-toolbar-separator {
1521
+ width: 1px;
1522
+ height: 24px;
1523
+ background: var(--border, #e0e0e0);
1524
+ margin: 0 4px;
1525
+ flex-shrink: 0;
2113
1526
  }
2114
1527
 
2115
- /* Semantic headers - flatten in edit mode */
2116
- .overtype-wrapper .overtype-preview h1,
2117
- .overtype-wrapper .overtype-preview h2,
2118
- .overtype-wrapper .overtype-preview h3 {
2119
- font-size: inherit !important;
2120
- font-weight: bold !important;
2121
- margin: 0 !important;
2122
- padding: 0 !important;
2123
- display: inline !important;
2124
- line-height: inherit !important;
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;
2125
1561
  }
2126
1562
 
2127
- /* Header colors for semantic headers */
2128
- .overtype-wrapper .overtype-preview h1 {
2129
- color: var(--h1, #f95738) !important;
1563
+ /* Dropdown menu styles */
1564
+ .overtype-toolbar-button {
1565
+ position: relative !important; /* Override reset - needed for dropdown */
2130
1566
  }
2131
- .overtype-wrapper .overtype-preview h2 {
2132
- color: var(--h2, #ee964b) !important;
1567
+
1568
+ .overtype-toolbar-button.dropdown-active {
1569
+ background: var(--toolbar-active, var(--hover-bg, #f0f0f0));
2133
1570
  }
2134
- .overtype-wrapper .overtype-preview h3 {
2135
- color: var(--h3, #3d8a51) !important;
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 */
2136
1582
  }
2137
1583
 
2138
- /* Lists - remove styling in edit mode */
2139
- .overtype-wrapper .overtype-preview ul,
2140
- .overtype-wrapper .overtype-preview ol {
2141
- list-style: none !important;
2142
- margin: 0 !important;
2143
- padding: 0 !important;
2144
- display: block !important; /* Lists need to be block for line breaks */
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;
2145
1596
  }
2146
1597
 
2147
- .overtype-wrapper .overtype-preview li {
2148
- display: block !important; /* Each item on its own line */
2149
- margin: 0 !important;
2150
- padding: 0 !important;
2151
- /* Don't set list-style here - let ul/ol control it */
1598
+ .overtype-dropdown-item:hover {
1599
+ background: var(--hover-bg, #f0f0f0);
2152
1600
  }
2153
1601
 
2154
- /* Bold text */
2155
- .overtype-wrapper .overtype-preview strong {
2156
- color: var(--strong, #ee964b) !important;
2157
- font-weight: bold !important;
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);
2158
1610
  }
2159
1611
 
2160
- /* Italic text */
2161
- .overtype-wrapper .overtype-preview em {
2162
- color: var(--em, #f95738) !important;
2163
- text-decoration-color: var(--em, #f95738) !important;
2164
- text-decoration-thickness: 1px !important;
2165
- font-style: italic !important;
1612
+ .overtype-dropdown-icon {
1613
+ width: 20px;
1614
+ margin-right: 8px;
1615
+ text-align: center;
2166
1616
  }
2167
1617
 
2168
- /* Strikethrough text */
2169
- .overtype-wrapper .overtype-preview del {
2170
- color: var(--del, #ee964b) !important;
2171
- text-decoration: line-through !important;
2172
- text-decoration-color: var(--del, #ee964b) !important;
2173
- text-decoration-thickness: 1px !important;
1618
+ /* Preview mode styles */
1619
+ .overtype-container[data-mode="preview"] .overtype-input {
1620
+ display: none !important;
2174
1621
  }
2175
1622
 
2176
- /* Inline code */
2177
- .overtype-wrapper .overtype-preview code {
2178
- background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
2179
- color: var(--code, #0d3b66) !important;
2180
- padding: 0 !important;
2181
- border-radius: 2px !important;
2182
- font-family: inherit !important;
2183
- font-size: inherit !important;
2184
- line-height: inherit !important;
2185
- font-weight: normal !important;
1623
+ .overtype-container[data-mode="preview"] .overtype-preview {
1624
+ pointer-events: auto !important;
1625
+ user-select: text !important;
1626
+ cursor: text !important;
2186
1627
  }
2187
1628
 
2188
- /* Code blocks - consolidated pre blocks */
2189
- .overtype-wrapper .overtype-preview pre {
2190
- padding: 0 !important;
2191
- margin: 0 !important;
2192
- border-radius: 4px !important;
2193
- overflow-x: auto !important;
1629
+ /* Hide syntax markers in preview mode */
1630
+ .overtype-container[data-mode="preview"] .syntax-marker {
1631
+ display: none !important;
2194
1632
  }
2195
1633
 
2196
- /* Code block styling in normal mode - yellow background */
2197
- .overtype-wrapper .overtype-preview pre.code-block {
2198
- background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
2199
- white-space: break-spaces !important; /* Prevent horizontal scrollbar that breaks alignment */
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;
2200
1638
  }
2201
-
2202
- /* Code inside pre blocks - remove background */
2203
- .overtype-wrapper .overtype-preview pre code {
2204
- background: transparent !important;
2205
- color: var(--code, #0d3b66) !important;
2206
- font-family: ${fontFamily} !important; /* Match textarea font exactly for alignment */
1639
+
1640
+ /* Hide all syntax markers inside links too */
1641
+ .overtype-container[data-mode="preview"] a .syntax-marker {
1642
+ display: none !important;
2207
1643
  }
2208
1644
 
2209
- /* Blockquotes */
2210
- .overtype-wrapper .overtype-preview .blockquote {
2211
- color: var(--blockquote, #5a7a9b) !important;
2212
- padding: 0 !important;
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;
2213
1651
  margin: 0 !important;
2214
- border: none !important;
1652
+ display: block !important;
1653
+ color: inherit !important; /* Use parent text color */
1654
+ line-height: 1 !important; /* Tight line height for headings */
2215
1655
  }
2216
-
2217
- /* Links */
2218
- .overtype-wrapper .overtype-preview a {
2219
- color: var(--link, #0d3b66) !important;
2220
- text-decoration: underline !important;
2221
- font-weight: normal !important;
1656
+
1657
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1 {
1658
+ font-size: 2em !important;
2222
1659
  }
2223
-
2224
- .overtype-wrapper .overtype-preview a:hover {
2225
- text-decoration: underline !important;
2226
- color: var(--link, #0d3b66) !important;
1660
+
1661
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2 {
1662
+ font-size: 1.5em !important;
2227
1663
  }
2228
-
2229
- /* Lists - no list styling */
2230
- .overtype-wrapper .overtype-preview ul,
2231
- .overtype-wrapper .overtype-preview ol {
2232
- list-style: none !important;
2233
- margin: 0 !important;
2234
- padding: 0 !important;
1664
+
1665
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
1666
+ font-size: 1.17em !important;
2235
1667
  }
2236
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
+ }
2237
1676
 
2238
- /* Horizontal rules */
2239
- .overtype-wrapper .overtype-preview hr {
2240
- border: none !important;
2241
- color: var(--hr, #5a7a9b) !important;
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;
2242
1686
  margin: 0 !important;
2243
1687
  padding: 0 !important;
2244
1688
  }
2245
1689
 
2246
- .overtype-wrapper .overtype-preview .hr-marker {
2247
- color: var(--hr, #5a7a9b) !important;
2248
- opacity: 0.6 !important;
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;
2249
1694
  }
2250
1695
 
2251
- /* Code fence markers - with background when not in code block */
2252
- .overtype-wrapper .overtype-preview .code-fence {
2253
- color: var(--code, #0d3b66) !important;
2254
- background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
2255
- }
2256
-
2257
- /* Code block lines - background for entire code block */
2258
- .overtype-wrapper .overtype-preview .code-block-line {
2259
- background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
2260
- }
2261
-
2262
- /* Remove background from code fence when inside code block line */
2263
- .overtype-wrapper .overtype-preview .code-block-line .code-fence {
2264
- background: transparent !important;
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;
2265
1700
  }
2266
1701
 
2267
- /* Raw markdown line */
2268
- .overtype-wrapper .overtype-preview .raw-line {
2269
- color: var(--raw-line, #5a7a9b) !important;
2270
- font-style: normal !important;
2271
- font-weight: normal !important;
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;
2272
1705
  }
2273
1706
 
2274
- /* Syntax markers */
2275
- .overtype-wrapper .overtype-preview .syntax-marker {
2276
- color: var(--syntax-marker, rgba(13, 59, 102, 0.52)) !important;
2277
- opacity: 0.7 !important;
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;
2278
1710
  }
2279
1711
 
2280
- /* List markers */
2281
- .overtype-wrapper .overtype-preview .list-marker {
2282
- color: var(--list-marker, #ee964b) !important;
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;
2283
1718
  }
2284
1719
 
2285
- /* Stats bar */
2286
-
2287
- /* Stats bar - positioned by flexbox */
2288
- .overtype-stats {
2289
- height: 40px !important;
2290
- padding: 0 20px !important;
2291
- background: #f8f9fa !important;
2292
- border-top: 1px solid #e0e0e0 !important;
2293
- display: flex !important;
2294
- justify-content: space-between !important;
2295
- align-items: center !important;
2296
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
2297
- font-size: 0.85rem !important;
2298
- color: #666 !important;
2299
- flex-shrink: 0 !important; /* Don't shrink */
2300
- z-index: 10001 !important; /* Above link tooltip */
2301
- position: relative !important; /* Enable z-index */
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;
2302
1729
  }
2303
1730
 
2304
- /* Dark theme stats bar */
2305
- .overtype-container[data-theme="cave"] .overtype-stats {
2306
- background: var(--bg-secondary, #1D2D3E) !important;
2307
- border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
2308
- color: var(--text, #c5dde8) !important;
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;
2309
1734
  }
2310
-
2311
- .overtype-stats .overtype-stat {
2312
- display: flex !important;
2313
- align-items: center !important;
2314
- gap: 5px !important;
2315
- white-space: nowrap !important;
1735
+
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;
2316
1743
  }
2317
-
2318
- .overtype-stats .live-dot {
2319
- width: 8px !important;
2320
- height: 8px !important;
2321
- background: #4caf50 !important;
2322
- border-radius: 50% !important;
2323
- animation: overtype-pulse 2s infinite !important;
1744
+
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;
2324
1748
  }
2325
-
2326
- @keyframes overtype-pulse {
2327
- 0%, 100% { opacity: 1; transform: scale(1); }
2328
- 50% { opacity: 0.6; transform: scale(1.2); }
1749
+
1750
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-fence {
1751
+ display: none !important;
2329
1752
  }
2330
-
2331
1753
 
2332
- /* Toolbar Styles */
2333
- .overtype-toolbar {
2334
- display: flex !important;
2335
- align-items: center !important;
2336
- gap: 4px !important;
2337
- padding: 8px !important; /* Override reset */
2338
- background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
2339
- border-bottom: 1px solid var(--toolbar-border, transparent) !important; /* Override reset */
2340
- overflow-x: auto !important; /* Allow horizontal scrolling */
2341
- overflow-y: hidden !important; /* Hide vertical overflow */
2342
- -webkit-overflow-scrolling: touch !important;
2343
- flex-shrink: 0 !important;
2344
- height: auto !important;
2345
- position: relative !important; /* Override reset */
2346
- z-index: 100 !important; /* Ensure toolbar is above wrapper */
2347
- scrollbar-width: thin; /* Thin scrollbar on Firefox */
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;
2348
1761
  }
2349
-
2350
- /* Thin scrollbar styling */
2351
- .overtype-toolbar::-webkit-scrollbar {
2352
- height: 4px;
1762
+
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 */
2353
1769
  }
2354
-
2355
- .overtype-toolbar::-webkit-scrollbar-track {
2356
- background: transparent;
1770
+
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;
2357
1778
  }
2358
-
2359
- .overtype-toolbar::-webkit-scrollbar-thumb {
2360
- background: rgba(0, 0, 0, 0.2);
2361
- border-radius: 2px;
1779
+
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 */
2362
1784
  }
2363
1785
 
2364
- .overtype-toolbar-button {
2365
- display: flex;
2366
- align-items: center;
2367
- justify-content: center;
2368
- width: 32px;
2369
- height: 32px;
2370
- padding: 0;
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;
1786
+ .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview em {
1787
+ font-style: italic !important;
1788
+ color: inherit !important; /* Use parent text color */
2378
1789
  }
2379
1790
 
2380
- .overtype-toolbar-button svg {
2381
- width: 20px;
2382
- height: 20px;
2383
- fill: currentColor;
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;
2384
1797
  }
2385
1798
 
2386
- .overtype-toolbar-button:hover {
2387
- background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
2388
- color: var(--toolbar-icon, var(--text-primary, #333));
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;
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;
2389
1819
  }
2390
1820
 
2391
- .overtype-toolbar-button:active {
2392
- transform: scale(0.95);
1821
+ .overtype-link-tooltip.visible {
1822
+ display: flex !important;
2393
1823
  }
2394
1824
 
2395
- .overtype-toolbar-button.active {
2396
- background: var(--toolbar-active, var(--primary, #007bff));
2397
- color: var(--toolbar-icon, var(--text-primary, #333));
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
+ }
2398
1835
  }
2399
1836
 
2400
- .overtype-toolbar-button:disabled {
2401
- opacity: 0.5;
2402
- cursor: not-allowed;
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);
1938
+ }
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);
1970
+ }
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)));
1980
+ }
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);
2403
1993
  }
2404
-
2405
- .overtype-toolbar-separator {
2406
- width: 1px;
2407
- height: 24px;
2408
- background: var(--border, #e0e0e0);
2409
- margin: 0 4px;
2410
- flex-shrink: 0;
1994
+ textarea.contentEditable = "false";
1995
+ }
1996
+ if (debugMode2) {
1997
+ console.log("canInsertText before:", canInsertText);
1998
+ console.log("execCommand result:", canInsertText);
1999
+ }
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);
2411
2006
  }
2412
-
2413
- /* Adjust wrapper when toolbar is present */
2414
- /* Mobile toolbar adjustments */
2415
- @media (max-width: 640px) {
2416
- .overtype-toolbar {
2417
- padding: 6px;
2418
- gap: 2px;
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)));
2419
2012
  }
2420
-
2421
- .overtype-toolbar-button {
2422
- width: 36px;
2423
- height: 36px;
2013
+ }
2014
+ }
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) {
2424
2024
  }
2425
-
2426
- .overtype-toolbar-separator {
2427
- margin: 0 2px;
2025
+ textarea.value = before + text + after;
2026
+ try {
2027
+ document.execCommand("ms-endUndoUnit");
2028
+ } catch (e) {
2428
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");
2429
2034
  }
2430
-
2431
- /* Plain mode - hide preview and show textarea text */
2432
- .overtype-container[data-mode="plain"] .overtype-preview {
2433
- display: none !important;
2434
- }
2435
-
2436
- .overtype-container[data-mode="plain"] .overtype-input {
2437
- color: var(--text, #0d3b66) !important;
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;
2441
- }
2442
-
2443
- /* Ensure textarea remains transparent in overlay mode */
2444
- .overtype-container:not([data-mode="plain"]) .overtype-input {
2445
- color: transparent !important;
2446
- }
2447
-
2448
- /* Dropdown menu styles */
2449
- .overtype-toolbar-button {
2450
- position: relative !important; /* Override reset - needed for dropdown */
2451
- }
2452
-
2453
- .overtype-toolbar-button.dropdown-active {
2454
- background: var(--toolbar-active, var(--hover-bg, #f0f0f0));
2455
- }
2456
-
2457
- .overtype-dropdown-menu {
2458
- position: fixed !important; /* Fixed positioning relative to viewport */
2459
- background: var(--bg-secondary, white) !important; /* Override reset */
2460
- border: 1px solid var(--border, #e0e0e0) !important; /* Override reset */
2461
- border-radius: 6px;
2462
- box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important; /* Override reset */
2463
- z-index: 10000; /* Very high z-index to ensure visibility */
2464
- min-width: 150px;
2465
- padding: 4px 0 !important; /* Override reset */
2466
- /* Position will be set via JavaScript based on button position */
2467
- }
2468
-
2469
- .overtype-dropdown-item {
2470
- display: flex;
2471
- align-items: center;
2472
- width: 100%;
2473
- padding: 8px 12px;
2474
- border: none;
2475
- background: none;
2476
- text-align: left;
2477
- cursor: pointer;
2478
- font-size: 14px;
2479
- color: var(--text, #333);
2480
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
2481
- }
2482
-
2483
- .overtype-dropdown-item:hover {
2484
- background: var(--hover-bg, #f0f0f0);
2485
- }
2486
-
2487
- .overtype-dropdown-item.active {
2488
- font-weight: 600;
2489
- }
2490
-
2491
- .overtype-dropdown-check {
2492
- width: 16px;
2493
- margin-right: 8px;
2494
- color: var(--h1, #007bff);
2495
- }
2496
-
2497
- .overtype-dropdown-icon {
2498
- width: 20px;
2499
- margin-right: 8px;
2500
- text-align: center;
2501
- }
2502
-
2503
- /* Preview mode styles */
2504
- .overtype-container[data-mode="preview"] .overtype-input {
2505
- display: none !important;
2506
- }
2507
-
2508
- .overtype-container[data-mode="preview"] .overtype-preview {
2509
- pointer-events: auto !important;
2510
- user-select: text !important;
2511
- cursor: text !important;
2512
- }
2513
-
2514
- /* Hide syntax markers in preview mode */
2515
- .overtype-container[data-mode="preview"] .syntax-marker {
2516
- display: none !important;
2517
- }
2518
-
2519
- /* Hide URL part of links in preview mode - extra specificity */
2520
- .overtype-container[data-mode="preview"] .syntax-marker.url-part,
2521
- .overtype-container[data-mode="preview"] .url-part {
2522
- display: none !important;
2523
- }
2524
-
2525
- /* Hide all syntax markers inside links too */
2526
- .overtype-container[data-mode="preview"] a .syntax-marker {
2527
- display: none !important;
2528
- }
2529
-
2530
- /* Headers - restore proper sizing in preview mode */
2531
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1,
2532
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2,
2533
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
2534
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
2535
- font-weight: 600 !important;
2536
- margin: 0 !important;
2537
- display: block !important;
2538
- color: inherit !important; /* Use parent text color */
2539
- line-height: 1 !important; /* Tight line height for headings */
2540
- }
2541
-
2542
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h1 {
2543
- font-size: 2em !important;
2544
- }
2545
-
2546
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h2 {
2547
- font-size: 1.5em !important;
2548
- }
2549
-
2550
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview h3 {
2551
- font-size: 1.17em !important;
2552
- }
2553
-
2554
- /* Lists - restore list styling in preview mode */
2555
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ul {
2556
- display: block !important;
2557
- list-style: disc !important;
2558
- padding-left: 2em !important;
2559
- margin: 1em 0 !important;
2560
- }
2561
-
2562
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview ol {
2563
- display: block !important;
2564
- list-style: decimal !important;
2565
- padding-left: 2em !important;
2566
- margin: 1em 0 !important;
2567
- }
2568
-
2569
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li {
2570
- display: list-item !important;
2571
- margin: 0 !important;
2572
- padding: 0 !important;
2573
- }
2574
-
2575
- /* Task list checkboxes - only in preview mode */
2576
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list {
2577
- list-style: none !important;
2578
- position: relative !important;
2035
+ }
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);
2042
+ }
2043
+ if (debugMode2) {
2044
+ console.log("Final value length:", textarea.value.length);
2045
+ console.groupEnd();
2046
+ }
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--;
2055
+ }
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++;
2063
+ }
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;
2579
2073
  }
2580
-
2581
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview li.task-list input[type="checkbox"] {
2582
- margin-right: 0.5em !important;
2583
- cursor: default !important;
2584
- vertical-align: middle !important;
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
+ }
2585
2080
  }
2586
-
2587
- /* Task list in normal mode - keep syntax visible */
2588
- .overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list {
2589
- list-style: none !important;
2081
+ counter += lineLength;
2082
+ }
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;
2590
2096
  }
2591
-
2592
- .overtype-container:not([data-mode="preview"]) .overtype-wrapper .overtype-preview li.task-list .syntax-marker {
2593
- color: var(--syntax, #999999) !important;
2594
- font-weight: normal !important;
2097
+ }
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);
2111
+ }
2112
+ if (afterSelection.match(/\S/) && newlinesAfterSelection < 2) {
2113
+ newlinesToPrepend = "\n".repeat(2 - newlinesAfterSelection);
2114
+ }
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--;
2125
+ }
2126
+ if (noInitialSelection) {
2127
+ let lineEnd = originalStart;
2128
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
2129
+ lineEnd++;
2595
2130
  }
2596
-
2597
- /* Links - make clickable in preview mode */
2598
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview a {
2599
- pointer-events: auto !important;
2600
- cursor: pointer !important;
2601
- color: var(--link, #0066cc) !important;
2602
- text-decoration: underline !important;
2131
+ textarea.selectionStart = lineStart;
2132
+ textarea.selectionEnd = lineEnd;
2133
+ } else {
2134
+ expandSelectionToLine(textarea);
2135
+ }
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
+ }
2603
2162
  }
2604
-
2605
- /* Code blocks - proper pre/code styling in preview mode */
2606
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
2607
- background: #2d2d2d !important;
2608
- color: #f8f8f2 !important;
2609
- padding: 1.2em !important;
2610
- border-radius: 3px !important;
2611
- overflow-x: auto !important;
2612
- margin: 0 !important;
2613
- display: block !important;
2163
+ }
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}`;
2614
2181
  }
2615
-
2616
- /* Cave theme code block background in preview mode */
2617
- .overtype-container[data-theme="cave"][data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block {
2618
- background: #11171F !important;
2182
+ }
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;
2193
+ }
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;
2619
2203
  }
2620
-
2621
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview pre.code-block code {
2622
- background: transparent !important;
2623
- color: inherit !important;
2624
- padding: 0 !important;
2625
- font-family: ${fontFamily} !important;
2626
- font-size: 0.9em !important;
2627
- line-height: 1.4 !important;
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;
2628
2216
  }
2629
-
2630
- /* Hide old code block lines and fences in preview mode */
2631
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-block-line {
2632
- display: none !important;
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 };
2228
+ }
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;
2633
2253
  }
2634
-
2635
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .code-fence {
2636
- display: none !important;
2254
+ }
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, ""));
2264
+ }
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));
2277
+ }
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}. `;
2288
+ }
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;
2637
2328
  }
2638
-
2639
- /* Blockquotes - enhanced styling in preview mode */
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;
2329
+ return { text: pristineText, selectionStart, selectionEnd };
2330
+ }
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;
2646
2343
  }
2647
-
2648
- /* Typography improvements in preview mode */
2649
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview {
2650
- font-family: Georgia, 'Times New Roman', serif !important;
2651
- font-size: 16px !important;
2652
- line-height: 1.8 !important;
2653
- color: var(--text, #333) !important; /* Consistent text color */
2344
+ }
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
+ }
2654
2410
  }
2655
-
2656
- /* Inline code in preview mode - keep monospace */
2657
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview code {
2658
- font-family: ${fontFamily} !important;
2659
- font-size: 0.9em !important;
2660
- background: rgba(135, 131, 120, 0.15) !important;
2661
- padding: 0.2em 0.4em !important;
2662
- border-radius: 3px !important;
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;
2663
2426
  }
2664
-
2665
- /* Strong and em elements in preview mode */
2666
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview strong {
2667
- font-weight: 700 !important;
2668
- color: inherit !important; /* Use parent text color */
2427
+ lineStart += line.length + 1;
2428
+ }
2429
+ if (currentLine.startsWith("- ")) {
2430
+ if (currentLine.startsWith("- [ ] ") || currentLine.startsWith("- [x] ")) {
2431
+ formats.push("task-list");
2432
+ } else {
2433
+ formats.push("bullet-list");
2669
2434
  }
2670
-
2671
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview em {
2672
- font-style: italic !important;
2673
- color: inherit !important; /* Use parent text color */
2435
+ }
2436
+ if (/^\d+\.\s/.test(currentLine)) {
2437
+ formats.push("numbered-list");
2438
+ }
2439
+ if (currentLine.startsWith("> ")) {
2440
+ formats.push("quote");
2441
+ }
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");
2674
2458
  }
2675
-
2676
- /* HR in preview mode */
2677
- .overtype-container[data-mode="preview"] .overtype-wrapper .overtype-preview .hr-marker {
2678
- display: block !important;
2679
- border-top: 2px solid var(--hr, #ddd) !important;
2680
- text-indent: -9999px !important;
2681
- height: 2px !important;
2459
+ }
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");
2682
2467
  }
2683
-
2684
- /* Link Tooltip - Base styles (all browsers) */
2685
- .overtype-link-tooltip {
2686
- /* Visual styles that work for both positioning methods */
2687
- background: #333 !important;
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;
2468
+ }
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");
2704
2474
  }
2705
-
2706
- .overtype-link-tooltip.visible {
2707
- display: flex !important;
2475
+ }
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
+ }
2708
2486
  }
2709
-
2710
- /* CSS Anchor Positioning (modern browsers only) */
2711
- @supports (position-anchor: --x) and (position-area: center) {
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;
2487
+ }
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 = "";
2527
+ }
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;
2533
+ }
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--;
2592
+ }
2593
+ let lineEnd = originalEnd;
2594
+ while (lineEnd < value.length && value[lineEnd] !== "\n") {
2595
+ lineEnd++;
2596
+ }
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
+ }
2719
2664
  }
2720
2665
  }
2721
-
2722
- ${mobileStyles}
2723
- `;
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 @@ var Toolbar = class {
2780
2738
  });
2781
2739
  return button;
2782
2740
  }
2783
- button._clickHandler = async (e) => {
2741
+ button._clickHandler = (e) => {
2784
2742
  e.preventDefault();
2785
- this.editor.textarea.focus();
2786
- try {
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 (used by keyboard shortcuts)
2813
- * @param {Object} buttonConfig - Button configuration object with action function
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(buttonConfig) {
2816
- this.editor.textarea.focus();
2817
- try {
2818
- if (buttonConfig.action) {
2819
- await buttonConfig.action({
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 @@ var LinkTooltip = class {
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 @@ var LinkTooltip = class {
3031
2978
  this.hide();
3032
2979
  }
3033
2980
  });
3034
- this.editor.textarea.addEventListener("blur", () => this.hide());
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", () => this.cancelHide());
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 @@ var LinkTooltip = class {
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 @@ var LinkTooltip = class {
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 @@ var eyeIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke
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, event }) => {
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, event }) => {
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, event }) => {
3228
+ action: ({ editor }) => {
3266
3229
  toggleCode(editor.textarea);
3267
3230
  editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3268
3231
  }
@@ -3273,63 +3236,70 @@ var toolbarButtons = {
3273
3236
  },
3274
3237
  link: {
3275
3238
  name: "link",
3239
+ actionId: "insertLink",
3276
3240
  icon: linkIcon,
3277
3241
  title: "Insert Link",
3278
- action: ({ editor, event }) => {
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, event }) => {
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, event }) => {
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, event }) => {
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, event }) => {
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, event }) => {
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, event }) => {
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 @@ var toolbarButtons = {
3338
3308
  },
3339
3309
  quote: {
3340
3310
  name: "quote",
3311
+ actionId: "toggleQuote",
3341
3312
  icon: quoteIcon,
3342
3313
  title: "Quote",
3343
- action: ({ editor, event }) => {
3314
+ action: ({ editor }) => {
3344
3315
  toggleQuote(editor.textarea);
3345
3316
  editor.textarea.dispatchEvent(new Event("input", { bubbles: true }));
3346
3317
  }
@@ -3374,6 +3345,45 @@ var defaultToolbarButtons = [
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 @@ var _OverType = class _OverType {
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 @@ var _OverType = class _OverType {
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 @@ var _OverType = class _OverType {
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 @@ var _OverType = class _OverType {
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
  }