nesquena/hermes-webui

feat(cron): add a name filter to the Scheduled Jobs sidebar (parity with the other list panels)

Open

#6,724 opened on Aug 3, 2026

 (2 comments) (0 reactions) (0 assignees)Python (2,309 forks)github user discovery
enhancementhelp wantedtasksux

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-search block with #cronSearch between the panel head and .cron-list, mirroring #skillsSearch (including type="search" and the password-manager opt-out attributes).
  • Filter _activeJobs / _pausedJobs by a case-insensitive substring of job.name, right after the partition — so the paused (N) count reflects matches and _appendCronProfileToggle() keeps working unchanged.
  • filterCrons() re-renders from the cached _cronList with no API call, exactly like filterKanban().
  • 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), alongside filter_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:

  1. name-only, or name + status/profile?
  2. should the query persist across reloads (localStorage, like cron-paused-collapsed), or reset each time like the Chat filter?

Contributor guide