zenit-sdk 0.0.8 → 0.1.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.
@@ -1,7 +1,7 @@
1
1
  // src/react/ZenitMap.tsx
2
- import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, forwardRef } from "react";
3
- import { GeoJSON, MapContainer, Marker, TileLayer, ZoomControl, useMap } from "react-leaflet";
4
- import L from "leaflet";
2
+ import React3, { useCallback as useCallback2, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useState as useState2, forwardRef } from "react";
3
+ import { MapContainer, Marker as Marker2, TileLayer, ZoomControl } from "react-leaflet";
4
+ import L4 from "leaflet";
5
5
 
6
6
  // src/maps/helpers.ts
7
7
  function toNumber(value) {
@@ -206,9 +206,9 @@ function getAccentByLayerId(layerId, mapLayers) {
206
206
 
207
207
  // src/react/zoomOpacity.ts
208
208
  var DEFAULT_OPTIONS = {
209
- minZoom: 10,
210
- maxZoom: 17,
211
- minFactor: 0.6,
209
+ minZoom: 11,
210
+ maxZoom: 15,
211
+ minFactor: 0.3,
212
212
  maxFactor: 1,
213
213
  minOpacity: 0.1,
214
214
  maxOpacity: 0.92
@@ -245,358 +245,215 @@ function getEffectiveLayerOpacity(baseOpacity, zoom, layerType, geometryType, op
245
245
  return clampNumber(effective, settings.minOpacity, settings.maxOpacity);
246
246
  }
247
247
 
248
- // src/react/ZenitMap.tsx
249
- import { jsx, jsxs } from "react/jsx-runtime";
250
- var DEFAULT_CENTER = [0, 0];
251
- var DEFAULT_ZOOM = 3;
252
- var LABELS_PANE_NAME = "zenit-labels-pane";
253
- function computeBBoxFromGeojson(geojson) {
254
- if (!geojson) return null;
255
- if (!Array.isArray(geojson.features) || geojson.features.length === 0) return null;
256
- const coords = [];
257
- const collect = (candidate) => {
258
- if (!Array.isArray(candidate)) return;
259
- if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number") {
260
- coords.push([candidate[0], candidate[1]]);
261
- return;
262
- }
263
- candidate.forEach((entry) => collect(entry));
264
- };
265
- geojson.features.forEach((feature) => {
266
- collect(feature.geometry?.coordinates);
267
- });
268
- if (coords.length === 0) return null;
269
- const [firstLon, firstLat] = coords[0];
270
- const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
271
- coords.forEach(([lon, lat]) => {
272
- bbox.minLon = Math.min(bbox.minLon, lon);
273
- bbox.minLat = Math.min(bbox.minLat, lat);
274
- bbox.maxLon = Math.max(bbox.maxLon, lon);
275
- bbox.maxLat = Math.max(bbox.maxLat, lat);
276
- });
277
- return bbox;
278
- }
279
- function mergeBBoxes(bboxes) {
280
- const valid = bboxes.filter((bbox) => !!bbox);
281
- if (valid.length === 0) return null;
282
- const first = valid[0];
283
- return valid.slice(1).reduce(
284
- (acc, bbox) => ({
285
- minLon: Math.min(acc.minLon, bbox.minLon),
286
- minLat: Math.min(acc.minLat, bbox.minLat),
287
- maxLon: Math.max(acc.maxLon, bbox.maxLon),
288
- maxLat: Math.max(acc.maxLat, bbox.maxLat)
289
- }),
290
- { ...first }
291
- );
292
- }
293
- function isRecord(value) {
294
- return typeof value === "object" && value !== null;
248
+ // src/react/map/layer-geojson.tsx
249
+ import { GeoJSON } from "react-leaflet";
250
+ import L from "leaflet";
251
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
252
+ var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
253
+ function getGeometryType(feature) {
254
+ const t = feature?.geometry?.type;
255
+ return typeof t === "string" ? t : null;
295
256
  }
296
- function isGeoJsonFeatureCollection(value) {
297
- if (!isRecord(value)) return false;
298
- const features = value.features;
299
- if (!Array.isArray(features)) return false;
300
- const type = value.type;
301
- return type === void 0 || type === "FeatureCollection";
257
+ function isPointGeometry(feature) {
258
+ const geometryType = getGeometryType(feature);
259
+ return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
302
260
  }
303
- function extractGeoJsonFeatureCollection(value) {
304
- if (isRecord(value) && "data" in value) {
305
- const data = value.data;
306
- return isGeoJsonFeatureCollection(data) ? data : null;
307
- }
308
- return isGeoJsonFeatureCollection(value) ? value : null;
261
+ function isNonPointGeometry(feature) {
262
+ const geometryType = getGeometryType(feature);
263
+ return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
309
264
  }
310
- function getFeatureLayerId(feature) {
311
- const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
312
- if (layerId === void 0 || layerId === null) return null;
313
- return layerId;
265
+ function buildFeatureCollection(features) {
266
+ return {
267
+ type: "FeatureCollection",
268
+ features
269
+ };
314
270
  }
315
- function escapeHtml(value) {
316
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
271
+ var LayerGeoJson = ({
272
+ layerId,
273
+ data,
274
+ baseOpacity,
275
+ isMobile,
276
+ panesReady,
277
+ mapInstance,
278
+ fillPaneName,
279
+ pointsPaneName,
280
+ layerType,
281
+ styleFn,
282
+ onEachFeature,
283
+ onPolygonLabel
284
+ }) => {
285
+ const features = data.features ?? [];
286
+ const fillFeatures = features.filter(isNonPointGeometry);
287
+ const pointFeatures = features.filter(isPointGeometry);
288
+ const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
289
+ const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
290
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
291
+ fillData && /* @__PURE__ */ jsx(
292
+ GeoJSON,
293
+ {
294
+ data: fillData,
295
+ pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
296
+ style: (feature) => styleFn(feature, layerType, baseOpacity),
297
+ onEachFeature: (feature, layer) => {
298
+ onEachFeature(feature, layer);
299
+ onPolygonLabel?.(feature, layer);
300
+ }
301
+ },
302
+ `fill-${layerId}`
303
+ ),
304
+ pointsData && /* @__PURE__ */ jsx(
305
+ GeoJSON,
306
+ {
307
+ data: pointsData,
308
+ pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
309
+ pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
310
+ radius: isMobile ? 8 : 6,
311
+ ...styleFn(feature, layerType, baseOpacity)
312
+ }),
313
+ onEachFeature
314
+ },
315
+ `points-${layerId}`
316
+ )
317
+ ] });
318
+ };
319
+
320
+ // src/react/map/location-control.tsx
321
+ import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
322
+ import { createPortal } from "react-dom";
323
+ import { Circle, Marker, useMap } from "react-leaflet";
324
+ import L3 from "leaflet";
325
+
326
+ // src/react/hooks/use-geolocation.ts
327
+ import { useCallback, useEffect, useRef, useState } from "react";
328
+ function useGeolocation(options) {
329
+ const [isTracking, setIsTracking] = useState(false);
330
+ const [location, setLocation] = useState(null);
331
+ const [error, setError] = useState(null);
332
+ const watchIdRef = useRef(null);
333
+ const stopTracking = useCallback(() => {
334
+ if (watchIdRef.current !== null && typeof navigator !== "undefined" && navigator.geolocation) {
335
+ navigator.geolocation.clearWatch(watchIdRef.current);
336
+ }
337
+ watchIdRef.current = null;
338
+ setIsTracking(false);
339
+ }, []);
340
+ const startTracking = useCallback(() => {
341
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
342
+ setError({ code: 0, message: "La geolocalizaci\xF3n no est\xE1 disponible en este navegador." });
343
+ return;
344
+ }
345
+ setError(null);
346
+ watchIdRef.current = navigator.geolocation.watchPosition(
347
+ (position) => {
348
+ setLocation({
349
+ lat: position.coords.latitude,
350
+ lon: position.coords.longitude,
351
+ accuracy: position.coords.accuracy
352
+ });
353
+ setIsTracking(true);
354
+ },
355
+ (err) => {
356
+ setError({ code: err.code, message: err.message });
357
+ stopTracking();
358
+ },
359
+ {
360
+ enableHighAccuracy: options?.enableHighAccuracy ?? true,
361
+ timeout: options?.timeout ?? 12e3,
362
+ maximumAge: options?.maximumAge ?? 0
363
+ }
364
+ );
365
+ }, [options?.enableHighAccuracy, options?.maximumAge, options?.timeout, stopTracking]);
366
+ const toggleTracking = useCallback(() => {
367
+ if (isTracking) {
368
+ stopTracking();
369
+ } else {
370
+ startTracking();
371
+ }
372
+ }, [isTracking, startTracking, stopTracking]);
373
+ const clearError = useCallback(() => setError(null), []);
374
+ useEffect(() => {
375
+ return () => {
376
+ stopTracking();
377
+ };
378
+ }, [stopTracking]);
379
+ return {
380
+ isTracking,
381
+ location,
382
+ error,
383
+ startTracking,
384
+ stopTracking,
385
+ toggleTracking,
386
+ clearError
387
+ };
317
388
  }
318
- var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
319
- var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry"]);
320
- var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
389
+
390
+ // src/react/map/map-utils.ts
391
+ import L2 from "leaflet";
321
392
  var POPUP_STYLE_ID = "zenit-leaflet-popup-styles";
322
- var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 350, minWidth: 280, maxHeight: 480 };
323
- var MOBILE_POPUP_DIMENSIONS = { maxWidth: 280, minWidth: 240, maxHeight: 380 };
393
+ var POPUP_EXCLUDED_KEYS = /* @__PURE__ */ new Set(["geom", "geometry", "_private"]);
394
+ var POPUP_HEADER_KEYS = ["nombre", "name", "title", "titulo"];
395
+ var DESKTOP_POPUP_DIMENSIONS = { maxWidth: 360, minWidth: 280, maxHeight: 520 };
396
+ var MOBILE_POPUP_DIMENSIONS = { maxWidth: 300, minWidth: 240, maxHeight: 420 };
324
397
  var ZENIT_LEAFLET_POPUP_STYLES = `
325
- /* ===== Zenit Leaflet Popup - Modern Professional Styling ===== */
326
-
327
- /* Main popup wrapper */
328
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
398
+ .custom-leaflet-popup .leaflet-popup-content-wrapper {
329
399
  border-radius: 12px;
330
- box-shadow:
331
- 0 4px 6px -1px rgba(0, 0, 0, 0.1),
332
- 0 2px 4px -2px rgba(0, 0, 0, 0.1),
333
- 0 0 0 1px rgba(0, 0, 0, 0.05);
334
400
  padding: 0;
335
401
  background: #ffffff;
336
- overflow: hidden;
402
+ box-shadow: 0 12px 24px rgba(15, 23, 42, 0.18);
403
+ border: 1px solid rgba(15, 23, 42, 0.08);
337
404
  }
338
405
 
339
- /* Content area with scroll support */
340
- .zenit-leaflet-popup .leaflet-popup-content {
406
+ .custom-leaflet-popup .leaflet-popup-content {
341
407
  margin: 0;
342
- padding: 0;
408
+ padding: 12px 14px;
343
409
  font-size: 13px;
344
- line-height: 1.5;
345
- color: #374151;
346
- min-width: 100%;
347
- max-height: min(70vh, 480px);
348
- overflow-y: auto;
349
- overflow-x: hidden;
350
- scrollbar-width: thin;
351
- scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
352
- }
353
-
354
- /* Popup tip/arrow shadow */
355
- .zenit-leaflet-popup .leaflet-popup-tip-container {
356
- filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
357
- }
358
-
359
- .zenit-leaflet-popup .leaflet-popup-tip {
360
- background: #ffffff;
361
- box-shadow: none;
362
- }
363
-
364
- /* Close button styling */
365
- .zenit-leaflet-popup .leaflet-popup-close-button {
366
- color: #9ca3af;
367
- font-size: 18px;
368
- font-weight: 400;
369
- width: 28px;
370
- height: 28px;
371
- padding: 0;
372
- margin: 8px 8px 0 0;
373
- display: flex;
374
- align-items: center;
375
- justify-content: center;
376
- border-radius: 6px;
377
- transition: all 0.15s ease;
378
- z-index: 10;
379
- }
380
-
381
- .zenit-leaflet-popup .leaflet-popup-close-button:hover {
382
- color: #374151;
383
- background-color: #f3f4f6;
384
- }
385
-
386
- .zenit-leaflet-popup .leaflet-popup-close-button:active {
387
- background-color: #e5e7eb;
388
- }
389
-
390
- /* Main card container */
391
- .zenit-popup-card {
392
- display: flex;
393
- flex-direction: column;
394
- gap: 0;
395
- padding: 16px;
396
- }
397
-
398
- .zenit-popup-header {
399
- padding-bottom: 12px;
400
- border-bottom: 1px solid #e5e7eb;
401
- margin-bottom: 4px;
402
- }
403
-
404
- .zenit-popup-title {
405
- font-size: 14px;
406
- font-weight: 700;
407
- color: #111827;
408
- letter-spacing: 0.01em;
409
410
  line-height: 1.4;
411
+ color: #0f172a;
412
+ max-height: min(70vh, 520px);
413
+ overflow: auto;
414
+ scrollbar-width: thin;
415
+ scrollbar-color: rgba(148, 163, 184, 0.6) transparent;
410
416
  }
411
417
 
412
- /* Individual row styling with subtle separator */
413
- .zenit-popup-row {
414
- display: flex;
415
- flex-direction: column;
416
- gap: 2px;
417
- padding: 10px 0;
418
- border-bottom: 1px solid #f3f4f6;
419
- }
420
-
421
- .zenit-popup-row:first-child {
422
- padding-top: 0;
423
- }
424
-
425
- .zenit-popup-row:last-child {
426
- border-bottom: none;
427
- padding-bottom: 0;
428
- }
429
-
430
- /* Label styling - small, gray, uppercase */
431
- .zenit-popup-label {
432
- font-size: 10px;
433
- font-weight: 500;
434
- color: #9ca3af;
435
- text-transform: uppercase;
436
- letter-spacing: 0.05em;
437
- line-height: 1.4;
438
- }
439
-
440
- /* Value styling - darker, readable */
441
- .zenit-popup-value {
442
- font-size: 13px;
443
- font-weight: 400;
444
- color: #1f2937;
445
- overflow-wrap: break-word;
446
- word-break: break-word;
447
- line-height: 1.5;
448
- }
449
-
450
- .zenit-popup-link {
451
- color: #2563eb;
452
- text-decoration: underline;
453
- font-weight: 500;
454
- }
455
-
456
- .zenit-popup-link:hover {
457
- color: #1d4ed8;
458
- }
459
-
460
- /* Special styling for description field */
461
- .zenit-popup-row.zenit-popup-description {
462
- background-color: #f9fafb;
463
- margin: 0 -16px;
464
- padding: 12px 16px;
465
- border-bottom: 1px solid #e5e7eb;
466
- }
467
-
468
- .zenit-popup-row.zenit-popup-description:first-child {
469
- margin-top: 0;
470
- border-radius: 0;
471
- }
472
-
473
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
474
- font-size: 13px;
475
- line-height: 1.6;
476
- color: #374151;
477
- max-height: 150px;
478
- overflow-y: auto;
479
- padding-right: 4px;
480
- }
481
-
482
- /* Preformatted text (JSON objects) */
483
- .zenit-popup-pre {
484
- margin: 0;
485
- font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
486
- font-size: 11px;
487
- white-space: pre-wrap;
488
- word-break: break-word;
489
- color: #4b5563;
490
- background-color: #f9fafb;
491
- padding: 8px;
492
- border-radius: 6px;
493
- border: 1px solid #e5e7eb;
418
+ .custom-leaflet-popup .leaflet-popup-close-button {
419
+ color: #64748b;
420
+ font-size: 16px;
421
+ padding: 6px 8px;
422
+ border-radius: 8px;
423
+ transition: background-color 0.15s ease, color 0.15s ease;
494
424
  }
495
425
 
496
- /* Empty state styling */
497
- .zenit-popup-empty {
498
- font-size: 13px;
499
- color: #9ca3af;
500
- font-style: italic;
501
- text-align: center;
502
- padding: 20px 0;
426
+ .custom-leaflet-popup .leaflet-popup-close-button:hover {
427
+ color: #0f172a;
428
+ background: rgba(148, 163, 184, 0.2);
503
429
  }
504
430
 
505
- /* Webkit scrollbar styling */
506
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
431
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar {
507
432
  width: 6px;
508
433
  }
509
434
 
510
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
511
- background: transparent;
512
- }
513
-
514
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
515
- background-color: rgba(156, 163, 175, 0.4);
516
- border-radius: 3px;
435
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb {
436
+ background: rgba(148, 163, 184, 0.5);
437
+ border-radius: 999px;
517
438
  }
518
439
 
519
- .zenit-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-thumb:hover {
520
- background-color: rgba(107, 114, 128, 0.6);
521
- }
522
-
523
- /* Scrollbar for description field */
524
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar {
525
- width: 4px;
526
- }
527
-
528
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-track {
440
+ .custom-leaflet-popup .leaflet-popup-content::-webkit-scrollbar-track {
529
441
  background: transparent;
530
442
  }
531
443
 
532
- .zenit-popup-row.zenit-popup-description .zenit-popup-value::-webkit-scrollbar-thumb {
533
- background-color: rgba(156, 163, 175, 0.4);
534
- border-radius: 2px;
535
- }
536
-
537
- /* ===== Responsive: Mobile (<640px) ===== */
538
444
  @media (max-width: 640px) {
539
- .zenit-leaflet-popup .leaflet-popup-content-wrapper {
540
- border-radius: 10px;
541
- }
542
-
543
- .zenit-leaflet-popup .leaflet-popup-close-button {
544
- width: 26px;
545
- height: 26px;
546
- font-size: 16px;
547
- margin: 6px 6px 0 0;
548
- }
549
-
550
- .zenit-popup-card {
551
- padding: 12px;
552
- }
553
-
554
- .zenit-leaflet-popup .leaflet-popup-content {
555
- max-height: min(65vh, 380px);
556
- }
557
-
558
- .zenit-popup-header {
559
- padding-bottom: 10px;
560
- }
561
-
562
- .zenit-popup-title {
563
- font-size: 13px;
564
- }
565
-
566
- .zenit-popup-row {
567
- padding: 8px 0;
568
- }
569
-
570
- .zenit-popup-label {
571
- font-size: 9px;
572
- }
573
-
574
- .zenit-popup-value {
445
+ .custom-leaflet-popup .leaflet-popup-content {
575
446
  font-size: 12px;
576
- }
577
-
578
- .zenit-popup-row.zenit-popup-description {
579
- margin: 0 -12px;
580
447
  padding: 10px 12px;
448
+ max-height: min(65vh, 420px);
581
449
  }
582
450
 
583
- .zenit-popup-row.zenit-popup-description .zenit-popup-value {
584
- font-size: 12px;
585
- max-height: 120px;
586
- }
587
-
588
- .zenit-popup-pre {
589
- font-size: 10px;
590
- padding: 6px;
591
- }
592
-
593
- .zenit-popup-empty {
594
- font-size: 12px;
595
- padding: 16px 0;
451
+ .custom-leaflet-popup .leaflet-popup-close-button {
452
+ font-size: 14px;
453
+ padding: 4px 6px;
596
454
  }
597
455
  }
598
456
 
599
- /* ===== Map tooltip styling ===== */
600
457
  .zenit-map-tooltip {
601
458
  background-color: rgba(31, 41, 55, 0.95);
602
459
  border: none;
@@ -613,16 +470,21 @@ var ZENIT_LEAFLET_POPUP_STYLES = `
613
470
  }
