git diff --cached --name-status | while read status file; do
echo "# $file"
echo '````'
if [ "$status" = "D" ]; then
git show HEAD:$file 2>/dev/null || echo "[deleted]"
else
git show :$file 2>/dev/null || echo "[binary]"
fi
echo '````'
echo ""
done | clip
git diff --cached --name-status | while read status file; do
echo "# [staged] $file"
echo '````'
if [ "$status" = "D" ]; then
git show HEAD:$file 2>/dev/null || echo "[deleted]"
else
git show :$file 2>/dev/null || echo "[binary]"
fi
echo '````'
echo ""
done | clip
{
# === STAGED ===
git diff --cached --name-status | while read status file; do
echo "# [staged] $file"
echo '````'
if [ "$status" = "D" ]; then
git show HEAD:$file 2>/dev/null || echo "[deleted]"
else
git show :$file 2>/dev/null || echo "[binary]"
fi
echo '````'
echo ""
done
# === UNSTAGED ===
git diff --name-status | while read status file; do
echo "# [unstaged] $file"
echo '````'
if [ "$status" = "D" ]; then
cat $file 2>/dev/null || echo "[deleted]"
else
cat $file 2>/dev/null || echo "[binary or missing]"
fi
echo '````'
echo ""
done
} | clip
/* eslint-disable @typescript-eslint/no-namespace */
declare const L: typeof import("leaflet");
type LatLng = import("leaflet").LatLng;
type LatLngTuple = import("leaflet").LatLngTuple;
type LatLngBoundsExpression = import("leaflet").LatLngBoundsExpression;
type LeafletMouseEvent = import("leaflet").LeafletMouseEvent;
type Polyline = import("leaflet").Polyline;
type Tooltip = import("leaflet").Tooltip;
type CircleMarker = import("leaflet").CircleMarker;
type LayerGroup = import("leaflet").LayerGroup;
type Map = import("leaflet").Map;
type Marker = import("leaflet").Marker;
type ControlOptions = import("leaflet").ControlOptions;
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace iconify {
const loadIcon: (iconName: string) => Promise<FullIconifyIcon>;
type FullIconifyIcon = object;
}
type Coordinates = `${number}, ${number}`;
interface MarkerDataSet {
name: string;
link: string;
coordinates: Coordinates;
icon: string;
colour: string;
minZoom: string;
maxZoom: string;
}
interface MapDataSet {
src: string;
osmMode: string;
startCoordinate: string;
height: string;
minZoom: string;
maxZoom: string;
defaultZoom: string;
zoomDelta: string;
scale: string;
unit: string;
enableCopyTool: string;
}
const C = {
map: {
default: {
minZoom: "0",
maxZoom: "2",
zoomDelta: "0.5",
zoomSnap: "0.01",
height: "600",
scale: "1",
unit: "",
enableCopyTool: "false",
},
},
tiles: {
light: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
dark: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
attribution:
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors ' +
'© <a href="https://carto.com/attributions">CARTO</a>',
subdomains: "abcd",
maxZoom: 20,
},
} as const;
function getCurrentTheme(): "light" | "dark" {
return document.documentElement.getAttribute("saved-theme") === "dark" ? "dark" : "light";
}
async function loadScript(urls: string[]): Promise<void> {
for (const url of urls) {
try {
await new Promise<void>((resolve, reject) => {
const script = document.createElement("script");
script.src = url;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load: ${url}`));
document.head.appendChild(script);
});
return;
} catch {
// Try next URL
}
}
throw new Error(`All CDN sources failed for: ${urls.join(", ")}`);
}
async function loadDependencies(): Promise<void> {
if (typeof L === "undefined") {
await loadScript([
"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js",
"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",
]);
}
if (typeof iconify === "undefined") {
await loadScript([
"https://cdn.jsdelivr.net/npm/iconify-icon@3.0.2/dist/iconify-icon.min.js",
"https://unpkg.com/iconify-icon@3.0.2/dist/iconify-icon.min.js",
"https://code.iconify.design/iconify-icon/3.0.2/iconify-icon.min.js",
]);
}
}
function isNonEmptyObject(value: unknown): value is { [key: string]: string } {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
return (
Object.keys(value).length > 0 &&
Object.values(value).every((value) => typeof value === "string")
);
}
function parseCoordinates(coordinates: string): LatLngTuple {
const parsedCoordinates = coordinates
.replace(/\s/g, "")
.split(",")
.map((coordinate) => parseFloat(coordinate));
if (!isLatLngTuple(parsedCoordinates)) {
throw new Error("Coordinates not properly validated");
}
return parsedCoordinates;
}
function isLatLngTuple(value: unknown): value is LatLngTuple {
return (
!!value &&
Array.isArray(value) &&
value.length === 2 &&
value.every((value) => typeof value === "number" && !isNaN(value))
);
}
function distance(a: LatLng, b: LatLng): number {
const square = (value: number) => value * value;
return Math.sqrt(square(a.lat - b.lat) + square(a.lng - b.lng));
}
function isMarkerDataSet(dataset: unknown): dataset is MarkerDataSet {
if (!isNonEmptyObject(dataset)) return false;
if (
!dataset["name"] ||
!dataset["link"] ||
!dataset["coordinates"] ||
!dataset["icon"] ||
!dataset["colour"] ||
!dataset["minZoom"] ||
!dataset["maxZoom"]
) {
return false;
}
return true;
}
function getMarkerData(map: HTMLElement): MarkerDataSet[] {
const markers: NodeListOf<HTMLElement> = map.querySelectorAll("div.leaflet-marker");
const data: MarkerDataSet[] = [];
markers.forEach((marker) => {
if (isMarkerDataSet(marker.dataset)) {
data.push(marker.dataset);
}
marker.remove();
});
return data;
}
function prepareIconName(icon: string) {
// Obsidian build-in icons are prefixed with `lucide-` instead of `lucide:`
return icon.replace(/^lucide-/g, "lucide:");
}
function buildMarkerIcon(link: string, icon: string, colour: string) {
return L.divIcon({
className: "leaflet-marker-icon",
html: `
<a href="${link}">
<svg class="leaflet-marker-pin" style="fill:${colour}" viewBox="0 0 32 48">
<path d="m32,19c0,12 -12,24 -16,29c-4,-5 -16,-16 -16,-29a16,19 0 0 1 32,0"/>
</svg>
<iconify-icon class="icon leaflet-marker-inner-icon" icon="${prepareIconName(icon)}" width="19px" height="19px"></iconify-icon>
</a>
`,
iconSize: [32, 48],
iconAnchor: [16, 48],
tooltipAnchor: [17, -30],
});
}
function addMarker(markerData: MarkerDataSet, mapItem: Map, wrapHorizontally: boolean = false) {
const { link, icon, colour, minZoom, maxZoom, coordinates, name } = markerData;
const markerMinZoom = parseFloat(minZoom);
const markerMaxZoom = parseFloat(maxZoom);
const [lat, lng] = parseCoordinates(coordinates);
const tolerance = 0.00001;
const instances = new Map<number, Marker>();
function ensureInstance(offset: number): Marker {
let marker = instances.get(offset);
if (!marker) {
const options = { icon: buildMarkerIcon(link, icon, colour) };
marker = L.marker([lat, lng + offset * 360], options).bindTooltip(name);
instances.set(offset, marker);
}
return marker;
}
function update() {
const z = mapItem.getZoom();
const zoomVisible = z >= markerMinZoom - tolerance && z <= markerMaxZoom + tolerance;
if (!zoomVisible) {
for (const marker of instances.values()) marker.remove();
return;
}
if (!wrapHorizontally) {
ensureInstance(0).addTo(mapItem);
return;
}
const bounds = mapItem.getBounds();
const west = bounds.getWest();
const east = bounds.getEast();
const minOffset = Math.floor((west - lng) / 360);
const maxOffset = Math.ceil((east - lng) / 360);
for (const [offset, marker] of instances) {
if (offset < minOffset || offset > maxOffset) {
marker.remove();
instances.delete(offset);
}
}
for (let offset = minOffset; offset <= maxOffset; offset++) {
ensureInstance(offset).addTo(mapItem);
}
}
update();
mapItem.on("zoomend", update);
mapItem.on("moveend", update);
}
interface SubControlOptions {
index: number;
map: Map;
onSelectCallback: (index: number) => void;
}
class SubControl {
readonly index: number;
readonly map: Map;
private onSelectCallback: (index: number) => void = () => {};
protected button: HTMLDivElement | undefined;
protected options: MapDataSet = {
...C.map.default,
defaultZoom: C.map.default.minZoom,
src: "",
osmMode: "false",
startCoordinate: "",
};
private _isSelected: boolean = false;
get isSelected(): boolean {
return this._isSelected;
}
constructor(options: SubControlOptions) {
this.index = options.index;
this.map = options.map;
this.onSelectCallback = options.onSelectCallback;
}
setSelected(isSelected: boolean): void {
if (this._isSelected === isSelected) return;
this._isSelected = isSelected;
if (isSelected) {
this.button?.classList.add("selected");
this.onSelected();
} else {
this.button?.classList.remove("selected");
this.onDeselected();
}
}
onAdd(containerEl: HTMLElement): void {
this.button = L.DomUtil.create("div", "leaflet-control-button", containerEl);
this.button.addEventListener("click", () => this.onSelectCallback(this.index));
L.DomEvent.disableClickPropagation(containerEl);
this.onAdded();
}
onRemove(): void {
this.onRemoved();
this.button?.removeEventListener("click", () => {});
this.button?.replaceChildren();
}
updateSettings(options: MapDataSet): void {
this.options = { ...this.options, ...options };
}
protected onAdded(): void {
throw new Error("Not implemented");
}
protected onRemoved(): void {}
protected onSelected(): void {}
protected onDeselected(): void {}
mapClicked(_event: LeafletMouseEvent): void {
throw new Error("Not implemented");
}
}
class PanControl extends SubControl {
override onAdded(): void {
if (this.button) {
this.button.innerHTML = `<iconify-icon icon="lucide:mouse-pointer-2" width="19px" height="19px"></iconify-icon>`;
this.button.ariaLabel = "Pan";
}
}
override mapClicked(_event: LeafletMouseEvent): void {}
}
enum MeasureState {
Ready,
Measuring,
Finishing,
Done,
}
class MeasureControl extends SubControl {
private state: MeasureState = MeasureState.Ready;
private pathItems: LatLng[] = [];
private distance: number = 0;
private lineLayer: LayerGroup | undefined;
private pointLayer: LayerGroup | undefined;
private pathLine: Polyline | undefined;
private previewLine: Polyline | undefined;
private previewTooltip: Tooltip | undefined;
private lastElement: CircleMarker | undefined;
override onAdded(): void {
if (this.button) {
this.button.innerHTML = `<iconify-icon icon="lucide:ruler" width="19px" height="19px"></iconify-icon>`;
this.button.ariaLabel = "Measure";
}
this.lineLayer = L.layerGroup().addTo(this.map);
this.pointLayer = L.layerGroup().addTo(this.map);
this.pathLine = L.polyline([]).addTo(this.lineLayer);
this.previewLine = L.polyline([], { dashArray: "8" }).addTo(this.lineLayer);
this.previewTooltip = this.getTooltip(true).setLatLng([0, 0]);
}
override onSelected(): void {
this.map.getContainer().style.cursor = "crosshair";
this.map.on("mousemove", (event: LeafletMouseEvent) => {
this.renderPreview(event.latlng);
});
}
override onDeselected(): void {
this.map.getContainer().style.cursor = "";
this.map.removeEventListener("mousemove");
this.resetPath();
this.state = MeasureState.Ready;
}
override mapClicked(event: LeafletMouseEvent): void {
if (!this.lineLayer) throw new Error("Line layer not initialised");
switch (this.state) {
case MeasureState.Ready:
case MeasureState.Measuring: {
this.state = MeasureState.Measuring;
this.pathItems.push(event.latlng);
this.renderPath();
this.previewTooltip?.addTo(this.lineLayer);
this.renderPreview(event.latlng);
break;
}
case MeasureState.Finishing: {
this.state = MeasureState.Done;
this.lastElement?.bindTooltip(this.getTooltip(true)).bringToFront();
this.previewTooltip?.remove();
break;
}
case MeasureState.Done: {
this.resetPath();
this.state = MeasureState.Ready;
}
}
}
private renderPath(): void {
this.cleanLastElement();
this.updatePolyline(this.pathLine, this.pathItems);
const lastCoordinate = this.pathItems.at(-1);
if (lastCoordinate === undefined) return;
this.lastElement = this.getCircleMarker(lastCoordinate);
const secondLastCoordinate = this.pathItems.at(-2);
if (secondLastCoordinate === undefined) return;
this.distance +=
distance(lastCoordinate, secondLastCoordinate) * parseFloat(this.options.scale);
}
private renderPreview(mouseCoordinate: LatLng): void {
if (this.state !== MeasureState.Measuring) return;
const lastCoordinate = this.pathItems.at(-1);
if (lastCoordinate === undefined) return;
this.updatePolyline(this.previewLine, [lastCoordinate, mouseCoordinate]);
this.previewTooltip = this.previewTooltip
?.setLatLng(mouseCoordinate)
.setContent(
this.getContent(
this.distance +
distance(lastCoordinate, mouseCoordinate) * parseFloat(this.options.scale),
),
);
}
private resetPath(): void {
this.pathItems = [];
this.distance = 0;
this.cleanLastElement();
this.pointLayer?.clearLayers();
this.updatePolyline(this.pathLine, []);
this.updatePolyline(this.previewLine, []);
this.previewTooltip?.remove();
}
private updatePolyline(line: Polyline | undefined, coordinates: LatLng[]): void {
line?.setLatLngs(coordinates).redraw();
line?.getElement()?.classList.remove("leaflet-interactive");
}
private cleanLastElement(): void {
this.lastElement?.removeEventListener("click");
this.lastElement?.getElement()?.classList.remove("leaflet-interactive");
}
private getTooltip(permanent: boolean = false): Tooltip {
return L.tooltip({ permanent, offset: [15, 0] }).setContent(this.getContent(this.distance));
}
private getCircleMarker(coordinate: LatLng): CircleMarker {
if (!this.pointLayer) throw new Error("Point layer not initialised");
return L.circleMarker(coordinate, {
radius: 4,
fill: true,
fillColor: "#3388ff",
fillOpacity: 1,
})
.addTo(this.pointLayer)
.addEventListener("click", () => (this.state = MeasureState.Finishing));
}
private getContent(measurement: number): string {
return `${measurement.toFixed(1)} ${this.options?.unit ?? C.map.default.unit}`;
}
}
class CopyControl extends SubControl {
private previewTooltip: Tooltip | undefined;
override onAdded(): void {
if (this.button) {
this.button.innerHTML = `<iconify-icon icon="lucide:pin" width="19px" height="19px"></iconify-icon>`;
this.button.ariaLabel = "Copy";
}
this.previewTooltip = L.tooltip({ permanent: true, offset: [15, 0] }).setLatLng([0, 0]);
}
override onSelected(): void {
this.map.getContainer().style.cursor = "crosshair";
this.map.on("mousemove", (event: LeafletMouseEvent) => {
this.renderPreview(event.latlng);
});
this.previewTooltip?.addTo(this.map);
}
override onDeselected(): void {
this.map.getContainer().style.cursor = "";
this.map.removeEventListener("mousemove");
this.previewTooltip?.remove();
}
override mapClicked(event: LeafletMouseEvent): void {
void navigator.clipboard.writeText(this.getContent(event.latlng));
}
private renderPreview(mouseCoordinate: LatLng): void {
this.previewTooltip
?.setContent(this.getContent(mouseCoordinate))
.setLatLng(mouseCoordinate);
}
private getContent(coordinate: LatLng): string {
return `${Math.round(coordinate.lat)}, ${Math.round(coordinate.lng)}`;
}
}
interface ResetControlOptions extends SubControlOptions {
startCoord: LatLngTuple | null;
defaultZoom: number;
}
class ResetControl extends SubControl {
private startCoord: LatLngTuple | null;
private defaultZoom: number;
constructor(options: ResetControlOptions) {
super(options);
this.startCoord = options.startCoord;
this.defaultZoom = options.defaultZoom;
}
override onAdd(containerEl: HTMLElement): void {
this.button = L.DomUtil.create("div", "leaflet-control-button", containerEl);
this.button.addEventListener("click", () => {
if (!this.startCoord) return;
if (Number.isFinite(this.defaultZoom)) {
this.map.flyTo(this.startCoord, this.defaultZoom);
} else {
this.map.panTo(this.startCoord);
}
});
L.DomEvent.disableClickPropagation(containerEl);
this.onAdded();
}
override onAdded(): void {
if (this.button) {
this.button.innerHTML = `<iconify-icon icon="lucide:locate-fixed" width="19px" height="19px"></iconify-icon>`;
this.button.ariaLabel = "Reset View";
this.button.title = "Reset View";
}
}
override mapClicked(_event: LeafletMouseEvent): void {}
}
interface ControlContainerOptions extends ControlOptions {
enableCopyTool: boolean;
startCoord: LatLngTuple | null;
defaultZoom: number;
}
const DefaultControlContainerOptions: ControlContainerOptions = {
enableCopyTool: false,
startCoord: null,
defaultZoom: NaN,
};
// Lazily define ControlContainer after Leaflet is loaded (L.Control is unavailable at parse time)
type ControlContainerClass = new (options: ControlContainerOptions) => L.Control & {
updateSettings(options: MapDataSet): void;
};
let _ControlContainer: ControlContainerClass | null = null;
function getControlContainerClass(): ControlContainerClass {
if (_ControlContainer) return _ControlContainer;
class ControlContainer extends L.Control {
private controls: SubControl[] = [];
private activeIndex: number = 0;
private settings: ControlContainerOptions;
constructor(options: ControlContainerOptions) {
super({ position: "topleft" });
this.settings = { ...DefaultControlContainerOptions, ...options };
}
override onAdd(map: Map): HTMLElement {
this.registerSubControl(PanControl, map);
this.registerSubControl(MeasureControl, map);
if (this.settings.enableCopyTool) this.registerSubControl(CopyControl, map);
if (this.settings.startCoord) {
this.registerResetControl(map, this.settings.startCoord, this.settings.defaultZoom);
}
const containerEl = L.DomUtil.create("div", "leaflet-bar leaflet-control");
for (const control of this.controls) {
control.onAdd(containerEl);
}
this.controls[this.activeIndex]?.setSelected(true);
map.on("click", (event: LeafletMouseEvent) => {
for (const control of this.controls) {
if (control.isSelected) control.mapClicked(event);
}
});
return containerEl;
}
override onRemove(map: Map | undefined): void {
map?.removeEventListener("click");
for (const control of this.controls) {
control.onRemove();
}
this.controls = [];
}
updateSettings(options: MapDataSet): void {
for (const control of this.controls) {
control.updateSettings(options);
}
}
private registerSubControl(control: typeof SubControl, map: Map): void {
const onSelectCallback = (controlIndex: number) => {
this.controls.at(this.activeIndex)?.setSelected(false);
this.controls.at(controlIndex)?.setSelected(true);
this.activeIndex = controlIndex;
};
const options = { index: this.controls.length, map, onSelectCallback };
this.controls.push(new control(options));
}
private registerResetControl(
map: Map,
startCoord: LatLngTuple,
defaultZoom: number,
): void {
const options: ResetControlOptions = {
index: this.controls.length,
map,
onSelectCallback: () => {},
startCoord,
defaultZoom,
};
this.controls.push(new ResetControl(options));
}
}
_ControlContainer = ControlContainer as unknown as ControlContainerClass;
return _ControlContainer;
}
function isMapDataSet(dataset: unknown): dataset is MapDataSet {
if (!isNonEmptyObject(dataset)) return false;
const isOsmMode = dataset["osmMode"] === "true";
if (isOsmMode) {
if (!dataset["startCoordinate"]) return false;
} else {
if (!dataset["src"]) return false;
}
if (
!dataset["height"] ||
!dataset["minZoom"] ||
!dataset["maxZoom"] ||
!dataset["defaultZoom"] ||
!dataset["zoomDelta"]
) {
return false;
}
return true;
}
async function getImageMeta(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = (error) => reject(error);
image.src = url;
});
}
interface MapHandle {
map: Map;
themeHandler?: () => void;
}
async function initialiseMap(
mapElement: HTMLElement,
markers: MarkerDataSet[],
): Promise<MapHandle | undefined> {
const dataset = mapElement.dataset;
if (!isMapDataSet(dataset)) {
return;
}
mapElement.style.height = `${dataset.height}px`;
const isOsmMode = dataset.osmMode === "true";
const minZoom = parseFloat(dataset.minZoom);
const maxZoom = parseFloat(dataset.maxZoom);
const defaultZoom = parseFloat(dataset.defaultZoom);
const zoomDelta = parseFloat(dataset.zoomDelta);
let startCoord: LatLngTuple | null = null;
if (dataset.startCoordinate) {
try {
startCoord = parseCoordinates(dataset.startCoordinate);
} catch {
startCoord = null;
}
}
let mapItem: Map;
let themeHandler: (() => void) | undefined;
if (isOsmMode) {
mapItem = L.map(mapElement, {
crs: L.CRS.EPSG3857,
center: startCoord ?? [0, 0],
zoom: Number.isFinite(defaultZoom) ? defaultZoom : minZoom,
minZoom,
maxZoom,
zoomSnap: 0.01,
zoomDelta,
});
const tileLayer = L.tileLayer(C.tiles[getCurrentTheme()], {
attribution: C.tiles.attribution,
subdomains: C.tiles.subdomains,
maxZoom: C.tiles.maxZoom,
}).addTo(mapItem);
themeHandler = () => {
tileLayer.setUrl(C.tiles[getCurrentTheme()]);
};
document.addEventListener("themechange", themeHandler);
} else {
const image = await getImageMeta(dataset.src);
const bounds: LatLngBoundsExpression = [
[0, 0],
[image.naturalHeight, image.naturalWidth],
];
mapItem = L.map(mapElement, {
crs: L.CRS.Simple,
maxBounds: bounds,
minZoom,
maxZoom,
zoomSnap: 0.01,
zoomDelta,
});
L.imageOverlay(dataset.src, bounds).addTo(mapItem);
if (startCoord) {
mapItem.setView(startCoord, Number.isFinite(defaultZoom) ? defaultZoom : minZoom);
} else {
mapItem.fitBounds(bounds);
mapItem.setZoom(Number.isFinite(defaultZoom) ? defaultZoom : minZoom);
}
}
const ControlContainer = getControlContainerClass();
const controls = new ControlContainer({
enableCopyTool: dataset.enableCopyTool === "true",
startCoord,
defaultZoom,
});
controls.addTo(mapItem);
controls.updateSettings(dataset);
markers.map((marker) => addMarker(marker, mapItem, isOsmMode));
/* ===================================== */
/* 🔥 CUSTOM TILE TRANSFORM (LINEAR) */
/* ===================================== */
const OFFSET_Y = -88; // 🔥 SATU-SATUNYA VALUE
function getOffsetY(zoom: number): number
{
const scale = Math.pow(2, zoom * 0.9); // tweak 0.7–0.9
return OFFSET_Y / scale;
}
function adjustTiles(zoom: number) {
const tiles = mapItem
.getPanes()
.tilePane.querySelectorAll(".leaflet-tile-container");
const offset = getOffsetY(zoom);
tiles.forEach((el) => {
if (!(el instanceof HTMLElement)) return;
let transform = el.style.transform;
if (!transform) return;
// ❗ remove old translateY (anti stacking)
transform = transform.replace(/translateY\([^)]+\)/, "").trim();
// ✅ APPLY DI DEPAN (biar tidak kena scale)
el.style.transform = `translateY(${offset}px) ${transform}`;
});
}
/* ===================================== */
/* 🔥 EVENTS (FIX NO DRIFT) */
/* ===================================== */
// saat animasi zoom (pakai target zoom, bukan interpolasi)
mapItem.on("zoomanim", (e: any) => {
const z = (mapItem as any)._animateToZoom ?? e.zoom;
requestAnimationFrame(() => adjustTiles(z));
});
// saat zoom selesai (snap final)
mapItem.on("zoomend", () => {
adjustTiles(mapItem.getZoom());
});
// initial apply (WAJIB)
adjustTiles(mapItem.getZoom());
return { map: mapItem, themeHandler };
}
function cleanupMap(handle: MapHandle | undefined) {
if (!handle) return;
if (handle.themeHandler) {
document.removeEventListener("themechange", handle.themeHandler);
}
handle.map.clearAllEventListeners();
handle.map.remove();
}
async function initializeLeafletMaps() {
const maps: NodeListOf<HTMLElement> = document.querySelectorAll("div.leaflet-map");
if (maps.length === 0) return;
try {
await loadDependencies();
} catch (err) {
console.error("[leaflet-map] Failed to load dependencies:", err);
for (const map of Array.from(maps)) {
map.textContent =
"Failed to load map dependencies. Check your browser's content blocking settings.";
}
return;
}
for (const map of Array.from(maps)) {
const markerData = getMarkerData(map);
const mapItem = await initialiseMap(map, markerData);
window.addCleanup(() => cleanupMap(mapItem));
}
}
document.addEventListener("nav", initializeLeafletMaps);
document.addEventListener("render", initializeLeafletMaps);
export default "";