Adam 3 месяцев назад
Родитель
Сommit
7af6eafb41

+ 28 - 0
bun.lock

@@ -671,17 +671,25 @@
         "@solidjs/meta": "catalog:",
         "@solidjs/router": "catalog:",
         "@solidjs/start": "catalog:",
+        "d3-geo": "3.1.1",
         "d3-scale": "4.0.2",
         "effect": "catalog:",
+        "i18n-iso-countries": "7.14.0",
         "nitro": "3.0.1-alpha.1",
         "solid-js": "catalog:",
         "sst": "catalog:",
+        "topojson-client": "3.1.0",
         "vite": "catalog:",
+        "world-atlas": "2.0.2",
       },
       "devDependencies": {
         "@cloudflare/workers-types": "catalog:",
         "@types/bun": "catalog:",
+        "@types/d3-geo": "3.1.0",
         "@types/d3-scale": "4.0.9",
+        "@types/geojson": "7946.0.16",
+        "@types/topojson-client": "3.1.5",
+        "@types/topojson-specification": "1.0.5",
         "@typescript/native-preview": "catalog:",
         "typescript": "catalog:",
       },
@@ -2466,6 +2474,8 @@
 
     "@types/cross-spawn": ["@types/cross-spawn@6.0.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA=="],
 
+    "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="],
+
     "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
 
     "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
@@ -2486,6 +2496,8 @@
 
     "@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="],
 
+    "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
+
     "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
 
     "@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="],
@@ -2568,6 +2580,10 @@
 
     "@types/ssri": ["@types/ssri@7.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw=="],
 
+    "@types/topojson-client": ["@types/topojson-client@3.1.5", "", { "dependencies": { "@types/geojson": "*", "@types/topojson-specification": "*" } }, "sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw=="],
+
+    "@types/topojson-specification": ["@types/topojson-specification@1.0.5", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ=="],
+
     "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
 
     "@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="],
@@ -3048,6 +3064,8 @@
 
     "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
 
+    "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
+
     "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
 
     "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
@@ -3124,6 +3142,8 @@
 
     "dfa": ["dfa@1.2.0", "", {}, "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="],
 
+    "diacritics": ["diacritics@1.3.0", "", {}, "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="],
+
     "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
 
     "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
@@ -3598,6 +3618,8 @@
 
     "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
 
+    "i18n-iso-countries": ["i18n-iso-countries@7.14.0", "", { "dependencies": { "diacritics": "1.3.0" } }, "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg=="],
+
     "i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
 
     "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="],
@@ -4918,6 +4940,8 @@
 
     "toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.8", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-b+5ynEFp4Woe5a22hzNQm42lD23t13ZMihVxHbzjA50zdcM9aOSJTIjdJ0PDSd4/50HbBXcpHiQsz6rM4N88ww=="],
 
+    "topojson-client": ["topojson-client@3.1.0", "", { "dependencies": { "commander": "2" }, "bin": { "topo2geo": "bin/topo2geo", "topomerge": "bin/topomerge", "topoquantize": "bin/topoquantize" } }, "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw=="],
+
     "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
 
     "traverse": ["traverse@0.3.9", "", {}, "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ=="],
@@ -5174,6 +5198,8 @@
 
     "workerd": ["workerd@1.20251118.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251118.0", "@cloudflare/workerd-darwin-arm64": "1.20251118.0", "@cloudflare/workerd-linux-64": "1.20251118.0", "@cloudflare/workerd-linux-arm64": "1.20251118.0", "@cloudflare/workerd-windows-64": "1.20251118.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Om5ns0Lyx/LKtYI04IV0bjIrkBgoFNg0p6urzr2asekJlfP18RqFzyqMFZKf0i9Gnjtz/JfAS/Ol6tjCe5JJsQ=="],
 
