From f297275e54fdc95173e73c9518e8812505efb8cb Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 20 Oct 2026 22:22:40 -0700 Subject: [PATCH] fix(gui-extensions): move details menu registry out of module state (#57644) --- .../gui-extensions/src/details/popover.tsx | 19 +++++++++++-------- .../gui-extensions/src/details/renderer.tsx | 12 ++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/gui-extensions/src/details/popover.tsx b/packages/gui-extensions/src/details/popover.tsx index fa5d5e6ffd3..1ed08ffa5a4 100644 --- a/packages/gui-extensions/src/details/popover.tsx +++ b/packages/gui-extensions/src/details/popover.tsx @@ -9,8 +9,12 @@ import { Command, createKeyed, createVisitState, useExtension, type MountedSessi import type Details from "./index" import type { DetailsPanelProps } from "./panel" +/** Every mounted details menu, so a shortcut finds the one on screen. */ +export type DetailsMenus = Set<{ trigger: () => HTMLButtonElement | undefined; toggle: () => void }> + /** The session details menu of one timeline header. Cached timelines each keep their own. */ export function DetailsHeader(props: { + menus: DetailsMenus session: MountedSession screen: SessionScreen active: boolean @@ -49,7 +53,7 @@ export function DetailsHeader(props: { return ( {(project) => ( - + HTMLButtonElement | undefined; toggle: () => void }>() - -function DetailsPopover(props: ParentProps<{ active: boolean; open: boolean; onOpenChange: (open: boolean) => void }>) { +function DetailsPopover( + props: ParentProps<{ menus: DetailsMenus; active: boolean; open: boolean; onOpenChange: (open: boolean) => void }>, +) { const ctx = useExtension() const keybinds = ctx.keybinds @@ -79,8 +82,8 @@ function DetailsPopover(props: ParentProps<{ active: boolean; open: boolean; onO // A shortcut opens the menu without its trigger, so the trigger is named as the anchor. let trigger: HTMLButtonElement | undefined const self = { trigger: () => trigger, toggle: () => props.onOpenChange(!props.open) } - menus.add(self) - onCleanup(() => menus.delete(self)) + props.menus.add(self) + onCleanup(() => props.menus.delete(self)) ctx.add(Command, (): Command | undefined => props.active @@ -91,7 +94,7 @@ function DetailsPopover(props: ParentProps<{ active: boolean; open: boolean; onO section: "session", bind: "mod+shift+y", // A detached timeline can stay active, so the shortcut toggles the menu whose trigger is on screen. - run: () => [...menus].find((menu) => menu.trigger()?.isConnected)?.toggle(), + run: () => [...props.menus].find((menu) => menu.trigger()?.isConnected)?.toggle(), } : undefined, ) diff --git a/packages/gui-extensions/src/details/renderer.tsx b/packages/gui-extensions/src/details/renderer.tsx index 30a6a9404e4..301872dbe66 100644 --- a/packages/gui-extensions/src/details/renderer.tsx +++ b/packages/gui-extensions/src/details/renderer.tsx @@ -2,12 +2,14 @@ import { createMemo, lazy, onCleanup, Show, Suspense } from "solid-js" import { createStore } from "solid-js/store" import { createKeyed, onIdle, Panel, Slot, Style, useDrawer, usePanel, type Setup } from "../sdk" import type Details from "./index" -import { DetailsHeader } from "./popover" +import { DetailsHeader, type DetailsMenus } from "./popover" const setup: Setup = (ctx) => { const sessions = ctx.sessions const changes = ctx.uses.changes + const menus: DetailsMenus = new Set() + const DetailsPanel = lazy(() => Promise.all([import("./panel"), import("./details.css?inline")]).then(([panel, css]) => { ctx.add(Style, css.default) @@ -23,7 +25,13 @@ const setup: Setup = (ctx) => { at: "session.header", order: 20, render: (input) => ( - + ), })