Support widget specific contextmenu options in vue (#8431)

<img width="614" height="485" alt="image"
src="https://github.com/user-attachments/assets/2a635dec-8bed-4fab-9881-5e6057d482e1"
/>

These options were defined in `litegraphService`. While the existing
code for defining options is reused (to ensure there's no implementation
drift) these extra widget options use the litegraph format for context
menu options and do not belong in `useSelectionMenuOptions`. They have
been moved out of `useLitegraphService` (good), but left in
`litegraphService` (not great)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8431-Support-widget-specific-contextmenu-options-in-vue-2f76d73d3650814fb20fca352dc81e3b)
by [Unito](https://www.unito.io)
This commit is contained in:
AustinMroz
2026-01-29 11:38:41 -08:00
committed by GitHub
parent cabd08f0ec
commit af8433fb3d
5 changed files with 77 additions and 45 deletions

View File

@@ -1,8 +1,9 @@
import { computed, ref } from 'vue'
import type { Ref } from 'vue'
import type { LGraphGroup } from '@/lib/litegraph/src/litegraph'
import type { LGraphGroup, LGraphNode } from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { getExtraOptionsForWidget } from '@/services/litegraphService'
import { isLGraphGroup } from '@/utils/litegraphUtil'
import {
@@ -45,6 +46,8 @@ export enum BadgeVariant {
// Global singleton for NodeOptions component reference
let nodeOptionsInstance: null | NodeOptionsInstance = null
const hoveredWidgetName = ref<string>()
/**
* Toggle the node options popover
* @param event - The trigger event
@@ -61,6 +64,13 @@ export function toggleNodeOptions(event: Event) {
* @param event - The trigger event (must be MouseEvent for position)
*/
export function showNodeOptions(event: MouseEvent) {
hoveredWidgetName.value = undefined
const target = event.target
if (target instanceof HTMLElement) {
const widgetEl = target.closest('.lg-node-widget')
if (widgetEl instanceof HTMLElement)
hoveredWidgetName.value = widgetEl.dataset.widgetName
}
if (nodeOptionsInstance?.show) {
nodeOptionsInstance.show(event)
}
@@ -133,8 +143,8 @@ export function useMoreOptionsMenu() {
} = useGroupMenuOptions()
const {
getBasicSelectionOptions,
getSubgraphOptions,
getMultipleNodesOptions
getMultipleNodesOptions,
getSubgraphOptions
} = useSelectionMenuOptions()
const hasSubgraphs = hasSubgraphsComputed
@@ -164,13 +174,13 @@ export function useMoreOptionsMenu() {
// For single node selection, also get LiteGraph menu items to merge
const litegraphOptions: MenuOption[] = []
const node: LGraphNode | undefined = selectedNodes.value[0]
if (
selectedNodes.value.length === 1 &&
!groupContext &&
canvasStore.canvas
) {
try {
const node = selectedNodes.value[0]
const rawItems = canvasStore.canvas.getNodeMenuOptions(node)
// Don't apply structuring yet - we'll do it after merging with Vue options
litegraphOptions.push(
@@ -249,6 +259,18 @@ export function useMoreOptionsMenu() {
options.push(...getImageMenuOptions(selectedNodes.value[0]))
options.push({ type: 'divider' })
}
const rawName = hoveredWidgetName.value
const widget = node?.widgets?.find((w) => w.name === rawName)
if (widget) {
const widgetOptions = convertContextMenuToOptions(
getExtraOptionsForWidget(node, widget)
)
if (widgetOptions) {
options.push(...widgetOptions)
options.push({ type: 'divider' })
}
}
// Section 6 & 7: Extensions and Delete are handled by buildStructuredMenu
// Mark all Vue options with source