---
name: wodrex
description: Monta o treino do dia (WOD) no formato do WodRex, o arquivo JSON que o coach cola em "Montar treino > Importar" para o treino aparecer na TV do box. Use quando pedirem para montar, passar ou converter um treino (texto, foto do quadro, planilha) para o WodRex.
---

# Treino para o WodRex

O WodRex mostra o treino do dia numa TV do box (tela 16:9, lida de longe) e roda o
cronômetro da aula. O coach descreve o treino; você devolve **um único bloco de código
JSON** no formato abaixo. Ele copia, abre o app em **Treinos > Montar treino > Importar**
e toca **Colar treino**. O app abre o treino no editor e o coach confere antes de salvar.

## Como responder

1. Monte o JSON a partir do que o coach mandou (texto, foto do quadro branco, planilha).
2. Responda com **um** bloco ```json com o arquivo inteiro, e no máximo duas linhas
   antes ou depois (ex.: o que você supôs). Nada de explicar o formato ao coach.
3. Ficou em dúvida sobre algo que muda o treino na TV ou o cronômetro (os tempos, se
   a turma se divide em grupos, como separar as partes)? **Pergunte antes de montar**,
   numa mensagem curta: até 3 perguntas, cada uma com as opções que este guia permite
   (ex.: "As zonas: (a) a turma toda faz A e depois B, ou (b) a turma em 2 grupos que
   trocam de zona?"). Detalhe pequeno (um nome, a ordem de duas linhas): escolha o mais
   comum no CrossFit e diga numa linha o que supôs.
4. Use **exatamente** os campos deste guia. Um formato parecido mas inventado
   (`blocks`, `movements`, `reps`, `unit`…) não abre no app.
5. Copie os textos como o coach escreveu (movimentos, cargas, nomes), em MAIÚSCULAS
   como nos quadros de box. Não invente movimentos, cargas nem vídeos.

## O arquivo

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Treino de terça",
  "layout": { "type": "block", "title": "WOD", "items": [] }
}
```

- `format` e `version` são sempre esses dois valores.
- `name`: opcional, só um rótulo.
- `layout`: a tela, uma árvore de **grupos** e **blocos**.

## A tela

```ts
type Group = {
  type: "group";
  dir: "row" | "col";          // "row": filhos lado a lado; "col": um embaixo do outro
  sizes?: number[];            // proporção de cada filho (padrão: todos 1). [1, 2] = o 2º é o dobro
  children: (Group | Block)[]; // pelo menos 1
  title?: string;              // ex. "WOD": aparece em cima, abraçando os filhos
  subtitle?: string;           // ao lado do título
  titleSize?: "s" | "l";       // título pequeno ou grande (sem: médio)
  // cronômetro (ver abaixo): duration, each, eachRest, restAfter, count
};

type Block = {
  type: "block";
  title?: string;              // "WARM UP", "BLOCO A", "STRENGTH"
  subtitle?: string;           // o formato: "AMRAP 12'", "EMOM 10'", "FOR TIME · CAP 15'", "3 ROUNDS"
  titleSize?: "s" | "l";
  borderless?: true;           // sem moldura (bom para o logo)
  items: Item[];
  // cronômetro: duration, rounds, roundRest, restAfter, count
};

type Item =
  | { type: "rows"; rows: Row[] }                  // o principal: a lista de movimentos
  | { type: "text"; text: string; size?: "s" | "l" } // texto livre; "\n" quebra a linha
  | { type: "divider" }                            // uma linha separando itens
  | { type: "image"; ref: "logo" }                 // o logo do box (a única imagem possível aqui)
  | { type: "qr"; url: string; caption?: string }  // QR code para um link
  | { type: "video"; videoId: string; caption?: string; start?: number; end?: number };

type Row = {
  label: string;               // à esquerda, em destaque: reps ("21", "15"), letra ("A"), tempo ('40"'), "MIN 1"
  value: string;               // o movimento: "THRUSTER (43/30 KG)"
  duration?: number;           // segundos, para o cronômetro passar linha a linha
  rest?: number;               // segundos de descanso depois desta linha
};
```

`id` em grupos e blocos é opcional: o app cria os seus.

### Como fica bonito na TV

