From fba22f50f86ac84ea52165827e6123a2a2b7aa06 Mon Sep 17 00:00:00 2001 From: azank Date: Tue, 13 Oct 2026 03:28:50 +0500 Subject: [PATCH 1/2] site: cities dashboard and the live feed straight after the hero The home page now reads: hero, every city built so far, GitHub live, then how to read a city, sharing, self-hosting and the FAQ. - The dashboard is /explore's: filter, people/orgs/repos, sort (featured, latest, commits, agent- and bot-written, lines, repos) and a count. It shows two full rows, then "Show all N cities". - The live sample is one wide card: the human/bot/agent split and a longer feed side by side, with the agent roster underneath. The agent lens card's per-city ranking duplicated the dashboard's sort and is gone. - The hero's 3D city loads on "Play in 3D" instead of on idle: the viewer, a city's data and WebGL were the heaviest things on the page, and most visitors never touched them. The URL swap band, which repeated the hero's first line on a 1.7 s timer, is gone too. --- web/public/index.html | 260 +++++++++++++++++++++--------------------- web/site/index.html | 260 +++++++++++++++++++++--------------------- 2 files changed, 254 insertions(+), 266 deletions(-) diff --git a/web/public/index.html b/web/public/index.html index 350e362..1f9eb7d 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -44,10 +44,14 @@ .stage .poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; transition: opacity 0.9s; } .stage iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; opacity: 0; transition: opacity 1.1s; } .stage iframe.on { opacity: 1; } -.stage .shield { position: absolute; inset: 0; z-index: 2; display: grid; place-items: end center; padding-bottom: 18px; cursor: grab; } -.stage .shield span { padding: 7px 13px; border-radius: 99px; background: rgba(4, 6, 12, 0.72); border: 1px solid var(--line); color: var(--ink-2); font-size: 12.5px; - backdrop-filter: blur(8px); opacity: 0; transform: translateY(6px); transition: 0.3s; } -.stage:hover .shield span { opacity: 1; transform: none; } +.stage .shield { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; width: 100%; cursor: pointer; } +.stage .shield span { display: inline-flex; align-items: center; gap: 10px; padding: 7px 16px 7px 7px; border-radius: 99px; background: rgba(4, 6, 12, 0.74); + border: 1px solid rgba(95, 211, 243, 0.35); color: var(--ink); font-size: 14px; font-weight: 600; backdrop-filter: blur(8px); transition: 0.2s; } +.stage .shield i { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: var(--accent); color: var(--accent-ink); font-style: normal; font-size: 12px; } +.stage .shield:hover span, .stage .shield:focus-visible span { border-color: var(--accent); transform: scale(1.04); } +.stage .shield.busy { cursor: progress; place-items: end center; padding-bottom: 18px; } +.stage .shield.busy span { padding: 7px 13px; font-size: 12.5px; font-weight: 500; color: var(--ink-2); border-color: var(--line); transform: none; } +.stage .shield.busy i { display: none; } .stage .cap { position: absolute; left: 14px; top: 14px; z-index: 3; display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px 6px 9px; border-radius: 99px; background: rgba(4, 6, 12, 0.72); border: 1px solid var(--line); font-size: 12.5px; color: var(--ink-2); backdrop-filter: blur(8px); font-family: var(--mono); } .stage .cap:hover { color: var(--ink); text-decoration: none; border-color: rgba(95, 211, 243, 0.4); } @@ -63,24 +67,7 @@ .tabs button[aria-selected="true"] { color: var(--ink); border-color: rgba(95, 211, 243, 0.5); background: var(--accent-soft); } @media (max-width: 980px) { .hero .wrap { grid-template-columns: minmax(0, 1fr); } .hero .lede { max-width: none; } } -/* the URL swap */ -.swapband { padding: 26px 0; border-block: 1px solid var(--line); background: linear-gradient(90deg, transparent, rgba(95, 211, 243, 0.035), transparent); } -.swapband .wrap { display: flex; align-items: center; justify-content: center; gap: 18px; flex-wrap: wrap; } -.urlbar { display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 18px; border-radius: 12px; border: 1px solid var(--line-2); background: var(--bg-1); - font: 500 clamp(14px, 1.8vw, 18px) var(--mono); color: var(--ink); min-width: min(440px, 100%); } -.urlbar svg { width: 14px; height: 14px; fill: var(--muted); flex: none; } -.urlbar .url { display: inline-flex; align-items: center; white-space: nowrap; } -.urlbar .dom { position: relative; display: inline-grid; } -.urlbar .dom span { justify-self: end; } -.urlbar .dom span { grid-area: 1 / 1; transition: opacity 0.35s, transform 0.35s var(--ease), color 0.35s; } -.urlbar .dom .a { color: var(--muted); } -.urlbar .dom .b { color: var(--accent); opacity: 0; transform: translateY(60%); } -.urlbar.swapped .dom .a { opacity: 0; transform: translateY(-60%); } -.urlbar.swapped .dom .b { opacity: 1; transform: none; } -.urlbar .path { color: var(--ink); } -.urlbar .caret { width: 2px; height: 1.1em; background: var(--accent); animation: blink 1s steps(1) infinite; margin-left: 3px; } @keyframes blink { 50% { opacity: 0; } } -.swapband p { margin: 0; color: var(--muted); font-size: 14px; } /* read the city */ .read { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: clamp(28px, 4vw, 56px); align-items: center; } @@ -102,41 +89,44 @@ .langs i { width: 9px; height: 9px; border-radius: 2px; } @media (max-width: 900px) { .read { grid-template-columns: minmax(0, 1fr); } .read > :first-child { position: static; } } -/* agents */ -.agents { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; } -.agents .card { padding: 28px; display: flex; flex-direction: column; } -.agents .card .h3 { margin: 14px 0 6px; } -.livebig { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 22px 0 14px; } +/* cities dashboard */ +.dash-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 20px; flex-wrap: wrap; margin-bottom: 22px; } +.dash-head > div { max-width: 640px; } +.dash-head .h2 { margin: 14px 0 10px; } +.dash-head p { margin: 0; color: var(--ink-2); font-size: 16px; } +.dash-head .btn.ghost { margin-inline: -12px; } +.dbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 18px; } +.dbar .field { flex: 1 1 220px; max-width: 360px; height: 40px; } +.dbar .field svg { width: 16px; height: 16px; stroke: var(--muted); fill: none; stroke-width: 2; flex: none; } +.dbar .count { margin-left: auto; color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; } +.more { display: flex; justify-content: center; margin-top: 22px; } +@media (max-width: 640px) { .dbar .count { display: none; } .dbar .seg { order: 3; } } + +/* live */ +.livecard { display: grid; grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); gap: clamp(24px, 4vw, 48px); padding: clamp(22px, 3vw, 34px); } +.livecard .h2 { margin: 14px 0 12px; } +.livecard p { margin: 0; color: var(--ink-2); font-size: 15px; } +.livebig { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 24px 0 14px; } .livebig div { display: flex; flex-direction: column; gap: 2px; } .livebig b { font-size: clamp(28px, 3.4vw, 40px); font-weight: 760; letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; } .livebig label { color: var(--muted); font-size: 12.5px; display: inline-flex; align-items: center; gap: 6px; } .livebig label i { width: 8px; height: 8px; border-radius: 2px; } -.feed { list-style: none; margin: 18px 0 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; min-height: 196px; } +#lv-note { display: block; margin-top: 8px; font-size: 12px; } +.feed { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: 2px; min-height: 456px; } .feed li { display: grid; grid-template-columns: 24px 1fr auto; gap: 10px; align-items: center; padding: 6px 4px; border-radius: 8px; font-size: 13px; color: var(--ink-2); animation: feedin 0.5s var(--ease); } .feed li img { width: 24px; height: 24px; border-radius: 50%; background: var(--bg-3); } .feed li .tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .feed li .tx b { color: var(--ink); font-weight: 600; } .feed li .tg { font-size: 11px; padding: 2px 8px; border-radius: 99px; border: 1px solid; white-space: nowrap; } @keyframes feedin { from { opacity: 0; transform: translateY(-6px); } } -.agentlist { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; margin: 22px 0 6px; } -.agentlist a { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 14px; color: var(--ink-2); font-size: 13.5px; } -.agentlist a:hover { text-decoration: none; color: var(--ink); } -.agentlist .split { grid-column: 1 / -1; height: 7px; } -.agentlist .nm { font-family: var(--mono); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); } -.agentlist .pc { font-variant-numeric: tabular-nums; color: var(--agent); } -.roster { margin-top: 22px; } +.livecard .roster { grid-column: 1 / -1; padding-top: 20px; border-top: 1px solid var(--line); } .roster small { display: block; color: var(--muted); font-size: 12px; margin-bottom: 10px; } .roster div { display: flex; flex-wrap: wrap; gap: 6px; } .ag { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: 99px; font-size: 12px; color: var(--ink-2); border: 1px solid color-mix(in srgb, var(--c) 32%, transparent); background: color-mix(in srgb, var(--c) 7%, transparent); } .ag i { width: 7px; height: 7px; border-radius: 50%; background: var(--c); box-shadow: 0 0 6px var(--c); } -.agents .foot2 { margin-top: auto; padding-top: 18px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; } -.beams { display: flex; gap: 16px; margin-top: 18px; } -.beams div { flex: 1; text-align: center; padding: 14px 8px; border-radius: 12px; border: 1px solid var(--line); background: var(--bg-1); } -.beams svg { width: 100%; height: 54px; } -.beams b { display: block; font-size: 13px; margin-top: 6px; } -.beams small { color: var(--muted); font-size: 11.5px; } -@media (max-width: 900px) { .agents { grid-template-columns: minmax(0, 1fr); } } +.livecard .foot2 { margin-top: 22px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; } +@media (max-width: 900px) { .livecard { grid-template-columns: minmax(0, 1fr); } .feed { min-height: 0; } } /* share */ .share .card { display: flex; flex-direction: column; gap: 14px; overflow: hidden; } @@ -199,7 +189,7 @@
- newThe agent lens: see what AI wrote, commit by commit → + newThe agent lens: see what AI wrote, commit by commit →

