diff --git a/sidecar/handlers_countries.go b/sidecar/handlers_countries.go
new file mode 100644
index 0000000..73c2f54
--- /dev/null
+++ b/sidecar/handlers_countries.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "gorm.io/gorm"
+)
+
+// PpCountry is the country aggregation row returned to the client: a
+// 2-letter ISO 3166-1 code, the user-scoped photo count, and a representative
+// thumb hash for the sidebar row.
+type PpCountry struct {
+ Code string `json:"Code"`
+ PhotoCount int `json:"PhotoCount"`
+ Thumb string `json:"Thumb"`
+}
+
+// handleCountries aggregates photos.photo_country directly against
+// PhotoPrism's DB (no upstream proxy needed — this is a simple GROUP BY)
+// and scopes the result to the caller's BasePath, mirroring handleLabels.
+//
+// Route: GET /api/sidecar/countries (behind requireSession, ppDb != nil)
+func handleCountries(ppDb *gorm.DB) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ basePath := ctxBasePath(c)
+
+ type countryStat struct {
+ Code string `gorm:"column:code"`
+ Cnt int64 `gorm:"column:cnt"`
+ ThumbHash string `gorm:"column:thumb_hash"`
+ }
+ var stats []countryStat
+
+ query := ppDb.Table("photos p").
+ Select(`p.photo_country AS code,
+ COUNT(DISTINCT p.id) AS cnt,
+ COALESCE(MIN(f.file_hash), '') AS thumb_hash`).
+ Joins(`LEFT JOIN files f ON f.photo_uid = p.photo_uid
+ AND f.file_primary = 1
+ AND f.file_missing = 0`).
+ Where("p.deleted_at IS NULL").
+ Where("p.photo_country != '' AND p.photo_country != 'zz'")
+
+ if basePath != "" {
+ prefix := basePath + "/%"
+ query = query.Where("(p.photo_path = ? OR p.photo_path LIKE ?)", basePath, prefix)
+ }
+
+ if err := query.
+ Group("p.photo_country").
+ Having("cnt > 0").
+ Order("cnt DESC").
+ Scan(&stats).Error; err != nil {
+ c.JSON(http.StatusBadGateway, gin.H{"error": "country stats query failed"})
+ return
+ }
+
+ out := make([]PpCountry, 0, len(stats))
+ for _, s := range stats {
+ out = append(out, PpCountry{
+ Code: s.Code,
+ PhotoCount: int(s.Cnt),
+ Thumb: s.ThumbHash,
+ })
+ }
+
+ c.JSON(http.StatusOK, out)
+ }
+}
diff --git a/sidecar/main.go b/sidecar/main.go
index 8ef86ab..c0acb91 100644
--- a/sidecar/main.go
+++ b/sidecar/main.go
@@ -104,6 +104,7 @@ func main() {
if ppDb != nil {
auth.GET("/labels", handleLabels(pp, ppDb))
auth.GET("/counts", handleScopedCounts(ppDb))
+ auth.GET("/countries", handleCountries(ppDb))
}
// User-scoped photos — post-filters by BasePath so review/archive
diff --git a/web/package-lock.json b/web/package-lock.json
index fc3308e..fb63c8a 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -14,7 +14,6 @@
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"lucide-svelte": "^1.0.1",
- "maplibre-gl": "^5.24.0",
"mode-watcher": "^1.1.0",
"svelte-sonner": "^1.1.1",
"tailwind-merge": "^3.6.0",
@@ -148,110 +147,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "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.2.0",
- "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
- "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
- "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.5",
- "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.8.5.tgz",
- "integrity": "sha512-EzEJmMt6thioRH7GI9LWS7ahXTcAhAPGWCe6oTP2Ps4YnsXOOAfeqx854lZaiDnwURfHmcCKV1mr6oo0i23x6w==",
- "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",
- "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.9",
- "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.9.tgz",
- "integrity": "sha512-g/tD8EYJB97udq33ipuJ9a4Q7fcbZnTEnUrgnEc/tLMmEL+zaCbR+X5fkDBO2dgpaAMsLH179qE3UXg2N0Nc/g==",
- "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/@napi-rs/wasm-runtime": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
@@ -998,12 +893,6 @@
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"license": "MIT"
},
- "node_modules/@types/geojson": {
- "version": "7946.0.16",
- "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
- "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
- "license": "MIT"
- },
"node_modules/@types/node": {
"version": "25.8.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz",
@@ -1014,15 +903,6 @@
"undici-types": ">=7.24.0 <7.24.7"
}
},
- "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/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -1248,12 +1128,6 @@
"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/enhanced-resolve": {
"version": "5.21.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.3.tgz",
@@ -1451,12 +1325,6 @@
"node": ">= 0.4"
}
},
- "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/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1553,18 +1421,6 @@
"jiti": "lib/jiti-cli.mjs"
}
},
- "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/kdbush": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
- "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
- "license": "ISC"
- },
"node_modules/kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -1879,40 +1735,6 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
- "node_modules/maplibre-gl": {
- "version": "5.24.0",
- "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz",
- "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "@mapbox/jsonlint-lines-primitives": "^2.0.2",
- "@mapbox/point-geometry": "^1.1.0",
- "@mapbox/tiny-sdf": "^2.1.0",
- "@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",
@@ -1952,15 +1774,6 @@
"node": ">= 0.6"
}
},
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/mode-watcher": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz",
@@ -2050,12 +1863,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"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/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
@@ -2086,18 +1893,6 @@
],
"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/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -2147,18 +1942,6 @@
"node": "^10 || ^12 || >=14"
}
},
- "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/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/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@@ -2168,12 +1951,6 @@
"node": ">=10"
}
},
- "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/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -2188,15 +1965,6 @@
"url": "https://paulmillr.com/funding/"
}
},
- "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/rolldown": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz",
@@ -2309,15 +2077,6 @@
"inline-style-parser": "0.2.7"
}
},
- "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/svelte": {
"version": "5.55.7",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.7.tgz",
@@ -2489,12 +2248,6 @@
"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/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
diff --git a/web/package.json b/web/package.json
index 90c09a0..5a53a4b 100644
--- a/web/package.json
+++ b/web/package.json
@@ -30,7 +30,6 @@
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"lucide-svelte": "^1.0.1",
- "maplibre-gl": "^5.24.0",
"mode-watcher": "^1.1.0",
"svelte-sonner": "^1.1.1",
"tailwind-merge": "^3.6.0",
diff --git a/web/src/lib/components/layout/LeftSidebar.svelte b/web/src/lib/components/layout/LeftSidebar.svelte
index ebeba30..4179646 100644
--- a/web/src/lib/components/layout/LeftSidebar.svelte
+++ b/web/src/lib/components/layout/LeftSidebar.svelte
@@ -214,7 +214,8 @@
keywords: 'Keywords',
people: 'People',
colors: 'Colors',
- ratings: 'Ratings'
+ ratings: 'Ratings',
+ countries: 'Countries'
};
function isTagCategoryActive(cat: TagCategory): boolean {
@@ -381,7 +382,7 @@
//
// `getCount` is a getter (not a snapshot) so the badge reads the latest
// derived value on every render — the arrays themselves are constant.
- // Map and Tags intentionally render without a count badge; the count
+ // Tags intentionally renders without a count badge; the count
// columns inside the TagsBrowserSidebar are the canonical surface for
// per-tag totals. Review rolls in the duplicates tabs hosted under
// /review — stacks always contributes; cross-folder only contributes
@@ -396,10 +397,9 @@
// separate "everything regardless of folder" destination would just
// duplicate it for users whose photos live under the root.
const views: ViewItem[] = [
- { kind: 'route', href: '/map', label: 'Map', getCount: () => undefined }
// Tags is rendered as a bespoke expandable block below the
// `views` loop — it has sub-categories (Labels/Keywords/Colors/
- // Ratings) and a chevron, neither of which fits the flat
+ // Ratings/Countries) and a chevron, neither of which fits the flat
// section/route ViewItem shape. Notes lives under that expandable
// alongside the tag categories.
];
diff --git a/web/src/lib/components/sidebar/RightSidebar.svelte b/web/src/lib/components/sidebar/RightSidebar.svelte
index 61c5e88..39eb49a 100644
--- a/web/src/lib/components/sidebar/RightSidebar.svelte
+++ b/web/src/lib/components/sidebar/RightSidebar.svelte
@@ -6,7 +6,6 @@
PUT (Details fields need the full body).
-->
@@ -448,6 +483,69 @@
{/if}
{/if}
+ {:else if category === 'countries'}
+ {#if countriesQuery.isPending}
+
+ Loading more… ({visibleCount} / {filteredCountries.length}) +
+ {/if} +Loading colors…
diff --git a/web/src/lib/services/photoprism.ts b/web/src/lib/services/photoprism.ts index dbaf096..d8eb382 100644 --- a/web/src/lib/services/photoprism.ts +++ b/web/src/lib/services/photoprism.ts @@ -560,36 +560,19 @@ export async function listFolderCounts(paths: string[]): Promise