+    "world-atlas": ["world-atlas@2.0.2", "", {}, "sha512-IXfV0qwlKXpckz1FhwXVwKRjiIhOnWttOskm5CtxMsjgE/MXAYRHWJqgXOpM8IkcPBoXnyTU5lFHcYa5ChG0LQ=="],
+
     "wrangler": ["wrangler@4.50.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.11", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251118.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251118.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251118.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-+nuZuHZxDdKmAyXOSrHlciGshCoAPiy5dM+t6mEohWm7HpXvTHmWQGUf/na9jjWlWJHCJYOWzkA1P5HBJqrIEA=="],
 
     "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
@@ -6152,6 +6178,8 @@
 
     "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
 
+    "topojson-client/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
+
     "tree-sitter-bash/node-addon-api": ["node-addon-api@8.7.0", "", {}, "sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA=="],
 
     "tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],

+ 10 - 2
packages/stats/app/package.json

@@ -18,17 +18,25 @@
     "@solidjs/meta": "catalog:",
     "@solidjs/router": "catalog:",
     "@solidjs/start": "catalog:",
+    "d3-geo": "3.1.1",
     "d3-scale": "4.0.2",
     "effect": "catalog:",
+    "i18n-iso-countries": "7.14.0",
     "nitro": "3.0.1-alpha.1",
-    "sst": "catalog:",
     "solid-js": "catalog:",
-    "vite": "catalog:"
+    "sst": "catalog:",
+    "topojson-client": "3.1.0",
+    "vite": "catalog:",
+    "world-atlas": "2.0.2"
   },
   "devDependencies": {
     "@cloudflare/workers-types": "catalog:",
     "@types/bun": "catalog:",
+    "@types/d3-geo": "3.1.0",
     "@types/d3-scale": "4.0.9",
+    "@types/geojson": "7946.0.16",
+    "@types/topojson-client": "3.1.5",
+    "@types/topojson-specification": "1.0.5",
     "@typescript/native-preview": "catalog:",
     "typescript": "catalog:"
   },

+ 203 - 0
packages/stats/app/src/routes/index.css

@@ -1647,6 +1647,7 @@
   :is(
     [data-section="leaderboard"],
     [data-section="market-share"],
+    [data-section="geo-breakdown"],
     [data-section="token-cost"],
     [data-section="cache-ratio"],
     [data-section="session-cost"]
@@ -2123,6 +2124,191 @@
   white-space: nowrap;
 }
 
