mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-30 01:05:46 +00:00
Compare commits
19 Commits
refactor/a
...
austin/bra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c523ba2c8 | ||
|
|
cd33b16061 | ||
|
|
dbd2767225 | ||
|
|
2e944a6be4 | ||
|
|
09a481588d | ||
|
|
1855d4cc03 | ||
|
|
b5007573b1 | ||
|
|
2a065c5093 | ||
|
|
58b795b419 | ||
|
|
8448821ee1 | ||
|
|
081a55007a | ||
|
|
3f684bd95c | ||
|
|
8b32d696f0 | ||
|
|
08799e8163 | ||
|
|
61760be9ff | ||
|
|
9f70912899 | ||
|
|
bd596997b8 | ||
|
|
98d0cca188 | ||
|
|
a125261b8a |
@@ -249,6 +249,7 @@
|
||||
--node-component-slot-dot-outline-opacity: 5%;
|
||||
--node-component-slot-dot-outline: var(--color-black);
|
||||
--node-component-slot-text: var(--color-ash-800);
|
||||
--node-component-slot-text-highlight: var(--color-black);
|
||||
--node-component-surface-highlight: var(--color-ash-500);
|
||||
--node-component-surface-hovered: var(--color-smoke-200);
|
||||
--node-component-surface-selected: var(--color-charcoal-200);
|
||||
@@ -391,6 +392,7 @@
|
||||
--node-component-slot-dot-outline-opacity: 10%;
|
||||
--node-component-slot-dot-outline: var(--color-white);
|
||||
--node-component-slot-text: var(--color-slate-200);
|
||||
--node-component-slot-text-highlight: var(--color-white);
|
||||
--node-component-surface-highlight: var(--color-slate-100);
|
||||
--node-component-surface-hovered: var(--color-charcoal-600);
|
||||
--node-component-surface-selected: var(--color-charcoal-200);
|
||||
@@ -537,6 +539,9 @@
|
||||
)
|
||||
);
|
||||
--color-node-component-slot-text: var(--node-component-slot-text);
|
||||
--color-node-component-slot-text-highlight: var(
|
||||
--node-component-slot-text-highlight
|
||||
);
|
||||
--color-node-component-surface-highlight: var(
|
||||
--node-component-surface-highlight
|
||||
);
|
||||
|
||||
@@ -572,6 +572,20 @@ function withComfyAutogrow(node: LGraphNode): asserts node is AutogrowNode {
|
||||
}
|
||||
}
|
||||
)
|
||||
// Restore renamed labels after configure (autogrow recreates inputs fresh)
|
||||
node.onConfigure = useChainCallback(
|
||||
node.onConfigure,
|
||||
(data: { inputs?: Array<{ label?: string; name: string }> }) => {
|
||||
if (!data?.inputs) return
|
||||
for (const serializedInput of data.inputs) {
|
||||
if (!serializedInput.label) continue
|
||||
const match = node.inputs.find(
|
||||
(inp) => inp.name === serializedInput.name
|
||||
)
|
||||
if (match) match.label = serializedInput.label
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
function applyAutogrow(node: LGraphNode, inputSpecV2: InputSpecV2) {
|
||||
withComfyAutogrow(node)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { shallowReactive } from 'vue'
|
||||
import { computed, ref, shallowReactive, watch } from 'vue'
|
||||
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
|
||||
@@ -124,6 +124,42 @@ function onCustomComboCreated(this: LGraphNode) {
|
||||
addOption(this)
|
||||
}
|
||||
|
||||
const renameTrigger = ref(0)
|
||||
function onBranchSelectorCreated(this: LGraphNode) {
|
||||
this.applyToGraph = applyToGraph
|
||||
|
||||
//FIXME: watchers probably leak since we're not in a vue component
|
||||
const connectionsTrigger = ref(0)
|
||||
this.widgets?.pop()
|
||||
const labels = computed(() => {
|
||||
void renameTrigger.value
|
||||
void connectionsTrigger.value
|
||||
return this.inputs.slice(0, -2).map((inp) => inp.label)
|
||||
})
|
||||
const values = () => labels.value
|
||||
const node = this
|
||||
|
||||
const comboWidget = this.addWidget('combo', 'branch', '', () => {}, {
|
||||
values
|
||||
})
|
||||
watch([renameTrigger, connectionsTrigger], () => {
|
||||
if (app.configuringGraph || labels.value.includes(`${comboWidget.value}`))
|
||||
return
|
||||
|
||||
comboWidget.value = labels.value[0] ?? ''
|
||||
comboWidget.callback?.(comboWidget.value)
|
||||
})
|
||||
|
||||
comboWidget.serializeValue = () =>
|
||||
node.inputs.slice(0, -1).findIndex((inp) => inp.label === comboWidget.value)
|
||||
|
||||
// Refresh on connection changes (add/remove inputs)
|
||||
this.onConnectionsChange = useChainCallback(
|
||||
this.onConnectionsChange,
|
||||
() => connectionsTrigger.value++
|
||||
)
|
||||
}
|
||||
|
||||
function onCustomIntCreated(this: LGraphNode) {
|
||||
const valueWidget = this.widgets?.[0]
|
||||
if (!valueWidget) return
|
||||
@@ -227,6 +263,11 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated,
|
||||
onCustomComboCreated
|
||||
)
|
||||
else if (nodeData?.name === 'BranchNode')
|
||||
nodeType.prototype.onNodeCreated = useChainCallback(
|
||||
nodeType.prototype.onNodeCreated,
|
||||
onBranchSelectorCreated
|
||||
)
|
||||
else if (nodeData?.name === 'PrimitiveInt')
|
||||
nodeType.prototype.onNodeCreated = useChainCallback(
|
||||
nodeType.prototype.onNodeCreated,
|
||||
@@ -237,5 +278,11 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated,
|
||||
onCustomFloatCreated
|
||||
)
|
||||
},
|
||||
init() {
|
||||
app.graph.onTrigger = useChainCallback(app.graph.onTrigger, (e) => {
|
||||
if (e.type !== 'node:slot-label:changed') return
|
||||
renameTrigger.value++
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -97,6 +97,7 @@ import {
|
||||
LinkDirection,
|
||||
LinkMarkerShape,
|
||||
LinkRenderType,
|
||||
NodeSlotType,
|
||||
RenderShape,
|
||||
TitleMode
|
||||
} from './types/globalEnums'
|
||||
@@ -8803,6 +8804,10 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
if (input?.value) {
|
||||
if (slot_info) {
|
||||
slot_info.label = input.value
|
||||
node.graph?.trigger('node:slot-label:changed', {
|
||||
nodeId: node.id,
|
||||
slotType: info.input ? NodeSlotType.INPUT : NodeSlotType.OUTPUT
|
||||
})
|
||||
}
|
||||
setDirty()
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
<div v-if="renderError" class="node-error p-1 text-xs text-red-500">⚠️</div>
|
||||
<div
|
||||
v-else
|
||||
v-tooltip.left="tooltipConfig"
|
||||
:class="
|
||||
cn(
|
||||
'lg-slot lg-slot--input group m-0 flex items-center rounded-r-lg',
|
||||
'lg-slot lg-slot--input group/slot m-0 flex items-center rounded-r-lg',
|
||||
'cursor-crosshair',
|
||||
dotOnly ? 'lg-slot--dot-only' : 'pr-6',
|
||||
{
|
||||
@@ -20,6 +19,7 @@
|
||||
<!-- Connection Dot -->
|
||||
<SlotConnectionDot
|
||||
ref="connectionDotRef"
|
||||
v-tooltip.left="tooltipConfig"
|
||||
:class="
|
||||
cn(
|
||||
'w-3 -translate-x-1/2',
|
||||
@@ -31,40 +31,51 @@
|
||||
@click="onClick"
|
||||
@dblclick="onDoubleClick"
|
||||
@pointerdown="onPointerDown"
|
||||
@contextmenu.stop.prevent="startRename"
|
||||
/>
|
||||
|
||||
<!-- Slot Name -->
|
||||
<div class="flex h-full min-w-0 items-center">
|
||||
<input
|
||||
v-if="isRenaming"
|
||||
ref="renameInputRef"
|
||||
v-model="renameValue"
|
||||
class="m-0 w-full truncate border-none bg-transparent p-0 text-[length:inherit] leading-[inherit] text-node-component-slot-text outline-none"
|
||||
@blur="finishRename"
|
||||
@keydown.enter.prevent="finishRename"
|
||||
@keydown.escape.prevent="cancelRename"
|
||||
@click.stop
|
||||
@pointerdown.stop
|
||||
/>
|
||||
<span
|
||||
v-if="!props.dotOnly && !hasNoLabel"
|
||||
v-else-if="!props.dotOnly && !hasNoLabel"
|
||||
:class="
|
||||
cn(
|
||||
'truncate text-node-component-slot-text',
|
||||
'truncate text-node-component-slot-text hover:text-node-component-slot-text-highlight',
|
||||
hasError && 'font-medium text-error'
|
||||
)
|
||||
"
|
||||
@dblclick.stop="startRename"
|
||||
>
|
||||
{{
|
||||
slotData.label ||
|
||||
slotData.localized_name ||
|
||||
(slotData.name ?? `Input ${index}`)
|
||||
}}
|
||||
{{ displayLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onErrorCaptured, ref, watchEffect } from 'vue'
|
||||
import { computed, nextTick, onErrorCaptured, ref, watchEffect } from 'vue'
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
|
||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||
import { useSlotLinkDragUIState } from '@/renderer/core/canvas/links/slotLinkDragUIState'
|
||||
import { getSlotKey } from '@/renderer/core/layout/slots/slotIdentifier'
|
||||
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
|
||||
import { useSlotElementTracking } from '@/renderer/extensions/vueNodes/composables/useSlotElementTracking'
|
||||
import { useSlotLinkInteraction } from '@/renderer/extensions/vueNodes/composables/useSlotLinkInteraction'
|
||||
import { app } from '@/scripts/app'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import SlotConnectionDot from './SlotConnectionDot.vue'
|
||||
@@ -83,6 +94,15 @@ interface InputSlotProps {
|
||||
|
||||
const props = defineProps<InputSlotProps>()
|
||||
|
||||
const labelOverride = ref<string | null>(null)
|
||||
const displayLabel = computed(
|
||||
() =>
|
||||
labelOverride.value ||
|
||||
props.slotData.label ||
|
||||
props.slotData.localized_name ||
|
||||
(props.slotData.name ?? `Input ${props.index}`)
|
||||
)
|
||||
|
||||
const hasNoLabel = computed(
|
||||
() =>
|
||||
!props.slotData.label &&
|
||||
@@ -142,4 +162,48 @@ const { onClick, onDoubleClick, onPointerDown } = useSlotLinkInteraction({
|
||||
index: props.index,
|
||||
type: 'input'
|
||||
})
|
||||
|
||||
// ── Inline rename ─────────────────────────────────────────────
|
||||
const isRenaming = ref(false)
|
||||
const renameValue = ref('')
|
||||
const renameInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
function startRename() {
|
||||
if (props.slotData.nameLocked) return
|
||||
renameValue.value = displayLabel.value
|
||||
isRenaming.value = true
|
||||
nextTick(() => {
|
||||
renameInputRef.value?.select()
|
||||
})
|
||||
}
|
||||
|
||||
let renameCommitted = false
|
||||
|
||||
function finishRename() {
|
||||
if (!isRenaming.value || renameCommitted) return
|
||||
renameCommitted = true
|
||||
|
||||
const newLabel = renameValue.value.trim()
|
||||
const node = app.canvas?.graph?.getNodeById(props.nodeId ?? '')
|
||||
const slot = node?.inputs?.[props.index]
|
||||
|
||||
if (newLabel && newLabel !== displayLabel.value && slot) {
|
||||
slot.label = newLabel
|
||||
labelOverride.value = newLabel
|
||||
node?.graph?.trigger('node:slot-label:changed', {
|
||||
nodeId: node.id,
|
||||
slotType: NodeSlotType.INPUT
|
||||
})
|
||||
app.canvas?.setDirty(true, true)
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
isRenaming.value = false
|
||||
renameCommitted = false
|
||||
})
|
||||
}
|
||||
|
||||
function cancelRename() {
|
||||
isRenaming.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user