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 surgem com frequência. Está a faltar alguma? Abra uma issue ou pergunte ao assistente na página.
Em que é que o Blume é diferente do Mintlify, do Fumadocs e de outros?
A maioria das ferramentas de documentação situa-se num de dois extremos. Plataformas geridas como o Mintlify dão-lhe um resultado polido rapidamente, mas a construção e o alojamento são o serviço deles — 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-lhe uma aplicação (um projeto Next.js ou React) que tem de montar, ligar e manter antes e depois de escrever uma única palavra.
O Blume segue um terceiro caminho: a framework é o template. Aponta-o para uma pasta de Markdown e ele gera e conduz o site inteiro — navegação, pesquisa, temas, imagens Open Graph, SEO e endpoints de IA — sem nenhuma aplicação para gerir. É totalmente open-source e auto-alojável, portanto não há serviço gerido nem dependência de fornecedor, mas também não há boilerplate para manter.
| Blume | Mintlify | Fumadocs / Nextra / Docusaurus | |
|---|---|---|---|
| Modelo | Framework de configuração zero; apenas conteúdo | Plataforma alojada | Biblioteca + aplicação que monta |
| Código-fonte | Open-source (MIT) | Núcleo fechado | Open-source |
| Alojamento | Em qualquer lado — estático ou uma função de servidor | A infraestrutura gerida deles | Em qualquer lado; você constrói e publica |
| Você mantém | O seu Markdown | O seu Markdown + configuração da plataforma | O seu Markdown + a aplicação à volta dele |
| Renderização | Astro; o tema principal não envia JS de cliente | O runtime deles | Runtime React/Next.js |
| Funcionalidades de IA | llms.txt, Markdown em bruto, Ask AI, MCP — integrados, sem serviço alojado |
Integradas (alojadas) | Traga as suas |
Vale a pena destacar algumas consequências:
- O resultado é seu. O
blume buildproduz um site simples que aloja na Vercel, Netlify, Cloudflare, S3 ou na sua própria máquina. Nada envia 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 numa aplicação Astro autónoma que continua a usar o pacoteblumequando quiser controlo total. - Rápido por omissão. O tema principal não usa React e renderiza HTML estático, por isso as páginas pontuam bem nos Core Web Vitals sem afinações. Adere às funcionalidades de servidor (Ask AI, MCP) apenas quando precisar delas.
- Configuração com segurança de tipos. O
blume.config.tse cadameta.tssão TypeScript real validado por um esquema — e não YAML com tipagem frouxa.
Consulte Porque existe o Blume para a versão mais longa.
O Blume é gratuito e open-source?
Sim — o Blume é licenciado sob MIT e é gratuito. Instala o pacote blume, mantém o seu conteúdo no seu próprio repositório e aloja a build onde quiser. Não há nível pago, nem preço por utilizador, nem conta para criar. O código-fonte está no GitHub.
Preciso de saber Astro, React ou Tailwind?
Não. Uma pasta de Markdown é um site completo — a navegação, a pesquisa e os temas são inferidos ou definidos com um punhado de tokens. Só recorre à stack subjacente quando quiser personalizar: ilhas interativas (React), substituições de componentes ou tokens de tema (Tailwind). Mesmo assim, o blume.config.ts é tipado, por isso o seu editor guia-o.
Posso usar componentes React e MDX?
Sim. Qualquer página pode ser .md ou .mdx, e o MDX permite-lhe inserir os componentes integrados sem imports. Também pode adicionar as suas próprias ilhas .tsx/.jsx — o Blume ativa automaticamente o React apenas nas páginas que as usam, para que o tema principal continue livre de JavaScript em todo o resto.
Onde o posso publicar?
Em qualquer lado. O blume build gera HTML estático por omissão, que pode servir a partir de qualquer alojamento estático ou CDN — Vercel, Netlify, Cloudflare Pages, GitHub Pages, S3 ou o seu próprio servidor. As funcionalidades exclusivas de servidor (Ask AI, o servidor MCP, renderização a pedido) mudam a build para uma função de servidor através de um adaptador para a Vercel, Node, Netlify ou Cloudflare. Consulte Publicação.
A pesquisa precisa de um serviço alojado?
Não. O Orama constrói um índice local que funciona tanto em desenvolvimento como em produção, sem nada para alojar ou pagar. Para sites muito grandes, o Pagefind está à distância de uma flag. Em qualquer dos casos, o índice é distribuído como parte do seu site.
Como personalizo o aspeto?
Comece pelos tokens de tema — cor de destaque, tipos de letra, raio e um theme.css para tudo o resto que o Tailwind consiga exprimir. Vá mais longe substituindo componentes integrados ou adicionando páginas personalizadas. Quando quiser o próprio projeto Astro, o blume eject entrega-lhe uma aplicação autónoma que continua a usar o pacote blume.
Porque é que o oxfmt / Ultracite está a colapsar as minhas diretivas?
Se formatar o seu Markdown com o Ultracite (que corre o oxlint + oxfmt) — tal como o próprio Blume faz — poderá reparar que as diretivas de contentor ficam achatadas numa única linha depois de uma passagem de formatação:
:::note
Regenerate the project with blume dev.
:::
torna-se
:::note Regenerate the project with blume dev. :::
Assim que a linha de abertura :::note é unida à prosa, deixa de ser uma diretiva, pelo que é renderizada como texto literal em vez de um callout.
Porque acontece
Isto é um bug no formatador de Markdown do oxfmt (herdado do printer de Markdown do Prettier — ver prettier/prettier#19040). Quando faz a quebra da prosa, trata as linhas de fence ::: como texto vulgar e junta-as à linha adjacente, quebrando a diretiva. Afeta todos os tipos de diretiva de contentor — :::note, :::tip, :::info, :::warning, :::danger, :::success.
Reportámos o problema a montante em oxc-project/oxc#24096; até ser corrigido lá, o patch abaixo é a solução alternativa.
A correção
Aplique um patch ao oxfmt para que preserve a quebra de linha que fica diretamente encostada a um fence :::. O Blume inclui a mesma correção no seu próprio repositório, e pode aplicá-la em qualquer projeto.
-
Guarde o patch como
patches/oxfmt@0.61.0.patch:diff --git a/dist/markdown-ZuiQU4Xe.js b/dist/markdown-ZuiQU4Xe.js index 566b9e6d27f36061d64b93736e238e871e1ee2b2..82d0595acc010807c2939fc4a1717dde887a8555 100644 --- a/dist/markdown-ZuiQU4Xe.js +++ b/dist/markdown-ZuiQU4Xe.js @@ -4875,7 +4875,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": { -
Registe-o no
patchedDependenciesdo seu gestor de pacotes. Com o Bun ou o pnpm, adicione aopackage.json:{ "patchedDependencies": { "oxfmt@0.61.0": "patches/oxfmt@0.61.0.patch" } } -
Reinstale para que o patch seja aplicado:
npm installpnpm installyarn installbun install