@snksergio/design-system 0.61.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/ai/blocos/chart/budget-breakdown.tsx +174 -0
- package/dist-lib/ai/blocos/indice.md +43 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-com-tabela.tsx +450 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-de-tarefa-com-abas.tsx +570 -0
- package/dist-lib/ai/blocos/paneldetail/detalhe-do-registro.tsx +555 -0
- package/dist-lib/ai/componentes/AlertModal.md +47 -0
- package/dist-lib/ai/componentes/AppShell.md +117 -0
- package/dist-lib/ai/componentes/Breadcrumb.md +187 -0
- package/dist-lib/ai/componentes/Button.md +116 -0
- package/dist-lib/ai/componentes/ButtonGroup.md +162 -0
- package/dist-lib/ai/componentes/CardCheckbox.md +99 -0
- package/dist-lib/ai/componentes/CardOption.md +133 -0
- package/dist-lib/ai/componentes/Chart.md +93 -0
- package/dist-lib/ai/componentes/Chip.md +68 -0
- package/dist-lib/ai/componentes/ChoroplethMap.md +118 -0
- package/dist-lib/ai/componentes/ColorPicker.md +70 -0
- package/dist-lib/ai/componentes/Combobox.md +51 -0
- package/dist-lib/ai/componentes/ConversationListItem.md +90 -0
- package/dist-lib/ai/componentes/DataList.md +111 -0
- package/dist-lib/ai/componentes/DataTable.md +867 -0
- package/dist-lib/ai/componentes/DatePicker.md +84 -0
- package/dist-lib/ai/componentes/DateSeparatorChip.md +59 -0
- package/dist-lib/ai/componentes/EmptyState.md +72 -0
- package/dist-lib/ai/componentes/FileUploadField.md +95 -0
- package/dist-lib/ai/componentes/FloatingPanel.md +118 -0
- package/dist-lib/ai/componentes/FooterTable.md +62 -0
- package/dist-lib/ai/componentes/FormField.md +110 -0
- package/dist-lib/ai/componentes/Gantt.md +552 -0
- package/dist-lib/ai/componentes/Header.md +98 -0
- package/dist-lib/ai/componentes/Icon.md +65 -0
- package/dist-lib/ai/componentes/Kanban.md +343 -0
- package/dist-lib/ai/componentes/Kpi.md +103 -0
- package/dist-lib/ai/componentes/List.md +61 -0
- package/dist-lib/ai/componentes/MarkdownText.md +59 -0
- package/dist-lib/ai/componentes/MenuSidebar.md +128 -0
- package/dist-lib/ai/componentes/MessageAck.md +53 -0
- package/dist-lib/ai/componentes/MessageBubble.md +115 -0
- package/dist-lib/ai/componentes/MessageComposer.md +80 -0
- package/dist-lib/ai/componentes/MessageVariablesPicker.md +104 -0
- package/dist-lib/ai/componentes/Modal.md +88 -0
- package/dist-lib/ai/componentes/MonthYearPicker.md +49 -0
- package/dist-lib/ai/componentes/PageHeader.md +129 -0
- package/dist-lib/ai/componentes/Panel.md +84 -0
- package/dist-lib/ai/componentes/Scheduler.md +421 -0
- package/dist-lib/ai/componentes/ScreenLoader.md +60 -0
- package/dist-lib/ai/componentes/SingleMenuSidebar.md +171 -0
- package/dist-lib/ai/componentes/Spinner.md +52 -0
- package/dist-lib/ai/componentes/Table.md +192 -0
- package/dist-lib/ai/componentes/TableToolbar.md +87 -0
- package/dist-lib/ai/componentes/TabsNavigation.md +152 -0
- package/dist-lib/ai/componentes/Toast.md +49 -0
- package/dist-lib/ai/componentes/_primitivos.md +74 -0
- package/dist-lib/ai/componentes/avatar-ig.md +181 -0
- package/dist-lib/ai/componentes/indice.json +49 -0
- package/dist-lib/ai/exemplos/app-shell/app-shell-example.tsx +140 -0
- package/dist-lib/ai/exemplos/app-shell/index.ts +3 -0
- package/dist-lib/ai/exemplos/app-shell/nav-data.ts +97 -0
- package/dist-lib/ai/exemplos/app-shell/routes.tsx +75 -0
- package/dist-lib/ai/exemplos/chat/chat-screen.tsx +152 -0
- package/dist-lib/ai/exemplos/chat/chat-v2-mocks.ts +171 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.styles.ts +23 -0
- package/dist-lib/ai/exemplos/chat/chat-v2.types.ts +81 -0
- package/dist-lib/ai/exemplos/chat/components/ChannelDot/channel-dot.tsx +28 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/conversation-actions-menu.tsx +83 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationActionsMenu/index.ts +4 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.styles.ts +22 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.tsx +203 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/conversation-column.types.ts +10 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.styles.ts +55 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.tsx +52 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/conversation-list-item.types.ts +7 -0
- package/dist-lib/ai/exemplos/chat/components/ConversationListItem/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/DateSeparator/date-separator.tsx +18 -0
- package/dist-lib/ai/exemplos/chat/components/DetailField/detail-field.tsx +23 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/detail-section.tsx +41 -0
- package/dist-lib/ai/exemplos/chat/components/DetailSection/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.tsx +142 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/details-column.types.ts +16 -0
- package/dist-lib/ai/exemplos/chat/components/DetailsColumn/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/filter-row.tsx +45 -0
- package/dist-lib/ai/exemplos/chat/components/FilterRow/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.styles.ts +27 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.tsx +66 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/filters-rail.tsx +38 -0
- package/dist-lib/ai/exemplos/chat/components/FiltersColumn/index.ts +6 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.styles.ts +37 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.tsx +29 -0
- package/dist-lib/ai/exemplos/chat/components/MessageBubble/message-bubble.types.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/index.ts +5 -0
- package/dist-lib/ai/exemplos/chat/components/PersonAvatar/person-avatar.tsx +31 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/index.ts +2 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.styles.ts +19 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.tsx +140 -0
- package/dist-lib/ai/exemplos/chat/components/QueueColumn/queue-column.types.ts +13 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/index.ts +1 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.styles.ts +30 -0
- package/dist-lib/ai/exemplos/chat/components/RailItem/rail-item.tsx +42 -0
- package/dist-lib/ai/exemplos/chat/hooks/use-resizable.ts +98 -0
- package/dist-lib/ai/exemplos/chat/index.ts +1 -0
- package/dist-lib/ai/exemplos/clientes/_table-data.ts +59 -0
- package/dist-lib/ai/exemplos/clientes/clientes-screen.tsx +505 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/clientes/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.styles.ts +28 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.tsx +303 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/detail-drawer.types.ts +14 -0
- package/dist-lib/ai/exemplos/clientes/components/DetailDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/clientes/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/clientes/index.ts +1 -0
- package/dist-lib/ai/exemplos/dashboard/dashboard-brazil-map.ts +33 -0
- package/dist-lib/ai/exemplos/dashboard/dashboard-screen.tsx +1110 -0
- package/dist-lib/ai/exemplos/dashboard/index.ts +1 -0
- package/dist-lib/ai/exemplos/edit-page/components/StepNav.tsx +81 -0
- package/dist-lib/ai/exemplos/edit-page/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/edit-page/edit-page-screen.tsx +234 -0
- package/dist-lib/ai/exemplos/edit-page/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/_table-data.ts +57 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro-mocks.ts +227 -0
- package/dist-lib/ai/exemplos/finance/clientes-financeiro.types.ts +75 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase-mocks.ts +117 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.styles.ts +13 -0
- package/dist-lib/ai/exemplos/finance/clientes-showcase.types.ts +16 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/editar-finance-drawer.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/EditarFinanceDrawer/index.ts +5 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/extrato-expansion.tsx +172 -0
- package/dist-lib/ai/exemplos/finance/components/ExtratoExpansion/index.ts +1 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/finance-detail-panel.tsx +241 -0
- package/dist-lib/ai/exemplos/finance/components/FinanceDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/NovoClienteDrawer/novo-cliente-drawer.tsx +207 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/index.ts +2 -0
- package/dist-lib/ai/exemplos/finance/components/SacarDialog/sacar-dialog.tsx +346 -0
- package/dist-lib/ai/exemplos/finance/finance-screen.tsx +821 -0
- package/dist-lib/ai/exemplos/finance/index.ts +1 -0
- package/dist-lib/ai/exemplos/gantt/_gantt-data.tsx +402 -0
- package/dist-lib/ai/exemplos/gantt/gantt-screen.tsx +512 -0
- package/dist-lib/ai/exemplos/gantt/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/index.ts +1 -0
- package/dist-lib/ai/exemplos/login/login-screen.tsx +246 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/consultor-detail-panel.tsx +158 -0
- package/dist-lib/ai/exemplos/mapa-rede/components/ConsultorDetailPanel/index.ts +2 -0
- package/dist-lib/ai/exemplos/mapa-rede/index.ts +1 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede-mocks.ts +655 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-de-rede.types.ts +53 -0
- package/dist-lib/ai/exemplos/mapa-rede/mapa-rede-screen.tsx +227 -0
- package/dist-lib/ai/exemplos/order-detail/components/ActivityTab.tsx +77 -0
- package/dist-lib/ai/exemplos/order-detail/components/AttachmentsTab.tsx +45 -0
- package/dist-lib/ai/exemplos/order-detail/components/CommentsTab.tsx +111 -0
- package/dist-lib/ai/exemplos/order-detail/components/DetailsTab.tsx +189 -0
- package/dist-lib/ai/exemplos/order-detail/components/OverviewTab.tsx +200 -0
- package/dist-lib/ai/exemplos/order-detail/components/section-card.tsx +85 -0
- package/dist-lib/ai/exemplos/order-detail/index.ts +1 -0
- package/dist-lib/ai/exemplos/order-detail/order-detail-screen.tsx +119 -0
- package/dist-lib/ai/exemplos/order-detail/order-mocks.ts +186 -0
- package/dist-lib/ai/exemplos/order-detail/order.types.ts +118 -0
- package/dist-lib/ai/global/componentes.md +217 -0
- package/dist-lib/ai/global/composicao.md +182 -0
- package/dist-lib/ai/indice.json +192 -0
- package/dist-lib/ai/lint/ds-lint-patterns.mjs +115 -0
- package/dist-lib/ai/manifest.json +42 -0
- package/dist-lib/ai/regras/design.md +88 -0
- package/dist-lib/ai/regras/temas.md +192 -0
- package/dist-lib/ai/regras-por-componente.json +103 -0
- package/dist-lib/ai/roteiros/app-builder/roteiro.md +155 -0
- package/dist-lib/ai/roteiros/auth-builder/roteiro.md +42 -0
- package/dist-lib/ai/roteiros/cards/roteiro.md +34 -0
- package/dist-lib/ai/roteiros/charts/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/chat/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/crud-builder/blueprint.md +63 -0
- package/dist-lib/ai/roteiros/crud-builder/entrevista.md +139 -0
- package/dist-lib/ai/roteiros/crud-builder/geracao.md +113 -0
- package/dist-lib/ai/roteiros/crud-builder/roteiro.md +89 -0
- package/dist-lib/ai/roteiros/dashboard-builder/blueprint.md +47 -0
- package/dist-lib/ai/roteiros/dashboard-builder/entrevista.md +62 -0
- package/dist-lib/ai/roteiros/dashboard-builder/geracao.md +88 -0
- package/dist-lib/ai/roteiros/dashboard-builder/roteiro.md +88 -0
- package/dist-lib/ai/roteiros/drawers/roteiro.md +41 -0
- package/dist-lib/ai/roteiros/list-builder/blueprint.md +94 -0
- package/dist-lib/ai/roteiros/list-builder/entrevista.md +188 -0
- package/dist-lib/ai/roteiros/list-builder/geracao.md +63 -0
- package/dist-lib/ai/roteiros/list-builder/roteiro.md +91 -0
- package/dist-lib/ai/roteiros/module-replicator/roteiro.md +56 -0
- package/dist-lib/ai/roteiros/page-detail/roteiro.md +32 -0
- package/dist-lib/ai/roteiros/page-edit/roteiro.md +31 -0
- package/dist-lib/ai/roteiros/screen-composer/roteiro.md +95 -0
- package/package.json +4 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AppShell": [
|
|
3
|
+
"tem áreas separadas (Comercial, Financeiro…)? → `sidebar=\"menu\"` + `contexts`. Não tem? → `\"single\"` + `categories`, sem `sidebarModules` nem `sidebarShowSearch`",
|
|
4
|
+
"NÃO passe `sidebarLogo`: o default é a marca iGreen. Só com marca própria pedida explicitamente",
|
|
5
|
+
"`sidebarTitle` = nome do projeto (vai à direita da logo) — pergunte, não invente"
|
|
6
|
+
],
|
|
7
|
+
"avatar-ig": [
|
|
8
|
+
"vários avatares juntos → `<AvatarGroup>`, nunca `-ml-*` + `ring` na mão: ele resolve sobreposição por tamanho, cor do anel e excedente",
|
|
9
|
+
"`<AvatarGroup surface=\"...\">` = a superfície ATRÁS do grupo (`table` numa linha de tabela). Errar isso põe um halo claro em volta de cada avatar",
|
|
10
|
+
"`max` + `total`: o `+N` conta pelo `total` (o do servidor), senão uma lista paginada mostra `+0` tendo 40 pessoas",
|
|
11
|
+
"`colorHex` escolhe a cor do texto por contraste WCAG (L-027) — nunca `text-white` na unha",
|
|
12
|
+
"foto de pessoa → `<Avatar src=\"…\">` com as iniciais em `children` (são o fallback da URL que falha), nunca `<img>` solto nem o compound do shadcn dentro do grupo"
|
|
13
|
+
],
|
|
14
|
+
"Breadcrumb": [
|
|
15
|
+
"o caminho e o seletor vêm do MESMO item: `import { Breadcrumb, BreadcrumbItem, BreadcrumbSwitcher } from \"@/components/ui/Breadcrumb\"`",
|
|
16
|
+
"página de DETALHE (ficha de cliente, UC, contrato, chamado) → o item do registro no breadcrumb é `<BreadcrumbSwitcher>`, não texto: quem está numa ficha quer pular pra outra, não voltar à lista",
|
|
17
|
+
"é controlado e **não navega**: `onValueChange` devolve o `value` e quem decide rota/fetch é você",
|
|
18
|
+
"no `Header`, o item vira seletor com os TRÊS juntos — `switcher` + `value` + `onValueChange`; faltando um, fica texto",
|
|
19
|
+
"escolher valor de FORMULÁRIO não é isto: é `combobox` (o trigger dele tem cara de campo de propósito)"
|
|
20
|
+
],
|
|
21
|
+
"CardOption": [
|
|
22
|
+
"switch = efeito IMEDIATO, sem Salvar, e só em `layout=\"list\"`; tela com botão Salvar → `type=\"checkbox\"`",
|
|
23
|
+
"opções que se COMPARAM (plano, tier, preço, descrição longa) → cards espaçados; itens do mesmo tipo com rótulo curto (settings, permissões, pagamento, endereço) → `layout=\"list\"`",
|
|
24
|
+
"mais de ~5 opções → não é CardOption: `Select`/`Combobox`. E on/off de UMA coisa nunca são 2 radios",
|
|
25
|
+
"`type=\"radio\"` EXIGE `<CardOptionGroup type=\"radio\">` em volta; checkbox e switch funcionam soltos",
|
|
26
|
+
"em lista o selecionado NÃO pinta por default (a borda ali é a divisória) — queira pintado? `highlightSelected` no grupo",
|
|
27
|
+
"omita `orientation`, `highlightSelected` e `size`: derivam do type · `md` é o calibrado"
|
|
28
|
+
],
|
|
29
|
+
"DataTable": [
|
|
30
|
+
"filtro é NATIVO e reativo: `enableColumnFilter` na coluna — nunca select/form solto acima da grade. Pra ele aparecer desde o load (e não ficar escondido atrás do ícone), `showEmptyFilterChips={[\"status\", …]}`",
|
|
31
|
+
"não fixe `width`: com `autoFit` (default) ele é PISO e entra no rateio, não trava. Travar de verdade = `width` + `maxWidth` iguais",
|
|
32
|
+
"vazio são DOIS casos distintos: sem dado nenhum → `renderEmpty` (CTA de criar); filtro/busca zerou → `renderNoResults` (o \"limpar filtros\" já vem cabeado)"
|
|
33
|
+
],
|
|
34
|
+
"FloatingPanel": [
|
|
35
|
+
"painel de DETALHE → siga o bloco `dsgreen-paneldetail-1`, que é o **padrão**. As variações são opt-in: use `-2` (tarefa com abas) ou `-3` (com tabela, `size=\"xl\"`) **só se o usuário citar o ID**",
|
|
36
|
+
"em todos: identidade ou contexto no `titleSlot`, ações de ícone `soft` + `aria-label` no `headerActions`, ação primária no `footer` — nunca botão de ação solto no corpo",
|
|
37
|
+
"`bodyPadded={false}` quando usar `FloatingPanelSection` (a section gerencia padding e divisória full-width)",
|
|
38
|
+
"é REDIMENSIONÁVEL em runtime — nenhuma largura escrita na mão acompanha o arrasto",
|
|
39
|
+
"aba dentro dele → `<Tabs fullWidth>` na variante default; `line` aqui vira trilho curto"
|
|
40
|
+
],
|
|
41
|
+
"Gantt": [
|
|
42
|
+
"monte a tela a partir do `example-gantt` (`igreen:add example-gantt`) — é o comportamento COMPLETO, não um toy",
|
|
43
|
+
"o pai precisa ter ALTURA (o componente é `h-full`): sem isso você vê só a toolbar",
|
|
44
|
+
"busca do servidor → passe `loading`, senão `rows={[]}` afirma \"Nenhuma tarefa neste período\"",
|
|
45
|
+
"`colorKey` diz CATEGORIA (qual frente), não status: status vai em `row.trailing` como `Chip`"
|
|
46
|
+
],
|
|
47
|
+
"Kpi": [
|
|
48
|
+
"`tone` e `KpiDelta tone` são DECISÃO, não derivação: subir nem sempre é bom (tempo de espera ↑ é ruim). `signed` só quando o sinal é literalmente bom/ruim",
|
|
49
|
+
"o valor sai do preset `stat-*` pela prop `size` (default `md`=24px) — nunca `text-[Npx]` na unha; fora do `Kpi`, `text-stat-* tabular-nums`",
|
|
50
|
+
"`divided` no `KpiGroup` vira UM card com divisórias e os filhos viram `plain` — não empilhe card dentro de card pra conseguir isso"
|
|
51
|
+
],
|
|
52
|
+
"Modal": [
|
|
53
|
+
"aba dentro dele → `<Tabs fullWidth>` até o size `lg`; no `xl` (1100px) use hug",
|
|
54
|
+
"ação destrutiva → `AlertModal`, não confirmação montada na mão"
|
|
55
|
+
],
|
|
56
|
+
"Panel": [
|
|
57
|
+
"painel de DETALHE → siga o bloco `dsgreen-paneldetail-1` (o **padrão**). `-2` (tarefa com abas) e `-3` (com tabela) só se o usuário citar o ID",
|
|
58
|
+
"header aqui é só STRING (`title`/`description`): precisa de avatar, Chip de status ou ação de ícone? → `FloatingPanel`",
|
|
59
|
+
"form dentro dele → `<FormField>` + `gap-form-gap`, nunca `<label>` cru",
|
|
60
|
+
"aba dentro dele → `<Tabs fullWidth>` na variante default (560px é estreito pro `line`)"
|
|
61
|
+
],
|
|
62
|
+
"ScreenLoader": [
|
|
63
|
+
"omita `size`: `md` é o calibrado pro slot do AppShell; `lg` não é \"pra dar destaque\"",
|
|
64
|
+
"omita `skeletonLayout`: `page` serve pra qualquer tela — só mude se ela TEM KPIs no topo",
|
|
65
|
+
"o pai precisa ter altura, senão a variante spinner colapsa no topo",
|
|
66
|
+
"loading inline (botão, célula) → `Spinner` direto; overlay é composição do consumidor"
|
|
67
|
+
],
|
|
68
|
+
"SingleMenuSidebar": [
|
|
69
|
+
"NÃO passe `logo`: o default é a marca iGreen. Só com marca própria pedida explicitamente",
|
|
70
|
+
"`title` = nome do projeto (vai à direita da logo) — pergunte, não invente",
|
|
71
|
+
"é a escolha quando NÃO há divisão em áreas: `showSearch={false}`, sem `module`/`modules`"
|
|
72
|
+
],
|
|
73
|
+
"TabsNavigation": [
|
|
74
|
+
"aba de NAVEGADOR (sessão que abre/fecha: conversa, chamado, registro) → `<TabsNavigation>`; filtro de conteúdo dentro da tela → `tabs` do shadcn",
|
|
75
|
+
"`<TabsNavigation>` é SEMPRE controlado (`value` + `onValueChange`) e NÃO hospeda conteúdo — o painel pode morar fora, e aí a aba leva `panelId`",
|
|
76
|
+
"`surface` = a superfície do conteúdo ABAIXO da tira (`surface` num card, `canvas` na página). Errar quebra a união da aba ativa, que é o componente",
|
|
77
|
+
"`actions` na aba SUBSTITUI o `⋯`+`×` — é por ele que entram ✓/✗ de aceitar/recusar; sem `actions` e sem `onClose` a aba não tem ação"
|
|
78
|
+
],
|
|
79
|
+
"tabs": [
|
|
80
|
+
"`variant` default (segmented) dentro de superfície (card, bloco, Panel, Modal, drawer); `line` só pra seção de página",
|
|
81
|
+
"`fullWidth` em superfície compacta; ⛔ nunca `w-full` no List nem `flex-1` no trigger na mão"
|
|
82
|
+
],
|
|
83
|
+
"drawer": [
|
|
84
|
+
"é bottom-sheet (vaul): mobile ou gesto de arrastar. Detalhe lateral em desktop → `Panel` (bloqueia) ou `FloatingPanel` (coexiste com a lista)",
|
|
85
|
+
"painel de DETALHE → siga o bloco `dsgreen-paneldetail-1` (o **padrão**). `-2` (tarefa com abas) e `-3` (com tabela) só se o usuário citar o ID"
|
|
86
|
+
],
|
|
87
|
+
"select": [
|
|
88
|
+
"⛔ nunca `<SelectItem value=\"\">` — é a sentinela do Radix e apaga valor em silêncio",
|
|
89
|
+
"em form, prefira `FormFieldSelect` (já trata a sentinela e o eco do valor)"
|
|
90
|
+
],
|
|
91
|
+
"card": [
|
|
92
|
+
"`size` (`sm`/`md`/`lg`) declare SÓ no `<Card>` — Header/Content/Footer herdam por contexto; repetir nas partes é o que deixa uma seção com densidade diferente",
|
|
93
|
+
"`CardHeader variant=\"banded\"` com ação à direita pede `className=\"flex …\"`, NUNCA `flex-row`: o header é `grid` e `flex-row` não troca o display — o botão cai embaixo do título"
|
|
94
|
+
],
|
|
95
|
+
"alert-dialog": [
|
|
96
|
+
"confirmação comum → use o composto `ui/AlertModal` (já monta tom + ícone + botões); o primitivo cru só pra caso fora do padrão",
|
|
97
|
+
"⚠️ ESC FECHA (só o clique fora é bloqueado). Decisão inescapável exige `onEscapeKeyDown={(e) => e.preventDefault()}`"
|
|
98
|
+
],
|
|
99
|
+
"sonner": [
|
|
100
|
+
"`<Toaster/>` **1× no root**, senão o toast não aparece e não há erro",
|
|
101
|
+
"card ergonômico (title/description/action) → use o composto `ui/Toast`"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# app-builder — Esqueleto de aplicação (AppShell + navegação + rotas)
|
|
9
|
+
|
|
10
|
+
O "chassi" onde as telas moram. Não é uma tela de conteúdo — é o shell + nav +
|
|
11
|
+
roteamento. Conteúdo de cada tela vem depois dos builders (crud/list/dashboard).
|
|
12
|
+
Não gere de memória: puxe e adapte o exemplo.
|
|
13
|
+
|
|
14
|
+
## Passo 0 — duas perguntas ao usuário, nesta ordem
|
|
15
|
+
|
|
16
|
+
### 1. Como o projeto se chama?
|
|
17
|
+
|
|
18
|
+
Vai **à direita da logo** na sidebar (`sidebarTitle`), na raiz do breadcrumb e no título da
|
|
19
|
+
aba. É a única coisa deste passo que o DS não adivinha — e `sidebarTitle` é obrigatória de
|
|
20
|
+
propósito, pra o TS cobrar a pergunta.
|
|
21
|
+
|
|
22
|
+
### 2. O app tem módulos?
|
|
23
|
+
|
|
24
|
+
⚠️ **Vocabulário — leia ANTES de formular a pergunta.** A mesma coisa tem três nomes na boca
|
|
25
|
+
do usuário: **módulo**, **workspace** e, às vezes, **categoria**. E "categoria" é armadilha:
|
|
26
|
+
na API da sidebar única, `categories` são os **grupos do menu**, não os módulos — a palavra
|
|
27
|
+
nomeia os dois lados da escolha. Então **não** use "categoria" na pergunta, e **não** use
|
|
28
|
+
nome de componente (`MenuSidebar`/`SingleMenuSidebar`): descreva **o que aparece na tela**.
|
|
29
|
+
|
|
30
|
+
**Espelhe a palavra do usuário** — se ele disse "módulo" ou "workspace", use a dele. **Exceto
|
|
31
|
+
"categoria"**: essa é ambígua e você **não pode** adivinhar. Se ele disse "categorias",
|
|
32
|
+
desambigue antes de qualquer coisa:
|
|
33
|
+
|
|
34
|
+
> *"Quando você fala 'categorias', são **áreas grandes**, cada uma com o menu dela — tipo
|
|
35
|
+
> Comercial, Financeiro, Suporte? Ou são os **grupos dentro de um menu só** — tipo Cadastros,
|
|
36
|
+
> Relatórios, Configurações?"*
|
|
37
|
+
|
|
38
|
+
**Se ele JÁ listou as áreas** ("tenho Comercial, Financeiro e Suporte"), **não pergunte do
|
|
39
|
+
zero** — recomende e confirme numa linha: *"Como você tem 3 áreas separadas, vou de menu com
|
|
40
|
+
a coluna de módulos à esquerda (é o link X). Confirma?"*.
|
|
41
|
+
|
|
42
|
+
Fora isso, ofereça **duas** opções, e só essas duas — **com recomendação**, não neutro:
|
|
43
|
+
|
|
44
|
+
> *"Seu sistema é dividido em áreas grandes e separadas — tipo Comercial, Financeiro,
|
|
45
|
+
> Suporte — cada uma com o menu dela? Ou é um sistema só, com um menu único?*
|
|
46
|
+
>
|
|
47
|
+
> *1. **Tem áreas separadas** — cada área vira um ícone numa coluna estreita à esquerda, e o
|
|
48
|
+
> menu dela (com submenus) abre ao lado:
|
|
49
|
+
> https://igreen-desingsystem-admin.vercel.app/#/menu-sidebar*
|
|
50
|
+
>
|
|
51
|
+
> *2. **É um sistema só** — sem a coluna de ícones: só o nome do projeto no topo e, abaixo, os
|
|
52
|
+
> grupos do menu com seus submenus:
|
|
53
|
+
> https://igreen-desingsystem-admin.vercel.app/#/single-menu-sidebar*
|
|
54
|
+
>
|
|
55
|
+
> *Pelo que você me contou, eu iria de **<a que você inferiu>** — mas abra os dois links e me
|
|
56
|
+
> diga."*
|
|
57
|
+
|
|
58
|
+
⚠️ **Recomende sempre uma das duas**, com o motivo em meia linha ("porque você tem 3 áreas
|
|
59
|
+
distintas" / "porque é um sistema só"). Pergunta neutra devolve a decisão pra quem não conhece
|
|
60
|
+
os componentes — e foi assim que um consumidor escolheu errado sem querer (2026-08-22).
|
|
61
|
+
|
|
62
|
+
| resposta | `sidebar` | dados | o que NÃO passar |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| **tem áreas/módulos** | `"menu"` (default) | `contexts` | — |
|
|
65
|
+
| **é um sistema só** | `"single"` | `categories` | `sidebarModules` e `sidebarShowSearch` — é a variação **"Sem módulo / sem busca"** |
|
|
66
|
+
|
|
67
|
+
**Regra de tendência:** existe divisão → `"menu"`. Não existe → `"single"` enxuta. A sidebar
|
|
68
|
+
única **também** aceita módulos (`sidebarModules`), e é justamente o que não oferecer: quando
|
|
69
|
+
existe divisão, o rail da `"menu"` mostra as áreas todas de uma vez, enquanto na única elas
|
|
70
|
+
ficam atrás de um botão acima da busca. Quer conhecer as outras variações? Mande a página do
|
|
71
|
+
componente — ampliar o leque na entrevista é o que produz escolha errada.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<AppShell
|
|
75
|
+
sidebar="single"
|
|
76
|
+
categories={CATEGORIES}
|
|
77
|
+
sidebarTitle="Meu Sistema" {/* ← o nome do projeto, à direita da logo */}
|
|
78
|
+
activeItemId={ativo}
|
|
79
|
+
onSidebarItemClick={setAtivo}
|
|
80
|
+
breadcrumb={[{ label: "Meu Sistema" }]}
|
|
81
|
+
>…</AppShell>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Logo e nome — aplique, não pergunte
|
|
85
|
+
|
|
86
|
+
- **A logo é a da iGreen, sempre.** **Não passe `sidebarLogo`** — o default já é a marca
|
|
87
|
+
(lib 0.46.0+; antes a prop era obrigatória, e trocar pra sidebar única fazia a logo da
|
|
88
|
+
iGreen desaparecer, porque a API pedia outra). Só troque se o usuário pedir marca própria
|
|
89
|
+
**explicitamente**, e aí ele fornece o arquivo. **Nunca pergunte "qual logo?"**.
|
|
90
|
+
- **O nome do projeto vai à direita dela** — é a resposta da pergunta 1.
|
|
91
|
+
- Numa lib anterior a essa a prop é obrigatória: passe
|
|
92
|
+
`sidebarLogo={<SidebarBrandMark />}` (de `@/components/ui/MenuSidebar/sidebar-brand`) em
|
|
93
|
+
vez de inventar uma.
|
|
94
|
+
|
|
95
|
+
**O AppShell monta as DUAS** (lib ≥ 0.41.0). O tipo é união discriminada: o TS cobra o
|
|
96
|
+
conjunto certo pra cada escolha. O toggle do Header funciona nos dois sem cabeamento — e na
|
|
97
|
+
variante `single` ele **sai** do Header no desktop, porque a sidebar tem o próprio botão (no
|
|
98
|
+
mobile volta, senão não haveria como abrir).
|
|
99
|
+
|
|
100
|
+
⚠️ **Não ligue `sidebarShowSearch`** se o Header já tem `commandGroups` — seriam duas buscas
|
|
101
|
+
na mesma tela. O shell entrega a busca da sidebar **desligada** por default.
|
|
102
|
+
|
|
103
|
+
> ⚠️ **Até o CLI 0.25.0 esta seção dizia o contrário** — que o AppShell só montava com
|
|
104
|
+
> `menu-sidebar`, porque o `single-menu-sidebar` "não tem colapso nem drawer mobile". **Era
|
|
105
|
+
> falso**: ele tem colapso controlado (`expanded`) e tem mobile. Se você montou layout na mão
|
|
106
|
+
> por causa dessa instrução, agora dá pra usar o AppShell.
|
|
107
|
+
|
|
108
|
+
**Item de menu com destino → declare `href`** (as duas sidebars emitem `<a>`: ctrl+clique e
|
|
109
|
+
nova aba funcionam). Com router próprio, passe
|
|
110
|
+
`renderLink={({ href, ...rest }) => <Link to={href} {...rest} />}`.
|
|
111
|
+
|
|
112
|
+
**AppShell dentro de container com altura** (layout com footer seu, aba, preview) →
|
|
113
|
+
`fillHeight`. Sem isso ele mede 100vh, transborda e o conteúdo fica cortado na base.
|
|
114
|
+
|
|
115
|
+
## Fluxo
|
|
116
|
+
|
|
117
|
+
1. `npm run igreen:add -- example-app-shell` (traz o esqueleto + AppShell, MenuSidebar, Header, PageHeader).
|
|
118
|
+
2. **Leia** `src/examples/app-shell/` — 3 arquivos:
|
|
119
|
+
- `nav-data.ts` — contextos (módulos do rail) + itens/subitens + helpers.
|
|
120
|
+
- `routes.tsx` — **mapa de rotas declarativo** (href → tela) + resolveRoute.
|
|
121
|
+
- `app-shell-example.tsx` — o cabeamento do AppShell (não costuma mexer).
|
|
122
|
+
+ `src/components/ui/AppShell/USAGE.md`.
|
|
123
|
+
3. Adapte: **`nav-data.ts`** (os módulos/telas do app do usuário) e **`routes.tsx`**
|
|
124
|
+
(uma linha por tela; troque `<StubPage/>` pela tela real conforme for criando).
|
|
125
|
+
4. Ligue ao seu app (renderize `<AppShellExample/>` na raiz, ou copie o cabeamento).
|
|
126
|
+
`npx tsc --noEmit` limpo.
|
|
127
|
+
|
|
128
|
+
> **Modo submódulo (ds-link).** Se existe `.claude/ds-config.json` com `"mode": "submodule"`,
|
|
129
|
+
> componentes/exemplos JÁ estão em `<dsPath>/src` — **não** rode `igreen:add`. Use `importBase`
|
|
130
|
+
> do config e leia o exemplo em `<dsPath>/src/examples/app-shell/`.
|
|
131
|
+
|
|
132
|
+
## Gotchas do tipo
|
|
133
|
+
|
|
134
|
+
- **Rota é um MAPA declarativo, não `if/else`**: registrar tela = 1 linha em
|
|
135
|
+
`ROUTES` (`"#/path": () => <Screen/>`). Nunca reintroduza a cadeia de `if`.
|
|
136
|
+
- **AppShell é controlled pra navegação real**: ligue `activeContextId`/`onContextChange`
|
|
137
|
+
+ `activeItemHref`/`onItemClick` ao seu router; `href` = path. Os `default*` são só
|
|
138
|
+
pro modo uncontrolled/preview.
|
|
139
|
+
- **breadcrumb + ⌘K + notificações derivam da nav** (não hardcode): veja como o
|
|
140
|
+
exemplo mapeia `NAV_ENTRIES` → commandGroups e a entry ativa → breadcrumb.
|
|
141
|
+
- **Tema**: o exemplo usa state local que alterna `.dark` no `<html>`. Se o app já
|
|
142
|
+
tem provider de tema, ligue nele (`theme` + `onThemeChange`).
|
|
143
|
+
- **Conteúdo NÃO vai aqui**: cada tela é um builder (crud/list/dashboard). O shell
|
|
144
|
+
só navega e resolve a rota.
|
|
145
|
+
- Fullscreen sem outro shell por volta. Body do AppShell já tem padding próprio —
|
|
146
|
+
**responsivo**: 18px (<768) · 24px (768–1535) · 32px (≥1536). Não adicione padding
|
|
147
|
+
por fora nem por dentro do slot.
|
|
148
|
+
- **O menu NASCE COLAPSADO abaixo de 1536px.** Notebook (1366/1440) abre com o rail,
|
|
149
|
+
ganhando ~264px de largura útil. Se o app precisa do menu sempre aberto, passe
|
|
150
|
+
**`defaultMenuCollapsed={false}` explícito** — o default responsivo só vale quando a
|
|
151
|
+
prop é omitida. É aplicado **só no mount**: não reage a resize, de propósito (senão
|
|
152
|
+
brigaria com quem abriu o menu na mão). Abaixo de 768px o menu vira drawer e ignora
|
|
153
|
+
isso.
|
|
154
|
+
|
|
155
|
+
Aplique `DESIGN.md` + `.claude/rules/ds-design.md`. Handoff: `APP_PRONTO: <app>` + rotas.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# auth-builder — Tela de login / autenticação
|
|
9
|
+
|
|
10
|
+
Tela de login split (form + painel de marca) usando o padrão do DS. Não gere de
|
|
11
|
+
memória — puxe e adapte o exemplo.
|
|
12
|
+
|
|
13
|
+
## Fluxo
|
|
14
|
+
|
|
15
|
+
1. `npm run igreen:add -- example-login` (traz a tela + `Button`, `FormField`).
|
|
16
|
+
2. **Leia** `src/examples/login/login-screen.tsx` + `src/components/ui/FormField/USAGE.md`.
|
|
17
|
+
3. Adapte ao caso: marca/copy do painel direito, campos do form (email/senha,
|
|
18
|
+
ou telefone/código, SSO…), e o handler `entrar` (plugue a auth real).
|
|
19
|
+
4. Registre a rota onde o usuário indicar (login é fullscreen, sem AppShell).
|
|
20
|
+
`npx tsc --noEmit` limpo.
|
|
21
|
+
|
|
22
|
+
> **Modo submódulo (ds-link).** Se existe `.claude/ds-config.json` com `"mode": "submodule"`,
|
|
23
|
+
> o DS é consumido como **submódulo** (não copy-in): componentes/exemplos JÁ estão no disco
|
|
24
|
+
> em `<dsPath>/src` e **não** há registry. Use `importBase` do config (ex.: `@ds/components/ui/FormField`)
|
|
25
|
+
> e leia o exemplo direto em `<dsPath>/src/examples/login/login-screen.tsx` — **NÃO** rode `igreen:add`.
|
|
26
|
+
|
|
27
|
+
## Gotchas do tipo
|
|
28
|
+
|
|
29
|
+
- **Login é fullscreen SEM chrome de app** — não embrulhe em `AppShell`/`MenuSidebar`.
|
|
30
|
+
Root = `flex min-h-screen ... items-center justify-center bg-bg-canvas`.
|
|
31
|
+
- **Sempre `<FormField>`** (FormFieldInput/Checkbox) — nunca `<label>` cru (L-023).
|
|
32
|
+
Spacing do form = `gap-form-gap` (L-024). Inputs `min-h-form-xl` (44px, WCAG mobile).
|
|
33
|
+
- **Painel de marca = 100% tokens, sem imagem externa** (evita "imagem quebrada"):
|
|
34
|
+
`bg-bg-brand` + camadas decorativas via `var(--color-fg-on-brand)` + texto
|
|
35
|
+
`text-fg-on-brand`. Some no mobile (`hidden lg:flex`). Se PRECISAR de foto, use
|
|
36
|
+
caminho base-aware (`import.meta.env.BASE_URL`), nunca `/arquivo.png` absoluto.
|
|
37
|
+
- **Botão Entrar** = `<Button color="primary" variant="filled" size="lg" className="w-full">`.
|
|
38
|
+
Toggle de senha = `<button type="button">` no `endAddon` do FormFieldInput.
|
|
39
|
+
- Ring/focus por token (`focus-visible:ring-4 focus-visible:ring-ring-brand`), nunca
|
|
40
|
+
`outline-none` sozinho.
|
|
41
|
+
|
|
42
|
+
Aplique `DESIGN.md` + `.claude/rules/ds-design.md`. Handoff: `LOGIN_PRONTO: <marca>` + rota.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# cards — Composição de cards/painéis
|
|
9
|
+
|
|
10
|
+
**(Guia de composição, não puxa example.)** Cards são blocos; a tela ao redor
|
|
11
|
+
segue `DESIGN.md`. Para uma TELA inteira baseada em cards (dashboard), use a skill `dashboard-builder` (`/ds-create-dashboard`).
|
|
12
|
+
|
|
13
|
+
## Fluxo
|
|
14
|
+
1. `npm run igreen:add -- card` (shadcn Card: `Card`/`CardHeader`/`CardTitle`/`CardContent`/`CardFooter`) — bloco simples.
|
|
15
|
+
2. `npm run igreen:add -- panel` (iGreen `Panel` + `CardHead`) — card com cabeçalho título+subtítulo+ação, usado em dashboards.
|
|
16
|
+
3. **Leia** os respectivos `USAGE.md`. Para ver variações montadas, consulte o **showcase hospedado**: https://igreen-desingsystem-admin.vercel.app
|
|
17
|
+
4. Componha os cards na tela do usuário; `npx tsc --noEmit` limpo.
|
|
18
|
+
|
|
19
|
+
> **Modo submódulo (ds-link).** Se existe `.claude/ds-config.json` com `"mode": "submodule"`,
|
|
20
|
+
> o DS é consumido como **submódulo** (não copy-in): os componentes/exemplos JÁ estão no disco
|
|
21
|
+
> em `<dsPath>/src` e **não** há registry. Use `importBase` do config (ex.: `@ds/components/ui/Panel`)
|
|
22
|
+
> e leia os `USAGE.md`/exemplos direto em `<dsPath>/src` — **NÃO** rode `igreen:add`.
|
|
23
|
+
|
|
24
|
+
## Gotchas do tipo
|
|
25
|
+
- Card: use o componente **`<Card>`** (não replique a superfície na unha — a lista de classes que estava aqui divergia do componente real em 3 itens). Densidade pela prop **`size`**: `sm` 16px · **`md` 20px (default)** · `lg` 24px, e ela vale pro `CardHeader`/`CardContent`/`CardFooter` juntos (declare só no `<Card>`).
|
|
26
|
+
- Header em faixa: **`<CardHeader variant="banded">`** — fundo `bg-bg-subtle` + divisória embaixo, encostado nas bordas. Aceita qualquer estrutura dentro e acompanha o `size`. Use quando a seção precisa de cabeçalho destacado (ficha, detalhe, formulário por etapas).
|
|
27
|
+
- Título de card: **não passe `font-*`** — `text-title-md` já é semibold (600) por preset.
|
|
28
|
+
- Grid de cards: `grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-gp-md`.
|
|
29
|
+
- KPI card: label `caption-md`/`uppercase` em `fg-muted` + valor grande **`text-stat-lg`/`text-stat-md` + `tabular-nums`** (nunca `display-*`/`heading-*`) + opcional `Chip` de tendência. (Ver `example-dashboard`.)
|
|
30
|
+
- Card de seção (label/valor): título da seção `title`, linhas rótulo (`fg-muted`) + valor.
|
|
31
|
+
- Dark mode: sombra vira borda hairline (automático via token).
|
|
32
|
+
- **Don't:** não invente padding/gap avulso — no `<Card>` a densidade é a prop `size`, e fora dele use `p-pad-card-*` e `gap-gp-*`; não use hex (cor por token); não empilhe cards "grudados" (`gap-gp-md` entre eles).
|
|
33
|
+
|
|
34
|
+
Aplique `DESIGN.md`. Handoff: `CARDS_PRONTO`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# charts — Gráficos (Chart / Recharts 3)
|
|
9
|
+
|
|
10
|
+
## Fluxo
|
|
11
|
+
1. `npm run igreen:add -- chart` (e `example-dashboard` se quiser gráfico em contexto de painel).
|
|
12
|
+
2. **Leia** `src/components/ui/Chart/USAGE.md` (API: `ChartContainer` + `ChartConfig`) + os gráficos em `src/examples/dashboard/dashboard-screen.tsx`.
|
|
13
|
+
3. Monte o gráfico dentro de `<ChartContainer config={...}>`; cor só por token.
|
|
14
|
+
4. Registre a rota/local onde o usuário indicar. `npx tsc --noEmit` limpo.
|
|
15
|
+
|
|
16
|
+
> **Modo submódulo (ds-link).** Se existe `.claude/ds-config.json` com `"mode": "submodule"`,
|
|
17
|
+
> o DS é consumido como **submódulo** (não copy-in): os componentes/exemplos JÁ estão no disco
|
|
18
|
+
> em `<dsPath>/src` e **não** há registry. Use `importBase` do config (ex.: `@ds/components/ui/Chart`)
|
|
19
|
+
> e leia `<dsPath>/src/components/ui/Chart/USAGE.md` + `<dsPath>/src/examples/dashboard/dashboard-screen.tsx`
|
|
20
|
+
> direto — **NÃO** rode `igreen:add`.
|
|
21
|
+
|
|
22
|
+
## Gotchas do tipo
|
|
23
|
+
- Gráfico SEMPRE em `<ChartContainer config={...}>`; cor só por token (`chart-1..5` / chaves do config). 2 séries = verde (`chart-1`) + âmbar (`chart-4`); pizza = rampa monocromática da marca.
|
|
24
|
+
- Grid: `<CartesianGrid vertical={false} strokeDasharray="4 4" />` (token `chart-grid`, **sem** passar `stroke` — o `ChartContainer` reescreve via token).
|
|
25
|
+
- Card do gráfico: `Panel`/`Card` + título (`title`/`heading`).
|
|
26
|
+
- **Caveats do Recharts 3 (quebram mudo):**
|
|
27
|
+
1. `text-display-sm`/`display-xs` **não existem** → valor de KPI usa **`text-stat-{sm,md,lg,xl}`** (20/24/30/34px, bold, com `tabular-nums`). Nunca `display-*`/`heading-*` pra número de indicador.
|
|
28
|
+
2. Pizza: sem `activeIndex`/`activeShape` → use `shape={(props,index)=><Sector/>}`.
|
|
29
|
+
3. Radial empilhado/gauge parcial → `<PolarAngleAxis type="number" domain={[0,total]} />`.
|
|
30
|
+
4. Eixo Y omite tick de borda (ex.: `0`) → `interval={0}`; `domain` máximo = maior tick (senão linha-guia duplicada no topo).
|
|
31
|
+
|
|
32
|
+
Aplique `DESIGN.md`. Para uma TELA inteira de painel, use a skill `dashboard-builder` (`/ds-create-dashboard`). Handoff: `CHART_PRONTO: <tipo>`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# chat — Inbox / conversas
|
|
9
|
+
|
|
10
|
+
Tela de chat: coluna de conversas + thread de mensagens + composer.
|
|
11
|
+
|
|
12
|
+
## Fluxo
|
|
13
|
+
1. `npm run igreen:add -- example-chat` (traz a tela + componentes).
|
|
14
|
+
2. **Leia** `src/examples/chat/chat-screen.tsx` + os sub-componentes (ConversationColumn, etc) + USAGE dos componentes DS usados.
|
|
15
|
+
3. Adapte canais, conversas e mensagens aos dados do usuário.
|
|
16
|
+
4. Registre a rota. `npx tsc --noEmit` limpo.
|
|
17
|
+
|
|
18
|
+
> **Modo submódulo (ds-link).** Se existe `.claude/ds-config.json` com `"mode": "submodule"`,
|
|
19
|
+
> o DS é consumido como **submódulo** (não copy-in): os componentes/exemplos JÁ estão no disco
|
|
20
|
+
> em `<dsPath>/src` e **não** há registry. Use `importBase` do config (ex.: `@ds/components/ui/avatar-ig`)
|
|
21
|
+
> e leia o exemplo direto em `<dsPath>/src/examples/chat/chat-screen.tsx` —
|
|
22
|
+
> **NÃO** rode `igreen:add`.
|
|
23
|
+
|
|
24
|
+
## Gotchas do tipo
|
|
25
|
+
- Chat ocupa 100% da altura disponível: wrapper `flex flex-col h-full min-h-0` (sem o gap-2xl de página comum — a tela é edge-to-edge).
|
|
26
|
+
- Layout em colunas: lista de conversas (largura fixa) + thread (flex-1) + opcional painel de detalhe.
|
|
27
|
+
- Avatares de pessoa via **`avatar-ig`** (`@/components/ui/avatar-ig`) — cor por `colorHex` quando custom, com contraste WCAG automático. O item `avatar` do registry é o Radix puro e **não** tem `colorHex`.
|
|
28
|
+
- Status de canal/online via dot/`Chip`.
|
|
29
|
+
- Scroll independente por coluna; composer fixo no rodapé da thread.
|
|
30
|
+
|
|
31
|
+
Aplique `DESIGN.md` + regras. Handoff: `CHAT_PRONTO` + rota.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
> **Modo submódulo (`ds-link`).** Existe `.claude/ds-config.json` com `"mode": "submodule"`?
|
|
8
|
+
> Então **NÃO rode `igreen:add`** — esse script não existe no seu projeto. Os componentes e
|
|
9
|
+
> exemplos já estão no disco em `<dsPath>/src`: importe pelo `importBase` do config
|
|
10
|
+
> (compostos) e pelo `primitivesBase` (primitivos shadcn), e **leia** o exemplo direto de
|
|
11
|
+
> `<dsPath>/src/examples/`.
|
|
12
|
+
|
|
13
|
+
# CRUD Builder — Blueprint [GATE]
|
|
14
|
+
|
|
15
|
+
Consolide a entrevista num preview ÚNICO e **pare** pra aprovação. Nenhuma edição
|
|
16
|
+
em disco antes do "aprovar".
|
|
17
|
+
|
|
18
|
+
## Apresente ao usuário
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Entidade: <Nome> (tipo <NomeRow>)
|
|
22
|
+
Modo: client | server (endpoint: ...)
|
|
23
|
+
Onde: <PAGES_DIR>/<Arquivo>.tsx · registro: <REGISTRO>
|
|
24
|
+
Wrapper: AppShell + PageHeader | puro (chrome do print entregue? sim/não)
|
|
25
|
+
Colunas: <campo:tipo[:sortable][:editável]>, ... (primária: <campo>)
|
|
26
|
+
Busca/Filtros: busca=<sim/não> · filtros=<col:tipo, ...>
|
|
27
|
+
Ações linha: <editar, excluir(confirm AlertModal), ...> · seleção/bulk: <...>
|
|
28
|
+
Export: <não | escopo tudo/filtrado/selecionado · csv>
|
|
29
|
+
Views: presets=<nome → filtro>, ... · do usuário=<sim/não · savedViewsService+persistId>
|
|
30
|
+
Paginação/virt: <pageSize / virtualize> · totalizadores: <colunas>
|
|
31
|
+
Drawers: criar/editar=<sim/não> · campos=<campo:obrigatório?:máscara?> · detalhe=<sim/não>
|
|
32
|
+
Estados: loading=<skeleton> · vazio=<msg + CTA> · sem-resultado=<msg + limpar>
|
|
33
|
+
Kanban: <não | campo de agrupamento>
|
|
34
|
+
Componentes a puxar: data-table[, example-finance p/ drawers][, alert-modal p/ excluir][, ...]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## ⚠️ Decisões inferidas — vete se discordar
|
|
38
|
+
|
|
39
|
+
Depois do bloco acima, liste **só** o que o usuário não disse e você decidiu sozinho —
|
|
40
|
+
uma linha cada, com o que muda se ele vetar. Sem inferência → `nenhuma`; não apague a
|
|
41
|
+
seção: é a ausência dela que faz a decisão passar aprovada em pacote.
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
⚠️ Inferido (não perguntei):
|
|
45
|
+
• wrapper = puro → a tela nasce SEM rail/menu/header
|
|
46
|
+
• tema = light → a referência mostrava dark
|
|
47
|
+
• <coluna> = <tipo> → confiança baixa (inferido só pelo nome do campo)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Pré-validações (rode ANTES de pedir aprovação)
|
|
51
|
+
- Todo filtro tem operador compatível com o filterType (multiSelect⇒isAnyOf, text⇒contains, date⇒between, number⇒gte/lte/between, boolean⇒equals).
|
|
52
|
+
- `virtualize` ⇒ paginação off + container com altura. `groupBy`/kanban ⇒ paginação off.
|
|
53
|
+
- Kanban: cada lane mapeia uma option do campo de agrupamento.
|
|
54
|
+
- Coluna `actions` se há ações por linha; cada action tem `id` único.
|
|
55
|
+
- **Ação `destructive` (excluir) ⇒ tem `AlertModal` de confirmação no plano.**
|
|
56
|
+
- **Os 3 estados (loading/vazio/sem-resultado) estão definidos** (mesmo que com default).
|
|
57
|
+
- Nome/rota da página sem colisão.
|
|
58
|
+
|
|
59
|
+
Liste qualquer ajuste necessário. Só depois pergunte: **"Aprovar e gerar?"**
|
|
60
|
+
|
|
61
|
+
- `aprovar` → carregue `generate.md`.
|
|
62
|
+
- pedir mudança → ajuste o blueprint e reapresente.
|
|
63
|
+
- `cancelar` → encerre (zero edições).
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
> **Nota de portabilidade.** Este roteiro foi extraído do pipeline do iGreen DS
|
|
2
|
+
> para Claude Code. Menções a slash command (`/ds-create-…`), a `.claude/` e a
|
|
3
|
+
> ferramentas daquele ambiente descrevem **como ele é acionado lá** — aqui o
|
|
4
|
+
> equivalente é carregar este roteiro pelo `indice.json`. O conteúdo de decisão
|
|
5
|
+
> (entrevista, blueprint, regras de composição) vale igual.
|
|
6
|
+
|
|
7
|
+
> **Modo submódulo (`ds-link`).** Existe `.claude/ds-config.json` com `"mode": "submodule"`?
|
|
8
|
+
> Então **NÃO rode `igreen:add`** — esse script não existe no seu projeto. Os componentes e
|
|
9
|
+
> exemplos já estão no disco em `<dsPath>/src`: importe pelo `importBase` do config
|
|
10
|
+
> (compostos) e pelo `primitivesBase` (primitivos shadcn), e **leia** o exemplo direto de
|
|
11
|
+
> `<dsPath>/src/examples/`.
|
|
12
|
+
|
|
13
|
+
# CRUD Builder — Entrevista
|
|
14
|
+
|
|
15
|
+
Pergunte em fases, **uma pergunta de cada vez quando possível**, acumulando as
|
|
16
|
+
escolhas. NÃO edite arquivo nenhum aqui. Ao fim, vá pro `blueprint.md`.
|
|
17
|
+
|
|
18
|
+
## ⛔ Perguntas obrigatórias (NUNCA pular, mesmo que dê pra inferir)
|
|
19
|
+
|
|
20
|
+
A IA tende a inferir tudo e ir direto pro blueprint — **não faça isso**. Estas duas
|
|
21
|
+
são sempre explícitas:
|
|
22
|
+
|
|
23
|
+
1. **Colunas** — mesmo que dê pra inferir do nome da entidade, **LISTE** as colunas
|
|
24
|
+
inferidas (campo · rótulo · tipo) e **confirme** (adicionar/remover/reordenar) +
|
|
25
|
+
pergunte se **alguma tem particularidade** (ordenável, editável inline, alinhamento,
|
|
26
|
+
fixar/pin, largura, filtro, totalizador). Nunca gere com colunas só inferidas.
|
|
27
|
+
2. **Views salvas** — **sempre OFEREÇA e explique** (não trate como "off" silencioso):
|
|
28
|
+
|
|
29
|
+
> "Quer **views salvas**? São abas com filtros + ordenação + colunas/densidade
|
|
30
|
+
> pré-configurados (ex.: _Ativos_, _Alto valor_, _Atrasados_). O usuário troca de
|
|
31
|
+
> recorte num clique — sem precisar de uma tela nova pra cada um."
|
|
32
|
+
|
|
33
|
+
Pergunte quais (nome + filtro + sort). Default = nenhuma, **mas só depois de perguntar**.
|
|
34
|
+
|
|
35
|
+
## Fase 0 — Entidade & origem dos dados
|
|
36
|
+
|
|
37
|
+
- Qual entidade? (ex.: Clientes, Pedidos, Faturas) → vira o nome da tela/tipo.
|
|
38
|
+
- Dados **client-side** (array em memória/mock) ou **server-side** (fetch paginado da API)?
|
|
39
|
+
- Server: qual o endpoint/forma de buscar? (vira `fetchData` em `useCallback`).
|
|
40
|
+
- Onde a página vai morar e como registra a rota? (`PAGES_DIR`/`REGISTRO`).
|
|
41
|
+
|
|
42
|
+
## Fase 1 — Página e shell
|
|
43
|
+
|
|
44
|
+
| Pergunta | Default |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| Título da página | plural da entidade |
|
|
47
|
+
| Descrição (1-2 frases) | gerada do domínio, confirmar |
|
|
48
|
+
| **Wrapper** | ver abaixo |
|
|
49
|
+
| Onde mora + como registra a rota | `PAGES_DIR` / `REGISTRO` do seu projeto |
|
|
50
|
+
|
|
51
|
+
**Antes de perguntar o wrapper, VERIFIQUE** — as duas respostas costumam decidir sozinhas:
|
|
52
|
+
|
|
53
|
+
1. existe `AppShell` no projeto? (grep em `src/`)
|
|
54
|
+
2. a referência que o usuário deu — print, Figma, tela existente — mostra rail/menu/header?
|
|
55
|
+
|
|
56
|
+
- **`AppShell` + `PageHeader`** → chrome na referência **e** shell disponível. Sem perguntar.
|
|
57
|
+
- **componente puro** (no seu `PAGES_DIR`) → sem referência visual, ou app sem shell e sem
|
|
58
|
+
chrome na referência.
|
|
59
|
+
- **chrome na referência e SEM shell no projeto** → **pare e ofereça `/ds-create-app` junto**.
|
|
60
|
+
Se o usuário recusar, o blueprint diz em **linha destacada** o que do print não vai ser
|
|
61
|
+
entregue.
|
|
62
|
+
|
|
63
|
+
> Print de app inteiro + pedido de "uma tela" é **`shell + tela`**, não `tela`. Foi este
|
|
64
|
+
> recorte que fez uma tela nascer sem o menu que o print mostrava. A regra de o que é da
|
|
65
|
+
> referência e o que é do DS está em `_claude/rules/ds-design.md`.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Fase 2 — Colunas
|
|
70
|
+
|
|
71
|
+
Pra cada coluna: campo, rótulo, **tipo** (text, number, currency, percentage, date,
|
|
72
|
+
datetime, email, phone, url, status/badge, boolean, user, tags, actions), e se é
|
|
73
|
+
sortable / editável (inline). Pergunte quais colunas o usuário quer e em que ordem.
|
|
74
|
+
Marque a coluna "primária" (a que abre detalhe / leva avatar).
|
|
75
|
+
|
|
76
|
+
**`copyable` é inferido, não perguntado** — marque nas colunas cujo valor a pessoa
|
|
77
|
+
**cola em outro lugar** (documento, e-mail, telefone, conta/chave PIX, protocolo/NF/
|
|
78
|
+
rastreio, token, ID externo) e **só** nelas; nome, status, data, valor e quantidade
|
|
79
|
+
ficam de fora. Não abra pergunta própria pra isso: **declare** as colunas marcadas
|
|
80
|
+
quando apresentar a lista de colunas, junto do tipo e do filtro, pra o usuário tirar
|
|
81
|
+
em lote ("CNPJ sem copyable"). Critério completo e limites: `generate.md` item 4.
|
|
82
|
+
|
|
83
|
+
## Fase 3 — Busca, filtros & ações
|
|
84
|
+
|
|
85
|
+
> ⛔ **Anti-pattern — NUNCA gerar form/selects soltos ACIMA da tabela.** Intenção de
|
|
86
|
+
> "adicionar filtro" (select de status em cima, campo de período, "filtrar por X") →
|
|
87
|
+
> **sugira o padrão certo** (a tabela já filtra reativo, com chips clicáveis/editáveis):
|
|
88
|
+
>
|
|
89
|
+
> - **Filtro por COLUNA** (status/categoria/tipo/data…) → `enableColumnFilter` +
|
|
90
|
+
> `filterType`. Quer abrir já filtrado? **pré-aplique** (`defaultViews`/`presetView`
|
|
91
|
+
> ou `filterModel`) → abre com o **chip aplicado**, editável, reativo. Pode pré-setar
|
|
92
|
+
> **vários** (id/período/status/…), sem campos.
|
|
93
|
+
> - **toolbar.actions é SÓ pra caso pequeno e simples que NÃO reage com coluna** (ex.:
|
|
94
|
+
> data/período, escopo) — **label curta**, **máx ~2**. Se mexe com coluna, é grande, ou
|
|
95
|
+
> são muitos → **NÃO use o toolbar.**
|
|
96
|
+
> - **Muitos filtros, ou ligados a coluna** → SEMPRE os nativos **pré-aplicados** (chips no
|
|
97
|
+
> load) + drawer "Filtros". Nunca empilhar selects.
|
|
98
|
+
>
|
|
99
|
+
> Regra de ouro: **filtro é recurso da tabela (reativo), não UI montada na unha.**
|
|
100
|
+
|
|
101
|
+
- Busca global? (`enableSearch`)
|
|
102
|
+
- Filtros por coluna? **Default = TODAS as colunas de dados filtram** (`enableColumnFilter: true` + `filterType` por tipo) — pergunte só "alguma NÃO deve filtrar?". O funil/drawer só lista colunas com `enableColumnFilter`; marcar só 2 = bug. (Ver §Regras de coluna em `generate.md`.)
|
|
103
|
+
- Ações por linha (editar, excluir, ...)? (coluna `actions`, `getActions`) — vai por **último**; o DataTable ancora à direita/estreita sozinho (sem `pinned`/`width`). NÃO setar `width` nas demais (autoFit distribui).
|
|
104
|
+
**Toda ação destrutiva (Excluir) → confirmação OBRIGATÓRIA via `AlertModal`** (nunca
|
|
105
|
+
deletar direto). Pergunte o texto do confirm.
|
|
106
|
+
- Seleção + ações em massa (exportar, etc.)? (`selectionConfig` + bulk actions).
|
|
107
|
+
- Export: escopo (tudo / filtrado / selecionado) + formato (csv)? (`toolbar.enableExport`).
|
|
108
|
+
- Ação custom no toolbar (ex.: seletor de período/mês, botão extra)? `toolbar.actions: ToolbarAction[]` (`button`/`dropdown`/`input`) — inline no desktop, colapsa no ⋯ no mobile. Oferecer só se o usuário pedir.
|
|
109
|
+
|
|
110
|
+
## Fase 4 — Views, paginação & densidade
|
|
111
|
+
|
|
112
|
+
- Views (ver bloco obrigatório). Dois sabores, pergunte os dois:
|
|
113
|
+
- **Presets fixos** que VOCÊ define (`defaultViews`/`presetView`, ex.: "Ativos", "Alto valor").
|
|
114
|
+
- **Views do usuário**: ele cria/salva/persiste as próprias (`savedViewsService` + `persistId`).
|
|
115
|
+
- Paginação (tamanho inicial, opções) ou virtualização (10k+ linhas)?
|
|
116
|
+
- Totalizadores no rodapé? (`showTotalizers` + `aggregate`).
|
|
117
|
+
|
|
118
|
+
## Fase 5 — Criar/editar/detalhe (drawers)
|
|
119
|
+
|
|
120
|
+
- Precisa criar/editar registro? → drawer estilo `NovoClienteDrawer` (Panel + FormField).
|
|
121
|
+
- **Quais campos no form?** (default: espelha as colunas editáveis). Pra cada campo:
|
|
122
|
+
**obrigatório?** **máscara** (CNPJ / telefone / CEP / moeda)? **validação** (formato,
|
|
123
|
+
min/max)? Todo label via `<FormField>` (nunca `<label>` cru) + `gap-form-gap`.
|
|
124
|
+
- Precisa ver detalhe ao clicar na linha? → `FinanceDetailPanel` (FloatingPanel).
|
|
125
|
+
- (Puxar `example-finance` na geração pra reusar esses padrões.)
|
|
126
|
+
|
|
127
|
+
## Fase 6 — Kanban (opcional)
|
|
128
|
+
|
|
129
|
+
- Quer alternar tabela↔kanban? Se sim: qual campo agrupa as colunas (ex.: status)?
|
|
130
|
+
Cada lane = uma option desse campo. (`viewMode` controlado + `kanbanConfig`.)
|
|
131
|
+
|
|
132
|
+
## Fase 7 — Estados (SEMPRE perguntar — sai faltando se não)
|
|
133
|
+
|
|
134
|
+
- **Loading** (`loading` + `renderLoading`): skeleton enquanto carrega — obrigatório em server mode.
|
|
135
|
+
- **Vazio** (`renderEmpty`): sem nenhum registro → "Nenhuma `<entidade>` ainda" + CTA _Adicionar_.
|
|
136
|
+
- **Sem resultado** (`renderNoResults`): filtro/busca sem match → "Nenhum resultado" + _Limpar filtros_.
|
|
137
|
+
- (Aceite defaults sensatos se o usuário não quiser customizar — mas **sempre wire os três**.)
|
|
138
|
+
|
|
139
|
+
Ao terminar: resuma as escolhas e siga pro `blueprint.md` (gate).
|