# Autoria prática — decks e presentations

Receitas para construir um `deck`/`presentation` do zero via YAML, achar
referências reais, calcular layout no canvas, e reconhecer os modos de falha
mais comuns antes que virem retrabalho.

## Achando `appId` (fácil)

```bash
lumo list app                       # apps do workspace local
lumo info app:<id>                  # detalhe básico
lumo info app:<id> --full           # relatório exaustivo: colunas, dashboards, expressions
```

`appId` é só o id numérico do app dono do gráfico — qualquer um desses
comandos te dá o número certo. Sem mistério aqui.

## Achando `widgetGuid`

`widgetGuid` não aparece no domínio do app que `lumo pull app:<id>` /
`lumo info app:<id> --full` mostram — lá a identidade de widget dentro de
`dashboards[].widgets[]` é o `id` **numérico** (decisão deliberada do
backend: se o guid entrasse no corpo dual-doc do `app`, todo app YAML
existente ganharia um campo fantasma no próximo `lumo pull`). Para achar o
par `appId`+`widgetGuid` de um gráfico real, use o comando de descoberta
dedicado:

```bash
lumo app widgets app:<id>                 # tabela: id, guid, nome, type, dashboard
lumo app widgets app:<id> --format json   # mesma coisa, machine-readable
```

Ele lista todo widget do app com o guid junto — é a única rota (`GET
/app/:id/widgets`) que expõe guid de propósito, escopada a essa descoberta
(o guid nunca aparece no corpo dual-doc do `app`). Ache a linha do widget que
você quer, copie `guid` + o `appId` já conhecido pro elemento `horus-widget`
que você está montando.

Nunca "adivinhe" ou derive um guid a partir do `id` numérico do widget — não
há relação determinística entre os dois; um guid errado só vai aparecer como
falha em `lumo deck preview`, silenciosamente até esse ponto.

Depois de copiar um par real, **sempre rode `lumo deck preview`** — é o que
confirma que o guid ainda é válido (o widget pode ter sido apagado ou
recriado desde a última vez que alguém o referenciou) antes de você seguir
para export/publish.

## Matemática de layout no canvas 1000×562.5

Unidades do canvas, não pixels de tela — o render escala o documento inteiro
para caber no viewport final. Convenção prática: margem de 20 unidades nas
bordas.

**Linha de KPIs no topo + gráfico full-width embaixo** (2 KPIs + 1 gráfico):

```yaml
elements:
  # KPI 1 — metade esquerda da largura útil (1000 - 2*20 = 960 → duas colunas de 470 com 20 de gap)
  - type: horus-widget
    id: kpi1
    left: 20
    top: 20
    width: 470
    height: 150
    rotate: 0
    appId: 18421
    widgetGuid: "guid-kpi-receita"
    title: { mode: widget }
    background: { mode: card }
    theme: light
    filters: null
    frozen: false

  # KPI 2 — metade direita
  - type: horus-widget
    id: kpi2
    left: 510
    top: 20
    width: 470
    height: 150
    rotate: 0
    appId: 18421
    widgetGuid: "guid-kpi-margem"
    title: { mode: widget }
    background: { mode: card }
    theme: light
    filters: null
    frozen: false

  # Gráfico principal — largura cheia abaixo dos KPIs
  - type: horus-widget
    id: chart1
    left: 20
    top: 190
    width: 960
    height: 340
    rotate: 0
    appId: 18421
    widgetGuid: "guid-vendas-por-mes"
    title: { mode: widget }
    background: { mode: card }
    theme: light
    filters: null
    frozen: false
```

Total vertical usado: `20 (margem) + 150 (kpi) + 20 (gap) + 340 (chart) + 32.5
(margem inferior) = 562.5` — bate exato com a altura do canvas. Ajuste as
proporções, não a lógica: margem constante, gap entre blocos, altura do
último bloco = altura restante.

`id` de elemento/slide é só uma string única — não precisa ser um GUID de
verdade; um id curto e legível (`kpi1`, `s1`) funciona, contanto que não
colida com outro id no mesmo documento. O editor gera `nanoid(10)` por
convenção; reutilizar ids simples em documentos montados à mão é seguro
desde que sejam únicos dentro do deck.

## Modos de falha comuns — o que pega cada um

| Erro | Onde aparece | Por quê |
|---|---|---|
| `appId` inexistente/de outro tenant | `lumo deck preview` (`ok: false`, mensagem do servidor) | `lint` só verifica que é um inteiro — não confere existência |
| `widgetGuid` inválido ou de um widget apagado | `lumo deck preview` (`ok: false`) | mesma razão — forma válida, referência morta |
| `horus-widget` sem `appId`/`widgetGuid` | `lumo lint` (falha de schema, antes mesmo de tentar `push`) | esses dois campos são os únicos validados por forma |
| Elemento com `type` errado ou campo interno com typo (ex.: `content` vs `contnet` num `text`) | Silencioso no `lint`; só aparece no render | todo campo fora de `horus-widget`/geometria é freeform — confirme visualmente com `lumo deck export --format png` |
| `presentation` item `type: deck` sem `deckId` (ou `type: dashboard` sem `dashboardId`) | `lumo lint` (falha de schema) | regra condicional do schema — ver `presentations.md` |
| Link público não resolve depois de editar `items` | `lumo presentation check` (exit não-zero) | pode ser publish nunca feito, ou item apontando pra um `deckId`/`dashboardId` que não existe mais |

Regra geral: **`lint` pega forma; `preview`/`check` pegam realidade.** Rode os
dois sempre — `lint` é rápido e não bate no servidor, mas sozinho não garante
nada sobre dados existirem de verdade.
