Add App I/O selection system (#8965)

Adds a system for selecting the inputs and outputs which should be
displayed when inside linear mode. Functions only in litegraph
currently. Vue support will require a separate, larger PR.
Inputs and outputs can be re-ordered by dragging and dropping on the
side panel.

![builder_00001](https://github.com/user-attachments/assets/6345adbd-519e-455d-b71e-0020aa03c6b7)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8965-Add-App-I-O-selection-system-30b6d73d365081569b36c1682a1fdbc5)
by [Unito](https://www.unito.io)
This commit is contained in:
AustinMroz
2026-02-25 08:53:00 -08:00
committed by GitHub
parent 4689581674
commit 1ab48b42a7
14 changed files with 572 additions and 72 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useEventListener, useTimeout } from '@vueuse/core'
import { partition } from 'es-toolkit'
import { partition, remove, takeWhile } from 'es-toolkit'
import { storeToRefs } from 'pinia'
import { computed, ref, shallowRef } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -55,6 +55,27 @@ useEventListener(
() => (graphNodes.value = app.rootGraph.nodes)
)
const mappedSelections = computed(() => {
let unprocessedInputs = [...appModeStore.selectedInputs]
//FIXME strict typing here
const processedInputs: ReturnType<typeof nodeToNodeData>[] = []
while (unprocessedInputs.length) {
const nodeId = unprocessedInputs[0][0]
const inputGroup = takeWhile(
unprocessedInputs,
([id]) => id === nodeId
).map(([, widgetName]) => widgetName)
unprocessedInputs = unprocessedInputs.slice(inputGroup.length)
const node = app.rootGraph.getNodeById(nodeId)
if (!node) continue
const nodeData = nodeToNodeData(node)
remove(nodeData.widgets ?? [], (w) => !inputGroup.includes(w.name))
processedInputs.push(nodeData)
}
return processedInputs
})
function getDropIndicator(node: LGraphNode) {
if (node.type !== 'LoadImage') return undefined
@@ -231,11 +252,13 @@ defineExpose({ runButtonClick })
class="grow-1 md:overflow-y-auto md:contain-size"
>
<template
v-for="(nodeData, index) of partitionedNodes[1]"
v-for="(nodeData, index) of appModeStore.selectedInputs.length
? mappedSelections
: partitionedNodes[0]"
:key="nodeData.id"
>
<div
v-if="index !== 0"
v-if="index !== 0 && !appModeStore.selectedInputs.length"
class="w-full border-t-1 border-node-component-border"
/>
<DropZone