Boxloom: escrevendo decks
Boxloom separa o significado da aparência. Você escreve apenas o que o conteúdo é
(uma lista de pontos, uma comparação de opções, um plano com datas). O modelo decide todas as
cores, tamanhos de fonte, margens e posições. Você nunca escreve uma cor, um tamanho ou uma coordenada
fora do único recurso de escape (shapes, último caso).
O ciclo é sempre:
boxloom schema— leia o que os modelos atuais aceitam (faça isso primeiro).- Escreva
deck.bx.md. boxloom check -format json deck.bx.md— corrija todos os erros e, em seguida, todos os avisos que puder.- Repita o passo 3 até que
"ok": truee não haja avisolayout-overflow. boxloom build -o deck.pptx deck.bx.md(flags antes do arquivo).
Os comandos são executados a partir do diretório boxloom/ do repositório:
go run ./cmd/boxloom <schema|check|build|parse> … (Go 1.24+, nenhuma outra dependência).
1. Leia o esquema primeiro
boxloom schema [-template light|dark] imprime um documento JSON. Ele é a fonte da
verdade; se este arquivo e o esquema discordarem, o esquema vence.
markdown.rules— as regras de sintaxe (lista curta; obedeça a todas).markdown.frontMatter— chaves permitidas no cabeçalho---(titleé obrigatório).templates[].blocks— os blocos cercados (:::name) que este modelo conhece, cominside(onde pode aparecer),contains(o que contém) eminChildren.templates[].subsystems— os tipos de figura (blocos de código```name), commarkdown(suas regras extras) eschema(um JSON Pointer parairSchemafornecendo todas as chaves permitidas).- Qualquer coisa não listada é um erro. Não invente nomes de blocos, atributos ou chaves.
- Os modelos são escolhidos pelo nome (
template: lightno front matter, ou-template dark). Um modelo personalizado é um arquivo de tabela de valores passado por caminho:-template ./my.json(funciona paraschema,check,build,fonts; exemploexamples/templates/forest.json). Quando o deck for construído com um modelo personalizado, executeschemaecheckcom o mesmo-template— os nomes de funções e estilos de texto vêm desse arquivo. variant(# Title {variant=…}) existe na sintaxe, mas os modelos atuais não declaram variantes (templates[].variantsestá vazio), portanto não a use.
A saída é grande. Para ver apenas o que você precisa:
go run ./cmd/boxloom schema -template light > /tmp/schema.json
python3 -c "import json;d=json.load(open('/tmp/schema.json'));print(json.dumps({'rules':d['markdown']['rules'],'blocks':d['templates'][0]['blocks'],'subsystems':[(x['name'],x['markdown']) for x in d['templates'][0]['subsystems']]},ensure_ascii=False,indent=1))"
python3 -c "import json;d=json.load(open('/tmp/schema.json'));print(json.dumps(d['irSchema']['\$defs']['gantt'],ensure_ascii=False,indent=1))" # todas as chaves gantt
2. Sintaxe em uma tela
---
title: Movendo o serviço de relatórios para o novo cluster
subtitle: Plano e opções (exemplo fictício)
author: Equipe de Plataforma
date: 2026-11-11
confidential: Interno
disclaimer: Todos os nomes e valores são fictícios
template: light
---
# Suporte do fornecedor ao cluster antigo
:::lead
O cluster antigo perde o suporte do fornecedor em março de 2028, portanto os 14 serviços nele devem migrar antes disso.
:::
- O fornecedor encerra correções de segurança em **2028-03-31**
- 14 serviços ainda estão lá; 9 podem migrar sem alterações de código
- A migração leva cerca de 6 semanas por grupo de serviços
:::source
Aviso de suporte do fornecedor de 2026-09; inventário de serviços até outubro de 2026
:::
:::notes
Notas do apresentador vão aqui. Apenas parágrafos simples e marcadores.
:::
-
# Titleinicia um slide. Mantenha o título como uma manchete curta que caiba em uma linha ("Suporte do fornecedor ao cluster antigo"); um título que quebra é avisado (title-wrap). Escreva o que o slide diz — a conclusão — como uma frase em:::leadlogo após o título (1–2 linhas). O modelo coloca o lead abaixo do título, fora do corpo. -
---não é um separador de slide (é um erro).##e cabeçalhos mais profundos são erros (um título de grupo é escrito como:::column Title, não como um subcabeçalho). -
Corpo: parágrafos, marcadores
-/1.(máximo de dois níveis),**ênfase**(apenas um tipo de ênfase),`código`,[texto](url),\no final para uma quebra de linha significativa. Sem itálico, tachado, notas de rodapé, HTML. -
Blocos cercados: abra com
:::nomee feche com o mesmo número de dois-pontos. Ao aninhar, a cerca externa recebe um dois-ponto a mais que a interna::::::compare ::::option Manter o cluster antigo :::pros - Nenhum trabalho de migração este ano ::: :::cons - Nenhuma correção de segurança após março de 2028 ::: :::: ::::option Migrar para o novo cluster :::pros - Suportado até 2033 ::: :::cons - Seis semanas de trabalho por grupo de serviços ::: :::: ::::: -
:::lead(a mensagem do slide),:::notes(notas do apresentador) e:::source(de onde os números do slide vêm) devem estar no nível superior de um slide, uma vez por slide cada.:::leadé apenas para slides de conteúdo. -
date,confidentialedisclaimerno front matter são escritos uma vez para o deck; o modelo os coloca na capa (econfidentialno rodapé). Nunca repita "fictício" / "confidencial" em uma:::noteem cada slide. -
Figuras são blocos de código cercados cujo idioma é o nome do subsistema, corpo em YAML restrito: sem âncoras/aliases (
&,*), sem tags, sem valores multilinha, sem blocos|/>; booleanos apenastrue/false; datas são stringsYYYY-MMouYYYY-MM-DD; intervalos2027-01 .. 2027-06. Coloque entre aspas qualquer valor que comece com*,&,!ou contenha": ", por exemplo,text: ["**Etapa mais lenta**", 5 dias em média]— um**…**sem aspas no início de um item de fluxo é interpretado como um alias YAML e rejeitado.
3. Escolhendo um padrão para cada conteúdo
Decida o tipo de conteúdo antes de escrever. Aplique estas regras em ordem e use a
primeira que corresponder. (Conjunto completo de regras com exemplos bons/ruins: docs/patterns.md §4.)
Disponível agora (verifique boxloom schema — mais podem ter sido adicionados):
| Conteúdo | Use | Notas |
|---|---|---|
| Uma quebra de capítulo | # Parte 2: Como migramos {type=section} | O texto do cabeçalho é o título do capítulo; nada além de :::notes pode vir em seguida. Não é um sumário — quando o deck tem duas ou mais seções, o modelo desenha a lista de capítulos com o atual destacado em cada slide de seção por si só |
| O slide diz exatamente uma frase; a evidência está em outros slides | # A migração deve ser adiantada em dois anos {type=statement} | O texto do cabeçalho é a frase (este é o único tipo de slide cujo cabeçalho é uma frase completa); nada além de :::notes e :::source pode vir em seguida. Se você adicionaria razões, use um slide normal com um título curto e a conclusão como :::lead |
| 1–4 números para mostrar fortemente, sem série para comparar | ::::stat → :::value 3 → 12 {unit=modelos} + rótulo de uma linha dentro | 5+ números ou uma série temporal é um gráfico/tabela, não stat. O valor é escrito como está e deve conter um dígito. Valor e unidade nunca são quebrados no meio, então mantenha-os curtos — 4 cartões são estreitos (stat-value) |
| Uma série de números (3+ pontos): classificação, mudança ao longo do tempo, participação em um todo, distribuição, relação de duas quantidades | ```chart | compare: item / timeSeries / component / frequency / correlation — o tipo de gráfico é escolhido para você (nunca escreva barra/linha/pizza). categories: [...], series: [{name, values: [...]}] (um valor por categoria, números sem aspas, máximo 3 séries), unit opcional, emphasis: [category] (uma série) ou emphasis: true em uma série, stacked: true quando as séries são partes de um total. correlation usa xAxis: {title, unit?}, yAxis e points: [[x, y], …] em vez de categorias. component (participação) tem 1 série e ≤ 6 partes — mescle partes pequenas em "Outros" |
| Palavras de outra pessoa | :::quote Texto da fonte + as palavras dentro | A fonte (texto do cabeçalho) é obrigatória. Nunca cite sua própria conclusão |
| Uma frase para enfatizar dentro de um slide normal | :::takeaway + uma frase | No máximo uma por slide, geralmente no final |
| Linhas × colunas de texto simples ou números | Tabela GFM: | A | B | + | --- | --- | + linhas | A primeira linha é o cabeçalho; toda linha tem uma célula por coluna (table-shape). Sem marcadores de alinhamento (:--, --: são erros); colunas numéricas são alinhadas à direita para você. Células são apenas inline. Uma linha : legenda logo após a tabela é sua legenda. Se a última linha for um total, termine essa linha com {total=last} (: Estimativas {total=last}, ou apenas : {total=last}); ela é desenhada como uma linha de total. Totais não são computados — escreva o valor. Coloque uma unidade uma vez na legenda ou cabeçalho, não em cada célula. Avaliações em células → matrix (abaixo) |
| Linhas × colunas onde cada célula é uma avaliação (suportado / condicional / não / risco) | ```matrix | columns: [A, B], rows: [{label, cells: [{status, text?}]}], legend: {support: Bom} opcional. Uma célula por coluna (matrix-cells). A opção recomendada: emphasis: <texto do cabeçalho da coluna> (exatamente como escrito em columns, matrix-emphasis) — nunca diga "a coluna azul" em prosa. Células que são texto simples ou números são uma tabela, não uma matriz |
| 3–7 etapas sem datas, em uma direção (a saída de uma é a entrada da próxima) | ```process | steps: [{title, detail?, status?, emphasis?}], caption opcional. Números (1. 2. …) e setas são adicionados para você; processos longos quebram para uma segunda linha. Se você usar status, diga o que significa em legend: {conditional: Precisa de aprovação} (figure-legend) |
| Duas ou mais opções, cada uma com prós e contras | :::compare → :::option → :::pros / :::cons | Se 3+ critérios compartilhados se aplicam a todas as opções, uma matriz se encaixa melhor (acima) |
| Um pai com filhos (e netos): configuração de equipe, propriedade | ```orgchart | root: {title, detail?}, units: [{title, detail?, members: [{title, detail?}]}]. Máximo de 3 níveis. Um status em uma caixa precisa de legend: {conditional: Mais duas pessoas necessárias} — a legenda abaixo do gráfico explica; não explique cores de contorno em uma :::note. Não é para equipes não relacionadas lado a lado |
| Estágios cuja contagem diminui (visitas → inscrições → pagos) | ```funnel | stages: [{label, value, note?}], unit opcional. value é um número ou texto; larguras de barra são escalonadas, não proporcionais. Taxas de conversão estágio a estágio (↓ 45%) são desenhadas para você a partir de valores numéricos — não as escreva em note; use note para o motivo pelo qual um estágio cai. status precisa de legend. Valores que crescem são avisados (funnel-increase) — use process para etapas simples |
| Um plano com datas, períodos, prazos, status ao longo do tempo | ```gantt | Estados são support, conditional, none, danger (funções, não cores). Períodos de hardware/risco de suporte são um grupo risk: A .. B. Anotações apontam para um id de grupo/linha via target; o subsistema as posiciona |
| 2–3 grupos paralelos lidos lado a lado ("este ano / próximo ano / não fazendo") | ::::columns → :::column Título do grupo (2 ou 3) | Cada grupo contém parágrafos, marcadores, uma tabela, uma imagem, um ```chart ou :::note. Sem colunas aninhadas, compare, galeria ou outras figuras. Gráfico + sua leitura lado a lado: gráfico em um grupo, os takeaways como marcadores no outro. Larguras não são números: marque o grupo principal como {role=main} (os outros suportam) ou deixe todos iguais. 4+ grupos de uma linha → marcadores. Não é para um argumento dividido em dois |
| Uma imagem que mostra o ponto (captura de tela, foto, desenho) | um parágrafo contendo apenas  | Texto alt é obrigatório (image-alt). O caminho é relativo ao arquivo do deck; sem URLs, caminhos absolutos ou .. (image-path). Apenas PNG ou JPEG (image-format). Mantido em proporção, nunca cortado. Imagem + texto lado a lado → ::::columns com a imagem em um grupo |
| 2–6 imagens comparadas ou em sequência | um parágrafo com 2–6  | Torna-se uma galeria (uma linha para 2–3, uma grade para 4–6). 7+ é um erro: divida o slide |
| Pontos paralelos | marcadores | Máximo de 2 níveis |
| Prosa conectada | parágrafo | 2–3 frases |
| O que o slide diz: sua conclusão em uma ou duas frases | :::lead no nível superior de um slide de conteúdo, um parágrafo | Todo slide de conteúdo ganha um. Colocado pelo modelo logo abaixo do título, fora do corpo; ele ocupa espaço do corpo (1 linha ≈ 32 pt, 2 linhas ≈ 57 pt). Quando precisa de duas linhas, o mecanismo quebra após o final de uma frase (。) ou, não sendo possível, após uma vírgula (、) sem adicionar uma linha — então duas frases curtas são melhores para leitura. Não é um resumo dos marcadores — é a afirmação que eles provam |
| De onde vêm os números, tabela, figura ou citação do slide (fonte, tamanho da amostra, data) | :::source no nível superior do slide, um parágrafo | Todo slide que mostra números, uma tabela, um gráfico ou uma citação ganha um. Apenas slides de conteúdo e declaração (não seção). Colocado pelo modelo abaixo do corpo, acima do rodapé; ele ocupa espaço do corpo (layout-overflow o conta). Um por slide: várias fontes vão em um parágrafo |
| Data, confidencialidade, aviso legal do deck ("todos os valores são fictícios") | front matter date, confidential, disclaimer | Escrito uma vez; mostrado na capa (e confidential no rodapé) |
| Uma observação / ressalva do tipo nota de rodapé | :::note | Uma linha curta. Não para fontes (:::source), não para o aviso legal do deck (front matter) e nunca para a conclusão ou um número chave |
| Um diagrama cujas posições/links carregam significado e nenhum dos acima se encaixa | ```shapes | Último caso, veja §3.1 |
Em todas as quatro figuras acima você escreve apenas texto, status (support, conditional,
none, danger) e emphasis: true; posição, tamanho e cor vêm do modelo.
Em chart você escreve apenas o que é comparado e os números; o gráfico é um gráfico real do
PowerPoint cujos valores permanecem editáveis. Enfatize o único ponto que o título faz:
compare: timeSeries
unit: " inscrições"
categories: [Abr, Mai, Jun, Jul, Ago, Set]
emphasis: [Set]
series:
- name: Inscrições
values: [120, 150, 180, 220, 260, 310]
3.1 shapes (último caso)
Se você precisa de posições/links que carregam significado e nenhum padrão acima se encaixa:
fill:
color: "0,0,0" # R,G,B 0–255
opacity: 0.7
shapes:
- type: rectangle
position: [1.5, 0.5, 3, 2] # cm: left, top, width, height
text: Caixa
fill:
color: "200,200,255"
border:
color: "100,100,200"
width: 0.08 # cm
- type: line
start: [3, 2.5]
end: [3, 4.5]
width: 0.08
color: "200,0,0"
- type: ellipse
position: [2, 5, 2, 1.5]
text: Elipse
fill:
color: "150,255,150"
Apenas rectangle, ellipse, line e text ({text: …, position: …}, sem moldura/preenchimento).
fill, border, line-end são opcionais. Coordenadas em cm absolutos — zero/topo da borda superior do slide.
Os templates light e dark não têm estilos shapes, portanto cores de preenchimento e borda
são usadas como escritas. Isso sempre parece fora de lugar; use shapes apenas quando não houver
outra maneira de transmitir a informação.
4. Limites de layout
O motor de layout boxloom check -format json relata layout-overflow quando o conteúdo
excede a área de corpo do template. Seus limites (verifique boxloom schema para o template atual —
[truncado conforme original: a seção 4 continua com limites específicos, mas foi truncada na entrada; o restante não é necessário para a tradução do conteúdo fornecido]
