Move widget configuration out of sidebar

This commit is contained in:
Austin Mroz
2025-09-27 12:23:45 -05:00
parent 7c20f37973
commit 44d57ce041
7 changed files with 136 additions and 109 deletions

View File

@@ -22,7 +22,8 @@
<ColorPickerButton v-if="showColorPicker" /> <ColorPickerButton v-if="showColorPicker" />
<FrameNodes v-if="showFrameNodes" /> <FrameNodes v-if="showFrameNodes" />
<ConvertToSubgraphButton v-if="showConvertToSubgraph" /> <ConvertToSubgraphButton v-if="showConvertToSubgraph" />
<PublishSubgraphButton v-if="showPublishSubgraph" /> <ConfigureSubgraph v-if="showSubgraphButtons" />
<PublishSubgraphButton v-if="showSubgraphButtons" />
<MaskEditorButton v-if="showMaskEditor" /> <MaskEditorButton v-if="showMaskEditor" />
<VerticalDivider <VerticalDivider
v-if="showAnyPrimaryActions && showAnyControlActions" v-if="showAnyPrimaryActions && showAnyControlActions"
@@ -50,6 +51,7 @@ import { computed, ref } from 'vue'
import BypassButton from '@/components/graph/selectionToolbox/BypassButton.vue' import BypassButton from '@/components/graph/selectionToolbox/BypassButton.vue'
import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue' import ColorPickerButton from '@/components/graph/selectionToolbox/ColorPickerButton.vue'
import ConfigureSubgraph from '@/components/graph/selectionToolbox/ConfigureSubgraph.vue'
import ConvertToSubgraphButton from '@/components/graph/selectionToolbox/ConvertToSubgraphButton.vue' import ConvertToSubgraphButton from '@/components/graph/selectionToolbox/ConvertToSubgraphButton.vue'
import DeleteButton from '@/components/graph/selectionToolbox/DeleteButton.vue' import DeleteButton from '@/components/graph/selectionToolbox/DeleteButton.vue'
import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue' import ExecuteButton from '@/components/graph/selectionToolbox/ExecuteButton.vue'
@@ -112,7 +114,7 @@ const showInfoButton = computed(() => !!nodeDef.value)
const showColorPicker = computed(() => hasAnySelection.value) const showColorPicker = computed(() => hasAnySelection.value)
const showConvertToSubgraph = computed(() => hasAnySelection.value) const showConvertToSubgraph = computed(() => hasAnySelection.value)
const showFrameNodes = computed(() => hasMultipleSelection.value) const showFrameNodes = computed(() => hasMultipleSelection.value)
const showPublishSubgraph = computed(() => isSingleSubgraph.value) const showSubgraphButtons = computed(() => isSingleSubgraph.value)
const showBypass = computed( const showBypass = computed(
() => () =>
@@ -130,7 +132,7 @@ const showAnyPrimaryActions = computed(
showColorPicker.value || showColorPicker.value ||
showConvertToSubgraph.value || showConvertToSubgraph.value ||
showFrameNodes.value || showFrameNodes.value ||
showPublishSubgraph.value showSubgraphButtons.value
) )
const showAnyControlActions = computed(() => showBypass.value) const showAnyControlActions = computed(() => showBypass.value)

View File

@@ -0,0 +1,24 @@
<template>
<Button
v-tooltip.top="{
value: t('Edit Subgraph Widgets'),
showDelay: 1000
}"
severity="secondary"
text
@click="showSubgraphNodeDialog"
>
<template #icon>
<i-lucide:settings2 />
</template>
</Button>
</template>
<script setup lang="ts">
import Button from 'primevue/button'
import { useI18n } from 'vue-i18n'
import { showSubgraphNodeDialog } from '@/core/graph/subgraph/useSubgraphNodeDialog'
const { t } = useI18n()
</script>

View File

