1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
---
name: overtype
description: Use when working with OverType markdown editors - covers API, config options, DOM structure, view modes, and known workarounds
---

# OverType Markdown Editor

Loaded via CDN: `https://unpkg.com/overtype/dist/overtype.min.js`

Source: https://github.com/panphora/overtype

A transparent textarea over a rendered preview div. Monospace font required.
Markdown syntax stays visible in edit mode. ~95KB, zero dependencies.

## Constructor

```javascript
const [editor] = new OverType(target, options)
```

Always returns an array of instances, even for a single element.
`target` accepts a selector string, Element, NodeList, or array.

## Config Options

```javascript
{
  // Content
  value: "",
  placeholder: "Start typing...",

  // Typography
  fontSize: "14px",
  lineHeight: 1.6,
  fontFamily: "monospace",
  padding: "16px",

  // Theme: "solar", "cave", or custom { name, colors } object
  theme: "solar",
  // Per-instance color overrides (merged into active theme):
  colors: { text: "#1a1a1a" },

  // Auto-resize
  autoResize: false,
  minHeight: "100px",   // parsed with parseInt()
  maxHeight: null,

  // Behavior
  autofocus: false,
  smartLists: true,     // auto-continue lists on Enter
  toolbar: false,
  toolbarButtons: [],
  showStats: false,

  // Form integration
  textareaProps: { name: "content", required: true, maxLength: 500 },

  // Mobile (applied at <= 640px)
  mobile: { fontSize: "16px", padding: "12px", lineHeight: 1.5 },

  // Callbacks
  onChange: (value, instance) => {},
  onKeydown: (event, instance) => {},

  // Syntax highlighting
  codeHighlighter: (code, lang) => html,
}
```

## Instance Methods

```javascript
editor.getValue()                           // Get markdown string
editor.setValue(markdown)                    // Set content
editor.getCleanHTML()                       // HTML without OverType markup
editor.getRenderedHTML()                    // HTML with syntax markers
editor.getRenderedHTML({ cleanHTML: true }) // Same as getCleanHTML()
editor.getPreviewHTML()                     // Actual DOM from preview layer

editor.showNormalEditMode()   // Default WYSIWYG editing
editor.showPlainTextarea()    // Raw markdown, no preview
editor.showPreviewMode()      // Read-only preview, clickable links

editor.setTheme("cave")
editor.focus()
editor.blur()
editor.showStats(true)
editor.isInitialized()
editor.reinit(options)
editor.destroy()
```

## Static Methods

```javascript
OverType.init(target, options)        // Same as constructor
OverType.initFromData(".editor", {})  // Config via data-ot-* attributes
OverType.getInstance(element)
OverType.destroyAll()
OverType.setTheme("cave")            // or custom { name, colors } object
OverType.setCodeHighlighter(fn)
OverType.setCustomSyntax(fn)          // Must maintain 1:1 char alignment

// Standalone markdown parser (no editor instance needed)
OverType.MarkdownParser.parse(text)   // Returns rendered HTML
```

## DOM Structure

```
target (your element)
  .overtype-container
    .overtype-wrapper          ← position: relative
      .overtype-input          ← textarea, position: absolute, transparent
      .overtype-preview        ← rendered HTML, position: absolute
    .overtype-toolbar          ← if toolbar: true
    .overtype-stats            ← if showStats: true
```

## Internal CSS (relevant to sizing)

```css
.overtype-wrapper {
  min-height: 60px !important;   /* hardcoded default */
}
.overtype-input, .overtype-preview {
  height: 100% !important;
  position: absolute !important;
}
/* With autoResize: */
.overtype-container.overtype-auto-resize .overtype-wrapper {
  min-height: 60px !important;   /* still 60px */
}
```

Auto-resize measures `textarea.scrollHeight`, applies `Math.max(scrollHeight,
parseInt(minHeight))`, and sets `height` with `!important` on the wrapper,
textarea, and preview.

## Known Issues in This Project

### Theme text color

The default "solar" theme uses `#0d3b66` (dark blue) for text. This project
overrides it globally before any instances are created:

```javascript
OverType.setTheme({ name: "ketchup", colors: { text: "#1a1a1a" } })
```

`setTheme` accepts a string (built-in name) or an object with `name` and
`colors`. Partial `colors` objects merge into the base solar theme. The
per-instance `colors` config option does not work reliably.

### Sizing

Three OverType behaviors compound to inflate small editors:

1. The wrapper's `min-height: 60px !important` makes single-line editors too
   tall. The `minHeight` config only floors the auto-resize calculation; it
   does not override the CSS rule.
2. The app's global `textarea { padding; border }` applies to OverType's
   internal textarea, inflating the `scrollHeight` that auto-resize reads.
3. OverType's auto-resize fires on every keystroke, re-applying the inflated
   height and undoing any corrections.

### `compactOverType(el)` in `public/js/app.js`

A helper at the top of `app.js` fixes all three. Call it on the container
element after `new OverType(el, ...)`:

```javascript
const [editor] = new OverType(el, {
  value: text,
  autoResize: true,
  minHeight: 14,
  padding: "0 4px",
})
const resize = compactOverType(el)
```

It zeros the wrapper min-height, strips textarea padding and border, measures
true `scrollHeight` on the next frame, and hooks `input` to re-measure after
each keystroke. Returns a resize function for manual re-measurement (e.g.
after toggling `readOnly`), or `null` if the expected DOM nodes aren't found.

The element must be visible when the next animation frame fires, or
`scrollHeight` reads as 0. If the container is hidden behind an `x-show` that
hasn't toggled yet, defer the call:

```javascript
requestAnimationFrame(() => compactOverType(el))
```

Pass `padding: "0 4px"` in the OverType config to match the inline override on
the textarea — otherwise the preview layer keeps OverType's default padding
and the two layers render at different offsets.

## View Modes

`editor.showPreviewMode()` renders content read-only with clickable links.
`editor.showNormalEditMode()` returns to editing. However, preview mode uses
absolute positioning internally and does not auto-size to content.

For read-only markdown rendering without layout issues, use the standalone parser:

```javascript
el.innerHTML = OverType.MarkdownParser.parse(markdown)
```

## Limitations

- Images not supported (variable height breaks alignment)
- Monospace font required (variable-width breaks cursor alignment)
- Fixed font size across all content (no larger headers)
- Markdown syntax always visible in edit mode
- Links require Cmd/Ctrl+Click (direct click positions cursor)