@mapbox/mcp-server 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/resources/place-photo/PlacePhotoResource.d.ts +57 -0
- package/dist/commonjs/resources/place-photo/PlacePhotoResource.d.ts.map +1 -0
- package/dist/commonjs/resources/place-photo/PlacePhotoResource.js +202 -0
- package/dist/commonjs/resources/place-photo/PlacePhotoResource.js.map +1 -0
- package/dist/commonjs/resources/resourceRegistry.d.ts +3 -1
- package/dist/commonjs/resources/resourceRegistry.d.ts.map +1 -1
- package/dist/commonjs/resources/resourceRegistry.js +5 -0
- package/dist/commonjs/resources/resourceRegistry.js.map +1 -1
- package/dist/commonjs/resources/tool-response/ToolResponseResource.d.ts +20 -0
- package/dist/commonjs/resources/tool-response/ToolResponseResource.d.ts.map +1 -0
- package/dist/commonjs/resources/tool-response/ToolResponseResource.js +83 -0
- package/dist/commonjs/resources/tool-response/ToolResponseResource.js.map +1 -0
- package/dist/commonjs/resources/ui-apps/mapAppHtml.d.ts.map +1 -1
- package/dist/commonjs/resources/ui-apps/mapAppHtml.js +484 -11
- package/dist/commonjs/resources/ui-apps/mapAppHtml.js.map +1 -1
- package/dist/commonjs/tools/category-list-tool/CategoryListTool.d.ts.map +1 -1
- package/dist/commonjs/tools/category-list-tool/CategoryListTool.input.schema.js +1 -1
- package/dist/commonjs/tools/category-list-tool/CategoryListTool.input.schema.js.map +1 -1
- package/dist/commonjs/tools/category-list-tool/CategoryListTool.js +1 -1
- package/dist/commonjs/tools/category-list-tool/CategoryListTool.js.map +1 -1
- package/dist/commonjs/tools/directions-tool/DirectionsTool.d.ts.map +1 -1
- package/dist/commonjs/tools/directions-tool/DirectionsTool.input.schema.js +3 -3
- package/dist/commonjs/tools/directions-tool/DirectionsTool.input.schema.js.map +1 -1
- package/dist/commonjs/tools/directions-tool/DirectionsTool.js +17 -17
- package/dist/commonjs/tools/directions-tool/DirectionsTool.js.map +1 -1
- package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.d.ts.map +1 -1
- package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.js +1 -1
- package/dist/commonjs/tools/ground-location-tool/GroundLocationTool.js.map +1 -1
- package/dist/commonjs/tools/isochrone-tool/IsochroneTool.d.ts.map +1 -1
- package/dist/commonjs/tools/isochrone-tool/IsochroneTool.js +6 -13
- package/dist/commonjs/tools/isochrone-tool/IsochroneTool.js.map +1 -1
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.d.ts.map +1 -1
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.d.ts +4 -0
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.d.ts.map +1 -1
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.js +18 -2
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.input.schema.js.map +1 -1
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.js +9 -5
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.js.map +1 -1
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.output.schema.d.ts +4 -0
- package/dist/commonjs/tools/render-map-tool/RenderMapTool.output.schema.d.ts.map +1 -1
- package/dist/commonjs/utils/directionsRef.d.ts +27 -0
- package/dist/commonjs/utils/directionsRef.d.ts.map +1 -0
- package/dist/commonjs/utils/directionsRef.js +52 -0
- package/dist/commonjs/utils/directionsRef.js.map +1 -0
- package/dist/commonjs/utils/index.d.ts +8 -0
- package/dist/commonjs/utils/index.d.ts.map +1 -1
- package/dist/commonjs/utils/index.js +23 -1
- package/dist/commonjs/utils/index.js.map +1 -1
- package/dist/commonjs/utils/mapAppPayload.d.ts +21 -0
- package/dist/commonjs/utils/mapAppPayload.d.ts.map +1 -1
- package/dist/commonjs/utils/mapAppPayload.js +5 -1
- package/dist/commonjs/utils/mapAppPayload.js.map +1 -1
- package/dist/commonjs/utils/responseRef.d.ts +33 -0
- package/dist/commonjs/utils/responseRef.d.ts.map +1 -0
- package/dist/commonjs/utils/responseRef.js +73 -0
- package/dist/commonjs/utils/responseRef.js.map +1 -0
- package/dist/commonjs/utils/serverInstructions.d.ts +2 -2
- package/dist/commonjs/utils/serverInstructions.js +2 -2
- package/dist/commonjs/utils/storeMapPayload.js +1 -1
- package/dist/commonjs/utils/storeMapPayload.js.map +1 -1
- package/dist/commonjs/utils/urlSafety.d.ts +6 -0
- package/dist/commonjs/utils/urlSafety.d.ts.map +1 -1
- package/dist/commonjs/utils/urlSafety.js +15 -4
- package/dist/commonjs/utils/urlSafety.js.map +1 -1
- package/dist/commonjs/version.json +3 -3
- package/dist/esm/resources/place-photo/PlacePhotoResource.d.ts +57 -0
- package/dist/esm/resources/place-photo/PlacePhotoResource.d.ts.map +1 -0
- package/dist/esm/resources/place-photo/PlacePhotoResource.js +198 -0
- package/dist/esm/resources/place-photo/PlacePhotoResource.js.map +1 -0
- package/dist/esm/resources/resourceRegistry.d.ts +3 -1
- package/dist/esm/resources/resourceRegistry.d.ts.map +1 -1
- package/dist/esm/resources/resourceRegistry.js +5 -0
- package/dist/esm/resources/resourceRegistry.js.map +1 -1
- package/dist/esm/resources/tool-response/ToolResponseResource.d.ts +20 -0
- package/dist/esm/resources/tool-response/ToolResponseResource.d.ts.map +1 -0
- package/dist/esm/resources/tool-response/ToolResponseResource.js +79 -0
- package/dist/esm/resources/tool-response/ToolResponseResource.js.map +1 -0
- package/dist/esm/resources/ui-apps/mapAppHtml.d.ts.map +1 -1
- package/dist/esm/resources/ui-apps/mapAppHtml.js +484 -11
- package/dist/esm/resources/ui-apps/mapAppHtml.js.map +1 -1
- package/dist/esm/tools/category-list-tool/CategoryListTool.d.ts.map +1 -1
- package/dist/esm/tools/category-list-tool/CategoryListTool.input.schema.js +1 -1
- package/dist/esm/tools/category-list-tool/CategoryListTool.input.schema.js.map +1 -1
- package/dist/esm/tools/category-list-tool/CategoryListTool.js +1 -1
- package/dist/esm/tools/category-list-tool/CategoryListTool.js.map +1 -1
- package/dist/esm/tools/directions-tool/DirectionsTool.d.ts.map +1 -1
- package/dist/esm/tools/directions-tool/DirectionsTool.input.schema.js +3 -3
- package/dist/esm/tools/directions-tool/DirectionsTool.input.schema.js.map +1 -1
- package/dist/esm/tools/directions-tool/DirectionsTool.js +17 -17
- package/dist/esm/tools/directions-tool/DirectionsTool.js.map +1 -1
- package/dist/esm/tools/ground-location-tool/GroundLocationTool.d.ts.map +1 -1
- package/dist/esm/tools/ground-location-tool/GroundLocationTool.js +1 -1
- package/dist/esm/tools/ground-location-tool/GroundLocationTool.js.map +1 -1
- package/dist/esm/tools/isochrone-tool/IsochroneTool.d.ts.map +1 -1
- package/dist/esm/tools/isochrone-tool/IsochroneTool.js +6 -13
- package/dist/esm/tools/isochrone-tool/IsochroneTool.js.map +1 -1
- package/dist/esm/tools/render-map-tool/RenderMapTool.d.ts.map +1 -1
- package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.d.ts +4 -0
- package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.d.ts.map +1 -1
- package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.js +18 -2
- package/dist/esm/tools/render-map-tool/RenderMapTool.input.schema.js.map +1 -1
- package/dist/esm/tools/render-map-tool/RenderMapTool.js +9 -5
- package/dist/esm/tools/render-map-tool/RenderMapTool.js.map +1 -1
- package/dist/esm/tools/render-map-tool/RenderMapTool.output.schema.d.ts +4 -0
- package/dist/esm/tools/render-map-tool/RenderMapTool.output.schema.d.ts.map +1 -1
- package/dist/esm/utils/directionsRef.d.ts +27 -0
- package/dist/esm/utils/directionsRef.d.ts.map +1 -0
- package/dist/esm/utils/directionsRef.js +49 -0
- package/dist/esm/utils/directionsRef.js.map +1 -0
- package/dist/esm/utils/index.d.ts +8 -0
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +15 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/mapAppPayload.d.ts +21 -0
- package/dist/esm/utils/mapAppPayload.d.ts.map +1 -1
- package/dist/esm/utils/mapAppPayload.js +5 -1
- package/dist/esm/utils/mapAppPayload.js.map +1 -1
- package/dist/esm/utils/responseRef.d.ts +33 -0
- package/dist/esm/utils/responseRef.d.ts.map +1 -0
- package/dist/esm/utils/responseRef.js +68 -0
- package/dist/esm/utils/responseRef.js.map +1 -0
- package/dist/esm/utils/serverInstructions.d.ts +2 -2
- package/dist/esm/utils/serverInstructions.js +2 -2
- package/dist/esm/utils/storeMapPayload.js +1 -1
- package/dist/esm/utils/storeMapPayload.js.map +1 -1
- package/dist/esm/utils/urlSafety.d.ts +6 -0
- package/dist/esm/utils/urlSafety.d.ts.map +1 -1
- package/dist/esm/utils/urlSafety.js +14 -4
- package/dist/esm/utils/urlSafety.js.map +1 -1
- package/dist/esm/version.json +3 -3
- package/package.json +9 -2
|
@@ -71,12 +71,51 @@ export function renderMapAppHtml(params) {
|
|
|
71
71
|
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
|
|
72
72
|
cursor: pointer;
|
|
73
73
|
}
|
|
74
|
+
#side-panel {
|
|
75
|
+
position: absolute; top: 12px; right: 12px; bottom: 12px; z-index: 10;
|
|
76
|
+
width: 280px; max-width: calc(100% - 24px);
|
|
77
|
+
background: rgba(15, 23, 42, 0.88); color: #f1f5f9;
|
|
78
|
+
border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
79
|
+
display: flex; flex-direction: column; overflow: hidden;
|
|
80
|
+
}
|
|
81
|
+
#side-panel .panel-head {
|
|
82
|
+
padding: 10px 12px; font-size: 13px; font-weight: 600;
|
|
83
|
+
border-bottom: 1px solid rgba(255,255,255,0.12); flex: 0 0 auto;
|
|
84
|
+
display: flex; justify-content: space-between; gap: 8px; cursor: pointer;
|
|
85
|
+
}
|
|
86
|
+
/* Click the heading to collapse the list (see togglePanel). */
|
|
87
|
+
#side-panel .panel-head::after { content: '▾'; opacity: 0.7; }
|
|
88
|
+
#side-panel.collapsed { bottom: auto; }
|
|
89
|
+
#side-panel.collapsed .panel-list { display: none; }
|
|
90
|
+
#side-panel.collapsed .panel-head { border-bottom: none; }
|
|
91
|
+
#side-panel.collapsed .panel-head::after { content: '▸'; }
|
|
92
|
+
#side-panel .panel-list { list-style: none; margin: 0; padding: 6px; overflow-y: auto; flex: 1; }
|
|
93
|
+
#side-panel .panel-item {
|
|
94
|
+
display: flex; align-items: flex-start; gap: 6px; padding: 6px; border-radius: 6px; cursor: pointer;
|
|
95
|
+
}
|
|
96
|
+
#side-panel .panel-item:hover { background: rgba(255,255,255,0.08); }
|
|
97
|
+
/* Only added once a photo has loaded (see loadPanelPhoto), at the end of
|
|
98
|
+
the row so names stay aligned whether or not a row has one. */
|
|
99
|
+
#side-panel .panel-thumb {
|
|
100
|
+
flex: 0 0 auto; width: 40px; height: 40px; border-radius: 6px; margin-left: auto;
|
|
101
|
+
background-size: cover; background-position: center;
|
|
102
|
+
}
|
|
103
|
+
/* Visit-order badge, matching the numbered map marker. */
|
|
104
|
+
#side-panel .panel-badge {
|
|
105
|
+
flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%; margin-top: 1px;
|
|
106
|
+
background: #f97316; color: #1a0f04;
|
|
107
|
+
display: flex; align-items: center; justify-content: center;
|
|
108
|
+
font-size: 10px; font-weight: 700;
|
|
109
|
+
}
|
|
110
|
+
#side-panel .panel-name { font-size: 12.5px; font-weight: 600; }
|
|
111
|
+
#side-panel .panel-meta { font-size: 11px; color: #cbd5e1; margin-top: 2px; }
|
|
74
112
|
</style>
|
|
75
113
|
</head>
|
|
76
114
|
<body>
|
|
77
115
|
<div id="map"></div>
|
|
78
116
|
<div id="summary" style="display:none"></div>
|
|
79
117
|
<div id="legend" style="display:none"></div>
|
|
118
|
+
<div id="side-panel" style="display:none"></div>
|
|
80
119
|
<div id="loading">Loading…</div>
|
|
81
120
|
<div id="error" style="display:none"></div>
|
|
82
121
|
${initialDataScript}
|
|
@@ -90,6 +129,11 @@ ${initialDataScript}
|
|
|
90
129
|
var errorEl = document.getElementById('error');
|
|
91
130
|
var summaryEl = document.getElementById('summary');
|
|
92
131
|
var legendEl = document.getElementById('legend');
|
|
132
|
+
var panelEl = document.getElementById('side-panel');
|
|
133
|
+
// Populated by buildSearchMiniPayload's marker tagging so the panel (click
|
|
134
|
+
// -> flyTo) and Place Details enrichment can find a marker by mapbox_id
|
|
135
|
+
// without re-querying the map for it.
|
|
136
|
+
var markersById = {};
|
|
93
137
|
|
|
94
138
|
var map = null;
|
|
95
139
|
var mapLoaded = false;
|
|
@@ -109,7 +153,9 @@ ${initialDataScript}
|
|
|
109
153
|
var messageId = 0;
|
|
110
154
|
var pendingRequests = new Map();
|
|
111
155
|
|
|
112
|
-
|
|
156
|
+
// timeoutMs (optional) rejects a request nobody answers, for hosts that
|
|
157
|
+
// don't implement the method.
|
|
158
|
+
function sendRequest(method, params, timeoutMs) {
|
|
113
159
|
var id = ++messageId;
|
|
114
160
|
try {
|
|
115
161
|
window.parent.postMessage({
|
|
@@ -118,6 +164,13 @@ ${initialDataScript}
|
|
|
118
164
|
} catch (_) {}
|
|
119
165
|
return new Promise(function(resolve, reject) {
|
|
120
166
|
pendingRequests.set(id, { resolve: resolve, reject: reject });
|
|
167
|
+
if (timeoutMs) {
|
|
168
|
+
setTimeout(function() {
|
|
169
|
+
if (!pendingRequests.has(id)) return;
|
|
170
|
+
pendingRequests.delete(id);
|
|
171
|
+
reject(new Error(method + ' timed out'));
|
|
172
|
+
}, timeoutMs);
|
|
173
|
+
}
|
|
121
174
|
});
|
|
122
175
|
}
|
|
123
176
|
function sendNotification(method, params) {
|
|
@@ -152,13 +205,22 @@ ${initialDataScript}
|
|
|
152
205
|
}
|
|
153
206
|
});
|
|
154
207
|
|
|
208
|
+
// Set once a host answers ui/initialize, even with an error. A page from
|
|
209
|
+
// export_map_html_tool framed by some other site never gets an answer.
|
|
210
|
+
var hostAnswered = false;
|
|
155
211
|
sendRequest('ui/initialize', {
|
|
156
212
|
protocolVersion: '2026-01-26',
|
|
157
213
|
appCapabilities: {},
|
|
158
214
|
appInfo: { name: 'Mapbox Map', version: '1.0.0' }
|
|
159
215
|
}).then(
|
|
160
|
-
function() {
|
|
161
|
-
|
|
216
|
+
function() {
|
|
217
|
+
hostAnswered = true;
|
|
218
|
+
sendNotification('ui/notifications/initialized', {});
|
|
219
|
+
},
|
|
220
|
+
function() {
|
|
221
|
+
hostAnswered = true;
|
|
222
|
+
sendNotification('ui/notifications/initialized', {});
|
|
223
|
+
}
|
|
162
224
|
);
|
|
163
225
|
|
|
164
226
|
// --- Map setup ------------------------------------------------------------
|
|
@@ -362,6 +424,13 @@ ${initialDataScript}
|
|
|
362
424
|
trackedLayerIds = [];
|
|
363
425
|
trackedSourceIds = [];
|
|
364
426
|
trackedMarkers = [];
|
|
427
|
+
markersById = {};
|
|
428
|
+
panelEl.style.display = 'none';
|
|
429
|
+
clearPanel();
|
|
430
|
+
// Place Details responses still in flight belong to the old render.
|
|
431
|
+
panelGeneration += 1;
|
|
432
|
+
panelCount = 0;
|
|
433
|
+
panelIds = {};
|
|
365
434
|
}
|
|
366
435
|
|
|
367
436
|
function bboxAccumulator() {
|
|
@@ -447,14 +516,108 @@ ${initialDataScript}
|
|
|
447
516
|
if (m.popup) marker.setPopup(new mapboxgl.Popup().setText(m.popup));
|
|
448
517
|
marker.addTo(map);
|
|
449
518
|
trackedMarkers.push(marker);
|
|
519
|
+
// Tagged by buildSearchMiniPayload with the result's mapbox_id so the
|
|
520
|
+
// side panel (click-to-flyTo) and Place Details enrichment can look the
|
|
521
|
+
// marker back up later without walking trackedMarkers.
|
|
522
|
+
if (m.id) markersById[m.id] = { marker: marker, popupText: m.popup || '' };
|
|
450
523
|
bbox.extend(m.coordinates);
|
|
451
524
|
}
|
|
452
525
|
|
|
526
|
+
// Guarantees a marker that opts into the side panel (has .id) also gets
|
|
527
|
+
// a visible number on the map matching its panel row, unless the caller
|
|
528
|
+
// already gave it an explicit label. Without this, a caller that builds
|
|
529
|
+
// plain unlabeled pins for POI markers (observed live: Claude did this
|
|
530
|
+
// for inline markers, leaving the map showing generic blue pins with no
|
|
531
|
+
// way to tell which panel row is which) breaks the whole point of the
|
|
532
|
+
// panel numbering. Mutates markers in place, so it must run before
|
|
533
|
+
// addOneMarker draws them.
|
|
534
|
+
//
|
|
535
|
+
// Numbering runs across the whole render, not per call: a render can
|
|
536
|
+
// draw inline markers and then merge in self-fetched search results, and
|
|
537
|
+
// each set arrives numbered from 1. The first set keeps the caller's
|
|
538
|
+
// numbers when they're unique (a visit order, say); a later set, or one
|
|
539
|
+
// with repeated numbers, is renumbered to continue the panel's count.
|
|
540
|
+
// Letter labels ("A") are kept as they are. A place already in the panel
|
|
541
|
+
// this render is dropped rather than drawn and listed twice. Returns the
|
|
542
|
+
// markers to draw.
|
|
543
|
+
function applyPanelNumbering(markers) {
|
|
544
|
+
if (!Array.isArray(markers)) return [];
|
|
545
|
+
var seen = {};
|
|
546
|
+
var fresh = markers.filter(function(m) {
|
|
547
|
+
if (!m || !m.id) return true;
|
|
548
|
+
if (panelIds[m.id] || seen[m.id]) return false;
|
|
549
|
+
seen[m.id] = true;
|
|
550
|
+
return true;
|
|
551
|
+
});
|
|
552
|
+
var keep = panelCount === 0;
|
|
553
|
+
var used = {};
|
|
554
|
+
fresh.forEach(function(m) {
|
|
555
|
+
if (!m || !m.id || !isWholeNumber(m.label)) return;
|
|
556
|
+
if (used[m.label]) keep = false;
|
|
557
|
+
used[m.label] = true;
|
|
558
|
+
});
|
|
559
|
+
if (keep) {
|
|
560
|
+
Object.keys(used).forEach(function(n) {
|
|
561
|
+
panelCount = Math.max(panelCount, parseInt(n, 10));
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
fresh.forEach(function(m) {
|
|
565
|
+
if (!m || !m.id) return;
|
|
566
|
+
if (m.label && (keep || !isWholeNumber(m.label))) return;
|
|
567
|
+
panelCount += 1;
|
|
568
|
+
var n = String(panelCount);
|
|
569
|
+
if (!m.label) {
|
|
570
|
+
if (m.style !== 'start' && m.style !== 'end') m.style = 'numbered';
|
|
571
|
+
if (!m.color) m.color = '#f97316';
|
|
572
|
+
} else if (typeof m.popup === 'string' && m.popup.indexOf(m.label + '. ') === 0) {
|
|
573
|
+
// Search popups start with the old number ("3. Name").
|
|
574
|
+
m.popup = n + '. ' + m.popup.slice(String(m.label).length + 2);
|
|
575
|
+
}
|
|
576
|
+
m.label = n;
|
|
577
|
+
});
|
|
578
|
+
return fresh;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function isWholeNumber(label) {
|
|
582
|
+
if (label === undefined || label === null || label === '') return false;
|
|
583
|
+
var n = parseInt(label, 10);
|
|
584
|
+
return n > 0 && String(n) === String(label);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Bottom padding that keeps fitted data out from under the legend
|
|
588
|
+
// (bottom-left), unless the map is too short to spare the room.
|
|
589
|
+
function bottomPadding() {
|
|
590
|
+
if (legendEl.style.display !== 'block') return 50;
|
|
591
|
+
var bottom = (legendEl.offsetHeight || 0) + 24;
|
|
592
|
+
var el = document.getElementById('map');
|
|
593
|
+
var height = (el && el.clientHeight) || window.innerHeight || 0;
|
|
594
|
+
if (bottom <= 50 || (height && height - bottom - 70 < 160)) return 50;
|
|
595
|
+
return bottom;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Right padding that keeps fitted data out from under an open panel. On
|
|
599
|
+
// a narrow map there's no room beside it, so fit as if it weren't there
|
|
600
|
+
// (the panel starts collapsed there anyway; see appendPanelRows).
|
|
601
|
+
function rightPadding() {
|
|
602
|
+
if (panelEl.style.display !== 'flex' || panelCollapsed) return 30;
|
|
603
|
+
var right = (panelEl.offsetWidth || 280) + 24;
|
|
604
|
+
var mapWidth = mapContainerWidth();
|
|
605
|
+
if (mapWidth && mapWidth - right < 240) return 30;
|
|
606
|
+
return right;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function mapContainerWidth() {
|
|
610
|
+
var el = document.getElementById('map');
|
|
611
|
+
return (el && el.clientWidth) || window.innerWidth || 0;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
var lastFitBounds = null;
|
|
453
615
|
function fitToBounds(bounds) {
|
|
616
|
+
lastFitBounds = bounds;
|
|
454
617
|
setTimeout(function() {
|
|
455
618
|
map.resize();
|
|
456
619
|
map.fitBounds(bounds, {
|
|
457
|
-
padding: { top: 70, bottom:
|
|
620
|
+
padding: { top: 70, bottom: bottomPadding(), left: 30, right: rightPadding() },
|
|
458
621
|
duration: 600
|
|
459
622
|
});
|
|
460
623
|
}, 60);
|
|
@@ -470,13 +633,13 @@ ${initialDataScript}
|
|
|
470
633
|
(Array.isArray(payload.layers) ? payload.layers : []).forEach(function(layer) {
|
|
471
634
|
addOneLayer(layer, bbox);
|
|
472
635
|
});
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
});
|
|
636
|
+
var markers = applyPanelNumbering(payload.markers);
|
|
637
|
+
markers.forEach(function(m) { addOneMarker(m, bbox); });
|
|
476
638
|
if (payload.summary && summaryEl.style.display === 'none') {
|
|
477
639
|
summaryEl.textContent = payload.summary;
|
|
478
640
|
summaryEl.style.display = 'block';
|
|
479
641
|
}
|
|
642
|
+
maybeShowSidePanel(markers, payload.summary);
|
|
480
643
|
var bounds = bbox.bounds();
|
|
481
644
|
if (bounds) fitToBounds(bounds);
|
|
482
645
|
}
|
|
@@ -515,10 +678,14 @@ ${initialDataScript}
|
|
|
515
678
|
|
|
516
679
|
var bbox = bboxAccumulator();
|
|
517
680
|
var layers = Array.isArray(payload.layers) ? payload.layers : [];
|
|
518
|
-
var markers = Array.isArray(payload.markers) ? payload.markers : [];
|
|
519
|
-
|
|
520
681
|
layers.forEach(function(layer) { addOneLayer(layer, bbox); });
|
|
682
|
+
var markers = applyPanelNumbering(payload.markers);
|
|
521
683
|
markers.forEach(function(m) { addOneMarker(m, bbox); });
|
|
684
|
+
// Covers both inline markers (a caller-composed payload) and self-fetch
|
|
685
|
+
// markers (merged in later via mergeAdditionalPayload) — see
|
|
686
|
+
// maybeShowSidePanel below. Whichever path produced id-bearing POI
|
|
687
|
+
// markers gets the same results panel.
|
|
688
|
+
maybeShowSidePanel(markers, payload.summary);
|
|
522
689
|
|
|
523
690
|
// Legend
|
|
524
691
|
if (Array.isArray(payload.legend) && payload.legend.length > 0) {
|
|
@@ -1229,12 +1396,22 @@ ${initialDataScript}
|
|
|
1229
1396
|
popupParts.push(Math.round(props.distance) + ' m');
|
|
1230
1397
|
}
|
|
1231
1398
|
var coords = f.geometry.coordinates;
|
|
1399
|
+
// id/name/category/distanceMeters are the same generic POI fields an
|
|
1400
|
+
// inline (hand-composed) marker can carry — see maybeShowSidePanel,
|
|
1401
|
+
// which builds the results panel from either source identically.
|
|
1402
|
+
// mapbox_id is missing on the rare result that lacks one; the panel
|
|
1403
|
+
// just skips that row rather than show an unclickable/unenrichable
|
|
1404
|
+
// entry.
|
|
1232
1405
|
markers.push({
|
|
1406
|
+
id: props.mapbox_id,
|
|
1233
1407
|
coordinates: coords,
|
|
1234
1408
|
style: 'numbered',
|
|
1235
1409
|
label: String(i + 1),
|
|
1236
1410
|
color: '#f97316',
|
|
1237
|
-
popup: popupParts.join(' — ')
|
|
1411
|
+
popup: popupParts.join(' — '),
|
|
1412
|
+
name: props.name || ('Result ' + (i + 1)),
|
|
1413
|
+
category: Array.isArray(props.poi_category) ? props.poi_category[0] : undefined,
|
|
1414
|
+
distanceMeters: typeof props.distance === 'number' ? props.distance : undefined
|
|
1238
1415
|
});
|
|
1239
1416
|
features.push({
|
|
1240
1417
|
type: 'Feature',
|
|
@@ -1266,6 +1443,296 @@ ${initialDataScript}
|
|
|
1266
1443
|
return { summary: summary, layers: layers, markers: markers };
|
|
1267
1444
|
}
|
|
1268
1445
|
|
|
1446
|
+
// --- Side panel: result list + Place Details enrichment ----------------
|
|
1447
|
+
// Triggered generically by maybeShowSidePanel for ANY marker set that
|
|
1448
|
+
// includes id-bearing entries — self-fetch search/category-search
|
|
1449
|
+
// results (via mergeAdditionalPayload) and hand-composed inline markers
|
|
1450
|
+
// from render_map_tool's own "markers" input (via renderBody) both funnel
|
|
1451
|
+
// through the same path. Renders the initial (Search Box-only, or
|
|
1452
|
+
// whatever the caller already provided) list immediately so the panel
|
|
1453
|
+
// never blocks on the slower enrichment call. sourceLabel is plain text
|
|
1454
|
+
// shown as the panel heading (e.g. the query, category, or the payload's
|
|
1455
|
+
// own summary).
|
|
1456
|
+
function derivePanelItems(markers) {
|
|
1457
|
+
if (!Array.isArray(markers)) return [];
|
|
1458
|
+
var items = [];
|
|
1459
|
+
markers.forEach(function(m) {
|
|
1460
|
+
if (!m || !m.id) return;
|
|
1461
|
+
// The pin's own label, so a row reads the same as its pin ("A" too).
|
|
1462
|
+
var badge = isWholeNumber(m.label) ? parseInt(m.label, 10)
|
|
1463
|
+
: (m.label ? String(m.label) : items.length + 1);
|
|
1464
|
+
items.push({
|
|
1465
|
+
id: m.id,
|
|
1466
|
+
number: badge,
|
|
1467
|
+
name: m.name || ('Result ' + (items.length + 1)),
|
|
1468
|
+
category: m.category,
|
|
1469
|
+
distanceMeters: typeof m.distanceMeters === 'number' ? m.distanceMeters : undefined,
|
|
1470
|
+
color: m.color
|
|
1471
|
+
});
|
|
1472
|
+
});
|
|
1473
|
+
return items;
|
|
1474
|
+
}
|
|
1475
|
+
// Exposed so tests can exercise the pure derivation logic without a DOM.
|
|
1476
|
+
window.__derivePanelItems = derivePanelItems;
|
|
1477
|
+
|
|
1478
|
+
// Adds rows for places not yet in this render's panel and enriches just
|
|
1479
|
+
// those. A response is applied only if no newer render has started since
|
|
1480
|
+
// (panelGeneration), so a slow lookup can't write into a newer panel.
|
|
1481
|
+
function maybeShowSidePanel(markers, summary) {
|
|
1482
|
+
var items = derivePanelItems(markers).filter(function(it) {
|
|
1483
|
+
return !panelIds[it.id];
|
|
1484
|
+
});
|
|
1485
|
+
if (items.length === 0) return;
|
|
1486
|
+
items.forEach(function(it) { panelIds[it.id] = true; });
|
|
1487
|
+
appendPanelRows(items, summary || 'Results');
|
|
1488
|
+
var generation = panelGeneration;
|
|
1489
|
+
fetchPlaceDetailsBatch(
|
|
1490
|
+
items.map(function(it) { return it.id; }),
|
|
1491
|
+
TOKEN,
|
|
1492
|
+
API_ENDPOINT
|
|
1493
|
+
).then(function(result) {
|
|
1494
|
+
if (generation === panelGeneration) enrichSidePanel(result);
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// Keyed by mapbox_id -> { li, meta } elements, so enrichSidePanel can
|
|
1499
|
+
// update a row directly once Place Details resolves, without re-parsing
|
|
1500
|
+
// panelEl's markup back into a queryable DOM (innerHTML + querySelector
|
|
1501
|
+
// round-trips don't work in every MCP Apps host's iframe sandbox, and
|
|
1502
|
+
// building real nodes up front is simpler to reason about either way).
|
|
1503
|
+
// Reset by clearPanel, same lifecycle as the panel itself.
|
|
1504
|
+
var panelRowsById = {};
|
|
1505
|
+
// Bumped by teardown(), so responses from an earlier render are dropped.
|
|
1506
|
+
var panelGeneration = 0;
|
|
1507
|
+
// Id-bearing markers numbered so far this render, and the ids listed.
|
|
1508
|
+
var panelCount = 0;
|
|
1509
|
+
var panelIds = {};
|
|
1510
|
+
// The viewer's collapse choice, kept across renders; null until the first
|
|
1511
|
+
// panel decides a default from the map's width.
|
|
1512
|
+
var panelCollapsed = null;
|
|
1513
|
+
var panelListEl = null;
|
|
1514
|
+
|
|
1515
|
+
function buildPanelRow(it) {
|
|
1516
|
+
var li = document.createElement('li');
|
|
1517
|
+
li.className = 'panel-item';
|
|
1518
|
+
|
|
1519
|
+
var badge = document.createElement('span');
|
|
1520
|
+
badge.className = 'panel-badge';
|
|
1521
|
+
badge.textContent = String(it.number);
|
|
1522
|
+
// Match the pin when the caller colored it (e.g. gray for "other
|
|
1523
|
+
// results" next to a legend); the default orange is in the CSS.
|
|
1524
|
+
if (typeof it.color === 'string' && it.color.toLowerCase() !== '#f97316' &&
|
|
1525
|
+
(/^#[0-9a-f]{3,8}$/i).test(it.color)) {
|
|
1526
|
+
badge.style.background = it.color;
|
|
1527
|
+
badge.style.color = '#fff';
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
var name = document.createElement('div');
|
|
1531
|
+
name.className = 'panel-name';
|
|
1532
|
+
name.textContent = it.name;
|
|
1533
|
+
|
|
1534
|
+
var metaParts = [];
|
|
1535
|
+
if (it.category) metaParts.push(it.category);
|
|
1536
|
+
if (typeof it.distanceMeters === 'number') {
|
|
1537
|
+
metaParts.push(Math.round(it.distanceMeters) + ' m');
|
|
1538
|
+
}
|
|
1539
|
+
var meta = document.createElement('div');
|
|
1540
|
+
meta.className = 'panel-meta';
|
|
1541
|
+
meta.textContent = metaParts.join(' · ');
|
|
1542
|
+
|
|
1543
|
+
var body = document.createElement('div');
|
|
1544
|
+
body.appendChild(name);
|
|
1545
|
+
body.appendChild(meta);
|
|
1546
|
+
li.appendChild(badge);
|
|
1547
|
+
li.appendChild(body);
|
|
1548
|
+
|
|
1549
|
+
li.addEventListener('click', function() {
|
|
1550
|
+
var entry = markersById[it.id];
|
|
1551
|
+
if (entry) map.flyTo({ center: entry.marker.getLngLat(), zoom: 15, duration: 500 });
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
panelRowsById[it.id] = { li: li, meta: meta };
|
|
1555
|
+
return li;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function clearPanel() {
|
|
1559
|
+
while (panelEl.firstChild) panelEl.removeChild(panelEl.firstChild);
|
|
1560
|
+
panelRowsById = {};
|
|
1561
|
+
panelListEl = null;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function applyPanelCollapsed() {
|
|
1565
|
+
panelEl.className = panelCollapsed ? 'collapsed' : '';
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
function togglePanel() {
|
|
1569
|
+
panelCollapsed = !panelCollapsed;
|
|
1570
|
+
applyPanelCollapsed();
|
|
1571
|
+
if (lastFitBounds) fitToBounds(lastFitBounds);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Creates the panel on the render's first id-bearing markers, and adds
|
|
1575
|
+
// rows to it for later ones (e.g. search results merged in after inline
|
|
1576
|
+
// markers), so every numbered pin has a row.
|
|
1577
|
+
function appendPanelRows(items, sourceLabel) {
|
|
1578
|
+
if (!Array.isArray(items) || items.length === 0) return;
|
|
1579
|
+
if (!panelListEl) {
|
|
1580
|
+
var head = document.createElement('div');
|
|
1581
|
+
head.className = 'panel-head';
|
|
1582
|
+
head.textContent = sourceLabel || 'Results';
|
|
1583
|
+
head.addEventListener('click', togglePanel);
|
|
1584
|
+
|
|
1585
|
+
panelListEl = document.createElement('ul');
|
|
1586
|
+
panelListEl.className = 'panel-list';
|
|
1587
|
+
|
|
1588
|
+
panelEl.appendChild(head);
|
|
1589
|
+
panelEl.appendChild(panelListEl);
|
|
1590
|
+
// On a narrow map the open panel would cover most of it.
|
|
1591
|
+
if (panelCollapsed === null) {
|
|
1592
|
+
var width = mapContainerWidth();
|
|
1593
|
+
panelCollapsed = width > 0 && width < 560;
|
|
1594
|
+
}
|
|
1595
|
+
applyPanelCollapsed();
|
|
1596
|
+
panelEl.style.display = 'flex';
|
|
1597
|
+
}
|
|
1598
|
+
var list = panelListEl;
|
|
1599
|
+
items.forEach(function(it) { list.appendChild(buildPanelRow(it)); });
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// Search Box results mix Mapbox-native POI ids (decode to
|
|
1603
|
+
// "urn:mbxpoi:<uuid>") with OSM-sourced ones (decode to
|
|
1604
|
+
// "urn:mbxpoi-osm:n<osm-node-id>") — confirmed live against a real
|
|
1605
|
+
// category_search_tool-style query near Herndon, VA, where 2 of 10
|
|
1606
|
+
// "cafe" results were OSM-sourced. The Places API's batch endpoint
|
|
1607
|
+
// rejects the ENTIRE request with a 422 if even one id isn't its native
|
|
1608
|
+
// scheme (same 422 PlaceDetailsTool.ts already works around server-side
|
|
1609
|
+
// for boundary ids) — so filter client-side before sending, rather than
|
|
1610
|
+
// let one incompatible id in a 10-result batch silently zero out
|
|
1611
|
+
// enrichment for the other 9. The prefix alone isn't enough: near Union
|
|
1612
|
+
// Square, SF, a Starbucks decodes to "urn:mbxpoi:mapbox-n408854", which
|
|
1613
|
+
// the endpoint also rejects, so require the UUID form.
|
|
1614
|
+
var PLACES_ID = /^urn:mbxpoi:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1615
|
+
function isPlacesApiCompatibleId(id) {
|
|
1616
|
+
if (typeof id !== 'string' || id.length === 0) return false;
|
|
1617
|
+
try {
|
|
1618
|
+
return PLACES_ID.test(atob(id));
|
|
1619
|
+
} catch (e) {
|
|
1620
|
+
return false;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Places API Details endpoint — batch form. Mirrors the single-record
|
|
1625
|
+
// GET src/tools/place-details-tool/PlaceDetailsTool.ts calls server-side,
|
|
1626
|
+
// but the panel needs details for every result at once, so this uses the
|
|
1627
|
+
// POST /details/retrieve batch form (up to 100 ids) instead of one
|
|
1628
|
+
// request per pin. Docs: https://docs.mapbox.com/api/search/places/
|
|
1629
|
+
// Like every other self-fetch call, this runs client-side against the
|
|
1630
|
+
// public token — the Places API's docs list no secret scope requirement
|
|
1631
|
+
// for this endpoint, same as Search Box/Directions/etc.
|
|
1632
|
+
function fetchPlaceDetailsBatch(ids, publicToken, apiEndpoint) {
|
|
1633
|
+
var compatibleIds = (Array.isArray(ids) ? ids : []).filter(isPlacesApiCompatibleId);
|
|
1634
|
+
if (compatibleIds.length === 0) return Promise.resolve(null);
|
|
1635
|
+
var qp = new URLSearchParams();
|
|
1636
|
+
qp.append('access_token', publicToken);
|
|
1637
|
+
var url = apiEndpoint + 'places/v1/details/retrieve?' + qp.toString();
|
|
1638
|
+
var post = function(batch, retried) {
|
|
1639
|
+
return fetch(url, {
|
|
1640
|
+
method: 'POST',
|
|
1641
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1642
|
+
body: JSON.stringify({ ids: batch })
|
|
1643
|
+
}).then(function(res) {
|
|
1644
|
+
// 206 is a partial batch (some ids missing/unprocessed) — still
|
|
1645
|
+
// has usable "results", so treat it the same as 200.
|
|
1646
|
+
if (res.ok || res.status === 206) return res.json();
|
|
1647
|
+
// A 422 names the one id it rejected ("Invalid mapbox_id format:
|
|
1648
|
+
// <id>"), in case an id format slips past the filter above. Drop
|
|
1649
|
+
// that id and try once more, rather than lose the whole batch.
|
|
1650
|
+
if (res.status !== 422 || retried) return null;
|
|
1651
|
+
return res.json().then(function(body) {
|
|
1652
|
+
var msg = body && typeof body.message === 'string' ? body.message : '';
|
|
1653
|
+
var bad = msg.slice(msg.lastIndexOf(' ') + 1).split('=')[0];
|
|
1654
|
+
var rest = batch.filter(function(id) { return id.split('=')[0] !== bad; });
|
|
1655
|
+
if (!bad || rest.length === batch.length || rest.length === 0) return null;
|
|
1656
|
+
return post(rest, true);
|
|
1657
|
+
});
|
|
1658
|
+
});
|
|
1659
|
+
};
|
|
1660
|
+
return post(compatibleIds.slice(0, 100), false)
|
|
1661
|
+
// Enrichment is a nice-to-have on top of an already-rendered panel —
|
|
1662
|
+
// Public Preview quota (1,000 records/month) or a transient error
|
|
1663
|
+
// here should never surface as a map error, just leave the
|
|
1664
|
+
// Search-Box-only rows as they are.
|
|
1665
|
+
.catch(function() { return null; });
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// Place Details photos are hosted wherever each business's site hosts
|
|
1669
|
+
// them, so the in-chat CSP (mapAppCsp.ts) can't allow them. Inside a host,
|
|
1670
|
+
// the page asks the server for the photo (PlacePhotoResource, by mapbox_id)
|
|
1671
|
+
// and shows it as a data: URL. A standalone page from export_map_html_tool
|
|
1672
|
+
// has no host and no CSP, so it loads the photo URL directly.
|
|
1673
|
+
var MAX_PANEL_PHOTOS = 10;
|
|
1674
|
+
var EMBEDDED = window.parent !== window;
|
|
1675
|
+
var PHOTO_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
|
|
1676
|
+
var PHOTO_TIMEOUT_MS = 15000;
|
|
1677
|
+
|
|
1678
|
+
function loadPanelPhoto(row, id, photoUrl) {
|
|
1679
|
+
var show = function(src) {
|
|
1680
|
+
// A later render may have replaced the panel while this loaded.
|
|
1681
|
+
if (panelRowsById[id] !== row) return;
|
|
1682
|
+
var thumb = document.createElement('span');
|
|
1683
|
+
thumb.className = 'panel-thumb';
|
|
1684
|
+
thumb.style.backgroundImage = 'url("' + src.replace(/"/g, '') + '")';
|
|
1685
|
+
row.li.appendChild(thumb);
|
|
1686
|
+
};
|
|
1687
|
+
if (!EMBEDDED || !hostAnswered) {
|
|
1688
|
+
// No MCP host (a standalone or framed exported page), so no CSP of
|
|
1689
|
+
// ours either. Load it first: some sites refuse hotlinked photos (403), which
|
|
1690
|
+
// would leave an empty box in the row.
|
|
1691
|
+
if (photoUrl.indexOf('https://') !== 0 || typeof Image !== 'function') return;
|
|
1692
|
+
var img = new Image();
|
|
1693
|
+
img.onload = function() { show(photoUrl); };
|
|
1694
|
+
img.src = photoUrl;
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
// Hosts that don't support resources/read (ChatGPT) may never answer.
|
|
1698
|
+
sendRequest('resources/read', {
|
|
1699
|
+
uri: 'mapbox://place-photo/' + encodeURIComponent(id)
|
|
1700
|
+
}, PHOTO_TIMEOUT_MS).then(function(rr) {
|
|
1701
|
+
var c = rr && Array.isArray(rr.contents) ? rr.contents[0] : null;
|
|
1702
|
+
if (c && typeof c.blob === 'string' &&
|
|
1703
|
+
PHOTO_TYPES.indexOf(c.mimeType) !== -1) {
|
|
1704
|
+
show('data:' + c.mimeType + ';base64,' + c.blob);
|
|
1705
|
+
}
|
|
1706
|
+
}, function() {});
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
function enrichSidePanel(detailsResult) {
|
|
1710
|
+
if (!detailsResult || !Array.isArray(detailsResult.results)) return;
|
|
1711
|
+
var photosRequested = 0;
|
|
1712
|
+
detailsResult.results.forEach(function(place) {
|
|
1713
|
+
if (!place || !place.mapbox_id) return;
|
|
1714
|
+
var row = panelRowsById[place.mapbox_id];
|
|
1715
|
+
if (row) {
|
|
1716
|
+
var photoUrl = place.photos && place.photos[0] && place.photos[0].url;
|
|
1717
|
+
if (typeof photoUrl === 'string' && photosRequested < MAX_PANEL_PHOTOS) {
|
|
1718
|
+
photosRequested++;
|
|
1719
|
+
loadPanelPhoto(row, place.mapbox_id, photoUrl);
|
|
1720
|
+
}
|
|
1721
|
+
if (place.score && typeof place.score.popularity === 'number') {
|
|
1722
|
+
var extra = Math.round(place.score.popularity * 100) + '% popularity';
|
|
1723
|
+
row.meta.textContent = row.meta.textContent ? row.meta.textContent + ' · ' + extra : extra;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
var entry = markersById[place.mapbox_id];
|
|
1728
|
+
if (entry && place.phone) {
|
|
1729
|
+
entry.marker.setPopup(
|
|
1730
|
+
new mapboxgl.Popup().setText(entry.popupText + ' — ' + place.phone)
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1269
1736
|
function selfFetchSearch(params) {
|
|
1270
1737
|
if (!TOKEN || !isSafeSearchParams(params)) {
|
|
1271
1738
|
showError('Could not fetch search results: missing token or invalid parameters.');
|
|
@@ -1595,11 +2062,15 @@ ${initialDataScript}
|
|
|
1595
2062
|
parts.push(Math.round(poi.distance_meters) + ' m');
|
|
1596
2063
|
}
|
|
1597
2064
|
markers.push({
|
|
2065
|
+
id: poi.id,
|
|
1598
2066
|
coordinates: [poi.longitude, poi.latitude],
|
|
1599
2067
|
style: 'numbered',
|
|
1600
2068
|
label: String(i + 1),
|
|
1601
2069
|
color: '#f97316',
|
|
1602
|
-
popup: parts.join(' — ')
|
|
2070
|
+
popup: parts.join(' — '),
|
|
2071
|
+
name: poi.name,
|
|
2072
|
+
category: poi.category,
|
|
2073
|
+
distanceMeters: typeof poi.distance_meters === 'number' ? poi.distance_meters : undefined
|
|
1603
2074
|
});
|
|
1604
2075
|
});
|
|
1605
2076
|
return { summary: place, layers: [], markers: markers };
|
|
@@ -1658,8 +2129,10 @@ ${initialDataScript}
|
|
|
1658
2129
|
var props = f.properties || {};
|
|
1659
2130
|
var coords = (f.geometry && f.geometry.coordinates) || [params.longitude, params.latitude];
|
|
1660
2131
|
return {
|
|
2132
|
+
id: props.mapbox_id,
|
|
1661
2133
|
name: props.name || 'Unknown',
|
|
1662
2134
|
address: props.full_address || props.place_formatted,
|
|
2135
|
+
category: Array.isArray(props.poi_category) ? props.poi_category[0] : undefined,
|
|
1663
2136
|
longitude: coords[0],
|
|
1664
2137
|
latitude: coords[1],
|
|
1665
2138
|
distance_meters: props.distance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/mapAppHtml.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAIlC;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,wEAAwE;AACxE,6EAA6E;AAC7E,8CAA8C;AAC9C,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAAC,MAKhC;IACC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,yBAAyB,CAAC;IACpE,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,iBAAiB,CAAC;IAExD,MAAM,iBAAiB,GAAG,WAAW;QACnC,CAAC,CAAC,qDAAqD,eAAe,CAClE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAC5B,WAAW;QACd,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;mDAM0C,SAAS;oDACR,SAAS
|
|
1
|
+
{"version":3,"file":"mapAppHtml.js","sourceRoot":"","sources":["../../../../src/resources/ui-apps/mapAppHtml.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CategoryListTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAiBjF;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,kBAAkB,CACtD,OAAO,uBAAuB,EAC9B,OAAO,0BAA0B,CAClC;IACC,IAAI,SAAwB;IAC5B,WAAW,
|
|
1
|
+
{"version":3,"file":"CategoryListTool.d.ts","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAiBjF;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,kBAAkB,CACtD,OAAO,uBAAuB,EAC9B,OAAO,0BAA0B,CAClC;IACC,IAAI,SAAwB;IAC5B,WAAW,SACgY;IAC3Y,WAAW;;;;;;MAMT;gBAEU,MAAM,EAAE;QAAE,WAAW,EAAE,WAAW,CAAA;KAAE;cAQhC,OAAO,CACrB,KAAK,EAAE,iBAAiB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC;CAmE3B"}
|
|
@@ -9,7 +9,7 @@ export const CategoryListInputSchema = z.object({
|
|
|
9
9
|
.min(1)
|
|
10
10
|
.max(100)
|
|
11
11
|
.optional()
|
|
12
|
-
.describe('Number of categories to return (1-100).
|
|
12
|
+
.describe('Number of categories to return (1-100). If not specified, returns all categories.'),
|
|
13
13
|
offset: z
|
|
14
14
|
.number()
|
|
15
15
|
.min(0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CategoryListTool.input.schema.js","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.input.schema.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,
|
|
1
|
+
{"version":3,"file":"CategoryListTool.input.schema.js","sourceRoot":"","sources":["../../../../src/tools/category-list-tool/CategoryListTool.input.schema.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,kCAAkC;AAElC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,mFAAmF,CACpF;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,4DAA4D,CAAC;CAC1E,CAAC,CAAC"}
|
|
@@ -9,7 +9,7 @@ import { CategoryListResponseSchema } from './CategoryListTool.output.schema.js'
|
|
|
9
9
|
*/
|
|
10
10
|
export class CategoryListTool extends MapboxApiBasedTool {
|
|
11
11
|
name = 'category_list_tool';
|
|
12
|
-
description = '[DEPRECATED: Use resource_reader_tool with "mapbox://categories" URI instead] Tool for retrieving the list of supported categories from Mapbox Search API. This tool is kept for backward compatibility with clients that do not support MCP resources. Use this when another function requires a list of categories. Returns all available category IDs by default.
|
|
12
|
+
description = '[DEPRECATED: Use resource_reader_tool with "mapbox://categories" URI instead] Tool for retrieving the list of supported categories from Mapbox Search API. This tool is kept for backward compatibility with clients that do not support MCP resources. Use this when another function requires a list of categories. Returns all available category IDs by default. Supports limit/offset pagination.';
|
|
13
13
|
annotations = {
|
|
14
14
|
title: 'Category List Tool (Deprecated)',
|
|
15
15
|
readOnlyHint: true,
|