Title: Surface recently-used commands in the Command Palette (⌘K) · Issue #56 · modelstudioai/openwork · GitHub
Open Graph Title: Surface recently-used commands in the Command Palette (⌘K) · Issue #56 · modelstudioai/openwork
X Title: Surface recently-used commands in the Command Palette (⌘K) · Issue #56 · modelstudioai/openwork
Description: Problem or Motivation OpenWork's global command palette (⌘K / Ctrl+K, shipped in #42) lists every registered action grouped by category, but it has no memory of what you actually run. Every time you open it you start at the same static l...
Open Graph Description: Problem or Motivation OpenWork's global command palette (⌘K / Ctrl+K, shipped in #42) lists every registered action grouped by category, but it has no memory of what you actually run. Every time yo...
X Description: Problem or Motivation OpenWork's global command palette (⌘K / Ctrl+K, shipped in #42) lists every registered action grouped by category, but it has no memory of what you actually run. Every tim...
Opengraph URL: https://github.com/modelstudioai/openwork/issues/56
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Surface recently-used commands in the Command Palette (⌘K)","articleBody":"## Problem or Motivation\n\nOpenWork's global command palette (⌘K / Ctrl+K, shipped in #42) lists every\nregistered action grouped by category, but it has **no memory** of what you\nactually run. Every time you open it you start at the same static list and must\nre-search — even for the command you ran ten seconds ago.\n\nEvery mature command-palette surface fixes this with a **\"Recently used\"**\nsection pinned to the top: VS Code's ⌘⇧P, Raycast, Linear's ⌘K, and Claude Code\nDesktop's command menu all float your most-recent commands first so the actions\nyou use are one keystroke away. OpenWork's palette is the conspicuous exception.\n\n## Proposed Solution\n\nAdd a **\"Recently used\"** group at the top of the command palette:\n\n- Each action **run from the palette** is appended to a recents list (most-recent\n first), persisted in the renderer's `localStorage` (global, like a shell\n history), de-duplicated (an action never appears twice), and capped at 6\n entries.\n- When the palette opens with an **empty** query, the recents render in a\n dedicated **\"Recently used\"** group **above** the category groups.\n- The recents list is filtered to actions that still exist, aren't excluded from\n the palette, and can actually run right now (`canExecute`) — so it never\n surfaces a stale or dead command.\n- As soon as you **type a query**, the recents group is **hidden** — a search\n should rank by relevance, not recency — and normal cmdk filtering takes over.\n Clearing the query brings the recents back.\n\n## Feasibility\n\n**Classification: pure frontend — candidate.** No qwen-code backend involvement.\n\n- The palette already lists the centralized action registry and dispatches via\n `execute()`; recording a recent is a one-line call in the palette's existing\n `runAction`. No new IPC, no main-process change.\n- Persistence reuses the existing `lib/local-storage.ts` `KEYS`/`get`/`set`\n helper (one new key, `command-palette-recents`), exactly as other renderer UI\n prefs do.\n- The ordering logic (`pushRecent`: dedupe → prepend → cap) is a small pure\n module, fully unit-testable without a DOM.\n- The recents group reuses the same cmdk primitives (`CommandGroup`/`CommandItem`\n /`CommandShortcut`) the palette already renders. One new i18n key\n (`commands.recent`) added to all 7 locales for the group heading.\n\n## Alternatives Considered\n\n- **Frecency (frequency + recency) ranking:** richer, but heavier and harder to\n reason about; a simple most-recent-first list matches shell/VS-Code behaviour\n and can be layered up later.\n- **Showing recents even while searching:** conflicts with cmdk's relevance\n filtering and produces duplicate rows; hiding the group for non-empty queries\n is the standard, cleaner behaviour.\n- **Per-workspace recents:** a global history matches the palette's global,\n cross-workspace action set and is simpler; can be scoped later if wanted.\n\n## Additional Context\n\nConfirmed genuinely missing: `CommandPalette.tsx` builds its groups purely from\n`actionsByCategory` with no persistence and no recents concept.\n\n### Acceptance criteria (CDP e2e assertion)\n\nA new `e2e/assertions/command-palette-recents.assert.ts` drives the real built\napp over CDP entirely in the draft (no-session) state — no seeded conversation\nand no backend. The palette reads recents fresh from `localStorage` on each open,\nso the assertion seeds them and walks the full lifecycle:\n\n1. Seed `localStorage['craft-command-palette-recents'] = ['app.toggleTheme']`,\n open the palette → a **\"Recently used\"** group renders exactly one row\n (`Toggle Theme`), **above** the first category row.\n2. Type `sidebar` → the recents group **hides** (zero recent rows) while normal\n filtering still surfaces a `Toggle Sidebar` match; clearing the query brings\n the recents group **back**.\n3. Run a *new*, not-yet-recent action (`Toggle Sidebar`) from the palette, then\n **reopen** it → the recents group now has **two** rows with `Toggle Sidebar`\n **first** and `Toggle Theme` second.\n\nThe reopen step is load-bearing: reading the palette after running an action\nproves it actually **persists** what you run and orders recents newest-first —\nnot merely that a seeded list can render. In addition, the pure `pushRecent`\nmodule ships with unit tests covering dedupe, move-to-front, cap, and immutability.\n\nPart of the autonomous desktop-feature loop (`loop-bot`).","author":{"url":"https://github.com/DragonnZhang","@type":"Person","name":"DragonnZhang"},"datePublished":"2026-07-05T01:36:36.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/56/openwork/issues/56"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:7923e2b4-1d42-32f8-d92e-f8596de14894 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B76A:10FEF0:1199BF2:1798A6C:6A529A9B |
| html-safe-nonce | 8b599593716783b8cecc43edd7f3430839b7d1929cd2cfd830aeeb1ed8789f8d |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCNzZBOjEwRkVGMDoxMTk5QkYyOjE3OThBNkM6NkE1MjlBOUIiLCJ2aXNpdG9yX2lkIjoiMTA1MzMzNzQ3OTgzNzU1NTM1NSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 996d31a532d63cde545026c7d2225c4a808f7b455f72354801464545cc71e6ee |
| hovercard-subject-tag | issue:4811202804 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelstudioai/openwork/56/issue_layout |
| twitter:image | https://opengraph.githubassets.com/fde356aeb72618fce2f1ad419d209f4ffcd538daa22b1a10fea8619e294b4cbf/modelstudioai/openwork/issues/56 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/fde356aeb72618fce2f1ad419d209f4ffcd538daa22b1a10fea8619e294b4cbf/modelstudioai/openwork/issues/56 |
| og:image:alt | Problem or Motivation OpenWork's global command palette (⌘K / Ctrl+K, shipped in #42) lists every registered action grouped by category, but it has no memory of what you actually run. Every time yo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | DragonnZhang |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| go-import | github.com/modelstudioai/openwork git https://github.com/modelstudioai/openwork.git |
| octolytics-dimension-user_id | 283556804 |
| octolytics-dimension-user_login | modelstudioai |
| octolytics-dimension-repository_id | 1256697754 |
| octolytics-dimension-repository_nwo | modelstudioai/openwork |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1256697754 |
| octolytics-dimension-repository_network_root_nwo | modelstudioai/openwork |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 07a982c1d40157c619b364352b704c3ce66bb332 |
| ui-target | canary-1 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width