Files
ComfyUI_frontend/src/renderer/extensions/linearMode/LinearWelcome.vue
pythongosssss 5376b7ed1e feat: App mode empty graph handling (#9393)
## Summary

Adds handling for entering app mode with an empty graph prompting the
user to load a template as a starting point

## Changes

- **What**: 
- app mode handle empty workflows, disable builder button, show
different message
- fix fitView when switching from app mode to graph

## Review Focus

Moving the fitView since the canvas is hidden in app mode until after
the workflow is loaded and the mode has been switched back to graph, I
don't see how this could cause any issues but worth a closer eye

## Screenshots (if applicable)

<img width="1057" height="916" alt="image"
src="https://github.com/user-attachments/assets/2ffe2b6d-9ce1-4218-828a-b7bc336c365a"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9393-feat-App-mode-empty-graph-handling-3196d73d3650812cab0ce878109ed5c9)
by [Unito](https://www.unito.io)
2026-03-05 02:27:05 -08:00

80 lines
2.8 KiB
Vue

<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { useAppMode } from '@/composables/useAppMode'
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
import { useAppModeStore } from '@/stores/appModeStore'
import Button from '@/components/ui/button/Button.vue'
import { storeToRefs } from 'pinia'
const { t } = useI18n()
const { setMode } = useAppMode()
const appModeStore = useAppModeStore()
const { hasOutputs, hasNodes } = storeToRefs(appModeStore)
const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
</script>
<template>
<div
role="article"
data-testid="linear-welcome"
class="flex flex-col items-center justify-center h-full gap-6 p-8 max-w-lg mx-auto text-center"
>
<div class="flex flex-col gap-2">
<h2 class="text-3xl font-semibold text-muted-foreground">
{{ t('linearMode.welcome.title') }}
</h2>
</div>
<div class="flex flex-col gap-3 text-muted-foreground max-w-md text-[14px]">
<p class="mt-0">{{ t('linearMode.welcome.message') }}</p>
<p class="mt-0">{{ t('linearMode.welcome.controls') }}</p>
<p class="mt-0">{{ t('linearMode.welcome.sharing') }}</p>
</div>
<div v-if="hasOutputs" class="flex flex-row gap-2 text-[14px]">
<p class="mt-0 text-base-foreground">
<i18n-t keypath="linearMode.welcome.getStarted" tag="span">
<template #runButton>
<span
class="inline-flex items-center px-3.5 py-0.5 mx-0.5 transform -translate-y-0.5 rounded-sm bg-primary-background text-base-foreground text-xxs font-medium cursor-default"
>
{{ t('menu.run') }}
</span>
</template>
</i18n-t>
</p>
</div>
<template v-else>
<p v-if="!hasNodes" class="mt-0 text-base-foreground text-sm max-w-md">
{{ t('linearMode.emptyWorkflowExplanation') }}
</p>
<div class="flex flex-row gap-2">
<Button variant="textonly" size="lg" @click="setMode('graph')">
{{ t('linearMode.backToWorkflow') }}
</Button>
<Button
v-if="!hasNodes"
variant="secondary"
size="lg"
@click="templateSelectorDialog.show('appbuilder')"
>
{{ t('linearMode.loadTemplate') }}
</Button>
<Button
v-else
variant="primary"
size="lg"
@click="appModeStore.enterBuilder()"
>
<i class="icon-[lucide--hammer]" />
{{ t('linearMode.welcome.buildApp') }}
<div
class="bg-base-foreground text-base-background text-xxs rounded-full absolute -top-2 -right-2 px-1"
>
{{ t('g.experimental') }}
</div>
</Button>
</div>
</template>
</div>
</template>