FAQ
Perguntas comuns sobre o Blume — como ele se compara a outras ferramentas de documentação e por que um formatador de Markdown pode colapsar as suas diretivas de callout.
Respostas para perguntas que aparecem com frequência. Falta alguma? Abra uma issue ou pergunte ao assistente na própria página.
Em que o Blume é diferente do Mintlify, do Fumadocs e de outros?
A maioria das ferramentas de documentação fica em um de dois extremos. Plataformas gerenciadas como o Mintlify entregam um resultado polido rápido, mas o build e a hospedagem são o serviço deles — você escreve dentro do sistema deles e publica na infraestrutura deles. Bibliotecas de componentes e starters como o Fumadocs, o Nextra ou o Docusaurus são open-source e flexíveis, mas entregam para você um aplicativo (um projeto Next.js ou React) que você precisa montar, conectar e manter antes e depois de escrever uma única palavra.
O Blume segue um terceiro caminho: o framework é o template. Você aponta ele para uma pasta de Markdown e ele gera e conduz o site inteiro — navegação, busca, temas, imagens Open Graph, SEO e endpoints de IA — sem nenhum app para cuidar. É totalmente open-source e você mesmo pode hospedar, então não existe serviço gerenciado nem dependência de fornecedor, mas também não existe boilerplate para manter.
| Blume | Mintlify | Fumadocs / Nextra / Docusaurus | |
|---|---|---|---|
| Modelo | Framework de configuração zero; só conteúdo | Plataforma hospedada | Biblioteca + app que você monta |
| Código-fonte | Open-source (MIT) | Núcleo fechado | Open-source |
| Hospedagem | Em qualquer lugar — estático ou uma função de servidor | A infraestrutura gerenciada deles | Em qualquer lugar; você faz o build e publica |
| Você mantém | O seu Markdown | O seu Markdown + configuração da plataforma | O seu Markdown + o app em volta dele |
| Renderização | Astro; o tema principal não envia nenhum JS de cliente | O runtime deles | Runtime React/Next.js |
| Recursos de IA | llms.txt, Markdown bruto, Ask AI, MCP — integrados, sem serviço hospedado |
Integrados (hospedados) | Traga os seus |
Vale destacar algumas consequências:
- O resultado é seu. O
blume buildgera um site simples que você hospeda na Vercel, Netlify, Cloudflare, S3 ou na sua própria máquina. Nada manda dados para casa. - Sem dependência de fornecedor, com duas saídas. O seu conteúdo é Markdown portátil, e o
blume ejecttransforma o projeto em um app Astro autônomo que continua usando o pacoteblumequando você quiser controle total. - Rápido por padrão. O tema principal não usa React e renderiza HTML estático, então as páginas vão bem nos Core Web Vitals sem ajuste nenhum. Você adota os recursos de servidor (Ask AI, MCP) só quando precisa deles.
- Configuração com segurança de tipos. O
blume.config.tse cadameta.tssão TypeScript real validado por um schema — e não YAML de tipagem frouxa.
Veja Por que o Blume existe para a versão mais longa.
O Blume é gratuito e open-source?
Sim — o Blume tem licença MIT e é gratuito. Você instala o pacote blume, mantém o seu conteúdo no seu próprio repositório e hospeda o build onde quiser. Não tem plano pago, nem preço por usuário, nem conta para criar. O código-fonte está no GitHub.
Preciso saber Astro, React ou Tailwind?
Não. Uma pasta de Markdown é um site completo — navegação, busca e temas são inferidos ou definidos com um punhado de tokens. Você só recorre à stack por baixo quando quiser personalizar: ilhas interativas (React), substituições de componentes ou tokens de tema (Tailwind). E, mesmo assim, o blume.config.ts é tipado, então o seu editor te guia.
Posso usar componentes React e MDX?
Sim. Qualquer página pode ser .md ou .mdx, e o MDX permite inserir os componentes integrados sem imports. Você também pode adicionar as suas próprias ilhas .tsx/.jsx — o Blume ativa o React automaticamente só nas páginas que as usam, então o tema principal continua livre de JavaScript em todo o resto.
Onde posso publicar?
Em qualquer lugar. O blume build gera HTML estático por padrão, que você pode servir de qualquer hospedagem estática ou CDN — Vercel, Netlify, Cloudflare Pages, GitHub Pages, S3 ou o seu próprio servidor. Os recursos exclusivos de servidor (Ask AI, o servidor MCP, renderização sob demanda) mudam o build para uma função de servidor por meio de um adaptador para Vercel, Node, Netlify ou Cloudflare. Veja Publicação.
A busca precisa de um serviço hospedado?
Não. O Orama monta um índice local que funciona tanto em desenvolvimento quanto em produção, sem nada para hospedar ou pagar. Para sites muito grandes, o Pagefind está a uma flag de distância. De qualquer forma, o índice vai junto como parte do seu site.
Como personalizo a aparência?
Comece pelos tokens de tema — cor de destaque, fontes, raio e um theme.css para tudo o mais que o Tailwind conseguir expressar. Vá além substituindo componentes integrados ou adicionando páginas personalizadas. Quando você quiser o projeto Astro em si, o blume eject entrega um app autônomo que continua usando o pacote blume.
Por que o oxfmt / Ultracite está colapsando as minhas diretivas?
Se você formata o seu Markdown com o Ultracite (que roda oxlint + oxfmt) — como o próprio Blume faz — talvez note que as diretivas de contêiner ficam achatadas em uma única linha depois de uma passagem de formatação:
:::note
Regenerate the project with blume dev.
:::
vira
:::note Regenerate the project with blume dev. :::
Uma vez que a linha de abertura :::note é unida à prosa, ela deixa de ser uma diretiva, então é renderizada como texto literal em vez de um callout.
Por que isso acontece
Isso é um bug no formatador de Markdown do oxfmt (herdado do printer de Markdown do Prettier — veja prettier/prettier#19040). Quando ele quebra a prosa, trata as linhas de fence ::: como texto comum e junta com a linha vizinha, quebrando a diretiva. Isso afeta todos os tipos de diretiva de contêiner — :::note, :::tip, :::info, :::warning, :::danger, :::success.
Reportamos o problema upstream em oxc-project/oxc#24096; até ser corrigido lá, o patch abaixo é a solução alternativa.
A correção
Aplique um patch no oxfmt para que ele preserve a quebra de linha que fica encostada em um fence :::. O Blume inclui a mesma correção no próprio repositório, e você pode aplicá-la em qualquer projeto.
-
Salve o patch como
patches/oxfmt@0.66.0.patch:diff --git a/dist/markdown-BgZGxhM2.js b/dist/markdown-BgZGxhM2.js index 859231f9387a50df16419bc22b5268c4e446ddbc..dcc0ffda6f71adb27a03e3918a3db6fc7a58ffe9 100644 --- a/dist/markdown-BgZGxhM2.js +++ b/dist/markdown-BgZGxhM2.js @@ -4872,7 +4872,43 @@ function lu(e, t, r) { case "sentence": return Oh(e, r); case "word": return t.parser !== "mdx" ? zh(e, t) : Uh(e); case "whitespace": { - let { next: a } = e, u = a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap; + let { next: a, previous: oxfmtFencePrev } = e; + // Preserve line breaks that sit directly against a `:::` container + // directive fence, so `proseWrap: "never"` keeps the opening/closing + // fence on their own lines instead of joining them into the prose (which + // breaks the directive). Ordinary prose still wraps per proseWrap. + // See prettier/prettier#19040. + let oxfmtIsFence = (w) => w != null && typeof w.value === "string" && w.value.startsWith(":::"); + // A titled directive (`:::warning[Heads up]`) parses its `[title]` as a + // linkReference between two sentence nodes at the paragraph level: the + // fence word ends the sentence before the reference, and the body's + // leading newline opens the sentence after it. So when this whitespace + // starts its sentence, climb to the paragraph and check whether the two + // preceding siblings are a (link) reference and a sentence ending in a + // `:::` fence word. + let oxfmtPrevIsTitledFence = !1; + if (oxfmtFencePrev == null && e.index === 0 && e.grandparent != null && Array.isArray(e.grandparent.children)) { + let oxfmtSibs = e.grandparent.children, oxfmtSentIdx = oxfmtSibs.indexOf(e.parent); + if (oxfmtSentIdx >= 2) { + let oxfmtLink = oxfmtSibs[oxfmtSentIdx - 1], oxfmtBefore = oxfmtSibs[oxfmtSentIdx - 2]; + let oxfmtLastWord = oxfmtBefore && oxfmtBefore.type === "sentence" && Array.isArray(oxfmtBefore.children) ? oxfmtBefore.children[oxfmtBefore.children.length - 1] : null; + oxfmtPrevIsTitledFence = oxfmtLink != null && (oxfmtLink.type === "linkReference" || oxfmtLink.type === "link") && oxfmtIsFence(oxfmtLastWord); + } + } + // The plain-markdown parser keeps a titled fence's `[title]` as literal + // words, so the whole directive is one sentence. For a newline + // whitespace, walk back to the start of its visual line within the + // sentence; a line led by a `:::` word is a fence whose break must stay. + if (!oxfmtPrevIsTitledFence && e.node.value.includes("\n") && e.parent != null && Array.isArray(e.parent.children)) { + let oxfmtLineFirst = null; + for (let oxfmtJ = e.index - 1; oxfmtJ >= 0; oxfmtJ--) { + let oxfmtSib = e.parent.children[oxfmtJ]; + if (oxfmtSib.type === "whitespace" && typeof oxfmtSib.value === "string" && oxfmtSib.value.includes("\n")) break; + oxfmtLineFirst = oxfmtSib; + } + oxfmtPrevIsTitledFence = oxfmtIsFence(oxfmtLineFirst); + } + let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtPrevIsTitledFence || oxfmtIsFence(a) ? "preserve" : a && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(a.value) && !NE(e) && !(t.proseWrap === "preserve" && RE(e)) ? "never" : t.proseWrap; return ou(e, n.value, u, !1, t); } case "emphasis": { -
Registre ele no
patchedDependenciesdo seu gerenciador de pacotes. Com o Bun ou o pnpm, adicione aopackage.json:{ "patchedDependencies": { "oxfmt@0.66.0": "patches/oxfmt@0.66.0.patch" } } -
Reinstale para que o patch seja aplicado:
npm installpnpm installyarn installbun install