Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions src/components/LiteGraphCanvasSplitterOverlay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Splitter
:key="splitterRefreshKey"
class="bg-transparent pointer-events-none border-none flex-1 overflow-hidden"
:state-key="sidebarStateKey"
:state-key="isSelectMode ? 'builder-splitter' : sidebarStateKey"
state-storage="local"
@resizestart="onResizestart"
>
Expand All @@ -35,8 +35,10 @@
)
: 'bg-comfy-menu-bg pointer-events-auto'
"
:min-size="sidebarLocation === 'left' ? 10 : 15"
:size="20"
:min-size="
sidebarLocation === 'left' ? SIDEBAR_MIN_SIZE : BUILDER_MIN_SIZE
"
:size="SIDE_PANEL_SIZE"
:style="firstPanelStyle"
:role="sidebarLocation === 'left' ? 'complementary' : undefined"
:aria-label="
Expand All @@ -54,7 +56,7 @@
</SplitterPanel>

<!-- Main panel (always present) -->
<SplitterPanel :size="80" class="flex flex-col">
<SplitterPanel :size="CENTER_PANEL_SIZE" class="flex flex-col">
<slot name="topmenu" :sidebar-panel-visible />

<Splitter
Expand Down Expand Up @@ -95,8 +97,10 @@
)
: 'bg-comfy-menu-bg pointer-events-auto'
"
:min-size="sidebarLocation === 'right' ? 10 : 15"
:size="20"
:min-size="
sidebarLocation === 'right' ? SIDEBAR_MIN_SIZE : BUILDER_MIN_SIZE
"
:size="SIDE_PANEL_SIZE"
:style="lastPanelStyle"
:role="sidebarLocation === 'right' ? 'complementary' : undefined"
:aria-label="
Expand All @@ -123,8 +127,14 @@ import SplitterPanel from 'primevue/splitterpanel'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

