zenit-sdk 0.0.5 → 0.0.6

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/index.js CHANGED
@@ -1403,7 +1403,7 @@ function normalizeBbox(input) {
1403
1403
  }
1404
1404
 
1405
1405
  // src/react/ZenitMap.tsx
1406
- var import_react = __toESM(require("react"));
1406
+ var import_react = require("react");
1407
1407
  var import_react_leaflet = require("react-leaflet");
1408
1408
  var import_leaflet = __toESM(require("leaflet"));
1409
1409
 
@@ -1518,121 +1518,6 @@ function getFeatureLayerId(feature) {
1518
1518
  function escapeHtml(value) {
1519
1519
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1520
1520
  }
1521
- var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
1522
- function normalizeDescriptionValue(value) {
1523
- if (value === void 0 || value === null) return null;
1524
- if (typeof value === "string") {
1525
- const trimmed = value.trim();
1526
- return trimmed ? trimmed : null;
1527
- }
1528
- if (typeof value === "number" || typeof value === "boolean") {
1529
- return String(value);
1530
- }
1531
- return null;
1532
- }
1533
- function extractDescriptionValue(properties) {
1534
- if (!properties) return null;
1535
- const matches = Object.entries(properties).find(
1536
- ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
1537
- );
1538
- if (!matches) return null;
1539
- return normalizeDescriptionValue(matches[1]);
1540
- }
1541
- function safeJsonStringify(value) {
1542
- try {
1543
- const json = JSON.stringify(value, null, 2);
1544
- if (json !== void 0) return json;
1545
- } catch {
1546
- }
1547
- return String(value);
1548
- }
1549
- function renderPropertyValue(value) {
1550
- if (value === null || value === void 0) {
1551
- return '<span class="prop-empty">\u2014</span>';
1552
- }
1553
- if (typeof value === "object") {
1554
- const json = safeJsonStringify(value);
1555
- return `<pre class="prop-json">${escapeHtml(json)}</pre>`;
1556
- }
1557
- return `<span class="prop-text">${escapeHtml(String(value))}</span>`;
1558
- }
1559
- var POPUP_TITLE_KEYS = ["name", "title", "nombre", "label", "id"];
1560
- var POPUP_CHIP_KEYS = /* @__PURE__ */ new Set(["churn", "color", "sector"]);
1561
- function isChipValue(value) {
1562
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1563
- }
1564
- function getSanitizedChipColor(value) {
1565
- if (typeof value !== "string") return null;
1566
- const trimmed = value.trim();
1567
- if (/^#([0-9a-fA-F]{3}){1,2}$/.test(trimmed)) {
1568
- return trimmed;
1569
- }
1570
- if (/^rgb\((\s*\d+\s*,){2}\s*\d+\s*\)$/.test(trimmed)) {
1571
- return trimmed;
1572
- }
1573
- if (/^rgba\((\s*\d+\s*,){3}\s*(0|1|0?\.\d+)\s*\)$/.test(trimmed)) {
1574
- return trimmed;
1575
- }
1576
- return null;
1577
- }
1578
- function getPopupTitle(entries) {
1579
- for (const candidate of POPUP_TITLE_KEYS) {
1580
- const match = entries.find((entry) => entry.normalized === candidate);
1581
- if (!match || match.value === null || match.value === void 0) continue;
1582
- const value = String(match.value).trim();
1583
- if (!value) continue;
1584
- return { title: value, key: match.key };
1585
- }
1586
- return null;
1587
- }
1588
- function renderProperties(properties) {
1589
- const description = extractDescriptionValue(properties);
1590
- const entries = Object.entries(properties).filter(([key]) => !DESCRIPTION_KEYS.has(key.toLowerCase())).map(([key, value]) => ({
1591
- key,
1592
- value,
1593
- normalized: key.trim().toLowerCase()
1594
- }));
1595
- if (!description && entries.length === 0) return "";
1596
- const titleEntry = getPopupTitle(entries);
1597
- const titleText = titleEntry?.title ?? "Detalle del elemento";
1598
- const chipEntries = entries.filter(
1599
- (entry) => POPUP_CHIP_KEYS.has(entry.normalized) && isChipValue(entry.value)
1600
- );
1601
- const listEntries = entries.filter((entry) => {
1602
- if (titleEntry && entry.key === titleEntry.key) return false;
1603
- if (chipEntries.find((chip) => chip.key === entry.key)) return false;
1604
- return true;
1605
- });
1606
- const descriptionHtml = description ? `<div class="popup-description">${escapeHtml(description)}</div>` : "";
1607
- const chipsHtml = chipEntries.length ? `<div class="popup-chip-row">${chipEntries.map((entry) => {
1608
- const label = escapeHtml(entry.key.replace(/_/g, " "));
1609
- const value = escapeHtml(String(entry.value));
1610
- const color = getSanitizedChipColor(entry.value);
1611
- const colorStyle = color ? ` style="--chip-color: ${color}"` : "";
1612
- return `<span class="popup-chip"${colorStyle}><span class="popup-chip-label">${label}</span><span class="popup-chip-value">${value}</span></span>`;
1613
- }).join("")}</div>` : "";
1614
- const rowsHtml = listEntries.map((entry) => {
1615
- const label = escapeHtml(entry.key.replace(/_/g, " "));
1616
- const valueHtml = renderPropertyValue(entry.value);
1617
- return `<div class="prop-key">${label}</div><div class="prop-value">${valueHtml}</div>`;
1618
- }).join("");
1619
- const listHtml = rowsHtml ? `<div class="prop-list">${rowsHtml}</div>` : "";
1620
- return `
1621
- <div class="feature-popup">
1622
- <div class="feature-popup-card">
1623
- <div class="feature-popup-header">
1624
- <p class="popup-eyebrow">Informaci\xF3n</p>
1625
- <h3 class="popup-title">${escapeHtml(titleText)}</h3>
1626
- </div>
1627
- <div class="feature-popup-body">
1628
- ${descriptionHtml}
1629
- ${chipsHtml}
1630
- ${listHtml}
1631
- </div>
1632
- </div>
1633
- </div>
1634
- `;
1635
- }
1636
1521
  function withAlpha(color, alpha) {
1637
1522
  const trimmed = color.trim();
1638
1523
  if (trimmed.startsWith("#")) {
@@ -1701,39 +1586,40 @@ function getFeatureStyleOverrides(feature) {
1701
1586
  function buildFeaturePopupHtml(feature) {
1702
1587
  const properties = feature?.properties;
1703
1588
  if (!properties) return null;
1704
- const rendered = renderProperties(properties);
1705
- return rendered ? rendered : null;
1706
- }
1707
- var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
1708
- function getGeometryType(feature) {
1709
- const t = feature?.geometry?.type;
1710
- return typeof t === "string" ? t : null;
1711
- }
1712
- function isPointGeometry(feature) {
1713
- const geometryType = getGeometryType(feature);
1714
- return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
1715
- }
1716
- function isNonPointGeometry(feature) {
1717
- const geometryType = getGeometryType(feature);
1718
- return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
1719
- }
1720
- function buildFeatureCollection(features) {
1721
- return {
1722
- type: "FeatureCollection",
1723
- features
1724
- };
1725
- }
1726
- function pickIntersectFeature(baseFeature, candidates) {
1727
- if (!Array.isArray(candidates) || candidates.length === 0) return null;
1728
- const baseId = baseFeature?.id;
1729
- if (baseId !== void 0 && baseId !== null) {
1730
- const matchById = candidates.find((candidate) => candidate?.id === baseId);
1731
- if (matchById) return matchById;
1589
+ const layerName = properties.layerName ?? properties.layer_name ?? properties.name;
1590
+ const descripcion = properties.descripcion ?? properties.description;
1591
+ const reservedKeys = /* @__PURE__ */ new Set([
1592
+ "_style",
1593
+ "layerId",
1594
+ "layer_id",
1595
+ "__zenit_layerId",
1596
+ "layerName",
1597
+ "layer_name",
1598
+ "name",
1599
+ "descripcion",
1600
+ "description"
1601
+ ]);
1602
+ const extraEntries = Object.entries(properties).filter(([key, value]) => {
1603
+ if (reservedKeys.has(key)) return false;
1604
+ return ["string", "number", "boolean"].includes(typeof value);
1605
+ }).slice(0, 5);
1606
+ if (!layerName && !descripcion && extraEntries.length === 0) return null;
1607
+ const parts = [];
1608
+ if (layerName) {
1609
+ parts.push(`<div style="font-weight:600;margin-bottom:4px;">${escapeHtml(layerName)}</div>`);
1732
1610
  }
1733
- const matchWithDescription = candidates.find(
1734
- (candidate) => extractDescriptionValue(candidate?.properties)
1735
- );
1736
- return matchWithDescription ?? candidates[0];
1611
+ if (descripcion) {
1612
+ parts.push(`<div style="margin-bottom:6px;">${escapeHtml(descripcion)}</div>`);
1613
+ }
1614
+ if (extraEntries.length > 0) {
1615
+ const rows = extraEntries.map(([key, value]) => {
1616
+ const label = escapeHtml(key.replace(/_/g, " "));
1617
+ const val = escapeHtml(String(value));
1618
+ return `<div><strong>${label}:</strong> ${val}</div>`;
1619
+ }).join("");
1620
+ parts.push(`<div style="font-size:12px;line-height:1.4;">${rows}</div>`);
1621
+ }
1622
+ return `<div>${parts.join("")}</div>`;
1737
1623
  }
1738
1624
  function normalizeCenterTuple(center) {
1739
1625
  if (!center) return null;
@@ -1854,7 +1740,6 @@ var ZenitMap = (0, import_react.forwardRef)(({
1854
1740
  const [loadingMap, setLoadingMap] = (0, import_react.useState)(false);
1855
1741
  const [mapError, setMapError] = (0, import_react.useState)(null);
1856
1742
  const [mapInstance, setMapInstance] = (0, import_react.useState)(null);
1857
- const [panesReady, setPanesReady] = (0, import_react.useState)(false);
1858
1743
  const [currentZoom, setCurrentZoom] = (0, import_react.useState)(initialZoom ?? DEFAULT_ZOOM);
1859
1744
  const [isMobile, setIsMobile] = (0, import_react.useState)(() => {
1860
1745
  if (typeof window === "undefined") return false;
@@ -2169,23 +2054,16 @@ var ZenitMap = (0, import_react.forwardRef)(({
2169
2054
  (targetMap, targetLayers) => {
2170
2055
  const baseZIndex = 400;
2171
2056
  targetLayers.forEach((layer) => {
2057
+ const paneName = `zenit-layer-${layer.layerId}`;
2058
+ const pane = targetMap.getPane(paneName) ?? targetMap.createPane(paneName);
2172
2059
  const order = Number.isFinite(layer.displayOrder) ? layer.displayOrder : 0;
2173
- const fillPaneName = `zenit-layer-${layer.layerId}-fill`;
2174
- const pointPaneName = `zenit-layer-${layer.layerId}-points`;
2175
- const labelPaneName = `zenit-layer-${layer.layerId}-labels`;
2176
- const fillPane = targetMap.getPane(fillPaneName) ?? targetMap.createPane(fillPaneName);
2177
- const pointPane = targetMap.getPane(pointPaneName) ?? targetMap.createPane(pointPaneName);
2178
- const labelPane = targetMap.getPane(labelPaneName) ?? targetMap.createPane(labelPaneName);
2179
- fillPane.style.zIndex = String(baseZIndex + order);
2180
- pointPane.style.zIndex = String(baseZIndex + order + 1e3);
2181
- labelPane.style.zIndex = String(baseZIndex + order + 2e3);
2060
+ pane.style.zIndex = String(baseZIndex + order);
2182
2061
  });
2183
2062
  },
2184
2063
  []
2185
2064
  );
2186
2065
  const handleMapReady = (0, import_react.useCallback)(
2187
2066
  (instance) => {
2188
- setPanesReady(false);
2189
2067
  setMapInstance(instance);
2190
2068
  onMapReady?.(instance);
2191
2069
  },
@@ -2193,7 +2071,6 @@ var ZenitMap = (0, import_react.forwardRef)(({
2193
2071
  );
2194
2072
  (0, import_react.useEffect)(() => {
2195
2073
  if (!mapInstance) {
2196
- setPanesReady(false);
2197
2074
  return;
2198
2075
  }
2199
2076
  if (orderedLayers.length === 0) {
@@ -2204,11 +2081,6 @@ var ZenitMap = (0, import_react.forwardRef)(({
2204
2081
  displayOrder: layer.displayOrder
2205
2082
  }));
2206
2083
  ensureLayerPanes(mapInstance, layerTargets);
2207
- const first = layerTargets[0];
2208
- const testPane = mapInstance.getPane(`zenit-layer-${first.layerId}-labels`);
2209
- if (testPane) {
2210
- setPanesReady(true);
2211
- }
2212
2084
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
2213
2085
  const overlayOnEachFeature = (0, import_react.useMemo)(() => {
2214
2086
  return (feature, layer) => {
@@ -2226,14 +2098,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
2226
2098
  if (featureInfoMode === "popup") {
2227
2099
  const content = buildFeaturePopupHtml(feature);
2228
2100
  if (content) {
2229
- layer.bindPopup(content, {
2230
- maxWidth: 420,
2231
- minWidth: 280,
2232
- className: "zenit-feature-popup-shell",
2233
- autoPan: true,
2234
- closeButton: true,
2235
- offset: import_leaflet.default.point(0, -24)
2236
- });
2101
+ layer.bindPopup(content, { maxWidth: 320 });
2237
2102
  }
2238
2103
  }
2239
2104
  if (isPointFeature && layer.bindTooltip) {
@@ -2244,37 +2109,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
2244
2109
  className: "zenit-map-tooltip"
2245
2110
  });
2246
2111
  }
2247
- layer.on("click", () => {
2248
- if (featureInfoMode === "popup" && client && layerId !== void 0 && !extractDescriptionValue(feature?.properties) && feature?.geometry) {
2249
- const trackedFeature = feature;
2250
- if (!trackedFeature.__zenit_popup_loaded) {
2251
- trackedFeature.__zenit_popup_loaded = true;
2252
- client.layers.getLayerGeoJsonIntersect({
2253
- id: layerId,
2254
- geometry: feature.geometry
2255
- }).then((response) => {
2256
- const candidates = response.data?.features ?? [];
2257
- const resolved = pickIntersectFeature(feature, candidates);
2258
- if (!resolved?.properties) return;
2259
- const mergedProperties = {
2260
- ...trackedFeature.properties ?? {},
2261
- ...resolved.properties
2262
- };
2263
- trackedFeature.properties = mergedProperties;
2264
- const updatedHtml = buildFeaturePopupHtml({
2265
- ...feature,
2266
- properties: mergedProperties
2267
- });
2268
- if (updatedHtml && layer.setPopupContent) {
2269
- layer.setPopupContent(updatedHtml);
2270
- }
2271
- }).catch(() => {
2272
- trackedFeature.__zenit_popup_loaded = false;
2273
- });
2274
- }
2275
- }
2276
- onFeatureClick?.(feature, layerId);
2277
- });
2112
+ layer.on("click", () => onFeatureClick?.(feature, layerId));
2278
2113
  layer.on("mouseover", () => {
2279
2114
  if (layer instanceof import_leaflet.default.Path && originalStyle) {
2280
2115
  layer.setStyle({
@@ -2298,7 +2133,7 @@ var ZenitMap = (0, import_react.forwardRef)(({
2298
2133
  }
2299
2134
  });
2300
2135
  };
2301
- }, [client, featureInfoMode, onFeatureClick, onFeatureHover]);
2136
+ }, [featureInfoMode, onFeatureClick, onFeatureHover]);
2302
2137
  const buildLayerStyle = (layerId, baseOpacity, feature, layerType) => {
2303
2138
  const style = resolveLayerStyle(layerId);
2304
2139
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -2479,50 +2314,22 @@ var ZenitMap = (0, import_react.forwardRef)(({
2479
2314
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
2480
2315
  orderedLayers.map((layerState) => {
2481
2316
  const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
2482
- const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
2483
- const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
2484
- const labelPaneName = `zenit-layer-${layerState.mapLayer.layerId}-labels`;
2317
+ const paneName = `zenit-layer-${layerState.mapLayer.layerId}`;
2485
2318
  const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
2486
- const data = layerState.data?.features ?? [];
2487
- const fillFeatures = data.filter(isNonPointGeometry);
2488
- const pointFeatures = data.filter(isPointGeometry);
2489
- const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
2490
- const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
2491
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
2492
- fillData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2493
- import_react_leaflet.GeoJSON,
2494
- {
2495
- data: fillData,
2496
- pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
2497
- style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
2498
- onEachFeature: overlayOnEachFeature
2499
- }
2500
- ),
2501
- pointsData && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2502
- import_react_leaflet.GeoJSON,
2503
- {
2504
- data: pointsData,
2505
- pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
2506
- pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
2507
- radius: isMobile ? 8 : 6,
2508
- ...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
2509
- }),
2510
- onEachFeature: overlayOnEachFeature
2511
- }
2512
- ),
2513
- panesReady && mapInstance?.getPane(labelPaneName) ? labelMarkers.filter(
2514
- (marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
2515
- ).map((marker) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2516
- import_react_leaflet.Marker,
2517
- {
2518
- position: marker.position,
2519
- icon: buildLabelIcon(marker.label, marker.opacity, marker.color),
2520
- interactive: false,
2521
- pane: labelPaneName
2522
- },
2523
- marker.key
2524
- )) : null
2525
- ] }, layerState.mapLayer.layerId.toString());
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2320
+ import_react_leaflet.GeoJSON,
2321
+ {
2322
+ data: layerState.data,
2323
+ pane: mapInstance?.getPane(paneName) ? paneName : void 0,
2324
+ style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
2325
+ pointToLayer: (feature, latlng) => import_leaflet.default.circleMarker(latlng, {
2326
+ radius: isMobile ? 8 : 6,
2327
+ ...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
2328
+ }),
2329
+ onEachFeature: overlayOnEachFeature
2330
+ },
2331
+ layerState.mapLayer.layerId.toString()
2332
+ );
2526
2333
  }),
2527
2334
  overlayGeojson && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2528
2335
  import_react_leaflet.GeoJSON,
@@ -2532,7 +2339,16 @@ var ZenitMap = (0, import_react.forwardRef)(({
2532
2339
  onEachFeature: overlayOnEachFeature
2533
2340
  },
2534
2341
  "zenit-overlay-geojson"
2535
- )
2342
+ ),
2343
+ labelMarkers.map((marker) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2344
+ import_react_leaflet.Marker,
2345
+ {
2346
+ position: marker.position,
2347
+ icon: buildLabelIcon(marker.label, marker.opacity, marker.color),
2348
+ interactive: false
2349
+ },
2350
+ marker.key
2351
+ ))
2536
2352
  ]
2537
2353
  },
2538
2354
  String(mapId)
@@ -3814,19 +3630,9 @@ var FloatingChatBox = ({
3814
3630
  getAccessToken,
3815
3631
  onActionClick,
3816
3632
  onOpenChange,
3817
- hideButton,
3818
- open: openProp
3633
+ hideButton
3819
3634
  }) => {
3820
- const isControlled = openProp !== void 0;
3821
- const [internalOpen, setInternalOpen] = (0, import_react4.useState)(false);
3822
- const open = isControlled ? openProp : internalOpen;
3823
- const setOpen = (0, import_react4.useCallback)((value) => {
3824
- const newValue = typeof value === "function" ? value(open) : value;
3825
- if (!isControlled) {
3826
- setInternalOpen(newValue);
3827
- }
3828
- onOpenChange?.(newValue);
3829
- }, [isControlled, open, onOpenChange]);
3635
+ const [open, setOpen] = (0, import_react4.useState)(false);
3830
3636
  const [expanded, setExpanded] = (0, import_react4.useState)(false);
3831
3637
  const [messages, setMessages] = (0, import_react4.useState)([]);
3832
3638
  const [inputValue, setInputValue] = (0, import_react4.useState)("");
@@ -3843,6 +3649,9 @@ var FloatingChatBox = ({
3843
3649
  }, [accessToken, baseUrl, getAccessToken]);
3844
3650
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
3845
3651
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
3652
+ (0, import_react4.useEffect)(() => {
3653
+ onOpenChange?.(open);
3654
+ }, [open, onOpenChange]);
3846
3655
  (0, import_react4.useEffect)(() => {
3847
3656
  if (open && isMobile) {
3848
3657
  setExpanded(true);
@@ -3995,13 +3804,6 @@ var FloatingChatBox = ({
3995
3804
  ] }, index)) })
3996
3805
  ] });
3997
3806
  };
3998
- const handleActionClick = (0, import_react4.useCallback)((action) => {
3999
- if (isStreaming) return;
4000
- setOpen(false);
4001
- requestAnimationFrame(() => {
4002
- onActionClick?.(action);
4003
- });
4004
- }, [isStreaming, setOpen, onActionClick]);
4005
3807
  const renderActions = (response) => {
4006
3808
  if (!response?.suggestedActions?.length) return null;
4007
3809
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: styles.actionsSection, children: [
@@ -4015,7 +3817,7 @@ var FloatingChatBox = ({
4015
3817
  opacity: isStreaming ? 0.5 : 1,
4016
3818
  cursor: isStreaming ? "not-allowed" : "pointer"
4017
3819
  },
4018
- onClick: () => handleActionClick(action),
3820
+ onClick: () => !isStreaming && onActionClick?.(action),
4019
3821
  disabled: isStreaming,
4020
3822
  onMouseEnter: (e) => {
4021
3823
  if (!isStreaming) {