+[data-page="stats"] [data-component="geo-breakdown"] {
+  display: grid;
+  gap: 16px;
+  align-items: start;
+}
+
+[data-page="stats"] [data-slot="geo-map-panel"] {
+  position: relative;
+  min-width: 0;
+  overflow: hidden;
+  background: var(--stats-layer);
+  border: 1px solid var(--stats-line);
+}
+
+[data-page="stats"] [data-component="geo-world-map"] {
+  display: block;
+  width: 100%;
+  height: auto;
+}
+
+[data-page="stats"] [data-slot="geo-countries"] path {
+  fill: var(--stats-layer-2);
+  stroke: var(--stats-bg);
+  stroke-width: 0.45px;
+  transition:
+    fill 140ms ease,
+    opacity 140ms ease;
+}
+
+[data-page="stats"] [data-slot="geo-countries"] path[data-has-data="true"] {
+  fill: var(--stats-accent);
+  opacity: var(--geo-country-opacity);
+  cursor: pointer;
+}
+
+[data-page="stats"] [data-slot="geo-countries"] path[data-active="true"] {
+  fill: color-mix(in srgb, var(--stats-accent) 70%, var(--stats-text));
+  opacity: 1;
+}
+
+[data-page="stats"] [data-slot="geo-borders"] {
+  fill: none;
+  stroke: var(--stats-line-strong);
+  stroke-linejoin: round;
+  stroke-width: 0.6px;
+  pointer-events: none;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] {
+  position: absolute;
+  bottom: 16px;
+  left: 16px;
+  display: grid;
+  gap: 8px;
+  min-width: 168px;
+  max-width: calc(100% - 32px);
+  box-sizing: border-box;
+  padding: 12px;
+  background: color-mix(in srgb, var(--stats-bg) 92%, transparent);
+  box-shadow:
+    0 0 0 0.5px var(--stats-line-strong),
+    0 6px 16px #0000000d,
+    0 2px 6px #0000000f;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] span,
+[data-page="stats"] [data-slot="geo-active-country"] em {
+  color: var(--stats-faint);
+  font-style: normal;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] span {
+  font-size: 10px;
+  font-weight: 600;
+  line-height: 1;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] strong {
+  min-width: 0;
+  overflow: hidden;
+  color: var(--stats-text);
+  font-size: 16px;
+  font-weight: 600;
+  line-height: 1.2;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] p {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  color: var(--stats-muted);
+  font-size: 11px;
+  font-weight: 500;
+  line-height: 1;
+}
+
+[data-page="stats"] [data-slot="geo-active-country"] b {
+  color: var(--stats-accent-text);
+  font-weight: 600;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(min(100%, 212px), 1fr));
+  align-content: start;
+  gap: 8px 10px;
+  min-width: 0;
+  margin: 20px 0 0;
+  padding: 0;
+  list-style: none;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] li {
+  min-width: 0;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] button {
+  display: grid;
+  grid-template-columns: 28px 8px minmax(0, 1fr) auto auto;
+  align-items: center;
+  gap: 10px;
+  width: 100%;
+  min-width: 0;
+  height: 32px;
+  padding: 0 8px;
+  border: 1px solid var(--stats-line);
+  border-radius: 0;
+  background: var(--stats-layer);
+  color: var(--stats-muted);
+  font-size: 11px;
+  line-height: 1;
+  text-align: left;
+  transition:
+    border-color 120ms ease,
+    background 120ms ease,
+    box-shadow 120ms ease;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] button[data-active="true"],
+[data-page="stats"] [data-component="geo-country-list"] button:focus-visible {
+  border-color: var(--stats-text);
+  background: var(--stats-layer);
+  color: var(--stats-text);
+  box-shadow:
+    0 0 0 0.5px color-mix(in srgb, var(--stats-text) 70%, transparent),
+    0 1px 2px -1px #00000014,
+    0 2px 4px #0000000a;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] span {
+  color: var(--stats-faint);
+  font-weight: 500;
+  text-align: center;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] i {
+  width: 8px;
+  height: 8px;
+  background: var(--stats-accent);
+  opacity: var(--geo-row-opacity);
+}
+
+[data-page="stats"] [data-component="geo-country-list"] strong {
+  min-width: 0;
+  overflow: hidden;
+  color: var(--stats-text);
+  font-weight: 600;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] em,
+[data-page="stats"] [data-component="geo-country-list"] b {
+  color: var(--stats-faint);
+  font-style: normal;
+}
+
+[data-page="stats"] [data-component="geo-country-list"] b {
+  color: var(--stats-muted);
+  font-weight: 500;
+}
+
 [data-page="stats"] [data-component="token-cost"],
 [data-page="stats"] [data-component="cache-ratio"] {
   position: relative;
@@ -2734,6 +2920,7 @@
   [data-page="stats"] [data-section="top-models"],
   [data-page="stats"] [data-section="leaderboard"],
   [data-page="stats"] [data-section="market-share"],
+  [data-page="stats"] [data-section="geo-breakdown"],
   [data-page="stats"] [data-section="token-cost"],
   [data-page="stats"] [data-section="cache-ratio"],
   [data-page="stats"] [data-section="session-cost"] {
@@ -2866,6 +3053,7 @@
   [data-page="stats"] [data-section="top-models"],
   [data-page="stats"] [data-section="leaderboard"],
   [data-page="stats"] [data-section="market-share"],
+  [data-page="stats"] [data-section="geo-breakdown"],
   [data-page="stats"] [data-section="token-cost"],
   [data-page="stats"] [data-section="cache-ratio"],
   [data-page="stats"] [data-section="session-cost"] {
@@ -2960,6 +3148,21 @@
     height: 400px;
   }
 
+  [data-page="stats"] [data-slot="geo-active-country"] {
+    position: static;
+    min-width: 0;
+    max-width: none;
+    margin: 0 12px 12px;
+  }
+
+  [data-page="stats"] [data-component="geo-country-list"] button {
+    grid-template-columns: 26px 8px minmax(0, 1fr) auto;
+  }
+
+  [data-page="stats"] [data-component="geo-country-list"] b {
+    display: none;
+  }
+
   [data-page="stats"] [data-component="top-models-chart"][data-dense-labels="true"],
   [data-page="stats"] [data-component="market-share"][data-dense-labels="true"] {
     overflow-x: auto;

+ 219 - 1
packages/stats/app/src/routes/index.tsx

@@ -1,6 +1,11 @@
 import "./index.css"
 import { Link, Meta, Title } from "@solidjs/meta"
 import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
+import { geoEquirectangular, geoPath } from "d3-geo"
+import { scaleSqrt } from "d3-scale"
+import countryCodesSource from "i18n-iso-countries/codes.json?raw"
+import { feature, mesh } from "topojson-client"
+import countriesTopologySource from "world-atlas/countries-110m.json?raw"
 import ibmPlexMonoRegularLatin1 from "@ibm/plex/IBM-Plex-Mono/fonts/split/woff2/IBMPlexMono-Regular-Latin1.woff2?url"
 import ibmPlexMonoMediumLatin1 from "@ibm/plex/IBM-Plex-Mono/fonts/split/woff2/IBMPlexMono-Medium-Latin1.woff2?url"
 import ibmPlexMonoSemiBoldLatin1 from "@ibm/plex/IBM-Plex-Mono/fonts/split/woff2/IBMPlexMono-SemiBold-Latin1.woff2?url"
@@ -10,6 +15,7 @@ import statsUnfurlRankings from "../asset/unfurl-rankings.png?url"
 import {
   getStatsHomeData,
   type CacheRatioEntry,
+  type CountryEntry,
   type LeaderboardEntry,
   type MarketDay,
   type StatsHomeData,
@@ -21,6 +27,8 @@ import { runtime } from "@opencode-ai/stats-core/runtime"
 import { createAsync, query } from "@solidjs/router"
 import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show, type JSX } from "solid-js"
 import { getRequestEvent } from "solid-js/web"
+import type { FeatureCollection, GeometryObject, GeoJsonProperties } from "geojson"
+import type { GeometryCollection, Topology } from "topojson-specification"
 
 const products = ["All Users", "Zen", "Go"] as const
 const tokenProducts = ["Zen", "Go"] as const
@@ -42,6 +50,7 @@ const headerLinks = [
   { href: "#token-cost", label: "Token Cost" },
   { href: "#cache-ratio", label: "Cache Ratio" },
   { href: "#market-share", label: "Market Share" },
+  { href: "#geo-breakdown", label: "Geo Breakdown" },
 ] as const
 const githubLink = {
   href: "https://github.com/anomalyco/opencode",
@@ -75,11 +84,43 @@ const themePreferenceLabels = {
   system: "System",
 } as const
 const themeStorageKey = "opencode:stats-theme"
+const geoMapWidth = 960
+const geoMapHeight = 430
+const countryDisplayNames = new Intl.DisplayNames(["en"], { type: "region" })
 
 type UsageProduct = (typeof products)[number]
 type TokenProduct = (typeof tokenProducts)[number]
 type UsageRange = (typeof ranges)[number]
 type ThemePreference = (typeof themePreferences)[number]
+type IsoCountryCode = readonly [string, string, string]
+type WorldCountryProperties = GeoJsonProperties & { name?: string }
+type WorldTopology = Topology<{ countries: GeometryCollection<WorldCountryProperties> }>
+
+const countryNumericIds = new Map(
+  (JSON.parse(countryCodesSource) as IsoCountryCode[]).map((country) => [country[0], country[2]] as const),
+)
+const worldTopology = JSON.parse(countriesTopologySource) as WorldTopology
+const worldCountryGeometries: GeometryCollection<WorldCountryProperties> = {
+  ...worldTopology.objects.countries,
+  geometries: worldTopology.objects.countries.geometries.filter((country) => String(country.id ?? "") !== "010"),
+}
+const worldCountries = feature<WorldCountryProperties>(
+  worldTopology,
+  worldCountryGeometries,
+) as FeatureCollection<GeometryObject, WorldCountryProperties>
+const worldProjection = geoEquirectangular().fitExtent(
+  [
+    [10, 12],
+    [geoMapWidth - 10, geoMapHeight - 12],
+  ],
+  worldCountries,
+)
+const worldPath = geoPath(worldProjection)
+const worldCountryPaths = worldCountries.features.map((country) => ({
+  id: String(country.id ?? "").padStart(3, "0"),
+  path: worldPath(country) ?? "",
+}))
+const worldBorderPath = worldPath(mesh(worldTopology, worldCountryGeometries, (a, b) => a !== b)) ?? ""
 
 const getData = query(async () => {
   "use server"
@@ -165,6 +206,7 @@ export default function StatsHome() {
                 <TokenCostSection data={stats().tokenCost} />
                 <CacheRatioSection data={stats().cacheRatio} />
                 <MarketShareSection data={stats().market} />
+                <GeoBreakdownSection data={stats().country} />
               </>
             )}
           </Show>
@@ -1194,6 +1236,181 @@ function MarketShareList(props: {
   )
 }
 
+function GeoBreakdownSection(props: { data: StatsHomeData["country"] }) {
+  const [activeCountry, setActiveCountry] = createSignal<string>()
+  const data = createMemo(() => props.data["2M"])
+  const countryById = createMemo(
+    () =>
+      new Map(
+        data().flatMap((country) => {
+          const id = countryNumericId(country.country)
+          return id ? [[id, country] as const] : []
+        }),
+      ),
+  )
+  const maxTokens = createMemo(() => Math.max(0, ...data().map((country) => country.tokens)) || 1)
+  const topCountries = createMemo(() => data().slice(0, 15))
+  const active = createMemo(() => data().find((country) => country.country === activeCountry()) ?? data()[0])
+
+  return (
+    <section
+      id="geo-breakdown"
+      data-section="geo-breakdown"
+      onPointerLeave={(event) => {
+        if (event.pointerType === "touch") return
+        setActiveCountry(undefined)
+      }}
+    >
+      <SectionBridge label="MARKET SHARE" href="#market-share" />
+      <SectionTitle title="Geo Breakdown" description="Tokens used by country." />
+      <Show
+        when={data().length > 0}
+        fallback={<EmptyState title="No geo data" description="No geo_stat rows matched this range." />}
+      >
+        <div data-component="geo-breakdown">
+          <div data-slot="geo-map-panel">
+            <GeoWorldMap
+              countryById={countryById()}
+              activeCountry={activeCountry()}
+              maxTokens={maxTokens()}
+              onActiveCountryChange={setActiveCountry}
+            />
+            <Show when={active()}>
+              {(country) => (
+                <div data-slot="geo-active-country">
+                  <span>#{String(country().rank).padStart(2, "0")}</span>
+                  <strong>{formatCountryName(country().country)}</strong>
+                  <p>
+                    <b>{formatGeoTokens(country().tokens)}</b>
+                    <em>{formatGeoShare(country().share)}</em>
+                  </p>
+                </div>
+              )}
+            </Show>
+          </div>
+          <GeoCountryList
+            data={topCountries()}
+            activeCountry={activeCountry()}
+            maxTokens={maxTokens()}
+            onActiveCountryChange={setActiveCountry}
+          />
+        </div>
+      </Show>
+    </section>
+  )
+}
+
+function GeoWorldMap(props: {
+  countryById: Map<string, CountryEntry>
+  activeCountry: string | undefined
+  maxTokens: number
+  onActiveCountryChange: (country: string | undefined) => void
+}) {
+  const opacityScale = createMemo(() => scaleSqrt().domain([0, props.maxTokens]).range([0.26, 0.96]).clamp(true))
+  const countryOpacity = (country: CountryEntry | undefined) => {
+    if (!country) return 0
+    const opacity = opacityScale()(country.tokens)
+    if (!props.activeCountry || props.activeCountry === country.country) return opacity
+    return Math.max(0.18, opacity * 0.36)
+  }
+
+  return (
+    <svg
+      data-component="geo-world-map"
+      viewBox={`0 0 ${geoMapWidth} ${geoMapHeight}`}
+      role="img"
+      aria-label="World map of token usage by country"
+    >
+      <title>Geo Breakdown map</title>
+      <g data-slot="geo-countries">
+        <For each={worldCountryPaths}>
+          {(country) => {
+            const entry = () => props.countryById.get(country.id)
+            return (
+              <path
+                d={country.path}
+                data-has-data={entry() ? "true" : undefined}
+                data-active={entry()?.country === props.activeCountry ? "true" : undefined}
+                style={{ "--geo-country-opacity": String(countryOpacity(entry())) } as JSX.CSSProperties}
+                aria-hidden="true"
+                onPointerEnter={() => {
+                  const item = entry()
+                  if (!item) return
+                  props.onActiveCountryChange(item.country)
+                }}
+                onClick={() => {
+                  const item = entry()
+                  if (!item) return
+                  props.onActiveCountryChange(item.country)
+                }}
+              />
+            )
+          }}
+        </For>
+      </g>
+      <path data-slot="geo-borders" d={worldBorderPath} aria-hidden="true" />
+    </svg>
+  )
+}
+
+function GeoCountryList(props: {
+  data: CountryEntry[]
+  activeCountry: string | undefined
+  maxTokens: number
+  onActiveCountryChange: (country: string | undefined) => void
+}) {
+  const opacityScale = createMemo(() => scaleSqrt().domain([0, props.maxTokens]).range([0.26, 0.96]).clamp(true))
+
+  return (
+    <ol data-component="geo-country-list">
+      <For each={props.data}>
+        {(country) => (
+          <li>
+            <button
+              type="button"
+              data-active={props.activeCountry === country.country ? "true" : undefined}
+              style={{ "--geo-row-opacity": String(opacityScale()(country.tokens)) } as JSX.CSSProperties}
+              aria-label={`${formatCountryName(country.country)} ${formatGeoTokens(country.tokens)} ${formatGeoShare(
+                country.share,
+              )}`}
+              onClick={() => props.onActiveCountryChange(country.country)}
+              onPointerEnter={() => props.onActiveCountryChange(country.country)}
+              onFocus={() => props.onActiveCountryChange(country.country)}
+            >
+              <span>{String(country.rank).padStart(2, "0")}</span>
+              <i />
+              <strong>{formatCountryName(country.country)}</strong>
+              <em>{formatGeoTokens(country.tokens)}</em>
+              <b>{formatGeoShare(country.share)}</b>
+            </button>
+          </li>
+        )}
+      </For>
+    </ol>
+  )
+}
+
+function countryNumericId(country: string) {
+  return countryNumericIds.get(country.toUpperCase())?.padStart(3, "0")
+}
+
+function formatCountryName(country: string) {
+  const code = country.toUpperCase()
+  if (code === "ZZ") return "Unknown"
+  if (!countryNumericId(code)) return code
+  return countryDisplayNames.of(code) ?? code
+}
+
+function formatGeoTokens(value: number) {
+  if (value >= 1) return formatTrillions(value)
+  if (value >= 0.001) return `${Number((value * 1000).toFixed(value >= 0.01 ? 0 : 1))}B`
+  return `${Math.round(value * 1_000_000)}M`
+}
+
+function formatGeoShare(value: number) {
+  return `${value.toFixed(value > 0 && value < 1 ? 1 : 0)}%`
+}
+
 function getMarketSegmentColor(author: string, color: string, activeAuthor: string | undefined) {
   if (!activeAuthor) return color
   if (activeAuthor === author) return color
@@ -1747,6 +1964,7 @@ function Footer(props: {
     { href: "#token-cost", label: "Token Cost" },
     { href: "#cache-ratio", label: "Cache Ratio" },
     { href: "#market-share", label: "Market Share" },
+    { href: "#geo-breakdown", label: "Geo Breakdown" },
   ]
   const legal = [
     { href: "https://opencode.ai/legal/terms-of-service", label: "Terms of service" },
@@ -1762,7 +1980,7 @@ function Footer(props: {
 
   return (
     <footer data-component="footer">
-      <SectionBridge label="MARKET SHARE" href="#market-share" />
+      <SectionBridge label="GEO BREAKDOWN" href="#geo-breakdown" />
       <div data-slot="footer-grid">
         <a data-slot="footer-mark" href="https://opencode.ai" aria-label="OpenCode home">
           <OpenCodeMark />

+ 2 - 2
packages/stats/core/src/domain/home.ts

@@ -197,12 +197,12 @@ function buildMarketShare(rows: ProviderMetricRow[], range: UsageRange, window:
 
 function buildCountryStats(rows: GeoMetricRow[], window: DateWindow) {
   const countries = aggregateByCountry(rowsForProduct(rows, "All Users", window.start, window.end))
-    .filter((item) => item.tokens > 0)
+    .filter((item) => item.tokens > 0 && item.country !== "AQ")
     .toSorted((a, b) => b.tokens - a.tokens)
   const totalTokens = countries.reduce((sum, item) => sum + item.tokens, 0)
   if (totalTokens === 0) return []
 
-  return countries.slice(0, 16).map((item, index) => ({
+  return countries.map((item, index) => ({
     country: item.country,
     continent: item.continent,
     tokens: round(item.tokens / 1_000_000_000_000, 4),