---
title: Hub jumper (Ctrl+G — type two letters, jump to any hub)
---

# Hub jumper (Ctrl+G — type two letters, jump to any hub)

## What it is

The **hub jumper** is a window that opens in the middle of the screen when you press
**Ctrl+G** (⌘G on macOS). It does two jobs at once, and which one you get depends
entirely on whether you type anything.

Hubs are laid out as **a grid of icon tiles, six across** — closer to a phone's home
screen than to a menu. That is the point of a window this wide: one hub per line would
waste every pixel to the right of the longest name, and you would be scrolling again
within a couple of groups.

**Leave the box empty and it is a directory you browse.** Underneath the search field
you get your **Recent** hubs first — exactly one row of six — then the same groups your
sidebar has: GAMES & D&D, OMNISCIO, ARCHIVED, whatever you have made, each as a header
you can click to fold or unfold, with the number of hubs in it. Click any tile to go
there. Nothing is hidden from this view: a hub inside a collapsed sidebar group is still
listed here, because a directory that hides things is not a directory.

**Whatever is selected explains itself in a strip underneath its own row.** A tile is
small, so its name is trimmed to fit. Move onto a tile — with the arrow keys or just by
hovering — and a panel opens directly below that row with a caret pointing up at the
tile, showing the hub's full name, the group it belongs to, and the folder it lives in
on disk (or "Built-in hub" for one of the app's own tools like Gmail or Settings). It
opens inside the row rather than floating over the grid, so it pushes the rows below it
down instead of covering the tiles you were reading.

**Start typing and it becomes a ranked search.** The group headers drop away and the
grid refills best-first, so the hub you want is the top-left tile. That switch is
deliberate — grouping and ranking pull against each other. If you type `gm` you want
Gmail as the very first tile, not Gmail sitting three headers down inside whichever
group happens to own it. The detail strip still names the group each tile came from, so
you never lose the context.

It exists because the left sidebar stops working as a way to *get somewhere* once you
have a lot of hubs. At twenty it is a list you skim; at a hundred and forty it is a
list you scroll, and finding the hub you want becomes a search-with-your-eyes task
rather than a navigation one.

Two more things make it feel less like a plain filter box:

- **It ranks by what you actually use, not alphabetically.** A hub you opened this
  morning and open most days will sit above a hub with an equally good name match that
  you have never opened.
- **Opening it with an empty box shows where you have just been.** Recent is ordered
  newest-first and deliberately leaves out the hub you are currently in — you cannot
  jump to where you already are. So **Ctrl+G then Enter** takes you straight back to
  the hub you were in a moment ago, which is the most common jump there is.

## Where to find it

### How to use it

1. **Press Ctrl+G** from anywhere in the app. The window opens with the cursor already
   in the search box.
2. **Press Enter immediately** to go back to the hub you were in before this one.
3. **Or just look.** Scroll the groups, click a header to fold or unfold it, click a
   tile to go there. Folds are remembered — a group you keep shut stays shut tomorrow.
   A group of more than four rows' worth of hubs starts folded so one big group cannot
   bury the rest.
4. **Or type two or three letters** of a hub's name. Letters do not have to be adjacent
   or at the start — `smz` finds "Smrzz", `taskm` finds "Task Management". Matched
   letters are bolded so you can see why a tile matched. Delete what you typed and the
   grouped view comes straight back.
5. **Move with the arrow keys**, then Enter. Left and right step hub-to-hub through the
   whole grid, crossing between rows and groups as they go; up and down move a row at a
   time and stay in the same column. Arrows skip over the group headers and never land
   on a hub inside a folded group, and they stop at the ends rather than wrapping
   around. Left and right only steer the grid while the box is empty — once you have
   typed something they move the text cursor, so you can still edit what you typed.
6. **Press Escape** (or click outside, or the X) to close without going anywhere.

If nothing matches what you typed, the list says "No hubs match." rather than going
blank.

## How it behaves

### What it deliberately does not do

- **It does not change the Ctrl+1 … Ctrl+0 shortcuts.** Those already exist and jump to
  the Inbox plus the first nine hubs in sidebar order. They are unchanged by this
  feature, and nothing in the interface advertises them — that was a deliberate scoping
  decision, not an oversight.
- **It is desktop only.** There is no keyboard shortcut on a phone, and the mobile hub
  list has its own search box.
- **It does not offer the Prompt Tools action rows.** Bake-Off, Super Prompts, Super
  Prompt Creator and AI Council Launch look like hub rows in the sidebar but they open a
  dialog rather than navigate somewhere, so a jump list is the wrong place for them.

## For agents

### How it works

The palette is
[/src/renderer/src/features/hub-jumper/HubJumper.tsx](/src/renderer/src/features/hub-jumper/HubJumper.tsx),
a thin wrapper over the shared `CommandPaletteShell` component that every other palette
in the app uses (it supplies the portal, the dimmed backdrop, the focus trap, and
Escape-to-close). Whether it is open is held in a three-line store,
`hub-jumper-store.ts`, which exists so the keyboard handler — which is not a React
component — has something to flip.

The shortcut is declared as the `openHubJumper` action in
[/src/shared/keybindings/core.keybindings.ts](/src/shared/keybindings/core.keybindings.ts)
with a default of `Ctrl+G`, so it is rebindable in Settings → Keyboard like every other
shortcut. `Ctrl+K` was not available: it is already the app-wide conversation search,
which answers a different question ("find a message") than the jumper does ("go to a
hub").

The matching engine is [/src/shared/fuzzy-rank.ts](/src/shared/fuzzy-rank.ts), the same
subsequence matcher the Team Chat channel switcher uses — it was moved to a shared
location for this feature rather than copied. It scores a match higher when the letters
land at the start of the name, at a word boundary, or next to each other, and lower when
they are scattered far apart.

The browse view is built by `buildBrowseSections` in the same module: it assembles
Recent (capped, and with the currently-active hub filtered out), then the ungrouped
hubs, then one section per sidebar divider in the divider's own display order. A
companion `visibleRows` flattens only the EXPANDED sections into the sequence the
arrow keys walk, which is why arrowing never stops on a header or on a hub inside a
folded group. Both are pure functions, so the whole layout is unit-tested without
mounting React. Which groups you have folded lives in the same small palette store as
the open flag and is persisted; the open flag deliberately is not.

Ranking then adds a familiarity nudge in
[hub-jumper-rank.ts](/src/renderer/src/features/hub-jumper/hub-jumper-rank.ts): a recency
term that halves roughly every 24 hours since your last visit, plus a frequency term that
grows with visit count but flattens out (and is capped) so a hub with hundreds of old
visits cannot outrank one you opened minutes ago. Both are deliberately small relative to
the text score, so a clearly better name match always wins — the letters you typed are
what you meant.

The visit history behind that ranking lives in
[hub-visit-store.ts](/src/renderer/src/stores/hub-visit-store.ts), saved on your own
machine (not the database, and not synced between devices) as a small map of hub id →
last-visited time and visit count, capped at 300 entries. It is recorded in exactly one
place — inside `setActiveProject`, the single function every hub switch in the app passes
through, whether you clicked the sidebar, pressed Ctrl+3, tabbed across, or used the
jumper. The one switch it ignores is the app restoring your last hub at startup: counting
that would let whatever was open when you quit climb the ranking on every launch.

Selecting a hub goes through `keyboardSwitchToProject`, the same guarded switch the
keyboard navigation uses, so Hardcore Mode's lock still stops you leaving a locked
session — the palette is not a back door around it.

Contract: [hub-jumper-contract.md](../../.claude/memory/contracts/hub-jumper-contract.md).

## Related

- [keyboard-shortcuts.md](keyboard-shortcuts.md) — the full keyboard map and how to rebind a shortcut
