overtype 1.1.8 → 1.2.0

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/src/styles.js CHANGED
@@ -41,11 +41,44 @@ export function generateStyles(options = {}) {
41
41
 
42
42
  return `
43
43
  /* OverType Editor Styles */
44
+
45
+ /* Middle-ground CSS Reset - Prevent parent styles from leaking in */
46
+ .overtype-container * {
47
+ /* Box model - these commonly leak */
48
+ margin: 0 !important;
49
+ padding: 0 !important;
50
+ border: 0 !important;
51
+
52
+ /* Layout - these can break our layout */
53
+ /* Don't reset position - it breaks dropdowns */
54
+ float: none !important;
55
+ clear: none !important;
56
+
57
+ /* Typography - only reset decorative aspects */
58
+ text-decoration: none !important;
59
+ text-transform: none !important;
60
+ letter-spacing: normal !important;
61
+
62
+ /* Visual effects that can interfere */
63
+ box-shadow: none !important;
64
+ text-shadow: none !important;
65
+
66
+ /* Ensure box-sizing is consistent */
67
+ box-sizing: border-box !important;
68
+
69
+ /* Keep inheritance for these */
70
+ /* font-family, color, line-height, font-size - inherit */
71
+ }
72
+
73
+ /* Container base styles after reset */
44
74
  .overtype-container {
45
75
  display: grid !important;
46
76
  grid-template-rows: auto 1fr auto !important;
47
77
  width: 100% !important;
48
78
  height: 100% !important;
79
+ position: relative !important; /* Override reset - needed for absolute children */
80
+ overflow: visible !important; /* Allow dropdown to overflow container */
81
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
49
82
  ${themeVars ? `
50
83
  /* Theme Variables */
51
84
  ${themeVars}` : ''}
@@ -64,20 +97,21 @@ export function generateStyles(options = {}) {
64
97
  }
65
98
 
66
99
  .overtype-wrapper {
67
- position: relative !important;
100
+ position: relative !important; /* Override reset - needed for absolute children */
68
101
  width: 100% !important;
69
102
  height: 100% !important; /* Take full height of grid cell */
70
103
  min-height: 60px !important; /* Minimum usable height */
71
104
  overflow: hidden !important;
72
105
  background: var(--bg-secondary, #ffffff) !important;
73
106
  grid-row: 2 !important; /* Always second row in grid */
107
+ z-index: 1; /* Below toolbar and dropdown */
74
108
  }
75
109
 
76
110
  /* Critical alignment styles - must be identical for both layers */
77
111
  .overtype-wrapper .overtype-input,
78
112
  .overtype-wrapper .overtype-preview {
79
113
  /* Positioning - must be identical */
80
- position: absolute !important;
114
+ position: absolute !important; /* Override reset - required for overlay */
81
115
  top: 0 !important;
82
116
  left: 0 !important;
83
117
  width: 100% !important;
@@ -144,7 +178,7 @@ export function generateStyles(options = {}) {
144
178
  /* Overflow */
145
179
  overflow-y: auto !important;
146
180
  overflow-x: auto !important;
147
- overscroll-behavior: none !important;
181
+ /* overscroll-behavior removed to allow scroll-through to parent */
148
182
  scrollbar-width: auto !important;
149
183
  scrollbar-gutter: auto !important;
150
184
 
@@ -232,6 +266,45 @@ export function generateStyles(options = {}) {
232
266
  color: var(--h3, #3d8a51) !important;
233
267
  }
234
268
 
269
+ /* Semantic headers - flatten in edit mode */
270
+ .overtype-wrapper .overtype-preview h1,
271
+ .overtype-wrapper .overtype-preview h2,
272
+ .overtype-wrapper .overtype-preview h3 {
273
+ font-size: inherit !important;
274
+ font-weight: bold !important;
275
+ margin: 0 !important;
276
+ padding: 0 !important;
277
+ display: inline !important;
278
+ line-height: inherit !important;
279
+ }
280
+
281
+ /* Header colors for semantic headers */
282
+ .overtype-wrapper .overtype-preview h1 {
283
+ color: var(--h1, #f95738) !important;
284
+ }
285
+ .overtype-wrapper .overtype-preview h2 {
286
+ color: var(--h2, #ee964b) !important;
287
+ }
288
+ .overtype-wrapper .overtype-preview h3 {
289
+ color: var(--h3, #3d8a51) !important;
290
+ }
291
+
292
+ /* Lists - remove styling in edit mode */
293
+ .overtype-wrapper .overtype-preview ul,
294
+ .overtype-wrapper .overtype-preview ol {
295
+ list-style: none !important;
296
+ margin: 0 !important;
297
+ padding: 0 !important;
298
+ display: block !important; /* Lists need to be block for line breaks */
299
+ }
300
+
301
+ .overtype-wrapper .overtype-preview li {
302
+ display: block !important; /* Each item on its own line */
303
+ margin: 0 !important;
304
+ padding: 0 !important;
305
+ /* Don't set list-style here - let ul/ol control it */
306
+ }
307
+
235
308
  /* Bold text */
236
309
  .overtype-wrapper .overtype-preview strong {
237
310
  color: var(--strong, #ee964b) !important;
@@ -258,17 +331,23 @@ export function generateStyles(options = {}) {
258
331
  font-weight: normal !important;
259
332
  }
260
333
 
261
- /* Code blocks */
334
+ /* Code blocks - consolidated pre blocks */
262
335
  .overtype-wrapper .overtype-preview pre {
263
- background: #1e1e1e !important;
264
336
  padding: 0 !important;
265
337
  margin: 0 !important;
266
338
  border-radius: 4px !important;
267
339
  overflow-x: auto !important;
268
340
  }
341
+
342
+ /* Code block styling in normal mode - yellow background */
343
+ .overtype-wrapper .overtype-preview pre.code-block {
344
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
345
+ }
269
346
 
347
+ /* Code inside pre blocks - remove background */
270
348
  .overtype-wrapper .overtype-preview pre code {
271
- background: none !important;
349
+ background: transparent !important;
350
+ color: var(--code, #0d3b66) !important;
272
351
  }
273
352
 
274
353
  /* Blockquotes */
@@ -299,11 +378,6 @@ export function generateStyles(options = {}) {
299
378
  padding: 0 !important;
300
379
  }
301
380
 
302
- .overtype-wrapper .overtype-preview li {
303
- margin: 0 !important;
304
- padding: 0 !important;
305
- list-style: none !important;
306
- }
307
381
 
308
382
  /* Horizontal rules */
309
383
  .overtype-wrapper .overtype-preview hr {
@@ -402,13 +476,31 @@ export function generateStyles(options = {}) {
402
476
  display: flex;
403
477
  align-items: center;
404
478
  gap: 4px;
405
- padding: 8px;
406
- background: var(--toolbar-bg, var(--bg-primary, #f8f9fa));
407
- overflow-x: auto;
479
+ padding: 8px !important; /* Override reset */
480
+ background: var(--toolbar-bg, var(--bg-primary, #f8f9fa)) !important; /* Override reset */
481
+ overflow-x: auto !important; /* Allow horizontal scrolling */
482
+ overflow-y: hidden !important; /* Hide vertical overflow */
408
483
  -webkit-overflow-scrolling: touch;
409
484
  flex-shrink: 0;
410
485
  height: auto !important;
411
486
  grid-row: 1 !important; /* Always first row in grid */
487
+ position: relative !important; /* Override reset */
488
+ z-index: 100; /* Ensure toolbar is above wrapper */
489
+ scrollbar-width: thin; /* Thin scrollbar on Firefox */
490
+ }
491
+
492
+ /* Thin scrollbar styling */
493
+ .overtype-toolbar::-webkit-scrollbar {
494
+ height: 4px;
495
+ }
496
+
497
+ .overtype-toolbar::-webkit-scrollbar-track {
498
+ background: transparent;
499
+ }
500
+
501
+ .overtype-toolbar::-webkit-scrollbar-thumb {
502
+ background: rgba(0, 0, 0, 0.2);
503
+ border-radius: 2px;
412
504
  }
413
505
 
414
506
  .overtype-toolbar-button {
@@ -498,6 +590,214 @@ export function generateStyles(options = {}) {
498
590
  color: transparent !important;
499
591
  }
500
592
 
593
+ /* Dropdown menu styles */
594
+ .overtype-toolbar-button {
595
+ position: relative !important; /* Override reset - needed for dropdown */
596
+ }
597
+
598
+ .overtype-toolbar-button.dropdown-active {
599
+ background: var(--toolbar-active, var(--hover-bg, #f0f0f0));
600
+ }
601
+
602
+ .overtype-dropdown-menu {
603
+ position: fixed !important; /* Fixed positioning relative to viewport */
604
+ background: var(--bg-secondary, white) !important; /* Override reset */
605
+ border: 1px solid var(--border, #e0e0e0) !important; /* Override reset */
606
+ border-radius: 6px;
607
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important; /* Override reset */
608
+ z-index: 10000; /* Very high z-index to ensure visibility */
609
+ min-width: 150px;
610
+ padding: 4px 0 !important; /* Override reset */
611
+ /* Position will be set via JavaScript based on button position */
612
+ }
613
+
614
+ .overtype-dropdown-item {
615
+ display: flex;
616
+ align-items: center;
617
+ width: 100%;
618
+ padding: 8px 12px;
619
+ border: none;
620
+ background: none;
621
+ text-align: left;
622
+ cursor: pointer;
623
+ font-size: 14px;
624
+ color: var(--text, #333);
625
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
626
+ }
627
+
628
+ .overtype-dropdown-item:hover {
629
+ background: var(--hover-bg, #f0f0f0);
630
+ }
631
+
632
+ .overtype-dropdown-item.active {
633
+ font-weight: 600;
634
+ }
635
+
636
+ .overtype-dropdown-check {
637
+ width: 16px;
638
+ margin-right: 8px;
639
+ color: var(--h1, #007bff);
640
+ }
641
+
642
+ /* Preview mode styles */
643
+ .overtype-container.preview-mode .overtype-input {
644
+ display: none !important;
645
+ }
646
+
647
+ .overtype-container.preview-mode .overtype-preview {
648
+ pointer-events: auto !important;
649
+ user-select: text !important;
650
+ cursor: text !important;
651
+ }
652
+
653
+ /* Hide syntax markers in preview mode */
654
+ .overtype-container.preview-mode .syntax-marker {
655
+ display: none !important;
656
+ }
657
+
658
+ /* Hide URL part of links in preview mode - extra specificity */
659
+ .overtype-container.preview-mode .syntax-marker.url-part,
660
+ .overtype-container.preview-mode .url-part {
661
+ display: none !important;
662
+ }
663
+
664
+ /* Hide all syntax markers inside links too */
665
+ .overtype-container.preview-mode a .syntax-marker {
666
+ display: none !important;
667
+ }
668
+
669
+ /* Headers - restore proper sizing in preview mode */
670
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1,
671
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2,
672
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
673
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
674
+ font-weight: 600 !important;
675
+ margin: 0 !important;
676
+ display: block !important;
677
+ color: inherit !important; /* Use parent text color */
678
+ line-height: 1 !important; /* Tight line height for headings */
679
+ }
680
+
681
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h1 {
682
+ font-size: 2em !important;
683
+ }
684
+
685
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h2 {
686
+ font-size: 1.5em !important;
687
+ }
688
+
689
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview h3 {
690
+ font-size: 1.17em !important;
691
+ }
692
+
693
+ /* Lists - restore list styling in preview mode */
694
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview ul {
695
+ display: block !important;
696
+ list-style: disc !important;
697
+ padding-left: 2em !important;
698
+ margin: 1em 0 !important;
699
+ }
700
+
701
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview ol {
702
+ display: block !important;
703
+ list-style: decimal !important;
704
+ padding-left: 2em !important;
705
+ margin: 1em 0 !important;
706
+ }
707
+
708
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview li {
709
+ display: list-item !important;
710
+ margin: 0 !important;
711
+ padding: 0 !important;
712
+ }
713
+
714
+ /* Links - make clickable in preview mode */
715
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview a {
716
+ pointer-events: auto !important;
717
+ cursor: pointer !important;
718
+ color: var(--link, #0066cc) !important;
719
+ text-decoration: underline !important;
720
+ }
721
+
722
+ /* Code blocks - proper pre/code styling in preview mode */
723
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block {
724
+ background: #2d2d2d !important;
725
+ color: #f8f8f2 !important;
726
+ padding: 1.2em !important;
727
+ border-radius: 3px !important;
728
+ overflow-x: auto !important;
729
+ margin: 0 !important;
730
+ display: block !important;
731
+ }
732
+
733
+ /* Cave theme code block background in preview mode */
734
+ .overtype-container[data-theme="cave"].preview-mode .overtype-wrapper .overtype-preview pre.code-block {
735
+ background: #11171F !important;
736
+ }
737
+
738
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview pre.code-block code {
739
+ background: transparent !important;
740
+ color: inherit !important;
741
+ padding: 0 !important;
742
+ font-family: ${fontFamily} !important;
743
+ font-size: 0.9em !important;
744
+ line-height: 1.4 !important;
745
+ }
746
+
747
+ /* Hide old code block lines and fences in preview mode */
748
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-block-line {
749
+ display: none !important;
750
+ }
751
+
752
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .code-fence {
753
+ display: none !important;
754
+ }
755
+
756
+ /* Blockquotes - enhanced styling in preview mode */
757
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .blockquote {
758
+ display: block !important;
759
+ border-left: 4px solid var(--blockquote, #ddd) !important;
760
+ padding-left: 1em !important;
761
+ margin: 1em 0 !important;
762
+ font-style: italic !important;
763
+ }
764
+
765
+ /* Typography improvements in preview mode */
766
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview {
767
+ font-family: Georgia, 'Times New Roman', serif !important;
768
+ font-size: 16px !important;
769
+ line-height: 1.8 !important;
770
+ color: var(--text, #333) !important; /* Consistent text color */
771
+ }
772
+
773
+ /* Inline code in preview mode - keep monospace */
774
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview code {
775
+ font-family: ${fontFamily} !important;
776
+ font-size: 0.9em !important;
777
+ background: rgba(135, 131, 120, 0.15) !important;
778
+ padding: 0.2em 0.4em !important;
779
+ border-radius: 3px !important;
780
+ }
781
+
782
+ /* Strong and em elements in preview mode */
783
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview strong {
784
+ font-weight: 700 !important;
785
+ color: inherit !important; /* Use parent text color */
786
+ }
787
+
788
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview em {
789
+ font-style: italic !important;
790
+ color: inherit !important; /* Use parent text color */
791
+ }
792
+
793
+ /* HR in preview mode */
794
+ .overtype-container.preview-mode .overtype-wrapper .overtype-preview .hr-marker {
795
+ display: block !important;
796
+ border-top: 2px solid var(--hr, #ddd) !important;
797
+ text-indent: -9999px !important;
798
+ height: 2px !important;
799
+ }
800
+
501
801
  ${mobileStyles}
502
802
  `;
503
803
  }
