Files
ComfyUI_frontend/src/views/LinearView.vue
AustinMroz 306fb94cf5 Linear mode arrangement tweaks (#8853)
Planning to keep updates smaller and more contained in the interest of
collaboration and velocity
- The breadcrumb hamburger menu that provides workflow options is now
displayed in linear mode
- As part of this change, the reka-ui popover component now accepts
primvevue format MenuItems
- I prefer the format I had, but this makes transitioning stuff easier.
- The simplified linear history is moved to always be horizontal and
shown beneath previews.
- The label has been removed from the "Give Feedback" button on desktop
so it does not overlap
- The full side toolbar is displayed in linear mode
  - This is temporary,  but it gets the dead code pruned out now.
- Lays some groundwork for selecting an asset from the assets panel to
also select the item in the main linear panel
- The api `promptQueued` event can now optionally include a promptIds,
which list the ids for all jobs that were queued together as part of
that batch
- Update the max for the `number of generations` field to respect the
recently updated cloud limits

| Before | After |
| ------ | ----- |
| <img width="360" alt="before"
src="https://github.com/user-attachments/assets/e632679c-d727-4882-841b-09e99a2f81a4"
/> | <img width="360" alt="after"
src="https://github.com/user-attachments/assets/a9bcd809-c314-49bd-a479-2448d1a88456"/>|

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8853-Linear-mode-arrangement-tweaks-3066d73d365081589355ef753513900b)
by [Unito](https://www.unito.io)
2026-02-20 03:17:19 -08:00

146 lines
5.5 KiB
Vue

<script setup lang="ts">
import { breakpointsTailwind, unrefElement, useBreakpoints } from '@vueuse/core'
import Splitter from 'primevue/splitter'
import SplitterPanel from 'primevue/splitterpanel'
import { computed, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import ExtensionSlot from '@/components/common/ExtensionSlot.vue'
import ModeToggle from '@/components/sidebar/ModeToggle.vue'
import SideToolbar from '@/components/sidebar/SideToolbar.vue'
import TopbarBadges from '@/components/topbar/TopbarBadges.vue'
import WorkflowTabs from '@/components/topbar/WorkflowTabs.vue'
import Button from '@/components/ui/button/Button.vue'
import Popover from '@/components/ui/Popover.vue'
import TypeformPopoverButton from '@/components/ui/TypeformPopoverButton.vue'
import { useWorkflowActionsMenu } from '@/composables/useWorkflowActionsMenu'
import { useSettingStore } from '@/platform/settings/settingStore'
import LinearControls from '@/renderer/extensions/linearMode/LinearControls.vue'
import LinearPreview from '@/renderer/extensions/linearMode/LinearPreview.vue'
import MobileMenu from '@/renderer/extensions/linearMode/MobileMenu.vue'
import { useCommandStore } from '@/stores/commandStore'
import { useWorkspaceStore } from '@/stores/workspaceStore'
const { t } = useI18n()
const settingStore = useSettingStore()
const workspaceStore = useWorkspaceStore()
const mobileDisplay = useBreakpoints(breakpointsTailwind).smaller('md')
const activeTab = computed(() => workspaceStore.sidebarTab.activeSidebarTab)
const { menuItems } = useWorkflowActionsMenu(
() => useCommandStore().execute('Comfy.RenameWorkflow'),
{ isRoot: true }
)
const topLeftRef = useTemplateRef('topLeftRef')
const topRightRef = useTemplateRef('topRightRef')
const bottomLeftRef = useTemplateRef('bottomLeftRef')
const bottomRightRef = useTemplateRef('bottomRightRef')
const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
</script>
<template>
<div class="absolute w-full h-full">
<div class="workflow-tabs-container pointer-events-auto h-9.5 w-full">
<div class="flex h-full items-center">
<WorkflowTabs />
<TopbarBadges />
</div>
</div>
<div
v-if="mobileDisplay"
class="justify-center border-border-subtle border-t overflow-y-scroll h-[calc(100%-38px)] bg-comfy-menu-bg"
>
<MobileMenu />
<div class="flex flex-col text-muted-foreground">
<LinearPreview
:run-button-click="linearWorkflowRef?.runButtonClick"
mobile
/>
</div>
<LinearControls ref="linearWorkflowRef" mobile />
<div class="text-base-foreground flex items-center gap-4">
<div class="border-r border-border-subtle mr-auto">
<ModeToggle class="m-2" />
</div>
<div v-text="t('linearMode.beta')" />
<TypeformPopoverButton data-tf-widget="gmVqFi8l" class="mx-2" />
</div>
</div>
<Splitter
v-else
class="h-[calc(100%-38px)] w-full bg-comfy-menu-secondary-bg"
:pt="{ gutter: { class: 'bg-transparent w-4 -mx-1' } }"
@resizestart="({ originalEvent }) => originalEvent.preventDefault()"
>
<SplitterPanel
id="linearLeftPanel"
:size="1"
class="min-w-min outline-none"
>
<div
v-if="settingStore.get('Comfy.Sidebar.Location') === 'left'"
class="flex h-full border-border-subtle border-r"
>
<SideToolbar />
<ExtensionSlot v-if="activeTab" :extension="activeTab" />
</div>
<LinearControls
v-else
ref="linearWorkflowRef"
:toast-to="unrefElement(bottomLeftRef) ?? undefined"
:notes-to="unrefElement(topLeftRef) ?? undefined"
/>
</SplitterPanel>
<SplitterPanel
id="linearCenterPanel"
:size="98"
class="flex flex-col min-w-min gap-4 mx-2 px-10 pt-8 pb-4 relative text-muted-foreground outline-none"
>
<LinearPreview :run-button-click="linearWorkflowRef?.runButtonClick" />
<div ref="topLeftRef" class="absolute z-21 top-4 left-4">
<Popover :entries="menuItems" align="start">
<template #button>
<Button size="icon" variant="textonly">
<i class="icon-[lucide--menu]" />
</Button>
</template>
</Popover>
</div>
<div ref="topRightRef" class="absolute z-21 top-4 right-4" />
<div ref="bottomLeftRef" class="absolute z-20 bottom-4 left-4" />
<div ref="bottomRightRef" class="absolute z-20 bottom-24 right-4" />
<div
class="absolute z-20 bottom-4 right-4 text-base-foreground flex items-center gap-4"
>
<TypeformPopoverButton
data-tf-widget="gmVqFi8l"
:align="
settingStore.get('Comfy.Sidebar.Location') === 'left'
? 'end'
: 'start'
"
/>
</div>
</SplitterPanel>
<SplitterPanel
id="linearRightPanel"
:size="1"
class="min-w-min outline-none"
>
<LinearControls
v-if="settingStore.get('Comfy.Sidebar.Location') === 'left'"
ref="linearWorkflowRef"
:toast-to="unrefElement(bottomRightRef) ?? undefined"
:notes-to="unrefElement(topRightRef) ?? undefined"
/>
<div v-else class="flex h-full border-border-subtle border-l">
<ExtensionSlot v-if="activeTab" :extension="activeTab" />
<SideToolbar class="border-border-subtle border-l" />
</div>
</SplitterPanel>
</Splitter>
</div>
</template>