1. A árvore de view¶
A unidade básica do tempestweb é a função view(). Ela recebe o app (que
expõe o estado atual) e devolve uma árvore de widgets. Não há JSX, não há
template — é Python puro, tipado.
A função view¶
from tempest_core import App, Column, Style, Text, Widget
from tempest_core.style import Edge
def view(app: App[CounterState]) -> Widget: # (1)!
"""Render the counter UI from the current state."""
return Column( # (2)!
style=Style(gap=8.0, padding=Edge.all(16)),
children=[
Text(content=f"Count: {app.state.value}", key="label"), # (3)!
],
)
viewrecebeApp[CounterState]— o handle tipado do estado — e sempre devolve umWidget. Tipo de entrada e saída são parte do contrato.Columné um container flex vertical.Rowé o horizontal. Ambos recebemstyleechildren.Textmostra texto.app.state.valuelê o estado atual — a view é uma função do estado.
A view é pura
view() não muta nada. Ela lê app.state e descreve a UI que
corresponde àquele estado. Mudar o estado é trabalho dos handlers (próxima
página) — a view só desenha.
Os widgets do counter¶
O counter usa quatro tipos de widget, todos vindos do core:
| Widget | O que é | Props principais |
|---|---|---|
Column |
Container flex vertical | style, children |
Row |
Container flex horizontal | style, children |
Text |
Texto | content, style, key |
Button |
Botão clicável | label, on_click, style, key |
A key: identidade estável¶
Repare no key="label". A key dá ao widget uma identidade estável entre
rebuilds. Quando o estado muda e a view roda de novo, o reconciliador usa a key
para casar o widget novo com o antigo — e assim emitir um patch mínimo (mudar só
o texto) em vez de recriar o nó.
Quando dar key
Dê key a qualquer widget que persiste entre rebuilds e cujo conteúdo
muda (o texto da contagem, os botões). Itens de lista dinâmica também querem
key estável. Sem key, a reconciliação cai no casamento posicional.
Estilo é um objeto tipado¶
Style é um objeto Pydantic — não uma string CSS. Você declara intenção e o
cliente traduz para CSS:
gap=8.0→gap: 8pxno container flex.Edge.all(16)→padding: 16px 16px 16px 16px.
Style → CSS é quase identidade
O Style foi desenhado copiando o vocabulário do CSS (flexbox, box model,
tipografia). A tradução vive no cliente (client/style.js) e é compartilhada
pelos dois modos. Detalhe completo no contrato de fronteira.
A árvore completa do counter¶
Juntando container, texto e botões:
from tempest_core import App, Button, Column, Row, Style, Text, Widget
from tempest_core.style import Edge
def view(app: App[CounterState]) -> Widget:
"""Render the counter UI from the current state."""
return Column(
style=Style(gap=8.0, padding=Edge.all(16)),
children=[
Text(content=f"Count: {app.state.value}", key="label"),
Row(
style=Style(gap=4.0),
children=[
Button(label="-", key="dec"), # on_click vem na página 2
Button(label="+", key="inc"),
],
),
],
)
Isso produz a árvore (IR) que o reconciliador serializa para o cliente — o
formato exato está fixado em
tests/fixtures/node_initial.json.
Recap¶
view(app) -> Widgeté uma função pura do estado.- Widgets (
Column,Row,Text,Button) são objetos tipados do core. keydá identidade estável para a reconciliação emitir patches mínimos.Styleé um objeto tipado que vira CSS no cliente.
Agora os botões precisam fazer algo. Vamos para estado e handlers. 🚀