summaryrefslogtreecommitdiffhomepage
path: root/src/features/plugins/logic/highlight.ts
blob: 1bcfaad3e2472704df0483043ade6fd0758f6070 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import type { PluginNode } from "./types";

/** The two highlight sets for the §8 bidirectional dependency hover. */
export interface HighlightSets {
  /** The hovered row's DIRECT dependencies (highlighted when hovering an item). */
  readonly deps: ReadonlySet<string>;
  /** The loaded/unloaded rows that DIRECTLY depend on the hovered row
   * (highlighted when hovering a dependency). */
  readonly dependents: ReadonlySet<string>;
}

const EMPTY: HighlightSets = { deps: new Set(), dependents: new Set() };

/**
 * Bidirectional hover highlighting (§8): hover any item → its DIRECT
 * dependencies highlight; hover a dependency → its dependents highlight.
 * One hop in each direction (the plan's grammar is "its dependencies" /
 * "its dependents" — the visual neighborhood, not the transitive closure;
 * the transitive closures live in `computeCascade` / `autoCheckBackendPrereqs`).
 *
 * Works across BOTH manager tables (a loaded row can highlight an unloaded
 * dependency row and vice versa) — the node list spans the whole catalog.
 * Pure input → output; `hoveredId` null (no hover) → both sets empty.
 */
export function dependencyHighlightSets(
  hoveredId: string | null,
  nodes: readonly PluginNode[],
): HighlightSets {
  if (hoveredId === null) return EMPTY;
  const hovered = nodes.find((n) => n.id === hoveredId);
  if (hovered === undefined) return EMPTY;
  const deps = new Set(hovered.dependsOn);
  const dependents = new Set(
    nodes.filter((n) => n.id !== hoveredId && n.dependsOn.includes(hoveredId)).map((n) => n.id),
  );
  return { deps, dependents };
}