Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/components/LiteGraphCanvasSplitterOverlay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

import { useSettingStore } from '@/platform/settings/settingStore'
import { useAppModeStore } from '@/stores/appModeStore'
import { useAppMode } from '@/composables/useAppMode'
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
Expand All @@ -145,12 +145,12 @@ const unifiedWidth = computed(() =>

const { focusMode } = storeToRefs(workspaceStore)

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

const sidebarPanelVisible = computed(() => activeSidebarTab.value !== null)
Expand Down
8 changes: 4 additions & 4 deletions src/components/appMode/AppModeToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemp
import { useCommandStore } from '@/stores/commandStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { cn } from '@/utils/tailwindUtil'
import { useAppModeStore } from '@/stores/appModeStore'
import { useAppMode } from '@/composables/useAppMode'

const { t } = useI18n()
const commandStore = useCommandStore()
const workspaceStore = useWorkspaceStore()
const appModeStore = useAppModeStore()
const { enableAppBuilder, setMode } = useAppMode()
const tooltipOptions = { showDelay: 300, hideDelay: 300 }

const isAssetsActive = computed(
Expand All @@ -24,7 +24,7 @@ const isWorkflowsActive = computed(
)

function enterBuilderMode() {
appModeStore.setMode('builder:select')
setMode('builder:select')
}

function openAssets() {
Expand Down Expand Up @@ -61,7 +61,7 @@ function openTemplates() {
</WorkflowActionsDropdown>

<Button
v-if="appModeStore.enableAppBuilder"
v-if="enableAppBuilder"
v-tooltip.right="{
value: t('linearMode.appModeToolbar.appBuilder'),
...tooltipOptions
Expand Down
30 changes: 12 additions & 18 deletions src/components/builder/BuilderToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
"
:aria-current="activeStep === step.id ? 'step' : undefined"
@click="appModeStore.setMode(step.id)"
@click="setMode(step.id)"
>
<StepBadge :step :index :model-value="activeStep" />
<StepLabel :step />
Expand All @@ -31,9 +31,9 @@

<!-- Save -->
<ConnectOutputPopover
v-if="!appModeStore.hasOutputs"
v-if="!hasOutputs"
:is-select-active="activeStep === 'builder:select'"
@switch="appModeStore.setMode('builder:select')"
@switch="setMode('builder:select')"
>
<button :class="cn(stepClasses, 'opacity-30 bg-transparent')">
<StepBadge :step="saveStep" :index="2" :model-value="activeStep" />
Expand All @@ -50,7 +50,7 @@
: 'hover:bg-secondary-background bg-transparent'
)
"
@click="appModeStore.setBuilderSaving(true)"
@click="setSaving(true)"
>
<StepBadge :step="saveStep" :index="2" :model-value="activeStep" />
<StepLabel :step="saveStep" />
Expand All @@ -62,31 +62,25 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useEventListener } from '@vueuse/core'

import { useAppMode } from '@/composables/useAppMode'
import type { AppMode } from '@/composables/useAppMode'
import { useAppModeStore } from '@/stores/appModeStore'
import type { AppMode } from '@/stores/appModeStore'
import { cn } from '@/utils/tailwindUtil'

import { useBuilderSave } from './useBuilderSave'
import ConnectOutputPopover from './ConnectOutputPopover.vue'
import StepBadge from './StepBadge.vue'
import StepLabel from './StepLabel.vue'
import type { BuilderToolbarStep } from './types'
import { storeToRefs } from 'pinia'

const { t } = useI18n()
const appModeStore = useAppModeStore()
const { mode, setMode } = useAppMode()
const { hasOutputs } = storeToRefs(useAppModeStore())
const { saving, setSaving } = useBuilderSave()

useEventListener(document, 'keydown', (e: KeyboardEvent) => {
if (e.key !== 'Escape') return

e.preventDefault()
e.stopPropagation()
void appModeStore.exitBuilder()
})

const activeStep = computed(() =>
appModeStore.isBuilderSaving ? 'save' : appModeStore.mode
)
const activeStep = computed(() => (saving.value ? 'save' : mode.value))

const stepClasses =
'inline-flex h-14 min-h-8 cursor-pointer items-center gap-3 rounded-lg py-2 pr-4 pl-2 transition-colors border-none'
Expand Down
48 changes: 30 additions & 18 deletions src/components/builder/useBuilderSave.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { watch } from 'vue'
import { ref, watch } from 'vue'

import { useErrorHandling } from '@/composables/useErrorHandling'
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { useDialogService } from '@/services/dialogService'
import { useAppMode } from '@/composables/useAppMode'
import { useAppModeStore } from '@/stores/appModeStore'
import { useDialogStore } from '@/stores/dialogStore'

Expand All @@ -13,18 +15,23 @@ const SAVE_DIALOG_KEY = 'builder-save'
const SUCCESS_DIALOG_KEY = 'builder-save-success'

export function useBuilderSave() {
const appModeStore = useAppModeStore()
const { setMode } = useAppMode()
const { toastErrorHandler } = useErrorHandling()
const workflowStore = useWorkflowStore()
const workflowService = useWorkflowService()
const dialogService = useDialogService()
const appModeStore = useAppModeStore()
const dialogStore = useDialogStore()

watch(
() => appModeStore.isBuilderSaving,
(saving) => {
if (saving) void onBuilderSave()
}
)
const saving = ref(false)

watch(saving, (value) => {
if (value) void onBuilderSave()
})

function setSaving(value: boolean) {
saving.value = value
}

async function onBuilderSave() {
const workflow = workflowStore.activeWorkflow
Expand All @@ -33,13 +40,14 @@ export function useBuilderSave() {
return
}

if (!workflow.isTemporary && workflow.activeState.extra?.linearMode) {
if (!workflow.isTemporary && workflow.initialMode != null) {
// Re-save with the previously chosen mode — no dialog needed.
try {
workflow.changeTracker?.checkState()
appModeStore.saveSelectedToWorkflow()
appModeStore.flushSelections()
await workflowService.saveWorkflow(workflow)
showSuccessDialog(workflow.filename, appModeStore.isAppMode)
} catch {
showSuccessDialog(workflow.filename, workflow.initialMode === 'app')
} catch (e) {
toastErrorHandler(e)
resetSaving()
}
return
Expand Down Expand Up @@ -73,17 +81,19 @@ export function useBuilderSave() {
const workflow = workflowStore.activeWorkflow
if (!workflow) return

appModeStore.saveSelectedToWorkflow()
appModeStore.flushSelections()
const mode = openAsApp ? 'app' : 'graph'
const saved = await workflowService.saveWorkflowAs(workflow, {
filename,
openAsApp
initialMode: mode
})

if (!saved) return

closeSaveDialog()
showSuccessDialog(filename, openAsApp)
} catch {
} catch (e) {
toastErrorHandler(e)
closeSaveDialog()
resetSaving()
}
Expand All @@ -97,7 +107,7 @@ export function useBuilderSave() {
workflowName,
savedAsApp,
onViewApp: () => {
appModeStore.setMode('app')
setMode('app')
closeSuccessDialog()
},
onClose: closeSuccessDialog
Expand All @@ -118,6 +128,8 @@ export function useBuilderSave() {
}

function resetSaving() {
appModeStore.setBuilderSaving(false)
saving.value = false
}

return { saving, setSaving }
}
21 changes: 9 additions & 12 deletions src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</div>
</div>
</template>
<template v-if="showUI && !appModeStore.isBuilderMode" #side-toolbar>
<template v-if="showUI && !isBuilderMode" #side-toolbar>
<SideToolbar />
</template>
<template v-if="showUI" #side-bar-panel>
Expand All @@ -31,27 +31,24 @@
<ExtensionSlot v-if="activeSidebarTab" :extension="activeSidebarTab" />
</div>
</template>
<template v-if="showUI && !appModeStore.isBuilderMode" #topmenu>
<template v-if="showUI && !isBuilderMode" #topmenu>
<TopMenuSection />
</template>
<template v-if="showUI" #bottom-panel>
<BottomPanel />
</template>
<template v-if="showUI" #right-side-panel>
<AppBuilder v-if="appModeStore.mode === 'builder:select'" />
<NodePropertiesPanel v-else-if="!appModeStore.isBuilderMode" />
<AppBuilder v-if="mode === 'builder:select'" />
<NodePropertiesPanel v-else-if="!isBuilderMode" />
</template>
<template #graph-canvas-panel>
<GraphCanvasMenu
v-if="canvasMenuEnabled && !appModeStore.isBuilderMode"
v-if="canvasMenuEnabled && !isBuilderMode"
class="pointer-events-auto"
/>
<MiniMap
v-if="
comfyAppReady &&
minimapEnabled &&
betaMenuEnabled &&
!appModeStore.isBuilderMode
comfyAppReady && minimapEnabled && betaMenuEnabled && !isBuilderMode
"
class="pointer-events-auto"
/>
Expand Down Expand Up @@ -127,10 +124,10 @@ import {
import { useI18n } from 'vue-i18n'

import { isMiddlePointerInput } from '@/base/pointerUtils'
import AppBuilder from '@/components/builder/AppBuilder.vue'
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
import TopMenuSection from '@/components/TopMenuSection.vue'
import BottomPanel from '@/components/bottomPanel/BottomPanel.vue'
import AppBuilder from '@/components/builder/AppBuilder.vue'
import ExtensionSlot from '@/components/common/ExtensionSlot.vue'
import DomWidgets from '@/components/graph/DomWidgets.vue'
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
Expand Down Expand Up @@ -184,7 +181,7 @@ import { useExecutionErrorStore } from '@/stores/executionErrorStore'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
import { useAppModeStore } from '@/stores/appModeStore'
import { useAppMode } from '@/composables/useAppMode'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { isNativeWindow } from '@/utils/envUtil'
import { forEachNode } from '@/utils/graphTraversalUtil'
Expand All @@ -205,7 +202,7 @@ const nodeSearchboxPopoverRef = shallowRef<InstanceType<
const settingStore = useSettingStore()
const nodeDefStore = useNodeDefStore()
const workspaceStore = useWorkspaceStore()
const appModeStore = useAppModeStore()
const { mode, isBuilderMode } = useAppMode()
const canvasStore = useCanvasStore()
const workflowStore = useWorkflowStore()
const executionStore = useExecutionStore()
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar/tabs/SidebarTabTemplate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
ref="containerRef"
:class="
cn(
'comfy-vue-side-bar-container group/sidebar-tab flex h-full flex-col',
'comfy-vue-side-bar-container group/sidebar-tab flex h-full flex-col w-full',
props.class
)
"
Expand Down
2 changes: 1 addition & 1 deletion src/components/topbar/WorkflowTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@click="handleClick"
>
<i
v-if="workflowOption.workflow.activeState?.extra?.linearMode"
v-if="workflowOption.workflow.initialMode === 'app'"
class="icon-[lucide--panels-top-left] bg-primary-background"
/>
<span
Expand Down
12 changes: 8 additions & 4 deletions src/components/ui/TypeformPopoverButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ whenever(feedbackRef, () => {
:href="`https://form.typeform.com/to/${dataTfWidget}`"
target="_blank"
variant="inverted"
class="rounded-full size-12"
class="flex h-10 items-center justify-center gap-2.5 px-3 py-2"
v-bind="$attrs"
>
<i class="icon-[lucide--circle-question-mark] size-6" />
<i class="icon-[lucide--circle-help] size-4" />
</Button>
<Popover v-else>
<template #button>
<Button variant="inverted" class="rounded-full size-12" v-bind="$attrs">
<i class="icon-[lucide--circle-question-mark] size-6" />
<Button
variant="inverted"
class="flex h-10 items-center justify-center gap-2.5 px-3 py-2"
v-bind="$attrs"
>
<i class="icon-[lucide--circle-help] size-4" />
</Button>
</template>
<div ref="feedbackRef" data-tf-auto-resize :data-tf-widget />
Expand Down
43 changes: 43 additions & 0 deletions src/composables/useAppMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { computed, ref } from 'vue'

import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'

export type AppMode = 'graph' | 'app' | 'builder:select' | 'builder:arrange'

const enableAppBuilder = ref(true)

export function useAppMode() {
const workflowStore = useWorkflowStore()
const mode = computed(
() =>
workflowStore.activeWorkflow?.activeMode ??
workflowStore.activeWorkflow?.initialMode ??
'graph'
)

const isBuilderMode = computed(
() => mode.value === 'builder:select' || mode.value === 'builder:arrange'
)
const isAppMode = computed(
() => mode.value === 'app' || mode.value === 'builder:arrange'
)
const isGraphMode = computed(
() => mode.value === 'graph' || mode.value === 'builder:select'
)

function setMode(newMode: AppMode) {
if (newMode === mode.value) return

const workflow = workflowStore.activeWorkflow
if (workflow) workflow.activeMode = newMode
}

return {
mode,
enableAppBuilder,
isBuilderMode,
isAppMode,
isGraphMode,
setMode
}
}
2 changes: 0 additions & 2 deletions src/composables/useCoreCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1338,8 +1338,6 @@ export function useCoreCommands(): ComfyCommand[] {
typeof metadata?.source === 'string' ? metadata.source : 'keybind'
const newMode = !canvasStore.linearMode
if (newMode) useTelemetry()?.trackEnterLinear({ source })
app.rootGraph.extra.linearMode = newMode
workflowStore.activeWorkflow?.changeTracker?.checkState()
canvasStore.linearMode = newMode
}
}
Expand Down
Loading
Loading