mirror of
https://github.com/block/buzz.git
synced 2026-11-08 10:21:46 +00:00
feat(desktop): render ```map fenced blocks as interactive maps
Messages and channel canvases can now embed a live map by writing a ```map block containing a small JSON spec (center/zoom, markers, lines). Agents can drive it with the existing `buzz canvas set` / messages commands, no new protocol or permissions needed. MapLibre GL with OpenFreeMap tiles (no API key), lazy-loaded so the ~1 MB chunk only downloads when a map is rendered. Signed-off-by: Wintermute <a00fdff03f06355c4fc6d6268f3a09ebe752c20b98df8fdab5fe81e266d6630a@meshllm.communities.buzz.xyz>
This commit is contained in:
@@ -69,6 +69,7 @@
|
||||
"jdenticon": "^3.3.0",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"lucide-react": "^1.0.0",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"mdast-util-from-markdown": "^2.0.3",
|
||||
"motion": "^12.38.0",
|
||||
"qrcode": "^1.5.4",
|
||||
|
||||
@@ -32,6 +32,8 @@ export const CODE_BLOCK_CLASS =
|
||||
"code-block-lines block min-w-full whitespace-pre font-mono text-sm font-medium text-foreground";
|
||||
const DIFF_ADD_RE = /\s*\/\/\s*\[!code\s*\+\+\]\s*$/;
|
||||
const DIFF_REMOVE_RE = /\s*\/\/\s*\[!code\s*--\]\s*$/;
|
||||
// maplibre is heavy; only load it when a message or canvas contains a map.
|
||||
const MapBlock = React.lazy(() => import("./MapBlock"));
|
||||
|
||||
function ensureHighlighter(): Promise<void> {
|
||||
if (shikiHighlighter) return Promise.resolve();
|
||||
@@ -96,6 +98,14 @@ export function MarkdownCodeBlock({
|
||||
[code],
|
||||
);
|
||||
|
||||
if (language === "map") {
|
||||
return (
|
||||
<React.Suspense fallback={null}>
|
||||
<MapBlock code={code} />
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="group relative" data-code-block="">
|
||||
<pre
|
||||
|
||||
102
desktop/src/shared/ui/markdown/MapBlock.tsx
Normal file
102
desktop/src/shared/ui/markdown/MapBlock.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import maplibregl from "maplibre-gl";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import * as React from "react";
|
||||
|
||||
import { useTheme } from "@/shared/theme/ThemeProvider";
|
||||
|
||||
import { type MapSpec, mapSpecPoints, parseMapSpec } from "./mapSpec";
|
||||
|
||||
// OpenFreeMap: free vector tiles, no API key.
|
||||
const STYLE_URL = {
|
||||
light: "https://tiles.openfreemap.org/styles/liberty",
|
||||
dark: "https://tiles.openfreemap.org/styles/dark",
|
||||
};
|
||||
const DEFAULT_COLOR = "#3b82f6";
|
||||
const DEFAULT_HEIGHT = 360;
|
||||
|
||||
function safeColor(color: string | undefined) {
|
||||
return color && CSS.supports("color", color) ? color : DEFAULT_COLOR;
|
||||
}
|
||||
|
||||
function addOverlays(map: maplibregl.Map, spec: MapSpec) {
|
||||
for (const marker of spec.markers) {
|
||||
const pin = new maplibregl.Marker({ color: safeColor(marker.color) })
|
||||
.setLngLat([marker.lng, marker.lat])
|
||||
.addTo(map);
|
||||
if (marker.label) {
|
||||
pin.getElement().title = marker.label;
|
||||
pin.setPopup(new maplibregl.Popup({ offset: 24 }).setText(marker.label));
|
||||
}
|
||||
}
|
||||
if (spec.lines.length === 0) return;
|
||||
map.addSource("buzz-lines", {
|
||||
type: "geojson",
|
||||
data: {
|
||||
type: "FeatureCollection",
|
||||
features: spec.lines.map((line) => ({
|
||||
type: "Feature",
|
||||
properties: { color: safeColor(line.color) },
|
||||
geometry: { type: "LineString", coordinates: line.coordinates },
|
||||
})),
|
||||
},
|
||||
});
|
||||
map.addLayer({
|
||||
id: "buzz-lines",
|
||||
type: "line",
|
||||
source: "buzz-lines",
|
||||
layout: { "line-cap": "round", "line-join": "round" },
|
||||
paint: { "line-color": ["get", "color"], "line-width": 4 },
|
||||
});
|
||||
}
|
||||
|
||||
function MapView({ spec }: { spec: MapSpec }) {
|
||||
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const { isDark } = useTheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
const points = mapSpecPoints(spec);
|
||||
const map = new maplibregl.Map({
|
||||
container,
|
||||
style: STYLE_URL[isDark ? "dark" : "light"],
|
||||
center: spec.center ?? points[0],
|
||||
zoom: spec.zoom ?? 12,
|
||||
attributionControl: { compact: true },
|
||||
cooperativeGestures: true,
|
||||
});
|
||||
map.addControl(new maplibregl.NavigationControl(), "top-right");
|
||||
if (!spec.center && points.length > 1) {
|
||||
const bounds = new maplibregl.LngLatBounds(points[0], points[0]);
|
||||
for (const point of points) bounds.extend(point);
|
||||
map.fitBounds(bounds, {
|
||||
padding: 48,
|
||||
maxZoom: spec.zoom ?? 15,
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
map.on("load", () => addOverlays(map, spec));
|
||||
return () => map.remove();
|
||||
}, [spec, isDark]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-hidden rounded-2xl border border-border/70"
|
||||
data-testid="markdown-map-block"
|
||||
ref={containerRef}
|
||||
style={{ height: spec.height ?? DEFAULT_HEIGHT }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MapBlock({ code }: { code: string }) {
|
||||
const result = React.useMemo(() => parseMapSpec(code), [code]);
|
||||
if (!result.ok) {
|
||||
return (
|
||||
<p className="rounded-xl border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
||||
{result.error}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return <MapView spec={result.spec} />;
|
||||
}
|
||||
69
desktop/src/shared/ui/markdown/mapSpec.test.mjs
Normal file
69
desktop/src/shared/ui/markdown/mapSpec.test.mjs
Normal file
@@ -0,0 +1,69 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { mapSpecPoints, parseMapSpec } from "./mapSpec.ts";
|
||||
|
||||
test("parses markers, lines, and viewport", () => {
|
||||
const result = parseMapSpec(
|
||||
JSON.stringify({
|
||||
center: [-122.42, 37.77],
|
||||
zoom: 30,
|
||||
height: 5000,
|
||||
markers: [{ lng: -122.4, lat: 37.78, label: "HQ", color: "#f00" }],
|
||||
lines: [
|
||||
{
|
||||
coordinates: [
|
||||
[-122.4, 37.78],
|
||||
[-122.41, 37.76],
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
assert.equal(result.ok, true);
|
||||
assert.deepEqual(result.spec, {
|
||||
center: [-122.42, 37.77],
|
||||
zoom: 22,
|
||||
height: 800,
|
||||
markers: [{ lng: -122.4, lat: 37.78, label: "HQ", color: "#f00" }],
|
||||
lines: [
|
||||
{
|
||||
coordinates: [
|
||||
[-122.4, 37.78],
|
||||
[-122.41, 37.76],
|
||||
],
|
||||
label: undefined,
|
||||
color: undefined,
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.deepEqual(mapSpecPoints(result.spec), [
|
||||
[-122.4, 37.78],
|
||||
[-122.4, 37.78],
|
||||
[-122.41, 37.76],
|
||||
]);
|
||||
});
|
||||
|
||||
test("markers alone are enough to frame the map", () => {
|
||||
const result = parseMapSpec('{"markers":[{"lng":1,"lat":2}]}');
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.spec.center, undefined);
|
||||
});
|
||||
|
||||
test("rejects malformed specs with a readable error", () => {
|
||||
assert.deepEqual(parseMapSpec("{nope"), {
|
||||
ok: false,
|
||||
error: "Map block is not valid JSON.",
|
||||
});
|
||||
assert.equal(parseMapSpec("[]").ok, false);
|
||||
assert.equal(parseMapSpec("{}").ok, false);
|
||||
assert.equal(parseMapSpec('{"center":[200,0]}').ok, false);
|
||||
assert.equal(
|
||||
parseMapSpec('{"markers":[{"lng":"x","lat":1}]}').error,
|
||||
"markers[0] needs valid lng/lat.",
|
||||
);
|
||||
assert.equal(
|
||||
parseMapSpec('{"lines":[{"coordinates":[[0,0]]}]}').error,
|
||||
"lines[0] needs at least two [lng, lat] coordinates.",
|
||||
);
|
||||
});
|
||||
135
desktop/src/shared/ui/markdown/mapSpec.ts
Normal file
135
desktop/src/shared/ui/markdown/mapSpec.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Spec for ```map fenced blocks. Authored by people or agents (for example
|
||||
* via `buzz canvas set`) as JSON; coordinates are [lng, lat] like GeoJSON.
|
||||
*/
|
||||
export type LngLat = [number, number];
|
||||
|
||||
export type MapMarker = {
|
||||
lng: number;
|
||||
lat: number;
|
||||
label?: string;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type MapLine = {
|
||||
coordinates: LngLat[];
|
||||
label?: string;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type MapSpec = {
|
||||
center?: LngLat;
|
||||
zoom?: number;
|
||||
height?: number;
|
||||
markers: MapMarker[];
|
||||
lines: MapLine[];
|
||||
};
|
||||
|
||||
export type MapSpecResult =
|
||||
| { ok: true; spec: MapSpec }
|
||||
| { ok: false; error: string };
|
||||
|
||||
const MIN_HEIGHT = 160;
|
||||
const MAX_HEIGHT = 800;
|
||||
|
||||
function isLngLat(value: unknown): value is LngLat {
|
||||
return (
|
||||
Array.isArray(value) &&
|
||||
value.length === 2 &&
|
||||
Number.isFinite(value[0]) &&
|
||||
Number.isFinite(value[1]) &&
|
||||
Math.abs(value[0]) <= 180 &&
|
||||
Math.abs(value[1]) <= 90
|
||||
);
|
||||
}
|
||||
|
||||
function optionalString(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim() ? value : undefined;
|
||||
}
|
||||
|
||||
export function parseMapSpec(source: string): MapSpecResult {
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = JSON.parse(source);
|
||||
} catch {
|
||||
return { ok: false, error: "Map block is not valid JSON." };
|
||||
}
|
||||
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
||||
return { ok: false, error: "Map block must be a JSON object." };
|
||||
}
|
||||
const input = raw as Record<string, unknown>;
|
||||
|
||||
const markers: MapMarker[] = [];
|
||||
for (const [index, item] of (Array.isArray(input.markers)
|
||||
? input.markers
|
||||
: []
|
||||
).entries()) {
|
||||
const marker = (item ?? {}) as Record<string, unknown>;
|
||||
if (!isLngLat([marker.lng, marker.lat])) {
|
||||
return { ok: false, error: `markers[${index}] needs valid lng/lat.` };
|
||||
}
|
||||
markers.push({
|
||||
lng: marker.lng as number,
|
||||
lat: marker.lat as number,
|
||||
label: optionalString(marker.label),
|
||||
color: optionalString(marker.color),
|
||||
});
|
||||
}
|
||||
|
||||
const lines: MapLine[] = [];
|
||||
for (const [index, item] of (Array.isArray(input.lines)
|
||||
? input.lines
|
||||
: []
|
||||
).entries()) {
|
||||
const line = (item ?? {}) as Record<string, unknown>;
|
||||
const coordinates = line.coordinates;
|
||||
if (
|
||||
!Array.isArray(coordinates) ||
|
||||
coordinates.length < 2 ||
|
||||
!coordinates.every(isLngLat)
|
||||
) {
|
||||
return {
|
||||
ok: false,
|
||||
error: `lines[${index}] needs at least two [lng, lat] coordinates.`,
|
||||
};
|
||||
}
|
||||
lines.push({
|
||||
coordinates,
|
||||
label: optionalString(line.label),
|
||||
color: optionalString(line.color),
|
||||
});
|
||||
}
|
||||
|
||||
if (input.center !== undefined && !isLngLat(input.center)) {
|
||||
return { ok: false, error: "center must be [lng, lat]." };
|
||||
}
|
||||
if (input.center === undefined && markers.length + lines.length === 0) {
|
||||
return { ok: false, error: "Map block needs a center, markers, or lines." };
|
||||
}
|
||||
|
||||
const zoom = Number.isFinite(input.zoom)
|
||||
? Math.min(22, Math.max(0, input.zoom as number))
|
||||
: undefined;
|
||||
const height = Number.isFinite(input.height)
|
||||
? Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, input.height as number))
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
spec: {
|
||||
center: input.center as LngLat | undefined,
|
||||
zoom,
|
||||
height,
|
||||
markers,
|
||||
lines,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Every coordinate in the spec, used to fit the initial viewport. */
|
||||
export function mapSpecPoints(spec: MapSpec): LngLat[] {
|
||||
return [
|
||||
...spec.markers.map((marker): LngLat => [marker.lng, marker.lat]),
|
||||
...spec.lines.flatMap((line) => line.coordinates),
|
||||
];
|
||||
}
|
||||
179
pnpm-lock.yaml
generated
179
pnpm-lock.yaml
generated
@@ -198,6 +198,9 @@ importers:
|
||||
lucide-react:
|
||||
specifier: ^1.0.0
|
||||
version: 1.16.0(react@19.2.8)
|
||||
maplibre-gl:
|
||||
specifier: ^5.24.0
|
||||
version: 5.24.0
|
||||
mdast-util-from-markdown:
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.3
|
||||
@@ -710,6 +713,42 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@mapbox/jsonlint-lines-primitives@2.0.3':
|
||||
resolution: {integrity: sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==}
|
||||
engines: {node: '>= 22'}
|
||||
|
||||
'@mapbox/point-geometry@1.1.0':
|
||||
resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==}
|
||||
|
||||
'@mapbox/tiny-sdf@2.3.0':
|
||||
resolution: {integrity: sha512-gBvcNDBBB66bgQYvnaJ6Jk+VCaMmVq5+IZR2kWPisT88Z8s0kBQt6hfVi8QgRW8Vpg0UWZzBA4ZSyzHHC+VeRA==}
|
||||
|
||||
'@mapbox/unitbezier@0.0.1':
|
||||
resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==}
|
||||
|
||||
'@mapbox/unitbezier@1.0.0':
|
||||
resolution: {integrity: sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==}
|
||||
|
||||
'@mapbox/vector-tile@2.0.5':
|
||||
resolution: {integrity: sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==}
|
||||
|
||||
'@mapbox/whoots-js@3.1.0':
|
||||
resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@maplibre/geojson-vt@6.1.2':
|
||||
resolution: {integrity: sha512-2ZYhobdAY+y4/P/z1xe0698165LywD0yDdWugdu3NRUrajgVWut+tLkcz7i/RJguMTURwK7gxwmwYMFJbgbQFw==}
|
||||
|
||||
'@maplibre/maplibre-gl-style-spec@24.10.0':
|
||||
resolution: {integrity: sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==}
|
||||
hasBin: true
|
||||
|
||||
'@maplibre/mlt@1.3.0':
|
||||
resolution: {integrity: sha512-7M2O7ABM80Oi8XPkfkrjKB8mOBIxGoVgkA52Mu4RMWoBvvMdagQ522+WxVYujZsMpqIr3BpzrTbXrm4k8lCzCQ==}
|
||||
|
||||
'@maplibre/vt-pbf@4.3.2':
|
||||
resolution: {integrity: sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==}
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.35':
|
||||
resolution: {integrity: sha512-HOvadwVRE6JC+45nyYhmnywnr5h/J8KZvOeUNVOG9q/0875pZgItznFB9bRTvLc264YSJqiZ1NsIpCStJw/egg==}
|
||||
|
||||
@@ -2076,6 +2115,9 @@ packages:
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/geojson@7946.0.16':
|
||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
@@ -2415,6 +2457,9 @@ packages:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
earcut@3.2.4:
|
||||
resolution: {integrity: sha512-FHc6yNpnTORB0tM5kmZ03iD2mDuNakDE2lvf4Hw1XkfxtEHIGVi01B/GqUSwx+NcC1WGjidAfpTXzeALtPtfpg==}
|
||||
|
||||
electron-to-chromium@1.5.361:
|
||||
resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==}
|
||||
|
||||
@@ -2567,6 +2612,9 @@ packages:
|
||||
gitdiff-parser@0.3.1:
|
||||
resolution: {integrity: sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==}
|
||||
|
||||
gl-matrix@3.4.4:
|
||||
resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2705,6 +2753,9 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
json-stringify-pretty-compact@4.0.0:
|
||||
resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==}
|
||||
|
||||
json5@2.2.3:
|
||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2716,6 +2767,9 @@ packages:
|
||||
just-once@1.1.0:
|
||||
resolution: {integrity: sha512-+rZVpl+6VyTilK7vB/svlMPil4pxqIJZkbnN7DKZTOzyXfun6ZiFeq2Pk4EtCEHZ0VU4EkdFzG8ZK5F3PErcDw==}
|
||||
|
||||
kdbush@4.1.0:
|
||||
resolution: {integrity: sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
@@ -2829,6 +2883,10 @@ packages:
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
maplibre-gl@5.24.0:
|
||||
resolution: {integrity: sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==}
|
||||
engines: {node: '>=16.14.0', npm: '>=8.1.0'}
|
||||
|
||||
markdown-it-task-lists@2.1.1:
|
||||
resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==}
|
||||
|
||||
@@ -3018,6 +3076,9 @@ packages:
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
murmurhash-js@1.0.0:
|
||||
resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==}
|
||||
|
||||
nanoid@3.3.16:
|
||||
resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -3087,6 +3148,14 @@ packages:
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
pbf@4.0.2:
|
||||
resolution: {integrity: sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==}
|
||||
hasBin: true
|
||||
|
||||
pbf@5.1.2:
|
||||
resolution: {integrity: sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==}
|
||||
hasBin: true
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -3132,6 +3201,9 @@ packages:
|
||||
resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
potpack@2.1.0:
|
||||
resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==}
|
||||
|
||||
prettier@3.8.3:
|
||||
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -3187,6 +3259,9 @@ packages:
|
||||
prosemirror-view@1.41.8:
|
||||
resolution: {integrity: sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==}
|
||||
|
||||
protocol-buffers-schema@3.6.1:
|
||||
resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==}
|
||||
|
||||
punycode.js@2.3.1:
|
||||
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3205,6 +3280,9 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
quickselect@3.0.0:
|
||||
resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==}
|
||||
|
||||
react-day-picker@10.0.1:
|
||||
resolution: {integrity: sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3315,6 +3393,9 @@ packages:
|
||||
require-main-filename@2.0.0:
|
||||
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||
|
||||
resolve-protobuf-schema@2.1.0:
|
||||
resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==}
|
||||
|
||||
rolldown@1.0.3:
|
||||
resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -3446,6 +3527,9 @@ packages:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinyqueue@3.0.0:
|
||||
resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==}
|
||||
|
||||
tinyrainbow@3.1.1:
|
||||
resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -4072,6 +4156,47 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@mapbox/jsonlint-lines-primitives@2.0.3': {}
|
||||
|
||||
'@mapbox/point-geometry@1.1.0': {}
|
||||
|
||||
'@mapbox/tiny-sdf@2.3.0': {}
|
||||
|
||||
'@mapbox/unitbezier@0.0.1': {}
|
||||
|
||||
'@mapbox/unitbezier@1.0.0': {}
|
||||
|
||||
'@mapbox/vector-tile@2.0.5':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@types/geojson': 7946.0.16
|
||||
pbf: 4.0.2
|
||||
|
||||
'@mapbox/whoots-js@3.1.0': {}
|
||||
|
||||
'@maplibre/geojson-vt@6.1.2':
|
||||
dependencies:
|
||||
kdbush: 4.1.0
|
||||
|
||||
'@maplibre/maplibre-gl-style-spec@24.10.0':
|
||||
dependencies:
|
||||
'@mapbox/jsonlint-lines-primitives': 2.0.3
|
||||
'@mapbox/unitbezier': 1.0.0
|
||||
json-stringify-pretty-compact: 4.0.0
|
||||
minimist: 1.2.8
|
||||
quickselect: 3.0.0
|
||||
tinyqueue: 3.0.0
|
||||
|
||||
'@maplibre/mlt@1.3.0':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
|
||||
'@maplibre/vt-pbf@4.3.2':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@types/geojson': 7946.0.16
|
||||
pbf: 5.1.2
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.35': {}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
|
||||
@@ -5345,6 +5470,8 @@ snapshots:
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/geojson@7946.0.16': {}
|
||||
|
||||
'@types/hast@3.0.4':
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -5653,6 +5780,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
earcut@3.2.4: {}
|
||||
|
||||
electron-to-chromium@1.5.361: {}
|
||||
|
||||
embla-carousel-react@8.6.0(react@19.2.8):
|
||||
@@ -5768,6 +5897,8 @@ snapshots:
|
||||
|
||||
gitdiff-parser@0.3.1: {}
|
||||
|
||||
gl-matrix@3.4.4: {}
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
@@ -5941,12 +6072,16 @@ snapshots:
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json-stringify-pretty-compact@4.0.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
just-debounce-it@1.1.0: {}
|
||||
|
||||
just-once@1.1.0: {}
|
||||
|
||||
kdbush@4.1.0: {}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
@@ -6030,6 +6165,28 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
maplibre-gl@5.24.0:
|
||||
dependencies:
|
||||
'@mapbox/jsonlint-lines-primitives': 2.0.3
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@mapbox/tiny-sdf': 2.3.0
|
||||
'@mapbox/unitbezier': 0.0.1
|
||||
'@mapbox/vector-tile': 2.0.5
|
||||
'@mapbox/whoots-js': 3.1.0
|
||||
'@maplibre/geojson-vt': 6.1.2
|
||||
'@maplibre/maplibre-gl-style-spec': 24.10.0
|
||||
'@maplibre/mlt': 1.3.0
|
||||
'@maplibre/vt-pbf': 4.3.2
|
||||
'@types/geojson': 7946.0.16
|
||||
earcut: 3.2.4
|
||||
gl-matrix: 3.4.4
|
||||
kdbush: 4.1.0
|
||||
murmurhash-js: 1.0.0
|
||||
pbf: 4.0.2
|
||||
potpack: 2.1.0
|
||||
quickselect: 3.0.0
|
||||
tinyqueue: 3.0.0
|
||||
|
||||
markdown-it-task-lists@2.1.1: {}
|
||||
|
||||
markdown-it@14.1.1:
|
||||
@@ -6424,6 +6581,8 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
murmurhash-js@1.0.0: {}
|
||||
|
||||
nanoid@3.3.16: {}
|
||||
|
||||
nanoid@3.3.17: {}
|
||||
@@ -6490,6 +6649,14 @@ snapshots:
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pbf@4.0.2:
|
||||
dependencies:
|
||||
resolve-protobuf-schema: 2.1.0
|
||||
|
||||
pbf@5.1.2:
|
||||
dependencies:
|
||||
resolve-protobuf-schema: 2.1.0
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.5: {}
|
||||
@@ -6527,6 +6694,8 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
potpack@2.1.0: {}
|
||||
|
||||
prettier@3.8.3: {}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
@@ -6614,6 +6783,8 @@ snapshots:
|
||||
prosemirror-state: 1.4.4
|
||||
prosemirror-transform: 1.12.0
|
||||
|
||||
protocol-buffers-schema@3.6.1: {}
|
||||
|
||||
punycode.js@2.3.1: {}
|
||||
|
||||
punycode@2.3.1: {}
|
||||
@@ -6628,6 +6799,8 @@ snapshots:
|
||||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
quickselect@3.0.0: {}
|
||||
|
||||
react-day-picker@10.0.1(@types/react@19.2.18)(react@19.2.8):
|
||||
dependencies:
|
||||
'@date-fns/tz': 1.5.0
|
||||
@@ -6771,6 +6944,10 @@ snapshots:
|
||||
|
||||
require-main-filename@2.0.0: {}
|
||||
|
||||
resolve-protobuf-schema@2.1.0:
|
||||
dependencies:
|
||||
protocol-buffers-schema: 3.6.1
|
||||
|
||||
rolldown@1.0.3:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.133.0
|
||||
@@ -6913,6 +7090,8 @@ snapshots:
|
||||
fdir: 6.5.0(picomatch@4.0.7)
|
||||
picomatch: 4.0.7
|
||||
|
||||
tinyqueue@3.0.0: {}
|
||||
|
||||
tinyrainbow@3.1.1: {}
|
||||
|
||||
tiptap-markdown@0.9.0(@tiptap/core@3.22.5(@tiptap/pm@3.22.5)):
|
||||
|
||||
Reference in New Issue
Block a user