<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Frankmd | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/frankmd/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:48 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/frankmd/index.xml" rel="self" type="application/rss+xml"/><item><title>Vibe Code: Fiz um Editor de Markdown do zero com Claude Code (FrankMD) PARTE 2</title><link>https://akitaonrails.github.io/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-parte-2/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-parte-2/</guid><pubDate>Sun, 01 Feb 2026 19:29:18 +0000</pubDate><description>&lt;p&gt;Se ainda não leu, veja a &lt;a href="https://akitaonrails.github.io/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-part-1/"&gt;Parte 1&lt;/a&gt; pra entender o que é o aplicativo que eu construí. O &lt;strong&gt;FrankMD&lt;/strong&gt; é um editor de Markdown que personalizei pra ser eficaz pro meu caso de uso pessoal: escrever blog posts. Não foi pra ser substituto de Obsidian nem de VS Code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener"&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_18-49-26.jpg" alt="FrankMD" loading="lazy" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;O &lt;strong&gt;TL;DR&lt;/strong&gt; é o seguinte: antigamente, se eu fosse fazer este web app do zero, teria me custado aproximadamente &lt;strong&gt;200 horas&lt;/strong&gt;, ao longo de 1 ou 2 meses, bem devagar, indo e voltando. E talvez não tivesse ficado tão completo quanto este.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se ainda não leu, veja a <a href="/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-part-1/">Parte 1</a> pra entender o que é o aplicativo que eu construí. O <strong>FrankMD</strong> é um editor de Markdown que personalizei pra ser eficaz pro meu caso de uso pessoal: escrever blog posts. Não foi pra ser substituto de Obsidian nem de VS Code.</p>
<p><a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_18-49-26.jpg" alt="FrankMD"  loading="lazy" /></a></p>
<p>O <strong>TL;DR</strong> é o seguinte: antigamente, se eu fosse fazer este web app do zero, teria me custado aproximadamente <strong>200 horas</strong>, ao longo de 1 ou 2 meses, bem devagar, indo e voltando. E talvez não tivesse ficado tão completo quanto este.</p>
<blockquote>
  <p>Com Claude, fiz tudo em pouco mais de <strong>30 horas</strong>, distribuído em 3 dias de super hiper foco (não recomendo; praticamente fiquei doente fazendo isso). No final, isso gerou um projeto com aproximadamente 130 pull requests. Um total de <strong>18 mil linhas de código</strong> (cobertura de testes de cerca de 70%). Foi tudo feito em Ruby on Rails 8 e Javascript (HotWire/Stimulus).</p>

</blockquote>
<p>Existem várias vantagens neste aplicativo em particular: é um escopo muito bem conhecido e bem documentado. Existem centenas de editores de texto de todos os tipos e de complexidades. Certamente todos os LLMs foram treinados com toneladas de material sobre isso. Então, quando eu peço pra fazer uma pesquisa por nome de arquivo, ele já sabe buscar algo como o algoritmo de Levenshtein, pra fuzzy matching, por exemplo.</p>
<p>Buscar e listar imagens em um grid, fazer upload pra S3, procurar e substituir texto, preview em HTML, etc. Tudo isso é super bem documentado e trivial de fazer. Mesmo sem LLMs, qualquer estagiário encontraria no primeiro link no Google e no Stack Overflow. Portanto, eu diria que mais de 90% do escopo foi fácil. Um aplicativo com um escopo mais desconhecido — digamos, um aplicativo de uso científico — teria sido bem mais difícil. Então este meu exemplo é só um pouco acima de um Hello World: repetir um escopo bem conhecido, que já existe.</p>
<p>Diferente do que fiz no meu artigo do primeiro exemplo de Vibe Coding, desta vez eu não vou comentar commit a commit; são mais de 130! Ficaríamos aqui a semana inteira. Em vez disso, vou comentar alguns temas que me chamam mais a atenção e que podem ser relevantes pra outros projetos.</p>
<h2>Escolha bem seus planos de LLM<span class="hx:absolute hx:-mt-20" id="escolha-bem-seus-planos-de-llm"></span>
    <a href="#escolha-bem-seus-planos-de-llm" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Durante os 3 dias, eu fui interrompido pelo Claude Code umas 2 ou 3 vezes e tive que usar outra coisa por 2 ou 3 horas. Isso porque ele tem algum limite diário de tokens e me manda parar depois que eu uso tokens demais.</p>