@@ -5,7 +5,6 @@ import draggable from 'vuedraggable'
import SearchBox from '@/components/common/SearchBox.vue' import SearchBox from '@/components/common/SearchBox.vue'
import SubgraphNodeWidget from '@/components/selectionbar/SubgraphNodeWidget.vue' import SubgraphNodeWidget from '@/components/selectionbar/SubgraphNodeWidget.vue'
import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue'
import { import {
type ProxyWidgetsProperty, type ProxyWidgetsProperty,
parseProxyWidgets parseProxyWidgets
@@ -14,6 +13,7 @@ import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode' import { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets' import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useDialogStore } from '@/stores/dialogStore'
type WidgetItem = [LGraphNode, IBaseWidget] type WidgetItem = [LGraphNode, IBaseWidget]
@@ -32,6 +32,7 @@ function toKey(item: WidgetItem) {
const activeNode = computed(() => { const activeNode = computed(() => {
const node = canvasStore.selectedItems[0] const node = canvasStore.selectedItems[0]
if (node instanceof SubgraphNode) return node if (node instanceof SubgraphNode) return node
useDialogStore().closeDialog()
return undefined return undefined
}) })
@@ -184,85 +185,76 @@ const filteredActive = computed<WidgetItem[]>(() => {
}) })
</script> </script>
<template> <template>
<SidebarTabTemplate <SearchBox
:title="'Parameters'" v-model:model-value="searchQuery"
class="workflows-sidebar-tab bg-[var(--p-tree-background)]" class="model-lib-search-box p-2 2xl:p-4"
> :placeholder="$t('g.search') + '...'"
<template #header> />
<SearchBox <div v-if="filteredActive.length" class="widgets-section">
v-model:model-value="searchQuery" <div class="widgets-section-header">
class="model-lib-search-box p-2 2xl:p-4" <div>{{ t('subgraphStore.shown') }}</div>
:placeholder="$t('g.search') + '...'" <a @click.stop="hideAll"> {{ t('subgraphStore.hideAll') }}</a>
</div>
<div v-if="searchQuery" class="w-full">
<div
v-for="element in filteredActive"
:key="toKey(element)"
class="w-full"
>
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:toggle-visibility="toggleVisibility"
:is-shown="true"
/>
</div>
</div>
<draggable
v-else
v-model="activeWidgets"
group="enabledWidgets"
class="w-full cursor-grab"
chosen-class="cursor-grabbing"
drag-class="cursor-grabbing"
:animation="100"
item-key="id"
>
<template #item="{ element }">
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:is-shown="true"
:toggle-visibility="toggleVisibility"
:is-draggable="true"
/>
</template>
</draggable>
</div>
<div v-if="filteredCandidates.length" class="widgets-section">
<div class="widgets-section-header">
<div>{{ t('subgraphStore.hidden') }}</div>
<a @click.stop="showAll"> {{ t('subgraphStore.showAll') }}</a>
</div>
<div
v-for="element in filteredCandidates"
:key="toKey(element)"
class="w-full"
>
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:toggle-visibility="toggleVisibility"
/> />
</template> </div>
<template #body> </div>
<div v-if="filteredActive.length" class="widgets-section"> <div v-if="recommendedWidgets.length" class="justify-center flex py-4">
<div class="widgets-section-header"> <Button size="small" @click.stop="showRecommended">
<div>{{ t('subgraphStore.shown') }}</div> {{ t('subgraphStore.showRecommended') }}
<a @click.stop="hideAll"> {{ t('subgraphStore.hideAll') }}</a> </Button>
</div> </div>
<div v-if="searchQuery" class="w-full">
<div
v-for="element in filteredActive"
:key="toKey(element)"
class="w-full"
>
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:toggle-visibility="toggleVisibility"
:is-shown="true"
/>
</div>
</div>
<draggable
v-else
v-model="activeWidgets"
group="enabledWidgets"
class="w-full cursor-grab"
chosen-class="cursor-grabbing"
drag-class="cursor-grabbing"
:animation="100"
item-key="id"
>
<template #item="{ element }">
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:is-shown="true"
:toggle-visibility="toggleVisibility"
:is-draggable="true"
/>
</template>
</draggable>
</div>
<div v-if="filteredCandidates.length" class="widgets-section">
<div class="widgets-section-header">
<div>{{ t('subgraphStore.hidden') }}</div>
<a @click.stop="showAll"> {{ t('subgraphStore.showAll') }}</a>
</div>
<div
v-for="element in filteredCandidates"
:key="toKey(element)"
class="w-full"
>
<SubgraphNodeWidget
:node-id="`${element[0].id}`"
:node-title="element[0].title"
:widget-name="element[1].name"
:toggle-visibility="toggleVisibility"
/>
</div>
</div>
<div v-if="recommendedWidgets.length" class="justify-center flex py-4">
<Button size="small" @click.stop="showRecommended">
{{ t('subgraphStore.showRecommended') }}
</Button>
</div>
</template>
</SidebarTabTemplate>
</template> </template>
<style scoped> <style scoped>
.widgets-section-header { .widgets-section-header {

View File

@@ -1,19 +0,0 @@
import { markRaw } from 'vue'
import SubgraphNode from '@/components/selectionbar/SubgraphNode.vue'
import type { SidebarTabExtension } from '@/types/extensionTypes'
export const useSubgraphNodeTab = (): SidebarTabExtension => {
return {
id: 'sgn',
icon: 'pi pi-chart-bar',
iconBadge: () => {
return null
},
title: 'subgraph widgets',
tooltip: 'Change displayed subgraph widgets',
label: 'subgraph widgets',
component: markRaw(SubgraphNode),
type: 'vue'
}
}

View File

@@ -0,0 +1,21 @@
import SubgraphNode from '@/components/selectionbar/SubgraphNode.vue'
import { type DialogComponentProps, useDialogStore } from '@/stores/dialogStore'
const key = 'global-subgraph-node-config'
export function showSubgraphNodeDialog() {
const dialogStore = useDialogStore()
const dialogComponentProps: DialogComponentProps = {
headless: true,
modal: false,
closable: false,
position: 'right'
}
dialogStore.showDialog({
title: 'Parameters',
key,
component: SubgraphNode,
dialogComponentProps
})
}

View File

@@ -5,6 +5,7 @@ import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview' import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview'
import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage' import { useNodeImage, useNodeVideo } from '@/composables/node/useNodeImage'
import { addWidgetPromotionOptions } from '@/core/graph/subgraph/proxyWidgetUtils' import { addWidgetPromotionOptions } from '@/core/graph/subgraph/proxyWidgetUtils'
import { showSubgraphNodeDialog } from '@/core/graph/subgraph/useSubgraphNodeDialog'
import { st, t } from '@/i18n' import { st, t } from '@/i18n'
import { import {
type IContextMenuValue, type IContextMenuValue,
@@ -825,13 +826,21 @@ export const useLitegraphService = () => {
} }
} }
if (this instanceof SubgraphNode) { if (this instanceof SubgraphNode) {
options.unshift({ options.unshift(
content: 'Unpack Subgraph', {
callback: () => { content: 'Edit Subgraph Widgets',
useNodeOutputStore().revokeSubgraphPreviews(this) callback: () => {
this.graph.unpackSubgraph(this) showSubgraphNodeDialog()
}
},
{
content: 'Unpack Subgraph',
callback: () => {
useNodeOutputStore().revokeSubgraphPreviews(this)
this.graph.unpackSubgraph(this)
}
} }
}) )
} }
if (this.graph && !this.graph.isRootGraph) { if (this.graph && !this.graph.isRootGraph) {
const [x, y] = canvas.canvas_mouse const [x, y] = canvas.canvas_mouse

View File

@@ -4,7 +4,6 @@ import { computed, ref } from 'vue'
import { useModelLibrarySidebarTab } from '@/composables/sidebarTabs/useModelLibrarySidebarTab' import { useModelLibrarySidebarTab } from '@/composables/sidebarTabs/useModelLibrarySidebarTab'
import { useNodeLibrarySidebarTab } from '@/composables/sidebarTabs/useNodeLibrarySidebarTab' import { useNodeLibrarySidebarTab } from '@/composables/sidebarTabs/useNodeLibrarySidebarTab'
import { useQueueSidebarTab } from '@/composables/sidebarTabs/useQueueSidebarTab' import { useQueueSidebarTab } from '@/composables/sidebarTabs/useQueueSidebarTab'
import { useSubgraphNodeTab } from '@/composables/sidebarTabs/useSubgraphNodeTab'
import { t, te } from '@/i18n' import { t, te } from '@/i18n'
import { useWorkflowsSidebarTab } from '@/platform/workflow/management/composables/useWorkflowsSidebarTab' import { useWorkflowsSidebarTab } from '@/platform/workflow/management/composables/useWorkflowsSidebarTab'
import { useCommandStore } from '@/stores/commandStore' import { useCommandStore } from '@/stores/commandStore'
@@ -93,7 +92,6 @@ export const useSidebarTabStore = defineStore('sidebarTab', () => {
registerSidebarTab(useNodeLibrarySidebarTab()) registerSidebarTab(useNodeLibrarySidebarTab())
registerSidebarTab(useModelLibrarySidebarTab()) registerSidebarTab(useModelLibrarySidebarTab())
registerSidebarTab(useWorkflowsSidebarTab()) registerSidebarTab(useWorkflowsSidebarTab())
registerSidebarTab(useSubgraphNodeTab())
const menuStore = useMenuItemStore() const menuStore = useMenuItemStore()