overtype 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/overtype.js CHANGED
@@ -144,8 +144,14 @@ class OverType {
144
144
  lineHeight: 1.5
145
145
  },
146
146
 
147
+ // Native textarea properties
148
+ textareaProps: {},
149
+
147
150
  // Behavior
148
151
  autofocus: false,
152
+ autoResize: false, // Auto-expand height with content
153
+ minHeight: '100px', // Minimum height for autoResize mode
154
+ maxHeight: null, // Maximum height for autoResize mode (null = unlimited)
149
155
  placeholder: 'Start typing...',
150
156
  value: '',
151
157
 
@@ -307,10 +313,6 @@ class OverType {
307
313
  this.wrapper = document.createElement('div');
308
314
  this.wrapper.className = 'overtype-wrapper';
309
315
 
310
- // Add stats wrapper class if stats are enabled
311
- if (this.options.showStats) {
312
- this.wrapper.classList.add('with-stats');
313
- }
314
316
 
315
317
  // Apply instance-specific styles via CSS custom properties
316
318
  if (this.options.fontSize) {
@@ -330,6 +332,19 @@ class OverType {
330
332
  this.textarea.className = 'overtype-input';
331
333
  this.textarea.placeholder = this.options.placeholder;
332
334
  this._configureTextarea();
335
+
336
+ // Apply any native textarea properties
337
+ if (this.options.textareaProps) {
338
+ Object.entries(this.options.textareaProps).forEach(([key, value]) => {
339
+ if (key === 'className' || key === 'class') {
340
+ this.textarea.className += ' ' + value;
341
+ } else if (key === 'style' && typeof value === 'object') {
342
+ Object.assign(this.textarea.style, value);
343
+ } else {
344
+ this.textarea.setAttribute(key, value);
345
+ }
346
+ });
347
+ }
333
348
 
334
349
  // Create preview div
335
350
  this.preview = document.createElement('div');
@@ -340,19 +355,42 @@ class OverType {
340
355
  this.wrapper.appendChild(this.textarea);
341
356
  this.wrapper.appendChild(this.preview);
342
357
 
343
- // Add stats bar if enabled
358
+ // Add wrapper to container first
359
+ this.container.appendChild(this.wrapper);
360
+
361
+ // Add stats bar at the end (bottom) if enabled
344
362
  if (this.options.showStats) {
345
363
  this.statsBar = document.createElement('div');
346
364
  this.statsBar.className = 'overtype-stats';
347
- this.wrapper.appendChild(this.statsBar);
365
+ this.container.appendChild(this.statsBar);
348
366
  this._updateStats();
349
367
  }
350
368
 
351
- // Add wrapper to container
352
- this.container.appendChild(this.wrapper);
353
-
354
369
  // Add container to element
355
370
  this.element.appendChild(this.container);
371
+
372
+ // Debug logging
373
+ if (window.location.pathname.includes('demo.html')) {
374
+ console.log('_createDOM completed:', {
375
+ elementId: this.element.id,
376
+ autoResize: this.options.autoResize,
377
+ containerClasses: this.container.className,
378
+ hasStats: !!this.statsBar,
379
+ hasToolbar: this.options.toolbar
380
+ });
381
+ }
382
+
383
+ // Setup auto-resize if enabled
384
+ if (this.options.autoResize) {
385
+ this._setupAutoResize();
386
+ } else {
387
+ // Ensure auto-resize class is removed if not using auto-resize
388
+ this.container.classList.remove('overtype-auto-resize');
389
+
390
+ if (window.location.pathname.includes('demo.html')) {
391
+ console.log('Removed auto-resize class from:', this.element.id);
392
+ }
393
+ }
356
394
  }
357
395
 
358
396
  /**
@@ -378,6 +416,16 @@ class OverType {
378
416
  if (this.options.autofocus) {
379
417
  this.textarea.focus();
380
418
  }
419
+
420
+ // Setup or remove auto-resize
421
+ if (this.options.autoResize) {
422
+ if (!this.container.classList.contains('overtype-auto-resize')) {
423
+ this._setupAutoResize();
424
+ }
425
+ } else {
426
+ // Ensure auto-resize class is removed
427
+ this.container.classList.remove('overtype-auto-resize');
428
+ }
381
429
 
382
430
  // Update preview with initial content
383
431
  this.updatePreview();
@@ -554,6 +602,11 @@ class OverType {
554
602
  setValue(value) {
555
603
  this.textarea.value = value;
556
604
  this.updatePreview();
605
+
606
+ // Update height if auto-resize is enabled
607
+ if (this.options.autoResize) {
608
+ this._updateAutoHeight();
609
+ }
557
610
  }
558
611
 
559
612
 
@@ -629,6 +682,89 @@ class OverType {
629
682
  }
630
683
  }
631
684
 
685
+ /**
686
+ * Setup auto-resize functionality
687
+ * @private
688
+ */
689
+ _setupAutoResize() {
690
+ // Add auto-resize class for styling
691
+ this.container.classList.add('overtype-auto-resize');
692
+
693
+ // Store previous height for comparison
694
+ this.previousHeight = null;
695
+
696
+ // Initial height update
697
+ this._updateAutoHeight();
698
+
699
+ // Listen for input events
700
+ this.textarea.addEventListener('input', () => this._updateAutoHeight());
701
+
702
+ // Listen for window resize
703
+ window.addEventListener('resize', () => this._updateAutoHeight());
704
+ }
705
+
706
+ /**
707
+ * Update height based on scrollHeight
708
+ * @private
709
+ */
710
+ _updateAutoHeight() {
711
+ if (!this.options.autoResize) return;
712
+
713
+ const textarea = this.textarea;
714
+ const preview = this.preview;
715
+ const wrapper = this.wrapper;
716
+
717
+ // Get computed styles
718
+ const computed = window.getComputedStyle(textarea);
719
+ const paddingTop = parseFloat(computed.paddingTop);
720
+ const paddingBottom = parseFloat(computed.paddingBottom);
721
+
722
+ // Store scroll positions
723
+ const scrollTop = textarea.scrollTop;
724
+
725
+ // Reset height to get accurate scrollHeight
726
+ textarea.style.setProperty('height', 'auto', 'important');
727
+
728
+ // Calculate new height based on scrollHeight
729
+ let newHeight = textarea.scrollHeight;
730
+
731
+ // Apply min height constraint
732
+ if (this.options.minHeight) {
733
+ const minHeight = parseInt(this.options.minHeight);
734
+ newHeight = Math.max(newHeight, minHeight);
735
+ }
736
+
737
+ // Apply max height constraint
738
+ let overflow = 'hidden';
739
+ if (this.options.maxHeight) {
740
+ const maxHeight = parseInt(this.options.maxHeight);
741
+ if (newHeight > maxHeight) {
742
+ newHeight = maxHeight;
743
+ overflow = 'auto';
744
+ }
745
+ }
746
+
747
+ // Apply the new height to all elements with !important to override base styles
748
+ const heightPx = newHeight + 'px';
749
+ textarea.style.setProperty('height', heightPx, 'important');
750
+ textarea.style.setProperty('overflow-y', overflow, 'important');
751
+
752
+ preview.style.setProperty('height', heightPx, 'important');
753
+ preview.style.setProperty('overflow-y', overflow, 'important');
754
+
755
+ wrapper.style.setProperty('height', heightPx, 'important');
756
+
757
+ // Restore scroll position
758
+ textarea.scrollTop = scrollTop;
759
+ preview.scrollTop = scrollTop;
760
+
761
+ // Track if height changed
762
+ if (this.previousHeight !== newHeight) {
763
+ this.previousHeight = newHeight;
764
+ // Could dispatch a custom event here if needed
765
+ }
766
+ }
767
+
632
768
  /**
633
769
  * Show or hide stats bar
634
770
  * @param {boolean} show - Whether to show stats
@@ -637,17 +773,15 @@ class OverType {
637
773
  this.options.showStats = show;
638
774
 
639
775
  if (show && !this.statsBar) {
640
- // Create stats bar
776
+ // Create stats bar (add to container, not wrapper)
641
777
  this.statsBar = document.createElement('div');
642
778
  this.statsBar.className = 'overtype-stats';
643
- this.wrapper.appendChild(this.statsBar);
644
- this.wrapper.classList.add('with-stats');
779
+ this.container.appendChild(this.statsBar);
645
780
  this._updateStats();
646
781
  } else if (!show && this.statsBar) {
647
782
  // Remove stats bar
648
783
  this.statsBar.remove();
649
784
  this.statsBar = null;
650
- this.wrapper.classList.remove('with-stats');
651
785
  }
652
786
  }
653
787
 
package/src/parser.js CHANGED
@@ -109,7 +109,9 @@ export class MarkdownParser {
109
109
  * @returns {string|null} Parsed code fence or null
110
110
  */
111
111
  static parseCodeBlock(html) {
112
- if (html.startsWith('```')) {
112
+ // Only treat as code block if ``` is alone or followed by a language identifier
113
+ // This prevents ```some code``` from being treated as a code fence
114
+ if (html.match(/^```(\s*|\w*)$/)) {
113
115
  return `<div><span class="code-fence">${html}</span></div>`;
114
116
  }
115
117
  return null;
package/src/styles.js CHANGED
@@ -14,7 +14,7 @@ export function generateStyles(options = {}) {
14
14
  const {
15
15
  fontSize = '14px',
16
16
  lineHeight = 1.6,
17
- fontFamily = "ui-monospace, 'SFMono-Regular', 'Menlo', 'Consolas', 'Liberation Mono', monospace",
17
+ 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',
18
18
  padding = '20px',
19
19
  theme = null,
20
20
  mobile = {}
@@ -41,7 +41,8 @@ export function generateStyles(options = {}) {
41
41
  return `
42
42
  /* OverType Editor Styles */
43
43
  .overtype-container {
44
- position: relative !important;
44
+ display: grid !important;
45
+ grid-template-rows: auto 1fr auto !important;
45
46
  width: 100% !important;
46
47
  height: 100% !important;
47
48
  ${themeVars ? `
@@ -49,12 +50,26 @@ export function generateStyles(options = {}) {
49
50
  ${themeVars}` : ''}
50
51
  }
51
52
 
53
+ /* Auto-resize mode styles */
54
+ .overtype-container.overtype-auto-resize {
55
+ height: auto !important;
56
+ grid-template-rows: auto auto auto !important;
57
+ }
58
+
59
+ .overtype-container.overtype-auto-resize .overtype-wrapper {
60
+ height: auto !important;
61
+ min-height: 60px !important;
62
+ overflow: visible !important;
63
+ }
64
+
52
65
  .overtype-wrapper {
53
66
  position: relative !important;
54
67
  width: 100% !important;
55
- height: 100% !important;
68
+ height: 100% !important; /* Take full height of grid cell */
69
+ min-height: 60px !important; /* Minimum usable height */
56
70
  overflow: hidden !important;
57
71
  background: var(--bg-secondary, #ffffff) !important;
72
+ grid-row: 2 !important; /* Always second row in grid */
58
73
  }
59
74
 
60
75
  /* Critical alignment styles - must be identical for both layers */
@@ -69,6 +84,8 @@ export function generateStyles(options = {}) {
69
84
 
70
85
  /* Font properties - any difference breaks alignment */
71
86
  font-family: ${fontFamily} !important;
87
+ font-synthesis: none !important; /* no faux bold/italic width drift */
88
+ font-variant-ligatures: none !important; /* keep metrics stable for code */
72
89
  font-size: var(--instance-font-size, ${fontSize}) !important;
73
90
  line-height: var(--instance-line-height, ${lineHeight}) !important;
74
91
  font-weight: normal !important;
@@ -335,15 +352,9 @@ export function generateStyles(options = {}) {
335
352
  }
336
353
 
337
354
  /* Stats bar */
338
- .overtype-wrapper.with-stats {
339
- padding-bottom: 40px !important;
340
- }
341
355
 
342
- .overtype-wrapper .overtype-stats {
343
- position: absolute !important;
344
- bottom: 0 !important;
345
- left: 0 !important;
346
- right: 0 !important;
356
+ /* Stats bar - positioned by grid, not absolute */
357
+ .overtype-stats {
347
358
  height: 40px !important;
348
359
  padding: 0 20px !important;
349
360
  background: #f8f9fa !important;
@@ -354,24 +365,24 @@ export function generateStyles(options = {}) {
354
365
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
355
366
  font-size: 0.85rem !important;
356
367
  color: #666 !important;
357
- z-index: 2 !important;
368
+ grid-row: 3 !important; /* Always third row in grid */
358
369
  }
359
370
 
360
371
  /* Dark theme stats bar */
361
- .overtype-wrapper[data-theme="cave"] .overtype-stats {
372
+ .overtype-container[data-theme="cave"] .overtype-stats {
362
373
  background: var(--bg-secondary, #1D2D3E) !important;
363
374
  border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
364
375
  color: var(--text, #c5dde8) !important;
365
376
  }
366
377
 
367
- .overtype-wrapper .overtype-stats .overtype-stat {
378
+ .overtype-stats .overtype-stat {
368
379
  display: flex !important;
369
380
  align-items: center !important;
370
381
  gap: 5px !important;
371
382
  white-space: nowrap !important;
372
383
  }
373
384
 
374
- .overtype-wrapper .overtype-stats .live-dot {
385
+ .overtype-stats .live-dot {
375
386
  width: 8px !important;
376
387
  height: 8px !important;
377
388
  background: #4caf50 !important;
@@ -384,11 +395,6 @@ export function generateStyles(options = {}) {
384
395
  50% { opacity: 0.6; transform: scale(1.2); }
385
396
  }
386
397
 
387
- /* Adjust textarea and preview for stats bar */
388
- .overtype-wrapper.with-stats .overtype-input,
389
- .overtype-wrapper.with-stats .overtype-preview {
390
- height: calc(100% - 40px) !important;
391
- }
392
398
 
393
399
  /* Toolbar Styles */
394
400
  .overtype-toolbar {
@@ -399,6 +405,9 @@ export function generateStyles(options = {}) {
399
405
  background: var(--toolbar-bg, var(--bg-primary, #f8f9fa));
400
406
  overflow-x: auto;
401
407
  -webkit-overflow-scrolling: touch;
408
+ flex-shrink: 0;
409
+ height: auto !important;
410
+ grid-row: 1 !important; /* Always first row in grid */
402
411
  }
403
412
 
404
413
  .overtype-toolbar-button {