<p>Estou usando o Plano Max (5x Pro), que acho que custa uns USD 100, ou seja, cerca de USD 6 por dia. Nesse caso, foi barato porque 3 dias custariam só USD 18.</p>
<p>Mas num desses intervalos, mudei pra usar Claude Opus via OpenRouter, e isso foi BEM mais caro. Fora do plano Max exclusivo do Claude Code, o custo por token é absurdamente maior. Em umas 2 ou 3 horas, já consumiu mais de USD 100 (o equivalente à mensalidade toda!)</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201165118_screenshot-2026-01-31_10-19-11.png" alt="open router billing"  loading="lazy" /></p>
<p>Ou seja, no total, por usar o plano Max fora, acabei gastando mais de USD 120. Mas se tivesse só parado pra descansar entre os limites diários, teria gasto um dia a mais (não teria ficado doente &#x1f605;) e não teria ficado muito além de USD 20. Portanto, precisa pesquisar bem os planos de cada plataforma antes de sair usando sem pensar, como eu nem. &#x1f602;</p>
<p>Como já cansei de falar: na dúvida, use o Claude Code com o plano Max.</p>
<h2>Escolha bem sua arquitetura<span class="hx:absolute hx:-mt-20" id="escolha-bem-sua-arquitetura"></span>
    <a href="#escolha-bem-sua-arquitetura" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando comecei a codificar (sempre que falar &ldquo;eu codifiquei&rdquo;, leiam como &ldquo;eu vibe codei&rdquo;, claro), o início foi bem rápido. Eu decidi poucas coisas técnicas: queria que fosse Ruby on Rails 8, queria que usasse Tailwind CSS, queria que não usasse nenhum banco de dados e implementasse tudo com base em sistemas de arquivos, queria que não tivesse autenticação, já que seria self-hosted pra uso somente meu.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-01-45.jpg" alt="first commits"  loading="lazy" /></p>
<p>Os primeiros 10 commits levaram pouco mais de 1 hora e meia. Fui adicionando funcionalidades pouco a pouco pra dar &ldquo;forma&rdquo; ao editor e, nesse ponto, eu já tinha um editor mínimo, bonito, com tema dark, que editava e exibia uma preview em html. Nada mal pra um protótipo.</p>
<p>Como falei no outro artigo, esse seria um dos pontos de &ldquo;já funciona&rdquo;.</p>
<p>Mas claro, ainda faltam mais de 120 commits pra realmente funcionar e quase 30 horas pra chegar no &ldquo;funciona de verdade&rdquo;.</p>
<p>Fui adicionando várias pequenas funcionalidades, nesta ordem:</p>
<ul>
<li>Adicionar um cheat-sheet de Markdown (help)</li>
<li>Dialog box pra adicionar fenced code blocks com autocomplete de linguagem</li>
<li>Sistema de temas, já adicionando uns 10 temas (mas ainda não ajustado pro Omarchy)</li>
<li>Dialog box pra customizar fontes</li>
<li>Controle de zoom no Preview</li>
<li>Primeira tentativa de &ldquo;typewriter mode&rdquo; (isso vai dar muita dor de cabeça ainda)</li>
<li>Dialog box de pesquisar arquivos por path com fuzzy finder</li>
<li>Primeira tentativa de sincronizar o scroll do editor e preview (isso também ainda vai dar muita dor de cabeça)</li>
<li>Dialog box de pesquisar arquivos por conteúdo com suporte a regex</li>
<li>Dialog box de pesquisar e embedar videos de YouTube</li>
<li>Dialog box de pesquisar imagens, upload e inserir</li>
<li>Suporte inicial a Docker</li>
</ul>
<p>Isso completa os primeiros <a href="https://github.com/akitaonrails/FrankMD/commit/ba42c81b47a6343ba42df8705c6973d9d45506a0"target="_blank" rel="noopener">34 commits</a> e cerca de 4 horas de trabalho. Novamente, muito próximo do &ldquo;já funciona&rdquo;.</p>
<p>Mas a diversão começa no <a href="https://github.com/akitaonrails/FrankMD/commit/7bbf03a4b594203ed1a36c6735f9d23c5ffc2c91"target="_blank" rel="noopener">commit 7bbf03a</a>, intitulado: &ldquo;Refactor to Restful Architecture&rdquo;.</p>
<blockquote>
  <p>Isto é uma coisa específica de Rails e de arquitetura em geral. O Claude, sem eu ter instruído, fez exatamente o que eu esperaria que um estagiário em Rails fizesse: embutiu toda a lógica de gerenciar diretórios e arquivos, upload de imagens e tudo mais em 2 ou 3 módulos de serviço.</p>

</blockquote>
<p>Também fez todas as rotas manualmente. Ou seja, não usou a coisa mais básica de Rails: o conceito de <a href="https://guides.rubyonrails.org/routing.html"target="_blank" rel="noopener">Restful Resources</a>. Leiam o guia no link; não vou explicar o que é isso aqui, só que qualquer pessoa experiente em Rails sabe disso de olhos fechados.</p>
<p>Portanto, ele criou rotas como &ldquo;<a href="http://localhost:3000/notes"target="_blank" rel="noopener">http://localhost:3000/notes</a>&rdquo; mas não habilitou ter coisas como &ldquo;<a href="http://localhost:3000/notes/hello"target="_blank" rel="noopener">http://localhost:3000/notes/hello</a>&rdquo; automaticamente abrir o &ldquo;hello.md&rdquo; e que verbos de HTTP como PATCH pra atualizar o arquivo ou DELETE pra apagar o arquivo. E também, sem a opção de GET, eu não teria a opção de fazer um bookmark direto de um arquivo. Tudo isso sai de graça se usar o RESTful. Então esse commit foi o refactoring em que eu o instruí a refazer tudo nessa arquitetura. Só isso foi quase <strong>1000 linhas</strong> adicionadas.</p>
<blockquote>
  <p>O &ldquo;correto&rdquo; seria ter, logo no começo, configurado um arquivo CLAUDE.MD ou CONTRIBUTING.MD ou similar com as regras específicas de arquitetura e convenções de código - mas eu não fiz, então fica aqui o lembrete pra vocês. &#x1f605;</p>

</blockquote>
<h3>Refactoring e Fixes<span class="hx:absolute hx:-mt-20" id="refactoring-e-fixes"></span>
    <a href="#refactoring-e-fixes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Depois desse primeiro grande refactor, voltei a fazer ajustes (fixes ou consertos) e continuar adicionando novas funcionalidades:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-17-55.jpg" alt="screenshot 2026 02 01 17 17 55"  loading="lazy" /></p>
<p>Neste ponto já cruzamos <strong>47 commits</strong> e mais de <strong>8 horas</strong> non-stop. E sim, eu não tirei o olho do monitor. Não dá pra ler 100% de tudo o que o Claude fica pensando e fazendo; é muita coisa, mas, no geral, já notei que precisa ficar batendo o olho e, principalmente, <strong>prestando muita atenção</strong>!</p>
<p>O tempo de processamento de múltiplos agentes em paralelo, muitos em modo de <em>reasoning/thinking</em> é muito demorado mesmo. O Claude é um dos mais rápidos entre os outros, mas, na prática, eu acho sofridamente demorado.</p>
<p>Depois do almoço, resolvi abrir um YouTube do lado enquanto esperava o Claude terminar as tarefas. Significa que eu não prestei atenção e só batia o olho se ele terminava com &ldquo;sucesso&rdquo; ou se me pedia alguma permissão pra executar alguma coisa.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201185351_screenshot-2026-01-30_19-32-51.png" alt="não faça isso kkk"  loading="lazy" /></p>
<p>Não lembro se foi neste ponto, mas aí fui dar uma olhada no que ele tinha gerado e, claro, a essa altura já tinha um <code>application.css</code> de mais de 1000 linhas, que mandei consertar <a href="https://github.com/akitaonrails/FrankMD/commit/4d3d6a07e572567dbf4ddb59faf54ba5f30aa259#diff-824082aab0a95adb8b632a5c383dd6ffb733b0899d4ba594a8380882467eba82"target="_blank" rel="noopener">neste commit</a> e ele quebrou um único arquivão em todos estes aqui:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-25-55.jpg" alt="application.js refactoring"  loading="lazy" /></p>
<p>Até os temas: estava tudo num único arquivo!! Esse é o nível que eu chamaria de &ldquo;estagiário&rdquo;. Como tudo &ldquo;funciona&rdquo;, vai só acumulando código, tudo no mesmo lugar, e deixa pra se preocupar com isso depois. Todo estagiário faz isso no começo se ninguém ensinar, e a LLM, pelo visto, faz do mesmo jeito. Ele não tenta fazer nada proativamente que você não pediu explicitamente. De fato, eu nunca disse no prompt <em>&ldquo;lembre-se de ir quebrando em pequenos arquivos&rdquo;</em>, mas deveria.</p>
<p>Mas esse não foi o pior. Não, senhor, <a href="https://github.com/akitaonrails/FrankMD/commit/e35f6eb738b218caba1340723d016633d8ead99f"target="_blank" rel="noopener">neste commit</a> está todo o Javascript, NUM ÚNICO ARQUIVO. Eu acho que já tinha passado de <strong>4 mil ou 5 mil linhas</strong>. Olha só em quantos arquivos ele quebrou, nem cabe tudo nesta foto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-29-19.jpg" alt="app_controller.js refactoring"  loading="lazy" /></p>
<p>O problema é que eu mesmo caí no que estou criticando aqui. Eu pedia pra adicionar uma nova funcionalidade, dava &ldquo;reload&rdquo; no navegador e <em>voilá</em> lá estava a funcionalidade nova! &ldquo;FUNCIONANDO&rdquo;!! (Ruby on Rails é muito produtivo!)</p>
<p>E assim fui, adicionando &ldquo;funciona já&rdquo;, adicionando, &ldquo;funciona já&rdquo;, quando fui ver, <strong>show de horror</strong>!!</p>
<p>No último refactoring estávamos na marca de umas 9 horas de trabalho. Aqui já terminamos o dia; estamos no dia seguinte e, tirando o tempo que eu dormi, já estamos em <strong>17 horas</strong>, 70 commits. Mais ou menos na METADE do projeto.</p>
<blockquote>
  <p>Tudo isso pra dizer o seguinte: a MAIOR parte de Vibe Coding, sempre vai ser REFATORAÇÃO e consertos de bugs e adicionar TESTES!</p>

</blockquote>
<h3>I18n - Internacionalização<span class="hx:absolute hx:-mt-20" id="i18n---internacionalização"></span>
    <a href="#i18n---internacionaliza%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Eu me adiantei um pouco; antes do mega-refactoring de Javascript, ainda tinha feito outro mega-refactoring antes: pedi pra ele extrair todas as strings em inglês e já me traduzir uma opção em português do Brasil. Mais um <a href="https://github.com/akitaonrails/FrankMD/commit/9aee048bcaaafbf83480c5c272032b5bb2941ac5"target="_blank" rel="noopener">commit gigante</a> de mais de <strong>1600 linhas</strong> modificadas.</p>
<p>Quis fazer uma seção separada só pra comentar isso porque este é um dos pontos em que eu acho que LLMs brilham. Eles foram inicialmente feitos justamente pra lidar com linguagem natural e traduções. LLMs são muito bons em línguas. Antigamente eu precisaria passar pelo Google Tradutor, que sempre foi bem ruim, e depois achar alguém nativo da língua pra ajustar termos que o Google traduziu errado.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-38-24.jpg" alt="i18n"  loading="lazy" /></p>
<p>Como esperado, depois de extrair tudo, mandar ajustar os testes pra levar i18n em consideração e ver que o português estava funcionando, saí adicionando outras de brincadeira e fiquei muito satisfeito com o resultado. I18n é definitivamente algo que não faz sentido fazer sem LLM hoje em dia. É de ordens de grandeza melhor do que o jeito antigo.</p>
<h2>A Última METADE<span class="hx:absolute hx:-mt-20" id="a-última-metade"></span>
    <a href="#a-%c3%baltima-metade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Do último grande refactoring até o fim, são mais uns 70 commits e aproximadamente 14 horas, o fim do segundo e do 3º dia, com intervalo de umas 15 horas no meio onde eu fiquei com febre e tive que me jogar na cama e me entupir de remédios… mas tudo bem, na manhã seguinte melhorei um pouco e aí terminei!</p>
<p>Daqui em diante, fui adicionando o resto das features, como emoji, emoticons, find and replace dialog, jump to line dialog, ajustes no sistema de configuração (via envs, via .fed) e Docker.</p>
<p>Uma coisa que não falei antes, mas que veio antes do esforço de I18n, foi um <strong>REBRANDING</strong>. Isso é outra coisa em que LLMs são muito boas pra resolver: substituição de texto, sabendo diferenciar textos, variáveis e código em geral. Eu não tava muito inspirado e, no começo, ia fazer um editorzinho web MUITO mais simples do que o que acabou ficando, e ia chamar só de &ldquo;WebNotes&rdquo; mesmo.</p>
<p>Mas quando cresceu e decidi que valia a pena compartilhar publicamente, resolvi dar um nome de verdade pra ele. Daí nasceu &ldquo;FrankMD&rdquo;. Só que não basta só dar nome; agora tem que sair renomeando TUDO dentro do código, strings, documentação, etc. Foi <a href="https://github.com/akitaonrails/FrankMD/commit/4d370f7cbae145fe6c28c6173c723fccfb818ffe"target="_blank" rel="noopener">neste commit</a>: umas boas <strong>700 linhas</strong> de modificações.</p>
<h3>Agora &ldquo;funciona??&rdquo;<span class="hx:absolute hx:-mt-20" id="agora-funciona"></span>
    <a href="#agora-funciona" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Estou escrevendo este artigo às 17h de domingo. Às 19:25 de sábado, mandei no Discord pra uns amigos testarem, inocentemente achando que já tinha chegado à versão 1.0.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-50-30.jpg" alt="discord"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_17-54-03.jpg" alt="issues"  loading="lazy" /></p>
<p>E lá vou eu, com febre precisando deitar, mas querendo chegar no 1.0!!</p>
<p>As issues em si matei rápido, mas <strong>Syntax Highlight</strong> eu não sabia que ia virar um inferno na minha vida. A primeira versão saiu rapidinho, <a href="https://github.com/akitaonrails/FrankMD/commit/e063a0bb3b6f3a1ab927ca335d06fe8eb7946d25"target="_blank" rel="noopener">neste commit</a></p>
<h3>Não Reinvente a Roda (duh)<span class="hx:absolute hx:-mt-20" id="não-reinvente-a-roda-duh"></span>
    <a href="#n%c3%a3o-reinvente-a-roda-duh" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Sim, é o clichê mais óbvio de todos, mas até eu cometo esse erro. Como falei no começo, minha expectativa com esse projeto era ser pouco mais do que um &ldquo;Notepad&rdquo; Web pras minhas anotações, e acabou virando o editor principal pro meu blog.</p>
<p>A parte do editor de textos começou com a primeira coisa que funciona primeiro: um mesmo textarea mesmo.</p>
<p>Mas no sábado eu queria colocar uma coluna de contador de linhas, que leva em consideração o wrapping das linhas, controle desse wrapping, modo typewriter sempre centralizado no meio da tela e sincronização do scroll do editor de texto com o preview.</p>
<p>Sem contar todos os dialog-boxes que adicionam coisas ao texto, como imagens, vídeos, código e tabelas. Sem contar os shortcuts pra coisas como bold, italic. Sem contar rotinas de auto-save, estatísticas de contagem de linha, linha atual, etc.</p>
<p>Pra quem não sabe, o problema de syntax highlight é que não dá pra editar direto no texto colorido (porque ele tem tags escondidas que formatam o texto). Uma das formas de resolver isso é duplicar o bloco de texto: um no background, que é o texto cru e você pode editar como quiser, e outro bloco formatado e colorido com html exatamente em cima, que você não mexe, mas ele atualiza com o que for editado. Foi assim que o Claude fez.</p>
<p>Mas tem UM MONTE de &ldquo;edge cases&rdquo;: problemas de alinhamento por conta de wrapping, problemas com tamanho e posição pra ajustar o sync do auto-scroll. Levei horas e horas brigando com isso. Olha o tipo de problema que eu estava lidando:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201183500_screenshot-2026-02-01_12-14-45.png" alt="syntax offset bug"  loading="lazy" /></p>
<p>Tudo se resolveu quando decidi: chega. Estou reinventando a roda. Obviamente esse problema já foi resolvido. Então pedi ao Claude pra remover toda a lógica ligada ao textarea customizado e substituir por <a href="https://codemirror.net/"target="_blank" rel="noopener"><strong>Code Mirror</strong></a>, que é um componente open source pra editor de código, com suporte a contagem de linha, syntax highlight e muito mais!</p>
<p><a href="https://github.com/akitaonrails/FrankMD/commit/d0d42c9b57a9d19d904023951e6b46d9a71fcba8"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_18-03-47.jpg" alt="codemirror refactoring"  loading="lazy" /></a></p>
<p>Não foram só esses 5 commits; foram os principais. Levaram umas boas <strong>3 horas</strong> ou mais pra estabilizar esse código e múltiplos ajustes pequenos ao longo de mais commits. Umas boas <strong>6.000 linhas de código modificadas</strong>. É o tipo de refactoring que, num projeto real, teria levado a semana inteira e, ainda assim, teria saído com vários bugs e efeitos colaterais. Mas, pra isso, a LLM também é muito boa. Grandes refactorings, <strong>especialmente se você preparou uma boa suite de testes automatizados</strong>, vão bastante bem com LLMs.</p>
<blockquote>
  <p>Isso é o tipo de coisa que acontece em projetos de verdade. Não posso culpar a LLM, é totalmente culpa de eu ter mudado os requerimentos do projeto: originalmente não ia ter syntax highlight!</p>

</blockquote>
<h3>Testes, testes, testes<span class="hx:absolute hx:-mt-20" id="testes-testes-testes"></span>
    <a href="#testes-testes-testes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>As principais recomendações de como fazer vibe coding são as mesmas de como programar do jeito antigo:</p>
<ul>
<li>Cuidado com a escolha de arquitetura (sim, exige experiência)</li>
<li>Faça pequenas funcionalidades de cada vez, cada uma com abstrações corretas e imediatamente com testes</li>
<li>Refatore de tempos em tempos pra consolidar ou abstrair código nos domínios corretos, ajustando e criando mais testes</li>
</ul>
<p>Se seguir esses 3 itens, já ajuda muito. Não é mágica: tudo isso são ferramentas que dão feedback à LLM pra ela saber o que fazer.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201183748_screenshot-2026-01-31_03-33-13.png" alt="tests"  loading="lazy" /></p>
<p>Se você é aquele tipo de cliente anta que só fala <em>&ldquo;ah, parou de funcionar&rdquo;</em> - você não serve pra falar com um programador, e sinto dizer que LLMs não vão te ajudar também:</p>
<ul>
<li>O &ldquo;QUE&rdquo; parou de funcionar?</li>
<li>Tudo parou de abrir?</li>
<li>Somente um elemento desapareceu?</li>
<li>O elemento aparece, mas ao clicar não acontece nada?</li>
<li>Dá pra clicar, mas aparece uma mensagem de erro?</li>
<li>Que mensagem exatamente?</li>
<li>O que você fez antes disso?</li>
<li>Mudou alguma coisa do teste anterior pra este?</li>
<li>etc</li>
</ul>
<p>TUDO ISSO e mais tem que estar no próximo prompt pra correções. Incluindo logs do servidor, linhas de comando pra reproduzir o erro e toda a evidência que puder ser produzida, pra facilitar o entendimento do erro.</p>
<blockquote>
  <p>Não existe <em>&ldquo;parou de funcionar&rdquo;</em> — sua cabeça que nunca funcionou.</p>

</blockquote>
<p>Uma coisa de que gostei muito do Claude foi quando apareceu um bug que antes não existia, mas não sabia se tinha sido no último commit ou em uns 3 commits pra trás. E em vez de chutar coisas aleatórias, o próprio Claude ofereceu pra fazer, manualmente, o equivalente a um <a href="https://git-scm.com/docs/git-bisect"target="_blank" rel="noopener">git bisect</a> e foi me oferecendo um commit de cada vez pra testar e continuar até acharmos em qual o erro foi introduzido:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201183240_screenshot-2026-02-01_15-05-30.png" alt="bisect"  loading="lazy" /></p>
<h3>Otimizações e Checagens<span class="hx:absolute hx:-mt-20" id="otimizações-e-checagens"></span>
    <a href="#otimiza%c3%a7%c3%b5es-e-checagens" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ao longo do projeto, umas 2 ou 3 vezes eu parei especificamente pra checar coisas como leaks de memória e buracos de segurança. Mesmo sendo um projeto pessoal, eu não queria buracos muito óbvios.</p>
<p>Já fiquem avisados de que Claude é campeão em fazer um monte de setTimeout em Javascript e não dar <a href="https://github.com/akitaonrails/FrankMD/commit/039f3f8d72b0bf8facd5a1f85f54523bbfc12b53"target="_blank" rel="noopener">clearTimeout</a> em nada!</p>
<p>Não é muita coisa, mas eu tive mais de <a href="https://github.com/akitaonrails/FrankMD/commit/afb669f9b96baef05bae02cd76a9731353f6eac4"target="_blank" rel="noopener">um commit</a> pra apagar <strong>&ldquo;Dead Code&rdquo;</strong>, que é código morto: código que, numa refatoração, foi movido pra outro módulo, mas se esqueceu de apagar da origem. As LLMs sempre vão deixar sobrando dead code, porque não têm como olhar 100% de todos os arquivos 100% do tempo. Eventualmente ele perde uma coisa ou outra. E não vai causar nenhum bug óbvio. Tem que ir tirando aos poucos, depois de cada refatoração.</p>
<p>Quando comecei a testar de verdade, com textos longos, notei que a interface estava com &ldquo;lag&rdquo;, meio devagar. Duas coisas pareciam estar afetando: a contagem estatística de quantas palavras e caracteres o texto tem estava muito agressiva.</p>
<blockquote>
  <p>Novamente &ldquo;Funciona já&rdquo; &hellip; mas devagar &hellip;</p>

</blockquote>
<p>Pior, quando mandei criar o editor de texto pela primeira vez, não disse exatamente como queria que fosse o &ldquo;auto-save&rdquo;. Então ele fez do jeito mais conservador, agressivo e lento de todos: pra mandar salvar tudo a cada tecla digitada!!!</p>
<p>Tinha <a href="https://github.com/akitaonrails/FrankMD/commit/76406744a3e268d7b96648a2a41c415e3781c1a4"target="_blank" rel="noopener">várias outras coisas</a> como não atualizar o Preview HTML se não estiver aberto e coisas assim.</p>
<blockquote>
  <p>LLMs não vão fazer o código mais performático logo de cara; você precisa pedir como!</p>

</blockquote>
<p>Performance é sempre um trade-off. Por exemplo, se eu deixar o mais rápido possível, o resto pode acabar super inseguro. Se eu aumentar a segurança demais, derruba a usabilidade. Se aumentar demais a otimização, derruba a manutenibilidade do código. Não existe como fazer tudo perfeito: um sempre vai afetar negativamente o outro e só um programador experiente sabe como escolher (ou ir na tentativa e erro até acertar, pro caso específico).</p>
<blockquote>
  <p>De novo, não é &ldquo;culpa&rdquo; da LLM; isso é normal em qualquer projeto e temos que ficar o tempo todo ajustando essas coisas.</p>

</blockquote>
<h3>Agora sim, chegando ao fim<span class="hx:absolute hx:-mt-20" id="agora-sim-chegando-ao-fim"></span>
    <a href="#agora-sim-chegando-ao-fim" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ainda sobrou muito débito técnico. O arquivo <a href="https://github.com/akitaonrails/FrankMD/blob/master/app/javascript/controllers/app_controller.js"target="_blank" rel="noopener">app_controller.js</a> ainda tem mais de <strong>2.000 linhas</strong>. Isso já é DEPOIS que eu gastei umas boas 3 horas refatorando só ele. Agora tem aproximadamente 28 controllers em JavaScript, mas antes tinha somente um arquivo.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201183730_screenshot-2026-01-31_11-05-30.png" alt="app controller"  loading="lazy" /></p>
<p>Além disso, as LLMs não conseguem ser consistentes o tempo todo, especialmente quando o código do projeto aumenta muito. Teve um tipo de funcionalidade que ele sempre fazia server-side, mas depois resolveu fazer client-side. Eu tive que interromper e falar: <em>&ldquo;ow, por que?&rdquo;</em>, daí ele refez do jeito certo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201183531_screenshot-2026-02-01_10-49-54.png" alt="interrupt"  loading="lazy" /></p>
<p>Todo código feito sem assistência humana nenhuma SEMPRE sai na pior qualidade de código possível. Com minha assistência, acho que o código está minimamente apresentável. Vocês mesmos podem checar no <a href="https://github.com/akitaonrails/FrankMD"target="_blank" rel="noopener">meu repositório no GitHub</a>.</p>
<p>Recomendo que fucem os commits: pelo menos uma coisa que as LLMs fazem bem é documentar o histórico do que foi modificado naquele commit, o que facilita muito pra pesquisar depois.</p>
<p>Teve um bug em que o Claude ficou em loop, fazendo tentativa e erro, pra descobrir quando meu CSS quebrou do nada. Ele mudava alguma coisa e me mandava testar; eu reiniciava o server, dava force reload e nada: quebrado.</p>
<p>Era sábado à noite; eu estava cansado e meio febril, mas aí que me caiu a ficha. Eu tinha feito uma besteira antes. Coloquei &ldquo;notes/&rdquo; no <code>.gitignore</code>, que era o diretório onde estava criando arquivos Markdown de teste no editor e não queria que commitasse.</p>
<p>Porém, isso também passou a ignorar diretórios como <code>app/views/notes/...</code>, por exemplo. E nem eu nem o Claude sabíamos disso, mas quando mandei ele pesquisar, voilá:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260201184244_screenshot-2026-01-30_19-55-28.png" alt="tailwind error"  loading="lazy" /></p>
<blockquote>
  <p>Tailwind build obedece ao <code>.gitignore</code> então tava cagando a compilação de CSS. &#x1f92c;</p>

</blockquote>
<p>Quando consertamos isso, passou a funcionar, mas aí já passaram 2 horas!</p>
<p>Como falei, você precisa estar esperto nos erros, ver por que a LLM tá fazendo tanta tentativa e erro, fazendo e desfazendo no mesmo lugar e não chegando a lugar algum. Isso não é incomum e pode passar metade do seu dia nisso, se não prestar atenção.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Quando comecei a fazer isso sexta de manhã, imaginei:</p>
<blockquote>
  <p><em>&ldquo;Ah, um dia inteiro de trabalho deve ser suficiente.&rdquo;</em></p>

</blockquote>
<p>E como sempre, toda estimativa é subestimada. Agora já é domingo, fim do dia, e só agora eu acho que cheguei perto da versão 1.0. Ainda não está perfeito, mas acho que está minimamente apresentável.</p>
<p>Lembrando que eu fiz isso no meu ritmo de <strong>extremo hiperfoco</strong>, quando eu quase nem pisco, meus olhos ressecam e começo a enxergar tudo embaçado, até a cabeça começa a doer um pouco de tanto ficar tenso, concentrado numa coisa só. Não é um estado que eu recomendo.</p>
<p>Mesmo essas 30 horas em 3 dias, o correto seria 30 horas ao longo de, pelo menos, 5 dias.</p>
<p>Se eu tivesse deixado o Claude fazer tudo sozinho, sem checar nada, teria levado metade do tempo e, no final, teria um código &ldquo;que funciona&rdquo;, mas inusitado, com um único arquivo css de umas 2000 linhas, um único arquivo JS de umas 8 mil linhas, tudo com ZERO testes e tudo parecendo um castelo de cartas onde se mexe numa coisa pequena e quebra 10 outras coisas sem saber por quê.</p>
<p>No geral, foi um bom experimento. Sabendo como e onde usar, LLMs são excepcionais. Como disse no começo, sem a LLM eu teria levado mais de 200 horas pra fazer sozinho. Mesmo sofrendo vários perrengues no meio do caminho, levei só 30 horas. Então a LLM realmente me fez trabalhar pelo menos 6 a 7 vezes mais rápido, talvez mais.</p>
<p>Finalmente eu sinto que chegamos ao elusivo <strong>&ldquo;Developer 10x&rdquo;</strong>. Basta que você seja sênior primeiro.</p>
<p>Pra acabar, um mini-relatório do <a href="https://github.com/XAMPPRocky/tokei"target="_blank" rel="noopener">Tokei</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Breakdown by language:
┌─────────────────┬───────┬────────┬────────┐
│    Language     │ Files │ Lines  │  Code  │
├─────────────────┼───────┼────────┼────────┤
│ JavaScript      │ 137   │ 26,621 │ 19,922 │
├─────────────────┼───────┼────────┼────────┤
│ Ruby            │ 60    │ 8,449  │ 5,883  │
├─────────────────┼───────┼────────┼────────┤
│ YAML            │ 10    │ 3,477  │ 2,867  │
├─────────────────┼───────┼────────┼────────┤
│ Ruby HTML (ERB) │ 33    │ 2,927  │ 2,634  │
├─────────────────┼───────┼────────┼────────┤
│ CSS             │ 29    │ 1,619  │ 1,297  │
├─────────────────┼───────┼────────┼────────┤
│ Markdown        │ 1     │ 716    │ -      │
├─────────────────┼───────┼────────┼────────┤
│ HTML            │ 5     │ 369    │ 329    │
├─────────────────┼───────┼────────┼────────┤
│ Dockerfile      │ 1     │ 88     │ 46     │
├─────────────────┼───────┼────────┼────────┤
│ Other           │ 6     │ 145    │ 76     │
└─────────────────┴───────┴────────┴────────┘
The codebase is primarily JavaScript (60%) and Ruby (17%) by code volume.</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Portanto, aproximadamente <strong>33 mil linhas de código</strong>. Isto é um projeto PEQUENO! Projetos grandes são 10 vezes maiores, com mais de 300 mil linhas de código. Eu chutaria que num iFood ou Mercado Livre da vida, com todos os subsistemas, é mais de 1 milhão de linhas de código. Arrumem suas expectativas!</p>
]]></content:encoded><category>frankmd</category><category>frank</category><category>llms</category><category>inteligencia-artificial</category></item><item><title>Vibe Code: Fiz um Editor de Markdown do zero com Claude Code (FrankMD) PARTE 1</title><link>https://akitaonrails.github.io/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-part-1/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-part-1/</guid><pubDate>Sun, 01 Feb 2026 10:40:43 -0300</pubDate><description>&lt;p&gt;Nos últimos &lt;a href="https://akitaonrails.com/2026/01/28/vibe-code-eu-fiz-um-appzinho-100-com-glm-4-7-tv-clipboard/"target="_blank" rel="noopener"&gt;dois&lt;/a&gt; &lt;a href="https://akitaonrails.com/2026/01/29/vibe-code-qual-llm-%c3%a9-a-melhor-vamos-falar-a-real/"target="_blank" rel="noopener"&gt;posts&lt;/a&gt; eu só quis mesmo maltratar as LLMs pra mostrar a dificuldade que é fazer algo simples do zero, quem dirá &lt;em&gt;&amp;ldquo;fazer um GTA 6 do zero&amp;rdquo;&lt;/em&gt;, como alguns idiotas acreditam.&lt;/p&gt;
&lt;p&gt;Eu falo isso faz anos: eu GOSTO das LLMs, uso bastante desde que foram lançadas e nunca tive problemas com elas. Meu problema é só balizar as expectativas IRREAIS dos &amp;ldquo;normies&amp;rdquo;, não-programadores: nunca vai sair um app como este com poucos prompts e sem saber exatamente os componentes e técnicas pra usar por baixo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Nos últimos <a href="https://akitaonrails.com/2026/01/28/vibe-code-eu-fiz-um-appzinho-100-com-glm-4-7-tv-clipboard/"target="_blank" rel="noopener">dois</a> <a href="https://akitaonrails.com/2026/01/29/vibe-code-qual-llm-%c3%a9-a-melhor-vamos-falar-a-real/"target="_blank" rel="noopener">posts</a> eu só quis mesmo maltratar as LLMs pra mostrar a dificuldade que é fazer algo simples do zero, quem dirá <em>&ldquo;fazer um GTA 6 do zero&rdquo;</em>, como alguns idiotas acreditam.</p>
<p>Eu falo isso faz anos: eu GOSTO das LLMs, uso bastante desde que foram lançadas e nunca tive problemas com elas. Meu problema é só balizar as expectativas IRREAIS dos &ldquo;normies&rdquo;, não-programadores: nunca vai sair um app como este com poucos prompts e sem saber exatamente os componentes e técnicas pra usar por baixo.</p>
<p>Desta vez, decidi resolver outro probleminha que tava no meu todo-list faz tempo: ter um web app de anotações self-hosted (pra colocar no meu <a href="/tags/homelab/">home-server</a>. Ao longo dos anos, já usei de tudo: Evernote, Notion, Obsidian, entre outros, mas nenhum deles realmente me agrada. Então, por que não fazer o meu próprio exatamente como eu quero?</p>
<p>Vou dividir este tema em 2 artigos: neste primeiro, vou falar exclusivamente sobre o meu aplicativo; <a href="/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-parte-2/">no segundo</a>, vou falar sobre o processo de vibe codar com o Claude. Spoilers: levou 3 dias inteiros (aproximadamente <strong>30 horas</strong>). 137 pull requests! Aguardem o post-mortem.</p>
<h2>O Problema<span class="hx:absolute hx:-mt-20" id="o-problema"></span>
    <a href="#o-problema" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No geral, é só frescura minha mesmo. Guardar anotações não é pra ser complicado: cria um diretório aí na sua máquina, abre o LazyVim, o VSCode ou qualquer outro, e acabou: edita os Markdown em código puro e seja feliz.</p>
<p>Enfim, recentemente estava usando apenas o Obsidian num diretório compartilhado no meu Dropbox. Assim, eu poderia acessar do meu celular, se precisasse. Mas é um saco porque se eu estiver em outro PC, preciso ter o Obsidian ou outro editor instalado. Se tiver plugins, também preciso instalar.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_11-43-09.jpg" alt="obsidian"  loading="lazy" /></p>
<p>Na prática, qualquer outro editor de textos serviria: são só arquivos de texto, mas a inconsistência de ter algo diferente em cada lugar e desconfigurado me desmotiva a escrever.</p>
<p>Em particular, estava usando Obsidian e, depois, LazyVim pra editar os posts do meu blog, que também são em Markdown. E aqui tenho um conjunto maior de problemas:</p>
<ul>
<li>tem path específico e frontMatter do Hugo; tomar cuidado com o título pra gerar o slug correto, etc.</li>
<li>tem imagens/screenshots que fico manualmente subindo no S3, pegando a URL e colando no post</li>
<li>evito usar tabelas porque é um saco &ldquo;desenhar&rdquo; tabelas direto em Markdown</li>
<li>fico com abas de Google Images, youtube, etc aberta pra poder ficar pegando links, alt-tab, e colando</li>
<li>não tem um bom preview, então eu deixo passar muito errinho (url de imagem duplicada, por exemplo)</li>
<li>não tem um bom corretor ortográfico, então passa muito errinho que depois tenho que ficar corrigindo</li>
</ul>
<p>Eu queria resolver esses problemas tudo num lugar só: um editor bonito como o Obsidian, que resolvesse todos esses inconvenientes e me deixasse só focar no meu texto.</p>
<h2>FrankMD<span class="hx:absolute hx:-mt-20" id="frankmd"></span>
    <a href="#frankmd" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Frank Rosenblatt foi o criador do Perceptron em 1957, o que deu origem às redes neurais modernas. Como eu fiz este editor totalmente com IA, resolvi homenagear o Frank.</p>
<p>Por isso, nomeei de Frank Markdown ou Frank Editor (fed). Este editor tem todo o básico que você esperaria em qualquer editor:</p>
<ul>
<li>syntax highlighting</li>
<li>HTML preview</li>
<li>facilitadores pra formatar markdown (Ctrl-B pra bold, por exemplo)</li>
<li>customizar família e tamanho de fontes</li>
</ul>
<p>Mas aí eu fui adicionando algumas coisas específicas do meu gosto pessoal e/ou que facilitariam minha vida pra editar os posts do meu blog. Vamos lá.</p>
<h3>Inserção de Imagens<span class="hx:absolute hx:-mt-20" id="inserção-de-imagens"></span>
    <a href="#inser%c3%a7%c3%a3o-de-imagens" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Esta é uma parte importante pra mim. Quando faço posts sobre código, sempre fico tirando fotos de tela do meu terminal ou do editor pra ilustrar. O processo é um print screen que manda pra <code>~/Pictures/Screenhots</code>. Eu abro o Nautilus (estou no Linux) e executo um scriptzinho que fiz pra subir essa imagem num bucket no meu AWS S3. Copio o link gerado e adiciono ao meu post.</p>
<p>É &ldquo;chatinho&rdquo; toda vez fazer assim. Além do mais, às vezes eu gostaria de redimensionar a imagem pra ficar mais otimizada. Mas isso significa ir ao terminal e rodar <code>magick screenshot.png -resize 50%</code>, o que é mais um passo manual.</p>
<p>Então decidi fazer um dialog-box com várias opções pra isso.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-03-02.jpg" alt="image dialog"  loading="lazy" /></p>
<p>Por padrão, posso configurar um diretório principal de imagens no meu PC e ele vai exibir um grid limitado a 10 dos mais recentes. Ou posso filtrar por nome. Tem opção pra linkar diretamente (caso eu queira subir a imagem junto com o html) ou servir do S3 e ele vai copiar o link gerado automaticamente (que é o que prefiro).</p>
<p>Mesma coisa pra imagens na Web (Google, Pinterest), onde posso escolher fazer download, redimensionar e subir no S3:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-06-07.jpg" alt="bing images example"  loading="lazy" /></p>
<p>E o link vai ser no formato esperado <code>...amazonaws.com/frankmd/...jpg</code>. Tudo bonito, fácil e prático! Pra mim, isso ajuda muito!</p>
<p>É um extra, já que não poderia deixar de integrar uma IA; a última aba é pro <a href="https://gemini.google/br/overview/image-generation/?hl=pt-BR"target="_blank" rel="noopener">Nano Banana</a>:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-10-24.jpg" alt="nano banana generator"  loading="lazy" /></p>
<p>Nano Banana é bem impressionante mesmo. olha o que esse prompt gerou:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/ai_1769965759787.png" alt="nano banana generated image"  loading="lazy" /></p>
<p>Mais importante ainda: posso ver o preview imediatamente (ctrl-shift-v) pra saber se inseri a imagem correta (coisa que eu errava muito editando pelo NeoVim, sem ver):</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-13-29.jpg" alt="real time preview"  loading="lazy" /></p>
<p>Pra mim, estes assistentes de inserção de imagem são os que vão me ajudar mais pra editar meus posts no blog.</p>
<h3>Facilitadores de Markdown<span class="hx:absolute hx:-mt-20" id="facilitadores-de-markdown"></span>
    <a href="#facilitadores-de-markdown" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Mais simples que uma imagem é embedar um player de vídeo. Posso ou passar um link qualquer com um arquivo .mp4 da vida, ou posso diretamente pesquisar no YouTube e embedar corretamente:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-12-34.jpg" alt="youtube searching"  loading="lazy" /></p>
<p>Ele vai inserir o HTML correto, <code>&lt;embed ...</code>, com a URL e o título corretos.</p>
<p>Também fiz um jeito fácil de adicionar e editar tabelas:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-28-00.jpg" alt="spreadsheet editor"  loading="lazy" /></p>
<p>E vai gerar o Markdown direitinho. Nunca mais preciso ficar editando essa porcaria na mão pra reajustar colunas ou linhas.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">| Cabeçalho 1 | Cabeçalho 2 |
</span></span><span class="line"><span class="cl">| ----------- | ----------- |
</span></span><span class="line"><span class="cl">| Linguagem   | Nota        |
</span></span><span class="line"><span class="cl">| Ruby        | 10          |
</span></span><span class="line"><span class="cl">| JS          | 5           |
</span></span><span class="line"><span class="cl">| Rust        | 6 😂        |</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mas se quiser fazer manualmente, também tem como:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-28-40.jpg" alt="code editor"  loading="lazy" /></p>
<p>Isso eu coloquei mais porque ficaria estranho omitir, mas, em geral, prefiro escrever blocos de código manualmente mesmo. Mas tá aí pra quem quiser um modo visual.</p>
<p>Daí, claro, tinha que ter um jeito fácil de editar Emojis &#x1f606; (ctrl-shift-e):</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-30-19.jpg" alt="emoji panel"  loading="lazy" /></p>
<p>Mas o que ninguém esperaria é que eu também oferecesse suporte a <strong>Emoticons</strong>!!
☆:.｡.o(≧▽≦)o.｡.:*☆</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-30-40.jpg" alt="emoticon panel"  loading="lazy" /></p>
<h3>Customização<span class="hx:absolute hx:-mt-20" id="customização"></span>
    <a href="#customiza%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Falando em Emoticons, me lembra que eu quis fazer este editor ser infinitamente customizável. Por exemplo, claro que adicionei internacionalização e tem japonês!!</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-34-30.jpg" alt="japanese locale"  loading="lazy" /></p>
<p>E mais outras línguas também; deem uma fuçada depois. Tem português também!</p>
<blockquote>
  <p>&ldquo;Qual a melhor fonte pra programação?&rdquo;</p>

</blockquote>
<p>Sei lá! Por isso adicionei várias pra escolher:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_15-22-35.jpg" alt="fonts"  loading="lazy" /></p>
<p>Como uso <a href="/tags/omarchy/">Omarchy</a> então também queria que meu editor tivesse suporte a exatamente os mesmos temas:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-37-05.jpg" alt="temas"  loading="lazy" /></p>
<p>No <a href="https://github.com/akitaonrails/FrankMD/blob/master/README.md"target="_blank" rel="noopener">README</a> tem documentado tudo o que dá pra customizar. Dá pra customizar várias coisas via variável e ambiente e também via um arquivo <code>.fed</code>, criado automaticamente no diretório onde se abre este editor. Tem bastante coisa, então recomendo fuçar.</p>
<h3>Foco no Texto<span class="hx:absolute hx:-mt-20" id="foco-no-texto"></span>
    <a href="#foco-no-texto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Obviamente eu incluí um <em>&ldquo;Typewriter Mode&rdquo;</em> que mantém meu cursor mais perto do meio da tela e centraliza o editor e, ao mesmo tempo, esconde todas as distrações.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-17-37.jpg" alt="typewriter mode"  loading="lazy" /></p>
<p>Eu normalmente prefiro TUIs, mas tenho que admitir que usar um navegador tem a vantagem de me dar acesso a extensões. A mais útil sem dúvida é <a href="https://app.grammarly.com/"target="_blank" rel="noopener">Grammarly</a>, pra qualquer língua:</p>
<p><a href="https://app.grammarly.com/"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-20-04.jpg" alt="grammarly"  loading="lazy" /></a></p>
<p>Mas, além disso, eu adicionei um botão de &ldquo;IA&rdquo; no toolbar (que é configurável via ENVs e um arquivo de configuração — leia o <a href="https://github.com/akitaonrails/FrankMD/blob/master/README.md"target="_blank" rel="noopener">README</a>). E ele faz isto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-22-28.jpg" alt="AI grammar check"  loading="lazy" /></p>
<p>Manda pro seu LLM de preferência (Ollama local, OpenAI, Anthropic, OpenRouter, etc.) com um prompt pra não reescrever seu texto, somente checar gramática, ortografia e erros desse tipo. Daí eu devolvo uma interface de &ldquo;diff&rdquo; pra comparar e aceitar, editar ou cancelar.</p>
<p>Eu acho que o Grammarly sozinho já é suficiente, mas não custava nada fazer esse, então fiz.</p>
<h3>Hugo Blogging<span class="hx:absolute hx:-mt-20" id="hugo-blogging"></span>
    <a href="#hugo-blogging" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Este blog é feito com Hextra/Hugo. Por isso, queria já embutir um jeito fácil de criar novos artigos no Hugo. Isso começa já na opção de criar um novo documento:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-39-53.jpg" alt="new hugo note"  loading="lazy" /></p>
<p>E ele já cria com o frontMatter, geração automática de slugs, subdiretórios timestamped e tudo mais:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_14-38-55.jpg" alt="hugo frontmatter"  loading="lazy" /></p>
<p>Como falei, este editor foi feito principalmente pra facilitar o meu workflow.</p>
<h2>Self-deploy vs CLI<span class="hx:absolute hx:-mt-20" id="self-deploy-vs-cli"></span>
    <a href="#self-deploy-vs-cli" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Originalmente pensei neste projeto pra ser um Notion-light ou algo assim. Um editor web que fizesse deploy no meu home server e servisse apenas como organizador, um lugar pra colocar anotações maiores do que um post-it (tipo histórico de configurações do meu Linux, passo a passo de comandos, etc.).</p>
<p>Mas enquanto ia construindo o app, mais e mais ele foi ficando maior e mais parecido mesmo com um desktop app. Mas aí já era tarde demais. Eu queria usar Ruby on Rails 8 (pra me desenferrujar e porque eu gosto). Mas só na metade pensei que deveria ter feito em Node, porque agora não posso mais embutir num app do Electron.</p>
<p>Tudo bem, a ideia é ser um editor de Markdown pra programadores. Todo programador tem Docker. Então encapsulei numa funçãozinho de shell e posso só chegar em qualquer diretório no meu Linux digitar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ~/Projects/akitaonrails-hugo
</span></span><span class="line"><span class="cl">fed .</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Leia o <a href="https://github.com/akitaonrails/FrankMD/blob/master/README.md#2-add-the-fed-function"target="_blank" rel="noopener">README</a> do projeto para saber como adicionar ao seu <code>.bashrc</code>. E fazendo assim, vai funcionar no meu Omarchy igualzinho a qualquer outro app nativo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_15-16-29.jpg" alt="omarchy web app"  loading="lazy" /></p>
<p>Na realidade, vou usar ambos os jeitos: como se fosse um app nativo. E como self-hosted, pra organizar um lugar central com minhas anotações — que vai ser diferente de onde organizo meus posts de blog. Então tem múltiplas utilidades mesmo.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu resumi as funcionalidades; há várias pequenas coisinhas que vocês vão descobrir à medida que começarem a usá-las. Tudo é clicável; se clicar no título do app, tem um About:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_15-21-03.jpg" alt="about"  loading="lazy" /></p>
<p>Botão direito em cima do texto selecionado tem um sub-menu de formatação; Ctrl-I funciona pra itálico; <code>Ctrl-H</code> tem &ldquo;find and replace&rdquo; básico; tem um monte de combinações de teclado pra acessar as coisas.</p>
<p>Ah é, nem falei que tem como pesquisar rapidamente arquivos por nome ou por conteúdo usando <code>Ctrl-P</code> e <code>Ctrl-Shift-F</code>:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_15-24-36.jpg" alt="search filename"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/frankmd/2026/02/screenshot-2026-02-01_15-25-59.jpg" alt="search file content"  loading="lazy" /></p>
<p>Pra garantir que tudo está funcionando, este artigo aqui já foi escrito inteiramente com o meu novo editor. E diria que, se não está perfeito (pra mim), está nos 99%. Fiquem à vontade pra abrir Issues com bugs ou features que fariam sentido — lembrando: a ideia não é substituir o VSCode e ter TUDO. Eu preciso que tenha só o SUFICIENTE.</p>
<p>Considerando que me custou <strong>3 dias de trabalho</strong> e que não escrevi nenhuma linha de código, o resultado é impressionante. Sim, editores de texto são aplicativos muito bem documentados e com vasto material de treinamento — por isso, o Claude conseguiu fazer sem problemas. Não se impressionem demais. Mas isso dito, sim, eu queria um editor customizado pro meu uso pessoal, que fosse bonito de usar, e ele conseguiu entregar, então estou satisfeito.</p>
<p>Não deixem de ver a <a href="/2026/02/01/vibe-code-fiz-um-editor-de-markdown-do-zero-com-claude-code-frankmd-parte-2/">PARTE 2</a> pros detalhes de como foi fazer isso.</p>
]]></content:encoded><category>frankmd</category><category>frank</category><category>llms</category><category>inteligencia-artificial</category></item></channel></rss>