614
471
 
615
472
  .polygon-label-tooltip {
616
- z-index: 600 !important;
617
- }
618
-
619
- .zenit-map-shell.popup-open .zenit-label-marker,
620
- .zenit-map-shell.popup-open .polygon-label-tooltip,
621
- .zenit-map-shell.popup-open .click-for-detail-hint,
622
- .zenit-map-shell.popup-open .zenit-map-tooltip {
623
- display: none !important;
473
+ background: rgba(15, 23, 42, 0.92);
474
+ border: none;
475
+ border-radius: 6px;
476
+ color: #f8fafc;
477
+ font-weight: 600;
478
+ font-size: 12px;
479
+ padding: 4px 8px;
624
480
  }
625
481
  `;
482
+ function clampNumber2(value, min, max) {
483
+ return Math.min(max, Math.max(min, value));
484
+ }
485
+ function clampOpacity4(value) {
486
+ return clampNumber2(value, 0, 1);
487
+ }
626
488
  function ensurePopupStyles() {
627
489
  if (typeof document === "undefined") return;
628
490
  if (document.getElementById(POPUP_STYLE_ID)) return;
@@ -632,72 +494,52 @@ function ensurePopupStyles() {
632
494
  document.head.appendChild(styleTag);
633
495
  }
634
496
  function getPopupDimensions() {
635
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
497
+ if (typeof window === "undefined") {
636
498
  return DESKTOP_POPUP_DIMENSIONS;
637
499
  }
638
- return window.matchMedia("(max-width: 640px)").matches ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
500
+ const isSmallWidth = window.innerWidth < 640;
501
+ const isShortHeight = window.innerHeight < 768;
502
+ const base = isSmallWidth ? MOBILE_POPUP_DIMENSIONS : DESKTOP_POPUP_DIMENSIONS;
503
+ const maxHeight = isShortHeight ? Math.min(base.maxHeight, 360) : base.maxHeight;
504
+ return { ...base, maxHeight };
639
505
  }
640
- function normalizeDescriptionValue(value) {
641
- if (value === void 0 || value === null) return null;
642
- if (typeof value === "string") {
643
- const trimmed = value.trim();
644
- return trimmed ? trimmed : null;
645
- }
646
- if (typeof value === "number" || typeof value === "boolean") {
647
- return String(value);
648
- }
649
- return null;
650
- }
651
- function extractDescriptionValue(properties) {
652
- if (!properties) return null;
653
- const matches = Object.entries(properties).find(
654
- ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
655
- );
656
- if (!matches) return null;
657
- return normalizeDescriptionValue(matches[1]);
506
+ function escapeHtml(value) {
507
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
658
508
  }
659
- function safeJsonStringify(value) {
660
- try {
661
- const json = JSON.stringify(value, null, 2);
662
- if (json !== void 0) return json;
663
- } catch {
664
- }
665
- return String(value);
509
+ function formatLabel(key) {
510
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
666
511
  }
667
512
  function renderPopupValue(value) {
668
- if (value === null || value === void 0) {
669
- return '<span class="zenit-popup-empty">Sin datos</span>';
670
- }
513
+ if (value === null || value === void 0) return '<span style="color:#94a3b8;">Sin datos</span>';
671
514
  if (value instanceof Date) {
672
- return `<span>${escapeHtml(value.toLocaleDateString("es-GT"))}</span>`;
515
+ return escapeHtml(value.toLocaleDateString("es-GT"));
673
516
  }
674
517
  if (typeof value === "number") {
675
- return `<span>${escapeHtml(value.toLocaleString("es-GT"))}</span>`;
518
+ return escapeHtml(value.toLocaleString("es-GT"));
676
519
  }
677
520
  if (typeof value === "string") {
678
521
  const trimmed = value.trim();
679
- const isLikelyDate = /^\d{4}-\d{2}-\d{2}/.test(trimmed) || trimmed.includes("T");
680
- if (isLikelyDate) {
681
- const parsed = Date.parse(trimmed);
682
- if (!Number.isNaN(parsed)) {
683
- return `<span>${escapeHtml(new Date(parsed).toLocaleDateString("es-GT"))}</span>`;
684
- }
685
- }
522
+ if (!trimmed) return '<span style="color:#94a3b8;">Sin datos</span>';
523
+ return escapeHtml(trimmed);
524
+ }
525
+ if (typeof value === "object") {
686
526
  try {
687
- const parsedUrl = new URL(trimmed);
688
- if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
689
- const safeHref = escapeHtml(parsedUrl.toString());
690
- return `<a class="zenit-popup-link" href="${safeHref}" target="_blank" rel="noopener noreferrer">${safeHref}</a>`;
691
- }
527
+ return `<pre style="margin:0; white-space:pre-wrap; font-size:11px; background:#f8fafc; border:1px solid #e2e8f0; padding:6px 8px; border-radius:6px;">${escapeHtml(
528
+ JSON.stringify(value, null, 2)
529
+ )}</pre>`;
692
530
  } catch {
531
+ return escapeHtml(String(value));
693
532
  }
694
- return `<span>${escapeHtml(trimmed || value)}</span>`;
695
- }
696
- if (typeof value === "object") {
697
- const json = safeJsonStringify(value);
698
- return `<pre class="zenit-popup-pre">${escapeHtml(json)}</pre>`;
699
533
  }
700
- return `<span>${escapeHtml(String(value))}</span>`;
534
+ return escapeHtml(String(value));
535
+ }
536
+ function extractPopupHeader(properties) {
537
+ const entry = Object.entries(properties).find(([key, value]) => {
538
+ if (typeof value !== "string") return false;
539
+ const normalized = key.trim().toLowerCase();
540
+ return POPUP_HEADER_KEYS.includes(normalized) && value.trim().length > 0;
541
+ });
542
+ return entry ? entry[1].trim() : null;
701
543
  }
702
544
  function shouldIncludePopupEntry(key, value) {
703
545
  if (!key) return false;
@@ -709,118 +551,480 @@ function shouldIncludePopupEntry(key, value) {
709
551
  if (typeof value === "string" && !value.trim()) return false;
710
552
  return true;
711
553
  }
712
- function isDescriptionKey(key) {
713
- const normalized = key.trim().toLowerCase();
714
- return DESCRIPTION_KEYS.has(normalized);
554
+ function createPopupContent(properties) {
555
+ const headerText = extractPopupHeader(properties);
556
+ const entries = Object.entries(properties).filter(([key, value]) => {
557
+ if (!shouldIncludePopupEntry(key, value)) return false;
558
+ if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) return false;
559
+ return true;
560
+ });
561
+ if (entries.length === 0) {
562
+ return '<div style="padding:8px 0; color:#64748b; text-align:center;">Sin datos disponibles</div>';
563
+ }
564
+ const headerHtml = headerText ? `<div style="font-weight:700; font-size:14px; margin-bottom:8px; color:#0f172a;">${escapeHtml(
565
+ headerText
566
+ )}</div>` : "";
567
+ const rowsHtml = entries.map(([key, value]) => {
568
+ const label = escapeHtml(formatLabel(key));
569
+ const valueHtml = renderPopupValue(value);
570
+ return `
571
+ <div style="display:grid; grid-template-columns:minmax(90px, 35%) 1fr; gap:8px; padding:6px 0; border-bottom:1px solid #e2e8f0;">
572
+ <div style="font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; color:#64748b;">${label}</div>
573
+ <div style="font-size:13px; color:#0f172a; word-break:break-word;">${valueHtml}</div>
574
+ </div>
575
+ `;
576
+ }).join("");
577
+ return `<div>${headerHtml}${rowsHtml}</div>`;
715
578
  }
716
- function extractPopupHeader(properties) {
717
- if (!properties) return null;
718
- const entry = Object.entries(properties).find(
719
- (candidate) => {
720
- const [key, value] = candidate;
721
- return POPUP_HEADER_KEYS.includes(key.trim().toLowerCase()) && typeof value === "string" && value.trim().length > 0;
579
+ function isPolygonType(layerType, geometryType) {
580
+ const candidate = (layerType ?? geometryType ?? "").toLowerCase();
581
+ return candidate === "polygon" || candidate === "multipolygon";
582
+ }
583
+ function calculateZoomBasedOpacity(zoom, baseOpacity, layerType, geometryType) {
584
+ if (!isPolygonType(layerType, geometryType)) return clampOpacity4(baseOpacity);
585
+ const minZoom = 11;
586
+ const maxZoom = 15;
587
+ const minFactor = 0.3;
588
+ if (maxZoom <= minZoom) return clampOpacity4(baseOpacity * minFactor);
589
+ const t = clampNumber2((zoom - minZoom) / (maxZoom - minZoom), 0, 1);
590
+ const factor = 1 - (1 - minFactor) * t;
591
+ return clampOpacity4(baseOpacity * factor);
592
+ }
593
+ function layerStyleToLeaflet(options) {
594
+ const { baseOpacity, zoom, layerStyle, geometryType, layerType } = options;
595
+ const sanitizedOpacity = clampOpacity4(baseOpacity);
596
+ const zoomOpacity = calculateZoomBasedOpacity(zoom, sanitizedOpacity, layerType, geometryType);
597
+ const styleFillOpacity = typeof layerStyle?.fillOpacity === "number" ? clampOpacity4(layerStyle.fillOpacity) : 0.8;
598
+ return {
599
+ color: layerStyle?.color ?? layerStyle?.fillColor ?? "#2563eb",
600
+ weight: layerStyle?.weight ?? 2,
601
+ fillColor: layerStyle?.fillColor ?? layerStyle?.color ?? "#2563eb",
602
+ opacity: clampOpacity4(Math.max(0.35, zoomOpacity * 0.9)),
603
+ fillOpacity: clampOpacity4(zoomOpacity * styleFillOpacity)
604
+ };
605
+ }
606
+ function getRgbFromColor(color) {
607
+ const trimmed = color.trim();
608
+ if (trimmed.startsWith("#")) {
609
+ const hex = trimmed.replace("#", "");
610
+ const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
611
+ if (expanded.length === 6) {
612
+ const r = parseInt(expanded.slice(0, 2), 16);
613
+ const g = parseInt(expanded.slice(2, 4), 16);
614
+ const b = parseInt(expanded.slice(4, 6), 16);
615
+ return { r, g, b };
616
+ }
617
+ }
618
+ const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
619
+ if (rgbMatch) {
620
+ const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
621
+ if ([r, g, b].every((value) => Number.isFinite(value))) {
622
+ return { r, g, b };
623
+ }
624
+ }
625
+ return null;
626
+ }
627
+ function getLabelTextStyles(color) {
628
+ const rgb = getRgbFromColor(color);
629
+ if (!rgb) {
630
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
631
+ }
632
+ const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
633
+ if (luminance > 0.6) {
634
+ return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
635
+ }
636
+ return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
637
+ }
638
+ function withAlpha(color, alpha) {
639
+ const trimmed = color.trim();
640
+ if (trimmed.startsWith("#")) {
641
+ const hex = trimmed.replace("#", "");
642
+ const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
643
+ if (expanded.length === 6) {
644
+ const r = parseInt(expanded.slice(0, 2), 16);
645
+ const g = parseInt(expanded.slice(2, 4), 16);
646
+ const b = parseInt(expanded.slice(4, 6), 16);
647
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
648
+ }
649
+ }
650
+ if (trimmed.startsWith("rgb(")) {
651
+ const inner = trimmed.slice(4, -1);
652
+ return `rgba(${inner}, ${alpha})`;
653
+ }
654
+ if (trimmed.startsWith("rgba(")) {
655
+ const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
656
+ return `rgba(${inner.join(", ")}, ${alpha})`;
657
+ }
658
+ return color;
659
+ }
660
+ function createCustomIcon(label, opacity, color) {
661
+ const size = 60;
662
+ const innerSize = 44;
663
+ const textStyles = getLabelTextStyles(color);
664
+ const safeLabel = escapeHtml(label);
665
+ const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
666
+ const innerBackground = withAlpha(color, 0.9);
667
+ return L2.divIcon({
668
+ className: "zenit-label-marker",
669
+ iconSize: [size, size],
670
+ iconAnchor: [size / 2, size / 2],
671
+ html: `
672
+ <div
673
+ title="${safeLabel}"
674
+ style="
675
+ width:${size}px;
676
+ height:${size}px;
677
+ border-radius:9999px;
678
+ background:rgba(255, 255, 255, 0.95);
679
+ border:3px solid rgba(255, 255, 255, 1);
680
+ display:flex;
681
+ align-items:center;
682
+ justify-content:center;
683
+ opacity:${clampedOpacity};
684
+ box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
685
+ pointer-events:none;
686
+ "
687
+ >
688
+ <div
689
+ style="
690
+ width:${innerSize}px;
691
+ height:${innerSize}px;
692
+ border-radius:9999px;
693
+ background:${innerBackground};
694
+ display:flex;
695
+ align-items:center;
696
+ justify-content:center;
697
+ box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
698
+ "
699
+ >
700
+ <span
701
+ style="
702
+ color:${textStyles.color};
703
+ font-size:20px;
704
+ font-weight:800;
705
+ text-shadow:${textStyles.shadow};
706
+ "
707
+ >
708
+ ${safeLabel}
709
+ </span>
710
+ </div>
711
+ </div>
712
+ `
713
+ });
714
+ }
715
+ function createLocationIcon() {
716
+ return L2.divIcon({
717
+ className: "zenit-location-marker",
718
+ iconSize: [18, 18],
719
+ iconAnchor: [9, 9],
720
+ html: `
721
+ <div style="width:18px;height:18px;border-radius:9999px;background:#2563eb;border:2px solid #fff;box-shadow:0 0 0 4px rgba(37, 99, 235, 0.25);"></div>
722
+ `
723
+ });
724
+ }
725
+
726
+ // src/react/map/location-control.tsx
727
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
728
+ var LOCATION_STYLE_ID = "zenit-location-control-styles";
729
+ var LOCATION_STYLES = `
730
+ .zenit-location-control {
731
+ display: flex;
732
+ flex-direction: column;
733
+ gap: 8px;
734
+ }
735
+
736
+ .zenit-location-button {
737
+ width: 42px;
738
+ height: 42px;
739
+ border-radius: 12px;
740
+ border: none;
741
+ background: #ffffff;
742
+ color: #0f172a;
743
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.2);
744
+ display: inline-flex;
745
+ align-items: center;
746
+ justify-content: center;
747
+ cursor: pointer;
748
+ position: relative;
749
+ }
750
+
751
+ .zenit-location-button.zenit-location-button--tracking {
752
+ animation: zenitLocationPulse 1.8s infinite;
753
+ }
754
+
755
+ .zenit-location-button:hover {
756
+ background: #f8fafc;
757
+ }
758
+
759
+ .zenit-location-badge {
760
+ position: absolute;
761
+ top: -6px;
762
+ right: -6px;
763
+ width: 18px;
764
+ height: 18px;
765
+ border-radius: 999px;
766
+ background: #ef4444;
767
+ color: #fff;
768
+ font-size: 11px;
769
+ font-weight: 700;
770
+ display: inline-flex;
771
+ align-items: center;
772
+ justify-content: center;
773
+ box-shadow: 0 4px 10px rgba(239, 68, 68, 0.35);
774
+ }
775
+
776
+ .zenit-location-error {
777
+ background: rgba(15, 23, 42, 0.92);
778
+ color: #f8fafc;
779
+ border-radius: 10px;
780
+ padding: 8px 10px;
781
+ font-size: 12px;
782
+ max-width: 200px;
783
+ box-shadow: 0 10px 24px rgba(15, 23, 42, 0.3);
784
+ }
785
+
786
+ @keyframes zenitLocationPulse {
787
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.35); }
788
+ 70% { box-shadow: 0 0 0 12px rgba(16, 185, 129, 0); }
789
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
790
+ }
791
+ `;
792
+ var LocateIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
793
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "3" }),
794
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "2", x2: "12", y2: "5" }),
795
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "22" }),
796
+ /* @__PURE__ */ jsx2("line", { x1: "2", y1: "12", x2: "5", y2: "12" }),
797
+ /* @__PURE__ */ jsx2("line", { x1: "19", y1: "12", x2: "22", y2: "12" })
798
+ ] });
799
+ var LocationControl = ({ position = "bottomleft", zoom = 16 }) => {
800
+ const map = useMap();
801
+ const controlRef = useRef2(null);
802
+ const hasCenteredRef = useRef2(false);
803
+ const { isTracking, location, error, toggleTracking, clearError } = useGeolocation();
804
+ useEffect2(() => {
805
+ if (typeof document === "undefined") return;
806
+ if (document.getElementById(LOCATION_STYLE_ID)) return;
807
+ const styleTag = document.createElement("style");
808
+ styleTag.id = LOCATION_STYLE_ID;
809
+ styleTag.textContent = LOCATION_STYLES;
810
+ document.head.appendChild(styleTag);
811
+ }, []);
812
+ useEffect2(() => {
813
+ const control = L3.control({ position });
814
+ control.onAdd = () => {
815
+ const container = L3.DomUtil.create("div", "zenit-location-control");
816
+ L3.DomEvent.disableClickPropagation(container);
817
+ controlRef.current = container;
818
+ return container;
819
+ };
820
+ control.addTo(map);
821
+ return () => {
822
+ control.remove();
823
+ controlRef.current = null;
824
+ };
825
+ }, [map, position]);
826
+ useEffect2(() => {
827
+ if (!location || !isTracking) return;
828
+ if (hasCenteredRef.current) return;
829
+ hasCenteredRef.current = true;
830
+ map.flyTo([location.lat, location.lon], zoom, { animate: true });
831
+ }, [isTracking, location, map, zoom]);
832
+ const markerIcon = useMemo(() => createLocationIcon(), []);
833
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
834
+ controlRef.current && createPortal(
835
+ /* @__PURE__ */ jsxs2("div", { children: [
836
+ /* @__PURE__ */ jsxs2(
837
+ "button",
838
+ {
839
+ type: "button",
840
+ className: `zenit-location-button${isTracking ? " zenit-location-button--tracking" : ""}`,
841
+ onClick: () => {
842
+ if (error) {
843
+ clearError();
844
+ }
845
+ toggleTracking();
846
+ },
847
+ "aria-label": isTracking ? "Detener ubicaci\xF3n" : "Mostrar mi ubicaci\xF3n",
848
+ children: [
849
+ /* @__PURE__ */ jsx2(LocateIcon, {}),
850
+ error && /* @__PURE__ */ jsx2("span", { className: "zenit-location-badge", children: "!" })
851
+ ]
852
+ }
853
+ ),
854
+ error && /* @__PURE__ */ jsx2("div", { className: "zenit-location-error", children: error.message || "No se pudo acceder a tu ubicaci\xF3n." })
855
+ ] }),
856
+ controlRef.current
857
+ ),
858
+ location && /* @__PURE__ */ jsxs2(Fragment2, { children: [
859
+ /* @__PURE__ */ jsx2(Marker, { position: [location.lat, location.lon], icon: markerIcon }),
860
+ /* @__PURE__ */ jsx2(
861
+ Circle,
862
+ {
863
+ center: [location.lat, location.lon],
864
+ radius: location.accuracy,
865
+ pathOptions: { color: "#2563eb", fillColor: "#2563eb", fillOpacity: 0.15 }
866
+ }
867
+ )
868
+ ] })
869
+ ] });
870
+ };
871
+
872
+ // src/react/map/map-handlers.tsx
873
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
874
+ import { useMap as useMap2 } from "react-leaflet";
875
+ function computeBBoxFromGeojson(geojson) {
876
+ if (!geojson || !Array.isArray(geojson.features)) return null;
877
+ const coords = [];
878
+ const collect = (candidate) => {
879
+ if (!Array.isArray(candidate)) return;
880
+ if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
881
+ coords.push([candidate[0], candidate[1]]);
882
+ return;
722
883
  }
884
+ candidate.forEach((item) => collect(item));
885
+ };
886
+ geojson.features.forEach((feature) => {
887
+ collect(feature.geometry?.coordinates);
888
+ });
889
+ if (coords.length === 0) return null;
890
+ const [firstLon, firstLat] = coords[0];
891
+ const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
892
+ coords.forEach(([lon, lat]) => {
893
+ bbox.minLon = Math.min(bbox.minLon, lon);
894
+ bbox.minLat = Math.min(bbox.minLat, lat);
895
+ bbox.maxLon = Math.max(bbox.maxLon, lon);
896
+ bbox.maxLat = Math.max(bbox.maxLat, lat);
897
+ });
898
+ return bbox;
899
+ }
900
+ function mergeBBoxes(bboxes) {
901
+ const valid = bboxes.filter((bbox) => !!bbox);
902
+ if (valid.length === 0) return null;
903
+ const first = valid[0];
904
+ return valid.slice(1).reduce(
905
+ (acc, bbox) => ({
906
+ minLon: Math.min(acc.minLon, bbox.minLon),
907
+ minLat: Math.min(acc.minLat, bbox.minLat),
908
+ maxLon: Math.max(acc.maxLon, bbox.maxLon),
909
+ maxLat: Math.max(acc.maxLat, bbox.maxLat)
910
+ }),
911
+ { ...first }
723
912
  );
724
- if (!entry) return null;
725
- return entry[1].trim();
726
913
  }
727
- function formatLabel(key) {
728
- return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").trim();
914
+ var BBoxZoomHandler = ({
915
+ bbox,
916
+ geojson,
917
+ autoGeojson = [],
918
+ enabled = true
919
+ }) => {
920
+ const map = useMap2();
921
+ const lastAppliedBBox = useRef3(null);
922
+ const lastUserInteracted = useRef3(false);
923
+ useEffect3(() => {
924
+ const handleInteraction = () => {
925
+ lastUserInteracted.current = true;
926
+ };
927
+ map.on("dragstart", handleInteraction);
928
+ map.on("zoomstart", handleInteraction);
929
+ return () => {
930
+ map.off("dragstart", handleInteraction);
931
+ map.off("zoomstart", handleInteraction);
932
+ };
933
+ }, [map]);
934
+ useEffect3(() => {
935
+ if (!enabled) return;
936
+ let resolvedBBox = bbox ?? null;
937
+ if (!resolvedBBox && geojson) {
938
+ resolvedBBox = computeBBoxFromGeojson(geojson);
939
+ }
940
+ if (!resolvedBBox && autoGeojson.length > 0) {
941
+ const bboxes = autoGeojson.map((collection) => computeBBoxFromGeojson(collection));
942
+ resolvedBBox = mergeBBoxes(bboxes);
943
+ }
944
+ if (!resolvedBBox) return;
945
+ const serialized = JSON.stringify(resolvedBBox);
946
+ if (lastAppliedBBox.current === serialized) return;
947
+ if (lastUserInteracted.current && !bbox && !geojson) {
948
+ lastUserInteracted.current = false;
949
+ return;
950
+ }
951
+ const bounds = [
952
+ [resolvedBBox.minLat, resolvedBBox.minLon],
953
+ [resolvedBBox.maxLat, resolvedBBox.maxLon]
954
+ ];
955
+ map.fitBounds(bounds, { padding: [12, 12] });
956
+ lastAppliedBBox.current = serialized;
957
+ }, [autoGeojson, bbox, enabled, geojson, map]);
958
+ return null;
959
+ };
960
+ var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
961
+ const map = useMap2();
962
+ useEffect3(() => {
963
+ const handleZoom = () => {
964
+ onZoomChange(map.getZoom());
965
+ };
966
+ map.on("zoomend", handleZoom);
967
+ handleZoom();
968
+ return () => {
969
+ map.off("zoomend", handleZoom);
970
+ };
971
+ }, [map, onZoomChange]);
972
+ return null;
973
+ };
974
+ var MapInstanceBridge = ({ onReady }) => {
975
+ const map = useMap2();
976
+ useEffect3(() => {
977
+ onReady(map);
978
+ }, [map, onReady]);
979
+ return null;
980
+ };
981
+
982
+ // src/react/ZenitMap.tsx
983
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
984
+ var DEFAULT_CENTER = [0, 0];
985
+ var DEFAULT_ZOOM = 3;
986
+ var LABELS_PANE_NAME = "zenit-labels-pane";
987
+ function isRecord(value) {
988
+ return typeof value === "object" && value !== null;
989
+ }
990
+ function isGeoJsonFeatureCollection(value) {
991
+ if (!isRecord(value)) return false;
992
+ const features = value.features;
993
+ if (!Array.isArray(features)) return false;
994
+ const type = value.type;
995
+ return type === void 0 || type === "FeatureCollection";
729
996
  }
730
- function createPopupContent(properties) {
731
- const headerText = extractPopupHeader(properties);
732
- const entries = Object.entries(properties).filter(([key, value]) => {
733
- if (!shouldIncludePopupEntry(key, value)) return false;
734
- if (headerText && POPUP_HEADER_KEYS.includes(key.trim().toLowerCase())) {
735
- return false;
736
- }
737
- return true;
738
- });
739
- if (entries.length === 0) {
740
- return `<div class="zenit-popup-card"><div class="zenit-popup-empty">Sin datos disponibles</div></div>`;
741
- }
742
- const descriptionEntry = entries.find(([key]) => isDescriptionKey(key));
743
- const otherEntries = entries.filter(([key]) => !isDescriptionKey(key));
744
- let rowsHtml = "";
745
- if (descriptionEntry) {
746
- const [key, value] = descriptionEntry;
747
- const label = escapeHtml(formatLabel(key));
748
- const valueHtml = renderPopupValue(value);
749
- rowsHtml += `
750
- <div class="zenit-popup-row zenit-popup-description">
751
- <div class="zenit-popup-label">${label}</div>
752
- <div class="zenit-popup-value">${valueHtml}</div>
753
- </div>
754
- `;
997
+ function extractGeoJsonFeatureCollection(value) {
998
+ if (isRecord(value) && "data" in value) {
999
+ const data = value.data;
1000
+ return isGeoJsonFeatureCollection(data) ? data : null;
755
1001
  }
756
- rowsHtml += otherEntries.map(([key, value]) => {
757
- const label = escapeHtml(formatLabel(key));
758
- const valueHtml = renderPopupValue(value);
759
- return `
760
- <div class="zenit-popup-row">
761
- <div class="zenit-popup-label">${label}</div>
762
- <div class="zenit-popup-value">${valueHtml}</div>
763
- </div>
764
- `;
765
- }).join("");
766
- const headerHtml = headerText ? `<div class="zenit-popup-header"><div class="zenit-popup-title">${escapeHtml(
767
- headerText
768
- )}</div></div>` : "";
769
- return `<div class="zenit-popup-card">${headerHtml}${rowsHtml}</div>`;
1002
+ return isGeoJsonFeatureCollection(value) ? value : null;
770
1003
  }
771
- function withAlpha(color, alpha) {
772
- const trimmed = color.trim();
773
- if (trimmed.startsWith("#")) {
774
- const hex = trimmed.replace("#", "");
775
- const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
776
- if (expanded.length === 6) {
777
- const r = parseInt(expanded.slice(0, 2), 16);
778
- const g = parseInt(expanded.slice(2, 4), 16);
779
- const b = parseInt(expanded.slice(4, 6), 16);
780
- return `rgba(${r}, ${g}, ${b}, ${alpha})`;
781
- }
782
- }
783
- if (trimmed.startsWith("rgb(")) {
784
- const inner = trimmed.slice(4, -1);
785
- return `rgba(${inner}, ${alpha})`;
786
- }
787
- if (trimmed.startsWith("rgba(")) {
788
- const inner = trimmed.slice(5, -1).split(",").slice(0, 3).map((value) => value.trim());
789
- return `rgba(${inner.join(", ")}, ${alpha})`;
790
- }
791
- return color;
1004
+ function getFeatureLayerId(feature) {
1005
+ const layerId = feature?.properties?.__zenit_layerId ?? feature?.properties?.layerId ?? feature?.properties?.layer_id;
1006
+ if (layerId === void 0 || layerId === null) return null;
1007
+ return layerId;
792
1008
  }
793
- function getRgbFromColor(color) {
794
- const trimmed = color.trim();
795
- if (trimmed.startsWith("#")) {
796
- const hex = trimmed.replace("#", "");
797
- const expanded = hex.length === 3 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
798
- if (expanded.length === 6) {
799
- const r = parseInt(expanded.slice(0, 2), 16);
800
- const g = parseInt(expanded.slice(2, 4), 16);
801
- const b = parseInt(expanded.slice(4, 6), 16);
802
- return { r, g, b };
803
- }
1009
+ var DESCRIPTION_KEYS = /* @__PURE__ */ new Set(["descripcion", "description"]);
1010
+ function normalizeDescriptionValue(value) {
1011
+ if (value === void 0 || value === null) return null;
1012
+ if (typeof value === "string") {
1013
+ const trimmed = value.trim();
1014
+ return trimmed ? trimmed : null;
804
1015
  }
805
- const rgbMatch = trimmed.match(/rgba?\(([^)]+)\)/i);
806
- if (rgbMatch) {
807
- const [r, g, b] = rgbMatch[1].split(",").map((value) => parseFloat(value.trim())).slice(0, 3);
808
- if ([r, g, b].every((value) => Number.isFinite(value))) {
809
- return { r, g, b };
810
- }
1016
+ if (typeof value === "number" || typeof value === "boolean") {
1017
+ return String(value);
811
1018
  }
812
1019
  return null;
813
1020
  }
814
- function getLabelTextStyles(color) {
815
- const rgb = getRgbFromColor(color);
816
- if (!rgb) {
817
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.6)" };
818
- }
819
- const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
820
- if (luminance > 0.6) {
821
- return { color: "#0f172a", shadow: "0 1px 2px rgba(255, 255, 255, 0.7)" };
822
- }
823
- return { color: "#ffffff", shadow: "0 1px 2px rgba(0, 0, 0, 0.4)" };
1021
+ function extractDescriptionValue(properties) {
1022
+ if (!properties) return null;
1023
+ const matches = Object.entries(properties).find(
1024
+ ([key]) => DESCRIPTION_KEYS.has(key.toLowerCase())
1025
+ );
1026
+ if (!matches) return null;
1027
+ return normalizeDescriptionValue(matches[1]);
824
1028
  }
825
1029
  function getFeatureStyleOverrides(feature) {
826
1030
  const candidate = feature?.properties?._style;
@@ -839,25 +1043,6 @@ function buildFeaturePopupHtml(feature) {
839
1043
  const rendered = createPopupContent(properties);
840
1044
  return rendered ? rendered : null;
841
1045
  }
842
- var POINT_GEOMETRY_TYPES = /* @__PURE__ */ new Set(["Point", "MultiPoint"]);
843
- function getGeometryType(feature) {
844
- const t = feature?.geometry?.type;
845
- return typeof t === "string" ? t : null;
846
- }
847
- function isPointGeometry(feature) {
848
- const geometryType = getGeometryType(feature);
849
- return geometryType !== null && POINT_GEOMETRY_TYPES.has(geometryType);
850
- }
851
- function isNonPointGeometry(feature) {
852
- const geometryType = getGeometryType(feature);
853
- return geometryType !== null && !POINT_GEOMETRY_TYPES.has(geometryType);
854
- }
855
- function buildFeatureCollection(features) {
856
- return {
857
- type: "FeatureCollection",
858
- features
859
- };
860
- }
861
1046
  function pickIntersectFeature(baseFeature, candidates) {
862
1047
  if (!Array.isArray(candidates) || candidates.length === 0) return null;
863
1048
  const baseId = baseFeature?.id;
@@ -884,80 +1069,6 @@ function normalizeCenterTuple(center) {
884
1069
  }
885
1070
  return null;
886
1071
  }
887
- var FitToBounds = ({ bbox }) => {
888
- const mapInstance = useMap();
889
- const lastAppliedBBox = useRef(null);
890
- useEffect(() => {
891
- const targetBBox = bbox;
892
- if (!targetBBox) return;
893
- const serialized = JSON.stringify(targetBBox);
894
- if (lastAppliedBBox.current === serialized) return;
895
- const bounds = [
896
- [targetBBox.minLat, targetBBox.minLon],
897
- [targetBBox.maxLat, targetBBox.maxLon]
898
- ];
899
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
900
- lastAppliedBBox.current = serialized;
901
- }, [bbox, mapInstance]);
902
- return null;
903
- };
904
- var AutoFitToBounds = ({
905
- bbox,
906
- enabled = true
907
- }) => {
908
- const mapInstance = useMap();
909
- const lastAutoBBoxApplied = useRef(null);
910
- const lastUserInteracted = useRef(false);
911
- useEffect(() => {
912
- if (!enabled) return;
913
- const handleInteraction = () => {
914
- lastUserInteracted.current = true;
915
- };
916
- mapInstance.on("dragstart", handleInteraction);
917
- mapInstance.on("zoomstart", handleInteraction);
918
- return () => {
919
- mapInstance.off("dragstart", handleInteraction);
920
- mapInstance.off("zoomstart", handleInteraction);
921
- };
922
- }, [enabled, mapInstance]);
923
- useEffect(() => {
924
- if (!enabled) return;
925
- if (!bbox) return;
926
- const serialized = JSON.stringify(bbox);
927
- if (lastAutoBBoxApplied.current === serialized) return;
928
- if (lastUserInteracted.current) {
929
- lastUserInteracted.current = false;
930
- }
931
- const bounds = [
932
- [bbox.minLat, bbox.minLon],
933
- [bbox.maxLat, bbox.maxLon]
934
- ];
935
- mapInstance.fitBounds(bounds, { padding: [12, 12] });
936
- lastAutoBBoxApplied.current = serialized;
937
- }, [bbox, enabled, mapInstance]);
938
- return null;
939
- };
940
- var ZoomBasedOpacityHandler = ({ onZoomChange }) => {
941
- const mapInstance = useMap();
942
- useEffect(() => {
943
- const handleZoom = () => {
944
- onZoomChange(mapInstance.getZoom());
945
- };
946
- mapInstance.on("zoomend", handleZoom);
947
- handleZoom();
948
- return () => {
949
- mapInstance.off("zoomend", handleZoom);
950
- };
951
- }, [mapInstance, onZoomChange]);
952
- return null;
953
- };
954
- var MapInstanceBridge = ({ onReady }) => {
955
- const mapInstance = useMap();
956
- useEffect(() => {
957
- onReady(mapInstance);
958
- }, [mapInstance, onReady]);
959
- return null;
960
- };
961
1072
  var ZenitMap = forwardRef(({
962
1073
  client,
963
1074
  mapId,
@@ -983,21 +1094,21 @@ var ZenitMap = forwardRef(({
983
1094
  onZoomChange,
984
1095
  onMapReady
985
1096
  }, ref) => {
986
- const [map, setMap] = useState(null);
987
- const [layers, setLayers] = useState([]);
988
- const [effectiveStates, setEffectiveStates] = useState([]);
989
- const [loadingMap, setLoadingMap] = useState(false);
990
- const [mapError, setMapError] = useState(null);
991
- const [mapInstance, setMapInstance] = useState(null);
992
- const [panesReady, setPanesReady] = useState(false);
993
- const [currentZoom, setCurrentZoom] = useState(initialZoom ?? DEFAULT_ZOOM);
994
- const [isPopupOpen, setIsPopupOpen] = useState(false);
995
- const [isMobile, setIsMobile] = useState(() => {
1097
+ const [map, setMap] = useState2(null);
1098
+ const [layers, setLayers] = useState2([]);
1099
+ const [effectiveStates, setEffectiveStates] = useState2([]);
1100
+ const [loadingMap, setLoadingMap] = useState2(false);
1101
+ const [mapError, setMapError] = useState2(null);
1102
+ const [mapInstance, setMapInstance] = useState2(null);
1103
+ const [panesReady, setPanesReady] = useState2(false);
1104
+ const [currentZoom, setCurrentZoom] = useState2(initialZoom ?? DEFAULT_ZOOM);
1105
+ const [isPopupOpen, setIsPopupOpen] = useState2(false);
1106
+ const [isMobile, setIsMobile] = useState2(() => {
996
1107
  if (typeof window === "undefined") return false;
997
1108
  return window.matchMedia("(max-width: 768px)").matches;
998
1109
  });
999
- const normalizedLayers = useMemo(() => normalizeMapLayers(map), [map]);
1000
- useEffect(() => {
1110
+ const normalizedLayers = useMemo2(() => normalizeMapLayers(map), [map]);
1111
+ useEffect4(() => {
1001
1112
  if (typeof window === "undefined") return;
1002
1113
  const mql = window.matchMedia("(max-width: 768px)");
1003
1114
  const onChange = (e) => {
@@ -1015,17 +1126,17 @@ var ZenitMap = forwardRef(({
1015
1126
  }
1016
1127
  return;
1017
1128
  }, []);
1018
- useEffect(() => {
1129
+ useEffect4(() => {
1019
1130
  if (featureInfoMode === "popup") {
1020
1131
  ensurePopupStyles();
1021
1132
  }
1022
1133
  }, [featureInfoMode]);
1023
- useEffect(() => {
1134
+ useEffect4(() => {
1024
1135
  if (featureInfoMode !== "popup") {
1025
1136
  setIsPopupOpen(false);
1026
1137
  }
1027
1138
  }, [featureInfoMode]);
1028
- useEffect(() => {
1139
+ useEffect4(() => {
1029
1140
  if (!mapInstance) return;
1030
1141
  const popupPane = mapInstance.getPane("popupPane");
1031
1142
  if (popupPane) {
@@ -1034,7 +1145,7 @@ var ZenitMap = forwardRef(({
1034
1145
  const labelsPane = mapInstance.getPane(LABELS_PANE_NAME) ?? mapInstance.createPane(LABELS_PANE_NAME);
1035
1146
  labelsPane.style.zIndex = "600";
1036
1147
  }, [mapInstance]);
1037
- useEffect(() => {
1148
+ useEffect4(() => {
1038
1149
  if (!mapInstance) return;
1039
1150
  const handlePopupOpen = () => setIsPopupOpen(true);
1040
1151
  const handlePopupClose = () => setIsPopupOpen(false);
@@ -1045,7 +1156,7 @@ var ZenitMap = forwardRef(({
1045
1156
  mapInstance.off("popupclose", handlePopupClose);
1046
1157
  };
1047
1158
  }, [mapInstance]);
1048
- const layerStyleIndex = useMemo(() => {
1159
+ const layerStyleIndex = useMemo2(() => {
1049
1160
  const index = /* @__PURE__ */ new Map();
1050
1161
  (map?.mapLayers ?? []).forEach((entry) => {
1051
1162
  const layerStyle = entry.layer?.style ?? entry.mapLayer?.layer?.style ?? entry.style ?? null;
@@ -1056,7 +1167,7 @@ var ZenitMap = forwardRef(({
1056
1167
  });
1057
1168
  return index;
1058
1169
  }, [map]);
1059
- const labelKeyIndex = useMemo(() => {
1170
+ const labelKeyIndex = useMemo2(() => {
1060
1171
  const index = /* @__PURE__ */ new Map();
1061
1172
  normalizedLayers.forEach((entry) => {
1062
1173
  const label = entry.layer?.label ?? entry.mapLayer?.label ?? entry.mapLayer.layerConfig?.label;
@@ -1066,7 +1177,7 @@ var ZenitMap = forwardRef(({
1066
1177
  });
1067
1178
  return index;
1068
1179
  }, [normalizedLayers]);
1069
- const layerMetaIndex = useMemo(() => {
1180
+ const layerMetaIndex = useMemo2(() => {
1070
1181
  const index = /* @__PURE__ */ new Map();
1071
1182
  normalizedLayers.forEach((entry) => {
1072
1183
  index.set(String(entry.layerId), {
@@ -1076,7 +1187,7 @@ var ZenitMap = forwardRef(({
1076
1187
  });
1077
1188
  return index;
1078
1189
  }, [normalizedLayers]);
1079
- const overlayStyleFunction = useMemo(() => {
1190
+ const overlayStyleFunction = useMemo2(() => {
1080
1191
  return (feature) => {
1081
1192
  const featureLayerId = getFeatureLayerId(feature);
1082
1193
  const featureStyleOverrides = getFeatureStyleOverrides(feature);
@@ -1095,11 +1206,15 @@ var ZenitMap = forwardRef(({
1095
1206
  return defaultOptions;
1096
1207
  };
1097
1208
  }, [layerStyleIndex, mapLayers, overlayStyle]);
1098
- const [baseStates, setBaseStates] = useState([]);
1099
- const [mapOverrides, setMapOverrides] = useState([]);
1100
- const [controlOverrides, setControlOverrides] = useState([]);
1101
- const [uiOverrides, setUiOverrides] = useState([]);
1102
- useEffect(() => {
1209
+ const overlayStyleFn = useCallback2(
1210
+ (feature, _layerType, _baseOpacity) => overlayStyleFunction(feature),
1211
+ [overlayStyleFunction]
1212
+ );
1213
+ const [baseStates, setBaseStates] = useState2([]);
1214
+ const [mapOverrides, setMapOverrides] = useState2([]);
1215
+ const [controlOverrides, setControlOverrides] = useState2([]);
1216
+ const [uiOverrides, setUiOverrides] = useState2([]);
1217
+ useEffect4(() => {
1103
1218
  let isMounted = true;
1104
1219
  setLoadingMap(true);
1105
1220
  setMapError(null);
@@ -1122,7 +1237,7 @@ var ZenitMap = forwardRef(({
1122
1237
  isMounted = false;
1123
1238
  };
1124
1239
  }, [client.maps, mapId, onError, onLoadingChange]);
1125
- useEffect(() => {
1240
+ useEffect4(() => {
1126
1241
  if (normalizedLayers.length === 0) {
1127
1242
  setLayers([]);
1128
1243
  setBaseStates([]);
@@ -1153,7 +1268,7 @@ var ZenitMap = forwardRef(({
1153
1268
  setMapOverrides(initialOverrides);
1154
1269
  setUiOverrides([]);
1155
1270
  }, [normalizedLayers]);
1156
- useEffect(() => {
1271
+ useEffect4(() => {
1157
1272
  if (!layerControls) {
1158
1273
  setControlOverrides([]);
1159
1274
  return;
@@ -1165,7 +1280,7 @@ var ZenitMap = forwardRef(({
1165
1280
  }));
1166
1281
  setControlOverrides(overrides);
1167
1282
  }, [layerControls]);
1168
- useEffect(() => {
1283
+ useEffect4(() => {
1169
1284
  if (layerStates) {
1170
1285
  return;
1171
1286
  }
@@ -1175,12 +1290,12 @@ var ZenitMap = forwardRef(({
1175
1290
  onLayerStateChange?.(reset);
1176
1291
  }
1177
1292
  }, [baseStates, effectiveStates.length, layerControls, layerStates, onLayerStateChange]);
1178
- useEffect(() => {
1293
+ useEffect4(() => {
1179
1294
  if (layerStates) {
1180
1295
  setEffectiveStates(layerStates);
1181
1296
  }
1182
1297
  }, [layerStates]);
1183
- useEffect(() => {
1298
+ useEffect4(() => {
1184
1299
  if (layerStates) {
1185
1300
  return;
1186
1301
  }
@@ -1193,11 +1308,11 @@ var ZenitMap = forwardRef(({
1193
1308
  setEffectiveStates(next);
1194
1309
  onLayerStateChange?.(next);
1195
1310
  }, [baseStates, controlOverrides, layerStates, mapOverrides, onLayerStateChange, uiOverrides]);
1196
- useEffect(() => {
1311
+ useEffect4(() => {
1197
1312
  if (!Array.isArray(layerControls) || layerControls.length > 0) return;
1198
1313
  setUiOverrides([]);
1199
1314
  }, [layerControls]);
1200
- useEffect(() => {
1315
+ useEffect4(() => {
1201
1316
  if (layerStates) {
1202
1317
  return;
1203
1318
  }
@@ -1217,18 +1332,13 @@ var ZenitMap = forwardRef(({
1217
1332
  return [...filtered, nextEntry];
1218
1333
  });
1219
1334
  };
1220
- const updateOpacityFromUi = useCallback(
1335
+ const updateOpacityFromUi = useCallback2(
1221
1336
  (layerId, uiOpacity) => {
1222
1337
  const meta = layerMetaIndex.get(String(layerId));
1223
- const zoomFactor = getLayerZoomOpacityFactor(
1338
+ const baseOpacity = clampOpacity3(uiOpacity);
1339
+ const effectiveOpacity = calculateZoomBasedOpacity(
1224
1340
  currentZoom,
1225
- meta?.layerType,
1226
- meta?.geometryType
1227
- );
1228
- const baseOpacity = clampOpacity3(uiOpacity / zoomFactor);
1229
- const effectiveOpacity = getEffectiveLayerOpacity(
1230
1341
  baseOpacity,
1231
- currentZoom,
1232
1342
  meta?.layerType,
1233
1343
  meta?.geometryType
1234
1344
  );
@@ -1261,7 +1371,7 @@ var ZenitMap = forwardRef(({
1261
1371
  },
1262
1372
  [currentZoom, effectiveStates, layerMetaIndex, layerStates, onLayerStateChange]
1263
1373
  );
1264
- const center = useMemo(() => {
1374
+ const center = useMemo2(() => {
1265
1375
  if (initialCenter) {
1266
1376
  return initialCenter;
1267
1377
  }
@@ -1272,36 +1382,30 @@ var ZenitMap = forwardRef(({
1272
1382
  return DEFAULT_CENTER;
1273
1383
  }, [initialCenter, map?.settings?.center]);
1274
1384
  const zoom = initialZoom ?? map?.settings?.zoom ?? DEFAULT_ZOOM;
1275
- useEffect(() => {
1385
+ useEffect4(() => {
1276
1386
  setCurrentZoom(zoom);
1277
1387
  }, [zoom]);
1278
- const decoratedLayers = useMemo(() => {
1388
+ const decoratedLayers = useMemo2(() => {
1279
1389
  return layers.map((layer) => ({
1280
1390
  ...layer,
1281
1391
  effective: effectiveStates.find((state) => state.layerId === layer.mapLayer.layerId),
1282
1392
  data: layerGeojson?.[layer.mapLayer.layerId] ?? layerGeojson?.[String(layer.mapLayer.layerId)] ?? null
1283
1393
  }));
1284
1394
  }, [effectiveStates, layerGeojson, layers]);
1285
- const orderedLayers = useMemo(() => {
1395
+ const orderedLayers = useMemo2(() => {
1286
1396
  return [...decoratedLayers].filter((layer) => layer.effective?.visible && layer.data).sort((a, b) => a.displayOrder - b.displayOrder);
1287
1397
  }, [decoratedLayers]);
1288
- const explicitZoomBBox = useMemo(() => {
1289
- if (zoomToBbox) return zoomToBbox;
1290
- if (zoomToGeojson) return computeBBoxFromGeojson(zoomToGeojson);
1291
- return null;
1292
- }, [zoomToBbox, zoomToGeojson]);
1293
- const autoZoomBBox = useMemo(() => {
1294
- if (explicitZoomBBox) return null;
1295
- const visibleBBoxes = orderedLayers.map((layer) => computeBBoxFromGeojson(layer.data));
1296
- return mergeBBoxes(visibleBBoxes);
1297
- }, [explicitZoomBBox, orderedLayers]);
1298
- const resolveLayerStyle = useCallback(
1398
+ const autoZoomGeojson = useMemo2(
1399
+ () => orderedLayers.map((layer) => layer.data).filter((collection) => !!collection),
1400
+ [orderedLayers]
1401
+ );
1402
+ const resolveLayerStyle = useCallback2(
1299
1403
  (layerId) => {
1300
1404
  return getStyleByLayerId(layerId, mapLayers) ?? layerStyleIndex.get(String(layerId)) ?? null;
1301
1405
  },
1302
1406
  [layerStyleIndex, mapLayers]
1303
1407
  );
1304
- const labelMarkers = useMemo(() => {
1408
+ const labelMarkers = useMemo2(() => {
1305
1409
  const markers = [];
1306
1410
  decoratedLayers.forEach((layerState) => {
1307
1411
  if (!layerState.effective?.visible) return;
@@ -1311,7 +1415,14 @@ var ZenitMap = forwardRef(({
1311
1415
  if (!data) return;
1312
1416
  const resolvedStyle = resolveLayerStyle(layerState.mapLayer.layerId);
1313
1417
  const layerColor = resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "rgba(37, 99, 235, 1)";
1314
- const opacity = layerState.effective?.opacity ?? 1;
1418
+ const meta = layerMetaIndex.get(String(layerState.mapLayer.layerId));
1419
+ const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1420
+ const opacity = calculateZoomBasedOpacity(
1421
+ currentZoom,
1422
+ baseOpacity,
1423
+ meta?.layerType,
1424
+ meta?.geometryType
1425
+ );
1315
1426
  data.features.forEach((feature, index) => {
1316
1427
  const properties = feature.properties;
1317
1428
  const value = properties?.[labelKey];
@@ -1330,8 +1441,8 @@ var ZenitMap = forwardRef(({
1330
1441
  });
1331
1442
  });
1332
1443
  return markers;
1333
- }, [decoratedLayers, labelKeyIndex, resolveLayerStyle]);
1334
- const ensureLayerPanes = useCallback(
1444
+ }, [currentZoom, decoratedLayers, labelKeyIndex, layerMetaIndex, resolveLayerStyle]);
1445
+ const ensureLayerPanes = useCallback2(
1335
1446
  (targetMap, targetLayers) => {
1336
1447
  const baseZIndex = 400;
1337
1448
  targetLayers.forEach((layer) => {
@@ -1347,7 +1458,7 @@ var ZenitMap = forwardRef(({
1347
1458
  },
1348
1459
  []
1349
1460
  );
1350
- const handleMapReady = useCallback(
1461
+ const handleMapReady = useCallback2(
1351
1462
  (instance) => {
1352
1463
  setPanesReady(false);
1353
1464
  setMapInstance(instance);
@@ -1355,7 +1466,7 @@ var ZenitMap = forwardRef(({
1355
1466
  },
1356
1467
  [onMapReady]
1357
1468
  );
1358
- useEffect(() => {
1469
+ useEffect4(() => {
1359
1470
  if (!mapInstance) {
1360
1471
  setPanesReady(false);
1361
1472
  return;
@@ -1375,19 +1486,19 @@ var ZenitMap = forwardRef(({
1375
1486
  setPanesReady(true);
1376
1487
  }
1377
1488
  }, [mapInstance, orderedLayers, ensureLayerPanes]);
1378
- const overlayOnEachFeature = useMemo(() => {
1489
+ const overlayOnEachFeature = useMemo2(() => {
1379
1490
  return (feature, layer) => {
1380
1491
  const layerId = getFeatureLayerId(feature) ?? void 0;
1381
1492
  const geometryType = feature?.geometry?.type;
1382
- const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof L.CircleMarker;
1383
- const originalStyle = layer instanceof L.Path ? {
1493
+ const isPointFeature = geometryType === "Point" || geometryType === "MultiPoint" || layer instanceof L4.CircleMarker;
1494
+ const originalStyle = layer instanceof L4.Path ? {
1384
1495
  color: layer.options.color,
1385
1496
  weight: layer.options.weight,
1386
1497
  fillColor: layer.options.fillColor,
1387
1498
  opacity: layer.options.opacity,
1388
1499
  fillOpacity: layer.options.fillOpacity
1389
1500
  } : null;
1390
- const originalRadius = layer instanceof L.CircleMarker ? layer.getRadius() : null;
1501
+ const originalRadius = layer instanceof L4.CircleMarker ? layer.getRadius() : null;
1391
1502
  if (featureInfoMode === "popup") {
1392
1503
  const content = buildFeaturePopupHtml(feature);
1393
1504
  if (content) {
@@ -1396,11 +1507,10 @@ var ZenitMap = forwardRef(({
1396
1507
  maxWidth,
1397
1508
  minWidth,
1398
1509
  maxHeight,
1399
- className: "zenit-leaflet-popup custom-leaflet-popup",
1510
+ className: "custom-leaflet-popup",
1400
1511
  autoPan: true,
1401
1512
  closeButton: true,
1402
- keepInView: true,
1403
- offset: L.point(0, -24)
1513
+ keepInView: true
1404
1514
  });
1405
1515
  }
1406
1516
  }
@@ -1445,7 +1555,7 @@ var ZenitMap = forwardRef(({
1445
1555
  onFeatureClick?.(feature, layerId);
1446
1556
  });
1447
1557
  layer.on("mouseover", () => {
1448
- if (layer instanceof L.Path && originalStyle) {
1558
+ if (layer instanceof L4.Path && originalStyle) {
1449
1559
  layer.setStyle({
1450
1560
  ...originalStyle,
1451
1561
  weight: (originalStyle.weight ?? 2) + 1,
@@ -1453,16 +1563,16 @@ var ZenitMap = forwardRef(({
1453
1563
  fillOpacity: Math.min(1, (originalStyle.fillOpacity ?? 0.8) + 0.1)
1454
1564
  });
1455
1565
  }
1456
- if (layer instanceof L.CircleMarker && typeof originalRadius === "number") {
1566
+ if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
1457
1567
  layer.setRadius(originalRadius + 1);
1458
1568
  }
1459
1569
  onFeatureHover?.(feature, layerId);
1460
1570
  });
1461
1571
  layer.on("mouseout", () => {
1462
- if (layer instanceof L.Path && originalStyle) {
1572
+ if (layer instanceof L4.Path && originalStyle) {
1463
1573
  layer.setStyle(originalStyle);
1464
1574
  }
1465
- if (layer instanceof L.CircleMarker && typeof originalRadius === "number") {
1575
+ if (layer instanceof L4.CircleMarker && typeof originalRadius === "number") {
1466
1576
  layer.setRadius(originalRadius);
1467
1577
  }
1468
1578
  });
@@ -1474,79 +1584,19 @@ var ZenitMap = forwardRef(({
1474
1584
  const resolvedStyle = featureStyleOverrides ? { ...style ?? {}, ...featureStyleOverrides } : style;
1475
1585
  const geometryType = feature?.geometry?.type;
1476
1586
  const resolvedLayerType = layerType ?? geometryType;
1477
- const sanitizedBaseOpacity = clampOpacity3(baseOpacity);
1478
- const normalizedStyleFill = typeof resolvedStyle?.fillOpacity === "number" ? clampOpacity3(resolvedStyle.fillOpacity) : 0.8;
1479
- const effectiveOpacity = getEffectiveLayerOpacity(
1480
- sanitizedBaseOpacity,
1481
- currentZoom,
1482
- resolvedLayerType,
1483
- geometryType
1484
- );
1485
- const fillOpacity = clampOpacity3(effectiveOpacity * normalizedStyleFill);
1486
- const strokeOpacity = clampOpacity3(Math.max(0.35, effectiveOpacity * 0.9));
1487
- return {
1488
- color: resolvedStyle?.color ?? resolvedStyle?.fillColor ?? "#2563eb",
1489
- weight: resolvedStyle?.weight ?? 2,
1490
- fillColor: resolvedStyle?.fillColor ?? resolvedStyle?.color ?? "#2563eb",
1491
- opacity: strokeOpacity,
1492
- fillOpacity
1587
+ return layerStyleToLeaflet({
1588
+ baseOpacity,
1589
+ zoom: currentZoom,
1590
+ layerStyle: resolvedStyle,
1591
+ geometryType,
1592
+ layerType: resolvedLayerType
1593
+ });
1594
+ };
1595
+ const makeStyleFnForLayer = (layerId) => {
1596
+ return (feature, layerType, baseOpacity) => {
1597
+ return buildLayerStyle(layerId, baseOpacity ?? 1, feature, layerType);
1493
1598
  };
1494
1599
  };
1495
- const buildLabelIcon = useCallback((label, opacity, color) => {
1496
- const size = 60;
1497
- const innerSize = 44;
1498
- const textStyles = getLabelTextStyles(color);
1499
- const safeLabel = escapeHtml(label);
1500
- const clampedOpacity = Math.min(1, Math.max(0.92, opacity));
1501
- const innerBackground = withAlpha(color, 0.9);
1502
- return L.divIcon({
1503
- className: "zenit-label-marker",
1504
- iconSize: [size, size],
1505
- iconAnchor: [size / 2, size / 2],
1506
- html: `
1507
- <div
1508
- title="${safeLabel}"
1509
- style="
1510
- width:${size}px;
1511
- height:${size}px;
1512
- border-radius:9999px;
1513
- background:rgba(255, 255, 255, 0.95);
1514
- border:3px solid rgba(255, 255, 255, 1);
1515
- display:flex;
1516
- align-items:center;
1517
- justify-content:center;
1518
- opacity:${clampedOpacity};
1519
- box-shadow:0 2px 6px rgba(0, 0, 0, 0.25);
1520
- pointer-events:none;
1521
- "
1522
- >
1523
- <div
1524
- style="
1525
- width:${innerSize}px;
1526
- height:${innerSize}px;
1527
- border-radius:9999px;
1528
- background:${innerBackground};
1529
- display:flex;
1530
- align-items:center;
1531
- justify-content:center;
1532
- box-shadow:inset 0 0 0 1px rgba(15, 23, 42, 0.12);
1533
- "
1534
- >
1535
- <span
1536
- style="
1537
- color:${textStyles.color};
1538
- font-size:20px;
1539
- font-weight:800;
1540
- text-shadow:${textStyles.shadow};
1541
- "
1542
- >
1543
- ${safeLabel}
1544
- </span>
1545
- </div>
1546
- </div>
1547
- `
1548
- });
1549
- }, []);
1550
1600
  useImperativeHandle(ref, () => ({
1551
1601
  setLayerOpacity: (layerId, opacity) => {
1552
1602
  upsertUiOverride(layerId, { overrideOpacity: opacity });
@@ -1597,10 +1647,10 @@ var ZenitMap = forwardRef(({
1597
1647
  getMapInstance: () => mapInstance
1598
1648
  }), [effectiveStates, mapInstance]);
1599
1649
  if (loadingMap) {
1600
- return /* @__PURE__ */ jsx("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1650
+ return /* @__PURE__ */ jsx3("div", { style: { padding: 16, height, width }, children: "Cargando mapa..." });
1601
1651
  }
1602
1652
  if (mapError) {
1603
- return /* @__PURE__ */ jsxs("div", { style: { padding: 16, height, width, color: "red" }, children: [
1653
+ return /* @__PURE__ */ jsxs3("div", { style: { padding: 16, height, width, color: "red" }, children: [
1604
1654
  "Error al cargar mapa: ",
1605
1655
  mapError
1606
1656
  ] });
@@ -1612,7 +1662,7 @@ var ZenitMap = forwardRef(({
1612
1662
  setCurrentZoom(zoomValue);
1613
1663
  onZoomChange?.(zoomValue);
1614
1664
  };
1615
- return /* @__PURE__ */ jsxs(
1665
+ return /* @__PURE__ */ jsxs3(
1616
1666
  "div",
1617
1667
  {
1618
1668
  style: {
@@ -1625,12 +1675,12 @@ var ZenitMap = forwardRef(({
1625
1675
  boxSizing: "border-box"
1626
1676
  },
1627
1677
  children: [
1628
- /* @__PURE__ */ jsx(
1678
+ /* @__PURE__ */ jsx3(
1629
1679
  "div",
1630
1680
  {
1631
1681
  className: `zenit-map-shell${isPopupOpen ? " popup-open" : ""}`,
1632
1682
  style: { flex: 1, position: "relative" },
1633
- children: /* @__PURE__ */ jsxs(
1683
+ children: /* @__PURE__ */ jsxs3(
1634
1684
  MapContainer,
1635
1685
  {
1636
1686
  center,
@@ -1639,57 +1689,68 @@ var ZenitMap = forwardRef(({
1639
1689
  scrollWheelZoom: true,
1640
1690
  zoomControl: false,
1641
1691
  children: [
1642
- /* @__PURE__ */ jsx(
1692
+ /* @__PURE__ */ jsx3(
1643
1693
  TileLayer,
1644
1694
  {
1645
1695
  url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
1646
1696
  attribution: "\xA9 OpenStreetMap contributors"
1647
1697
  }
1648
1698
  ),
1649
- /* @__PURE__ */ jsx(ZoomControl, { position: "topright" }),
1650
- /* @__PURE__ */ jsx(MapInstanceBridge, { onReady: handleMapReady }),
1651
- /* @__PURE__ */ jsx(FitToBounds, { bbox: explicitZoomBBox ?? void 0 }),
1652
- /* @__PURE__ */ jsx(AutoFitToBounds, { bbox: autoZoomBBox ?? void 0, enabled: !explicitZoomBBox }),
1653
- /* @__PURE__ */ jsx(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1699
+ /* @__PURE__ */ jsx3(ZoomControl, { position: "topright" }),
1700
+ /* @__PURE__ */ jsx3(MapInstanceBridge, { onReady: handleMapReady }),
1701
+ /* @__PURE__ */ jsx3(
1702
+ BBoxZoomHandler,
1703
+ {
1704
+ bbox: zoomToBbox ?? void 0,
1705
+ geojson: zoomToGeojson ?? void 0,
1706
+ autoGeojson: autoZoomGeojson
1707
+ }
1708
+ ),
1709
+ /* @__PURE__ */ jsx3(ZoomBasedOpacityHandler, { onZoomChange: handleZoomChange }),
1710
+ /* @__PURE__ */ jsx3(LocationControl, {}),
1654
1711
  orderedLayers.map((layerState) => {
1655
1712
  const baseOpacity = layerState.effective?.baseOpacity ?? layerState.effective?.opacity ?? 1;
1656
1713
  const fillPaneName = `zenit-layer-${layerState.mapLayer.layerId}-fill`;
1657
1714
  const pointsPaneName = `zenit-layer-${layerState.mapLayer.layerId}-points`;
1658
1715
  const layerType = layerState.layer?.layerType ?? layerState.mapLayer.layerType ?? void 0;
1659
- const data = layerState.data?.features ?? [];
1660
- const fillFeatures = data.filter(isNonPointGeometry);
1661
- const pointFeatures = data.filter(isPointGeometry);
1662
- const fillData = fillFeatures.length > 0 ? buildFeatureCollection(fillFeatures) : null;
1663
- const pointsData = pointFeatures.length > 0 ? buildFeatureCollection(pointFeatures) : null;
1664
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1665
- fillData && /* @__PURE__ */ jsx(
1666
- GeoJSON,
1667
- {
1668
- data: fillData,
1669
- pane: panesReady && mapInstance?.getPane(fillPaneName) ? fillPaneName : void 0,
1670
- style: (feature) => buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType),
1671
- onEachFeature: overlayOnEachFeature
1672
- }
1673
- ),
1674
- pointsData && /* @__PURE__ */ jsx(
1675
- GeoJSON,
1716
+ const labelKey = labelKeyIndex.get(String(layerState.mapLayer.layerId));
1717
+ return /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
1718
+ layerState.data && /* @__PURE__ */ jsx3(
1719
+ LayerGeoJson,
1676
1720
  {
1677
- data: pointsData,
1678
- pane: panesReady && mapInstance?.getPane(pointsPaneName) ? pointsPaneName : void 0,
1679
- pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
1680
- radius: isMobile ? 8 : 6,
1681
- ...buildLayerStyle(layerState.mapLayer.layerId, baseOpacity, feature, layerType)
1682
- }),
1683
- onEachFeature: overlayOnEachFeature
1721
+ layerId: layerState.mapLayer.layerId,
1722
+ data: layerState.data,
1723
+ baseOpacity,
1724
+ isMobile,
1725
+ panesReady,
1726
+ mapInstance,
1727
+ fillPaneName,
1728
+ pointsPaneName,
1729
+ layerType,
1730
+ styleFn: makeStyleFnForLayer(layerState.mapLayer.layerId),
1731
+ onEachFeature: overlayOnEachFeature,
1732
+ onPolygonLabel: labelKey ? (feature, layer) => {
1733
+ const geometryType = feature?.geometry?.type;
1734
+ if (geometryType !== "Polygon" && geometryType !== "MultiPolygon") return;
1735
+ const properties = feature?.properties;
1736
+ const value = properties?.[labelKey];
1737
+ if (!value || !layer.bindTooltip) return;
1738
+ layer.bindTooltip(String(value), {
1739
+ sticky: true,
1740
+ direction: "center",
1741
+ opacity: 0.9,
1742
+ className: "polygon-label-tooltip"
1743
+ });
1744
+ } : void 0
1684
1745
  }
1685
1746
  ),
1686
1747
  panesReady && mapInstance?.getPane(LABELS_PANE_NAME) ? labelMarkers.filter(
1687
1748
  (marker) => String(marker.layerId) === String(layerState.mapLayer.layerId)
1688
- ).map((marker) => /* @__PURE__ */ jsx(
1689
- Marker,
1749
+ ).map((marker) => /* @__PURE__ */ jsx3(
1750
+ Marker2,
1690
1751
  {
1691
1752
  position: marker.position,
1692
- icon: buildLabelIcon(marker.label, marker.opacity, marker.color),
1753
+ icon: createCustomIcon(marker.label, marker.opacity, marker.color),
1693
1754
  interactive: false,
1694
1755
  pane: LABELS_PANE_NAME
1695
1756
  },
@@ -1697,14 +1758,20 @@ var ZenitMap = forwardRef(({
1697
1758
  )) : null
1698
1759
  ] }, layerState.mapLayer.layerId.toString());
1699
1760
  }),
1700
- overlayGeojson && /* @__PURE__ */ jsx(
1701
- GeoJSON,
1761
+ overlayGeojson && /* @__PURE__ */ jsx3(
1762
+ LayerGeoJson,
1702
1763
  {
1764
+ layerId: "overlay-geojson",
1703
1765
  data: overlayGeojson,
1704
- style: overlayStyleFunction,
1766
+ baseOpacity: 1,
1767
+ isMobile,
1768
+ panesReady,
1769
+ mapInstance,
1770
+ fillPaneName: "zenit-overlay-fill",
1771
+ pointsPaneName: "zenit-overlay-points",
1772
+ styleFn: overlayStyleFn,
1705
1773
  onEachFeature: overlayOnEachFeature
1706
- },
1707
- "zenit-overlay-geojson"
1774
+ }
1708
1775
  )
1709
1776
  ]
1710
1777
  },
@@ -1712,7 +1779,7 @@ var ZenitMap = forwardRef(({
1712
1779
  )
1713
1780
  }
1714
1781
  ),
1715
- showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs(
1782
+ showLayerPanel && decoratedLayers.length > 0 && /* @__PURE__ */ jsxs3(
1716
1783
  "div",
1717
1784
  {
1718
1785
  style: {
@@ -1725,7 +1792,7 @@ var ZenitMap = forwardRef(({
1725
1792
  overflowY: "auto"
1726
1793
  },
1727
1794
  children: [
1728
- overlayGeojson && /* @__PURE__ */ jsxs(
1795
+ overlayGeojson && /* @__PURE__ */ jsxs3(
1729
1796
  "div",
1730
1797
  {
1731
1798
  style: {
@@ -1737,8 +1804,8 @@ var ZenitMap = forwardRef(({
1737
1804
  marginBottom: 12
1738
1805
  },
1739
1806
  children: [
1740
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1741
- /* @__PURE__ */ jsxs("div", { style: { fontSize: 13 }, children: [
1807
+ /* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Overlay activo" }),
1808
+ /* @__PURE__ */ jsxs3("div", { style: { fontSize: 13 }, children: [
1742
1809
  "GeoJSON externo con ",
1743
1810
  (overlayGeojson.features?.length ?? 0).toLocaleString(),
1744
1811
  " elementos."
@@ -1746,14 +1813,14 @@ var ZenitMap = forwardRef(({
1746
1813
  ]
1747
1814
  }
1748
1815
  ),
1749
- /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1750
- decoratedLayers.map((layerState) => /* @__PURE__ */ jsxs(
1816
+ /* @__PURE__ */ jsx3("div", { style: { fontWeight: 600, marginBottom: 12 }, children: "Capas" }),
1817
+ decoratedLayers.map((layerState) => /* @__PURE__ */ jsxs3(
1751
1818
  "div",
1752
1819
  {
1753
1820
  style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 10, marginBottom: 10 },
1754
1821
  children: [
1755
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1756
- /* @__PURE__ */ jsx(
1822
+ /* @__PURE__ */ jsxs3("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
1823
+ /* @__PURE__ */ jsx3(
1757
1824
  "input",
1758
1825
  {
1759
1826
  type: "checkbox",
@@ -1764,17 +1831,17 @@ var ZenitMap = forwardRef(({
1764
1831
  }
1765
1832
  }
1766
1833
  ),
1767
- /* @__PURE__ */ jsx("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1834
+ /* @__PURE__ */ jsx3("span", { children: layerState.layer?.name ?? `Capa ${layerState.mapLayer.layerId}` })
1768
1835
  ] }),
1769
- /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
1770
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1771
- /* @__PURE__ */ jsx("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1772
- /* @__PURE__ */ jsxs("span", { children: [
1836
+ /* @__PURE__ */ jsxs3("div", { style: { marginTop: 8 }, children: [
1837
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4 }, children: [
1838
+ /* @__PURE__ */ jsx3("span", { style: { color: "#4a5568" }, children: "Opacidad" }),
1839
+ /* @__PURE__ */ jsxs3("span", { children: [
1773
1840
  Math.round((layerState.effective?.opacity ?? 1) * 100),
1774
1841
  "%"
1775
1842
  ] })
1776
1843
  ] }),
1777
- /* @__PURE__ */ jsx(
1844
+ /* @__PURE__ */ jsx3(
1778
1845
  "input",
1779
1846
  {
1780
1847
  type: "range",
@@ -1804,13 +1871,13 @@ var ZenitMap = forwardRef(({
1804
1871
  ZenitMap.displayName = "ZenitMap";
1805
1872
 
1806
1873
  // src/react/ZenitLayerManager.tsx
1807
- import React2, { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
1874
+ import React4, { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
1808
1875
 
1809
1876
  // src/react/icons.tsx
1810
1877
  import { Eye, EyeOff, ChevronLeft, ChevronRight, Layers, Upload, X, ZoomIn } from "lucide-react";
1811
1878
 
1812
1879
  // src/react/ZenitLayerManager.tsx
1813
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1880
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1814
1881
  var FLOAT_TOLERANCE = 1e-3;
1815
1882
  function areEffectiveStatesEqual(a, b) {
1816
1883
  if (a.length !== b.length) return false;
@@ -1850,15 +1917,15 @@ var ZenitLayerManager = ({
1850
1917
  layerFeatureCounts,
1851
1918
  mapLayers
1852
1919
  }) => {
1853
- const [map, setMap] = useState2(null);
1854
- const [loadingMap, setLoadingMap] = useState2(false);
1855
- const [mapError, setMapError] = useState2(null);
1856
- const [layers, setLayers] = useState2([]);
1857
- const [activeTab, setActiveTab] = useState2("layers");
1858
- const [panelVisible, setPanelVisible] = useState2(true);
1859
- const lastEmittedStatesRef = useRef2(null);
1920
+ const [map, setMap] = useState3(null);
1921
+ const [loadingMap, setLoadingMap] = useState3(false);
1922
+ const [mapError, setMapError] = useState3(null);
1923
+ const [layers, setLayers] = useState3([]);
1924
+ const [activeTab, setActiveTab] = useState3("layers");
1925
+ const [panelVisible, setPanelVisible] = useState3(true);
1926
+ const lastEmittedStatesRef = useRef5(null);
1860
1927
  const isControlled = Array.isArray(layerStates) && typeof onLayerStatesChange === "function";
1861
- const baseStates = useMemo2(
1928
+ const baseStates = useMemo3(
1862
1929
  () => initLayerStates(
1863
1930
  layers.map((entry) => ({
1864
1931
  ...entry.mapLayer,
@@ -1869,7 +1936,7 @@ var ZenitLayerManager = ({
1869
1936
  ),
1870
1937
  [layers]
1871
1938
  );
1872
- const overrideStates = useMemo2(
1939
+ const overrideStates = useMemo3(
1873
1940
  () => layers.map(
1874
1941
  (entry) => ({
1875
1942
  layerId: entry.mapLayer.layerId,
@@ -1879,11 +1946,11 @@ var ZenitLayerManager = ({
1879
1946
  ),
1880
1947
  [layers]
1881
1948
  );
1882
- const effectiveStates = useMemo2(
1949
+ const effectiveStates = useMemo3(
1883
1950
  () => layerStates ?? applyLayerOverrides(baseStates, overrideStates),
1884
1951
  [baseStates, layerStates, overrideStates]
1885
1952
  );
1886
- const layerMetaIndex = useMemo2(() => {
1953
+ const layerMetaIndex = useMemo3(() => {
1887
1954
  const index = /* @__PURE__ */ new Map();
1888
1955
  mapLayers?.forEach((entry) => {
1889
1956
  const key = String(entry.layerId);
@@ -1897,7 +1964,7 @@ var ZenitLayerManager = ({
1897
1964
  });
1898
1965
  return index;
1899
1966
  }, [map, mapLayers]);
1900
- const resolveUserOpacity = React2.useCallback((state) => {
1967
+ const resolveUserOpacity = React4.useCallback((state) => {
1901
1968
  if (typeof state.overrideOpacity === "number") return state.overrideOpacity;
1902
1969
  if (typeof state.overrideOpacity === "string") {
1903
1970
  const parsed = Number.parseFloat(state.overrideOpacity);
@@ -1905,7 +1972,7 @@ var ZenitLayerManager = ({
1905
1972
  }
1906
1973
  return state.opacity ?? 1;
1907
1974
  }, []);
1908
- const resolveEffectiveOpacity = React2.useCallback(
1975
+ const resolveEffectiveOpacity = React4.useCallback(
1909
1976
  (layerId, userOpacity) => {
1910
1977
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1911
1978
  return userOpacity;
@@ -1921,7 +1988,7 @@ var ZenitLayerManager = ({
1921
1988
  },
1922
1989
  [autoOpacityConfig, autoOpacityOnZoom, layerMetaIndex, mapZoom]
1923
1990
  );
1924
- const effectiveStatesWithZoom = useMemo2(() => {
1991
+ const effectiveStatesWithZoom = useMemo3(() => {
1925
1992
  if (!autoOpacityOnZoom || typeof mapZoom !== "number") {
1926
1993
  return effectiveStates;
1927
1994
  }
@@ -1935,7 +2002,7 @@ var ZenitLayerManager = ({
1935
2002
  };
1936
2003
  });
1937
2004
  }, [autoOpacityOnZoom, effectiveStates, mapZoom, resolveEffectiveOpacity, resolveUserOpacity]);
1938
- useEffect2(() => {
2005
+ useEffect5(() => {
1939
2006
  let cancelled = false;
1940
2007
  setLoadingMap(true);
1941
2008
  setMapError(null);
@@ -1967,12 +2034,12 @@ var ZenitLayerManager = ({
1967
2034
  cancelled = true;
1968
2035
  };
1969
2036
  }, [client.maps, mapId]);
1970
- useEffect2(() => {
2037
+ useEffect5(() => {
1971
2038
  if (!showUploadTab && activeTab === "upload") {
1972
2039
  setActiveTab("layers");
1973
2040
  }
1974
2041
  }, [activeTab, showUploadTab]);
1975
- useEffect2(() => {
2042
+ useEffect5(() => {
1976
2043
  if (isControlled) return;
1977
2044
  if (!onLayerStatesChange) return;
1978
2045
  const emitStates = autoOpacityOnZoom && typeof mapZoom === "number" ? effectiveStatesWithZoom : effectiveStates;
@@ -1990,7 +2057,7 @@ var ZenitLayerManager = ({
1990
2057
  mapZoom,
1991
2058
  onLayerStatesChange
1992
2059
  ]);
1993
- const updateLayerVisible = React2.useCallback(
2060
+ const updateLayerVisible = React4.useCallback(
1994
2061
  (layerId, visible) => {
1995
2062
  if (!onLayerStatesChange) return;
1996
2063
  const next = effectiveStates.map(
@@ -2000,7 +2067,7 @@ var ZenitLayerManager = ({
2000
2067
  },
2001
2068
  [effectiveStates, onLayerStatesChange]
2002
2069
  );
2003
- const updateLayerOpacity = React2.useCallback(
2070
+ const updateLayerOpacity = React4.useCallback(
2004
2071
  (layerId, opacity) => {
2005
2072
  if (!onLayerStatesChange) return;
2006
2073
  const adjustedOpacity = resolveEffectiveOpacity(layerId, opacity);
@@ -2011,7 +2078,7 @@ var ZenitLayerManager = ({
2011
2078
  },
2012
2079
  [effectiveStates, onLayerStatesChange, resolveEffectiveOpacity]
2013
2080
  );
2014
- const resolveFeatureCount = React2.useCallback(
2081
+ const resolveFeatureCount = React4.useCallback(
2015
2082
  (layerId, layer) => {
2016
2083
  const resolvedFeatureCount = layerFeatureCounts?.[layerId] ?? layerFeatureCounts?.[String(layerId)];
2017
2084
  if (typeof resolvedFeatureCount === "number") return resolvedFeatureCount;
@@ -2020,7 +2087,7 @@ var ZenitLayerManager = ({
2020
2087
  },
2021
2088
  [layerFeatureCounts]
2022
2089
  );
2023
- const decoratedLayers = useMemo2(() => {
2090
+ const decoratedLayers = useMemo3(() => {
2024
2091
  return layers.map((entry) => ({
2025
2092
  ...entry,
2026
2093
  effective: effectiveStates.find((state) => state.layerId === entry.mapLayer.layerId),
@@ -2049,7 +2116,7 @@ var ZenitLayerManager = ({
2049
2116
  return String(a.mapLayer.layerId).localeCompare(String(b.mapLayer.layerId));
2050
2117
  });
2051
2118
  }, [effectiveStates, layers, resolveFeatureCount]);
2052
- const resolveLayerStyle = React2.useCallback(
2119
+ const resolveLayerStyle = React4.useCallback(
2053
2120
  (layerId) => {
2054
2121
  const layerKey = String(layerId);
2055
2122
  const fromProp = mapLayers?.find((entry) => String(entry.layerId) === layerKey)?.style;
@@ -2079,10 +2146,10 @@ var ZenitLayerManager = ({
2079
2146
  ...height ? { height } : {}
2080
2147
  };
2081
2148
  if (loadingMap) {
2082
- return /* @__PURE__ */ jsx2("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
2149
+ return /* @__PURE__ */ jsx4("div", { className, style: panelStyle, children: "Cargando capas\u2026" });
2083
2150
  }
2084
2151
  if (mapError) {
2085
- return /* @__PURE__ */ jsxs2("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
2152
+ return /* @__PURE__ */ jsxs4("div", { className, style: { ...panelStyle, color: "#c53030" }, children: [
2086
2153
  "Error al cargar mapa: ",
2087
2154
  mapError
2088
2155
  ] });
@@ -2100,7 +2167,7 @@ var ZenitLayerManager = ({
2100
2167
  boxShadow: "0 1px 0 rgba(148, 163, 184, 0.25)"
2101
2168
  };
2102
2169
  const renderLayerCards = () => {
2103
- return /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
2170
+ return /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: decoratedLayers.map((layerState) => {
2104
2171
  const layerId = layerState.mapLayer.layerId;
2105
2172
  const layerName = layerState.layerName ?? `Capa ${layerId}`;
2106
2173
  const visible = layerState.effective?.visible ?? false;
@@ -2110,7 +2177,7 @@ var ZenitLayerManager = ({
2110
2177
  const muted = !visible;
2111
2178
  const opacityPercent = Math.round(userOpacity * 100);
2112
2179
  const sliderBackground = `linear-gradient(to right, ${layerColor} 0%, ${layerColor} ${opacityPercent}%, #e5e7eb ${opacityPercent}%, #e5e7eb 100%)`;
2113
- return /* @__PURE__ */ jsxs2(
2180
+ return /* @__PURE__ */ jsxs4(
2114
2181
  "div",
2115
2182
  {
2116
2183
  className: `zlm-card${muted ? " is-muted" : ""}`,
@@ -2125,9 +2192,9 @@ var ZenitLayerManager = ({
2125
2192
  width: "100%"
2126
2193
  },
2127
2194
  children: [
2128
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
2129
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
2130
- /* @__PURE__ */ jsx2(
2195
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", gap: 12 }, children: [
2196
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 10, alignItems: "flex-start", minWidth: 0, flex: 1 }, children: [
2197
+ /* @__PURE__ */ jsx4(
2131
2198
  "div",
2132
2199
  {
2133
2200
  style: {
@@ -2142,7 +2209,7 @@ var ZenitLayerManager = ({
2142
2209
  title: "Color de la capa"
2143
2210
  }
2144
2211
  ),
2145
- showLayerVisibilityIcon && /* @__PURE__ */ jsx2(
2212
+ showLayerVisibilityIcon && /* @__PURE__ */ jsx4(
2146
2213
  "button",
2147
2214
  {
2148
2215
  type: "button",
@@ -2153,11 +2220,11 @@ var ZenitLayerManager = ({
2153
2220
  )
2154
2221
  ),
2155
2222
  "aria-label": visible ? "Ocultar capa" : "Mostrar capa",
2156
- children: visible ? /* @__PURE__ */ jsx2(Eye, { size: 16 }) : /* @__PURE__ */ jsx2(EyeOff, { size: 16 })
2223
+ children: visible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 })
2157
2224
  }
2158
2225
  ),
2159
- /* @__PURE__ */ jsxs2("div", { style: { minWidth: 0, flex: 1 }, children: [
2160
- /* @__PURE__ */ jsx2(
2226
+ /* @__PURE__ */ jsxs4("div", { style: { minWidth: 0, flex: 1 }, children: [
2227
+ /* @__PURE__ */ jsx4(
2161
2228
  "div",
2162
2229
  {
2163
2230
  className: "zlm-layer-name",
@@ -2175,26 +2242,26 @@ var ZenitLayerManager = ({
2175
2242
  children: layerName
2176
2243
  }
2177
2244
  ),
2178
- /* @__PURE__ */ jsxs2("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2245
+ /* @__PURE__ */ jsxs4("div", { style: { color: muted ? "#94a3b8" : "#64748b", fontSize: 12 }, children: [
2179
2246
  "ID ",
2180
2247
  layerId
2181
2248
  ] })
2182
2249
  ] })
2183
2250
  ] }),
2184
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ jsxs2("span", { className: "zlm-badge", children: [
2251
+ /* @__PURE__ */ jsx4("div", { style: { display: "flex", alignItems: "flex-start", gap: 6, flexShrink: 0 }, children: typeof featureCount === "number" && /* @__PURE__ */ jsxs4("span", { className: "zlm-badge", children: [
2185
2252
  featureCount.toLocaleString(),
2186
2253
  " features"
2187
2254
  ] }) })
2188
2255
  ] }),
2189
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ jsxs2("div", { style: { flex: 1 }, children: [
2190
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2191
- /* @__PURE__ */ jsx2("span", { children: "Opacidad" }),
2192
- /* @__PURE__ */ jsxs2("span", { children: [
2256
+ /* @__PURE__ */ jsx4("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: /* @__PURE__ */ jsxs4("div", { style: { flex: 1 }, children: [
2257
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 6, color: "#64748b", fontSize: 12 }, children: [
2258
+ /* @__PURE__ */ jsx4("span", { children: "Opacidad" }),
2259
+ /* @__PURE__ */ jsxs4("span", { children: [
2193
2260
  opacityPercent,
2194
2261
  "%"
2195
2262
  ] })
2196
2263
  ] }),
2197
- /* @__PURE__ */ jsx2(
2264
+ /* @__PURE__ */ jsx4(
2198
2265
  "input",
2199
2266
  {
2200
2267
  className: "zlm-range",
@@ -2232,8 +2299,8 @@ var ZenitLayerManager = ({
2232
2299
  );
2233
2300
  }) });
2234
2301
  };
2235
- return /* @__PURE__ */ jsxs2("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2236
- /* @__PURE__ */ jsx2("style", { children: `
2302
+ return /* @__PURE__ */ jsxs4("div", { className: ["zenit-layer-manager", className].filter(Boolean).join(" "), style: panelStyle, children: [
2303
+ /* @__PURE__ */ jsx4("style", { children: `
2237
2304
  .zenit-layer-manager .zlm-card {
2238
2305
  transition: box-shadow 0.2s ease, transform 0.2s ease, opacity 0.2s ease;
2239
2306
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.08);
@@ -2328,16 +2395,16 @@ var ZenitLayerManager = ({
2328
2395
  outline-offset: 2px;
2329
2396
  }
2330
2397
  ` }),
2331
- /* @__PURE__ */ jsxs2("div", { style: headerStyle, children: [
2332
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2333
- /* @__PURE__ */ jsxs2("div", { children: [
2334
- /* @__PURE__ */ jsx2("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2335
- /* @__PURE__ */ jsxs2("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2398
+ /* @__PURE__ */ jsxs4("div", { style: headerStyle, children: [
2399
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [
2400
+ /* @__PURE__ */ jsxs4("div", { children: [
2401
+ /* @__PURE__ */ jsx4("div", { style: { fontWeight: 800, fontSize: 16, color: "#0f172a" }, children: "Gesti\xF3n de Capas" }),
2402
+ /* @__PURE__ */ jsxs4("div", { style: { color: "#64748b", fontSize: 12 }, children: [
2336
2403
  "Mapa #",
2337
2404
  map.id
2338
2405
  ] })
2339
2406
  ] }),
2340
- /* @__PURE__ */ jsxs2(
2407
+ /* @__PURE__ */ jsxs4(
2341
2408
  "button",
2342
2409
  {
2343
2410
  type: "button",
@@ -2345,13 +2412,13 @@ var ZenitLayerManager = ({
2345
2412
  className: "zlm-panel-toggle",
2346
2413
  "aria-label": panelVisible ? "Ocultar panel de capas" : "Mostrar panel de capas",
2347
2414
  children: [
2348
- panelVisible ? /* @__PURE__ */ jsx2(Eye, { size: 16 }) : /* @__PURE__ */ jsx2(EyeOff, { size: 16 }),
2415
+ panelVisible ? /* @__PURE__ */ jsx4(Eye, { size: 16 }) : /* @__PURE__ */ jsx4(EyeOff, { size: 16 }),
2349
2416
  panelVisible ? "Ocultar" : "Mostrar"
2350
2417
  ]
2351
2418
  }
2352
2419
  )
2353
2420
  ] }),
2354
- /* @__PURE__ */ jsxs2(
2421
+ /* @__PURE__ */ jsxs4(
2355
2422
  "div",
2356
2423
  {
2357
2424
  style: {
@@ -2364,26 +2431,26 @@ var ZenitLayerManager = ({
2364
2431
  background: "#f1f5f9"
2365
2432
  },
2366
2433
  children: [
2367
- /* @__PURE__ */ jsxs2(
2434
+ /* @__PURE__ */ jsxs4(
2368
2435
  "button",
2369
2436
  {
2370
2437
  type: "button",
2371
2438
  className: `zlm-tab${activeTab === "layers" ? " is-active" : ""}`,
2372
2439
  onClick: () => setActiveTab("layers"),
2373
2440
  children: [
2374
- /* @__PURE__ */ jsx2(Layers, { size: 16 }),
2441
+ /* @__PURE__ */ jsx4(Layers, { size: 16 }),
2375
2442
  "Capas"
2376
2443
  ]
2377
2444
  }
2378
2445
  ),
2379
- showUploadTab && /* @__PURE__ */ jsxs2(
2446
+ showUploadTab && /* @__PURE__ */ jsxs4(
2380
2447
  "button",
2381
2448
  {
2382
2449
  type: "button",
2383
2450
  className: `zlm-tab${activeTab === "upload" ? " is-active" : ""}`,
2384
2451
  onClick: () => setActiveTab("upload"),
2385
2452
  children: [
2386
- /* @__PURE__ */ jsx2(Upload, { size: 16 }),
2453
+ /* @__PURE__ */ jsx4(Upload, { size: 16 }),
2387
2454
  "Subir"
2388
2455
  ]
2389
2456
  }
@@ -2392,15 +2459,15 @@ var ZenitLayerManager = ({
2392
2459
  }
2393
2460
  )
2394
2461
  ] }),
2395
- panelVisible && /* @__PURE__ */ jsxs2("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2462
+ panelVisible && /* @__PURE__ */ jsxs4("div", { style: { padding: "12px 10px 18px", overflowY: "auto", flex: 1, minHeight: 0 }, children: [
2396
2463
  activeTab === "layers" && renderLayerCards(),
2397
- showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx2("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2464
+ showUploadTab && activeTab === "upload" && /* @__PURE__ */ jsx4("div", { style: { color: "#475569", fontSize: 13 }, children: "Pr\xF3ximamente podr\xE1s subir capas desde este panel." })
2398
2465
  ] })
2399
2466
  ] });
2400
2467
  };
2401
2468
 
2402
2469
  // src/react/ZenitFeatureFilterPanel.tsx
2403
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2470
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2404
2471
  var ZenitFeatureFilterPanel = ({
2405
2472
  title = "Filtros",
2406
2473
  description,
@@ -2408,7 +2475,7 @@ var ZenitFeatureFilterPanel = ({
2408
2475
  style,
2409
2476
  children
2410
2477
  }) => {
2411
- return /* @__PURE__ */ jsxs3(
2478
+ return /* @__PURE__ */ jsxs5(
2412
2479
  "section",
2413
2480
  {
2414
2481
  className,
@@ -2421,22 +2488,22 @@ var ZenitFeatureFilterPanel = ({
2421
2488
  ...style
2422
2489
  },
2423
2490
  children: [
2424
- /* @__PURE__ */ jsxs3("header", { style: { marginBottom: 12 }, children: [
2425
- /* @__PURE__ */ jsx3("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2426
- description && /* @__PURE__ */ jsx3("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2491
+ /* @__PURE__ */ jsxs5("header", { style: { marginBottom: 12 }, children: [
2492
+ /* @__PURE__ */ jsx5("h3", { style: { margin: 0, fontSize: 16 }, children: title }),
2493
+ description && /* @__PURE__ */ jsx5("p", { style: { margin: "6px 0 0", color: "#475569", fontSize: 13 }, children: description })
2427
2494
  ] }),
2428
- /* @__PURE__ */ jsx3("div", { children })
2495
+ /* @__PURE__ */ jsx5("div", { children })
2429
2496
  ]
2430
2497
  }
2431
2498
  );
2432
2499
  };
2433
2500
 
2434
2501
  // src/react/ai/FloatingChatBox.tsx
2435
- import { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
2436
- import { createPortal } from "react-dom";
2502
+ import { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef7, useState as useState5 } from "react";
2503
+ import { createPortal as createPortal2 } from "react-dom";
2437
2504
 
2438
2505
  // src/react/hooks/use-chat.ts
2439
- import { useCallback as useCallback2, useRef as useRef3, useState as useState3 } from "react";
2506
+ import { useCallback as useCallback3, useRef as useRef6, useState as useState4 } from "react";
2440
2507
 
2441
2508
  // src/ai/chat.service.ts
2442
2509
  var DEFAULT_ERROR_MESSAGE = "No fue posible completar la solicitud al asistente.";
@@ -2572,9 +2639,9 @@ var createChatService = (config) => ({
2572
2639
 
2573
2640
  // src/react/hooks/use-chat.ts
2574
2641
  var useSendMessage = (config) => {
2575
- const [isLoading, setIsLoading] = useState3(false);
2576
- const [error, setError] = useState3(null);
2577
- const send = useCallback2(
2642
+ const [isLoading, setIsLoading] = useState4(false);
2643
+ const [error, setError] = useState4(null);
2644
+ const send = useCallback3(
2578
2645
  async (mapId, request, options) => {
2579
2646
  setIsLoading(true);
2580
2647
  setError(null);
@@ -2592,18 +2659,18 @@ var useSendMessage = (config) => {
2592
2659
  return { sendMessage: send, isLoading, error };
2593
2660
  };
2594
2661
  var useSendMessageStream = (config) => {
2595
- const [isStreaming, setIsStreaming] = useState3(false);
2596
- const [streamingText, setStreamingText] = useState3("");
2597
- const [completeResponse, setCompleteResponse] = useState3(null);
2598
- const [error, setError] = useState3(null);
2599
- const requestIdRef = useRef3(0);
2600
- const reset = useCallback2(() => {
2662
+ const [isStreaming, setIsStreaming] = useState4(false);
2663
+ const [streamingText, setStreamingText] = useState4("");
2664
+ const [completeResponse, setCompleteResponse] = useState4(null);
2665
+ const [error, setError] = useState4(null);
2666
+ const requestIdRef = useRef6(0);
2667
+ const reset = useCallback3(() => {
2601
2668
  setIsStreaming(false);
2602
2669
  setStreamingText("");
2603
2670
  setCompleteResponse(null);
2604
2671
  setError(null);
2605
2672
  }, []);
2606
- const send = useCallback2(
2673
+ const send = useCallback3(
2607
2674
  async (mapId, request, options) => {
2608
2675
  const requestId = requestIdRef.current + 1;
2609
2676
  requestIdRef.current = requestId;
@@ -2657,7 +2724,7 @@ var useSendMessageStream = (config) => {
2657
2724
  // src/react/components/MarkdownRenderer.tsx
2658
2725
  import ReactMarkdown from "react-markdown";
2659
2726
  import remarkGfm from "remark-gfm";
2660
- import { jsx as jsx4 } from "react/jsx-runtime";
2727
+ import { jsx as jsx6 } from "react/jsx-runtime";
2661
2728
  function normalizeAssistantMarkdown(text) {
2662
2729
  if (!text || typeof text !== "string") return "";
2663
2730
  let normalized = text;
@@ -2673,28 +2740,28 @@ var MarkdownRenderer = ({ content, className }) => {
2673
2740
  if (!normalizedContent) {
2674
2741
  return null;
2675
2742
  }
2676
- return /* @__PURE__ */ jsx4("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx4(
2743
+ return /* @__PURE__ */ jsx6("div", { className, style: { wordBreak: "break-word" }, children: /* @__PURE__ */ jsx6(
2677
2744
  ReactMarkdown,
2678
2745
  {
2679
2746
  remarkPlugins: [remarkGfm],
2680
2747
  components: {
2681
2748
  // Headings with proper spacing
2682
- h1: ({ children, ...props }) => /* @__PURE__ */ jsx4("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2683
- h2: ({ children, ...props }) => /* @__PURE__ */ jsx4("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2684
- h3: ({ children, ...props }) => /* @__PURE__ */ jsx4("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2685
- h4: ({ children, ...props }) => /* @__PURE__ */ jsx4("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2686
- h5: ({ children, ...props }) => /* @__PURE__ */ jsx4("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2687
- h6: ({ children, ...props }) => /* @__PURE__ */ jsx4("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2749
+ h1: ({ children, ...props }) => /* @__PURE__ */ jsx6("h1", { style: { fontSize: "1.5em", fontWeight: 700, marginTop: "1em", marginBottom: "0.5em" }, ...props, children }),
2750
+ h2: ({ children, ...props }) => /* @__PURE__ */ jsx6("h2", { style: { fontSize: "1.3em", fontWeight: 700, marginTop: "0.9em", marginBottom: "0.45em" }, ...props, children }),
2751
+ h3: ({ children, ...props }) => /* @__PURE__ */ jsx6("h3", { style: { fontSize: "1.15em", fontWeight: 600, marginTop: "0.75em", marginBottom: "0.4em" }, ...props, children }),
2752
+ h4: ({ children, ...props }) => /* @__PURE__ */ jsx6("h4", { style: { fontSize: "1.05em", fontWeight: 600, marginTop: "0.6em", marginBottom: "0.35em" }, ...props, children }),
2753
+ h5: ({ children, ...props }) => /* @__PURE__ */ jsx6("h5", { style: { fontSize: "1em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2754
+ h6: ({ children, ...props }) => /* @__PURE__ */ jsx6("h6", { style: { fontSize: "0.95em", fontWeight: 600, marginTop: "0.5em", marginBottom: "0.3em" }, ...props, children }),
2688
2755
  // Paragraphs with comfortable line height
2689
- p: ({ children, ...props }) => /* @__PURE__ */ jsx4("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2756
+ p: ({ children, ...props }) => /* @__PURE__ */ jsx6("p", { style: { marginTop: "0.5em", marginBottom: "0.5em", lineHeight: 1.6 }, ...props, children }),
2690
2757
  // Lists with proper indentation
2691
- ul: ({ children, ...props }) => /* @__PURE__ */ jsx4("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2692
- ol: ({ children, ...props }) => /* @__PURE__ */ jsx4("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2693
- li: ({ children, ...props }) => /* @__PURE__ */ jsx4("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2758
+ ul: ({ children, ...props }) => /* @__PURE__ */ jsx6("ul", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2759
+ ol: ({ children, ...props }) => /* @__PURE__ */ jsx6("ol", { style: { paddingLeft: "1.5em", marginTop: "0.5em", marginBottom: "0.5em" }, ...props, children }),
2760
+ li: ({ children, ...props }) => /* @__PURE__ */ jsx6("li", { style: { marginTop: "0.25em", marginBottom: "0.25em" }, ...props, children }),
2694
2761
  // Code blocks
2695
2762
  code: ({ inline, children, ...props }) => {
2696
2763
  if (inline) {
2697
- return /* @__PURE__ */ jsx4(
2764
+ return /* @__PURE__ */ jsx6(
2698
2765
  "code",
2699
2766
  {
2700
2767
  style: {
@@ -2709,7 +2776,7 @@ var MarkdownRenderer = ({ content, className }) => {
2709
2776
  }
2710
2777
  );
2711
2778
  }
2712
- return /* @__PURE__ */ jsx4(
2779
+ return /* @__PURE__ */ jsx6(
2713
2780
  "code",
2714
2781
  {
2715
2782
  style: {
@@ -2729,9 +2796,9 @@ var MarkdownRenderer = ({ content, className }) => {
2729
2796
  );
2730
2797
  },
2731
2798
  // Pre (code block wrapper)
2732
- pre: ({ children, ...props }) => /* @__PURE__ */ jsx4("pre", { style: { margin: 0 }, ...props, children }),
2799
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsx6("pre", { style: { margin: 0 }, ...props, children }),
2733
2800
  // Blockquotes
2734
- blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2801
+ blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2735
2802
  "blockquote",
2736
2803
  {
2737
2804
  style: {
@@ -2747,11 +2814,11 @@ var MarkdownRenderer = ({ content, className }) => {
2747
2814
  }
2748
2815
  ),
2749
2816
  // Strong/bold
2750
- strong: ({ children, ...props }) => /* @__PURE__ */ jsx4("strong", { style: { fontWeight: 600 }, ...props, children }),
2817
+ strong: ({ children, ...props }) => /* @__PURE__ */ jsx6("strong", { style: { fontWeight: 600 }, ...props, children }),
2751
2818
  // Emphasis/italic
2752
- em: ({ children, ...props }) => /* @__PURE__ */ jsx4("em", { style: { fontStyle: "italic" }, ...props, children }),
2819
+ em: ({ children, ...props }) => /* @__PURE__ */ jsx6("em", { style: { fontStyle: "italic" }, ...props, children }),
2753
2820
  // Horizontal rule
2754
- hr: (props) => /* @__PURE__ */ jsx4(
2821
+ hr: (props) => /* @__PURE__ */ jsx6(
2755
2822
  "hr",
2756
2823
  {
2757
2824
  style: {
@@ -2764,7 +2831,7 @@ var MarkdownRenderer = ({ content, className }) => {
2764
2831
  }
2765
2832
  ),
2766
2833
  // Tables (GFM)
2767
- table: ({ children, ...props }) => /* @__PURE__ */ jsx4("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ jsx4(
2834
+ table: ({ children, ...props }) => /* @__PURE__ */ jsx6("div", { style: { overflowX: "auto", marginTop: "0.5em", marginBottom: "0.5em" }, children: /* @__PURE__ */ jsx6(
2768
2835
  "table",
2769
2836
  {
2770
2837
  style: {
@@ -2776,7 +2843,7 @@ var MarkdownRenderer = ({ content, className }) => {
2776
2843
  children
2777
2844
  }
2778
2845
  ) }),
2779
- th: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2846
+ th: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2780
2847
  "th",
2781
2848
  {
2782
2849
  style: {
@@ -2790,7 +2857,7 @@ var MarkdownRenderer = ({ content, className }) => {
2790
2857
  children
2791
2858
  }
2792
2859
  ),
2793
- td: ({ children, ...props }) => /* @__PURE__ */ jsx4(
2860
+ td: ({ children, ...props }) => /* @__PURE__ */ jsx6(
2794
2861
  "td",
2795
2862
  {
2796
2863
  style: {
@@ -2808,32 +2875,32 @@ var MarkdownRenderer = ({ content, className }) => {
2808
2875
  };
2809
2876
 
2810
2877
  // src/react/ai/FloatingChatBox.tsx
2811
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2812
- var ChatIcon = () => /* @__PURE__ */ jsx5("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2813
- var CloseIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2814
- /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2815
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2878
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2879
+ var ChatIcon = () => /* @__PURE__ */ jsx7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
2880
+ var CloseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2881
+ /* @__PURE__ */ jsx7("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2882
+ /* @__PURE__ */ jsx7("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2816
2883
  ] });
2817
- var ExpandIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2818
- /* @__PURE__ */ jsx5("polyline", { points: "15 3 21 3 21 9" }),
2819
- /* @__PURE__ */ jsx5("polyline", { points: "9 21 3 21 3 15" }),
2820
- /* @__PURE__ */ jsx5("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2821
- /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2884
+ var ExpandIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2885
+ /* @__PURE__ */ jsx7("polyline", { points: "15 3 21 3 21 9" }),
2886
+ /* @__PURE__ */ jsx7("polyline", { points: "9 21 3 21 3 15" }),
2887
+ /* @__PURE__ */ jsx7("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
2888
+ /* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2822
2889
  ] });
2823
- var CollapseIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2824
- /* @__PURE__ */ jsx5("polyline", { points: "4 14 10 14 10 20" }),
2825
- /* @__PURE__ */ jsx5("polyline", { points: "20 10 14 10 14 4" }),
2826
- /* @__PURE__ */ jsx5("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2827
- /* @__PURE__ */ jsx5("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2890
+ var CollapseIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2891
+ /* @__PURE__ */ jsx7("polyline", { points: "4 14 10 14 10 20" }),
2892
+ /* @__PURE__ */ jsx7("polyline", { points: "20 10 14 10 14 4" }),
2893
+ /* @__PURE__ */ jsx7("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
2894
+ /* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
2828
2895
  ] });
2829
- var SendIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2830
- /* @__PURE__ */ jsx5("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2831
- /* @__PURE__ */ jsx5("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2896
+ var SendIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2897
+ /* @__PURE__ */ jsx7("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
2898
+ /* @__PURE__ */ jsx7("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
2832
2899
  ] });
2833
- var LayersIcon = () => /* @__PURE__ */ jsxs4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2834
- /* @__PURE__ */ jsx5("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2835
- /* @__PURE__ */ jsx5("polyline", { points: "2 17 12 22 22 17" }),
2836
- /* @__PURE__ */ jsx5("polyline", { points: "2 12 12 17 22 12" })
2900
+ var LayersIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2901
+ /* @__PURE__ */ jsx7("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
2902
+ /* @__PURE__ */ jsx7("polyline", { points: "2 17 12 22 22 17" }),
2903
+ /* @__PURE__ */ jsx7("polyline", { points: "2 12 12 17 22 12" })
2837
2904
  ] });
2838
2905
  var styles = {
2839
2906
  root: {
@@ -2842,8 +2909,8 @@ var styles = {
2842
2909
  // Floating button (closed state - wide with text, open state - circular with X)
2843
2910
  floatingButton: {
2844
2911
  position: "fixed",
2845
- bottom: 24,
2846
- right: 24,
2912
+ bottom: 16,
2913
+ right: 16,
2847
2914
  borderRadius: "999px",
2848
2915
  border: "none",
2849
2916
  cursor: "pointer",
@@ -2853,30 +2920,30 @@ var styles = {
2853
2920
  display: "flex",
2854
2921
  alignItems: "center",
2855
2922
  justifyContent: "center",
2856
- fontSize: 15,
2923
+ fontSize: 14,
2857
2924
  fontWeight: 600,
2858
2925
  zIndex: 99999,
2859
2926
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2860
2927
  },
2861
2928
  floatingButtonClosed: {
2862
- padding: "14px 24px",
2929
+ padding: "12px 20px",
2863
2930
  gap: 8
2864
2931
  },
2865
2932
  floatingButtonOpen: {
2866
- width: 56,
2867
- height: 56,
2933
+ width: 52,
2934
+ height: 52,
2868
2935
  padding: 0
2869
2936
  },
2870
2937
  floatingButtonMobile: {
2871
- width: 56,
2872
- height: 56,
2938
+ width: 52,
2939
+ height: 52,
2873
2940
  padding: 0
2874
2941
  },
2875
2942
  // Panel (expandable)
2876
2943
  panel: {
2877
2944
  position: "fixed",
2878
- bottom: 92,
2879
- right: 24,
2945
+ bottom: 80,
2946
+ right: 16,
2880
2947
  background: "#fff",
2881
2948
  borderRadius: 16,
2882
2949
  boxShadow: "0 20px 60px rgba(15, 23, 42, 0.3), 0 0 0 1px rgba(15, 23, 42, 0.05)",
@@ -2887,16 +2954,16 @@ var styles = {
2887
2954
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2888
2955
  },
2889
2956
  panelNormal: {
2890
- width: 400,
2891
- height: 550
2957
+ width: 360,
2958
+ height: 520
2892
2959
  },
2893
2960
  panelExpanded: {
2894
- width: 520,
2895
- height: 700
2961
+ width: 480,
2962
+ height: 640
2896
2963
  },
2897
2964
  // Header with green gradient
2898
2965
  header: {
2899
- padding: "16px 18px",
2966
+ padding: "14px 16px",
2900
2967
  background: "linear-gradient(135deg, #10b981, #059669)",
2901
2968
  color: "#fff",
2902
2969
  display: "flex",
@@ -2905,7 +2972,7 @@ var styles = {
2905
2972
  },
2906
2973
  title: {
2907
2974
  margin: 0,
2908
- fontSize: 16,
2975
+ fontSize: 15,
2909
2976
  fontWeight: 600,
2910
2977
  letterSpacing: "-0.01em"
2911
2978
  },
@@ -2918,8 +2985,8 @@ var styles = {
2918
2985
  border: "none",
2919
2986
  background: "rgba(255, 255, 255, 0.15)",
2920
2987
  color: "#fff",
2921
- width: 32,
2922
- height: 32,
2988
+ width: 30,
2989
+ height: 30,
2923
2990
  borderRadius: 8,
2924
2991
  cursor: "pointer",
2925
2992
  display: "flex",
@@ -2930,7 +2997,7 @@ var styles = {
2930
2997
  // Messages area
2931
2998
  messages: {
2932
2999
  flex: 1,
2933
- padding: "20px 18px",
3000
+ padding: "16px",
2934
3001
  overflowY: "auto",
2935
3002
  background: "#f8fafc",
2936
3003
  display: "flex",
@@ -2945,10 +3012,10 @@ var styles = {
2945
3012
  },
2946
3013
  messageBubble: {
2947
3014
  maxWidth: "85%",
2948
- padding: "12px 14px",
3015
+ padding: "10px 12px",
2949
3016
  borderRadius: 16,
2950
3017
  lineHeight: 1.5,
2951
- fontSize: 14,
3018
+ fontSize: 13,
2952
3019
  whiteSpace: "pre-wrap",
2953
3020
  wordBreak: "break-word"
2954
3021
  },
@@ -3062,7 +3129,7 @@ var styles = {
3062
3129
  // Input area
3063
3130
  inputWrapper: {
3064
3131
  borderTop: "1px solid #e2e8f0",
3065
- padding: "14px 16px",
3132
+ padding: "10px 14px",
3066
3133
  display: "flex",
3067
3134
  gap: 10,
3068
3135
  alignItems: "flex-end",
@@ -3073,8 +3140,8 @@ var styles = {
3073
3140
  resize: "none",
3074
3141
  borderRadius: 12,
3075
3142
  border: "1.5px solid #cbd5e1",
3076
- padding: "10px 12px",
3077
- fontSize: 14,
3143
+ padding: "8px 10px",
3144
+ fontSize: 13,
3078
3145
  fontFamily: "inherit",
3079
3146
  lineHeight: 1.4,
3080
3147
  transition: "border-color 0.2s"
@@ -3086,18 +3153,18 @@ var styles = {
3086
3153
  sendButton: {
3087
3154
  borderRadius: 12,
3088
3155
  border: "none",
3089
- padding: "10px 14px",
3156
+ padding: "8px 12px",
3090
3157
  background: "linear-gradient(135deg, #10b981, #059669)",
3091
3158
  color: "#fff",
3092
3159
  cursor: "pointer",
3093
- fontSize: 14,
3160
+ fontSize: 13,
3094
3161
  fontWeight: 600,
3095
3162
  display: "flex",
3096
3163
  alignItems: "center",
3097
3164
  justifyContent: "center",
3098
3165
  transition: "opacity 0.2s, transform 0.2s",
3099
- minWidth: 44,
3100
- height: 44
3166
+ minWidth: 40,
3167
+ height: 40
3101
3168
  },
3102
3169
  // Status messages
3103
3170
  statusNote: {
@@ -3127,42 +3194,42 @@ var FloatingChatBox = ({
3127
3194
  open: openProp
3128
3195
  }) => {
3129
3196
  const isControlled = openProp !== void 0;
3130
- const [internalOpen, setInternalOpen] = useState4(false);
3197
+ const [internalOpen, setInternalOpen] = useState5(false);
3131
3198
  const open = isControlled ? openProp : internalOpen;
3132
- const setOpen = useCallback3((value) => {
3199
+ const setOpen = useCallback4((value) => {
3133
3200
  const newValue = typeof value === "function" ? value(open) : value;
3134
3201
  if (!isControlled) {
3135
3202
  setInternalOpen(newValue);
3136
3203
  }
3137
3204
  onOpenChange?.(newValue);
3138
3205
  }, [isControlled, open, onOpenChange]);
3139
- const [expanded, setExpanded] = useState4(false);
3140
- const [messages, setMessages] = useState4([]);
3141
- const [inputValue, setInputValue] = useState4("");
3142
- const [conversationId, setConversationId] = useState4();
3143
- const [errorMessage, setErrorMessage] = useState4(null);
3144
- const [isFocused, setIsFocused] = useState4(false);
3145
- const [isMobile, setIsMobile] = useState4(false);
3146
- const messagesEndRef = useRef4(null);
3147
- const messagesContainerRef = useRef4(null);
3148
- const chatBoxRef = useRef4(null);
3149
- const chatConfig = useMemo3(() => {
3206
+ const [expanded, setExpanded] = useState5(false);
3207
+ const [messages, setMessages] = useState5([]);
3208
+ const [inputValue, setInputValue] = useState5("");
3209
+ const [conversationId, setConversationId] = useState5();
3210
+ const [errorMessage, setErrorMessage] = useState5(null);
3211
+ const [isFocused, setIsFocused] = useState5(false);
3212
+ const [isMobile, setIsMobile] = useState5(false);
3213
+ const messagesEndRef = useRef7(null);
3214
+ const messagesContainerRef = useRef7(null);
3215
+ const chatBoxRef = useRef7(null);
3216
+ const chatConfig = useMemo4(() => {
3150
3217
  if (!baseUrl) return void 0;
3151
3218
  return { baseUrl, accessToken, getAccessToken };
3152
3219
  }, [accessToken, baseUrl, getAccessToken]);
3153
3220
  const { sendMessage: sendMessage2, isStreaming, streamingText, completeResponse } = useSendMessageStream(chatConfig);
3154
3221
  const canSend = Boolean(mapId) && Boolean(baseUrl) && inputValue.trim().length > 0 && !isStreaming;
3155
- useEffect3(() => {
3222
+ useEffect6(() => {
3156
3223
  if (open && isMobile) {
3157
3224
  setExpanded(true);
3158
3225
  }
3159
3226
  }, [open, isMobile]);
3160
- const scrollToBottom = useCallback3(() => {
3227
+ const scrollToBottom = useCallback4(() => {
3161
3228
  if (messagesEndRef.current) {
3162
3229
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
3163
3230
  }
3164
3231
  }, []);
3165
- useEffect3(() => {
3232
+ useEffect6(() => {
3166
3233
  if (open && messages.length === 0) {
3167
3234
  setMessages([
3168
3235
  {
@@ -3173,10 +3240,10 @@ var FloatingChatBox = ({
3173
3240
  ]);
3174
3241
  }
3175
3242
  }, [open, messages.length]);
3176
- useEffect3(() => {
3243
+ useEffect6(() => {
3177
3244
  scrollToBottom();
3178
3245
  }, [messages, streamingText, scrollToBottom]);
3179
- useEffect3(() => {
3246
+ useEffect6(() => {
3180
3247
  if (!open) return;
3181
3248
  if (isMobile && expanded) return;
3182
3249
  const handleClickOutside = (event) => {
@@ -3189,7 +3256,7 @@ var FloatingChatBox = ({
3189
3256
  document.removeEventListener("mousedown", handleClickOutside);
3190
3257
  };
3191
3258
  }, [open, isMobile, expanded]);
3192
- useEffect3(() => {
3259
+ useEffect6(() => {
3193
3260
  if (typeof window === "undefined") return;
3194
3261
  const mediaQuery = window.matchMedia("(max-width: 768px)");
3195
3262
  const updateMobile = () => setIsMobile(mediaQuery.matches);
@@ -3207,7 +3274,7 @@ var FloatingChatBox = ({
3207
3274
  }
3208
3275
  };
3209
3276
  }, []);
3210
- useEffect3(() => {
3277
+ useEffect6(() => {
3211
3278
  if (typeof document === "undefined") return;
3212
3279
  if (!open || !isMobile) return;
3213
3280
  document.body.style.overflow = "hidden";
@@ -3215,10 +3282,10 @@ var FloatingChatBox = ({
3215
3282
  document.body.style.overflow = "";
3216
3283
  };
3217
3284
  }, [open, isMobile]);
3218
- const addMessage = useCallback3((message) => {
3285
+ const addMessage = useCallback4((message) => {
3219
3286
  setMessages((prev) => [...prev, message]);
3220
3287
  }, []);
3221
- const handleSend = useCallback3(async () => {
3288
+ const handleSend = useCallback4(async () => {
3222
3289
  if (!mapId) {
3223
3290
  setErrorMessage("Selecciona un mapa para usar el asistente.");
3224
3291
  return;
@@ -3253,11 +3320,11 @@ var FloatingChatBox = ({
3253
3320
  response
3254
3321
  });
3255
3322
  } catch (error) {
3256
- setErrorMessage(error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado.");
3323
+ setErrorMessage("Ocurri\xF3 un error al generar la respuesta.");
3257
3324
  addMessage({
3258
3325
  id: `error-${Date.now()}`,
3259
3326
  role: "assistant",
3260
- content: `\u274C Error: ${error instanceof Error ? error.message : "Ocurri\xF3 un error inesperado."}`
3327
+ content: "\u274C Ocurri\xF3 un error al generar la respuesta."
3261
3328
  });
3262
3329
  }
3263
3330
  }, [
@@ -3271,7 +3338,7 @@ var FloatingChatBox = ({
3271
3338
  sendMessage2,
3272
3339
  userId
3273
3340
  ]);
3274
- const handleKeyDown = useCallback3(
3341
+ const handleKeyDown = useCallback4(
3275
3342
  (event) => {
3276
3343
  if (event.key === "Enter" && !event.shiftKey) {
3277
3344
  event.preventDefault();
@@ -3282,20 +3349,20 @@ var FloatingChatBox = ({
3282
3349
  },
3283
3350
  [canSend, handleSend]
3284
3351
  );
3285
- const handleFollowUpClick = useCallback3((question) => {
3352
+ const handleFollowUpClick = useCallback4((question) => {
3286
3353
  setInputValue(question);
3287
3354
  }, []);
3288
3355
  const renderMetadata = (response) => {
3289
3356
  if (!response?.metadata) return null;
3290
3357
  const referencedLayers = response.metadata.referencedLayers;
3291
3358
  if (!referencedLayers || referencedLayers.length === 0) return null;
3292
- return /* @__PURE__ */ jsxs4("div", { style: styles.metadataSection, children: [
3293
- /* @__PURE__ */ jsxs4("div", { style: styles.metadataTitle, children: [
3294
- /* @__PURE__ */ jsx5(LayersIcon, {}),
3359
+ return /* @__PURE__ */ jsxs6("div", { style: styles.metadataSection, children: [
3360
+ /* @__PURE__ */ jsxs6("div", { style: styles.metadataTitle, children: [
3361
+ /* @__PURE__ */ jsx7(LayersIcon, {}),
3295
3362
  "Capas Analizadas"
3296
3363
  ] }),
3297
- /* @__PURE__ */ jsx5("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ jsxs4("li", { style: styles.metadataItem, children: [
3298
- /* @__PURE__ */ jsx5("strong", { children: layer.layerName }),
3364
+ /* @__PURE__ */ jsx7("ul", { style: styles.metadataList, children: referencedLayers.map((layer, index) => /* @__PURE__ */ jsxs6("li", { style: styles.metadataItem, children: [
3365
+ /* @__PURE__ */ jsx7("strong", { children: layer.layerName }),
3299
3366
  " (",
3300
3367
  layer.featureCount,
3301
3368
  " ",
@@ -3304,7 +3371,7 @@ var FloatingChatBox = ({
3304
3371
  ] }, index)) })
3305
3372
  ] });
3306
3373
  };
3307
- const handleActionClick = useCallback3((action) => {
3374
+ const handleActionClick = useCallback4((action) => {
3308
3375
  if (isStreaming) return;
3309
3376
  setOpen(false);
3310
3377
  requestAnimationFrame(() => {
@@ -3313,9 +3380,9 @@ var FloatingChatBox = ({
3313
3380
  }, [isStreaming, setOpen, onActionClick]);
3314
3381
  const renderActions = (response) => {
3315
3382
  if (!response?.suggestedActions?.length) return null;
3316
- return /* @__PURE__ */ jsxs4("div", { style: styles.actionsSection, children: [
3317
- /* @__PURE__ */ jsx5("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3318
- /* @__PURE__ */ jsx5("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ jsx5(
3383
+ return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
3384
+ /* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Acciones Sugeridas" }),
3385
+ /* @__PURE__ */ jsx7("div", { style: styles.actionsGrid, children: response.suggestedActions.map((action, index) => /* @__PURE__ */ jsx7(
3319
3386
  "button",
3320
3387
  {
3321
3388
  type: "button",
@@ -3346,9 +3413,9 @@ var FloatingChatBox = ({
3346
3413
  };
3347
3414
  const renderFollowUps = (response) => {
3348
3415
  if (!response?.followUpQuestions?.length) return null;
3349
- return /* @__PURE__ */ jsxs4("div", { style: styles.actionsSection, children: [
3350
- /* @__PURE__ */ jsx5("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3351
- /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ jsx5(
3416
+ return /* @__PURE__ */ jsxs6("div", { style: styles.actionsSection, children: [
3417
+ /* @__PURE__ */ jsx7("div", { style: styles.sectionLabel, children: "Preguntas Relacionadas" }),
3418
+ /* @__PURE__ */ jsx7("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: response.followUpQuestions.map((question, index) => /* @__PURE__ */ jsx7(
3352
3419
  "button",
3353
3420
  {
3354
3421
  type: "button",
@@ -3377,8 +3444,8 @@ var FloatingChatBox = ({
3377
3444
  )) })
3378
3445
  ] });
3379
3446
  };
3380
- const chatContent = /* @__PURE__ */ jsxs4("div", { style: styles.root, children: [
3381
- /* @__PURE__ */ jsx5("style", { children: `
3447
+ const chatContent = /* @__PURE__ */ jsxs6("div", { style: styles.root, children: [
3448
+ /* @__PURE__ */ jsx7("style", { children: `
3382
3449
  @keyframes zenitBlink {
3383
3450
  0%, 49% { opacity: 1; }
3384
3451
  50%, 100% { opacity: 0; }
@@ -3421,11 +3488,13 @@ var FloatingChatBox = ({
3421
3488
  @media (max-width: 768px) {
3422
3489
  .zenit-chat-panel.zenit-chat-panel--fullscreen {
3423
3490
  position: fixed !important;
3424
- inset: 0 !important;
3425
- width: 100vw !important;
3426
- max-width: 100vw !important;
3427
- height: 100vh !important;
3428
- height: 100dvh !important;
3491
+ left: 0 !important;
3492
+ right: 0 !important;
3493
+ top: 4rem !important;
3494
+ bottom: 0 !important;
3495
+ width: 100% !important;
3496
+ max-width: 100% !important;
3497
+ height: auto !important;
3429
3498
  border-radius: 0 !important;
3430
3499
  display: flex !important;
3431
3500
  flex-direction: column !important;
@@ -3448,7 +3517,7 @@ var FloatingChatBox = ({
3448
3517
  }
3449
3518
  }
3450
3519
  ` }),
3451
- open && /* @__PURE__ */ jsxs4(
3520
+ open && /* @__PURE__ */ jsxs6(
3452
3521
  "div",
3453
3522
  {
3454
3523
  ref: chatBoxRef,
@@ -3458,10 +3527,10 @@ var FloatingChatBox = ({
3458
3527
  ...expanded ? styles.panelExpanded : styles.panelNormal
3459
3528
  },
3460
3529
  children: [
3461
- /* @__PURE__ */ jsxs4("header", { style: styles.header, children: [
3462
- /* @__PURE__ */ jsx5("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3463
- /* @__PURE__ */ jsxs4("div", { style: styles.headerButtons, children: [
3464
- /* @__PURE__ */ jsx5(
3530
+ /* @__PURE__ */ jsxs6("header", { style: styles.header, children: [
3531
+ /* @__PURE__ */ jsx7("h3", { style: styles.title, children: "Asistente Zenit AI" }),
3532
+ /* @__PURE__ */ jsxs6("div", { style: styles.headerButtons, children: [
3533
+ /* @__PURE__ */ jsx7(
3465
3534
  "button",
3466
3535
  {
3467
3536
  type: "button",
@@ -3474,10 +3543,10 @@ var FloatingChatBox = ({
3474
3543
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3475
3544
  },
3476
3545
  "aria-label": expanded ? "Contraer" : "Expandir",
3477
- children: expanded ? /* @__PURE__ */ jsx5(CollapseIcon, {}) : /* @__PURE__ */ jsx5(ExpandIcon, {})
3546
+ children: expanded ? /* @__PURE__ */ jsx7(CollapseIcon, {}) : /* @__PURE__ */ jsx7(ExpandIcon, {})
3478
3547
  }
3479
3548
  ),
3480
- /* @__PURE__ */ jsx5(
3549
+ /* @__PURE__ */ jsx7(
3481
3550
  "button",
3482
3551
  {
3483
3552
  type: "button",
@@ -3490,20 +3559,20 @@ var FloatingChatBox = ({
3490
3559
  e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
3491
3560
  },
3492
3561
  "aria-label": "Cerrar",
3493
- children: /* @__PURE__ */ jsx5(CloseIcon, {})
3562
+ children: /* @__PURE__ */ jsx7(CloseIcon, {})
3494
3563
  }
3495
3564
  )
3496
3565
  ] })
3497
3566
  ] }),
3498
- /* @__PURE__ */ jsxs4("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3499
- messages.map((message) => /* @__PURE__ */ jsx5(
3567
+ /* @__PURE__ */ jsxs6("div", { ref: messagesContainerRef, className: "zenit-ai-body", style: styles.messages, children: [
3568
+ messages.map((message) => /* @__PURE__ */ jsx7(
3500
3569
  "div",
3501
3570
  {
3502
3571
  style: {
3503
3572
  ...styles.messageWrapper,
3504
3573
  alignItems: message.role === "user" ? "flex-end" : "flex-start"
3505
3574
  },
3506
- children: /* @__PURE__ */ jsxs4(
3575
+ children: /* @__PURE__ */ jsxs6(
3507
3576
  "div",
3508
3577
  {
3509
3578
  style: {
@@ -3511,7 +3580,7 @@ var FloatingChatBox = ({
3511
3580
  ...message.role === "user" ? styles.userMessage : styles.assistantMessage
3512
3581
  },
3513
3582
  children: [
3514
- message.role === "assistant" ? /* @__PURE__ */ jsx5(MarkdownRenderer, { content: message.content }) : message.content,
3583
+ message.role === "assistant" ? /* @__PURE__ */ jsx7(MarkdownRenderer, { content: message.content }) : message.content,
3515
3584
  message.role === "assistant" && renderMetadata(message.response),
3516
3585
  message.role === "assistant" && renderActions(message.response),
3517
3586
  message.role === "assistant" && renderFollowUps(message.response)
@@ -3521,39 +3590,39 @@ var FloatingChatBox = ({
3521
3590
  },
3522
3591
  message.id
3523
3592
  )),
3524
- isStreaming && /* @__PURE__ */ jsx5(
3593
+ isStreaming && /* @__PURE__ */ jsx7(
3525
3594
  "div",
3526
3595
  {
3527
3596
  style: {
3528
3597
  ...styles.messageWrapper,
3529
3598
  alignItems: "flex-start"
3530
3599
  },
3531
- children: /* @__PURE__ */ jsx5(
3600
+ children: /* @__PURE__ */ jsx7(
3532
3601
  "div",
3533
3602
  {
3534
3603
  style: {
3535
3604
  ...styles.messageBubble,
3536
3605
  ...styles.assistantMessage
3537
3606
  },
3538
- children: streamingText ? /* @__PURE__ */ jsxs4(Fragment, { children: [
3539
- /* @__PURE__ */ jsx5(MarkdownRenderer, { content: streamingText }),
3540
- /* @__PURE__ */ jsx5("span", { style: styles.cursor })
3541
- ] }) : /* @__PURE__ */ jsxs4("div", { style: styles.thinkingText, children: [
3542
- /* @__PURE__ */ jsx5("span", { children: "Analizando" }),
3543
- /* @__PURE__ */ jsxs4("div", { style: styles.typingIndicator, children: [
3544
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3545
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3546
- /* @__PURE__ */ jsx5("div", { className: "zenit-typing-dot", style: styles.typingDot })
3607
+ children: streamingText ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
3608
+ /* @__PURE__ */ jsx7(MarkdownRenderer, { content: streamingText }),
3609
+ /* @__PURE__ */ jsx7("span", { style: styles.cursor })
3610
+ ] }) : /* @__PURE__ */ jsxs6("div", { style: styles.thinkingText, children: [
3611
+ /* @__PURE__ */ jsx7("span", { children: "Pensando" }),
3612
+ /* @__PURE__ */ jsxs6("div", { style: styles.typingIndicator, children: [
3613
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3614
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot }),
3615
+ /* @__PURE__ */ jsx7("div", { className: "zenit-typing-dot", style: styles.typingDot })
3547
3616
  ] })
3548
3617
  ] })
3549
3618
  }
3550
3619
  )
3551
3620
  }
3552
3621
  ),
3553
- /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
3622
+ /* @__PURE__ */ jsx7("div", { ref: messagesEndRef })
3554
3623
  ] }),
3555
- /* @__PURE__ */ jsxs4("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3556
- /* @__PURE__ */ jsx5(
3624
+ /* @__PURE__ */ jsxs6("div", { className: "zenit-ai-input-area", style: styles.inputWrapper, children: [
3625
+ /* @__PURE__ */ jsx7(
3557
3626
  "textarea",
3558
3627
  {
3559
3628
  style: {
@@ -3570,7 +3639,7 @@ var FloatingChatBox = ({
3570
3639
  disabled: !mapId || !baseUrl || isStreaming
3571
3640
  }
3572
3641
  ),
3573
- /* @__PURE__ */ jsx5(
3642
+ /* @__PURE__ */ jsx7(
3574
3643
  "button",
3575
3644
  {
3576
3645
  type: "button",
@@ -3579,36 +3648,38 @@ var FloatingChatBox = ({
3579
3648
  onClick: () => void handleSend(),
3580
3649
  disabled: !canSend,
3581
3650
  "aria-label": "Enviar mensaje",
3582
- children: /* @__PURE__ */ jsx5(SendIcon, {})
3651
+ children: /* @__PURE__ */ jsx7(SendIcon, {})
3583
3652
  }
3584
3653
  )
3585
3654
  ] }),
3586
- errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.errorText, children: errorMessage }),
3587
- !mapId && !errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3588
- !baseUrl && !errorMessage && /* @__PURE__ */ jsx5("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3655
+ errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.errorText, children: errorMessage }),
3656
+ isStreaming && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Generando sugerencias..." }),
3657
+ !mapId && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Selecciona un mapa para usar el asistente" }),
3658
+ !baseUrl && !errorMessage && /* @__PURE__ */ jsx7("div", { style: styles.statusNote, children: "Configura la baseUrl del SDK" })
3589
3659
  ]
3590
3660
  }
3591
3661
  ),
3592
- !(hideButton && !open) && /* @__PURE__ */ jsx5(
3662
+ !(hideButton && !open) && /* @__PURE__ */ jsx7(
3593
3663
  "button",
3594
3664
  {
3595
3665
  type: "button",
3596
3666
  className: `zenit-ai-button ${open ? "open" : ""}${open && isMobile ? " zenit-ai-button--hidden-mobile" : ""}`,
3597
3667
  style: {
3598
3668
  ...styles.floatingButton,
3599
- ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed
3669
+ ...open ? styles.floatingButtonOpen : isMobile ? styles.floatingButtonMobile : styles.floatingButtonClosed,
3670
+ zIndex: open ? 100001 : 99999
3600
3671
  },
3601
3672
  onClick: () => setOpen((prev) => !prev),
3602
3673
  "aria-label": open ? "Cerrar asistente" : "Abrir asistente Zenit AI",
3603
- children: open ? /* @__PURE__ */ jsx5(CloseIcon, {}) : /* @__PURE__ */ jsxs4(Fragment, { children: [
3604
- /* @__PURE__ */ jsx5(ChatIcon, {}),
3605
- !isMobile && /* @__PURE__ */ jsx5("span", { children: "Asistente IA" })
3674
+ children: open ? /* @__PURE__ */ jsx7(CloseIcon, {}) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
3675
+ /* @__PURE__ */ jsx7(ChatIcon, {}),
3676
+ !isMobile && /* @__PURE__ */ jsx7("span", { children: "Asistente IA" })
3606
3677
  ] })
3607
3678
  }
3608
3679
  )
3609
3680
  ] });
3610
3681
  if (typeof document !== "undefined") {
3611
- return createPortal(chatContent, document.body);
3682
+ return createPortal2(chatContent, document.body);
3612
3683
  }
3613
3684
  return chatContent;
3614
3685
  };
@@ -3651,4 +3722,4 @@ export {
3651
3722
  useSendMessageStream,
3652
3723
  FloatingChatBox
3653
3724
  };
3654
- //# sourceMappingURL=chunk-52CLFD4L.mjs.map
3725
+ //# sourceMappingURL=chunk-PCTRVN4O.mjs.map