Show HN: OverType – 一个基于 Markdown 的所见即所得编辑器,本质上只是一个文本框
23 分•作者: panphora•9 个月前
嗨,HN!我一直对现代的 WYSIWYG 编辑器感到非常沮丧,于是开始尝试构建自己的编辑器。<p>我遇到的问题很简单:我想要一种低技术含量的方式来输入带样式的文本,但我不想加载一个复杂的 500KB 的库,尤其是在同一个页面上要初始化几十次的情况下。<p>纯 <textarea> 中的 Markdown 是一个比完整的 WYSIWYG 更好的选择,但它的主要缺点是,在没有任何格式的情况下,它看起来很丑陋。我可以接受,但我的客户肯定不行。<p>我深入研究了 ContentEditable 几年,但最终意识到别人已经比我做得更好了。<p>我一直在思考这个问题:为什么我不能拥有一个简单、高效、美观的 Markdown 编辑器?我见过的最好的解决方案是 Ghost 的分屏编辑器:左边是 Markdown,右边是预览,并带有同步滚动。<p>然后,大约一年前,我脑海中闪过一个想法:如果我们在一块 <textarea> 后面叠加一个预览窗格会怎么样?如果我们将它们完美对齐,那么即使你只编辑纯文本,它看起来和感觉也像是在编辑富文本!<p>当然,也会有一些缺点:你必须使用等宽字体,所有内容都必须使用相同字号,并且所有 Markdown 标记都必须显示在最终预览中。<p>但这些都是我可以接受的权衡。<p>总之,版本 1 进展得并不顺利……事实证明,保持 textarea 和渲染的预览对齐比我想象的要难。这是我发现的:<p>- 列表很难对齐 - 项目符号会破坏字符对齐。使用 HTML 实体(• 用于项目符号)解决,以保持等宽<p>- 并非所有等宽字体都是真正的等宽字体 - 粗体和斜体文本即使在“等宽”字体中也可能具有不同的宽度,从而破坏了完美的叠加<p>- 嵌入是一个噩梦 - 来自父页面的任何继承 CSS(边距、填充、行高)都会改变对齐。即使是 1px 的偏移也会完全破坏这种错觉<p>解决方案是进行强迫症式的规范化:<p><pre><code> // 整个技巧:一个透明的 textarea 覆盖在预览 div 上
layerElements(textarea, preview)
applyIdenticalSpacing(textarea, preview)
// 使 textarea 不可见,但保留光标
textarea.style.background = 'transparent'
textarea.style.color = 'transparent'
textarea.style.caretColor = 'black'
// 保持它们同步
textarea.addEventListener('input', () => {
preview.innerHTML = parseMarkdown(textarea.value)
syncScroll(textarea, preview)
})
</code></pre>
一周前,我开始尝试版本 2,并发现了 GitHub 的 <markdown-toolbar> 元素,它在纯 <textarea> 中很好地处理了 Markdown 格式。<p>这个实验变成了 OverType (<a href="https://overtype.dev" rel="nofollow">https://overtype.dev</a>),我今天向你展示它——它是一个富 Markdown 编辑器,实际上只是一个 <textarea>。关键的见解是,一旦你解决了对齐挑战,你就可以免费获得所有原生 textarea 提供的功能:撤销/重做、移动键盘、可访问性和原生性能。<p>到目前为止,它在浏览器和移动设备上的表现都出奇地好。我得到了一个小型包(总共 45KB)中的高性能富文本编辑功能。这有点傻,但它有效!我计划在我的所有项目中使用它,并且我想保持它的简单和极简。<p>如果你能试用一下,并告诉我你对它的看法,我将非常高兴。编辑愉快!<p>---<p>演示和文档:<a href="https://overtype.dev" rel="nofollow">https://overtype.dev</a><p>GitHub:<a href="https://github.com/panphora/overtype" rel="nofollow">https://github.com/panphora/overtype</a>
查看原文
Hi HN! I got so frustrated with modern WYSIWYG editors that I started to play around with building my own.<p>The problem I had was simple: I wanted a low-tech way to type styled text, but I didn't want to load a complex 500KB library, especially if I was going to initialize it dozens of times on the same page.<p>Markdown in a plain <textarea> was the best alternative to a full WYSIWYG, but its main drawback is how ugly it looks without any formatting. I can handle it, but my clients certainly can't.<p>I went down the ContentEditable rabbit hole for a few years, but always came to realize others had solved it better than I ever could.<p>I kept coming back to this problem: why can't I have a simple, performant, beautiful markdown editor? The best solution I ever saw was Ghost's split-screen editor: markdown on the left, preview on the right, with synchronized scrolling.<p>Then, about a year ago, an idea popped into my head: what if we layered a preview pane behind a <textarea>? If we aligned them perfectly, then even though you were only editing plain text, it would look and feel like you were editing rich text!<p>Of course, there would be downsides: you'd have to use a monospace font, all content would have to have the same font size, and all the markdown markup would have to be displayed in the final preview.<p>But those were tradeoffs I could live with.<p>Anyways, version 1 didn't go so well... it turns out it's harder to keep a textarea and a rendered preview in alignment than I thought. Here's what I discovered:<p>- Lists were hard to align - bullet points threw off character alignment. Solved with HTML entities (• for bullets) that maintain monospace width<p>- Not all monospace fonts are truly monospace - bold and italic text can have different widths even in "monospace" fonts, breaking the perfect overlay<p>- Embedding was a nightmare - any inherited CSS from parent pages (margin, padding, line-height) would shift alignment. Even a 1px shift completely broke the illusion<p>The solution was obsessive normalization:<p><pre><code> // The entire trick: a transparent textarea over a preview div
layerElements(textarea, preview)
applyIdenticalSpacing(textarea, preview)
// Make textarea invisible but keep the cursor
textarea.style.background = 'transparent'
textarea.style.color = 'transparent'
textarea.style.caretColor = 'black'
// Keep them in sync
textarea.addEventListener('input', () => {
preview.innerHTML = parseMarkdown(textarea.value)
syncScroll(textarea, preview)
})
</code></pre>
A week ago I started playing with version 2 and discovered GitHub's <markdown-toolbar> element, which handles markdown formatting in a plain <textarea> really well.<p>That experiment turned into OverType (<a href="https://overtype.dev" rel="nofollow">https://overtype.dev</a>), which I'm showing to you today -- it's a rich markdown editor that's really just a <textarea>. The key insight was that once you solve the alignment challenges, you get everything native textareas provide for free: undo/redo, mobile keyboard, accessibility, and native performance.<p>So far it works surprisingly well across browsers and mobile. I get performant rich text editing in one small package (45KB total). It's kind of a dumb idea, but it works! I'm planning to use it in all my projects and I'd like to keep it simple and minimal.<p>I would love it if you would kick the tires and let me know what you think of it. Happy editing!<p>---<p>Demo & docs: <a href="https://overtype.dev" rel="nofollow">https://overtype.dev</a><p>GitHub: <a href="https://github.com/panphora/overtype" rel="nofollow">https://github.com/panphora/overtype</a>