Merge pull request #7 from blamouche/feat/maplibre-match-obsidian-maps

v1.5.0: migrate to MapLibre GL with OpenFreeMap styles
This commit is contained in:
Benoit 2026-04-22 06:17:45 +02:00 committed by GitHub
commit 63015be761
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 388 additions and 773 deletions

View file

@ -1,5 +1,21 @@
# Changelog
## 1.5.0
### Features
- **Map engine migration** — Both the geocoder modal preview and the `geocode-map` code block now use MapLibre GL JS with OpenFreeMap vector styles, matching the look and feel of Obsidian's built-in Bases maps. The basemap auto-switches between the `bright` and `dark` styles based on the active Obsidian theme.
- **Marker restyling** — Markers now rebuild live when the icon or color changes in the modal without losing the current position on the map.
### Fixes
- **Obsidian Maps locate button stacking** — The experimental locate button injected into Bases map views no longer duplicates when Bases already ships a native MapLibre `GeolocateControl`, and stale buttons from previous plugin versions are cleaned up on load.
### Changes
- Replaced the Leaflet + CARTO Voyager raster tiles with MapLibre GL + OpenFreeMap. The plugin no longer depends on Leaflet; bundle size increases accordingly.
- esbuild target bumped to `es2020` (required by MapLibre's BigInt usage).
## 1.4.0
### Features

View file

@ -31,7 +31,7 @@ const context = await esbuild.context({
...builtins,
],
format: "cjs",
target: "es2018",
target: "es2020",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,

View file

@ -1,7 +1,7 @@
{
"id": "geocode-note",
"name": "Geocode Note",
"version": "1.4.0",
"version": "1.5.0",
"minAppVersion": "0.15.0",
"description": "Add geographic coordinates, icon and color to your notes via geolocation or address lookup.",
"author": "Benoit Lamouche",

263
package-lock.json generated
View file

@ -1,18 +1,17 @@
{
"name": "obsidian-geocode-note",
"version": "1.0.1",
"version": "1.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-geocode-note",
"version": "1.0.1",
"version": "1.4.0",
"license": "MIT",
"dependencies": {
"leaflet": "^1.9.4"
"maplibre-gl": "^5.23.0"
},
"devDependencies": {
"@types/leaflet": "^1.9.21",
"@types/node": "^16.11.6",
"@typescript-eslint/parser": "^8.58.2",
"builtin-modules": "3.3.0",
@ -709,6 +708,111 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
"license": "ISC"
},
"node_modules/@mapbox/tiny-sdf": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.1.0.tgz",
"integrity": "sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/vector-tile": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz",
"integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/point-geometry": "~1.1.0",
"@types/geojson": "^7946.0.16",
"pbf": "^4.0.1"
}
},
"node_modules/@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
"license": "ISC",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@maplibre/geojson-vt": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.0.tgz",
"integrity": "sha512-2eIY4gZxeKIVOZVNkAMb+5NgXhgsMQpOveTQAvnp53LYqHGJZDidk7Ew0Tged9PThidpbS+NFTh0g4zivhPDzQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "24.8.1",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.8.1.tgz",
"integrity": "sha512-zxa92qF96ZNojLxeAjnaRpjVCy+swoUNJvDhtpC90k7u5F0TMr4GmvNqMKvYrMoPB8d7gRSXbMG1hBbmgESIsw==",
"license": "ISC",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/unitbezier": "^0.0.1",
"json-stringify-pretty-compact": "^4.0.0",
"minimist": "^1.2.8",
"quickselect": "^3.0.0",
"rw": "^1.3.3",
"tinyqueue": "^3.0.0"
},
"bin": {
"gl-style-format": "dist/gl-style-format.mjs",
"gl-style-migrate": "dist/gl-style-migrate.mjs",
"gl-style-validate": "dist/gl-style-validate.mjs"
}
},
"node_modules/@maplibre/mlt": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.8.tgz",
"integrity": "sha512-8vtfYGidr1rNkv5IwIoU2lfe3Oy+Wa8HluzQYcQi9cveU9K3pweAal/poQj4GJ0K/EW4bTQp2wVAs09g2yDRZg==",
"license": "(MIT OR Apache-2.0)",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0"
}
},
"node_modules/@maplibre/vt-pbf": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.0.tgz",
"integrity": "sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==",
"license": "MIT",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0",
"@mapbox/vector-tile": "^2.0.4",
"@maplibre/geojson-vt": "^5.0.4",
"@types/geojson": "^7946.0.16",
"@types/supercluster": "^7.1.3",
"pbf": "^4.0.1",
"supercluster": "^8.0.1"
}
},
"node_modules/@maplibre/vt-pbf/node_modules/@maplibre/geojson-vt": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz",
"integrity": "sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==",
"license": "ISC"
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
@ -807,7 +911,6 @@
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
@ -824,16 +927,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/leaflet": {
"version": "1.9.21",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/node": {
"version": "16.18.126",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz",
@ -841,6 +934,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/supercluster": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz",
"integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/tern": {
"version": "0.23.9",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
@ -1640,6 +1742,12 @@
"node": ">= 0.4"
}
},
"node_modules/earcut": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
"license": "ISC"
},
"node_modules/empathic": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
@ -2846,6 +2954,12 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/gl-matrix": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz",
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
"license": "MIT"
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -3544,6 +3658,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/json-stringify-pretty-compact": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
"license": "MIT"
},
"node_modules/json5": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
@ -3623,6 +3743,12 @@
"node": ">=4.0"
}
},
"node_modules/kdbush": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
"integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
"license": "ISC"
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -3633,12 +3759,6 @@
"json-buffer": "3.0.1"
}
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause"
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -3689,6 +3809,40 @@
"loose-envify": "cli.js"
}
},
"node_modules/maplibre-gl": {
"version": "5.23.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.23.0.tgz",
"integrity": "sha512-aou8YBNFS8uVtDWFWt0W/6oorfl18wt+oIA8fnXk1kivjkbtXi9gGrQvflTpwrR3hG13aWdIdbYWeN0NFMV7ag==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/point-geometry": "^1.1.0",
"@mapbox/tiny-sdf": "^2.0.7",
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^2.0.4",
"@mapbox/whoots-js": "^3.1.0",
"@maplibre/geojson-vt": "^6.1.0",
"@maplibre/maplibre-gl-style-spec": "^24.8.1",
"@maplibre/mlt": "^1.1.8",
"@maplibre/vt-pbf": "^4.3.0",
"@types/geojson": "^7946.0.16",
"earcut": "^3.0.2",
"gl-matrix": "^3.4.4",
"kdbush": "^4.0.2",
"murmurhash-js": "^1.0.0",
"pbf": "^4.0.1",
"potpack": "^2.1.0",
"quickselect": "^3.0.0",
"tinyqueue": "^3.0.0"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.1.0"
},
"funding": {
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -3719,7 +3873,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -3749,6 +3902,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
"license": "MIT"
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -4031,6 +4190,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/pbf": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
"license": "BSD-3-Clause",
"dependencies": {
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/picomatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
@ -4054,6 +4225,12 @@
"node": ">= 0.4"
}
},
"node_modules/potpack": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz",
"integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
"license": "ISC"
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -4076,6 +4253,12 @@
"react-is": "^16.13.1"
}
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
"integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -4086,6 +4269,12 @@
"node": ">=6"
}
},
"node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -4201,6 +4390,15 @@
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"license": "MIT",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@ -4211,6 +4409,12 @@
"node": ">=0.12"
}
},
"node_modules/rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
"license": "BSD-3-Clause"
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@ -4601,6 +4805,15 @@
"license": "MIT",
"peer": true
},
"node_modules/supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -4682,6 +4895,12 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC"
},
"node_modules/toml-eslint-parser": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-geocode-note",
"version": "1.4.0",
"version": "1.5.0",
"description": "Add geographic coordinates to Obsidian notes",
"main": "main.js",
"scripts": {
@ -11,7 +11,6 @@
"author": "Benoit Lamouche",
"license": "MIT",
"devDependencies": {
"@types/leaflet": "^1.9.21",
"@types/node": "^16.11.6",
"@typescript-eslint/parser": "^8.58.2",
"builtin-modules": "3.3.0",
@ -23,6 +22,6 @@
"typescript": "5.5.4"
},
"dependencies": {
"leaflet": "^1.9.4"
"maplibre-gl": "^5.23.0"
}
}

