feat(cron): add a name filter to the Scheduled Jobs sidebar (parity with the other list panels)
#6,724 opened on Aug 3, 2026
Repository metrics
- Stars
- (16,904 stars)
- PR merge metrics
- (Avg merge 14h 31m) (314 merged PRs in 30d)
Description
Problem
The Scheduled Jobs sidebar (#panelTasks) is the only list panel in the sidebar without a filter box. Past ~10 jobs the list scrolls, and finding a specific job becomes a visual scan.
On my install I have 15+ jobs whose names share long prefixes (05h_skill_mercado_research_be…, 06h_skill_open_source_modelos…, 06h30_skill_consolidado_dedupl…). They are truncated in the sidebar, so the distinguishing part of the name is exactly the part I cannot see.
The inconsistency
Every other list panel already has this, and they all share the same .sidebar-search component:
| Panel | Input | Handler |
|---|---|---|
| Chat | #sessionSearch |
filterSessions() — static/sessions.js |
| Kanban | #kanbanSearch |
filterKanban() → _kanbanRenderBoard() — static/panels.js |
| Skills | #skillsSearch |
filterSkills() |
| Settings | #settingsSearch |
— |
| MCP tools | #mcpToolSearch |
— |
| Scheduled Jobs | — | — |
.sidebar-search in static/style.css is already panel-agnostic (it is not scoped to the chat panel), so this needs no new CSS.
Suggested shape
loadCrons() in static/panels.js already has the right seam. The active/paused partition from #4026 carries the comment "_cronList stays the single source of truth — only the render is split, which keeps openCronDetail, _cronNewJobIds, and detail refresh untouched." A name filter fits the same split:
- Add a
.sidebar-searchblock with#cronSearchbetween the panel head and.cron-list, mirroring#skillsSearch(includingtype="search"and the password-manager opt-out attributes). - Filter
_activeJobs/_pausedJobsby a case-insensitive substring ofjob.name, right after the partition — so the paused(N)count reflects matches and_appendCronProfileToggle()keeps working unchanged. filterCrons()re-renders from the cached_cronListwith no API call, exactly likefilterKanban().- Because the filter lives inside the render, it survives both the refresh button and the polling refresh — no extra bookkeeping.
- One new i18n key (e.g.
filter_jobs), alongsidefilter_conversations. - Empty-result state: reuse the existing muted-text block with a "no jobs match" string rather than showing an empty panel.
I deliberately kept this to the job name, mirroring filter_conversations. Also matching status or profile would be nearly free (both are already rendered per item), but it produces surprising hits — a job named "Cleanup after errors" would match a search for error. If status filtering is wanted, separate chips would likely be cleaner than overloading the text input.
Notes
Happy to send a PR if you're open to it — following CONTRIBUTING Path 2 and opening this first to align on direction. Two things worth deciding before I do:
- name-only, or name + status/profile?
- should the query persist across reloads (
localStorage, likecron-paused-collapsed), or reset each time like the Chat filter?