feat(node): show Enter Subgraph and Error buttons side by side in node footer (#9126)

## Summary
Show the \"Enter Subgraph\" and \"Error\" buttons simultaneously in the
node footer when a subgraph node has errors, instead of only showing the
Error button.

## Changes
- **What**: Replaced the single mutually-exclusive `Button` in the node
footer with a flex container that can display both the \"Enter
Subgraph\" button and the \"Error\" button side by side, separated by a
divider
- **What**: When a subgraph node has errors, the Enter button shows a
short label \"Enter\" to fit alongside the Error button; otherwise it
shows the full \"Enter Subgraph\" label
- **What**: Advanced inputs toggle button is now explicitly restricted
to non-subgraph nodes (preserving existing behavior)
- **What**: Added `"enter": "Enter"` i18n key for the shortened subgraph
entry label

## Review Focus
- Layout behavior when both buttons are visible (subgraph node with
errors)
- Collapsed vs. expanded node states with the new flex container
- The `divide-x divide-component-node-border` divider between buttons

> Note: May need to backport to a stable release branch.

## Screenshot


https://github.com/user-attachments/assets/1eb4afe0-bf82-4677-ad86-6e15a9c0a487



- Fixes <!-- #ISSUE_NUMBER -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9126-feat-node-show-Enter-Subgraph-and-Error-buttons-side-by-side-in-node-footer-3106d73d3650818a9afdeb51813825d9)
by [Unito](https://www.unito.io)
This commit is contained in:
jaeone94
2026-02-23 17:43:17 +09:00
committed by GitHub
parent a0e8f7d960
commit dd8520020e
2 changed files with 66 additions and 26 deletions

View File

@@ -69,6 +69,7 @@
"icon": "Icon",
"color": "Color",
"error": "Error",
"enter": "Enter",
"enterSubgraph": "Enter Subgraph",
"resizeFromBottomRight": "Resize from bottom-right corner",
"resizeFromTopRight": "Resize from top-right corner",

View File

@@ -122,46 +122,85 @@
<NodeBadges v-bind="badges" :pricing="undefined" class="mt-auto" />
</div>
</template>
<Button
variant="textonly"
<div
v-if="
(hasAnyError && showErrorsTabEnabled) ||
lgraphNode?.isSubgraphNode() ||
showAdvancedState ||
showAdvancedInputsButton
"
:class="
cn(
'w-full h-7 rounded-b-2xl py-2 -z-1 text-xs rounded-t-none',
hasAnyError && 'hover:bg-destructive-background-hover',
!isCollapsed && '-mt-5 pt-7 h-12'
'flex w-full h-7 rounded-b-2xl -z-1 text-xs rounded-t-none overflow-hidden divide-x divide-component-node-border',
!isCollapsed && '-mt-5 h-12'
)
"
as-child
>
<button
v-if="hasAnyError && showErrorsTabEnabled"
@click.stop="useRightSidePanelStore().openPanel('errors')"
>
<span>{{ t('g.error') }}</span>
<i class="icon-[lucide--info] size-4" />
</button>
<button
v-else-if="lgraphNode?.isSubgraphNode()"
<Button
v-if="lgraphNode?.isSubgraphNode()"
variant="textonly"
:class="
cn(
'flex-1 rounded-none h-full',
hasAnyError &&
showErrorsTabEnabled &&
!nodeData.color &&
'bg-node-component-header-surface',
isCollapsed ? 'py-2' : 'pt-7 pb-2'
)
"
data-testid="subgraph-enter-button"
@click.stop="handleEnterSubgraph"
>
<span>{{ t('g.enterSubgraph') }}</span>
<i class="icon-[comfy--workflow] size-4" />
</button>
<button
v-else-if="showAdvancedState || showAdvancedInputsButton"
<span class="truncate">{{
hasAnyError && showErrorsTabEnabled
? t('g.enter')
: t('g.enterSubgraph')
}}</span>
<i class="icon-[comfy--workflow] size-4 shrink-0" />
</Button>
<Button
v-if="hasAnyError && showErrorsTabEnabled"
variant="textonly"
:class="
cn(
'flex-1 rounded-none h-full bg-error hover:bg-destructive-background-hover',
isCollapsed ? 'py-2' : 'pt-7 pb-2'
)
"
@click.stop="useRightSidePanelStore().openPanel('errors')"
>
<span class="truncate">{{ t('g.error') }}</span>
<i class="icon-[lucide--info] size-4 shrink-0" />
</Button>
<!-- Advanced inputs (non-subgraph nodes only) -->
<Button
v-if="
!lgraphNode?.isSubgraphNode() &&
(showAdvancedState || showAdvancedInputsButton)
"
variant="textonly"
:class="
cn('flex-1 rounded-none h-full', isCollapsed ? 'py-2' : 'pt-7 pb-2')
"
@click.stop="showAdvancedState = !showAdvancedState"
>
<template v-if="showAdvancedState">
<span>{{ t('rightSidePanel.hideAdvancedInputsButton') }}</span>
<i class="icon-[lucide--chevron-up] size-4" />
<span class="truncate">{{
t('rightSidePanel.hideAdvancedInputsButton')
}}</span>
<i class="icon-[lucide--chevron-up] size-4 shrink-0" />
</template>
<template v-else>
<span>{{ t('rightSidePanel.showAdvancedInputsButton') }} </span>
<i class="icon-[lucide--settings-2] size-4" />
<span class="truncate">{{
t('rightSidePanel.showAdvancedInputsButton')
}}</span>
<i class="icon-[lucide--settings-2] size-4 shrink-0" />
</template>
</button>
</Button>
</Button>
</div>
<template v-if="!isCollapsed && nodeData.resizable !== false">
<div
v-for="handle in RESIZE_HANDLES"