- A tela é larga (16:9). O mais comum: uma coluna estreita à esquerda com o logo e o
  aquecimento, e o WOD à direita, maior (`"dir": "row", "sizes": [1, 2.2]`).
- Use poucos blocos (até 5 ou 6). O texto se ajusta sozinho ao espaço do bloco: muito
  texto num bloco pequeno fica miúdo.
- **Linhas (`rows`) em vez de texto** para movimentos: uma linha por movimento,
  `label` com as reps e `value` com o movimento. Labels de até 3 caracteres ("21",
  "10", "A", '40"') aparecem bem grandes; mais longos ("MIN 1") ficam do tamanho do
  movimento. Linha sem número: `"label": ""`.
- O formato vai no `subtitle` do bloco ("AMRAP 12'", "EMOM 16'", "5 ROUNDS FOR TIME"),
  não numa linha.
- Carga e observações vão no próprio `value` ("DEADLIFT (100/70 KG)") ou num `text`
  pequeno no fim (`"size": "s"`), depois de um `divider`.
- Esquema de reps tipo 21-15-9: no `subtitle` ("21-15-9 FOR TIME") e as linhas com
  `"label": ""`.
- **O logo do box vai sempre**, a menos que o coach peça para tirar ou que o treino
  seja tão cheio que o logo aperte o resto (aí deixe de fora e diga numa linha). É um
  bloco `{ "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] }`,
  em geral no alto da coluna da esquerda, menor que os blocos do treino.
- Vídeo: **não ponha vídeo por conta própria.** Só quando o coach mandar o link do
  YouTube (`videoId` são os 11 caracteres depois de `v=`, `youtu.be/` ou `shorts/`) ou
  pedir que você ache um. Nunca escreva um `videoId` de memória: IDs lembrados quase
  sempre estão errados ou apontam para vídeos que saíram do ar.
- Se o coach pedir para você achar um vídeo, só use um que você abriu de verdade nesta
  conversa (busca na web) e confira em
  `https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=ID`:
  se responder com o título, serve; se der erro, não use. Sem como buscar ou conferir,
  deixe o vídeo de fora e diga numa linha que o coach pode colar o link no editor.
  O app também confere ao importar e tira o vídeo que não existe.

## Cronômetro

Na aula, o coach toca play e a TV conta o tempo de cada parte com tempo, em ordem de
leitura (de cima para baixo, da esquerda para a direita). Todo tempo é em **segundos**,
número inteiro (12 minutos = `720`). Parte sem tempo (força, técnica) não entra no
cronômetro; deixe sem.

- **Bloco com `duration`**: roda aquele tempo. AMRAP 12' → `"duration": 720`.
  For Time com cap de 15' → `"duration": 900, "count": "up"`.
- **`count`**: `"up"` conta para cima (For Time), `"down"` conta para baixo (AMRAP,
  EMOM). Sem ele, o coach escolhe na hora. Ponha quando o formato deixa claro.
- **Linhas com `duration`**: o bloco passa linha a linha, destacando a atual (EMOM,
  Tabata, intervalos). O `duration` do bloco é ignorado nesse caso.
- **`rounds`** (2 a 50): repete o bloco (as linhas com tempo, ou o `duration` dele).
  EMOM 12' com 3 movimentos alternados = 3 linhas de 60 s com `"rounds": 4`.
- **`rest` na linha**: descanso depois dela (não roda depois da última linha da última
  rodada). Ou uma linha própria de descanso: `{ "label": "1'", "value": "REST", "duration": 60 }`.
