@aurea-uds/react 0.37.1 → 0.39.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/graph.d.ts +10 -1
- package/dist/graph.js +17 -5
- package/package.json +1 -1
package/dist/graph.d.ts
CHANGED
|
@@ -154,6 +154,15 @@ export interface DependencyGraphProps extends Omit<React.HTMLAttributes<HTMLDivE
|
|
|
154
154
|
controls?: boolean;
|
|
155
155
|
/** A legenda das espécies de linha, montada sozinha pelo `legend` de cada aresta (MNT-15). */
|
|
156
156
|
legend?: boolean;
|
|
157
|
+
/** A legenda ganha o botão "Legenda", parado no canto, que a abre e fecha; aberta, a lista sobe acima
|
|
158
|
+
* dele; fechada, sobra só o botão (MNT-31). */
|
|
159
|
+
legendCollapsible?: boolean;
|
|
160
|
+
/** A legenda que fecha começa aberta. Ligado por padrão. */
|
|
161
|
+
defaultLegendOpen?: boolean;
|
|
162
|
+
/** O botão que esconde e mostra o minimapa, junto dos botões de zoom, em cima (MNT-31). */
|
|
163
|
+
minimapToggle?: boolean;
|
|
164
|
+
/** O minimapa com botão começa aberto. Ligado por padrão. */
|
|
165
|
+
defaultMinimapOpen?: boolean;
|
|
157
166
|
/** Centraliza e marca este nó — o "ir até" de uma busca (MNT-18.2). */
|
|
158
167
|
focusId?: string | null;
|
|
159
168
|
/** Destaca um caminho (nós e arestas) e apaga o resto (MNT-18.3). */
|
|
@@ -198,4 +207,4 @@ export interface DependencyGraphProps extends Omit<React.HTMLAttributes<HTMLDivE
|
|
|
198
207
|
/** O trabalhador que arruma FORA da tela principal (MNT-19.1). A receita de cada empacotador está no README. */
|
|
199
208
|
layoutWorker?: () => Worker;
|
|
200
209
|
}
|
|
201
|
-
export declare function DependencyGraph({ nodes, edges, label, selectedId, onSelect, connectable, onConnect, onNodeMove, height, className, orientation, layout, rootId, edgeShape, groupParallel, minimap, controls, legend, focusId, highlight, highlightNeighbors, hiddenIds, onNodeHover, onNodeContextMenu, onEdgeSelect, textSize, highContrast, apiRef, editable, grid, onLayoutChange, onSelectionChange, collapsible, onCollapseChange, visibleOnly, layoutWorker, style, ...props }: DependencyGraphProps): React.JSX.Element;
|
|
210
|
+
export declare function DependencyGraph({ nodes, edges, label, selectedId, onSelect, connectable, onConnect, onNodeMove, height, className, orientation, layout, rootId, edgeShape, groupParallel, minimap, controls, legend, legendCollapsible, defaultLegendOpen, minimapToggle, defaultMinimapOpen, focusId, highlight, highlightNeighbors, hiddenIds, onNodeHover, onNodeContextMenu, onEdgeSelect, textSize, highContrast, apiRef, editable, grid, onLayoutChange, onSelectionChange, collapsible, onCollapseChange, visibleOnly, layoutWorker, style, ...props }: DependencyGraphProps): React.JSX.Element;
|
package/dist/graph.js
CHANGED
|
@@ -43,7 +43,7 @@ import { ReactFlow, ReactFlowProvider, Background, BaseEdge, EdgeLabelRenderer,
|
|
|
43
43
|
import { cx, useAureaStrings } from "./internal.js";
|
|
44
44
|
import { Icon } from "./system.js";
|
|
45
45
|
import { Badge } from "./markup.js";
|
|
46
|
-
import { IconButton, Toolbar, ToolbarButton, ToolbarSeparator } from "./actions.js";
|
|
46
|
+
import { Button, IconButton, Toolbar, ToolbarButton, ToolbarSeparator } from "./actions.js";
|
|
47
47
|
import { alinhar, distribuir, subarvores, recolher, remapear, simplificar, pontes, tracado, meioDoTracado, ladoDoTrecho, rotaCruza, bordaNaDirecao, rotulosQueSomem, contornoDeNuvem, naGrade, rotaNaGrade, guiasDoArraste } from "./graph-geometria.js";
|
|
48
48
|
// ── O layout ─────────────────────────────────────────────────────────────────────────────
|
|
49
49
|
// O motor recebe `x`/`y` PRONTOS: ele não posiciona nada. Sem isto, um grafo sem coordenadas
|
|
@@ -590,7 +590,7 @@ const ESPERA_DO_ARRUMADOR = 30;
|
|
|
590
590
|
// inverso), e não foto do mapa inteiro: o mapa é controlado pelo app, e cada passo — para frente ou
|
|
591
591
|
// para trás — vira um `onLayoutChange` com as posições, que o app guarda (decisão do Victor, 10/10/2026).
|
|
592
592
|
const LIMITE_DO_DESFAZER = 100;
|
|
593
|
-
export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, connectable, onConnect, onNodeMove, height, className, orientation = "horizontal", layout = "simple", rootId, edgeShape = "curve", groupParallel, minimap, controls, legend, focusId, highlight, highlightNeighbors, hiddenIds, onNodeHover, onNodeContextMenu, onEdgeSelect, textSize = "md", highContrast, apiRef, editable, grid = true, onLayoutChange, onSelectionChange, collapsible, onCollapseChange, visibleOnly, layoutWorker, style, ...props }) {
|
|
593
|
+
export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, connectable, onConnect, onNodeMove, height, className, orientation = "horizontal", layout = "simple", rootId, edgeShape = "curve", groupParallel, minimap, controls, legend, legendCollapsible, defaultLegendOpen = true, minimapToggle, defaultMinimapOpen = true, focusId, highlight, highlightNeighbors, hiddenIds, onNodeHover, onNodeContextMenu, onEdgeSelect, textSize = "md", highContrast, apiRef, editable, grid = true, onLayoutChange, onSelectionChange, collapsible, onCollapseChange, visibleOnly, layoutWorker, style, ...props }) {
|
|
594
594
|
const s = useAureaStrings();
|
|
595
595
|
const m = React.useMemo(() => medidasDoNo(textSize), [textSize]);
|
|
596
596
|
const vertical = orientation === "vertical";
|
|
@@ -1243,6 +1243,15 @@ export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, con
|
|
|
1243
1243
|
return { tipos, estados };
|
|
1244
1244
|
}, [edges]);
|
|
1245
1245
|
const idDaLegenda = React.useId();
|
|
1246
|
+
// MNT-31: a legenda e o minimapa cobriam os nós dos cantos de baixo. A legenda ganha o botão de
|
|
1247
|
+
// esconder no próprio canto — o botão fica parado e a lista sobe acima dele —, e o minimapa, o dele
|
|
1248
|
+
// junto dos botões de zoom (a L1 e a M2 da bancada, escolha do Victor de 21/10/2026).
|
|
1249
|
+
const [legendaAberta, setLegendaAberta] = React.useState(defaultLegendOpen);
|
|
1250
|
+
const [minimapaAberto, setMinimapaAberto] = React.useState(defaultMinimapOpen);
|
|
1251
|
+
const idDoMinimapa = React.useId();
|
|
1252
|
+
const temLegenda = !!legend && (legenda.tipos.length > 0 || legenda.estados.length > 0);
|
|
1253
|
+
const mostraMinimapa = !!minimap && (!minimapToggle || minimapaAberto);
|
|
1254
|
+
const listasDaLegenda = _jsxs(_Fragment, { children: [legenda.tipos.length > 0 && _jsxs("div", { className: "graph-legend-part", children: [legenda.estados.length > 0 && _jsx("p", { className: "graph-legend-title", id: `${idDaLegenda}-tipo`, children: s.graphLegendKind }), _jsx("ul", { "aria-labelledby": legenda.estados.length > 0 ? `${idDaLegenda}-tipo` : undefined, children: legenda.tipos.map(t => _jsxs("li", { children: [_jsx(AmostraDaLinha, { padrao: t.padrao, peso: t.peso, marca: t.marca }), _jsx("span", { children: t.nome })] }, t.nome)) })] }), legenda.estados.length > 0 && _jsxs("div", { className: "graph-legend-part", children: [legenda.tipos.length > 0 && _jsx("p", { className: "graph-legend-title", id: `${idDaLegenda}-estado`, children: s.graphLegendStatus }), _jsx("ul", { "aria-labelledby": legenda.tipos.length > 0 ? `${idDaLegenda}-estado` : undefined, children: legenda.estados.map(e => _jsxs("li", { children: [_jsx(AmostraDaLinha, { tom: e.tom, marca: e.marca }), _jsx("span", { children: e.nome })] }, e.nome)) })] })] });
|
|
1246
1255
|
// O texto que o MOTOR põe na tela (descrição do nó para o leitor de tela, aviso de "movido", nome das
|
|
1247
1256
|
// alças e do minimapa) sai na língua da Aurea — o padrão dele é inglês.
|
|
1248
1257
|
const rotulosDoMotor = React.useMemo(() => ({
|
|
@@ -1376,7 +1385,10 @@ export function DependencyGraph({ nodes, edges, label, selectedId, onSelect, con
|
|
|
1376
1385
|
// a fluidez. O valor é a extensão do próprio layout, então no servidor o grafo cabe exato.
|
|
1377
1386
|
const estilo = { ...style, ...(height ? { blockSize: height } : null), "--graph-node-w": `${m.larg}px` };
|
|
1378
1387
|
return _jsx("div", { ref: raiz, className: cx("dependency-graph", className), style: estilo, "data-text": textSize === "md" ? undefined : textSize, "data-contrast": highContrast ? "high" : undefined, "data-editable": editavel || undefined, role: "group", "aria-label": label ?? s.graphLabel, ...props, children: _jsx(ReactFlowProvider, { initialNodes: nosDoMotor, initialEdges: arestasDoMotor, fitView: true, initialWidth: automatico.extensao.larg, initialHeight: automatico.extensao.alt, children: _jsxs(ReactFlow, { nodes: nosVivos, onNodesChange: aoMudarNos, edges: arestasDoMotor, nodeTypes: TIPOS, edgeTypes: TIPOS_DE_ARESTA, fitView: true, proOptions: { hideAttribution: false }, minZoom: ESCALA_MINIMA, nodesDraggable: editavel || !!onNodeMove, nodesConnectable: !!connectable, elementsSelectable: editavel, nodesFocusable: editavel, edgesFocusable: !!onEdgeSelect, disableKeyboardA11y: !editavel, selectionOnDrag: editavel, panOnDrag: editavel ? BOTOES_DE_ARRASTAR : true, selectionMode: SelectionMode.Partial, multiSelectionKeyCode: TECLAS_DE_SOMAR, deleteKeyCode: null, snapToGrid: editavel && grid, snapGrid: [GRADE, GRADE], onlyRenderVisibleElements: !!visibleOnly, ariaLabelConfig: rotulosDoMotor, onSelectionChange: editavel ? aoMudarEscolha : undefined, onNodeMouseEnter: onNodeHover ? (_, no) => onNodeHover(no.id) : undefined, onNodeMouseLeave: onNodeHover ? () => onNodeHover(null) : undefined, onNodeContextMenu: onNodeContextMenu ? (ev, no) => { ev.preventDefault(); onNodeContextMenu(no.id, { x: ev.clientX, y: ev.clientY }); } : undefined, onEdgeClick: onEdgeSelect ? (_, a) => onEdgeSelect(a.id) : undefined, onConnect: onConnect ? c => { if (c.source && c.target)
|
|
1379
|
-
onConnect({ from: c.source, to: c.target, fromPort: c.sourceHandle ?? undefined, toPort: c.targetHandle ?? undefined }); } : undefined, children: [_jsx(Background, { gap: editavel && grid ? GRADE : undefined }), guias.length > 0 && _jsx(ViewportPortal, { children: guias.map(g => _jsx("div", { className: g.eixo === "x" ? "graph-guide graph-guide-x" : "graph-guide graph-guide-y", "aria-hidden": "true", style: g.eixo === "x" ? { transform: `translate(${g.em}px, ${g.de}px)`, blockSize: g.ate - g.de } : { transform: `translate(${g.de}px, ${g.em}px)`, inlineSize: g.ate - g.de } }, `${g.eixo}${g.em}`)) }), _jsx(Comandos, { raiz: raiz, teclado: teclado, focusId: focusId, pedidoDeCaber: pedidoDeCaber, controls: !!controls,
|
|
1388
|
+
onConnect({ from: c.source, to: c.target, fromPort: c.sourceHandle ?? undefined, toPort: c.targetHandle ?? undefined }); } : undefined, children: [_jsx(Background, { gap: editavel && grid ? GRADE : undefined }), guias.length > 0 && _jsx(ViewportPortal, { children: guias.map(g => _jsx("div", { className: g.eixo === "x" ? "graph-guide graph-guide-x" : "graph-guide graph-guide-y", "aria-hidden": "true", style: g.eixo === "x" ? { transform: `translate(${g.em}px, ${g.de}px)`, blockSize: g.ate - g.de } : { transform: `translate(${g.de}px, ${g.em}px)`, inlineSize: g.ate - g.de } }, `${g.eixo}${g.em}`)) }), _jsx(Comandos, { raiz: raiz, teclado: teclado, focusId: focusId, pedidoDeCaber: pedidoDeCaber, controls: !!controls, minimapa: minimap && minimapToggle ? { aberto: minimapaAberto, alternar: () => setMinimapaAberto(a => !a), id: idDoMinimapa } : undefined, apiRef: apiRef, linhas: linhas, forma: edgeShape, titulo: label ?? s.graphLabel, desenho: tracados, editavel: editavel, barra: barra, acoes: acoes }), mostraMinimapa && (minimapToggle ? _jsx("div", { id: idDoMinimapa, children: _jsx(MiniMap, { pannable: true, zoomable: true, ariaLabel: s.graphOverview, className: "graph-minimap" }) })
|
|
1389
|
+
: _jsx(MiniMap, { pannable: true, zoomable: true, ariaLabel: s.graphOverview, className: "graph-minimap" })), temLegenda && (legendCollapsible
|
|
1390
|
+
? _jsxs(Panel, { position: "bottom-left", className: "graph-dock", children: [legendaAberta && _jsx("div", { className: "graph-legend", id: `${idDaLegenda}-lista`, role: "group", "aria-label": s.graphLegend, children: listasDaLegenda }), _jsx("div", { className: "graph-controls", children: _jsx(Button, { variant: "ghost", leadingIcon: "list-bullets", trailingIcon: legendaAberta ? "caret-down" : "caret-up", "aria-expanded": legendaAberta, "aria-controls": legendaAberta ? `${idDaLegenda}-lista` : undefined, onClick: () => setLegendaAberta(a => !a), children: s.graphLegend }) })] })
|
|
1391
|
+
: _jsx(Panel, { position: "bottom-left", children: _jsx("div", { className: "graph-legend", role: "group", "aria-label": s.graphLegend, children: listasDaLegenda }) }))] }) }) });
|
|
1380
1392
|
}
|
|
1381
1393
|
/** O primeiro alvo de Tab de um nó no mapa de leitura: o botão dele; sem ele, o de fechar. */
|
|
1382
1394
|
function ancoraDoNo(raiz, id) {
|
|
@@ -1409,7 +1421,7 @@ function esconderRotulos(el) {
|
|
|
1409
1421
|
* até", as setas de nó em nó, os botões de zoom, a barra do modo de arrumar, os rótulos que somem e
|
|
1410
1422
|
* o `apiRef`.
|
|
1411
1423
|
*/
|
|
1412
|
-
function Comandos({ raiz, teclado, focusId, pedidoDeCaber, controls, apiRef, linhas, forma, titulo, desenho, editavel, barra, acoes }) {
|
|
1424
|
+
function Comandos({ raiz, teclado, focusId, pedidoDeCaber, controls, minimapa, apiRef, linhas, forma, titulo, desenho, editavel, barra, acoes }) {
|
|
1413
1425
|
const rf = useReactFlow();
|
|
1414
1426
|
const s = useAureaStrings();
|
|
1415
1427
|
// Quem pediu menos movimento não vê a tela deslizar: o "ir até" e o caber na tela pulam direto.
|
|
@@ -1574,7 +1586,7 @@ function Comandos({ raiz, teclado, focusId, pedidoDeCaber, controls, apiRef, lin
|
|
|
1574
1586
|
relayout: () => acoes.current.reorganizar(), getLayout: () => acoes.current.arrumacao(),
|
|
1575
1587
|
}), [rf, centralizar, linhas, forma, titulo, raiz, acoes, caber]);
|
|
1576
1588
|
const a = acoes.current;
|
|
1577
|
-
return _jsxs(_Fragment, { children: [editavel && _jsx(Panel, { ref: barraRef, position: "top-left", className: "graph-toolbar", children: _jsxs(Toolbar, { label: s.graphArrange, children: [_jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphUndo, disabled: !barra.podeDesfazer, onClick: () => a.desfazer(), children: _jsx(Icon, { name: "arrow-u-up-left" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphRedo, disabled: !barra.podeRefazer, onClick: () => a.refazer(), children: _jsx(Icon, { name: "arrow-u-up-right" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignLeft, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("left"), children: _jsx(Icon, { name: "align-left" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignCenter, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("center"), children: _jsx(Icon, { name: "align-center-horizontal" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignRight, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("right"), children: _jsx(Icon, { name: "align-right" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignTop, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("top"), children: _jsx(Icon, { name: "align-top" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignMiddle, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("middle"), children: _jsx(Icon, { name: "align-center-vertical" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignBottom, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("bottom"), children: _jsx(Icon, { name: "align-bottom" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphDistributeH, disabled: barra.alinhaveis < 3, onClick: () => a.distribuir("horizontal"), children: _jsx(Icon, { name: "arrows-out-line-horizontal" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphDistributeV, disabled: barra.alinhaveis < 3, onClick: () => a.distribuir("vertical"), children: _jsx(Icon, { name: "arrows-out-line-vertical" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphPin, disabled: !barra.escolhidos, onClick: () => a.fixar(), "aria-pressed": barra.todosFixos, children: _jsx(Icon, { name: "push-pin" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphRelayout, disabled: false, onClick: () => a.reorganizar(), children: _jsx(Icon, { name: "tree-structure" }) })] }) }), controls && _jsxs(Panel, { ref: zoomRef, position: "top-right", className: "graph-controls", children: [_jsx(IconButton, { icon: "magnifying-glass-plus", label: s.graphZoomIn, onClick: () => void rf.zoomIn({ duration: duracao() }) }), _jsx(IconButton, { icon: "magnifying-glass-minus", label: s.graphZoomOut, onClick: () => void rf.zoomOut({ duration: duracao() }) }), _jsx(IconButton, { icon: "corners-out", label: s.graphFit, onClick: () => caber() })] })] });
|
|
1589
|
+
return _jsxs(_Fragment, { children: [editavel && _jsx(Panel, { ref: barraRef, position: "top-left", className: "graph-toolbar", children: _jsxs(Toolbar, { label: s.graphArrange, children: [_jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphUndo, disabled: !barra.podeDesfazer, onClick: () => a.desfazer(), children: _jsx(Icon, { name: "arrow-u-up-left" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphRedo, disabled: !barra.podeRefazer, onClick: () => a.refazer(), children: _jsx(Icon, { name: "arrow-u-up-right" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignLeft, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("left"), children: _jsx(Icon, { name: "align-left" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignCenter, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("center"), children: _jsx(Icon, { name: "align-center-horizontal" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignRight, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("right"), children: _jsx(Icon, { name: "align-right" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignTop, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("top"), children: _jsx(Icon, { name: "align-top" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignMiddle, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("middle"), children: _jsx(Icon, { name: "align-center-vertical" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphAlignBottom, disabled: barra.alinhaveis < 2, onClick: () => a.alinhar("bottom"), children: _jsx(Icon, { name: "align-bottom" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphDistributeH, disabled: barra.alinhaveis < 3, onClick: () => a.distribuir("horizontal"), children: _jsx(Icon, { name: "arrows-out-line-horizontal" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphDistributeV, disabled: barra.alinhaveis < 3, onClick: () => a.distribuir("vertical"), children: _jsx(Icon, { name: "arrows-out-line-vertical" }) }), _jsx(ToolbarSeparator, {}), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphPin, disabled: !barra.escolhidos, onClick: () => a.fixar(), "aria-pressed": barra.todosFixos, children: _jsx(Icon, { name: "push-pin" }) }), _jsx(ToolbarButton, { className: "btn-icon", "aria-label": s.graphRelayout, disabled: false, onClick: () => a.reorganizar(), children: _jsx(Icon, { name: "tree-structure" }) })] }) }), (controls || minimapa) && _jsxs(Panel, { ref: zoomRef, position: "top-right", className: "graph-controls", children: [controls && _jsxs(_Fragment, { children: [_jsx(IconButton, { icon: "magnifying-glass-plus", label: s.graphZoomIn, onClick: () => void rf.zoomIn({ duration: duracao() }) }), _jsx(IconButton, { icon: "magnifying-glass-minus", label: s.graphZoomOut, onClick: () => void rf.zoomOut({ duration: duracao() }) }), _jsx(IconButton, { icon: "corners-out", label: s.graphFit, onClick: () => caber() })] }), minimapa && _jsx(IconButton, { icon: "map-trifold", label: s.graphOverview, "aria-expanded": minimapa.aberto, "aria-controls": minimapa.aberto ? minimapa.id : undefined, onClick: minimapa.alternar })] })] });
|
|
1578
1590
|
}
|
|
1579
1591
|
/**
|
|
1580
1592
|
* A FOTO do mapa (MNT-19.2), pelo `modern-screenshot` (peer OPCIONAL, MIT), carregado só aqui. Sai
|