Skip to content

About

macOS menu bar readout for your tokens.ci AI coding token usage — today's tokens + leaderboard rank at a glance

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

50 Commits

Folders and files

Repository files navigation

English 简体中文

TokensBar icon

TokensBar

Your tokens.ci token usage and leaderboard rank, in the macOS menu bar — no browser tab needed.

Build Release License macOS 13+ Universal

Plain Swift, compiled with swiftc. No Xcode project, no dependencies beyond system frameworks.

macOS 27 · Apple silicon: Rosetta 2 is gone, so the tokens CLI must be a native arm64 build — brew install owo-network/brew/tokens on an Apple silicon Homebrew. A leftover x86_64 tokens from an Intel Homebrew won't launch; since 1.9.1 TokensBar skips it and shows the reason instead of a bare "submit failed".

The TokensBar dropdown panel in light mode The TokensBar dropdown panel in dark mode

menu bar:  ⚡ 12.4M  #87       ← all-time rank
     or:   ⚡ 12.4M  D#31      ← today's rank

The token figure is today's total for the whole account, the same number the site shows, so it lines up with the rank beside it. Hover the icon for this machine's live figure as well.

English and 简体中文 are both built in — switch from the panel's actions page or the Language rows in the right-click menu (English is the default), or set language in the config file.

The dropdown

Left-clicking the icon draws the panel above. Today's total leads, then the board the menu bar is showing with the gap to the person one place up as a bar, today against the average of the seven days before it, the other board and this week as cards, the last seven days as bars, and today's model split in the same colours the donut uses. Hovering a bar reads that day out next to the heading.

Clicking a section opens the matching view: the headline or the model list opens the donut, the bars open the contributions grid, the card opens the other board's breakdown. Clickable blocks carry a small chevron, and the line above the footer names what the block under the pointer will open — with the pointer over nothing it says how the panel works instead. Submit runs tokens submit and reports the result in the footer, beside the last local and server refresh times.

Right-clicking the icon — or the ⋯ button in the panel — reaches the same set of actions. The ⋯ button turns the panel to a second page rather than dropping a menu over it: submit, refresh, the rank and language switches as pills, a launch-at-login switch, the profile link, Copy Stats Card (a shareable image of your standing, straight to the clipboard), the update check, the tip page and Quit, with ‹ Actions & Settings back at the top and the build version at the bottom. The two are cleanly split now — left-click the icon for the panel, right-click it for the plain NSMenu, and nothing inside the panel reaches back into that menu. The menu keeps the same verbs and their keyboard shortcuts, one level deep: the numbers sit at the top of it, so the per-model breakdowns they open are a single click from the menu bar, and the rank and language choices are indented rows under a heading rather than submenus.

The actions page inside the panel, light mode The actions page inside the panel, dark mode

Model distribution

Clicking the headline, or a model row — or the Lifetime / Today row in the menu — opens a popover under the menu bar icon with a donut chart of the per-model split, and a Tokens / Cost toggle:

  • Lifetime comes from the API's modelUsage
  • Today comes from the day's entry in the API's contributions, so it covers every device on the account — the same figure the Today row shows. Before the day's first submission it falls back to the local scan.

The top five models get their own slice and the rest fold into "Others" — a donut stops being readable much past six segments. With one or two models it draws a single 100% bar instead. Light and dark mode each get their own colour steps, chosen for contrast against that surface and checked for colour-blind separation.

The chart draws itself in when the popover opens and when the metric changes. Hovering a segment — or its legend row — highlights both, dims the rest, and swaps the centre readout to that model's own figure, so the value leads and the name follows.

Today's model distribution by tokens Lifetime model distribution by cost

Contributions

The seven-day bars in the panel — or the Active days row in the menu — open a year of daily token activity as a heat grid, the same view the site carries. Hovering a day reads out its tokens, cost, message count and per-client split; the pointer-free state shows the active-day count and the window covered.

A year of daily token activity as a heat grid

The toggle in the corner switches what the readout breaks the day down by — Models, Clients or Cost. Cost also recolours the grid: token intensity comes from the API, while the cost view derives its own levels from the quartiles of the daily spend, so a cheap-but-busy day and an expensive-but-quiet one look different.

The ramp is one hue, light to dark, with separate steps for light and dark mode; days with any usage never fall back to the empty-cell colour.

