Files
Comfyui-Nodes-Stats/docs/plans/2026-06-21-mirror-search-design.md
T
Ethanfel aa2f1f62bf docs: design for disabled-node mirror search
Approved design: a standalone search palette (toolbar button + hotkey) over
nodes belonging to disabled packs, built frontend-only by joining Manager's
getmappings x getlist, with Enable 7d / Enable actions reusing the trial
feature. Native-search injection is infeasible (bundled frontend), so this
runs alongside as a mirror.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:12:25 +02:00

6.0 KiB

Design: Mirror search for disabled-pack nodes

Date: 2026-06-21 Status: Approved

Summary

A standalone "mirror search" palette that lets you find nodes belonging to currently-disabled custom-node packages — without loading those packages (which slows ComfyUI boot/runtime). Each result has Enable buttons; enabling re-enables the owning package (temporarily or permanently) and takes effect after a ComfyUI restart, after which the node appears in ComfyUI's native search.

This runs alongside ComfyUI's native node search rather than inside it: ComfyUI's frontend is compiled/bundled and exposes no search-provider or node-def injection hook, so the native search cannot be extended. A separate palette is the only viable design (and matches the request — a "mirror" search).

Constraints discovered (reconnaissance)

  • Native search injection is not feasible: bundled frontend, no public hook (app.registerExtension offers nodeCreated/beforeRegisterNodeDef only), and disabled packs never enter NODE_CLASS_MAPPINGS / /object_info.
  • /customnode/getmappings?mode=local returns a registry-wide map keyed by repo URL: { <repo_url>: [ [class_type, ...], { title_aux } ] }. It does include the class_type names for every pack, including disabled ones.
  • /customnode/getlist?mode=local&skip_update=true lists packs with state (disabled/enabled/not-installed), id, version, files, repository. There are 73 disabled packs in the reference install.
  • Available metadata for an unloaded node: class_type name + pack name/title only. No categories, descriptions, or input/output ports (those require the pack to be loaded).

Decisions (from brainstorming)

  • Trigger: dedicated palette opened by a toolbar button + a keyboard shortcut. Not a tab in the Node Stats dialog.
  • Enable actions: both "Enable 7d" (rolling trial) and "Enable" (permanent), reusing the trial-enable feature. Takes effect after restart.
  • Catalog construction: frontend-only. Fetch getmappings + getlist on first open, join by repo URL, cache in memory for the session, with a refresh affordance. No backend changes.
  • Scope: disabled packs only (not the full not-installed registry — that's Manager's job).

Architecture

All in js/nodes_stats.js; no backend changes.

Catalog

  • ensureDisabledCatalog() (cached in a module variable):
    1. fetchManagerInfo() (existing; getlist) → packs with state === 'disabled'.
    2. fetch('/customnode/getmappings?mode=local') → build repoUrl -> [class_types].
    3. Normalize URLs (lowercase, strip trailing / and .git) on both sides.
    4. For each disabled pack, look up class_types by its files[0]/repository; emit { class_type, pack: <dir name>, title, enableInfo } per node.
  • A refresh() clears the cache and rebuilds.

Search/filter (pure functions, for clarity + manual testability)

  • scoreEntry(entry, queryLower) → null if no match; else a rank where class_type prefix < word-start < substring, pack-name match ranked lower.
  • filterCatalog(catalog, query, limit=50) → sorted, capped list + total count.

Palette UI

  • openMirrorSearch(): ensure catalog; render a modal overlay (reusing the dialog styling helpers) with a text input (autofocused) and a results list.
  • Input keyup → re-render rows via filterCatalog.
  • Row: class_type · (pack) · [Enable 7d] [Enable].
  • Footer: "/ from disabled packs · enabling needs a restart" + "↻ refresh".
  • Empty/error/inert states handled explicitly.

Trigger

  • Toolbar button (like the existing Node Stats button), title "Search disabled nodes".
  • Keyboard shortcut: prefer ComfyUI's extension command/keybinding API if available; else a guarded document keydown listener (default Ctrl/Cmd+Shift+D), ignored when focus is in an input/textarea.

Actions

  • Reuse handleEnable(pkg, temporary) from the trial-enable feature:
    • [Enable 7d]temporary=true (Manager enable → trials/start).
    • [Enable]temporary=false (Manager enable → trials/stop).
  • Reuse the restart banner / toast. After enable, mark the row "enabled · restart".

Data flow

setup() -> add toolbar button + register hotkey
trigger -> openMirrorSearch()
        -> ensureDisabledCatalog()  (1st time: getmappings + getlist, join, cache)
        -> render modal (input + results)
type    -> filterCatalog() -> render rows (instant, in-memory)
Enable  -> handleEnable(pkg, temp?) -> Manager enable -> trials/start|stop
        -> restart banner/toast
refresh -> clear cache -> ensureDisabledCatalog() -> re-render

Error handling

  • ComfyUI Manager absent or getlist/getmappings fails → palette shows a clear message ("ComfyUI Manager not available" / "couldn't load disabled-node list"); the button stays but the palette is inert. No crash.
  • Zero disabled packs → "No disabled packages — nothing to search."
  • Enable failure → existing error toast; row left actionable.
  • URL-join misses for a pack → that pack contributes no rows (logged to console); never throws.

Testing

  • No backend changes → no new pytest.
  • Pure helpers (scoreEntry, filterCatalog, URL normalization, catalog join) written as small standalone functions; node --check for syntax.
  • Manual verification: open palette via button + hotkey; search a known disabled pack's node (e.g. an Inspire-Pack node); Enable 7d → getlist flips to enabled
    • /nodes-stats/trials shows it + restart banner; Enable (permanent) → enabled, no trial row; refresh rebuilds; Manager-absent path shows the inert message.

Files touched

  • js/nodes_stats.js — catalog, filter, palette UI, trigger, reuse enable.
  • README.md, pyproject.toml — docs + version bump.

Out of scope (YAGNI)

  • Injecting results into ComfyUI's native search (not feasible).
  • Rich node metadata (titles/categories/ports) for unloaded nodes (unavailable).
  • Auto-placing the node into the graph after restart.
  • Searching not-installed registry packs (Manager already does this).