Hub jumper (Ctrl+G — type two letters, jump to any hub)
A window that opens in the middle of the screen on Ctrl+G and does two jobs: it shows every hub as a grid of icon tiles to click, and it jumps you straight there the moment you type enough letters to identify one.
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
- Press Ctrl+G from anywhere in the app. The window opens with the cursor already in the search box.
- Press Enter immediately to go back to the hub you were in before this one.
- 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.
- Or type two or three letters of a hub's name. Letters do not have to be adjacent
or at the start —
smzfinds "Smrzz",taskmfinds "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. - 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.
- 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,
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
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, 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: 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, 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.
Related
- keyboard-shortcuts.md — the full keyboard map and how to rebind a shortcut
Last verified 2026-09-28