Your GitHub,
as a living city.

Swap github.com for commit.city on any profile, org or repo. A year of git history replays as a 3D city: every file a tower, every commit a beam of light, every bot and AI agent accounted for.

@@ -217,91 +207,83 @@

Your GitHub,
as a living city. commit.city/sindresorhus -
Drag to orbit · click to explore
+

-
+
-
- - github.comcommit.city/torvalds +
+
Explore

Every city, so far.

+

People, orgs and repos that visitors have built. Each one is cached, so it opens instantly.

+ Full page →
-

Same URL, one word different. Works for people, orgs and repos, and ?all=1 replays a repo from its first commit.

-
-
- -
-
-
- -
-
-
- Reading a city -

Real history, drawn to scale.

-

commit.city doesn't estimate from contribution counts. It replays git itself, so a tower is exactly as tall as that file was on that day.

+
+ +
+
-
    -
    + +
    +
    +

    -
    +
    -
    - Humans, bots and agents -

    Who's actually writing the code?

    -

    More and more commits come from AI agents and automation. commit.city reads the signatures they leave (bot accounts, agent branches, - Co-authored-by trailers) and colours every beam by who sent it.

    -
    -
    -
    +
    +
    GitHub, right now -

    The public timeline, live.

    -

    The latest public events on all of GitHub, sorted by who sent them.

    +

    Who's writing the code, live.

    +

    The latest public events on all of GitHub, sorted by who sent them. commit.city reads the signatures bots and AI agents leave (bot accounts, + agent branches, Co-authored-by trailers). Press A in any city for the same split.

    –
    –
    –
    -
      - -
      -
      - The agent lens -

      Every city, split by author.

      -

      Press A in any city and its beams recolour: people, automation and AI. The panel breaks it down by agent and by repo, at every moment of the year.

      - - -
      Recognises 17 agents today, from rules anyone can read and extend
      ClaudeCopilotCursorCodexDevinJulesGeminiAiderAmpOpenHandsReplit AgentLovablev0JunieSweepFactoryCodeRabbit
      - + +
      +
        +
        Recognises 17 agents, from rules anyone can read and extend
        ClaudeCopilotCursorCodexDevinJulesGeminiAiderAmpOpenHandsReplit AgentLovablev0JunieSweepFactoryCodeRabbit

        -
        -
        -
        -
        Explore

        Cities people have built.

        -

        Every city is public data, cached once built, so opening one someone else made is instant.

        - Browse all cities → +
        +
        +
        + +
        +
        +
        + Reading a city +

        Real history, drawn to scale.

        +

        commit.city doesn't estimate from contribution counts. It replays git itself, so a tower is exactly as tall as that file was on that day.

        +
        +
          +
          -
          @@ -412,7 +394,7 @@

          What does your year look like?