View file

@ -11,7 +11,32 @@ import {
TFile,
requestUrl,
} from "obsidian";
import * as L from "leaflet";
import * as maplibregl from "maplibre-gl";
import type { Map as MLMap, Marker as MLMarker } from "maplibre-gl";
// --- Basemap styles matching Obsidian Maps (OpenFreeMap via MapLibre GL) ---
const OPENFREEMAP_STYLE_BRIGHT = "https://tiles.openfreemap.org/styles/bright";
const OPENFREEMAP_STYLE_DARK = "https://tiles.openfreemap.org/styles/dark";
function isDarkTheme(): boolean {
return document.body.classList.contains("theme-dark");
}
function obsidianMapStyle(): string {
return isDarkTheme() ? OPENFREEMAP_STYLE_DARK : OPENFREEMAP_STYLE_BRIGHT;
}
function buildMarkerEl(iconName: string, colorName: string): HTMLElement {
const hex = resolveMarkerHex(colorName);
const markerEl = document.createElement("div");
markerEl.className = "geocode-inline-map-marker";
markerEl.style.setProperty("--geocode-marker-color", hex);
const iconEl = document.createElement("span");
iconEl.className = "geocode-inline-map-marker-icon";
setIcon(iconEl, iconName);
markerEl.appendChild(iconEl);
return markerEl;
}
// --- Icon catalog for map markers ---
const MARKER_ICONS: { category: string; icons: { name: string; label: string }[] }[] = [
@ -296,8 +321,8 @@ class GeocodeModal extends Modal {
private submitBtn: HTMLButtonElement | null = null;
private mapWrapper: HTMLElement | null = null;
private mapEl: HTMLElement | null = null;
private map: L.Map | null = null;
private marker: L.Marker | null = null;
private map: MLMap | null = null;
private marker: MLMarker | null = null;
private addressInput: HTMLInputElement | null = null;
private reverseGeocodeToken = 0;
@ -535,6 +560,7 @@ class GeocodeModal extends Modal {
btn.addEventListener("click", () => {
this.selectedIcon = icon.name;
this.refreshIconSelection();
this.updateMarkerStyle();
});
}
}
@ -555,6 +581,7 @@ class GeocodeModal extends Modal {
btn.addEventListener("click", () => {
this.selectedColor = color.name;
this.refreshColorSelection();
this.updateMarkerStyle();
});
}
}
@ -611,33 +638,28 @@ class GeocodeModal extends Modal {
this.mapWrapper.removeClass("geocode-hidden");
if (!this.map) {
const markerIcon = L.divIcon({
className: "geocode-leaflet-marker",
iconSize: [28, 28],
iconAnchor: [14, 28],
});
this.map = L.map(this.mapEl, {
center: [coords.lat, coords.lon],
const map = new maplibregl.Map({
container: this.mapEl,
style: obsidianMapStyle(),
center: [coords.lon, coords.lat],
zoom: 14,
zoomControl: true,
attributionControl: true,
attributionControl: { compact: true },
});
this.map = map;
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
L.tileLayer("https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png", {
subdomains: "abcd",
maxZoom: 19,
attribution: "© OpenStreetMap, © CARTO",
}).addTo(this.map);
this.marker = L.marker([coords.lat, coords.lon], {
const markerEl = buildMarkerEl(this.selectedIcon, this.selectedColor);
this.marker = new maplibregl.Marker({
element: markerEl,
anchor: "bottom",
draggable: true,
icon: markerIcon,
}).addTo(this.map);
})
.setLngLat([coords.lon, coords.lat])
.addTo(map);
this.marker.on("dragend", () => {
if (!this.marker) return;
const pos = this.marker.getLatLng();
const pos = this.marker.getLngLat();
this.latitude = pos.lat.toFixed(6);
this.longitude = pos.lng.toFixed(6);
if (this.coordDisplay) {
@ -648,15 +670,26 @@ class GeocodeModal extends Modal {
void this.refreshAddressFromCoords();
});
// Leaflet needs a size recalculation once the container is visible
setTimeout(() => this.map?.invalidateSize(), 0);
// MapLibre needs a resize once the container is laid out
setTimeout(() => this.map?.resize(), 0);
} else {
this.map.setView([coords.lat, coords.lon], this.map.getZoom());
this.marker?.setLatLng([coords.lat, coords.lon]);
setTimeout(() => this.map?.invalidateSize(), 0);
this.map.setCenter([coords.lon, coords.lat]);
this.marker?.setLngLat([coords.lon, coords.lat]);
setTimeout(() => this.map?.resize(), 0);
}
}
private updateMarkerStyle(): void {
if (!this.marker) return;
const el = this.marker.getElement();
el.empty();
const rebuilt = buildMarkerEl(this.selectedIcon, this.selectedColor);
// Copy children from the rebuilt marker to preserve MapLibre's wrapper
el.className = rebuilt.className;
el.style.setProperty("--geocode-marker-color", rebuilt.style.getPropertyValue("--geocode-marker-color"));
Array.from(rebuilt.childNodes).forEach((n) => el.appendChild(n));
}
private updateSubmitState() {
if (!this.submitBtn) return;
this.submitBtn.disabled = !(this.latitude && this.longitude);
@ -1049,7 +1082,7 @@ function resolveMarkerHex(colorName: string): string {
}
class InlineMapRenderChild extends MarkdownRenderChild {
private map: L.Map | null = null;
private map: MLMap | null = null;
private readonly opts: InlineMapOptions;
constructor(containerEl: HTMLElement, opts: InlineMapOptions) {
@ -1064,43 +1097,28 @@ class InlineMapRenderChild extends MarkdownRenderChild {
const mapEl = this.containerEl.createDiv({ cls: "geocode-inline-map" });
mapEl.style.height = `${this.opts.height}px`;
const hex = resolveMarkerHex(this.opts.color);
const markerEl = document.createElement("div");
markerEl.className = "geocode-inline-map-marker";
markerEl.style.setProperty("--geocode-marker-color", hex);
const iconEl = document.createElement("span");
iconEl.className = "geocode-inline-map-marker-icon";
setIcon(iconEl, this.opts.icon);
markerEl.appendChild(iconEl);
const markerIcon = L.divIcon({
className: "geocode-inline-map-marker-wrapper",
html: markerEl.outerHTML,
iconSize: [32, 38],
iconAnchor: [16, 38],
});
this.map = L.map(mapEl, {
center: [this.opts.lat, this.opts.lon],
const map = new maplibregl.Map({
container: mapEl,
style: obsidianMapStyle(),
center: [this.opts.lon, this.opts.lat],
zoom: 14,
zoomControl: true,
attributionControl: true,
scrollWheelZoom: false,
interactive: true,
scrollZoom: false,
attributionControl: { compact: true },
});
this.map = map;
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
L.tileLayer("https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png", {
subdomains: "abcd",
maxZoom: 19,
attribution: "© OpenStreetMap, © CARTO",
}).addTo(this.map);
const markerEl = buildMarkerEl(this.opts.icon, this.opts.color);
const marker = new maplibregl.Marker({ element: markerEl, anchor: "bottom" })
.setLngLat([this.opts.lon, this.opts.lat])
.addTo(map);
const marker = L.marker([this.opts.lat, this.opts.lon], { icon: markerIcon }).addTo(this.map);
if (this.opts.address) {
marker.bindPopup(this.opts.address);
marker.setPopup(new maplibregl.Popup({ offset: 24 }).setText(this.opts.address));
}
// Leaflet needs a size recalc once the container is attached
setTimeout(() => this.map?.invalidateSize(), 0);
setTimeout(() => this.map?.resize(), 0);
}
onunload(): void {
@ -1363,6 +1381,11 @@ export default class GeocodeNotePlugin extends Plugin {
const reg = this.findMapBasesRegistration();
if (!reg || typeof reg.factory !== "function") return;
// Wipe any buttons left behind by a previous (buggy) session before we
// (re)install. Otherwise the idempotent guard in tryAttachControl would
// keep those stale duplicates in place.
document.querySelectorAll(".geocode-maps-locate").forEach((n) => n.remove());
const origFactory = reg.factory.bind(reg) as (
controller: unknown,
containerEl: HTMLElement
@ -1399,16 +1422,25 @@ export default class GeocodeNotePlugin extends Plugin {
const map = (mv as { map?: DuckMapLibreMap }).map;
if (!map) return;
const ctrl = controls.get(map);
if (!ctrl) return;
try {
map.removeControl(ctrl);
} catch {
// ignore
if (ctrl) {
try {
map.removeControl(ctrl);
} catch {
// ignore
}
controls.delete(map);
}
controls.delete(map);
// Also scrub any untracked duplicates that accumulated from earlier buggy versions.
const container = (
map as { getContainer?: () => HTMLElement | undefined }
).getContainer?.();
container?.querySelectorAll(".geocode-maps-locate").forEach((n) => n.remove());
});
});
// Safety net: nuke any remaining .geocode-maps-locate anywhere in the workspace DOM.
document.querySelectorAll(".geocode-maps-locate").forEach((n) => n.remove());
this.obsidianMapsPatch = null;
}
@ -1463,7 +1495,15 @@ export default class GeocodeNotePlugin extends Plugin {
const v = view as {
initializeMap?: (...args: unknown[]) => unknown;
map?: DuckMapLibreMap;
__geocodeHooked?: boolean;
};
// If this view was already hooked, only attempt to (re)attach to its current map
if (v.__geocodeHooked) {
this.tryAttachControl(v, controls);
return;
}
const origInit = v.initializeMap;
if (typeof origInit !== "function") return;
@ -1472,6 +1512,7 @@ export default class GeocodeNotePlugin extends Plugin {
this.tryAttachControl(v, controls);
return result;
};
v.__geocodeHooked = true;
// Already-open view whose map is already built: attach immediately
this.tryAttachControl(v, controls);
@ -1483,7 +1524,23 @@ export default class GeocodeNotePlugin extends Plugin {
) {
const map = view.map;
if (!map || typeof map.addControl !== "function") return;
if (controls.has(map)) return;
const container = (map as { getContainer?: () => HTMLElement | undefined }).getContainer?.();
if (!container) return;
// Bases uses MapLibre's native GeolocateControl. Don't stack ours on top.
if (container.querySelector(".maplibregl-ctrl-geolocate")) {
container.querySelectorAll(".geocode-maps-locate").forEach((n) => n.remove());
return;
}
// Idempotent guard: if this map's container already has our button, do
// nothing. When Bases destroys and recreates the map, the container and
// its children are gone too, giving us a clean slate for the next attach.
if (container.querySelector(".geocode-maps-locate")) {
return;
}
try {
const ctrl = new MapLibreLocateControl();
map.addControl(ctrl, "top-right");

File diff suppressed because one or more lines are too long