import { useSettingStore } from '@/platform/settings/settingStore'
import { useAppMode } from '@/composables/useAppMode'
import {
BUILDER_MIN_SIZE,
CENTER_PANEL_SIZE,
SIDEBAR_MIN_SIZE,
SIDE_PANEL_SIZE
} from '@/constants/splitterConstants'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
Expand All @@ -145,12 +155,12 @@ const unifiedWidth = computed(() =>

const { focusMode } = storeToRefs(workspaceStore)

const { mode } = useAppMode()
const { isSelectMode } = useAppMode()
const { activeSidebarTabId, activeSidebarTab } = storeToRefs(sidebarTabStore)
const { bottomPanelVisible } = storeToRefs(useBottomPanelStore())
const { isOpen: rightSidePanelVisible } = storeToRefs(rightSidePanelStore)
const showOffsideSplitter = computed(
() => rightSidePanelVisible.value || mode.value === 'builder:select'
() => rightSidePanelVisible.value || isSelectMode.value
)

const sidebarPanelVisible = computed(() => activeSidebarTab.value !== null)
Expand All @@ -174,7 +184,7 @@ function onResizestart({ originalEvent: event }: SplitterResizeStartEvent) {
* to recalculate the width and panel order
*/
const splitterRefreshKey = computed(() => {
return `main-splitter${rightSidePanelVisible.value ? '-with-right-panel' : ''}-${sidebarLocation.value}`
return `main-splitter${rightSidePanelVisible.value ? '-with-right-panel' : ''}${isSelectMode.value ? '-builder' : ''}-${sidebarLocation.value}`
})

const firstPanelStyle = computed(() => {
Expand Down
60 changes: 57 additions & 3 deletions src/components/builder/AppBuilder.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script setup lang="ts">
import { remove } from 'es-toolkit'
import { computed, ref, toValue } from 'vue'
import { computed, provide, ref, toValue, watchEffect } from 'vue'
import type { MaybeRef } from 'vue'
import { useI18n } from 'vue-i18n'

import DraggableList from '@/components/common/DraggableList.vue'
import IoItem from '@/components/builder/IoItem.vue'
import PropertiesAccordionItem from '@/components/rightSidePanel/layout/PropertiesAccordionItem.vue'
import WidgetItem from '@/components/rightSidePanel/parameters/WidgetItem.vue'
import Button from '@/components/ui/button/Button.vue'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'
Expand All @@ -23,8 +24,10 @@ import TransformPane from '@/renderer/core/layout/transform/TransformPane.vue'
import { app } from '@/scripts/app'
import { DOMWidgetImpl } from '@/scripts/domWidget'
import { useDialogService } from '@/services/dialogService'
import { useAppMode } from '@/composables/useAppMode'
import { useAppModeStore } from '@/stores/appModeStore'
import { cn } from '@/utils/tailwindUtil'
import { HideLayoutFieldKey } from '@/types/widgetTypes'

type BoundStyle = { top: string; left: string; width: string; height: string }

Expand All @@ -36,10 +39,36 @@ const workflowStore = useWorkflowStore()
const { t } = useI18n()
const canvas: LGraphCanvas = canvasStore.getCanvas()

const { mode, isArrangeMode } = useAppMode()
const hoveringSelectable = ref(false)

provide(HideLayoutFieldKey, true)

workflowStore.activeWorkflow?.changeTracker?.reset()

// Prune stale entries whose node/widget no longer exists, so the
// DraggableList model always matches the rendered items.
watchEffect(() => {
const valid = appModeStore.selectedInputs.filter(([nodeId, widgetName]) => {
const node = app.rootGraph.getNodeById(nodeId)
return node?.widgets?.some((w) => w.name === widgetName)
})
if (valid.length < appModeStore.selectedInputs.length) {
appModeStore.selectedInputs = valid
}
})

const arrangeInputs = computed(() =>
appModeStore.selectedInputs
.map(([nodeId, widgetName]) => {
const node = app.rootGraph.getNodeById(nodeId)
const widget = node?.widgets?.find((w) => w.name === widgetName)
if (!node || !widget) return null
return { nodeId, widgetName, node, widget }
})
.filter((item): item is NonNullable<typeof item> => item !== null)
)

const inputsWithState = computed(() =>
appModeStore.selectedInputs.map(([nodeId, widgetName]) => {
const node = app.rootGraph.getNodeById(nodeId)
Expand Down Expand Up @@ -179,12 +208,36 @@ const renderedInputs = computed<[string, MaybeRef<BoundStyle> | undefined][]>(
</script>
<template>
<div class="flex font-bold p-2 border-border-subtle border-b items-center">
{{ t('linearMode.builder.title') }}
{{
isArrangeMode ? t('nodeHelpPage.inputs') : t('linearMode.builder.title')
}}
<Button class="ml-auto" @click="appModeStore.exitBuilder">
{{ t('linearMode.builder.exit') }}
</Button>
</div>
<DraggableList
v-if="isArrangeMode"
v-slot="{ dragClass }"
v-model="appModeStore.selectedInputs"
>
<div
v-for="{ nodeId, widgetName, node, widget } in arrangeInputs"
:key="`${nodeId}: ${widgetName}`"
:class="cn(dragClass, 'p-2 my-2 pointer-events-auto')"
:aria-label="`${widget.label ?? widgetName} — ${node.title}`"
>
<div class="pointer-events-none" inert>
<WidgetItem
:widget="widget"
:node="node"
show-node-name
hidden-widget-actions
/>
</div>
</div>
</DraggableList>
<PropertiesAccordionItem
v-else
:label="t('nodeHelpPage.inputs')"
enable-empty-state
:disabled="!appModeStore.selectedInputs.length"
Expand Down Expand Up @@ -232,6 +285,7 @@ const renderedInputs = computed<[string, MaybeRef<BoundStyle> | undefined][]>(
</DraggableList>
</PropertiesAccordionItem>
<PropertiesAccordionItem
v-if="!isArrangeMode"
:label="t('nodeHelpPage.outputs')"
enable-empty-state
:disabled="!appModeStore.selectedOutputs.length"
Expand Down Expand Up @@ -274,7 +328,7 @@ const renderedInputs = computed<[string, MaybeRef<BoundStyle> | undefined][]>(
</DraggableList>
</PropertiesAccordionItem>

<Teleport to="body">
<Teleport v-if="mode === 'builder:select'" to="body">
<div
:class="
cn(
Expand Down
7 changes: 6 additions & 1 deletion src/components/rightSidePanel/parameters/WidgetItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const {
node,
isDraggable = false,
hiddenFavoriteIndicator = false,
hiddenWidgetActions = false,
showNodeName = false,
parents = [],
isShownOnParents = false
Expand All @@ -42,6 +43,7 @@ const {
node: LGraphNode
isDraggable?: boolean
hiddenFavoriteIndicator?: boolean
hiddenWidgetActions?: boolean
showNodeName?: boolean
parents?: SubgraphNode[]
isShownOnParents?: boolean
Expand Down Expand Up @@ -170,7 +172,10 @@ const displayLabel = customRef((track, trigger) => {
>
{{ sourceNodeName }}
</span>
<div class="flex items-center gap-1 shrink-0 pointer-events-auto">
<div
v-if="!hiddenWidgetActions"
class="flex items-center gap-1 shrink-0 pointer-events-auto"
>
<WidgetActions
v-model:label="displayLabel"
:widget="widget"
Expand Down
6 changes: 5 additions & 1 deletion src/composables/useAppMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ export function useAppMode() {
)

const isBuilderMode = computed(
() => mode.value === 'builder:select' || mode.value === 'builder:arrange'
() => isSelectMode.value || isArrangeMode.value
)
const isSelectMode = computed(() => mode.value === 'builder:select')
const isArrangeMode = computed(() => mode.value === 'builder:arrange')
const isAppMode = computed(
() => mode.value === 'app' || mode.value === 'builder:arrange'
)
Expand All @@ -36,6 +38,8 @@ export function useAppMode() {
mode,
enableAppBuilder,
isBuilderMode,
isSelectMode,
isArrangeMode,
isAppMode,
isGraphMode,
setMode
Expand Down
11 changes: 11 additions & 0 deletions src/constants/splitterConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** Default panel size (%) for sidebar and builder panels */
export const SIDE_PANEL_SIZE = 20

/** Default panel size (%) for the center/main panel */
export const CENTER_PANEL_SIZE = 80

/** Minimum panel size (%) for the sidebar */
export const SIDEBAR_MIN_SIZE = 10

/** Minimum panel size (%) for the builder panel */
export const BUILDER_MIN_SIZE = 15
Loading
Loading