authorgravatar for jakeleventhal@me.comJake Leventhal <jakeleventhal@me.com> 2026-09-24 21:52:03-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-24 18:52:03-07:00
log3e2370fbbf1efacb341a68e6f28efb06b695e148
tree62880092d34d532e01138b247a1f3ac4fe21ca2c
parent91e53e501c9e7eb03f0ea4a5b72221d08eff9c5c
signature Signed by PGP key B5690EEEBB952194

feat(web): add usage page keybinding (#9434)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Julius Marminge <julius0216@outlook.com>

6 files changed, 24 insertions(+), 0 deletions(-)

apps/web/src/components/CommandPalette.tsx+11
...@@ -469,6 +469,7 @@ function overlayModeForCommand(command: string | null): SearchOverlayMode | null...@@ -469,6 +469,7 @@ function overlayModeForCommand(command: string | null): SearchOverlayMode | null
469}469}
470470
471export function CommandPalette({ children }: { children: ReactNode }) {471export function CommandPalette({ children }: { children: ReactNode }) {
472 const navigate = useNavigate();
472 const [state, dispatch] = useReducer(reduceCommandPaletteUiState, {473 const [state, dispatch] = useReducer(reduceCommandPaletteUiState, {
473 open: false,474 open: false,
474 mode: "command",475 mode: "command",
...@@ -561,6 +562,13 @@ export function CommandPalette({ children }: { children: ReactNode }) {...@@ -561,6 +562,13 @@ export function CommandPalette({ children }: { children: ReactNode }) {
561 });562 });
562 return;563 return;
563 }564 }
565 if (command === "usage.open") {
566 event.preventDefault();
567 event.stopPropagation();
568 setOpen(false);
569 void navigate({ to: "/usage" });
570 return;
571 }
564 const mode = overlayModeForCommand(command);572 const mode = overlayModeForCommand(command);
565 if (mode === null) {573 if (mode === null) {
566 return;574 return;
...@@ -574,9 +582,11 @@ export function CommandPalette({ children }: { children: ReactNode }) {...@@ -574,9 +582,11 @@ export function CommandPalette({ children }: { children: ReactNode }) {
574 }, [582 }, [
575 appearanceMode,583 appearanceMode,
576 keybindings,584 keybindings,
585 navigate,
577 previewOpen,586 previewOpen,
578 resolvedTheme,587 resolvedTheme,
579 setAppearanceMode,588 setAppearanceMode,
589 setOpen,
580 terminalOpen,590 terminalOpen,
581 theme,591 theme,
582 themeHalves,592 themeHalves,
...@@ -2023,6 +2033,7 @@ function OpenCommandPaletteDialog(props: {...@@ -2023,6 +2033,7 @@ function OpenCommandPaletteDialog(props: {
2023 searchTerms: ["usage", "use", "tokens", "cost", "spend", "limits", "stats", "analytics"],2033 searchTerms: ["usage", "use", "tokens", "cost", "spend", "limits", "stats", "analytics"],
2024 title: "Open usage",2034 title: "Open usage",
2025 icon: <ChartNoAxesColumnIcon className={ITEM_ICON_CLASS} />,2035 icon: <ChartNoAxesColumnIcon className={ITEM_ICON_CLASS} />,
2036 shortcutCommand: "usage.open",
2026 run: async () => {2037 run: async () => {
2027 await navigate({ to: "/usage" });2038 await navigate({ to: "/usage" });
2028 },2039 },
apps/web/src/components/settings/KeybindingsSettings.logic.test.ts+1
...@@ -243,6 +243,7 @@ describe("KeybindingsSettings.logic", () => {...@@ -243,6 +243,7 @@ describe("KeybindingsSettings.logic", () => {
243 "chat.new",243 "chat.new",
244 "rightPanel.toggleMaximized",244 "rightPanel.toggleMaximized",
245 "thread.stop",245 "thread.stop",
246 "usage.open",
246 "script.setup-db.run",247 "script.setup-db.run",
247 ]),248 ]),
248 );249 );
docs/user/usage.md+4
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1# Usage and limits1# Usage and limits
22
3Open **Usage** from the sidebar or the command palette, or press `mod+u` on web and
4desktop when the terminal is not focused. Customize `usage.open` in
5**Settings → Keybindings**.
6
3## Understand your usage7## Understand your usage
48
5**Usage** combines Codex, Claude Code, and Grok Build session history from your connected9**Usage** combines Codex, Claude Code, and Grok Build session history from your connected
packages/contracts/src/keybindings.test.ts+6
...@@ -78,6 +78,12 @@ it.effect("parses keybinding rules", () =>...@@ -78,6 +78,12 @@ it.effect("parses keybinding rules", () =>
78 });78 });
79 assert.strictEqual(parsedProjectSearch.command, "projectSearch.toggle");79 assert.strictEqual(parsedProjectSearch.command, "projectSearch.toggle");
8080
81 const parsedUsageOpen = yield* decode(KeybindingRule, {
82 key: "mod+u",
83 command: "usage.open",
84 });
85 assert.strictEqual(parsedUsageOpen.command, "usage.open");
86
81 const parsedThemeEditor = yield* decode(KeybindingRule, {87 const parsedThemeEditor = yield* decode(KeybindingRule, {
82 key: "mod+alt+shift+t",88 key: "mod+alt+shift+t",
83 command: "themeEditor.toggle",89 command: "themeEditor.toggle",
packages/contracts/src/keybindings.ts+1
...@@ -78,6 +78,7 @@ export const STATIC_KEYBINDING_COMMANDS = [...@@ -78,6 +78,7 @@ export const STATIC_KEYBINDING_COMMANDS = [
78 "commandPalette.toggle",78 "commandPalette.toggle",
79 "filePicker.toggle",79 "filePicker.toggle",
80 "projectSearch.toggle",80 "projectSearch.toggle",
81 "usage.open",
81 "theme.select",82 "theme.select",
82 "appearance.cycle",83 "appearance.cycle",
83 "themeEditor.toggle",84 "themeEditor.toggle",
packages/shared/src/keybindings.ts+1
...@@ -40,6 +40,7 @@ export const DEFAULT_KEYBINDINGS: ReadonlyArray<KeybindingRule> = [...@@ -40,6 +40,7 @@ export const DEFAULT_KEYBINDINGS: ReadonlyArray<KeybindingRule> = [
40 { key: "mod+k", command: "commandPalette.toggle", when: "!terminalFocus" },40 { key: "mod+k", command: "commandPalette.toggle", when: "!terminalFocus" },
41 { key: "mod+p", command: "filePicker.toggle", when: "!terminalFocus" },41 { key: "mod+p", command: "filePicker.toggle", when: "!terminalFocus" },
42 { key: "mod+shift+f", command: "projectSearch.toggle", when: "!terminalFocus" },42 { key: "mod+shift+f", command: "projectSearch.toggle", when: "!terminalFocus" },
43 { key: "mod+u", command: "usage.open", when: "!terminalFocus" },
43 { key: "mod+alt+a", command: "theme.select", when: "!terminalFocus" },44 { key: "mod+alt+a", command: "theme.select", when: "!terminalFocus" },
44 { key: "mod+alt+shift+a", command: "appearance.cycle", when: "!terminalFocus" },45 { key: "mod+alt+shift+a", command: "appearance.cycle", when: "!terminalFocus" },
45 { key: "mod+alt+shift+t", command: "themeEditor.toggle" },46 { key: "mod+alt+shift+t", command: "themeEditor.toggle" },