- **`roundRest`**: descanso entre as rodadas do bloco.
- **`restAfter`**: descanso antes do próximo bloco com tempo (ex. 2' entre o A e o B).
- **Grupo com `duration`**: um tempo só para o grupo inteiro (os tempos de dentro são
  ignorados). Use quando o treino todo tem um cap ou um AMRAP grande dividido em partes.
- **Grupo com `each`** (estações): cada bloco do grupo roda `each` segundos, um depois
  do outro, `eachRest` entre eles (veja "Turma em grupos"). Não use `each` e `duration`
  no mesmo grupo.

### Turma em grupos

Quando a turma se divide e os grupos trocam de lugar no mesmo relógio (estações,
zonas, "metade começa no A e metade no B, depois invertem"), o app sabe fazer o
rodízio: na aula, o coach escolhe em quantos grupos a turma se divide, o grupo 1
começa na 1ª estação, o 2 na 2ª, e a cada troca todos passam para a seguinte. A TV
mostra o número de cada grupo na sua estação. O número de grupos **não vai no
arquivo**: o arquivo só precisa ter as estações num dos dois formatos abaixo. Não
force: se o treino é a turma toda junta, monte do jeito normal.

- **Estações em blocos**: um grupo com `each` (o tempo de cada estação) e `eachRest`
  (a troca), com **um bloco por estação, filhos diretos do grupo** (não ponha grupos
  dentro dele, nem divida uma estação em dois blocos). Os tempos dos blocos lá dentro
  são ignorados: o formato de cada estação ("AMRAP", "MAX REPS") vai no `subtitle`.
  Sem grupos, a turma toda faz uma estação depois da outra, no mesmo relógio.
- **Estações em linhas**: um bloco com uma linha por estação, **todas com o mesmo
  `duration` e o mesmo `rest`** (ex. 4 linhas de 60 s com `"rest": 15` e
  `"rounds": 3`). Linhas de descanso ("REST") são de todos e não contam como estação.

Exemplos: "Zonas que invertem" e "Estações" abaixo.

Limites: até 3 horas por tempo (`10800`), até 6 níveis de grupos. O que estiver fora do
formato é descartado na importação (o app avisa).

## Exemplos

### Aula típica: logo, aquecimento e duas partes

Aquecimento com 4 movimentos de 40" em 2 rounds, 1' até o WOD; Bloco A EMOM 12' (3
rounds de 4 minutos); 2' de descanso; Bloco B AMRAP 10'.

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Aula típica",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [1, 2.2],
    "children": [
      {
        "type": "group",
        "dir": "col",
        "sizes": [1, 2.2],
        "children": [
          { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
          {
            "type": "block",
            "title": "WARM UP",
            "subtitle": "2 ROUNDS",
            "rounds": 2,
            "restAfter": 60,
            "items": [
              {
                "type": "rows",
                "rows": [
                  { "label": "40\"", "value": "JUMPING JACK", "duration": 40 },
                  { "label": "40\"", "value": "AIR SQUAT", "duration": 40 },
                  { "label": "40\"", "value": "INCHWORM", "duration": 40 },
                  { "label": "40\"", "value": "HOLLOW ROCK", "duration": 40 }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "group",
        "title": "WOD",
        "dir": "col",
        "sizes": [1.15, 1],
        "children": [
          {
            "type": "block",
            "title": "BLOCO A",
            "subtitle": "EMOM 12'",
            "rounds": 3,
            "restAfter": 120,
            "count": "down",
            "items": [
              {
                "type": "rows",
                "rows": [
                  { "label": "MIN 1", "value": "15 KB SWING (24/16 KG)", "duration": 60 },
                  { "label": "MIN 2", "value": "10 BURPEE", "duration": 60 },
                  { "label": "MIN 3", "value": "12 WALL BALL (9/6 KG)", "duration": 60 },
                  { "label": "MIN 4", "value": "REST", "duration": 60 }
                ]
              }
            ]
          },
          {
            "type": "block",
            "title": "BLOCO B",
            "subtitle": "AMRAP 10'",
            "duration": 600,
            "count": "down",
            "items": [
              {
                "type": "rows",
                "rows": [
                  { "label": "10", "value": "DEADLIFT (60/40 KG)" },
                  { "label": "12", "value": "BOX JUMP OVER" },
                  { "label": "14", "value": "CAL ROW" }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}
```

### Força sem tempo e um For Time com cap

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Força + For Time",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [0.6, 1, 1.4],
    "children": [
      { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
      {
        "type": "block",
        "title": "STRENGTH",
        "subtitle": "BACK SQUAT",
        "items": [
          {
            "type": "rows",
            "rows": [
              { "label": "5", "value": "@ 70%" },
              { "label": "5", "value": "@ 75%" },
              { "label": "3", "value": "@ 80%" },
              { "label": "3", "value": "@ 85%" }
            ]
          },
          { "type": "divider" },
          { "type": "text", "text": "DESCANSO 2' ENTRE AS SÉRIES", "size": "s" }
        ]
      },
      {
        "type": "block",
        "title": "WOD",
        "subtitle": "21-15-9 FOR TIME · CAP 12'",
        "duration": 720,
        "count": "up",
        "items": [
          {
            "type": "rows",
            "rows": [
              { "label": "", "value": "THRUSTER (43/30 KG)" },
              { "label": "", "value": "PULL-UP" }
            ]
          }
        ]
      }
    ]
  }
}
```

### Um For Time longo em duas colunas, descanso e um AMRAP

Um tempo só para o grupo (o cap de 20'), com o chipper dividido em dois blocos lado a
lado para não virar uma coluna alta; 3' de descanso; depois o AMRAP. Partes do chipper
separadas por `divider`.

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Benchmarks",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [2.2, 1],
    "children": [
      {
        "type": "group",
        "title": "ZACHARY TELLIER",
        "subtitle": "HERO WOD · DUPLAS · FOR TIME 20'",
        "dir": "row",
        "duration": 1200,
        "count": "up",
        "restAfter": 180,
        "children": [
          {
            "type": "block",
            "items": [
              { "type": "rows", "rows": [
                { "label": "10", "value": "BURPEE" },
                { "label": "25", "value": "PUSH-UP" }
              ] },
              { "type": "divider" },
              { "type": "rows", "rows": [
                { "label": "10", "value": "BURPEE" },
                { "label": "25", "value": "PUSH-UP" },
                { "label": "50", "value": "LUNGE" }
              ] }
            ]
          },
          {
            "type": "block",
            "items": [
              { "type": "rows", "rows": [
                { "label": "10", "value": "BURPEE" },
                { "label": "25", "value": "PUSH-UP" },
                { "label": "50", "value": "LUNGE" },
                { "label": "100", "value": "SIT-UP" }
              ] },
              { "type": "divider" },
              { "type": "rows", "rows": [
                { "label": "10", "value": "BURPEE" },
                { "label": "25", "value": "PUSH-UP" },
                { "label": "50", "value": "LUNGE" },
                { "label": "100", "value": "SIT-UP" },
                { "label": "150", "value": "AIR SQUAT" }
              ] }
            ]
          }
        ]
      },
      {
        "type": "group",
        "dir": "col",
        "sizes": [1, 1.6],
        "children": [
          { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
          {
            "type": "block",
            "title": "BETTY",
            "subtitle": "AMRAP 10'",
            "duration": 600,
            "count": "down",
            "items": [
              { "type": "rows", "rows": [
                { "label": "12", "value": "PUSH PRESS (135/95 LB)" },
                { "label": "20", "value": "BOX JUMP" }
              ] }
            ]
          }
        ]
      }
    ]
  }
}
```

### Tabata (8 rounds de 20" on / 10" off)

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Tabata",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [1, 2.4],
    "children": [
      { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
      {
        "type": "block",
        "title": "TABATA",
        "subtitle": "8 ROUNDS · 20\" ON / 10\" OFF",
        "rounds": 8,
        "count": "down",
        "items": [
          { "type": "rows", "rows": [{ "label": "20\"", "value": "AIR SQUAT", "duration": 20, "rest": 10 }] }
        ]
      }
    ]
  }
}
```

### Zonas que invertem (turma em 2 grupos)

"Hyrox com warm up. A turma em 2 grupos: um começa na zona A (cardio), outro na zona B
(força), depois invertem. 10' por zona, 1'30" para trocar." Um grupo com `each` e um
bloco por zona; na aula, o coach escolhe 2 grupos.

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Hyrox zonas A e B",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [1, 2.2],
    "children": [
      {
        "type": "group",
        "dir": "col",
        "sizes": [1, 2],
        "children": [
          { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
          {
            "type": "block",
            "title": "WARM UP",
            "subtitle": "2 ROUNDS",
            "items": [
              { "type": "rows", "rows": [
                { "label": "200 M", "value": "RUN" },
                { "label": "10", "value": "AIR SQUAT" },
                { "label": "10", "value": "REVERSE LUNGE" },
                { "label": "10", "value": "PUSH-UP" }
              ] }
            ]
          }
        ]
      },
      {
        "type": "group",
        "title": "HYROX",
        "subtitle": "2 GRUPOS · 10' POR ZONA · 1'30\" PARA TROCAR",
        "dir": "row",
        "each": 600,
        "eachRest": 90,
        "count": "down",
        "children": [
          {
            "type": "block",
            "title": "ZONA A",
            "subtitle": "CARDIO · AMRAP",
            "items": [
              { "type": "rows", "rows": [
                { "label": "400 M", "value": "RUN" },
                { "label": "20", "value": "BURPEE BROAD JUMP" },
                { "label": "20", "value": "WALL BALL (9/6 KG)" },
                { "label": "500 M", "value": "ROW" }
              ] }
            ]
          },
          {
            "type": "block",
            "title": "ZONA B",
            "subtitle": "FORÇA · AMRAP",
            "items": [
              { "type": "rows", "rows": [
                { "label": "20 M", "value": "FARMER CARRY" },
                { "label": "15", "value": "DB THRUSTER" },
                { "label": "20 M", "value": "SANDBAG LUNGE" },
                { "label": "20", "value": "KB SWING" }
              ] }
            ]
          }
        ]
      }
    ]
  }
}
```

### Estações em linhas (4 estações de 1', 15" para trocar, 3 voltas)

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Circuito",
  "layout": {
    "type": "group",
    "dir": "row",
    "sizes": [1, 2.4],
    "children": [
      { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
      {
        "type": "block",
        "title": "CIRCUITO",
        "subtitle": "3 VOLTAS · 1' POR ESTAÇÃO",
        "rounds": 3,
        "count": "down",
        "items": [
          { "type": "rows", "rows": [
            { "label": "1'", "value": "CAL BIKE", "duration": 60, "rest": 15 },
            { "label": "1'", "value": "KB SWING (24/16 KG)", "duration": 60, "rest": 15 },
            { "label": "1'", "value": "BOX STEP-UP", "duration": 60, "rest": 15 },
            { "label": "1'", "value": "PLANK", "duration": 60, "rest": 15 }
          ] }
        ]
      }
    ]
  }
}
```

### Estações em blocos (cada uma 3', 30" para trocar)

```json
{
  "format": "wodboard/wod",
  "version": 1,
  "name": "Estações",
  "layout": {
    "type": "group",
    "dir": "col",
    "sizes": [1, 4],
    "children": [
      { "type": "block", "borderless": true, "items": [{ "type": "image", "ref": "logo" }] },
      {
        "type": "group",
        "title": "WOD",
        "subtitle": "4 ESTAÇÕES · 3' CADA",
        "dir": "row",
        "each": 180,
        "eachRest": 30,
        "count": "down",
        "children": [
          { "type": "block", "title": "ESTAÇÃO 1", "items": [{ "type": "rows", "rows": [{ "label": "", "value": "MAX CAL ROW" }] }] },
          { "type": "block", "title": "ESTAÇÃO 2", "items": [{ "type": "rows", "rows": [{ "label": "", "value": "MAX DOUBLE UNDER" }] }] },
          { "type": "block", "title": "ESTAÇÃO 3", "items": [{ "type": "rows", "rows": [{ "label": "", "value": "MAX WALL BALL (9/6 KG)" }] }] },
          { "type": "block", "title": "ESTAÇÃO 4", "items": [{ "type": "rows", "rows": [{ "label": "", "value": "MAX BURPEE" }] }] }
        ]
      }
    ]
  }
}
```

## Antes de responder, confira

- É JSON válido: aspas duplas, sem vírgula sobrando, sem comentários. As aspas de
  segundos dentro de texto são escapadas: `"40\""`.
- Todo `group` tem `dir` e `children`; todo `block` tem `items`.
- Se houver `sizes`, ele tem um número para cada filho.
- Tempos em segundos inteiros; `count` só "up" ou "down"; `rounds` de 2 a 50.
- O logo está lá (a não ser que o coach não queira ou não caiba).
- Estações com rodízio: um grupo com `each` e um bloco por estação direto nele, ou
  linhas com o mesmo `duration` e o mesmo `rest`.
- Nenhum vídeo que o coach não pediu, nenhum `videoId` de memória e nenhuma imagem
  além de `"ref": "logo"`.
