feat: right side panel favorites, no selection state, and more... (#7812)

Most of the features in this pull request are completed and can be
reviewed and merged.

## TODO

- [x] no selection panel
- [x] group selected panel
- [x] tabs 
  - [x] favorites tab
  - [x] global settings tab
  - [x] nodes tab
- [x] widget actions menu 
  - [x] [Bug]: style bugs
- [x] button zoom to the node on canvas.
- [x] rename widgets on widget actions
  - [ ] [Bug]: the canvas has not been updated after renaming. 
- [x] global settings
  - [ ] setting item: "show advanced parameters"
    - blocked by other things. skip for now.
  - [x] setting item: show toolbox on selection 
  - [x] setting item: nodes 2.0
  - [ ] setting item: "background color"
    - blocked by other things. skip for now.
  - [x] setting item: grid spacing
  - [x] setting item: snap nodes to grid
  - [x] setting item: link shape
  - [x] setting item: show connected links
  - [x] form style reuses the form style of node widgets
- [x] group node cases
  - [x] group node settings
  - [x] show all nodes in group
  - [x] show frame name on nodes when multiple selections are made
  - [x] group multiple selections
- [x] [Bug]: nodes without widgets cannot display the location and their
group
  - [x] [Bug]: labels layout
- [x] favorites
  - [x] the indicator on widgets
  - [x] favorite and unfavorite buttons on widgets
- [x] [Bug]: show node name in favorite widgets + improve labels layout
- [ ] [Bug]: After canceling the like, the like list will not be updated
immediately.
- [x] [Bug]: The favorite function does not work for the project on
Subgraph.
- [x] subgraph
- [x] add the node name from where this parameter comes from when node
is subgraph
  - [x] show and hide directly on Inputs
    - [x] some bugs need to be fixed.
- [x] advanced widgets 
  - [x] button: show advanced inputs
- Clicking button expands the "Advanced Inputs" section on the right
side panel, regardless of whether the panel is open or not
    - [x] [Bug]: style bugs
  - [x] advanced inputs section when node is subgraph
- [x] inputs tab rearranging
  - [x] favorited inputs rearranging
  - [x] subgraph inputs rearranging
- [ ] review and reconstruction to improve complexity and architecture

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7812-feat-right-side-panel-favorites-no-selection-state-and-more-2da6d73d36508134b503d676f9b3d248)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
This commit is contained in:
Rizumu Ayaka
2026-01-14 10:37:17 +07:00
committed by GitHub
parent 069e94b325
commit b1b2fd8a4f
44 changed files with 3352 additions and 571 deletions

View File

@@ -104,7 +104,7 @@ function widgetWithVueTrack(
return { get() {}, set() {} }
})
}
export function useReactiveWidgetValue(widget: IBaseWidget) {
function useReactiveWidgetValue(widget: IBaseWidget) {
widgetWithVueTrack(widget)
widget.vueTrack()
return widget.value
@@ -120,12 +120,59 @@ function getControlWidget(widget: IBaseWidget): SafeControlWidget | undefined {
update: (value) => (cagWidget.value = normalizeControlOption(value))
}
}
function getNodeType(node: LGraphNode, widget: IBaseWidget) {
if (!node.isSubgraphNode() || !isProxyWidget(widget)) return undefined
const subNode = node.subgraph.getNodeById(widget._overlay.nodeId)
return subNode?.type
}
/**
* Shared widget enhancements used by both safeWidgetMapper and Right Side Panel
*/
interface SharedWidgetEnhancements {
/** Reactive widget value that updates when the widget changes */
value: WidgetValue
/** Control widget for seed randomization/increment/decrement */
controlWidget?: SafeControlWidget
/** Input specification from node definition */
spec?: InputSpec
/** Node type (for subgraph promoted widgets) */
nodeType?: string
/** Border style for promoted/advanced widgets */
borderStyle?: string
/** Widget label */
label?: string
/** Widget options */
options?: Record<string, any>
}
/**
* Extracts common widget enhancements shared across different rendering contexts.
* This function centralizes the logic for extracting metadata and reactive values
* from widgets, ensuring consistency between Nodes 2.0 and Right Side Panel.
*/
export function getSharedWidgetEnhancements(
node: LGraphNode,
widget: IBaseWidget
): SharedWidgetEnhancements {
const nodeDefStore = useNodeDefStore()
return {
value: useReactiveWidgetValue(widget),
controlWidget: getControlWidget(widget),
spec: nodeDefStore.getInputSpecForWidget(node, widget.name),
nodeType: getNodeType(node, widget),
borderStyle: widget.promoted
? 'ring ring-component-node-widget-promoted'
: widget.advanced
? 'ring ring-component-node-widget-advanced'
: undefined,
label: widget.label,
options: widget.options
}
}
/**
* Validates that a value is a valid WidgetValue type
*/
@@ -161,16 +208,13 @@ export function safeWidgetMapper(
node: LGraphNode,
slotMetadata: Map<string, WidgetSlotMetadata>
): (widget: IBaseWidget) => SafeWidgetData {
const nodeDefStore = useNodeDefStore()
return function (widget) {
try {
const spec = nodeDefStore.getInputSpecForWidget(node, widget.name)
// Get shared enhancements used by both Nodes 2.0 and Right Side Panel
const sharedEnhancements = getSharedWidgetEnhancements(node, widget)
const slotInfo = slotMetadata.get(widget.name)
const borderStyle = widget.promoted
? 'ring ring-component-node-widget-promoted'
: widget.advanced
? 'ring ring-component-node-widget-advanced'
: undefined
// Wrapper callback specific to Nodes 2.0 rendering
const callback = (v: unknown) => {
const value = normalizeWidgetValue(v)
widget.value = value ?? undefined
@@ -185,16 +229,10 @@ export function safeWidgetMapper(
return {
name: widget.name,
type: widget.type,
value: useReactiveWidgetValue(widget),
borderStyle,
...sharedEnhancements,
callback,
controlWidget: getControlWidget(widget),
hasLayoutSize: typeof widget.computeLayoutSize === 'function',
isDOMWidget: isDOMWidget(widget),
label: widget.label,
nodeType: getNodeType(node, widget),
options: widget.options,
spec,
slotMetadata: slotInfo
}
} catch (error) {