Skip to content

Commit 07dab97

Browse files
App builder exit updates (#9218)
## Summary - remove exit builder button from right panel - add builder exit button to bottom of canvas - add builder menu with save & exit in top left ## Screenshots (if applicable) <img width="1544" height="998" alt="image" src="https://github.com/user-attachments/assets/f5deadc5-2bf5-4729-b644-2b6a815b9975" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9218-App-builder-exit-updates-3126d73d365081a0bf1adf92e1171060) by [Unito](https://www.unito.io)
1 parent f83daa6 commit 07dab97

File tree

7 files changed

+128
-16
lines changed

7 files changed

+128
-16
lines changed

src/components/builder/AppBuilder.vue

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import DraggableList from '@/components/common/DraggableList.vue'
88
import IoItem from '@/components/builder/IoItem.vue'
99
import PropertiesAccordionItem from '@/components/rightSidePanel/layout/PropertiesAccordionItem.vue'
1010
import WidgetItem from '@/components/rightSidePanel/parameters/WidgetItem.vue'
11-
import Button from '@/components/ui/button/Button.vue'
1211
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
1312
import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'
1413
import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces'
@@ -211,9 +210,6 @@ const renderedInputs = computed<[string, MaybeRef<BoundStyle> | undefined][]>(
211210
{{
212211
isArrangeMode ? t('nodeHelpPage.inputs') : t('linearMode.builder.title')
213212
}}
214-
<Button class="ml-auto" @click="appModeStore.exitBuilder">
215-
{{ t('linearMode.builder.exit') }}
216-
</Button>
217213
</div>
218214
<DraggableList
219215
v-if="isArrangeMode"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<template>
2+
<div
3+
class="fixed bottom-4 left-1/2 z-1000 flex -translate-x-1/2 items-center rounded-2xl border border-border-default bg-base-background p-2 shadow-interface"
4+
>
5+
<Button size="lg" @click="onExitBuilder">
6+
{{ t('builderMenu.exitAppBuilder') }}
7+
</Button>
8+
</div>
9+
</template>
10+
11+
<script setup lang="ts">
12+
import { useEventListener } from '@vueuse/core'
13+
import { useI18n } from 'vue-i18n'
14+
15+
import Button from '@/components/ui/button/Button.vue'
16+
import { useAppMode } from '@/composables/useAppMode'
17+
import { useAppModeStore } from '@/stores/appModeStore'
18+
import { useDialogStore } from '@/stores/dialogStore'
19+
20+
const { t } = useI18n()
21+
const appModeStore = useAppModeStore()
22+
const dialogStore = useDialogStore()
23+
const { isBuilderMode } = useAppMode()
24+
25+
useEventListener(window, 'keydown', (e: KeyboardEvent) => {
26+
if (
27+
e.key === 'Escape' &&
28+
!e.ctrlKey &&
29+
!e.altKey &&
30+
!e.metaKey &&
31+
dialogStore.dialogStack.length === 0 &&
32+
isBuilderMode.value
33+
) {
34+
e.preventDefault()
35+
e.stopPropagation()
36+
onExitBuilder()
37+
}
38+
})
39+
40+
function onExitBuilder() {
41+
void appModeStore.exitBuilder()
42+
}
43+
</script>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<template>
2+
<Popover :show-arrow="false" class="min-w-56 p-3">
3+
<template #button>
4+
<button
5+
:class="
6+
cn(
7+
'absolute left-4 top-[calc(var(--workflow-tabs-height)+16px)] z-1000 inline-flex h-10 cursor-pointer items-center gap-2.5 rounded-lg py-2 pr-2 pl-3 shadow-interface transition-colors border-none',
8+
'bg-secondary-background hover:bg-secondary-background-hover',
9+
'data-[state=open]:bg-secondary-background-hover'
10+
)
11+
"
12+
:aria-label="t('linearMode.appModeToolbar.appBuilder')"
13+
>
14+
<i class="icon-[lucide--hammer] size-4" />
15+
<span class="text-sm font-medium">
16+
{{ t('linearMode.appModeToolbar.appBuilder') }}
17+
</span>
18+
<i class="icon-[lucide--chevron-down] size-4 text-muted-foreground" />
19+
</button>
20+
</template>
21+
<template #default="{ close }">
22+
<button
23+
:class="
24+
cn(
25+
'flex w-full items-center gap-3 rounded-md bg-transparent px-3 py-2 text-sm border-none',
26+
hasOutputs
27+
? 'cursor-pointer hover:bg-secondary-background-hover'
28+
: 'opacity-50 pointer-events-none'
29+
)
30+
"
31+
:disabled="!hasOutputs"
32+
@click="onSave(close)"
33+
>
34+
<i class="icon-[lucide--save] size-4" />
35+
{{ t('builderMenu.saveApp') }}
36+
</button>
37+
<div class="my-1 border-t border-border-default" />
38+
<button
39+
class="flex w-full cursor-pointer items-center gap-3 rounded-md bg-transparent px-3 py-2 text-sm border-none hover:bg-secondary-background-hover"
40+
@click="onExitBuilder(close)"
41+
>
42+
<i class="icon-[lucide--square-pen] size-4" />
43+
{{ t('builderMenu.exitAppBuilder') }}
44+
</button>
45+
</template>
46+
</Popover>
47+
</template>
48+
49+
<script setup lang="ts">
50+
import { storeToRefs } from 'pinia'
51+
import { useI18n } from 'vue-i18n'
52+
53+
import Popover from '@/components/ui/Popover.vue'
54+
import { useAppModeStore } from '@/stores/appModeStore'
55+
import { cn } from '@/utils/tailwindUtil'
56+
57+
import { useBuilderSave } from './useBuilderSave'
58+
59+
const { t } = useI18n()
60+
const appModeStore = useAppModeStore()
61+
const { hasOutputs } = storeToRefs(appModeStore)
62+
const { setSaving } = useBuilderSave()
63+
64+
function onSave(close: () => void) {
65+
setSaving(true)
66+
close()
67+
}
68+
69+
function onExitBuilder(close: () => void) {
70+
void appModeStore.exitBuilder()
71+
close()
72+
}
73+
</script>

src/components/builder/BuilderToolbar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<nav
3-
class="fixed top-[calc(var(--workflow-tabs-height)+var(--spacing)*1.5)] left-1/2 z-[1000] -translate-x-1/2"
3+
class="fixed top-[calc(var(--workflow-tabs-height)+var(--spacing)*1.5)] left-1/2 z-1000 -translate-x-1/2"
44
:aria-label="t('builderToolbar.label')"
55
>
66
<div

src/locales/en/main.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3349,5 +3349,9 @@
33493349
"saveSuccessAppPrompt": "Would you like to view it now?",
33503350
"saveSuccessGraphMessage": "'{name}' has been saved. It will open as a node graph by default.",
33513351
"viewApp": "View app"
3352+
},
3353+
"builderMenu": {
3354+
"saveApp": "Save app",
3355+
"exitAppBuilder": "Exit app builder"
33523356
}
33533357
}

src/stores/appModeStore.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import { defineStore } from 'pinia'
22
import { reactive, computed, watch } from 'vue'
33

44
import { useAppMode } from '@/composables/useAppMode'
5-
import { t } from '@/i18n'
65
import type { NodeId } from '@/lib/litegraph/src/LGraphNode'
76
import type { LinearData } from '@/platform/workflow/management/stores/comfyWorkflow'
87
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
9-
import { useDialogService } from '@/services/dialogService'
108
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
119

1210
export const useAppModeStore = defineStore('appMode', () => {
@@ -69,14 +67,6 @@ export const useAppModeStore = defineStore('appMode', () => {
6967
)
7068

7169
async function exitBuilder() {
72-
if (
73-
!(await useDialogService().confirm({
74-
title: t('linearMode.builder.exitConfirmTitle'),
75-
message: t('linearMode.builder.exitConfirmMessage')
76-
}))
77-
)
78-
return
79-
8070
const workflow = workflowStore.activeWorkflow
8171
if (workflow) workflow.dirtyLinearData = null
8272
resetSelectedToWorkflow()

src/views/GraphView.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
<GraphCanvas @ready="onGraphReady" />
1414
</div>
1515
<LinearView v-if="linearMode" />
16-
<BuilderToolbar v-if="isBuilderMode" />
16+
<template v-if="isBuilderMode">
17+
<BuilderToolbar />
18+
<BuilderMenu />
19+
<BuilderExitButton />
20+
</template>
1721
</div>
1822

1923
<GlobalToast />
@@ -87,6 +91,8 @@ import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
8791
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
8892
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
8993
import { electronAPI } from '@/utils/envUtil'
94+
import BuilderExitButton from '@/components/builder/BuilderExitButton.vue'
95+
import BuilderMenu from '@/components/builder/BuilderMenu.vue'
9096
import BuilderToolbar from '@/components/builder/BuilderToolbar.vue'
9197
import LinearView from '@/views/LinearView.vue'
9298
import ManagerProgressToast from '@/workbench/extensions/manager/components/ManagerProgressToast.vue'

0 commit comments

Comments
 (0)