package/src/toolbar.js CHANGED
@@ -41,7 +41,7 @@ export class Toolbar {
41
41
  { name: 'orderedList', icon: icons.orderedListIcon, title: 'Numbered List', action: 'toggleNumberedList' },
42
42
  { name: 'taskList', icon: icons.taskListIcon, title: 'Task List', action: 'toggleTaskList' },
43
43
  { separator: true },
44
- { name: 'togglePlain', icon: icons.eyeIcon, title: 'Show plain textarea', action: 'toggle-plain' }
44
+ { name: 'viewMode', icon: icons.eyeIcon, title: 'View mode', action: 'toggle-view-menu', hasDropdown: true }
45
45
  ];
46
46
 
47
47
  // Create buttons
@@ -80,10 +80,19 @@ export class Toolbar {
80
80
  button.setAttribute('data-action', config.action);
81
81
  button.innerHTML = config.icon;
82
82
 
83
+ // Add dropdown if needed
84
+ if (config.hasDropdown) {
85
+ button.classList.add('has-dropdown');
86
+ // Store reference for dropdown
87
+ if (config.name === 'viewMode') {
88
+ this.viewModeButton = button;
89
+ }
90
+ }
91
+
83
92
  // Add click handler
84
93
  button.addEventListener('click', (e) => {
85
94
  e.preventDefault();
86
- this.handleAction(config.action);
95
+ this.handleAction(config.action, button);
87
96
  });
88
97
 
89
98
  return button;
@@ -92,11 +101,17 @@ export class Toolbar {
92
101
  /**
93
102
  * Handle toolbar button actions
94
103
  */
95
- async handleAction(action) {
104
+ async handleAction(action, button) {
96
105
  const textarea = this.editor.textarea;
97
106
  if (!textarea) return;
98
107
 
99
- // Focus textarea
108
+ // Handle dropdown toggle
109
+ if (action === 'toggle-view-menu') {
110
+ this.toggleViewDropdown(button);
111
+ return;
112
+ }
113
+
114
+ // Focus textarea for other actions
100
115
  textarea.focus();
101
116
 
102
117
  try {
@@ -210,11 +225,132 @@ export class Toolbar {
210
225
  }
211
226
  }
212
227
 
228
+ /**
229
+ * Toggle view mode dropdown menu
230
+ */
231
+ toggleViewDropdown(button) {
232
+ // Close any existing dropdown
233
+ const existingDropdown = document.querySelector('.overtype-dropdown-menu');
234
+ if (existingDropdown) {
235
+ existingDropdown.remove();
236
+ button.classList.remove('dropdown-active');
237
+ document.removeEventListener('click', this.handleDocumentClick);
238
+ return;
239
+ }
240
+
241
+ // Create dropdown menu
242
+ const dropdown = this.createViewDropdown();
243
+
244
+ // Position dropdown relative to button
245
+ const rect = button.getBoundingClientRect();
246
+ dropdown.style.top = `${rect.bottom + 4}px`;
247
+ dropdown.style.left = `${rect.left}px`;
248
+
249
+ // Append to body instead of button
250
+ document.body.appendChild(dropdown);
251
+ button.classList.add('dropdown-active');
252
+
253
+ // Store reference for document click handler
254
+ this.handleDocumentClick = (e) => {
255
+ if (!button.contains(e.target) && !dropdown.contains(e.target)) {
256
+ dropdown.remove();
257
+ button.classList.remove('dropdown-active');
258
+ document.removeEventListener('click', this.handleDocumentClick);
259
+ }
260
+ };
261
+
262
+ // Close on click outside
263
+ setTimeout(() => {
264
+ document.addEventListener('click', this.handleDocumentClick);
265
+ }, 0);
266
+ }
267
+
268
+ /**
269
+ * Create view mode dropdown menu
270
+ */
271
+ createViewDropdown() {
272
+ const dropdown = document.createElement('div');
273
+ dropdown.className = 'overtype-dropdown-menu';
274
+
275
+ // Determine current mode
276
+ const isPlain = this.editor.container.classList.contains('plain-mode');
277
+ const isPreview = this.editor.container.classList.contains('preview-mode');
278
+ const currentMode = isPreview ? 'preview' : (isPlain ? 'plain' : 'normal');
279
+
280
+ // Create menu items
281
+ const modes = [
282
+ { id: 'normal', label: 'Normal Edit', icon: '✓' },
283
+ { id: 'plain', label: 'Plain Textarea', icon: '✓' },
284
+ { id: 'preview', label: 'Preview Mode', icon: '✓' }
285
+ ];
286
+
287
+ modes.forEach(mode => {
288
+ const item = document.createElement('button');
289
+ item.className = 'overtype-dropdown-item';
290
+ item.type = 'button';
291
+
292
+ const check = document.createElement('span');
293
+ check.className = 'overtype-dropdown-check';
294
+ check.textContent = currentMode === mode.id ? mode.icon : '';
295
+
296
+ const label = document.createElement('span');
297
+ label.textContent = mode.label;
298
+
299
+ item.appendChild(check);
300
+ item.appendChild(label);
301
+
302
+ if (currentMode === mode.id) {
303
+ item.classList.add('active');
304
+ }
305
+
306
+ item.addEventListener('click', (e) => {
307
+ e.stopPropagation();
308
+ this.setViewMode(mode.id);
309
+ dropdown.remove();
310
+ this.viewModeButton.classList.remove('dropdown-active');
311
+ document.removeEventListener('click', this.handleDocumentClick);
312
+ });
313
+
314
+ dropdown.appendChild(item);
315
+ });
316
+
317
+ return dropdown;
318
+ }
319
+
320
+ /**
321
+ * Set view mode
322
+ */
323
+ setViewMode(mode) {
324
+ // Clear all mode classes
325
+ this.editor.container.classList.remove('plain-mode', 'preview-mode');
326
+
327
+ switch(mode) {
328
+ case 'plain':
329
+ this.editor.showPlainTextarea(true);
330
+ break;
331
+ case 'preview':
332
+ this.editor.showPreviewMode(true);
333
+ break;
334
+ case 'normal':
335
+ default:
336
+ // Normal edit mode
337
+ this.editor.showPlainTextarea(false);
338
+ if (typeof this.editor.showPreviewMode === 'function') {
339
+ this.editor.showPreviewMode(false);
340
+ }
341
+ break;
342
+ }
343
+ }
344
+
213
345
  /**
214
346
  * Destroy toolbar
215
347
  */
216
348
  destroy() {
217
349
  if (this.container) {
350
+ // Clean up event listeners
351
+ if (this.handleDocumentClick) {
352
+ document.removeEventListener('click', this.handleDocumentClick);
353
+ }
218
354
  this.container.remove();
219
355
  this.container = null;
220
356
  this.buttons = {};