Files
homarr/scripts
Thomas Camlong b4be884dfc perf(board): one shared context menu instead of one per widget
Mantine's Menu mounts Popover + PopoverDropdown + several context providers even
while closed, so a menu per widget was ~13 fibers each for something almost never
opened. WidgetContextMenuProvider now mounts a single menu for the board, anchored
to the cursor; items keep only a contextmenu/long-press listener.

The dropdown takes the section's gridstack ref as a prop rather than reading
SectionContext — the shared menu lives outside any section, and reading it from
context there crashed the board with "useSectionContext must be used within a
SectionContext".

Verified against both builds with scripts/benchmarks/verify-context-menu.mts
(opens on right-click, shows actions, closes on Escape, re-targets to a second
widget):

  idle fibers          18,670 -> 16,278  (-12.8%)
  fibers after use     +4,191 -> +4
  JS event listeners   10,128 -> 7,774   (-23%)
  DOM nodes             9,189 -> 8,287   (-10%)
  browser JS heap       54.1  -> 52.7 MiB
  median board load      520  -> 479 ms
  container CPU         20.4  -> 20.0 s

Two runs per side, four tabs across two boards. Server memory is unchanged, as
expected for a client-only change. The listener reduction is the point: the 650 MB
session had ~88k listeners and this removes a per-widget source of them.

The gate is validated by passing against unmodified code first. An earlier version
used Playwright's click({ button: "right" }), which emits no contextmenu event
here and so failed shipped code identically — it wrongly condemned a previous
attempt at this optimisation. A gate never run against the un-optimised build
proves only that something changed, not that it still works.

pnpm test 1794/1795 (pre-existing pi-hole Testcontainers flake, 9/9 in isolation);
typecheck clean across 37 packages; 0 lint errors.
2026-08-09 11:05:03 +02:00
..
2026-07-13 18:32:48 +02:00