Cleanup: Properties Panel (#7137)

## Summary

- Code cleanup
- Copy, padding, color, alignment of components
- Subgraph Edit mode changes
- Partial fix for the Node Info location (need to do context menu still)
- Editing node title

### Still to-do

- Bi-directionality in values

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7137-WIP-Cleanup-Properties-Panel-2be6d73d3650813e9430f6bcb09dfb4d)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Alexander Brown
2025-12-05 21:33:52 -08:00
committed by GitHub
parent cde49d5b64
commit f74c176423
45 changed files with 419 additions and 386 deletions

View File

@@ -1,15 +1,20 @@
<script setup lang="ts">
import { provide } from 'vue'
import { computed, provide } from 'vue'
import { useI18n } from 'vue-i18n'
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import WidgetLegacy from '@/renderer/extensions/vueNodes/widgets/components/WidgetLegacy.vue'
import { getComponent } from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
import {
getComponent,
shouldExpand
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
import { cn } from '@/utils/tailwindUtil'
import RightPanelSection from '../layout/RightPanelSection.vue'
import PropertiesAccordionItem from '../layout/PropertiesAccordionItem.vue'
defineProps<{
const { label, widgets } = defineProps<{
label?: string
widgets: { widget: IBaseWidget; node: LGraphNode }[]
}>()
@@ -17,6 +22,7 @@ defineProps<{
provide('hideLayoutField', true)
const canvasStore = useCanvasStore()
const { t } = useI18n()
function getWidgetComponent(widget: IBaseWidget) {
const component = getComponent(widget.type, widget.name)
@@ -31,17 +37,27 @@ function onWidgetValueChange(
widget.callback?.(value)
canvasStore.canvas?.setDirty(true, true)
}
const isEmpty = computed(() => widgets.length === 0)
const displayLabel = computed(
() =>
label ??
(isEmpty.value
? t('rightSidePanel.inputsNone')
: t('rightSidePanel.inputs'))
)
</script>
<template>
<RightPanelSection>
<PropertiesAccordionItem :is-empty>
<template #label>
<slot name="label">
{{ label ?? $t('rightSidePanel.inputs') }}
{{ displayLabel }}
</slot>
</template>
<div class="space-y-4 rounded-lg bg-interface-surface px-4">
<div v-if="!isEmpty" class="space-y-4 rounded-lg bg-interface-surface px-4">
<div
v-for="({ widget, node }, index) in widgets"
:key="`widget-${index}-${widget.name}`"
@@ -58,7 +74,7 @@ function onWidgetValueChange(
:model-value="widget.value"
:node-id="String(node.id)"
:node-type="node.type"
class="col-span-1"
:class="cn('col-span-1', shouldExpand(widget.type) && 'min-h-36')"
@update:model-value="
(value: string | number | boolean | object) =>
onWidgetValueChange(widget, value)
@@ -66,5 +82,5 @@ function onWidgetValueChange(
/>
</div>
</div>
</RightPanelSection>
</PropertiesAccordionItem>
</template>

View File

@@ -7,35 +7,30 @@ import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import SidePanelSearch from '../layout/SidePanelSearch.vue'
import SectionWidgets from './SectionWidgets.vue'
const props = defineProps<{
const { nodes } = defineProps<{
nodes: LGraphNode[]
}>()
const widgetsSectionDataList = computed(() => {
const list: {
widgets: { node: LGraphNode; widget: IBaseWidget }[]
node: LGraphNode
}[] = []
for (const node of props.nodes) {
const shownWidgets: IBaseWidget[] = []
for (const widget of node.widgets ?? []) {
if (widget.options?.canvasOnly || widget.options?.hidden) continue
shownWidgets.push(widget)
}
list.push({
widgets: shownWidgets?.map((widget) => ({ node, widget })) ?? [],
type NodeWidgetsList = Array<{ node: LGraphNode; widget: IBaseWidget }>
type NodeWidgetsListList = Array<{
node: LGraphNode
widgets: NodeWidgetsList
}>
const widgetsSectionDataList = computed((): NodeWidgetsListList => {
return nodes.map((node) => {
const { widgets = [] } = node
const shownWidgets = widgets
.filter((w) => !(w.options?.canvasOnly || w.options?.hidden))
.map((widget) => ({ node, widget }))
return {
widgets: shownWidgets,
node
})
}
return list
}
})
})
const searchedWidgetsSectionDataList = shallowRef<
{
widgets: { node: LGraphNode; widget: IBaseWidget }[]
node: LGraphNode
}[]
>([])
const searchedWidgetsSectionDataList = shallowRef<NodeWidgetsListList>([])
/**
* Searches widgets in all selected nodes and returns search results.
@@ -72,7 +67,7 @@ async function searcher(query: string) {
</script>
<template>
<div class="p-4 flex gap-2 border-b border-interface-stroke">
<div class="px-4 pb-4 flex gap-2 border-b border-interface-stroke">
<SidePanelSearch :searcher :update-key="widgetsSectionDataList" />
</div>
<SectionWidgets