@aurea-uds/react 0.50.0 → 0.52.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/agents.js CHANGED
@@ -19,6 +19,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
19
19
  // verbal. Aqui as ações entram como nó, e quem monta o botão sabe o que ele faz.
20
20
  import React from "react";
21
21
  import { cx, useAureaStrings } from "./internal.js";
22
+ import { proprio } from "./pure.js";
22
23
  import { Icon } from "./system.js";
23
24
  import { Status, Badge, Progress, Alert } from "./feedback.js";
24
25
  import { Avatar } from "./identity.js";
@@ -38,7 +39,7 @@ const ESTADO = {
38
39
  // exatamente o que a Parte J existe para impedir, um nível acima.
39
40
  export function AgentStatus({ state = "idle", label, className, ...props }) {
40
41
  const s = useAureaStrings();
41
- return _jsx(Status, { variant: ESTADO[state], className: className, "data-state": state, ...props, children: label ?? s.agentState[state] });
42
+ return _jsx(Status, { variant: proprio(ESTADO, state), className: className, "data-state": state, ...props, children: label ?? proprio(s.agentState, state) });
42
43
  }
43
44
  // AgentCard — a identidade do agente: quem é, em que estado está, o que sabe fazer.
44
45
  export function AgentCard({ name, description, avatarSrc, model, state, capabilities, actions, className, ...props }) {
@@ -93,7 +94,7 @@ const TAREFA = {
93
94
  // aqui faria a fila responder por quatro contratos e nenhum deles direito.
94
95
  export function TaskQueue({ tasks, label, onRetry, className, ...props }) {
95
96
  const s = useAureaStrings();
96
- return _jsx("ol", { className: cx("task-queue", className), "aria-label": label ?? s.taskQueueLabel, ...props, children: tasks.map(t => _jsxs("li", { className: "task-item", "data-state": t.state, children: [_jsxs("div", { className: "task-line", children: [_jsx("span", { className: "task-title", children: t.title }), t.priority && _jsx(Badge, { variant: t.priority === "high" ? "warning" : "neutral", children: s.taskPriority[t.priority] }), _jsx(Status, { variant: TAREFA[t.state], children: s.taskState[t.state] }), t.state === "failed" && onRetry && _jsx(IconButton, { variant: "ghost", size: "sm", icon: "arrow-counter-clockwise", label: `${s.uploadRetry} ${t.title}`, onClick: () => onRetry(t) })] }), t.description && _jsx("p", { className: "task-desc", children: t.description }), t.state === "running" && t.progress != null && _jsx(Progress, { value: Math.round(t.progress * 100), label: t.title })] }, t.id)) });
97
+ return _jsx("ol", { className: cx("task-queue", className), "aria-label": label ?? s.taskQueueLabel, ...props, children: tasks.map(t => _jsxs("li", { className: "task-item", "data-state": t.state, children: [_jsxs("div", { className: "task-line", children: [_jsx("span", { className: "task-title", children: t.title }), t.priority && _jsx(Badge, { variant: t.priority === "high" ? "warning" : "neutral", children: s.taskPriority[t.priority] }), _jsx(Status, { variant: proprio(TAREFA, t.state), children: proprio(s.taskState, t.state) }), t.state === "failed" && onRetry && _jsx(IconButton, { variant: "ghost", size: "sm", icon: "arrow-counter-clockwise", label: `${s.uploadRetry} ${t.title}`, onClick: () => onRetry(t) })] }), t.description && _jsx("p", { className: "task-desc", children: t.description }), t.state === "running" && t.progress != null && _jsx(Progress, { value: Math.round(t.progress * 100), label: t.title })] }, t.id)) });
97
98
  }
98
99
  // HumanApproval — o portão: o que vai acontecer, e a decisão de uma pessoa.
99
100
  export function HumanApproval({ title, description, details, risk, reasoning, deadline, decision, onApprove, onDeny, className, ...props }) {
@@ -169,7 +170,7 @@ const SAUDE = {
169
170
  // HealthMatrix — o que está de pé, numa grade.
170
171
  export function HealthMatrix({ entries, label, className, ...props }) {
171
172
  const s = useAureaStrings();
172
- return _jsx("ul", { className: cx("health-matrix", className), "aria-label": label ?? s.healthLabel, ...props, children: entries.map(e => _jsxs("li", { className: "health-cell", "data-state": e.state, children: [_jsx("span", { className: "health-name", children: e.name }), _jsx(Status, { variant: SAUDE[e.state], children: s.healthState[e.state] }), e.detail && _jsx("span", { className: "hint", children: e.detail })] }, e.id)) });
173
+ return _jsx("ul", { className: cx("health-matrix", className), "aria-label": label ?? s.healthLabel, ...props, children: entries.map(e => _jsxs("li", { className: "health-cell", "data-state": e.state, children: [_jsx("span", { className: "health-name", children: e.name }), _jsx(Status, { variant: proprio(SAUDE, e.state), children: proprio(s.healthState, e.state) }), e.detail && _jsx("span", { className: "hint", children: e.detail })] }, e.id)) });
173
174
  }
174
175
  // ─────────────────────────────────────────────────────────────────────────────
175
176
  // H.e — custo e memória: quanto se consumiu, contra que teto, e o que ficou guardado.
@@ -259,7 +260,7 @@ const LANCAMENTO = {
259
260
  // ordenar e filtrar o razão compõe as duas coisas na aplicação — o exemplo do catálogo mostra.
260
261
  export function MemoryLedger({ records, label, className, ...props }) {
261
262
  const s = useAureaStrings();
262
- return _jsx("ol", { className: cx("memory-ledger", className), "aria-label": label ?? s.memoryLabel, ...props, children: records.map(r => _jsxs("li", { className: "memory-entry", "data-operation": r.operation, "data-scope": r.scope, children: [_jsxs("div", { className: "memory-line", children: [_jsx(Badge, { variant: LANCAMENTO[r.operation], children: s.memoryOperation[r.operation] }), _jsx(Badge, { children: s.memoryScope[r.scope] }), _jsx("span", { className: "memory-content", children: r.content }), r.time && _jsx("time", { className: "memory-time", children: r.time })] }), (r.source || r.details?.length) && _jsxs("details", { className: "memory-provenance", children: [_jsx("summary", { children: s.memoryProvenance }), r.source && _jsx("p", { className: "memory-source", children: r.source }), !!r.details?.length && _jsx(DataList, { items: r.details })] })] }, r.id)) });
263
+ return _jsx("ol", { className: cx("memory-ledger", className), "aria-label": label ?? s.memoryLabel, ...props, children: records.map(r => _jsxs("li", { className: "memory-entry", "data-operation": r.operation, "data-scope": r.scope, children: [_jsxs("div", { className: "memory-line", children: [_jsx(Badge, { variant: proprio(LANCAMENTO, r.operation), children: proprio(s.memoryOperation, r.operation) }), _jsx(Badge, { children: proprio(s.memoryScope, r.scope) }), _jsx("span", { className: "memory-content", children: r.content }), r.time && _jsx("time", { className: "memory-time", children: r.time })] }), (r.source || r.details?.length) && _jsxs("details", { className: "memory-provenance", children: [_jsx("summary", { children: s.memoryProvenance }), r.source && _jsx("p", { className: "memory-source", children: r.source }), !!r.details?.length && _jsx(DataList, { items: r.details })] })] }, r.id)) });
263
264
  }
264
265
  const RECADO = {
265
266
  request: "info", response: "success", handoff: "primary", broadcast: "neutral", error: "danger",
@@ -279,7 +280,7 @@ export function InterAgentMessage({ messages, label, className, ...props }) {
279
280
  const s = useAureaStrings();
280
281
  return _jsx("ol", { className: cx("agent-messages", className), "aria-label": label ?? s.agentMessageLabel, ...props, children: messages.map(m => {
281
282
  const destino = m.to ?? s.agentMessageBroadcast;
282
- return _jsxs("li", { className: "agent-message", "data-kind": m.kind ?? "handoff", children: [_jsxs("div", { className: "agent-message-line", children: [_jsxs("span", { className: "agent-message-route", role: "group", "aria-label": `${m.from} ${s.agentMessageTo} ${destino}`, children: [_jsx("strong", { children: m.from }), _jsx(Icon, { name: "arrow-right", size: "sm", className: "agent-message-arrow" }), m.to ? _jsx("strong", { children: m.to }) : _jsx("span", { className: "hint", children: destino })] }), _jsx(Badge, { variant: RECADO[m.kind ?? "handoff"], children: s.agentMessageKind[m.kind ?? "handoff"] }), m.time && _jsx("time", { className: "agent-message-time", children: m.time })] }), _jsx("p", { className: "agent-message-body", children: m.body }), (m.reason || m.details?.length) && _jsxs("details", { className: "agent-message-why", children: [_jsx("summary", { children: s.agentMessageReason }), m.reason && _jsx("p", { className: "agent-message-reason", children: m.reason }), !!m.details?.length && _jsx(DataList, { items: m.details })] })] }, m.id);
283
+ return _jsxs("li", { className: "agent-message", "data-kind": m.kind ?? "handoff", children: [_jsxs("div", { className: "agent-message-line", children: [_jsxs("span", { className: "agent-message-route", role: "group", "aria-label": `${m.from} ${s.agentMessageTo} ${destino}`, children: [_jsx("strong", { children: m.from }), _jsx(Icon, { name: "arrow-right", size: "sm", className: "agent-message-arrow" }), m.to ? _jsx("strong", { children: m.to }) : _jsx("span", { className: "hint", children: destino })] }), _jsx(Badge, { variant: proprio(RECADO, m.kind ?? "handoff"), children: proprio(s.agentMessageKind, m.kind ?? "handoff") }), m.time && _jsx("time", { className: "agent-message-time", children: m.time })] }), _jsx("p", { className: "agent-message-body", children: m.body }), (m.reason || m.details?.length) && _jsxs("details", { className: "agent-message-why", children: [_jsx("summary", { children: s.agentMessageReason }), m.reason && _jsx("p", { className: "agent-message-reason", children: m.reason }), !!m.details?.length && _jsx(DataList, { items: m.details })] })] }, m.id);
283
284
  }) });
284
285
  }
285
286
  // AutomationCard — a regra que roda sozinha: QUANDO isto, ENTÃO aquilo.
@@ -300,5 +301,5 @@ export function AutomationCard({ name, description, trigger, action, enabled, on
300
301
  const s = useAureaStrings();
301
302
  // `data-enabled` só quando LIGADA: o `false` saía como `data-enabled="false"` e escapava da regra que faz a regra
302
303
  // desligada recuar (`:not([data-enabled])`) — achado do check 49, lote 1, 20/10/2026.
303
- return _jsxs("article", { className: cx("card", "automation", className), "data-enabled": enabled || undefined, ...props, children: [_jsxs("header", { className: "automation-head", children: [_jsxs("div", { className: "automation-id", children: [_jsx("strong", { children: name }), description && _jsx("span", { className: "hint", children: description })] }), onToggle && _jsx(Switch, { checked: !!enabled, onChange: e => onToggle(e.target.checked), label: s.automationEnabled })] }), _jsx(DataList, { items: [{ term: s.automationWhen, value: trigger }, { term: s.automationThen, value: action }, ...(details ?? [])] }), (lastRun || lastResult) && _jsxs("footer", { className: "automation-foot", children: [lastResult && _jsx(Status, { variant: lastResult === "success" ? "success" : "danger", children: s.automationResult[lastResult] }), lastRun && _jsxs("span", { className: "hint", children: [s.automationLastRun, ": ", lastRun] }), actions] }), !lastRun && !lastResult && actions && _jsx("footer", { className: "automation-foot", children: actions })] });
304
+ return _jsxs("article", { className: cx("card", "automation", className), "data-enabled": enabled || undefined, ...props, children: [_jsxs("header", { className: "automation-head", children: [_jsxs("div", { className: "automation-id", children: [_jsx("strong", { children: name }), description && _jsx("span", { className: "hint", children: description })] }), onToggle && _jsx(Switch, { checked: !!enabled, onChange: e => onToggle(e.target.checked), label: s.automationEnabled })] }), _jsx(DataList, { items: [{ term: s.automationWhen, value: trigger }, { term: s.automationThen, value: action }, ...(details ?? [])] }), (lastRun || lastResult) && _jsxs("footer", { className: "automation-foot", children: [lastResult && _jsx(Status, { variant: lastResult === "success" ? "success" : "danger", children: proprio(s.automationResult, lastResult) }), lastRun && _jsxs("span", { className: "hint", children: [s.automationLastRun, ": ", lastRun] }), actions] }), !lastRun && !lastResult && actions && _jsx("footer", { className: "automation-foot", children: actions })] });
304
305
  }
@@ -14,6 +14,7 @@
14
14
  // (o check 19 do `validate.py` cobra que só este arquivo e o do editor importam o `@lezer/`).
15
15
  import { highlightCode as colorirArvore, tagHighlighter, tags as t } from "@lezer/highlight";
16
16
  import { parser as js } from "@lezer/javascript";
17
+ import { proprio } from "./pure.js";
17
18
  import { parser as css } from "@lezer/css";
18
19
  import { parser as html } from "@lezer/html";
19
20
  import { parser as json } from "@lezer/json";
@@ -38,12 +39,14 @@ const ANALISADORES = {
38
39
  css, html, json,
39
40
  };
40
41
  let bash;
42
+ // A língua vem de fora (a cerca de um texto em markdown): só vale a que é chave DA tabela — "constructor" devolvia a
43
+ // função do `Object.prototype` e derrubava a tela (31/10/2026, a auditoria de segurança).
41
44
  // O bash vem do modo antigo do CodeMirror (o Lezer não tem gramática de shell); ele só é montado quando pedido.
42
45
  function analisador(lingua) {
43
46
  const l = lingua.toLowerCase();
44
47
  if (l === "bash" || l === "sh" || l === "shell")
45
48
  return bash ??= StreamLanguage.define(shell).parser;
46
- return ANALISADORES[l];
49
+ return proprio(ANALISADORES, l);
47
50
  }
48
51
  /** As línguas que `highlightCode` colore. Com outra, ele devolve `null` e o `CodeBlock` mostra o texto puro. */
49
52
  export const highlightLanguages = ["bash", "css", "html", "javascript", "js", "json", "jsx", "sh", "shell", "ts", "tsx", "typescript"];
package/dist/data-grid.js CHANGED
@@ -8,6 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  import React from "react";
9
9
  import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, getFacetedRowModel, getFacetedUniqueValues, flexRender } from "@tanstack/react-table";
10
10
  import { cx, useAureaStrings, stateSeverity } from "./internal.js";
11
+ import { proprio } from "./pure.js";
11
12
  import { Icon } from "./system.js";
12
13
  import { Button, Toolbar, ToolbarButton, ToolbarSeparator } from "./actions.js";
13
14
  import { Input, SearchField, MultiCombobox } from "./inputs.js";
@@ -152,9 +153,29 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
152
153
  // A função acima precisa da tabela, e a tabela é o que esta chamada devolve: a referência fecha o
153
154
  // círculo. Ela é lida só quando o motor filtra, que é sempre depois da primeira montagem.
154
155
  const tabelaRef = React.useRef(null);
156
+ // Um nome de coluna que vem de fora (o link, `?sort=constructor`) e é nome do `Object.prototype` derrubava o motor, que
157
+ // procura a coluna num objeto comum (31/10/2026, a auditoria de segurança). Do estado que o motor recebe sai só o que
158
+ // é um desses nomes E não é coluna declarada; o nome que não existe continua com o aviso do próprio motor.
159
+ const idsDeclarados = React.useMemo(() => {
160
+ const ids = new Set();
161
+ const ver = (cs) => {
162
+ for (const c of cs) {
163
+ const d = c;
164
+ const id = d.id ?? d.accessorKey ?? (typeof d.header === "string" ? d.header : undefined);
165
+ if (id)
166
+ ids.add(id);
167
+ if (d.columns)
168
+ ver(d.columns);
169
+ }
170
+ };
171
+ ver(colunas);
172
+ return ids;
173
+ }, [colunas]);
174
+ const sortingDoMotor = React.useMemo(() => sorting.filter(o => idsDeclarados.has(o.id) || !(o.id in Object.prototype)), [sorting, idsDeclarados]);
175
+ const filtrosDoMotor = React.useMemo(() => columnFilters.filter(f => idsDeclarados.has(f.id) || !(f.id in Object.prototype)), [columnFilters, idsDeclarados]);
155
176
  const table = useReactTable({
156
177
  data, columns: colunas, getRowId,
157
- state: { sorting, globalFilter, rowSelection, columnFilters, columnVisibility, columnSizing, ...(paginated ? { pagination } : {}) },
178
+ state: { sorting: sortingDoMotor, globalFilter, rowSelection, columnFilters: filtrosDoMotor, columnVisibility, columnSizing, ...(paginated ? { pagination } : {}) },
158
179
  onSortingChange: setSorting,
159
180
  onGlobalFilterChange: setGlobalFilter,
160
181
  onColumnFiltersChange: setColumnFilters,
@@ -375,7 +396,7 @@ export function DataGrid({ data, columns, label, filterable, pageSize, selectabl
375
396
  // ganha de mais geral; o universal entra para os quatro estados que a grade não tinha.
376
397
  const carregando = state === "loading";
377
398
  const universal = state && state !== "loading" && state !== "error" ? state : null;
378
- const recado = state && !carregando ? (stateMessage ?? (state === "stale" ? s.dataGridStale : state === "partial" ? s.dataGridPartial : state === "error" ? s.dataGridError : s.universalState[state])) : null;
399
+ const recado = state && !carregando ? (stateMessage ?? (state === "stale" ? s.dataGridStale : state === "partial" ? s.dataGridPartial : state === "error" ? s.dataGridError : proprio(s.universalState, state))) : null;
379
400
  // O marcador vai na GRADE, não no recado: quem está obsoleto é a tabela, e o `Alert` é só
380
401
  // como ela conta isso. Achado pelo check 30 na primeira execução dele — eu tinha marcado o
381
402
  // recado, e com `state="loading"` não há recado nenhum, então o estado não chegava ao DOM.
@@ -7,9 +7,13 @@ export interface AccordionItem {
7
7
  /** O item que não abre: anunciado como desligado e fora do Tab. */
8
8
  disabled?: boolean;
9
9
  }
10
- export declare function Accordion({ items, variant, multiple, defaultValue, className }: {
10
+ export declare function Accordion({ items, variant, multiple, defaultValue, indicator, size, className }: {
11
11
  items: AccordionItem[];
12
12
  variant?: AccordionVariant;
13
+ /** O desenho de abrir: `"chevron"` (o padrão, a setinha) ou `"plus"` (o "+" numa bolinha, que vira "×" quando abre). */
14
+ indicator?: "chevron" | "plus";
15
+ /** A altura da linha: `"md"` (o padrão) ou `"lg"` (60, com a letra do texto). */
16
+ size?: "md" | "lg";
13
17
  /** Deixa vários itens abertos ao mesmo tempo. Sem isto, abrir um fecha o outro. */
14
18
  multiple?: boolean;
15
19
  /** Os itens que começam abertos (o `id` de cada um). */
@@ -8,9 +8,11 @@ import { Collapsible as BaseCollapsible } from "@base-ui/react/collapsible";
8
8
  import { cx } from "./internal.js";
9
9
  import { soOValor } from "./pure.js";
10
10
  import { Icon } from "./system.js";
11
- export function Accordion({ items, variant = "default", multiple = false, defaultValue, className }) {
11
+ // O SITE B (30/10/2026, protótipo): `indicator="plus"` troca a setinha pelo "+" numa bolinha, que gira e vira o "×"
12
+ // amarelo quando abre; `size="lg"` é a linha de 60 com a letra do texto (16), a das perguntas do desenho aprovado.
13
+ export function Accordion({ items, variant = "default", multiple = false, defaultValue, indicator = "chevron", size = "md", className }) {
12
14
  const grupo = React.useId();
13
- return _jsx("div", { className: cx("accordion", variant === "surface" && "accordion-surface", className), children: items.map(i => _jsxs("details", { className: "accordion-item", name: multiple ? undefined : grupo, open: defaultValue?.includes(i.id) || undefined, children: [_jsxs("summary", { "aria-disabled": i.disabled || undefined, tabIndex: i.disabled ? -1 : undefined, onClick: i.disabled ? (e) => e.preventDefault() : undefined, children: [_jsx("span", { className: "accordion-title", children: i.title }), _jsx("span", { className: "accordion-chevron", "aria-hidden": "true" })] }), _jsx("div", { className: "accordion-content", children: i.content })] }, i.id)) });
15
+ return _jsx("div", { className: cx("accordion", variant === "surface" && "accordion-surface", indicator === "plus" && "accordion-plus", size === "lg" && "accordion-lg", className), children: items.map(i => _jsxs("details", { className: "accordion-item", name: multiple ? undefined : grupo, open: defaultValue?.includes(i.id) || undefined, children: [_jsxs("summary", { "aria-disabled": i.disabled || undefined, tabIndex: i.disabled ? -1 : undefined, onClick: i.disabled ? (e) => e.preventDefault() : undefined, children: [_jsx("span", { className: "accordion-title", children: i.title }), _jsx("span", { className: "accordion-chevron", "aria-hidden": "true" })] }), _jsx("div", { className: "accordion-content", children: i.content })] }, i.id)) });
14
16
  }
15
17
  // COLLAPSIBLE — uma seção que abre e fecha, com o estado do lado de quem usa.
16
18
  //
@@ -2,7 +2,7 @@
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  // Fase 9 (achado A5): este arquivo saiu do index.tsx de 970 linhas. Um módulo por categoria
4
4
  // do registry — a taxonomia já existia e é gateada. A ordem de import entre eles é um DAG:
5
- import { peleDoEixo } from "./pure.js";
5
+ import { peleDoEixo, proprio } from "./pure.js";
6
6
  // internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
7
7
  import React from "react";
8
8
  import { Popover as BasePopover } from "@base-ui/react/popover";
@@ -30,7 +30,7 @@ import { Skeleton, Badge, Switch } from "./markup.js";
30
30
  // seis caem na gravidade. Quem passa os dois manda, porque só o consumidor sabe se aquele
31
31
  // "esperando" dele é grave. E o rótulo é o do consumidor, como sempre foi: a string universal
32
32
  // entra só quando não há filho, para o componente não passar a inventar texto.
33
- export function Status({ variant, state, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? (state === "offline" ? "offline" : stateSeverity(state)) : "neutral"); return _jsxs("span", { className: cx("status", v !== "neutral" && `status-${v}`, className), "data-state": state, ...props, children: [_jsx("i", { className: "status-dot", "aria-hidden": "true" }), _jsx("span", { className: "status-label", children: children ?? (state ? s.universalState[state] : null) })] }); }
33
+ export function Status({ variant, state, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? (state === "offline" ? "offline" : stateSeverity(state)) : "neutral"); return _jsxs("span", { className: cx("status", v !== "neutral" && `status-${v}`, className), "data-state": state, ...props, children: [_jsx("i", { className: "status-dot", "aria-hidden": "true" }), _jsx("span", { className: "status-label", children: children ?? (state ? proprio(s.universalState, state) : null) })] }); }
34
34
  // O ícone é FUNÇÃO da variante, como o `role` já era. A referência congelada desenha o alerta
35
35
  // com ele em toda variante, e um sistema que faz o consumidor lembrar qual glifo significa
36
36
  // "perigo" acaba com quatro respostas diferentes para a mesma pergunta. `icon` sobrepõe quando o
@@ -55,14 +55,14 @@ const ICONE_DA_VARIANTE = { info: "info",
55
55
  // "esperando" dele é grave.
56
56
  // `action` (15/10/2026, o "Upgrade" da referência): o botão do aviso, depois do texto e antes do X; no celular ele desce
57
57
  // para baixo do texto, na mesma coluna (a referência repete o botão; aqui ele só muda de lugar).
58
- export function Alert({ variant, state, title, icon, onDismiss, action, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("alert", `alert-${v}`, action != null && "alert-has-action", className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [_jsx(Icon, { name: icon ?? ICONE_DA_VARIANTE[v], weight: icon ? undefined : "fill" }), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }), action != null && _jsx("div", { className: "alert-action", children: action }), onDismiss ? _jsx(CloseButton, { onClick: onDismiss }) : _jsx("span", {})] }); }
58
+ export function Alert({ variant, state, title, icon, onDismiss, action, children, className, ...props }) { const s = useAureaStrings(); const v = variant ?? (state ? stateSeverity(state) : "info"); return _jsxs("div", { className: cx("alert", `alert-${v}`, action != null && "alert-has-action", className), role: v === "danger" ? "alert" : "status", "data-state": state, ...props, children: [_jsx(Icon, { name: icon ?? proprio(ICONE_DA_VARIANTE, v) ?? "info", weight: icon ? undefined : "fill" }), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? proprio(s.universalState, state) : null)] }), action != null && _jsx("div", { className: "alert-action", children: action }), onDismiss ? _jsx(CloseButton, { onClick: onDismiss }) : _jsx("span", {})] }); }
59
59
  export function Banner(props) {
60
60
  if (props.variant === "consent")
61
61
  return _jsx(BannerConsent, { ...props });
62
62
  const { variant, state, title, icon, onDismiss, children, className, categories: _c, value: _v, onConsent: _o, ...rest } = props;
63
63
  const s = useAureaStrings();
64
64
  const v = variant ?? (state ? stateSeverity(state) : "info");
65
- return _jsxs("div", { className: cx("banner", `banner-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...rest, children: [icon ? _jsx(Icon, { name: icon }) : _jsx("span", {}), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? s.universalState[state] : null)] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.close, onClick: onDismiss }) : _jsx("span", {})] });
65
+ return _jsxs("div", { className: cx("banner", `banner-${v}`, className), role: v === "danger" ? "alert" : "status", "data-state": state, ...rest, children: [icon ? _jsx(Icon, { name: icon }) : _jsx("span", {}), _jsxs("div", { children: [title && _jsx("strong", { children: title }), children ?? (state ? proprio(s.universalState, state) : null)] }), onDismiss ? _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.close, onClick: onDismiss }) : _jsx("span", {})] });
66
66
  }
67
67
  // GAR-19 (lote U, 19/10/2026): o aviso de cookies é uma VARIAÇÃO do Banner (decisão do Victor). Nenhuma referência da fila
68
68
  // tem a peça aberta; as regras vêm de duas fontes primárias, lidas em 19/10/2026:
@@ -144,7 +144,7 @@ function CorpoDoDataState({ state, message, skeleton, emptyTitle, emptyIcon, act
144
144
  return caixa(_jsxs(_Fragment, { children: [_jsx(Alert, { variant: stateSeverity(state), state: state, children: message }), conteudo()] }));
145
145
  return caixa(conteudo());
146
146
  }
147
- export function EmptyState({ icon = "file", title, titleAs: TitleTag = "h3", description, action, state }) { const s = useAureaStrings(); const desc = description ?? (state ? s.universalState[state] : null); return _jsxs("div", { className: "empty-state", "data-state": state, children: [_jsx(Icon, { name: icon, size: "xl" }), _jsx(TitleTag, { className: "empty-title", children: title }), desc && _jsx("p", { className: "muted", children: desc }), action] }); }
147
+ export function EmptyState({ icon = "file", title, titleAs: TitleTag = "h3", description, action, state }) { const s = useAureaStrings(); const desc = description ?? (state ? proprio(s.universalState, state) : null); return _jsxs("div", { className: "empty-state", "data-state": state, children: [_jsx(Icon, { name: icon, size: "xl" }), _jsx(TitleTag, { className: "empty-title", children: title }), desc && _jsx("p", { className: "muted", children: desc }), action] }); }
148
148
  function groupNotifications(items) {
149
149
  const out = [];
150
150
  for (const it of items) {
package/dist/graph.js CHANGED
@@ -41,6 +41,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
41
41
  import React from "react";
42
42
  import { ReactFlow, ReactFlowProvider, Background, BaseEdge, EdgeLabelRenderer, EdgeText, Handle, MiniMap, Panel, Position, SelectionMode, ViewportPortal, applyNodeChanges, getBezierPath, getSmoothStepPath, useNodesInitialized, useOnViewportChange, useReactFlow, useStore } from "@xyflow/react";
43
43
  import { cx, useAureaStrings } from "./internal.js";
44
+ import { proprio } from "./pure.js";
44
45
  import { Icon } from "./system.js";
45
46
  import { Badge } from "./markup.js";
46
47
  import { Button, IconButton, Toolbar, ToolbarButton, ToolbarSeparator } from "./actions.js";
@@ -537,7 +538,7 @@ function paraDrawio(nos, linhas, forma, titulo) {
537
538
  if (!ids.has(e.from) || !ids.has(e.to))
538
539
  return;
539
540
  const id = `e-${i}`;
540
- const estilo = `endArrow=none;html=0;${forma === "step" ? "edgeStyle=orthogonalEdgeStyle;rounded=1;" : "curved=1;"}${TRACO_DRAWIO[e.pattern ?? "solid"]}strokeWidth=${PESO_DRAWIO[e.weight ?? "regular"]};`;
541
+ const estilo = `endArrow=none;html=0;${forma === "step" ? "edgeStyle=orthogonalEdgeStyle;rounded=1;" : "curved=1;"}${proprio(TRACO_DRAWIO, e.pattern ?? "solid") ?? ""}strokeWidth=${proprio(PESO_DRAWIO, e.weight ?? "regular") ?? 1};`;
541
542
  const dobras = rota && rota.length > 2 ? `<Array as="points">${rota.slice(1, -1).map(p => `<mxPoint x="${Math.round(p.x)}" y="${Math.round(p.y)}"/>`).join("")}</Array>` : "";
542
543
  celulas.push(`<mxCell id="${id}" value="${XML(e.label ?? "")}" style="${estilo}" edge="1" parent="1" source="${XML(`n-${e.from}`)}" target="${XML(`n-${e.to}`)}"><mxGeometry relative="1" as="geometry">${dobras}</mxGeometry></mxCell>`);
543
544
  // O nome da porta na ponta: o rótulo-filho da linha, a 80% do caminho para cada lado (o idioma do draw.io).
@@ -3,14 +3,14 @@ import { type Orientation, type Responsive } from "./pure.js";
3
3
  import { type IconButtonProps } from "./actions.js";
4
4
  import { type HeaderVariant, type HeaderTab, type SidebarVariant, type SidebarScrollbar, type SidebarEntry, type SidebarCollapsible, type SidebarIndicator } from "./navigation.js";
5
5
  declare function AppShellSidebarToggle(props: Omit<IconButtonProps, "icon" | "label" | "onClick" | "variant">): React.JSX.Element;
6
- export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, layout, sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle, sidebarShortcut, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, aside, asideLabel, asideVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
6
+ export declare function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant, topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout, sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle, sidebarShortcut, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant, aside, asideLabel, asideVariant, children, className, ...props }: HTMLAttributes<HTMLDivElement> & RefAttributes<HTMLDivElement> & {
7
7
  brand: ReactNode;
8
8
  navigation?: ReactNode;
9
9
  navItems?: SidebarEntry[];
10
10
  currentNavId?: string;
11
11
  navLabel?: string;
12
12
  topbar?: ReactNode;
13
- topbarVariant?: Exclude<HeaderVariant, "pill">;
13
+ topbarVariant?: HeaderVariant;
14
14
  topbarDivider?: boolean;
15
15
  sidebarVariant?: SidebarVariant;
16
16
  /** O arranjo (lote T, 17/10/2026): `"app"` (o padrão, o de sempre) ou `"docs"`, o da documentação da referência — um
@@ -30,6 +30,13 @@ export declare function AppShell({ brand, navigation, navItems, currentNavId, na
30
30
  topbarTabsLabel?: string;
31
31
  /** A ponta da linha das áreas (o `tabsEnd` do `Header`): a chave Web | Native da documentação. */
32
32
  topbarTabsEnd?: ReactNode;
33
+ /** A PONTA DA BARRA DE CIMA (MNT-37): o que fica na outra ponta, longe da marca e do `topbar` — a chave de tema, a
34
+ * conta, o sino. É o `actions` do `Header`. No `layout="sidebar"` a barra do conteúdo passa a existir também quando o app
35
+ * passa só isto; no `"docs"`, o botão da gaveta continua o último, depois delas. */
36
+ topbarActions?: ReactNode;
37
+ /** O site B (30/10/2026): o fundo da barra de cima que EMBAÇA o que passa por baixo — o `blurred` do `Header`, na
38
+ * `flush` e na `pill`. */
39
+ topbarBlurred?: boolean;
33
40
  /** 0.32.0: a barra de rolagem da lateral da esquerda — o `scrollbar` da `Sidebar`. Padrão `hover` (clara, só com o
34
41
  * ponteiro em cima); `visible` é a barra sempre à vista. */
35
42
  sidebarScrollbar?: SidebarScrollbar;
@@ -59,7 +59,8 @@ function focoDaGaveta(e) {
59
59
  // API pública intacta: as props seguem brand/navigation/topbar. O que mudou é ONDE a
60
60
  // marca sai — no topo, não na lateral. `topbarVariant` escolhe a pele do topo; o shell
61
61
  // precisa saber porque a lateral se encaixa abaixo dele (o `flush` não tem folga em
62
- // cima, então ela sobe um --space-4). `pill` é cabeçalho de site — não é pra shell.
62
+ // cima, então ela sobe um --space-4). ~~`pill` é cabeçalho de site — não é pra shell.~~ Desde o site B (30/10/2026), a
63
+ // `pill` entra no shell: a cápsula da largura do bloco, que flutua a --space-4 do alto (o `maxWidth` do `Header`).
63
64
  // A lateral RECOLHE em tela estreita: o <aside> é um popover nativo e o botão é o invoker. O
64
65
  // navegador entrega Escape, clique fora e o `aria-expanded` no disparador. Era o achado A8: em
65
66
  // 375px o `h1` da página começava 4,6 telas abaixo, depois dos 65 itens da navegação.
@@ -166,7 +167,7 @@ function ProvedorDaLateral({ ligado, children, ...p }) {
166
167
  return ligado ? _jsx(Sidebar.Provider, { id: SHELL_NAV_ID, ...p, children: children }) : _jsx(_Fragment, { children: children });
167
168
  }
168
169
  function AppShellSidebarToggle(props) { return _jsx(Sidebar.Toggle, { ...props }); }
169
- export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, layout = "app", sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle = "edge", sidebarShortcut = true, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", aside, asideLabel, asideVariant, children, className, ...props }) {
170
+ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel, topbar, topbarVariant = "floating", topbarDivider, topbarTabs, topbarCurrentTab, topbarTabsLabel, topbarTabsEnd, topbarActions, topbarBlurred, layout = "app", sidebarVariant, sidebarScrollbar, sidebarFooter, sidebarCollapsed, sidebarCollapsible, sidebarToggle = "edge", sidebarShortcut = true, sidebarIndicator, sidebarPanel, sidebarContrast, defaultSidebarCollapsed, onSidebarCollapsedChange, contentVariant = "surface", aside, asideLabel, asideVariant, children, className, ...props }) {
170
171
  const s = useAureaStrings();
171
172
  const docs = layout === "docs", lateral = layout === "sidebar";
172
173
  useGavetaFechaNoDesktop(docs);
@@ -223,9 +224,9 @@ export function AppShell({ brand, navigation, navItems, currentNavId, navLabel,
223
224
  // No arranjo, o topo é o pequeno (`size="sm"`), e o botão também: com o tamanho de sempre ele ficava 4 maior que os
224
225
  // vizinhos e fora da linha deles (medido a 375, sem toque; com toque, todos sobem para 44).
225
226
  const botaoDaGaveta = _jsx(IconButton, { className: "nav-toggle", icon: "list", label: s.navigationToggle, popoverTarget: SHELL_NAV_ID, ...(docs ? { size: "sm" } : {}) });
226
- return _jsx("div", { className: cx("app-shell", layout === "docs" && "app-shell-docs", lateral && "app-shell-lateral", !lateral && topbarVariant === "flush" && "app-shell-flush", recolhe && "app-shell-collapsible", aside != null && "app-shell-with-aside", className), ...props, children: _jsxs(ProvedorDaLateral, { ligado: recolhe, collapsed: efetiva, onCollapsedChange: definir, collapsible: forma, shortcut: sidebarShortcut, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), !lateral && _jsxs(Header, { variant: topbarVariant, divider: topbarDivider, tabs: topbarTabs, currentTab: topbarCurrentTab, tabsLabel: topbarTabsLabel, tabsEnd: topbarTabsEnd, ...(docs ? { maxWidth: "docs", size: "sm" } : {}), brand: docs ? brand : _jsxs(_Fragment, { children: [botaoDaGaveta, brand] }), children: [topbar, docs && botaoDaGaveta] }), recolhe && !lateral && sidebarToggle !== "none" && _jsx("div", { className: "sidebar-toggle-slot", children: _jsx(Tooltip, { content: rotulo, side: "right", children: _jsx(IconButton, { className: "sidebar-toggle", size: "xs", icon: paraOComeco === direita ? "caret-right" : "caret-left", label: rotulo, "aria-expanded": !efetiva, "aria-controls": SHELL_NAV_ID, onClick: alternar }) }) }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", variant: sidebarVariant, scrollbar: sidebarScrollbar, collapsed: efetiva, collapsible: forma, indicator: sidebarIndicator, panel: sidebarPanel, contrast: sidebarContrast, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, header: lateral ? brand : undefined, footer: sidebarFooter, children: navigation }), docs && aside != null && _jsx("div", { className: "docs-toc-bar", children: _jsxs("button", { type: "button", className: "docs-toc-toggle", popoverTarget: SHELL_TOC_ID, children: [_jsx(Icon, { name: "list-bullets" }), _jsx("span", { className: "docs-toc-current", "data-toc-current": "", children: asideLabel ?? s.tocLabel }), _jsx(Icon, { name: "caret-down" })] }) }), lateral
227
- ? _jsxs("div", { className: "app-shell-column", children: [topbar != null
228
- ? _jsx(Header, { variant: "flush", size: "sm", divider: topbarDivider, brand: _jsxs(_Fragment, { children: [botaoDaGaveta, sidebarToggle !== "none" && _jsx(AppShellSidebarToggle, {})] }), children: topbar })
227
+ return _jsx("div", { className: cx("app-shell", layout === "docs" && "app-shell-docs", lateral && "app-shell-lateral", !lateral && topbarVariant === "flush" && "app-shell-flush", !lateral && topbarVariant === "pill" && "app-shell-pill", recolhe && "app-shell-collapsible", aside != null && "app-shell-with-aside", className), ...props, children: _jsxs(ProvedorDaLateral, { ligado: recolhe, collapsed: efetiva, onCollapsedChange: definir, collapsible: forma, shortcut: sidebarShortcut, children: [_jsx("a", { className: "skip-link", href: `#${SHELL_MAIN_ID}`, children: s.skipToContent }), !lateral && _jsxs(Header, { variant: topbarVariant, divider: topbarDivider, tabs: topbarTabs, currentTab: topbarCurrentTab, tabsLabel: topbarTabsLabel, tabsEnd: topbarTabsEnd, ...(docs ? { maxWidth: "docs", ...(topbarVariant === "pill" ? {} : { size: "sm" }) } : topbarVariant === "pill" ? { maxWidth: "full" } : {}), blurred: topbarBlurred, brand: docs ? brand : _jsxs(_Fragment, { children: [botaoDaGaveta, brand] }), actions: topbarActions != null ? _jsxs(_Fragment, { children: [topbarActions, docs && botaoDaGaveta] }) : undefined, children: [topbar, docs && topbarActions == null && botaoDaGaveta] }), recolhe && !lateral && sidebarToggle !== "none" && _jsx("div", { className: "sidebar-toggle-slot", children: _jsx(Tooltip, { content: rotulo, side: "right", children: _jsx(IconButton, { className: "sidebar-toggle", size: "xs", icon: paraOComeco === direita ? "caret-right" : "caret-left", label: rotulo, "aria-expanded": !efetiva, "aria-controls": SHELL_NAV_ID, onClick: alternar }) }) }), _jsx(Sidebar, { id: SHELL_NAV_ID, popover: "auto", variant: sidebarVariant, scrollbar: sidebarScrollbar, collapsed: efetiva, collapsible: forma, indicator: sidebarIndicator, panel: sidebarPanel, contrast: sidebarContrast, items: navItems, current: currentNavId, label: navLabel, onToggle: focoDaGaveta, header: lateral ? brand : undefined, footer: sidebarFooter, children: navigation }), docs && aside != null && _jsx("div", { className: "docs-toc-bar", children: _jsxs("button", { type: "button", className: "docs-toc-toggle", popoverTarget: SHELL_TOC_ID, children: [_jsx(Icon, { name: "list-bullets" }), _jsx("span", { className: "docs-toc-current", "data-toc-current": "", children: asideLabel ?? s.tocLabel }), _jsx(Icon, { name: "caret-down" })] }) }), lateral
228
+ ? _jsxs("div", { className: "app-shell-column", children: [topbar != null || topbarActions != null
229
+ ? _jsx(Header, { variant: "flush", size: "sm", divider: topbarDivider, brand: _jsxs(_Fragment, { children: [botaoDaGaveta, sidebarToggle !== "none" && _jsx(AppShellSidebarToggle, {})] }), actions: topbarActions, children: topbar })
229
230
  : _jsxs("div", { className: "app-shell-narrow-bar", children: [botaoDaGaveta, brand] }), _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: cx("content", contentVariant === "plain" && "content-plain"), children: children })] })
230
231
  : _jsx("main", { id: SHELL_MAIN_ID, tabIndex: -1, className: cx("content", contentVariant === "plain" && "content-plain"), children: children }), aside != null && _jsx(Sidebar, { placement: "right", variant: asideVariant ?? sidebarVariant, className: "app-shell-aside", "aria-label": asideLabel ?? s.tocLabel, ...(docs ? { id: SHELL_TOC_ID, popover: "auto", onToggle: focoDaGaveta } : {}), children: aside })] }) });
231
232
  }
package/dist/markup.d.ts CHANGED
@@ -346,7 +346,8 @@ export interface HeaderProps extends HTMLAttributes<HTMLElement>, RefAttributes<
346
346
  current?: string;
347
347
  /** O que fica do lado direito: busca, botão de ação, conta. Fica na barra também no celular. */
348
348
  actions?: ReactNode;
349
- /** Até onde o miolo da barra cresce, pela escala do `Container` (`sm` 640 … `2xl` 1536, `full`). */
349
+ /** Até onde o miolo da barra cresce, pela escala do `Container` (`sm` 640 … `2xl` 1536, `full`). Na `pill` (o site B,
350
+ * 30/10/2026), a cápsula passa a ser o miolo, da largura do `Container`, e não do conteúdo. */
350
351
  maxWidth?: ContainerSize;
351
352
  /**
352
353
  * Onde ficam os links (14/10/2026): `"end"` (o padrão) logo antes das ações, na outra ponta da marca; `"start"`
@@ -360,8 +361,9 @@ export interface HeaderProps extends HTMLAttributes<HTMLElement>, RefAttributes<
360
361
  */
361
362
  size?: "md" | "sm";
362
363
  /**
363
- * Só na `flush` (15/10/2026): o fundo transparente que EMBAÇA o que passa por baixo, como o topo da página inicial
364
- * da referência. Quem pede menos transparência no sistema, ou o navegador sem desfoque, recebe o fundo cheio.
364
+ * Na `flush` (15/10/2026) e na `pill` (o site B, 30/10/2026): o fundo transparente que EMBAÇA o que passa por baixo,
365
+ * como o topo da página inicial da referência. Quem pede menos transparência no sistema, ou o navegador sem desfoque,
366
+ * recebe o fundo cheio.
365
367
  */
366
368
  blurred?: boolean;
367
369
  /** O que entra no MENU DO CELULAR, embaixo dos links (15/10/2026): o GitHub, a conta. Na barra larga não aparece. */
package/dist/markup.js CHANGED
@@ -217,7 +217,7 @@ export function Header({ variant = "floating", divider, brand, items, current, a
217
217
  // Sem o encaixe, a marcação de sempre; com ele, a linha das áreas e a ponta numa mesma faixa.
218
218
  return tabsEnd == null ? abas : _jsxs("div", { className: "topbar-tabs-row", children: [abas, _jsx("div", { className: "topbar-tabs-end", children: tabsEnd })] });
219
219
  })()] });
220
- return _jsx("header", { className: cx("topbar", `topbar-${variant}`, divider && variant === "flush" && "topbar-divider", site && "topbar-site", site && itemsJustify === "start" && "topbar-items-start", size === "sm" && "topbar-sm", blurred && variant === "flush" && "topbar-blurred", maxWidth && "topbar-contained", comAbas && "topbar-with-tabs", className), ...props, children: maxWidth ? _jsx("div", { className: cx("container", maxWidth !== "xl" && `container-${maxWidth}`, "topbar-inner"), children: miolo }) : miolo });
220
+ return _jsx("header", { className: cx("topbar", `topbar-${variant}`, divider && variant === "flush" && "topbar-divider", site && "topbar-site", site && itemsJustify === "start" && "topbar-items-start", size === "sm" && "topbar-sm", blurred && (variant === "flush" || variant === "pill") && "topbar-blurred", maxWidth && "topbar-contained", comAbas && "topbar-with-tabs", className), ...props, children: maxWidth ? _jsx("div", { className: cx("container", maxWidth !== "xl" && `container-${maxWidth}`, "topbar-inner"), children: miolo }) : miolo });
221
221
  }
222
222
  /** @deprecated `Topbar` é o nome antigo do `Header` (07/10/2026). Continua funcionando, igual; use `Header`. */
223
223
  export function Topbar(props) { return _jsx(Header, { ...props }); }
@@ -291,6 +291,13 @@ export function Link({ external, disabled, href, target, rel, className, childre
291
291
  // Transparente quando o texto diz alfa abaixo de 1 (hex de 4 ou 8 dígitos, `rgba()`, `hsla()`…). Cor de token ou
292
292
  // `oklch()` a marcação não lê: fica sem xadrez.
293
293
  const transparente = (cor) => { const c = parseColor(cor); return c !== null && c.a < 1; };
294
+ // A cor só entra no estilo quando É uma cor (31/10/2026, a auditoria de segurança): o texto ia cru para a variável, e
295
+ // no HTML do servidor um ";" abria regras novas (cobrir a tela, buscar imagem de outro endereço). Aceita o que a ficha
296
+ // promete — hex, nome, as funções de cor do CSS e `var(--token)` — e nada com ";", chaves, barra invertida, aspas,
297
+ // comentário ou outra função (`url(`). Recusada, a variável não se escreve: a amostra fica sem cor, e o nome continua.
298
+ // (O `parseColor` não serve de filtro: ele não lê `oklch()` nem `var()`, que a ficha promete.)
299
+ const COR_DE_CSS = /^(?:#[0-9a-f]{3,8}|[a-z]+|(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\([^;{}\\"'()]*\)|var\(--[\w-]+\))$/i;
300
+ const corDeCss = (cor) => { const c = cor.trim(); return COR_DE_CSS.test(c) && !c.includes("/*") ? c : undefined; };
294
301
  export function ColorSwatch(props) {
295
302
  if ("options" in props) {
296
303
  const { options, name, label, value, defaultValue, onChange, layout = "grid", indicator, showLabels, size = "md", disabled, id, className } = props;
@@ -302,14 +309,14 @@ export function ColorSwatch(props) {
302
309
  return _jsx("div", { role: "radiogroup", "aria-label": label, id: id, "aria-disabled": disabled || undefined, className: cx("color-swatch-picker", size !== "md" && `color-swatch-picker-${size}`, layout === "stack" && "color-swatch-picker-stack", showLabels && "color-swatch-picker-labels", className), children: options.map((o) => {
303
310
  const op = typeof o === "string" ? { color: o } : o;
304
311
  const cor = parseColor(op.color), rgb = cor && hsbToRgb(cor);
305
- const circulo = (Tag, key) => _jsxs(Tag, { className: cx("color-swatch-option", rgb && contrastingInk(rgb) === "dark" && "color-swatch-option-dark"), style: { ["--color-swatch-current"]: op.color }, children: [_jsx("input", { type: "radio", className: "color-swatch-input", name: name, value: op.color, "aria-label": op.label ?? op.color, checked: value === undefined ? undefined : value === op.color, defaultChecked: value === undefined ? defaultValue === op.color : undefined, disabled: disabled || op.disabled, ...(onChange ? { onChange } : {}) }), _jsx("span", { className: cx("color-swatch", cor && cor.a < 1 && "color-swatch-alpha"), "aria-hidden": "true" }), indicator != null && _jsx("span", { className: "color-swatch-indicator", "aria-hidden": "true", children: indicator })] }, key);
312
+ const circulo = (Tag, key) => _jsxs(Tag, { className: cx("color-swatch-option", rgb && contrastingInk(rgb) === "dark" && "color-swatch-option-dark"), style: { ["--color-swatch-current"]: corDeCss(op.color) }, children: [_jsx("input", { type: "radio", className: "color-swatch-input", name: name, value: op.color, "aria-label": op.label ?? op.color, checked: value === undefined ? undefined : value === op.color, defaultChecked: value === undefined ? defaultValue === op.color : undefined, disabled: disabled || op.disabled, ...(onChange ? { onChange } : {}) }), _jsx("span", { className: cx("color-swatch", cor && cor.a < 1 && "color-swatch-alpha"), "aria-hidden": "true" }), indicator != null && _jsx("span", { className: "color-swatch-indicator", "aria-hidden": "true", children: indicator })] }, key);
306
313
  return showLabels
307
314
  ? _jsxs("label", { className: "color-swatch-item", children: [circulo("span"), _jsx("span", { className: "color-swatch-name", "aria-hidden": "true", children: op.label ?? op.color })] }, op.color)
308
315
  : circulo("label", op.color);
309
316
  }) });
310
317
  }
311
318
  const { color, size = "md", label, className, style, ...resto } = props;
312
- return _jsx("span", { role: "img", "aria-label": label ?? color, className: cx("color-swatch", size !== "md" && `color-swatch-${size}`, transparente(color) && "color-swatch-alpha", className), style: { ...style, ["--color-swatch-current"]: color }, ...resto });
319
+ return _jsx("span", { role: "img", "aria-label": label ?? color, className: cx("color-swatch", size !== "md" && `color-swatch-${size}`, transparente(color) && "color-swatch-alpha", className), style: { ...style, ["--color-swatch-current"]: corDeCss(color) }, ...resto });
313
320
  }
314
321
  export function ProgressCircle({ value, min = 0, max = 100, size = "md", tone = "brand", label, className, ...props }) {
315
322
  const semTotal = value == null || Number.isNaN(value);
@@ -64,10 +64,14 @@ export interface TocItem {
64
64
  label: string;
65
65
  sub?: boolean;
66
66
  }
67
- export declare function TableOfContents({ items, current, label, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
67
+ export declare function TableOfContents({ items, current, label, progress, indicator, className, ...props }: HTMLAttributes<HTMLElement> & RefAttributes<HTMLElement> & {
68
68
  items: TocItem[];
69
69
  current?: string;
70
70
  label?: string;
71
+ /** O anel de leitura no lugar do ícone do nome: quanto da página já rolou, em anel e em número. */
72
+ progress?: boolean;
73
+ /** O desenho do item atual: `"line"` (o padrão, o traço do lado) ou `"segment"` (a cápsula levantada, como a da `Sidebar`). */
74
+ indicator?: "line" | "segment";
71
75
  }): React.JSX.Element;
72
76
  export interface TreeNode {
73
77
  id: string;
@@ -11,7 +11,7 @@ import { Dialog as BaseDialog } from "@base-ui/react/dialog";
11
11
  import { Popover as BasePopover } from "@base-ui/react/popover";
12
12
  import { useValorResponsivo } from "./responsivo-runtime.js";
13
13
  import { cx, fundirRender, useAureaStrings, usePortalContainer } from "./internal.js";
14
- import { Kbd } from "./markup.js";
14
+ import { Kbd, ProgressCircle } from "./markup.js";
15
15
  import { Icon } from "./system.js";
16
16
  import { Button, IconButton } from "./actions.js";
17
17
  import { Badge, Spinner } from "./feedback.js";
@@ -62,11 +62,29 @@ export function Pagination({ page, total, onPageChange, variant = "numbers", sum
62
62
  ? _jsx("li", { children: _jsx("span", { className: "pagination-ellipsis", "aria-hidden": "true", children: "\u2026" }) }, `e${i}`)
63
63
  : _jsx("li", { children: item(p, p, { atual: p === page, rotulo: `${s.paginationPage} ${p}` }) }, p)), _jsx("li", { children: proxima })] })] });
64
64
  }
65
- export function TableOfContents({ items, current, label, className, ...props }) {
65
+ //
66
+ // O SITE B (30/10/2026, protótipo): `progress` troca o ícone do nome pelo ANEL DE LEITURA — quanto da página já rolou, no
67
+ // anel (o `ProgressCircle`) e em número, na ponta da linha. É enfeite de leitura: o anel e o número ficam fora do leitor
68
+ // de tela (o que importa a ele é a seção atual, que o `aria-current` já diz).
69
+ export function TableOfContents({ items, current, label, progress, indicator = "line", className, ...props }) {
66
70
  const s = useAureaStrings();
67
71
  const meu = React.useRef(null);
68
72
  const observado = useSecaoEmVista(items, current === undefined, meu);
69
73
  const atual = current ?? observado;
74
+ const [lido, setLido] = React.useState(0);
75
+ const Rotulo = progress ? "div" : "p";
76
+ React.useEffect(() => {
77
+ if (!progress || typeof window === "undefined")
78
+ return;
79
+ const ler = () => {
80
+ const alto = document.documentElement.scrollHeight - window.innerHeight;
81
+ setLido(alto > 0 ? Math.round(Math.min(1, Math.max(0, window.scrollY / alto)) * 100) : 100);
82
+ };
83
+ ler();
84
+ window.addEventListener("scroll", ler, { passive: true });
85
+ window.addEventListener("resize", ler);
86
+ return () => { window.removeEventListener("scroll", ler); window.removeEventListener("resize", ler); };
87
+ }, [progress]);
70
88
  // O `ref` do consumidor continua chegando: em React 19 ele é prop comum, então espalhá-lo por
71
89
  // `...props` depois do nosso o sobrescreveria em silêncio. Os dois são atendidos aqui.
72
90
  const refDele = props.ref;
@@ -74,7 +92,9 @@ export function TableOfContents({ items, current, label, className, ...props })
74
92
  return _jsxs("nav", { ref: n => { meu.current = n; if (typeof refDele === "function")
75
93
  refDele(n);
76
94
  else if (refDele)
77
- refDele.current = n; }, className: cx("toc", className), "aria-label": label ?? s.tocLabel, ...resto, children: [_jsxs("p", { className: "toc-label", children: [_jsx("span", { className: "toc-label-icon", "aria-hidden": "true" }), label ?? s.tocLabel] }), items.map(i => _jsx("a", { href: `#${i.id}`, className: cx(i.sub && "toc-sub"), ...(i.id === atual ? { "aria-current": "true" } : {}), children: i.label }, i.id))] });
95
+ refDele.current = n; }, className: cx("toc", indicator === "segment" && "toc-segment", className), "aria-label": label ?? s.tocLabel, ...resto, children: [_jsxs(Rotulo, { className: cx("toc-label", progress && "toc-label-progress"), children: [progress
96
+ ? _jsx("span", { className: "toc-progress", "aria-hidden": "true", children: _jsx(ProgressCircle, { value: lido, size: "lg", label: label ?? s.tocLabel }) })
97
+ : _jsx("span", { className: "toc-label-icon", "aria-hidden": "true" }), label ?? s.tocLabel, progress && _jsxs("span", { className: "toc-progress-value", "aria-hidden": "true", children: [lido, "%"] })] }), items.map(i => _jsx("a", { href: `#${i.id}`, className: cx(i.sub && "toc-sub"), ...(i.id === atual ? { "aria-current": "true" } : {}), children: i.label }, i.id))] });
78
98
  }
79
99
  /** Qual dos `items` está na faixa de leitura, observando o documento. Devolve `undefined` até o
80
100
  * primeiro cálculo — e em SSR, onde não há documento para observar.
@@ -270,7 +290,7 @@ function SidebarGroup({ item, filhos, lid, ctx }) {
270
290
  ? _jsxs("button", { id: lid, type: "button", className: "sidebar-group-label sidebar-group-toggle", "aria-expanded": aberto, "aria-controls": lid + "-lista", onClick: () => setAberto(v => !v), children: [_jsx("span", { children: item.label }), _jsx(Icon, { name: aberto ? "caret-down" : direita ? "caret-left" : "caret-right", size: "sm", className: "sidebar-group-caret" })] })
271
291
  : _jsx("p", { id: lid, className: cx("sidebar-group-label", ctx.collapsed && "sr-only"), children: item.label });
272
292
  const lista = sidebarList(filhos, ctx, false, lid);
273
- return _jsxs("li", { children: [acao || dobra ? _jsxs("div", { className: "sidebar-group-head", children: [nome, acao && _jsx("span", { className: "sidebar-group-action", children: item.action })] }) : nome, dobra ? _jsx("div", { id: lid + "-lista", hidden: !aberto, children: lista }) : lista] });
293
+ return _jsxs("li", { children: [acao || dobra ? _jsxs("div", { className: "sidebar-group-head", children: [nome, acao && _jsx("span", { className: "sidebar-group-action", children: item.action })] }) : nome, dobra ? _jsx("div", { id: lid + "-lista", className: "sidebar-group-list", hidden: !aberto, children: lista }) : lista] });
274
294
  }
275
295
  // A GAVETA FECHA AO ESCOLHER (A-06, 23/09/2026). Abaixo de lg a lateral do `AppShell` é popover,
276
296
  // e a biblioteca a abria e NUNCA a fechava por código: `hidePopover` tinha zero ocorrências. O
package/dist/pure.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type ReactElement } from "react";
2
2
  export declare const cx: (...v: Array<string | false | null | undefined>) => string;
3
+ export declare const proprio: <V>(tabela: Readonly<Partial<Record<string, V>>>, chave: string | null | undefined) => V | undefined;
3
4
  export declare function fundirRender(render: ReactElement | undefined, props: {
4
5
  className?: string;
5
6
  } & Record<string, unknown>, padrao?: string): ReactElement;
package/dist/pure.js CHANGED
@@ -16,6 +16,10 @@
16
16
  // `.ts` aqui seria um arquivo que gate nenhum enxerga.
17
17
  import { cloneElement, createElement } from "react";
18
18
  export const cx = (...v) => v.filter(Boolean).join(" ");
19
+ // O valor de uma tabela só quando a chave é DELA (31/10/2026, a auditoria de segurança). Tabela escrita como objeto
20
+ // comum herda o `Object.prototype`: uma chave que vem de fora ("constructor", "__proto__", "toString") devolvia a
21
+ // função dele — no realce do `CodeBlock`, isso derrubava a tela. Interno: o barril não o publica.
22
+ export const proprio = (tabela, chave) => chave != null && Object.hasOwn(tabela, chave) ? tabela[chave] : undefined;
19
23
  // ── `render`: a superfície é da Aurea, o ELEMENTO é de quem consome ───────────────────────────
20
24
  //
21
25
  // Nasceu do `G-A11Y-11`. O `Card` oferecia `variant="interactive"` — ponteiro de mão, elevação
package/dist/system.js CHANGED
@@ -7,7 +7,7 @@ import React from "react";
7
7
  import { Toast as BaseToast } from "@base-ui/react/toast";
8
8
  import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
9
9
  import { DirectionProvider } from "@base-ui/react/direction-provider";
10
- import { peleDoEixo } from "./pure.js";
10
+ import { peleDoEixo, proprio } from "./pure.js";
11
11
  import { cx, StringsContext, SpriteContext, PortalContext, ThemeContext, DensityContext, DentroDoProviderContext, avisosAncorados, usePortalContainer, defaultStrings, defaultSpriteUrl, useSpriteUrl, useAureaStrings } from "./internal.js";
12
12
  // `theme` e `density` seguem a convenção controlado/não-controlado do resto da biblioteca
13
13
  // (`Toggle`, `ToggleGroup`): passe `theme` para mandar, `defaultTheme` para semear, e ouça por
@@ -186,7 +186,7 @@ function AureaToastList({ placement }) {
186
186
  const arrasto = [placement.startsWith("top") ? "up" : "down", "right"];
187
187
  return _jsx(_Fragment, { children: toasts.map(t => {
188
188
  const tipo = t.type === "error" ? "danger" : t.type;
189
- return _jsxs(BaseToast.Root, { toast: t, swipeDirection: arrasto, className: cx("toast", tipo && `toast-${tipo}`), children: [_jsxs(BaseToast.Content, { className: "toast-content", children: [_jsx("span", { className: "toast-indicator", "aria-hidden": "true", children: tipo === "loading" ? _jsx("span", { className: "spinner" }) : _jsx(Icon, { name: ICONE_DO_AVISO[tipo ?? ""] ?? "info" }) }), _jsxs("div", { className: "toast-text", children: [_jsx(BaseToast.Title, { className: "toast-title", render: _jsx("strong", {}) }), t.description ? _jsx(BaseToast.Description, { className: "toast-description" }) : null] }), t.actionProps ? _jsx(BaseToast.Action, { className: "btn btn-tertiary btn-sm toast-action" }) : null] }), _jsx(BaseToast.Close, { className: "btn btn-tertiary close-button toast-close", "aria-label": s.dismissNotification, children: _jsx("span", { children: _jsx(Icon, { name: "x", weight: "bold" }) }) })] }, t.id);
189
+ return _jsxs(BaseToast.Root, { toast: t, swipeDirection: arrasto, className: cx("toast", tipo && `toast-${tipo}`), children: [_jsxs(BaseToast.Content, { className: "toast-content", children: [_jsx("span", { className: "toast-indicator", "aria-hidden": "true", children: tipo === "loading" ? _jsx("span", { className: "spinner" }) : _jsx(Icon, { name: proprio(ICONE_DO_AVISO, tipo) ?? "info" }) }), _jsxs("div", { className: "toast-text", children: [_jsx(BaseToast.Title, { className: "toast-title", render: _jsx("strong", {}) }), t.description ? _jsx(BaseToast.Description, { className: "toast-description" }) : null] }), t.actionProps ? _jsx(BaseToast.Action, { className: "btn btn-tertiary btn-sm toast-action" }) : null] }), _jsx(BaseToast.Close, { className: "btn btn-tertiary close-button toast-close", "aria-label": s.dismissNotification, children: _jsx("span", { children: _jsx(Icon, { name: "x", weight: "bold" }) }) })] }, t.id);
190
190
  }) });
191
191
  }
192
192
  function AureaToastViewport({ placement }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurea-uds/react",
3
- "version": "0.50.0",
3
+ "version": "0.52.0",
4
4
  "description": "Os componentes React da Aurea UDS. Importe @aurea-uds/fonts/css e @aurea-uds/core/css junto.",
5
5
  "keywords": [
6
6
  "aurea",