The two ranks

  • All-time — ranked by lifetime tokens. This is the board on the tokens.ci front page.
  • Today — ranked by tokens submitted today only. Only people who submitted today are on it, so it is a smaller board and the rank moves more.

#87 / 265 means rank 87 out of the 265 people on that board. In the menu bar a daily rank is prefixed (D#31, 今#31 in Chinese) to tell it apart from a lifetime one.

Pick which one the menu bar shows from the "Rank Shown in Menu Bar" row on the panel's actions page, or the rows under the same heading in the right-click menu (the choice is remembered), or set menuBarRank in the config file for the default. Both ranks are always shown; the setting only changes the menu bar line and which board the gap bar refers to.

When the board being shown moves between refreshes, a short banner reacts to it — a climb celebrates ("Passed @rival — now D#31", or a crown at #1) with a brief chip on the menu-bar readout (🚀+2 / 🎉 / 👑), a slip nudges. A background move never throws the panel open; the chip carries it at a glance.

Requirements

The tokens CLI, installed and logged in:

brew install owo-network/brew/tokens
tokens login

Install

Download TokensBar.app.zip from Releases, unzip, move it to /Applications, then clear the quarantine flag:

xattr -dr com.apple.quarantine /Applications/TokensBar.app
open /Applications/TokensBar.app

The build is ad-hoc signed and not notarised, so macOS will refuse to open it until the quarantine attribute is gone. Alternatively, right-click → Open, or allow it in System Settings → Privacy & Security. The release binary is universal (arm64 + x86_64) and needs macOS 13 or newer.

Or build it yourself — nothing but the Swift toolchain that ships with Xcode is required:

git clone https://github.com/lucaisgrowing/tokens-menubar.git
cd tokens-menubar
./build.sh              # this Mac's architecture
./build.sh --universal  # arm64 + x86_64
open TokensBar.app

The app is an LSUIElement: menu bar only, no Dock icon and no windows.

The icon lives at Resources/AppIcon.icns and is committed. It is drawn in code — edit Tools/make-icon.swift and run swift Tools/make-icon.swift to regenerate it.

Configuration

Optional, ~/.config/tokens-menubar/config.json:

{
  "username": "your-github-username",
  "language": "en",
  "menuBarRank": "all",
  "menuBarItems": ["tokens", "rank"],
  "dailyCostBudget": 0,
  "apiRefreshSeconds": 300,
  "localRefreshSeconds": 120,
  "topModels": 5
}
Key Default Meaning
username read from ~/.config/tokens/credentials.json tokens.ci username
apiBase https://tokens.ci API base URL
language en UI language: en or zh
menuBarRank all Which rank the menu bar shows: all (lifetime) or today
menuBarItems ["tokens", "rank"] What the menu-bar line shows, in order — any of tokens, cost, rank
dailyCostBudget 0 Colour the readout amber past this daily spend (USD), red at 1.5×; 0 is off
apiRefreshSeconds 300 How often to hit the API (minimum 30)
localRefreshSeconds 120 How often to run the local scan (minimum 30)
topModels 5 Models listed in the dropdown, 0 to list none

Switching the rank or the language from the menu stores the choice in UserDefaults, which then wins over the config file. To hand control back: defaults delete ci.tokens.menubar menuBarRank (or language).

Updates

"Check for Updates…" compares the bundle version against the latest GitHub release. A daily background check runs too; when a newer release exists the menu item turns into an install link.

Clicking it installs in place: it downloads that release's TokensBar.app.zip, checks the bundle inside really is the advertised version, swaps it for the running app and relaunches — no browser, no unzip, no drag into /Applications, and no quarantine flag to clear. It asks first, and nothing is ever installed without that confirmation. If the app sits somewhere this account cannot write, or the release has no zip attached, the release page opens instead, exactly as it did before.

The trust here is HTTPS to GitHub. The download is not code-signature-verified, because the builds are ad-hoc signed rather than notarised — a SHA-256 published alongside the same download would prove nothing extra.

Command line

# Print the menu bar line, the whole dropdown and both chart breakdowns to
# stdout — useful for checking the data path without the GUI.
./TokensBar.app/Contents/MacOS/TokensBar --dump [--lang en|zh]

# Render the chart popover offscreen to a PNG, to preview the layout.
# `dark` renders dark mode, `hover N` the highlight for slice N, `reveal F`
# freezes the draw-in animation at progress F.
./TokensBar.app/Contents/MacOS/TokensBar --chart-png out.png \
    [today|lifetime] [tokens|cost] [dark] [hover N] [reveal F]

# Render the contributions grid offscreen.
./TokensBar.app/Contents/MacOS/TokensBar --contrib-png out.png \
    [models|clients|cost] [dark] [hover YYYY-MM-DD]

# Render the shareable stats card offscreen (the "Copy Stats Card" menu item).
./TokensBar.app/Contents/MacOS/TokensBar --share-png out.png [light]

# Render the dropdown panel offscreen, and print its clickable regions — a still
# image cannot show where a click lands. `dark` renders dark mode, `menu` the
# actions page, `hover N` the readout for day N of the seven (0 = oldest), `hit N`
# the hover state of clickable region N, which is what the hint line reacts to.
./TokensBar.app/Contents/MacOS/TokensBar --panel-png out.png [dark] [menu] [hover N] [hit N] [notice success|failure|info TEXT]

# Open the panel for real against a throwaway anchor and print its live geometry,
# optionally capturing the popover window. This is what catches the panel sitting
# off-centre: the popover insets its content view, so the sizes it reports and the
# sizes asked for are not the same thing. `menu` turns to the actions page while
# it is open, which is what resizes a shown popover.
./TokensBar.app/Contents/MacOS/TokensBar --panel-probe [out.png] [menu]

# Print the menu as a tree, with indented rows shown at their indent level.
./TokensBar.app/Contents/MacOS/TokensBar --menu-dump

# Compare this build against the latest GitHub release.
./TokensBar.app/Contents/MacOS/TokensBar --check-updates

# Download, verify and install the latest release over this bundle, then relaunch.
# Same path the update menu item takes, minus the click and the confirmation, so
# point a throwaway copy at it rather than the app you use.
./TokensBar.app/Contents/MacOS/TokensBar --self-update

# Run the same submit the menu item runs, to reproduce a failure in a terminal.
# Add --dry-run to send nothing.
./TokensBar.app/Contents/MacOS/TokensBar --submit [--dry-run]

# Launch at login (installs/removes ~/Library/LaunchAgents/ci.tokens.menubar.plist)
./TokensBar.app/Contents/MacOS/TokensBar --set-login on
./TokensBar.app/Contents/MacOS/TokensBar --set-login off

The "Launch at Login" checkbox in the menu does the same thing as --set-login.

How it works

Two data sources:

  • The API — the public read-only endpoints GET /api/users/<name> and GET /api/leaderboard?period=all|today. Both ranks, the lifetime totals, the model split, the contributions grid, and today's totals. No credentials are sent.
  • The local CLI — tokens --today --json and tokens --week --json. This week, and today on this machine.

Two things worth knowing:

  • Percentages in the dropdown are a share of tokens, recomputed locally. The API's own percentages are a share of cost, so the two can differ.
  • The Today row is the server's figure, so it agrees with the daily rank and with the site: every device on the account, as of the last submission. The dim line under it is this machine's live scan. The two differ in both directions — another machine's usage is in the first but not the second, and anything done since the last submission is in the second but not the first. The token gap can be far larger than the cost gap, because a cache-heavy free model contributes millions of tokens for almost nothing.

If the API cannot be reached, the dropdown reports that the server read failed and keeps the last figures it had.

Support

TokensBar is free and stays free. If it saved you a browser tab, USDT tips are welcome on either chain:

Chain Address
TRON (TRC20) TNKSjygE7JaMSJBPZcYPpWxVPUKrFr8DDU
Ethereum · BNB Chain · Base (ERC20 / BEP20) 0x027E4828B67f4c8cAc006A637Cab4f0164F501B8
TRON TRC20 address QR code
TRON (TRC20)
EVM address QR code
ERC20 / BEP20 / Base

Check the chain before sending — the two addresses are not interchangeable.

Credits

TokensBar is only a menu bar front end. All the actual usage accounting is done by:

No code from either project is copied here; TokensBar only reads their CLI output and public read-only API. Not affiliated with tokens.ci.

License

MIT, matching both upstream projects.

About

macOS menu bar readout for your tokens.ci AI coding token usage — today's tokens + leaderboard rank at a glance

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages