App mode - more updates & fixes (#9137)

## Summary

- fix sizing of sidebars in app mode
- update feedback button to match design
- update job queue notification
- clickable queue spinner item to allow clear queue
- refactor mode out of store to specific workflow instance
- support different saved vs active mode
- other styling/layout tweaks

## Changes

- **What**: Changes the store to a composable and moves the mode state
to the workflow.
- This enables switching between tabs and maintaining the mode they were
in

## Screenshots (if applicable)
<img width="1866" height="1455" alt="image"
src="https://github.com/user-attachments/assets/f9a8cd36-181f-4948-b48c-dd27bd9127cf"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9137-App-mode-more-updates-fixes-3106d73d365081a18ccff6ffe24fdec7)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
pythongosssss
2026-02-26 17:55:10 +00:00
committed by GitHub
parent ac12a3d9b9
commit 9fb93a5b0a
32 changed files with 689 additions and 236 deletions

View File

@@ -13,7 +13,7 @@
<GraphCanvas @ready="onGraphReady" />
</div>
<LinearView v-if="linearMode" />
<BuilderToolbar v-if="appModeStore.isBuilderMode" />
<BuilderToolbar v-if="isBuilderMode" />
</div>
<GlobalToast />
@@ -70,7 +70,7 @@ import { api } from '@/scripts/api'
import { app } from '@/scripts/app'
import { setupAutoQueueHandler } from '@/services/autoQueueService'
import { useKeybindingService } from '@/platform/keybindings/keybindingService'
import { useAppModeStore } from '@/stores/appModeStore'
import { useAppMode } from '@/composables/useAppMode'
import { useAssetsStore } from '@/stores/assetsStore'
import { useCommandStore } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
@@ -88,12 +88,10 @@ import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore'
import { electronAPI } from '@/utils/envUtil'
import BuilderToolbar from '@/components/builder/BuilderToolbar.vue'
import { useBuilderSave } from '@/components/builder/useBuilderSave'
import LinearView from '@/views/LinearView.vue'
import ManagerProgressToast from '@/workbench/extensions/manager/components/ManagerProgressToast.vue'
setupAutoQueueHandler()
useBuilderSave()
useProgressFavicon()
useBrowserTabTitle()
@@ -106,7 +104,7 @@ const queueStore = useQueueStore()
const assetsStore = useAssetsStore()
const versionCompatibilityStore = useVersionCompatibilityStore()
const graphCanvasContainerRef = ref<HTMLDivElement | null>(null)
const appModeStore = useAppModeStore()
const { isBuilderMode } = useAppMode()
const { linearMode } = storeToRefs(useCanvasStore())
const telemetry = useTelemetry()

View File

@@ -2,6 +2,7 @@
import { breakpointsTailwind, unrefElement, useBreakpoints } from '@vueuse/core'
import Splitter from 'primevue/splitter'
import SplitterPanel from 'primevue/splitterpanel'
import { storeToRefs } from 'pinia'
import { computed, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -12,17 +13,20 @@ import TopbarBadges from '@/components/topbar/TopbarBadges.vue'
import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue'
import TypeformPopoverButton from '@/components/ui/TypeformPopoverButton.vue'
import { useSettingStore } from '@/platform/settings/settingStore'
import { cn } from '@/utils/tailwindUtil'
import LinearControls from '@/renderer/extensions/linearMode/LinearControls.vue'
import LinearPreview from '@/renderer/extensions/linearMode/LinearPreview.vue'
import LinearProgressBar from '@/renderer/extensions/linearMode/LinearProgressBar.vue'
import MobileMenu from '@/renderer/extensions/linearMode/MobileMenu.vue'
import { useWorkspaceStore } from '@/stores/workspaceStore'
import { useAppMode } from '@/composables/useAppMode'
import { useAppModeStore } from '@/stores/appModeStore'
const { t } = useI18n()
const settingStore = useSettingStore()
const workspaceStore = useWorkspaceStore()
const appModeStore = useAppModeStore()
const { isBuilderMode } = useAppMode()
const { hasOutputs } = storeToRefs(useAppModeStore())
const mobileDisplay = useBreakpoints(breakpointsTailwind).smaller('md')
@@ -31,10 +35,14 @@ const sidebarOnLeft = computed(
() => settingStore.get('Comfy.Sidebar.Location') === 'left'
)
const hasLeftPanel = computed(
() => (sidebarOnLeft.value && activeTab.value) || !sidebarOnLeft.value
() =>
(sidebarOnLeft.value && activeTab.value) ||
(!sidebarOnLeft.value && !isBuilderMode.value && hasOutputs.value)
)
const hasRightPanel = computed(
() => sidebarOnLeft.value || (!sidebarOnLeft.value && activeTab.value)
() =>
(sidebarOnLeft.value && !isBuilderMode.value && hasOutputs.value) ||
(!sidebarOnLeft.value && activeTab.value)
)
const bottomLeftRef = useTemplateRef('bottomLeftRef')
@@ -105,22 +113,27 @@ const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
class="absolute top-0 left-0 w-[calc(100%+16px)] z-21"
/>
<LinearPreview :run-button-click="linearWorkflowRef?.runButtonClick" />
<div class="absolute z-21 top-1 left-1">
<AppModeToolbar v-if="!appModeStore.isBuilderMode" />
<div class="absolute z-21 top-4 left-4">
<AppModeToolbar v-if="!isBuilderMode" />
</div>
<div ref="bottomLeftRef" class="absolute z-20 bottom-4 left-4" />
<div ref="bottomRightRef" class="absolute z-20 bottom-24 right-4" />
<div ref="bottomLeftRef" class="absolute z-20 bottom-7 left-4" />
<div ref="bottomRightRef" class="absolute z-20 bottom-7 right-4" />
<div
class="absolute z-20 bottom-4 right-4 text-base-foreground flex items-center gap-4"
:class="
cn(
'absolute z-20 bottom-4 text-base-foreground flex items-center gap-2',
sidebarOnLeft ? 'left-4' : 'right-4'
)
"
>
<TypeformPopoverButton
data-tf-widget="gmVqFi8l"
:align="
settingStore.get('Comfy.Sidebar.Location') === 'left'
? 'end'
: 'start'
"
:align="sidebarOnLeft ? 'start' : 'end'"
/>
<div class="flex flex-col text-sm text-muted-foreground">
<span>{{ t('linearMode.beta') }}</span>
<span>{{ t('linearMode.giveFeedback') }}</span>
</div>
</div>
</SplitterPanel>
<SplitterPanel