@aurea-uds/react 0.18.0 → 0.19.1
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/communication.d.ts +57 -4
- package/dist/communication.js +237 -14
- package/dist/file-input.d.ts +1 -0
- package/dist/file-input.js +3 -1
- package/dist/media-client.d.ts +10 -1
- package/dist/media-client.js +31 -7
- package/dist/navigation-client.d.ts +4 -1
- package/dist/navigation-client.js +32 -15
- package/dist/pure.d.ts +10 -0
- package/dist/pure.js +2 -2
- package/package.json +1 -1
package/dist/communication.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import React, { type ReactNode } from "react";
|
|
2
2
|
import { type IconName } from "./system.js";
|
|
3
3
|
import { type BadgeVariant } from "./feedback.js";
|
|
4
|
+
export type ChatDirection = "incoming" | "outgoing";
|
|
5
|
+
export interface MessageAttachment {
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "image" | "video" | "file";
|
|
8
|
+
src?: string;
|
|
9
|
+
alt?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
bytes?: number;
|
|
12
|
+
duration?: number;
|
|
13
|
+
href?: string;
|
|
14
|
+
}
|
|
4
15
|
export interface ChatMessage {
|
|
5
16
|
id: string;
|
|
6
17
|
body: ReactNode;
|
|
@@ -14,18 +25,60 @@ export interface ChatMessage {
|
|
|
14
25
|
label: ReactNode;
|
|
15
26
|
variant?: BadgeVariant;
|
|
16
27
|
};
|
|
28
|
+
direction?: ChatDirection;
|
|
29
|
+
attachments?: MessageAttachment[];
|
|
30
|
+
replyTo?: {
|
|
31
|
+
id: string;
|
|
32
|
+
author?: ReactNode;
|
|
33
|
+
body: ReactNode;
|
|
34
|
+
};
|
|
35
|
+
forwardedFrom?: ReactNode;
|
|
36
|
+
edited?: boolean;
|
|
37
|
+
day?: string;
|
|
17
38
|
}
|
|
18
|
-
export
|
|
39
|
+
export interface MessageListProps {
|
|
19
40
|
messages: ChatMessage[];
|
|
20
41
|
label?: string;
|
|
21
42
|
className?: string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
hasMoreBefore?: boolean;
|
|
44
|
+
hasMoreAfter?: boolean;
|
|
45
|
+
loadingBefore?: boolean;
|
|
46
|
+
loadingAfter?: boolean;
|
|
47
|
+
onReachStart?: () => void;
|
|
48
|
+
onReachEnd?: () => void;
|
|
49
|
+
onOpenAttachment?: (message: ChatMessage, attachment: MessageAttachment) => void;
|
|
50
|
+
}
|
|
51
|
+
export declare function MessageList({ messages, label, className, hasMoreBefore, hasMoreAfter, loadingBefore, loadingAfter, onReachStart, onReachEnd, onOpenAttachment }: MessageListProps): React.JSX.Element;
|
|
52
|
+
export interface ComposerReply {
|
|
53
|
+
id: string;
|
|
54
|
+
author?: ReactNode;
|
|
55
|
+
body: ReactNode;
|
|
56
|
+
}
|
|
57
|
+
export interface ComposerEdit {
|
|
58
|
+
id: string;
|
|
59
|
+
body: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ComposerAttach {
|
|
62
|
+
accept?: string;
|
|
63
|
+
maxSize?: number;
|
|
64
|
+
multiple?: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface MessageSendDetails {
|
|
67
|
+
files: File[];
|
|
68
|
+
replyToId?: string;
|
|
69
|
+
editingId?: string;
|
|
70
|
+
}
|
|
71
|
+
export declare function MessageComposer({ onSend, placeholder, label, icon, sendLabel, disabled, className, attach, replyTo, onCancelReply, editing, onCancelEdit }: {
|
|
72
|
+
onSend: (text: string, details?: MessageSendDetails) => void;
|
|
25
73
|
placeholder?: string;
|
|
26
74
|
label?: string;
|
|
27
75
|
icon?: IconName;
|
|
28
76
|
sendLabel?: string;
|
|
29
77
|
disabled?: boolean;
|
|
30
78
|
className?: string;
|
|
79
|
+
attach?: boolean | ComposerAttach;
|
|
80
|
+
replyTo?: ComposerReply | null;
|
|
81
|
+
onCancelReply?: () => void;
|
|
82
|
+
editing?: ComposerEdit | null;
|
|
83
|
+
onCancelEdit?: () => void;
|
|
31
84
|
}): React.JSX.Element;
|
package/dist/communication.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
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
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
@@ -9,21 +9,244 @@ import { InputGroup, InputGroupAddon } from "./inputs.js";
|
|
|
9
9
|
import { Icon } from "./system.js";
|
|
10
10
|
import { IconButton } from "./actions.js";
|
|
11
11
|
import { Avatar } from "./identity.js";
|
|
12
|
-
import { Badge } from "./feedback.js";
|
|
13
|
-
|
|
12
|
+
import { Badge, Spinner } from "./feedback.js";
|
|
13
|
+
import { formatSize, matchesAccept } from "./file-input.js";
|
|
14
|
+
// A AN-02 põe álbum na mensagem com a `Gallery` que já existe. `media` mora no "resto" do DAG,
|
|
15
|
+
// como este módulo, e não importa `communication` — não há ciclo.
|
|
16
|
+
import { Gallery } from "./media.js";
|
|
17
|
+
// O contêiner que rola é o ancestral com `overflow` de rolagem, ou a página.
|
|
18
|
+
function rolador(el) {
|
|
19
|
+
for (let p = el.parentElement; p; p = p.parentElement) {
|
|
20
|
+
const o = getComputedStyle(p).overflowY;
|
|
21
|
+
if (o === "auto" || o === "scroll" || o === "overlay")
|
|
22
|
+
return p;
|
|
23
|
+
}
|
|
24
|
+
return document.scrollingElement ?? document.documentElement;
|
|
25
|
+
}
|
|
26
|
+
const ehPagina = (sc) => sc === document.scrollingElement || sc === document.documentElement;
|
|
27
|
+
function useVigia(alvo, ligado, avisar, deps) {
|
|
28
|
+
const cb = React.useRef(avisar);
|
|
29
|
+
cb.current = avisar;
|
|
30
|
+
React.useEffect(() => {
|
|
31
|
+
const el = alvo.current;
|
|
32
|
+
if (!el || !ligado || typeof IntersectionObserver === "undefined")
|
|
33
|
+
return;
|
|
34
|
+
const sc = rolador(el);
|
|
35
|
+
const obs = new IntersectionObserver(es => { if (es.some(e => e.isIntersecting))
|
|
36
|
+
cb.current?.(); }, { root: ehPagina(sc) ? null : sc });
|
|
37
|
+
obs.observe(el);
|
|
38
|
+
return () => obs.disconnect();
|
|
39
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
+
}, [ligado, ...deps]);
|
|
41
|
+
}
|
|
42
|
+
export function MessageList({ messages, label, className, hasMoreBefore, hasMoreAfter, loadingBefore, loadingAfter, onReachStart, onReachEnd, onOpenAttachment }) {
|
|
14
43
|
const s = useAureaStrings();
|
|
15
|
-
|
|
44
|
+
const janela = !!onReachStart || !!onReachEnd;
|
|
45
|
+
const raiz = React.useRef(null);
|
|
46
|
+
const topo = React.useRef(null);
|
|
47
|
+
const fim = React.useRef(null);
|
|
48
|
+
const ancora = React.useRef(null);
|
|
49
|
+
const noFim = React.useRef(true);
|
|
50
|
+
const ultimoAntes = React.useRef(undefined);
|
|
51
|
+
const montou = React.useRef(false);
|
|
52
|
+
const [anuncio, setAnuncio] = React.useState("");
|
|
53
|
+
const hasMoreAfterRef = React.useRef(hasMoreAfter);
|
|
54
|
+
hasMoreAfterRef.current = hasMoreAfter;
|
|
55
|
+
// A posição de quem está no alto da tela, e se a última está à vista. Lida a cada rolagem e
|
|
56
|
+
// depois de cada troca: entre uma troca e outra, só a rolagem move as mensagens.
|
|
57
|
+
const medir = React.useCallback(() => {
|
|
58
|
+
const el = raiz.current;
|
|
59
|
+
if (!el)
|
|
60
|
+
return;
|
|
61
|
+
const sc = rolador(el), pag = ehPagina(sc);
|
|
62
|
+
const alto = pag ? 0 : sc.getBoundingClientRect().top, baixo = pag ? window.innerHeight : sc.getBoundingClientRect().bottom;
|
|
63
|
+
const itens = el.querySelectorAll("[data-message-id]");
|
|
64
|
+
// A âncora é a primeira que COMEÇA dentro da tela, e não a primeira que aparece: medido no banco
|
|
65
|
+
// em 04/10/2026, um álbum cortado no alto cresceu 480 px quando as fotos chegaram — para baixo
|
|
66
|
+
// dele —, o topo dele não andou, e a conversa inteira desceu sem a conta perceber. Se nenhuma
|
|
67
|
+
// começa na tela (uma mensagem maior que ela), vale a que está à vista.
|
|
68
|
+
ancora.current = null;
|
|
69
|
+
let vista;
|
|
70
|
+
for (const it of Array.from(itens)) {
|
|
71
|
+
const r = it.getBoundingClientRect();
|
|
72
|
+
if (r.bottom <= alto)
|
|
73
|
+
continue;
|
|
74
|
+
vista ??= it;
|
|
75
|
+
if (r.top >= alto) {
|
|
76
|
+
ancora.current = { id: it.dataset.messageId, top: r.top };
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!ancora.current && vista)
|
|
81
|
+
ancora.current = { id: vista.dataset.messageId, top: vista.getBoundingClientRect().top };
|
|
82
|
+
const ultimo = itens[itens.length - 1];
|
|
83
|
+
noFim.current = !ultimo || ultimo.getBoundingClientRect().bottom <= baixo + 1;
|
|
84
|
+
}, []);
|
|
85
|
+
React.useEffect(() => {
|
|
86
|
+
if (!janela || !raiz.current)
|
|
87
|
+
return;
|
|
88
|
+
const sc = rolador(raiz.current), alvo = ehPagina(sc) ? window : sc;
|
|
89
|
+
alvo.addEventListener("scroll", medir, { passive: true });
|
|
90
|
+
return () => alvo.removeEventListener("scroll", medir);
|
|
91
|
+
}, [janela, medir]);
|
|
92
|
+
const ultimoId = messages[messages.length - 1]?.id;
|
|
93
|
+
React.useLayoutEffect(() => {
|
|
94
|
+
const el = raiz.current;
|
|
95
|
+
if (!janela || !el) {
|
|
96
|
+
ultimoAntes.current = ultimoId;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const sc = rolador(el);
|
|
100
|
+
const irAoFim = () => { sc.scrollTop = sc.scrollHeight; };
|
|
101
|
+
const antes = ultimoAntes.current;
|
|
102
|
+
// Chegou mensagem no fim: a última de antes continua na lista, e não é mais a última.
|
|
103
|
+
const ia = antes === undefined ? -1 : messages.findIndex(m => m.id === antes);
|
|
104
|
+
const chegou = montou.current && ia >= 0 && ia < messages.length - 1;
|
|
105
|
+
if (!montou.current) {
|
|
106
|
+
irAoFim();
|
|
107
|
+
montou.current = true;
|
|
108
|
+
}
|
|
109
|
+
else if (chegou && noFim.current && !hasMoreAfter)
|
|
110
|
+
irAoFim();
|
|
111
|
+
else if (ancora.current) {
|
|
112
|
+
const it = el.querySelector(`[data-message-id="${CSS.escape(ancora.current.id)}"]`);
|
|
113
|
+
if (it)
|
|
114
|
+
sc.scrollTop += it.getBoundingClientRect().top - ancora.current.top;
|
|
115
|
+
}
|
|
116
|
+
if (chegou) {
|
|
117
|
+
const novo = el.querySelector(`[data-message-id="${CSS.escape(ultimoId)}"]`);
|
|
118
|
+
setAnuncio(novo?.textContent ?? "");
|
|
119
|
+
}
|
|
120
|
+
ultimoAntes.current = ultimoId;
|
|
121
|
+
medir();
|
|
122
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
+
}, [messages]);
|
|
124
|
+
// A ALTURA MUDA DEPOIS DE MONTAR — a fonte chega, a foto decodifica, o app troca um texto — e
|
|
125
|
+
// nenhuma rolagem acontece para avisar. Medido no banco em 04/10/2026: a conversa abria na #4992
|
|
126
|
+
// e não na última. Então se observa o tamanho da lista: quem estava no fim continua no fim; quem
|
|
127
|
+
// não estava mantém a mensagem do alto onde ela estava.
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
const el = raiz.current;
|
|
130
|
+
if (!janela || !el || typeof ResizeObserver === "undefined")
|
|
131
|
+
return;
|
|
132
|
+
const ro = new ResizeObserver(() => {
|
|
133
|
+
const sc = rolador(el);
|
|
134
|
+
if (noFim.current && !hasMoreAfterRef.current)
|
|
135
|
+
sc.scrollTop = sc.scrollHeight;
|
|
136
|
+
else if (ancora.current) {
|
|
137
|
+
const it = el.querySelector(`[data-message-id="${CSS.escape(ancora.current.id)}"]`);
|
|
138
|
+
if (it)
|
|
139
|
+
sc.scrollTop += it.getBoundingClientRect().top - ancora.current.top;
|
|
140
|
+
}
|
|
141
|
+
medir();
|
|
142
|
+
});
|
|
143
|
+
ro.observe(el);
|
|
144
|
+
return () => ro.disconnect();
|
|
145
|
+
}, [janela, medir]);
|
|
146
|
+
useVigia(topo, janela && !!hasMoreBefore && !loadingBefore, onReachStart, [messages.length]);
|
|
147
|
+
useVigia(fim, janela && !!hasMoreAfter && !loadingAfter, onReachEnd, [messages.length]);
|
|
148
|
+
return _jsxs(_Fragment, { children: [_jsxs("div", { ref: raiz, className: cx("stack", className), role: "log", "aria-live": janela ? "off" : "polite", "aria-busy": (janela && (loadingBefore || loadingAfter)) || undefined, "aria-label": label ?? s.chatLabel, children: [janela && hasMoreBefore && _jsx("div", { ref: topo, className: "message-more", children: loadingBefore && _jsx(Spinner, {}) }), messages.map((m, i) => {
|
|
149
|
+
const lado = m.direction;
|
|
150
|
+
const midia = (m.attachments ?? []).filter(a => a.kind !== "file" && a.src);
|
|
151
|
+
const arquivos = (m.attachments ?? []).filter(a => a.kind === "file");
|
|
152
|
+
const dia = m.day != null && m.day !== messages[i - 1]?.day;
|
|
153
|
+
return _jsxs(React.Fragment, { children: [dia && _jsx("div", { className: "message-day", children: _jsx(Badge, { children: m.day }) }), _jsxs("div", { className: cx("message", lado === "outgoing" && "message-out", lado === "incoming" && "message-in"), "data-message-id": m.id, children: [lado === "outgoing" ? null : m.avatar ? _jsx(Avatar, { src: m.avatar.src, fallback: m.avatar.fallback }) : _jsx("span", {}), _jsxs("div", { className: "message-bubble", children: [(m.author || m.time || m.edited) && _jsxs("div", { className: "label", children: [_jsx("span", { children: m.author }), (m.time || m.edited) && _jsxs("span", { className: "hint", children: [m.edited && _jsxs(_Fragment, { children: [s.chatEdited, m.time != null && " · "] }), m.time] })] }), m.forwardedFrom != null && _jsxs("div", { className: "message-forwarded", children: [_jsx(Icon, { name: "share-fat", size: "sm" }), _jsxs("span", { children: [s.chatForwardedFrom, " ", m.forwardedFrom] })] }), m.replyTo && _jsx("div", { className: "message-quote", children: _jsxs("span", { className: "message-quote-text", children: [_jsx("span", { className: "message-quote-title", children: m.replyTo.author }), _jsx("span", { className: "message-quote-body", children: m.replyTo.body })] }) }), midia.length > 0 && _jsx(Gallery, { className: "message-album", label: s.chatAttachments, zoom: !onOpenAttachment, ratio: midia.length === 1 ? "4/3" : "1/1", items: midia.map(a => ({ id: a.id, src: a.src, alt: a.alt ?? a.name ?? "", kind: a.kind === "video" ? "video" : "image", duration: a.duration })), onSelect: onOpenAttachment ? (id => { const a = midia.find(x => x.id === id); if (a)
|
|
154
|
+
onOpenAttachment(m, a); }) : undefined }), arquivos.length > 0 && _jsx("ul", { className: "file-list", "aria-label": s.chatAttachments, children: arquivos.map(a => _jsxs("li", { className: "file-item", children: [_jsx(Icon, { name: "file-text" }), a.href ? _jsx("a", { className: "file-name", href: a.href, download: a.name ?? true, children: a.name ?? a.href }) : _jsx("span", { className: "file-name", children: a.name }), a.bytes != null && _jsx("span", { className: "file-size", children: formatSize(a.bytes) })] }, a.id)) }), m.body, m.status && _jsx("div", { className: "message-status", children: _jsxs(Badge, { variant: m.status.variant, children: [_jsx("i", { className: "status-dot" }), m.status.label] }) })] })] })] }, m.id);
|
|
155
|
+
}), janela && hasMoreAfter && _jsx("div", { ref: fim, className: "message-more", children: loadingAfter && _jsx(Spinner, {}) })] }), janela && _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: anuncio })] });
|
|
16
156
|
}
|
|
17
|
-
|
|
18
|
-
// Texto é estado interno (não controlado — controlar de fora só com demanda real,
|
|
19
|
-
// precedente MediaPlayer/FileInput). Não envia texto vazio/só-espaço; o botão de
|
|
20
|
-
// envio desabilita enquanto vazio. O input sempre tem nome acessível (aria-label),
|
|
21
|
-
// pois placeholder não serve de nome. ponytail: input de uma linha; textarea +
|
|
22
|
-
// Shift+Enter quando surgir demanda de multilinha.
|
|
23
|
-
export function MessageComposer({ onSend, placeholder, label, icon, sendLabel, disabled, className }) {
|
|
157
|
+
export function MessageComposer({ onSend, placeholder, label, icon, sendLabel, disabled, className, attach, replyTo, onCancelReply, editing, onCancelEdit }) {
|
|
24
158
|
const s = useAureaStrings();
|
|
25
159
|
const [text, setText] = React.useState("");
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
160
|
+
const [anexos, setAnexos] = React.useState([]);
|
|
161
|
+
const [recusados, setRecusados] = React.useState([]);
|
|
162
|
+
const [aviso, setAviso] = React.useState("");
|
|
163
|
+
const campo = React.useRef(null);
|
|
164
|
+
const seletor = React.useRef(null);
|
|
165
|
+
const seq = React.useRef(0);
|
|
166
|
+
const opcoes = attach === true ? {} : attach || undefined;
|
|
167
|
+
const editandoId = editing?.id;
|
|
168
|
+
const respondendoId = replyTo?.id;
|
|
169
|
+
// O texto antigo entra quando a edição COMEÇA (muda o id), e sai quando ela acaba — sem isso o
|
|
170
|
+
// texto da mensagem editada ficava no campo como rascunho de mensagem nova.
|
|
171
|
+
const editouAntes = React.useRef(undefined);
|
|
172
|
+
React.useEffect(() => {
|
|
173
|
+
if (editandoId !== undefined) {
|
|
174
|
+
setText(editing?.body ?? "");
|
|
175
|
+
campo.current?.focus();
|
|
176
|
+
}
|
|
177
|
+
else if (editouAntes.current !== undefined)
|
|
178
|
+
setText("");
|
|
179
|
+
editouAntes.current = editandoId;
|
|
180
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
181
|
+
}, [editandoId]);
|
|
182
|
+
React.useEffect(() => { if (respondendoId !== undefined)
|
|
183
|
+
campo.current?.focus(); }, [respondendoId]);
|
|
184
|
+
// As URLs das miniaturas vivem enquanto o anexo vive; desmontar solta todas (o FileInput faz igual).
|
|
185
|
+
const anexosRef = React.useRef(anexos);
|
|
186
|
+
anexosRef.current = anexos;
|
|
187
|
+
React.useEffect(() => () => anexosRef.current.forEach(a => a.url && URL.revokeObjectURL(a.url)), []);
|
|
188
|
+
const soltar = (lista) => lista.forEach(a => a.url && URL.revokeObjectURL(a.url));
|
|
189
|
+
const escolher = (lista) => {
|
|
190
|
+
if (!lista)
|
|
191
|
+
return;
|
|
192
|
+
const novos = [], fora = [];
|
|
193
|
+
for (const file of Array.from(lista)) {
|
|
194
|
+
if (!matchesAccept(file, opcoes?.accept))
|
|
195
|
+
fora.push({ name: file.name, reason: s.fileWrongType });
|
|
196
|
+
else if (opcoes?.maxSize != null && file.size > opcoes.maxSize)
|
|
197
|
+
fora.push({ name: file.name, reason: s.fileTooLarge });
|
|
198
|
+
else
|
|
199
|
+
novos.push({ id: `a${seq.current++}`, file, url: file.type.startsWith("image/") && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : undefined });
|
|
200
|
+
}
|
|
201
|
+
setRecusados(fora);
|
|
202
|
+
if (novos.length) {
|
|
203
|
+
if (opcoes?.multiple === false) {
|
|
204
|
+
soltar(anexos);
|
|
205
|
+
setAnexos(novos.slice(0, 1));
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
setAnexos(prev => [...prev, ...novos]);
|
|
209
|
+
setAviso(`${s.fileAdded}: ${novos.map(a => a.file.name).join(", ")}`);
|
|
210
|
+
}
|
|
211
|
+
if (seletor.current)
|
|
212
|
+
seletor.current.value = "";
|
|
213
|
+
};
|
|
214
|
+
const remover = (a) => { soltar([a]); setAnexos(prev => prev.filter(x => x.id !== a.id)); setAviso(`${s.fileRemoved}: ${a.file.name}`); campo.current?.focus(); };
|
|
215
|
+
const podeAnexar = !!opcoes && editandoId === undefined;
|
|
216
|
+
const vazio = !text.trim() && !(podeAnexar && anexos.length);
|
|
217
|
+
const submit = (e) => {
|
|
218
|
+
e.preventDefault();
|
|
219
|
+
if (vazio)
|
|
220
|
+
return;
|
|
221
|
+
const t = text.trim();
|
|
222
|
+
// O segundo argumento só vai quando o app usa uma prop nova: quem chamava `onSend(texto)` e
|
|
223
|
+
// conferia a chamada exata continua recebendo exatamente isso.
|
|
224
|
+
if (!opcoes && replyTo == null && editing == null)
|
|
225
|
+
onSend(t);
|
|
226
|
+
else
|
|
227
|
+
onSend(t, { files: podeAnexar ? anexos.map(a => a.file) : [], replyToId: editandoId === undefined ? respondendoId : undefined, editingId: editandoId });
|
|
228
|
+
setText("");
|
|
229
|
+
soltar(anexos);
|
|
230
|
+
setAnexos([]);
|
|
231
|
+
setRecusados([]);
|
|
232
|
+
};
|
|
233
|
+
const aoTeclar = (e) => {
|
|
234
|
+
if (e.key !== "Escape")
|
|
235
|
+
return;
|
|
236
|
+
if (editandoId !== undefined && onCancelEdit) {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
onCancelEdit();
|
|
239
|
+
}
|
|
240
|
+
else if (respondendoId !== undefined && onCancelReply) {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
onCancelReply();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const citacao = editing
|
|
246
|
+
? _jsxs("div", { className: "message-quote", children: [_jsx(Icon, { name: "pencil-simple", size: "sm" }), _jsxs("span", { className: "message-quote-text", children: [_jsx("span", { className: "message-quote-title", children: s.chatEditing }), _jsx("span", { className: "message-quote-body", children: editing.body })] }), onCancelEdit && _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.chatEditCancel, onClick: onCancelEdit })] })
|
|
247
|
+
: replyTo
|
|
248
|
+
? _jsxs("div", { className: "message-quote", children: [_jsx(Icon, { name: "arrow-bend-up-left", size: "sm" }), _jsxs("span", { className: "message-quote-text", children: [_jsxs("span", { className: "message-quote-title", children: [s.chatReplyTo, replyTo.author != null && _jsxs(_Fragment, { children: [" ", replyTo.author] })] }), _jsx("span", { className: "message-quote-body", children: replyTo.body })] }), onCancelReply && _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: s.chatReplyCancel, onClick: onCancelReply })] })
|
|
249
|
+
: null;
|
|
250
|
+
const temFaixa = !!citacao || (podeAnexar && (anexos.length > 0 || recusados.length > 0));
|
|
251
|
+
return _jsxs("form", { className: cx("message-composer", className), onSubmit: submit, children: [_jsxs(InputGroup, { children: [icon && _jsx(InputGroupAddon, { children: _jsx(Icon, { name: icon }) }), temFaixa && _jsxs("div", { className: "input-group-addon input-group-addon-start input-group-addon-block message-composer-context", children: [citacao, podeAnexar && anexos.length > 0 && _jsx("ul", { className: "file-list", "aria-label": s.chatAttachments, children: anexos.map(a => _jsxs("li", { className: "file-item", children: [a.url ? _jsx("img", { className: "file-thumb", src: a.url, alt: "" }) : _jsx(Icon, { name: "file-text" }), _jsx("span", { className: "file-name", children: a.file.name }), _jsx("span", { className: "file-size", children: formatSize(a.file.size) }), _jsx(IconButton, { variant: "ghost", size: "sm", icon: "x", label: `${s.fileRemove} ${a.file.name}`, onClick: () => remover(a) })] }, a.id)) }), podeAnexar && recusados.map((r, n) => _jsxs("span", { className: "field-error", children: [r.reason, ": ", r.name] }, n))] }), _jsx("input", { ref: campo, className: "input", value: text, disabled: disabled, placeholder: placeholder, "aria-label": label ?? s.chatMessage, onChange: e => setText(e.target.value), onKeyDown: aoTeclar }), podeAnexar && _jsx(InputGroupAddon, { side: "end", children: _jsx(IconButton, { variant: "ghost", size: "sm", icon: "paperclip", label: s.chatAttach, disabled: disabled, onClick: () => seletor.current?.click() }) })] }), podeAnexar && _jsx("input", { ref: seletor, type: "file", hidden: true, accept: opcoes?.accept, multiple: opcoes?.multiple !== false, onChange: e => escolher(e.target.files) }), _jsx(IconButton, { type: "submit", variant: "primary", icon: editing ? "check" : "paper-plane-tilt", label: editing ? s.chatSave : (sendLabel ?? s.chatSend), disabled: disabled || vazio }), podeAnexar && _jsx("span", { className: "sr-only", role: "status", "aria-live": "polite", children: aviso })] });
|
|
29
252
|
}
|
package/dist/file-input.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare function matchesAccept(file: {
|
|
|
26
26
|
name: string;
|
|
27
27
|
type: string;
|
|
28
28
|
}, accept?: string): boolean;
|
|
29
|
+
export declare function formatSize(bytes: number): string;
|
|
29
30
|
export declare function FileInput({ accept, maxSize, multiple, onFilesChange, upload, label, hint, id, className, initialQueue, onQueueChange, preview, checksum }: {
|
|
30
31
|
accept?: string;
|
|
31
32
|
maxSize?: number;
|
package/dist/file-input.js
CHANGED
|
@@ -57,7 +57,9 @@ async function somaSha256(file) {
|
|
|
57
57
|
// nova, zero branch novo.
|
|
58
58
|
const ATIVOS = new Set(["pending", "uploading", "paused"]);
|
|
59
59
|
const restaurar = (q) => ({ ...q, restored: true, status: ATIVOS.has(q.status) ? "canceled" : q.status });
|
|
60
|
-
|
|
60
|
+
// Exportada para dentro do pacote (não sai no barril): o `MessageComposer` mostra o tamanho do
|
|
61
|
+
// anexo no mesmo formato da fila daqui (AN-03).
|
|
62
|
+
export function formatSize(bytes) {
|
|
61
63
|
if (bytes < 1024)
|
|
62
64
|
return `${bytes} B`;
|
|
63
65
|
const units = ["KB", "MB", "GB"];
|
package/dist/media-client.d.ts
CHANGED
|
@@ -27,11 +27,14 @@ export interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElemen
|
|
|
27
27
|
render?: React.ReactElement;
|
|
28
28
|
}
|
|
29
29
|
export declare function Image({ ratio, fit, alt, className, style, render, onError, ...props }: ImageProps): React.JSX.Element;
|
|
30
|
+
export type GalleryItemKind = "image" | "video";
|
|
30
31
|
export interface GalleryItem {
|
|
31
32
|
id: string;
|
|
32
33
|
src: string;
|
|
33
34
|
alt: string;
|
|
34
35
|
caption?: ReactNode;
|
|
36
|
+
kind?: GalleryItemKind;
|
|
37
|
+
duration?: number;
|
|
35
38
|
}
|
|
36
39
|
export interface GalleryProps extends Omit<HTMLAttributes<HTMLUListElement>, "onSelect">, RefAttributes<HTMLUListElement> {
|
|
37
40
|
items: GalleryItem[];
|
|
@@ -40,8 +43,14 @@ export interface GalleryProps extends Omit<HTMLAttributes<HTMLUListElement>, "on
|
|
|
40
43
|
onSelect?: (id: string) => void;
|
|
41
44
|
zoom?: boolean;
|
|
42
45
|
ratio?: string;
|
|
46
|
+
selectionMode?: "single" | "multiple";
|
|
47
|
+
selectedIds?: string[];
|
|
48
|
+
onSelectionChange?: (ids: string[]) => void;
|
|
49
|
+
hasMore?: boolean;
|
|
50
|
+
loading?: boolean;
|
|
51
|
+
onReachEnd?: () => void;
|
|
43
52
|
}
|
|
44
|
-
export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, className, ...props }: GalleryProps): React.JSX.Element;
|
|
53
|
+
export declare function Gallery({ items, label, selected, onSelect, zoom, ratio, selectionMode, selectedIds, onSelectionChange, hasMore, loading, onReachEnd, className, ...props }: GalleryProps): React.JSX.Element;
|
|
45
54
|
export interface CarouselProps extends Omit<HTMLAttributes<HTMLDivElement>, "children">, RefAttributes<HTMLDivElement> {
|
|
46
55
|
children?: ReactNode;
|
|
47
56
|
label?: string;
|
package/dist/media-client.js
CHANGED
|
@@ -11,6 +11,8 @@ import { IconButton } from "./actions.js";
|
|
|
11
11
|
// A galeria AMPLIA num Dialog, e o Dialog já existe (trava do item L2). O import é para `overlays`,
|
|
12
12
|
// que como este módulo mora no "resto" do DAG e não importa `media` — não há ciclo.
|
|
13
13
|
import { Dialog } from "./overlays.js";
|
|
14
|
+
// AN-05: o tempo do vídeo e a linha de "carregando mais" reusam peças que já existem.
|
|
15
|
+
import { Badge, Spinner } from "./feedback.js";
|
|
14
16
|
// MediaPlayer (Fase 5): headless sobre <video>/<audio> nativos — o MOTOR de mídia é
|
|
15
17
|
// o browser; o componente só liga estado (play/tempo/buffer/volume/legenda) às
|
|
16
18
|
// classes .media-* já existentes. Sem CSS estrutural novo: o vídeo preenche o
|
|
@@ -143,23 +145,45 @@ export function Image({ ratio, fit, alt, className, style, render, onError, ...p
|
|
|
143
145
|
return _jsx("span", { className: cx("image", "image-broken", fit === "contain" && "image-contain", className), style: estilo, role: "img", "aria-label": alt, children: _jsx(Icon, { name: "image" }) });
|
|
144
146
|
return elemento;
|
|
145
147
|
}
|
|
146
|
-
export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1", className, ...props }) {
|
|
148
|
+
export function Gallery({ items, label, selected, onSelect, zoom, ratio = "1/1", selectionMode = "single", selectedIds, onSelectionChange, hasMore, loading, onReachEnd, className, ...props }) {
|
|
147
149
|
const s = useAureaStrings();
|
|
148
150
|
const [ampliado, setAmpliado] = React.useState(null);
|
|
149
|
-
const
|
|
151
|
+
const multipla = selectionMode === "multiple";
|
|
152
|
+
const interativo = !!onSelect || !!zoom || multipla;
|
|
150
153
|
const aberto = items.find(i => i.id === ampliado);
|
|
151
|
-
|
|
154
|
+
const escolhidos = new Set(selectedIds);
|
|
155
|
+
const alternar = (id) => onSelectionChange?.(escolhidos.has(id) ? (selectedIds ?? []).filter(x => x !== id) : [...(selectedIds ?? []), id]);
|
|
156
|
+
const fimRef = React.useRef(null);
|
|
157
|
+
const avisar = React.useRef(onReachEnd);
|
|
158
|
+
avisar.current = onReachEnd;
|
|
159
|
+
React.useEffect(() => {
|
|
160
|
+
const fim = fimRef.current;
|
|
161
|
+
if (!fim || !hasMore || loading || typeof IntersectionObserver === "undefined")
|
|
162
|
+
return;
|
|
163
|
+
const obs = new IntersectionObserver(entradas => { if (entradas.some(e => e.isIntersecting))
|
|
164
|
+
avisar.current?.(); });
|
|
165
|
+
obs.observe(fim);
|
|
166
|
+
return () => obs.disconnect();
|
|
167
|
+
}, [hasMore, loading, items.length]);
|
|
168
|
+
return _jsxs(_Fragment, { children: [_jsx("ul", { className: cx("gallery", className), "aria-label": label ?? s.galleryLabel, "aria-busy": loading || undefined, ...props, children: items.map(i => {
|
|
152
169
|
// LEGENDA VISÍVEL TORNA A MINIATURA DECORATIVA, e quem exigiu isso foi o axe, não a
|
|
153
170
|
// teoria: com `alt` e legenda dizendo a mesma coisa, ele reprova `image-redundant-alt` e o
|
|
154
171
|
// leitor de tela anuncia o texto DUAS vezes seguidas. É a regra de figura com legenda do
|
|
155
172
|
// WAI — quando o texto ao lado já diz, a imagem entra com `alt=""`. O `alt` de verdade não
|
|
156
173
|
// se perde: ele continua nomeando a foto AMPLIADA, que é onde não há legenda ao lado.
|
|
157
|
-
const
|
|
174
|
+
const foto = _jsx(Image, { src: i.src, alt: i.caption != null ? "" : i.alt, ratio: ratio });
|
|
175
|
+
const video = i.kind === "video";
|
|
176
|
+
const miolo = _jsxs(_Fragment, { children: [video
|
|
177
|
+
? _jsxs("span", { className: "gallery-media", children: [foto, _jsxs(Badge, { className: "gallery-duration", children: [_jsx(Icon, { name: "play", size: "sm" }), _jsx("span", { className: "sr-only", children: ` ${s.galleryVideo}, ` }), i.duration != null && clockTime(i.duration)] })] })
|
|
178
|
+
: foto, i.caption != null && _jsx("span", { className: "gallery-caption", children: i.caption })] });
|
|
179
|
+
const marcado = multipla ? escolhidos.has(i.id) : i.id === selected;
|
|
158
180
|
return _jsx("li", { className: "gallery-item", children: interativo
|
|
159
|
-
?
|
|
160
|
-
|
|
181
|
+
? (multipla
|
|
182
|
+
? _jsx("button", { type: "button", className: cx("gallery-tile", marcado && "is-selected"), "aria-pressed": marcado, onClick: () => alternar(i.id), children: miolo })
|
|
183
|
+
: _jsx("button", { type: "button", className: cx("gallery-tile", marcado && "is-selected"), "aria-current": marcado ? "true" : undefined, onClick: () => { onSelect?.(i.id); if (zoom)
|
|
184
|
+
setAmpliado(i.id); }, children: miolo }))
|
|
161
185
|
: miolo }, i.id);
|
|
162
|
-
}) }), zoom && _jsx(Dialog, { open: !!aberto, title: aberto ? (aberto.caption ?? aberto.alt) : "", onClose: () => setAmpliado(null), children: aberto && _jsx(Image, { src: aberto.src, alt: aberto.alt, fit: "contain" }) })] });
|
|
186
|
+
}) }), hasMore && _jsx("div", { ref: fimRef, className: "gallery-more", children: loading && _jsx(Spinner, {}) }), zoom && !multipla && _jsx(Dialog, { open: !!aberto, title: aberto ? (aberto.caption ?? aberto.alt) : "", onClose: () => setAmpliado(null), children: aberto && _jsx(Image, { src: aberto.src, alt: aberto.alt, fit: "contain" }) })] });
|
|
163
187
|
}
|
|
164
188
|
// ── Carousel (PLANO-1.0, item L1) ────────────────────────────────────────────────────────────
|
|
165
189
|
// MARCAÇÃO, NÃO MOTOR — e a pergunta que o item mandava decidir primeiro ("se `scroll-snap` do
|
|
@@ -56,11 +56,14 @@ export interface TreeNode {
|
|
|
56
56
|
label: ReactNode;
|
|
57
57
|
icon?: IconName;
|
|
58
58
|
children?: TreeNode[];
|
|
59
|
+
hasChildren?: boolean;
|
|
59
60
|
}
|
|
60
|
-
export declare function TreeView({ items, defaultExpandedIds, onSelect, label, className }: {
|
|
61
|
+
export declare function TreeView({ items, defaultExpandedIds, onSelect, onExpand, selectedId, label, className }: {
|
|
61
62
|
items: TreeNode[];
|
|
62
63
|
defaultExpandedIds?: string[];
|
|
63
64
|
onSelect?: (node: TreeNode) => void;
|
|
65
|
+
onExpand?: (node: TreeNode) => void | Promise<unknown>;
|
|
66
|
+
selectedId?: string | null;
|
|
64
67
|
label?: string;
|
|
65
68
|
className?: string;
|
|
66
69
|
}): ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
@@ -5,13 +5,14 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
5
5
|
// internal → system → actions → feedback → inputs → navigation → layout → data-display → resto.
|
|
6
6
|
import React, { useRef } from "react";
|
|
7
7
|
import { Tabs as BaseTabs } from "@base-ui/react/tabs";
|
|
8
|
+
import { useDirection } from "@base-ui/react/direction-provider";
|
|
8
9
|
import { Autocomplete as BaseAutocomplete } from "@base-ui/react/autocomplete";
|
|
9
10
|
import { useValorResponsivo } from "./responsivo-runtime.js";
|
|
10
11
|
import { cx, fundirRender, useAureaStrings, usePortalContainer } from "./internal.js";
|
|
11
12
|
import { Kbd } from "./markup.js";
|
|
12
13
|
import { Icon } from "./system.js";
|
|
13
14
|
import { Button } from "./actions.js";
|
|
14
|
-
import { Badge } from "./feedback.js";
|
|
15
|
+
import { Badge, Spinner } from "./feedback.js";
|
|
15
16
|
import { SearchField } from "./inputs.js";
|
|
16
17
|
// `overlays` PASSOU A VIR ANTES de `navigation` no DAG (18/08/2026, MAP.md §DAG). Ele só
|
|
17
18
|
// importa `internal`, `system` e `actions` — todos anteriores —, então não há ciclo: o que
|
|
@@ -90,6 +91,7 @@ function useSecaoEmVista(items, ligado, nav) {
|
|
|
90
91
|
}, [ids, ligado]);
|
|
91
92
|
return ligado ? atual : undefined;
|
|
92
93
|
}
|
|
94
|
+
const temFilhos = (node) => !!node.children?.length || !!node.hasChildren;
|
|
93
95
|
function flattenVisible(nodes, expanded, level = 1, parentId, acc = []) {
|
|
94
96
|
for (const node of nodes) {
|
|
95
97
|
acc.push({ node, level, parentId });
|
|
@@ -98,31 +100,46 @@ function flattenVisible(nodes, expanded, level = 1, parentId, acc = []) {
|
|
|
98
100
|
}
|
|
99
101
|
return acc;
|
|
100
102
|
}
|
|
101
|
-
export function TreeView({ items, defaultExpandedIds, onSelect, label, className }) {
|
|
103
|
+
export function TreeView({ items, defaultExpandedIds, onSelect, onExpand, selectedId, label, className }) {
|
|
102
104
|
const s = useAureaStrings();
|
|
103
105
|
const baseId = React.useId();
|
|
104
106
|
const [expanded, setExpanded] = React.useState(() => new Set(defaultExpandedIds));
|
|
105
|
-
const [
|
|
107
|
+
const [selectedInterno, setSelected] = React.useState();
|
|
108
|
+
const selected = selectedId !== undefined ? (selectedId ?? undefined) : selectedInterno;
|
|
109
|
+
const [carregando, setCarregando] = React.useState(() => new Set());
|
|
106
110
|
const [active, setActive] = React.useState(() => items[0]?.id);
|
|
107
111
|
const rootRef = React.useRef(null);
|
|
112
|
+
const rtl = useDirection() === "rtl";
|
|
108
113
|
const visible = flattenVisible(items, expanded);
|
|
109
114
|
// Roving tab stop derivado: se o nó ativo saiu do conjunto visível (dados
|
|
110
115
|
// trocados, nó removido), o primeiro visível volta a ser tabulável — senão a
|
|
111
116
|
// árvore inteira fica tabIndex=-1 e some da ordem do Tab (auditoria, MÉDIO 1).
|
|
112
117
|
const effectiveActive = active !== undefined && visible.some(v => v.node.id === active) ? active : visible[0]?.node.id;
|
|
113
118
|
const focusId = (id) => { setActive(id); rootRef.current?.querySelector(`[data-tree-id="${CSS.escape(id)}"]`)?.focus(); };
|
|
114
|
-
const
|
|
119
|
+
const marcar = (id, open) => setExpanded(prev => { const n = new Set(prev); if (open)
|
|
115
120
|
n.add(id);
|
|
116
121
|
else
|
|
117
122
|
n.delete(id); return n; });
|
|
118
|
-
const
|
|
123
|
+
const tirarDaCarga = (id) => setCarregando(prev => { const n = new Set(prev); n.delete(id); return n; });
|
|
124
|
+
const toggle = (node, open) => {
|
|
125
|
+
marcar(node.id, open);
|
|
126
|
+
if (!open || !onExpand)
|
|
127
|
+
return;
|
|
128
|
+
const r = onExpand(node);
|
|
129
|
+
if (node.children?.length || !r || typeof r.then !== "function")
|
|
130
|
+
return;
|
|
131
|
+
setCarregando(prev => new Set(prev).add(node.id));
|
|
132
|
+
r.then(() => tirarDaCarga(node.id), () => { tirarDaCarga(node.id); marcar(node.id, false); });
|
|
133
|
+
};
|
|
134
|
+
const select = (node) => { if (selectedId === undefined)
|
|
135
|
+
setSelected(node.id); onSelect?.(node); };
|
|
119
136
|
const onKeyDown = (e) => {
|
|
120
137
|
const idx = visible.findIndex(v => v.node.id === effectiveActive);
|
|
121
138
|
if (idx < 0)
|
|
122
139
|
return;
|
|
123
|
-
const cur = visible[idx], hasChildren =
|
|
124
|
-
const
|
|
125
|
-
const expandKey =
|
|
140
|
+
const cur = visible[idx], hasChildren = temFilhos(cur.node), isOpen = expanded.has(cur.node.id);
|
|
141
|
+
const rtlDom = getComputedStyle(e.currentTarget).direction === "rtl";
|
|
142
|
+
const expandKey = rtlDom ? "ArrowLeft" : "ArrowRight", collapseKey = rtlDom ? "ArrowRight" : "ArrowLeft";
|
|
126
143
|
switch (e.key) {
|
|
127
144
|
case "ArrowDown":
|
|
128
145
|
e.preventDefault();
|
|
@@ -137,14 +154,14 @@ export function TreeView({ items, defaultExpandedIds, onSelect, label, className
|
|
|
137
154
|
case expandKey:
|
|
138
155
|
e.preventDefault();
|
|
139
156
|
if (hasChildren && !isOpen)
|
|
140
|
-
toggle(cur.node
|
|
141
|
-
else if (
|
|
157
|
+
toggle(cur.node, true);
|
|
158
|
+
else if (isOpen && cur.node.children?.length)
|
|
142
159
|
focusId(cur.node.children[0].id);
|
|
143
160
|
break;
|
|
144
161
|
case collapseKey:
|
|
145
162
|
e.preventDefault();
|
|
146
163
|
if (hasChildren && isOpen)
|
|
147
|
-
toggle(cur.node
|
|
164
|
+
toggle(cur.node, false);
|
|
148
165
|
else if (cur.parentId)
|
|
149
166
|
focusId(cur.parentId);
|
|
150
167
|
break;
|
|
@@ -161,14 +178,14 @@ export function TreeView({ items, defaultExpandedIds, onSelect, label, className
|
|
|
161
178
|
e.preventDefault();
|
|
162
179
|
select(cur.node);
|
|
163
180
|
if (hasChildren)
|
|
164
|
-
toggle(cur.node
|
|
181
|
+
toggle(cur.node, !isOpen);
|
|
165
182
|
break;
|
|
166
183
|
}
|
|
167
184
|
};
|
|
168
185
|
const renderNodes = (nodes, level) => (_jsx("ul", { ref: level === 1 ? rootRef : undefined, className: cx(level === 1 ? "tree" : "tree-group", level === 1 && className), role: level === 1 ? "tree" : "group", "aria-label": level === 1 ? (label ?? s.treeLabel) : undefined, onKeyDown: level === 1 ? onKeyDown : undefined, children: nodes.map(node => {
|
|
169
|
-
const hasChildren =
|
|
170
|
-
return _jsxs("li", { className: "tree-item", role: "treeitem", "data-tree-id": node.id, "aria-level": level, "aria-expanded": hasChildren ? isOpen : undefined, "aria-selected": isSelected, "aria-labelledby": labelId, tabIndex: node.id === effectiveActive ? 0 : -1, children: [_jsxs("span", { className: "tree-node", "data-selected": isSelected || undefined, style: { paddingInlineStart: `calc(var(--space-3) + ${level - 1} * var(--space-4))` }, onClick: () => { focusId(node.id); select(node); if (hasChildren)
|
|
171
|
-
toggle(node
|
|
186
|
+
const hasChildren = temFilhos(node), isOpen = expanded.has(node.id), isSelected = selected === node.id, isLoading = isOpen && carregando.has(node.id), labelId = baseId + node.id;
|
|
187
|
+
return _jsxs("li", { className: "tree-item", role: "treeitem", "data-tree-id": node.id, "aria-level": level, "aria-expanded": hasChildren ? isOpen : undefined, "aria-selected": isSelected, "aria-busy": isLoading || undefined, "aria-labelledby": labelId, tabIndex: node.id === effectiveActive ? 0 : -1, children: [_jsxs("span", { className: "tree-node", "data-selected": isSelected || undefined, style: { paddingInlineStart: `calc(var(--space-3) + ${level - 1} * var(--space-4))` }, onClick: () => { focusId(node.id); select(node); if (hasChildren)
|
|
188
|
+
toggle(node, !isOpen); }, children: [isLoading ? _jsx(Spinner, { decorative: true, className: "tree-twist" }) : hasChildren ? _jsx(Icon, { name: "caret-right", size: "sm", className: cx("tree-twist", rtl && "tree-twist-rtl") }) : _jsx("span", { className: "tree-indent", "aria-hidden": "true" }), node.icon && _jsx(Icon, { name: node.icon, size: "sm" }), _jsx("span", { id: labelId, className: "tree-label", children: node.label })] }), isOpen && !!node.children?.length && renderNodes(node.children, level + 1)] }, node.id);
|
|
172
189
|
}) }));
|
|
173
190
|
return renderNodes(items, 1);
|
|
174
191
|
}
|
package/dist/pure.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export interface AureaStrings {
|
|
|
88
88
|
carouselPrev: string;
|
|
89
89
|
carouselNext: string;
|
|
90
90
|
galleryLabel: string;
|
|
91
|
+
galleryVideo: string;
|
|
91
92
|
sortableLabel: string;
|
|
92
93
|
sortableHandle: string;
|
|
93
94
|
sortableHelp: string;
|
|
@@ -166,6 +167,15 @@ export interface AureaStrings {
|
|
|
166
167
|
chatLabel: string;
|
|
167
168
|
chatMessage: string;
|
|
168
169
|
chatSend: string;
|
|
170
|
+
chatAttach: string;
|
|
171
|
+
chatAttachments: string;
|
|
172
|
+
chatReplyTo: string;
|
|
173
|
+
chatReplyCancel: string;
|
|
174
|
+
chatEditing: string;
|
|
175
|
+
chatEditCancel: string;
|
|
176
|
+
chatSave: string;
|
|
177
|
+
chatEdited: string;
|
|
178
|
+
chatForwardedFrom: string;
|
|
169
179
|
qrCode: string;
|
|
170
180
|
chartLabel: string;
|
|
171
181
|
copyCode: string;
|
package/dist/pure.js
CHANGED
|
@@ -53,10 +53,10 @@ export const universalStates = ["waiting_user", "waiting_approval", "waiting_dep
|
|
|
53
53
|
// perdeu qualidade é avisado. Um `role="alert"` aqui interromperia o leitor de tela por uma
|
|
54
54
|
// condição que não pede ação imediata, que é o oposto do que o papel serve.
|
|
55
55
|
export const stateSeverity = (state) => state.startsWith("waiting_") ? "info" : "warning";
|
|
56
|
-
export const defaultStrings = { passwordShow: "Show password", passwordHide: "Hide password", skipToContent: "Skip to content", close: "Close", confirmCancel: "Cancel", dataError: "This could not be loaded.", dataEmpty: "Nothing here yet", confirmProceed: "Continue", paginationLabel: "Pagination", previous: "Previous", next: "Next", breadcrumbLabel: "Breadcrumb", tabsLabel: "Tabs", optionsLabel: "Options", tableLabel: "Table", commandLabel: "Command palette", commandPlaceholder: "Type a command…", dismissNotification: "Dismiss notification", toolbarLabel: "Toolbar", loading: "Loading", otpDigit: "Digit", stepperLabel: "Steps", increment: "Increase", decrement: "Decrease", comboboxEmpty: "No results", comboboxClear: "Clear selection", comboboxOpen: "Open list", comboboxRemove: "Remove", comboboxLoading: "Loading…", fileDropPrompt: "Drag files here or click to select", fileAdded: "File added", fileRemoved: "File removed", fileRemove: "Remove", fileTooLarge: "File exceeds the size limit", fileWrongType: "File type not allowed", treeLabel: "Tree", notificationsLabel: "Notifications", notificationMarkAll: "Mark all as read", notificationEmpty: "No notifications", notificationUnread: "Unread", notificationNew: "new notifications", dataGridFilter: "Filter", dataGridEmpty: "No results", dataGridSelectAll: "Select all rows", dataGridSelectRow: "Select row", dataGridSelected: "selected", dataGridClearSelection: "Clear selection", dataGridBulkLabel: "Bulk actions", dataGridColumns: "Columns", dataGridResize: "Resize column", dataGridStale: "Showing data that may be out of date.", dataGridPartial: "Some rows could not be loaded. What you see is incomplete.", dataGridError: "The rows could not be loaded.", dataGridDetails: "Details", dataGridDetailPanel: "Row detail", dataGridExport: "Export", dataGridExportRows: "rows", dataGridExportFiltered: "filtered rows", dataGridExportSelected: "selected rows", mediaPlayer: "Media player", mediaPlay: "Play", mediaPause: "Pause", mediaMute: "Mute", mediaUnmute: "Unmute", mediaSeek: "Seek", mediaVolume: "Volume", mediaCaptionsShow: "Show captions", mediaCaptionsHide: "Hide captions", mediaSkipBack: "Skip back 10 seconds", mediaSkipForward: "Skip forward 10 seconds", mediaFullscreenEnter: "Enter full screen", mediaFullscreenExit: "Exit full screen", mediaEmbedOpen: "Open the video on its own site", carouselLabel: "Carousel", carouselSlide: "Slide", positionOf: "of", carouselPrev: "Previous slide", carouselNext: "Next slide", galleryLabel: "Gallery", sortableLabel: "Sortable list", sortableHandle: "Reorder", sortableHelp: "Press Space to pick this up, then Arrow Up and Arrow Down to move it. Space drops it, Escape puts it back.", sortableGrabbed: "Picked up", sortableDropped: "Dropped", sortableMoved: "Moved", sortableCanceled: "Put back", blockEditorLabel: "Content blocks", blockLabel: "Block", blockRemove: "Remove block", uploadSending: "Uploading", uploadCancel: "Cancel upload", uploadRetry: "Retry upload", uploadError: "Upload failed", uploadCanceled: "Upload canceled", uploadComplete: "Upload complete", uploadPause: "Pause upload", uploadResume: "Resume upload", uploadPaused: "Paused", uploadPending: "Waiting", uploadChecksum: "Checksum", uploadChecksumBad: "The server received different bytes than the ones sent.", fileConflict: "A file with this name is already in the queue", fileReplace: "Replace", fileKeepBoth: "Keep both", fileSkip: "Skip", uploadReceipt: "Receipt", uploadReceiptCopy: "Copy receipt", agentInspector: "Agent inspector", invocationLabel: "Invocation", invocationInput: "Input", invocationOutput: "Output", taskQueueLabel: "Task queue", taskState: { queued: "Queued", running: "Running", completed: "Completed", failed: "Failed", blocked: "Blocked", paused: "Paused" }, taskPriority: { low: "Low", medium: "Medium", high: "High" }, approvalLabel: "Approval", approvalApprove: "Approve", approvalDeny: "Deny", approvalApproved: "Approved", approvalDenied: "Denied", approvalDeadline: "Decide by", approvalRisk: { low: "Low risk", medium: "Medium risk", high: "High risk" }, permissionLabel: "Tool permissions", permissionAsk: "Ask", permissionAlways: "Always", permissionNever: "Never", eventStreamLabel: "Events", traceLabel: "Trace", healthLabel: "Health", healthState: { operational: "Operational", degraded: "Degraded", down: "Down", maintenance: "Maintenance", unknown: "Unknown" }, modelUsageLabel: "Model usage", usageMetric: { tokens: "Tokens", cost: "Cost", requests: "Requests" }, costMeterLabel: "Cost", costMeterSpent: "Spent", costMeterRemaining: "left", costMeterExceeded: "Over the limit", costMeterNear: "Spending is past the soft limit.", costMeterOver: "The limit has been reached. Further calls are blocked.", memoryLabel: "Memory ledger", memoryProvenance: "Where this came from", memoryOperation: { added: "Added", updated: "Updated", recalled: "Recalled", forgotten: "Forgotten" }, memoryScope: { episodic: "Episodic", semantic: "Semantic", procedural: "Procedural" }, agentMessageLabel: "Agent messages", agentMessageTo: "to", agentMessageBroadcast: "all agents", agentMessageReason: "Why it went this way", agentMessageKind: { request: "Request", response: "Response", handoff: "Handoff", broadcast: "Broadcast", error: "Error" }, automationWhen: "When", automationThen: "Then", automationEnabled: "Enabled", automationLastRun: "Last run", automationResult: { success: "Succeeded", failure: "Failed" }, graphLabel: "Dependency graph", agentState: { idle: "Idle", thinking: "Thinking", running: "Running", paused: "Paused", error: "Error", completed: "Completed" }, codeEditor: "Code editor", chatLabel: "Conversation", chatMessage: "Message", chatSend: "Send", qrCode: "QR code", chartLabel: "Chart", copyCode: "Copy code", tocLabel: "On this page", navigationToggle: "Navigation", sidebarLabel: "Sidebar", sidebarCollapse: "Collapse sidebar", sidebarExpand: "Expand sidebar", bottomNavLabel: "Main", themeToDark: "Switch to dark theme", themeToLight: "Switch to light theme", universalState: { waiting_user: "Waiting for someone to act.", waiting_approval: "Waiting for approval.", waiting_dependency: "Waiting for something else to finish.", offline: "No connection. This was loaded earlier.", stale: "This may be out of date.", partial: "Some of this could not be loaded.", degraded: "Working with reduced capability." } };
|
|
56
|
+
export const defaultStrings = { passwordShow: "Show password", passwordHide: "Hide password", skipToContent: "Skip to content", close: "Close", confirmCancel: "Cancel", dataError: "This could not be loaded.", dataEmpty: "Nothing here yet", confirmProceed: "Continue", paginationLabel: "Pagination", previous: "Previous", next: "Next", breadcrumbLabel: "Breadcrumb", tabsLabel: "Tabs", optionsLabel: "Options", tableLabel: "Table", commandLabel: "Command palette", commandPlaceholder: "Type a command…", dismissNotification: "Dismiss notification", toolbarLabel: "Toolbar", loading: "Loading", otpDigit: "Digit", stepperLabel: "Steps", increment: "Increase", decrement: "Decrease", comboboxEmpty: "No results", comboboxClear: "Clear selection", comboboxOpen: "Open list", comboboxRemove: "Remove", comboboxLoading: "Loading…", fileDropPrompt: "Drag files here or click to select", fileAdded: "File added", fileRemoved: "File removed", fileRemove: "Remove", fileTooLarge: "File exceeds the size limit", fileWrongType: "File type not allowed", treeLabel: "Tree", notificationsLabel: "Notifications", notificationMarkAll: "Mark all as read", notificationEmpty: "No notifications", notificationUnread: "Unread", notificationNew: "new notifications", dataGridFilter: "Filter", dataGridEmpty: "No results", dataGridSelectAll: "Select all rows", dataGridSelectRow: "Select row", dataGridSelected: "selected", dataGridClearSelection: "Clear selection", dataGridBulkLabel: "Bulk actions", dataGridColumns: "Columns", dataGridResize: "Resize column", dataGridStale: "Showing data that may be out of date.", dataGridPartial: "Some rows could not be loaded. What you see is incomplete.", dataGridError: "The rows could not be loaded.", dataGridDetails: "Details", dataGridDetailPanel: "Row detail", dataGridExport: "Export", dataGridExportRows: "rows", dataGridExportFiltered: "filtered rows", dataGridExportSelected: "selected rows", mediaPlayer: "Media player", mediaPlay: "Play", mediaPause: "Pause", mediaMute: "Mute", mediaUnmute: "Unmute", mediaSeek: "Seek", mediaVolume: "Volume", mediaCaptionsShow: "Show captions", mediaCaptionsHide: "Hide captions", mediaSkipBack: "Skip back 10 seconds", mediaSkipForward: "Skip forward 10 seconds", mediaFullscreenEnter: "Enter full screen", mediaFullscreenExit: "Exit full screen", mediaEmbedOpen: "Open the video on its own site", carouselLabel: "Carousel", carouselSlide: "Slide", positionOf: "of", carouselPrev: "Previous slide", carouselNext: "Next slide", galleryLabel: "Gallery", galleryVideo: "Video", sortableLabel: "Sortable list", sortableHandle: "Reorder", sortableHelp: "Press Space to pick this up, then Arrow Up and Arrow Down to move it. Space drops it, Escape puts it back.", sortableGrabbed: "Picked up", sortableDropped: "Dropped", sortableMoved: "Moved", sortableCanceled: "Put back", blockEditorLabel: "Content blocks", blockLabel: "Block", blockRemove: "Remove block", uploadSending: "Uploading", uploadCancel: "Cancel upload", uploadRetry: "Retry upload", uploadError: "Upload failed", uploadCanceled: "Upload canceled", uploadComplete: "Upload complete", uploadPause: "Pause upload", uploadResume: "Resume upload", uploadPaused: "Paused", uploadPending: "Waiting", uploadChecksum: "Checksum", uploadChecksumBad: "The server received different bytes than the ones sent.", fileConflict: "A file with this name is already in the queue", fileReplace: "Replace", fileKeepBoth: "Keep both", fileSkip: "Skip", uploadReceipt: "Receipt", uploadReceiptCopy: "Copy receipt", agentInspector: "Agent inspector", invocationLabel: "Invocation", invocationInput: "Input", invocationOutput: "Output", taskQueueLabel: "Task queue", taskState: { queued: "Queued", running: "Running", completed: "Completed", failed: "Failed", blocked: "Blocked", paused: "Paused" }, taskPriority: { low: "Low", medium: "Medium", high: "High" }, approvalLabel: "Approval", approvalApprove: "Approve", approvalDeny: "Deny", approvalApproved: "Approved", approvalDenied: "Denied", approvalDeadline: "Decide by", approvalRisk: { low: "Low risk", medium: "Medium risk", high: "High risk" }, permissionLabel: "Tool permissions", permissionAsk: "Ask", permissionAlways: "Always", permissionNever: "Never", eventStreamLabel: "Events", traceLabel: "Trace", healthLabel: "Health", healthState: { operational: "Operational", degraded: "Degraded", down: "Down", maintenance: "Maintenance", unknown: "Unknown" }, modelUsageLabel: "Model usage", usageMetric: { tokens: "Tokens", cost: "Cost", requests: "Requests" }, costMeterLabel: "Cost", costMeterSpent: "Spent", costMeterRemaining: "left", costMeterExceeded: "Over the limit", costMeterNear: "Spending is past the soft limit.", costMeterOver: "The limit has been reached. Further calls are blocked.", memoryLabel: "Memory ledger", memoryProvenance: "Where this came from", memoryOperation: { added: "Added", updated: "Updated", recalled: "Recalled", forgotten: "Forgotten" }, memoryScope: { episodic: "Episodic", semantic: "Semantic", procedural: "Procedural" }, agentMessageLabel: "Agent messages", agentMessageTo: "to", agentMessageBroadcast: "all agents", agentMessageReason: "Why it went this way", agentMessageKind: { request: "Request", response: "Response", handoff: "Handoff", broadcast: "Broadcast", error: "Error" }, automationWhen: "When", automationThen: "Then", automationEnabled: "Enabled", automationLastRun: "Last run", automationResult: { success: "Succeeded", failure: "Failed" }, graphLabel: "Dependency graph", agentState: { idle: "Idle", thinking: "Thinking", running: "Running", paused: "Paused", error: "Error", completed: "Completed" }, codeEditor: "Code editor", chatLabel: "Conversation", chatMessage: "Message", chatSend: "Send", chatAttach: "Attach file", chatAttachments: "Attachments", chatReplyTo: "Replying to", chatReplyCancel: "Cancel reply", chatEditing: "Editing message", chatEditCancel: "Cancel editing", chatSave: "Save", chatEdited: "edited", chatForwardedFrom: "Forwarded from", qrCode: "QR code", chartLabel: "Chart", copyCode: "Copy code", tocLabel: "On this page", navigationToggle: "Navigation", sidebarLabel: "Sidebar", sidebarCollapse: "Collapse sidebar", sidebarExpand: "Expand sidebar", bottomNavLabel: "Main", themeToDark: "Switch to dark theme", themeToLight: "Switch to light theme", universalState: { waiting_user: "Waiting for someone to act.", waiting_approval: "Waiting for approval.", waiting_dependency: "Waiting for something else to finish.", offline: "No connection. This was loaded earlier.", stale: "This may be out of date.", partial: "Some of this could not be loaded.", degraded: "Working with reduced capability." } };
|
|
57
57
|
// pt-BR preservado como locale (mesmos valores que já eram o default): passe
|
|
58
58
|
// <AureaProvider strings={ptBR}> para restaurar português.
|
|
59
|
-
export const ptBR = { passwordShow: "Mostrar senha", passwordHide: "Ocultar senha", skipToContent: "Pular para o conteúdo", close: "Fechar", confirmCancel: "Cancelar", dataError: "Não foi possível carregar.", dataEmpty: "Ainda não há nada aqui", confirmProceed: "Continuar", paginationLabel: "Paginação", previous: "Anterior", next: "Próxima", breadcrumbLabel: "Breadcrumb", tabsLabel: "Abas", optionsLabel: "Opções", tableLabel: "Tabela", commandLabel: "Paleta de comandos", commandPlaceholder: "Digite um comando…", dismissNotification: "Dispensar notificação", toolbarLabel: "Barra de ferramentas", loading: "Carregando", otpDigit: "Dígito", stepperLabel: "Etapas", increment: "Aumentar", decrement: "Diminuir", comboboxEmpty: "Nenhum resultado", comboboxClear: "Limpar seleção", comboboxOpen: "Abrir lista", comboboxRemove: "Remover", comboboxLoading: "Carregando…", fileDropPrompt: "Arraste arquivos aqui ou clique para selecionar", fileAdded: "Arquivo adicionado", fileRemoved: "Arquivo removido", fileRemove: "Remover", fileTooLarge: "Arquivo maior que o limite", fileWrongType: "Tipo de arquivo não aceito", treeLabel: "Árvore", notificationsLabel: "Notificações", notificationMarkAll: "Marcar todas como lidas", notificationEmpty: "Nenhuma notificação", notificationUnread: "Não lida", notificationNew: "novas notificações", dataGridFilter: "Filtrar", dataGridEmpty: "Nenhum resultado", dataGridSelectAll: "Selecionar todas as linhas", dataGridSelectRow: "Selecionar linha", dataGridSelected: "selecionadas", dataGridClearSelection: "Limpar seleção", dataGridBulkLabel: "Ações em lote", dataGridColumns: "Colunas", dataGridResize: "Redimensionar coluna", dataGridStale: "Mostrando dados que podem estar desatualizados.", dataGridPartial: "Algumas linhas não puderam ser carregadas. O que aparece está incompleto.", dataGridError: "Não foi possível carregar as linhas.", dataGridDetails: "Detalhes", dataGridDetailPanel: "Detalhe da linha", dataGridExport: "Exportar", dataGridExportRows: "linhas", dataGridExportFiltered: "linhas filtradas", dataGridExportSelected: "linhas selecionadas", mediaPlayer: "Reprodutor de mídia", mediaPlay: "Reproduzir", mediaPause: "Pausar", mediaMute: "Silenciar", mediaUnmute: "Ativar som", mediaSeek: "Posição da reprodução", mediaVolume: "Volume", mediaCaptionsShow: "Ativar legendas", mediaCaptionsHide: "Desativar legendas", mediaSkipBack: "Voltar 10 segundos", mediaSkipForward: "Avançar 10 segundos", mediaFullscreenEnter: "Tela cheia", mediaFullscreenExit: "Sair da tela cheia", mediaEmbedOpen: "Abrir o vídeo no site de origem", carouselLabel: "Carrossel", carouselSlide: "Slide", positionOf: "de", carouselPrev: "Slide anterior", carouselNext: "Próximo slide", galleryLabel: "Galeria", sortableLabel: "Lista ordenável", sortableHandle: "Reordenar", sortableHelp: "Aperte Espaço para pegar, e as setas para cima e para baixo para mover. Espaço solta, Esc devolve ao lugar.", sortableGrabbed: "Pego", sortableDropped: "Solto", sortableMoved: "Movido", sortableCanceled: "Devolvido", blockEditorLabel: "Blocos de conteúdo", blockLabel: "Bloco", blockRemove: "Remover bloco", uploadSending: "Enviando", uploadCancel: "Cancelar envio", uploadRetry: "Tentar envio novamente", uploadError: "Falha no envio", uploadCanceled: "Envio cancelado", uploadComplete: "Envio concluído", uploadPause: "Pausar envio", uploadResume: "Retomar envio", uploadPaused: "Pausado", uploadPending: "Aguardando", uploadChecksum: "Soma de verificação", uploadChecksumBad: "O servidor recebeu bytes diferentes dos enviados.", fileConflict: "Já existe um arquivo com este nome na fila", fileReplace: "Substituir", fileKeepBoth: "Manter os dois", fileSkip: "Pular", uploadReceipt: "Recibo", uploadReceiptCopy: "Copiar recibo", agentInspector: "Inspetor do agente", invocationLabel: "Invocação", invocationInput: "Entrada", invocationOutput: "Saída", taskQueueLabel: "Fila de tarefas", taskState: { queued: "Na fila", running: "Executando", completed: "Concluída", failed: "Falhou", blocked: "Bloqueada", paused: "Pausada" }, taskPriority: { low: "Baixa", medium: "Média", high: "Alta" }, approvalLabel: "Aprovação", approvalApprove: "Aprovar", approvalDeny: "Negar", approvalApproved: "Aprovado", approvalDenied: "Negado", approvalDeadline: "Decidir até", approvalRisk: { low: "Risco baixo", medium: "Risco médio", high: "Risco alto" }, permissionLabel: "Permissões de ferramenta", permissionAsk: "Perguntar", permissionAlways: "Sempre", permissionNever: "Nunca", eventStreamLabel: "Eventos", traceLabel: "Rastro", healthLabel: "Saúde", healthState: { operational: "Operando", degraded: "Degradado", down: "Fora do ar", maintenance: "Em manutenção", unknown: "Desconhecido" }, modelUsageLabel: "Consumo por modelo", usageMetric: { tokens: "Tokens", cost: "Custo", requests: "Chamadas" }, costMeterLabel: "Custo", costMeterSpent: "Gasto", costMeterRemaining: "restantes", costMeterExceeded: "Acima do limite", costMeterNear: "O gasto passou do limite brando.", costMeterOver: "O limite foi atingido. As próximas chamadas estão bloqueadas.", memoryLabel: "Livro-razão de memória", memoryProvenance: "De onde isto veio", memoryOperation: { added: "Guardada", updated: "Atualizada", recalled: "Lembrada", forgotten: "Esquecida" }, memoryScope: { episodic: "Episódica", semantic: "Semântica", procedural: "Procedural" }, agentMessageLabel: "Recados entre agentes", agentMessageTo: "para", agentMessageBroadcast: "todos os agentes", agentMessageReason: "Por que foi por aqui", agentMessageKind: { request: "Pedido", response: "Resposta", handoff: "Passagem", broadcast: "Difusão", error: "Erro" }, automationWhen: "Quando", automationThen: "Então", automationEnabled: "Ativa", automationLastRun: "Última execução", automationResult: { success: "Concluiu", failure: "Falhou" }, graphLabel: "Grafo de dependências", agentState: { idle: "Ocioso", thinking: "Pensando", running: "Executando", paused: "Pausado", error: "Erro", completed: "Concluído" }, codeEditor: "Editor de código", chatLabel: "Conversa", chatMessage: "Mensagem", chatSend: "Enviar", qrCode: "Código QR", chartLabel: "Gráfico", copyCode: "Copiar código", tocLabel: "Nesta página", navigationToggle: "Navegação", sidebarLabel: "Lateral", sidebarCollapse: "Recolher o menu", sidebarExpand: "Abrir o menu", bottomNavLabel: "Principal", themeToDark: "Mudar para o tema escuro", themeToLight: "Mudar para o tema claro", universalState: { waiting_user: "Esperando alguém agir.", waiting_approval: "Esperando aprovação.", waiting_dependency: "Esperando outra coisa terminar.", offline: "Sem conexão. Isto foi carregado antes.", stale: "Isto pode estar desatualizado.", partial: "Parte disto não pôde ser carregada.", degraded: "Funcionando com capacidade reduzida." } };
|
|
59
|
+
export const ptBR = { passwordShow: "Mostrar senha", passwordHide: "Ocultar senha", skipToContent: "Pular para o conteúdo", close: "Fechar", confirmCancel: "Cancelar", dataError: "Não foi possível carregar.", dataEmpty: "Ainda não há nada aqui", confirmProceed: "Continuar", paginationLabel: "Paginação", previous: "Anterior", next: "Próxima", breadcrumbLabel: "Breadcrumb", tabsLabel: "Abas", optionsLabel: "Opções", tableLabel: "Tabela", commandLabel: "Paleta de comandos", commandPlaceholder: "Digite um comando…", dismissNotification: "Dispensar notificação", toolbarLabel: "Barra de ferramentas", loading: "Carregando", otpDigit: "Dígito", stepperLabel: "Etapas", increment: "Aumentar", decrement: "Diminuir", comboboxEmpty: "Nenhum resultado", comboboxClear: "Limpar seleção", comboboxOpen: "Abrir lista", comboboxRemove: "Remover", comboboxLoading: "Carregando…", fileDropPrompt: "Arraste arquivos aqui ou clique para selecionar", fileAdded: "Arquivo adicionado", fileRemoved: "Arquivo removido", fileRemove: "Remover", fileTooLarge: "Arquivo maior que o limite", fileWrongType: "Tipo de arquivo não aceito", treeLabel: "Árvore", notificationsLabel: "Notificações", notificationMarkAll: "Marcar todas como lidas", notificationEmpty: "Nenhuma notificação", notificationUnread: "Não lida", notificationNew: "novas notificações", dataGridFilter: "Filtrar", dataGridEmpty: "Nenhum resultado", dataGridSelectAll: "Selecionar todas as linhas", dataGridSelectRow: "Selecionar linha", dataGridSelected: "selecionadas", dataGridClearSelection: "Limpar seleção", dataGridBulkLabel: "Ações em lote", dataGridColumns: "Colunas", dataGridResize: "Redimensionar coluna", dataGridStale: "Mostrando dados que podem estar desatualizados.", dataGridPartial: "Algumas linhas não puderam ser carregadas. O que aparece está incompleto.", dataGridError: "Não foi possível carregar as linhas.", dataGridDetails: "Detalhes", dataGridDetailPanel: "Detalhe da linha", dataGridExport: "Exportar", dataGridExportRows: "linhas", dataGridExportFiltered: "linhas filtradas", dataGridExportSelected: "linhas selecionadas", mediaPlayer: "Reprodutor de mídia", mediaPlay: "Reproduzir", mediaPause: "Pausar", mediaMute: "Silenciar", mediaUnmute: "Ativar som", mediaSeek: "Posição da reprodução", mediaVolume: "Volume", mediaCaptionsShow: "Ativar legendas", mediaCaptionsHide: "Desativar legendas", mediaSkipBack: "Voltar 10 segundos", mediaSkipForward: "Avançar 10 segundos", mediaFullscreenEnter: "Tela cheia", mediaFullscreenExit: "Sair da tela cheia", mediaEmbedOpen: "Abrir o vídeo no site de origem", carouselLabel: "Carrossel", carouselSlide: "Slide", positionOf: "de", carouselPrev: "Slide anterior", carouselNext: "Próximo slide", galleryLabel: "Galeria", galleryVideo: "Vídeo", sortableLabel: "Lista ordenável", sortableHandle: "Reordenar", sortableHelp: "Aperte Espaço para pegar, e as setas para cima e para baixo para mover. Espaço solta, Esc devolve ao lugar.", sortableGrabbed: "Pego", sortableDropped: "Solto", sortableMoved: "Movido", sortableCanceled: "Devolvido", blockEditorLabel: "Blocos de conteúdo", blockLabel: "Bloco", blockRemove: "Remover bloco", uploadSending: "Enviando", uploadCancel: "Cancelar envio", uploadRetry: "Tentar envio novamente", uploadError: "Falha no envio", uploadCanceled: "Envio cancelado", uploadComplete: "Envio concluído", uploadPause: "Pausar envio", uploadResume: "Retomar envio", uploadPaused: "Pausado", uploadPending: "Aguardando", uploadChecksum: "Soma de verificação", uploadChecksumBad: "O servidor recebeu bytes diferentes dos enviados.", fileConflict: "Já existe um arquivo com este nome na fila", fileReplace: "Substituir", fileKeepBoth: "Manter os dois", fileSkip: "Pular", uploadReceipt: "Recibo", uploadReceiptCopy: "Copiar recibo", agentInspector: "Inspetor do agente", invocationLabel: "Invocação", invocationInput: "Entrada", invocationOutput: "Saída", taskQueueLabel: "Fila de tarefas", taskState: { queued: "Na fila", running: "Executando", completed: "Concluída", failed: "Falhou", blocked: "Bloqueada", paused: "Pausada" }, taskPriority: { low: "Baixa", medium: "Média", high: "Alta" }, approvalLabel: "Aprovação", approvalApprove: "Aprovar", approvalDeny: "Negar", approvalApproved: "Aprovado", approvalDenied: "Negado", approvalDeadline: "Decidir até", approvalRisk: { low: "Risco baixo", medium: "Risco médio", high: "Risco alto" }, permissionLabel: "Permissões de ferramenta", permissionAsk: "Perguntar", permissionAlways: "Sempre", permissionNever: "Nunca", eventStreamLabel: "Eventos", traceLabel: "Rastro", healthLabel: "Saúde", healthState: { operational: "Operando", degraded: "Degradado", down: "Fora do ar", maintenance: "Em manutenção", unknown: "Desconhecido" }, modelUsageLabel: "Consumo por modelo", usageMetric: { tokens: "Tokens", cost: "Custo", requests: "Chamadas" }, costMeterLabel: "Custo", costMeterSpent: "Gasto", costMeterRemaining: "restantes", costMeterExceeded: "Acima do limite", costMeterNear: "O gasto passou do limite brando.", costMeterOver: "O limite foi atingido. As próximas chamadas estão bloqueadas.", memoryLabel: "Livro-razão de memória", memoryProvenance: "De onde isto veio", memoryOperation: { added: "Guardada", updated: "Atualizada", recalled: "Lembrada", forgotten: "Esquecida" }, memoryScope: { episodic: "Episódica", semantic: "Semântica", procedural: "Procedural" }, agentMessageLabel: "Recados entre agentes", agentMessageTo: "para", agentMessageBroadcast: "todos os agentes", agentMessageReason: "Por que foi por aqui", agentMessageKind: { request: "Pedido", response: "Resposta", handoff: "Passagem", broadcast: "Difusão", error: "Erro" }, automationWhen: "Quando", automationThen: "Então", automationEnabled: "Ativa", automationLastRun: "Última execução", automationResult: { success: "Concluiu", failure: "Falhou" }, graphLabel: "Grafo de dependências", agentState: { idle: "Ocioso", thinking: "Pensando", running: "Executando", paused: "Pausado", error: "Erro", completed: "Concluído" }, codeEditor: "Editor de código", chatLabel: "Conversa", chatMessage: "Mensagem", chatSend: "Enviar", chatAttach: "Anexar arquivo", chatAttachments: "Anexos", chatReplyTo: "Respondendo a", chatReplyCancel: "Cancelar resposta", chatEditing: "Editando mensagem", chatEditCancel: "Cancelar edição", chatSave: "Salvar", chatEdited: "editada", chatForwardedFrom: "Encaminhada de", qrCode: "Código QR", chartLabel: "Gráfico", copyCode: "Copiar código", tocLabel: "Nesta página", navigationToggle: "Navegação", sidebarLabel: "Lateral", sidebarCollapse: "Recolher o menu", sidebarExpand: "Abrir o menu", bottomNavLabel: "Principal", themeToDark: "Mudar para o tema escuro", themeToLight: "Mudar para o tema claro", universalState: { waiting_user: "Esperando alguém agir.", waiting_approval: "Esperando aprovação.", waiting_dependency: "Esperando outra coisa terminar.", offline: "Sem conexão. Isto foi carregado antes.", stale: "Isto pode estar desatualizado.", partial: "Parte disto não pôde ser carregada.", degraded: "Funcionando com capacidade reduzida." } };
|
|
60
60
|
// Sprite de ícones: CONFIGURAÇÃO DE APLICAÇÃO, então entra pelo provider, uma vez.
|
|
61
61
|
// Antes o default absoluto "/aurea-icons.svg" vivia no Icon e 15 componentes declaravam
|
|
62
62
|
// e repassavam `spriteUrl` à mão — 52 sítios de código. Consequência: aplicação servida
|