mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-27 17:52:16 +00:00
Linear mode bug fixes (#8054)
┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8054-Linear-mode-bug-fixes-2e86d73d365081ed8d75d6e2af679f6c) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
variant="textonly"
|
variant="textonly"
|
||||||
@click="toggleHelpCenter"
|
@click="toggleHelpCenter"
|
||||||
>
|
>
|
||||||
{{ $t('menu.helpAndFeedback') }}
|
<div class="not-md:hidden">{{ $t('menu.helpAndFeedback') }}</div>
|
||||||
<i class="icon-[lucide--circle-help] ml-0.5" />
|
<i class="icon-[lucide--circle-help] ml-0.5" />
|
||||||
<span
|
<span
|
||||||
v-if="shouldShowRedDot"
|
v-if="shouldShowRedDot"
|
||||||
|
|||||||
@@ -1234,7 +1234,7 @@ export function useCoreCommands(): ComfyCommand[] {
|
|||||||
{
|
{
|
||||||
id: 'Comfy.ToggleLinear',
|
id: 'Comfy.ToggleLinear',
|
||||||
icon: 'pi pi-database',
|
icon: 'pi pi-database',
|
||||||
label: 'toggle linear mode',
|
label: 'Toggle Simple Mode',
|
||||||
function: () => {
|
function: () => {
|
||||||
const newMode = !canvasStore.linearMode
|
const newMode = !canvasStore.linearMode
|
||||||
app.rootGraph.extra.linearMode = newMode
|
app.rootGraph.extra.linearMode = newMode
|
||||||
|
|||||||
@@ -276,7 +276,7 @@
|
|||||||
"label": "Help Center"
|
"label": "Help Center"
|
||||||
},
|
},
|
||||||
"Comfy_ToggleLinear": {
|
"Comfy_ToggleLinear": {
|
||||||
"label": "toggle linear mode"
|
"label": "Toggle Simple Mode"
|
||||||
},
|
},
|
||||||
"Comfy_ToggleQPOV2": {
|
"Comfy_ToggleQPOV2": {
|
||||||
"label": "Toggle Queue Panel V2"
|
"label": "Toggle Queue Panel V2"
|
||||||
|
|||||||
@@ -1187,7 +1187,7 @@
|
|||||||
"Experimental: Enable AssetAPI": "Experimental: Enable AssetAPI",
|
"Experimental: Enable AssetAPI": "Experimental: Enable AssetAPI",
|
||||||
"Canvas Performance": "Canvas Performance",
|
"Canvas Performance": "Canvas Performance",
|
||||||
"Help Center": "Help Center",
|
"Help Center": "Help Center",
|
||||||
"toggle linear mode": "toggle simple mode",
|
"toggle linear mode": "Toggle Simple Mode",
|
||||||
"Toggle Queue Panel V2": "Toggle Queue Panel V2",
|
"Toggle Queue Panel V2": "Toggle Queue Panel V2",
|
||||||
"Toggle Theme (Dark/Light)": "Toggle Theme (Dark/Light)",
|
"Toggle Theme (Dark/Light)": "Toggle Theme (Dark/Light)",
|
||||||
"Undo": "Undo",
|
"Undo": "Undo",
|
||||||
@@ -2494,7 +2494,7 @@
|
|||||||
"linearMode": "Simple Mode",
|
"linearMode": "Simple Mode",
|
||||||
"beta": "Beta - Give Feedback",
|
"beta": "Beta - Give Feedback",
|
||||||
"graphMode": "Graph Mode",
|
"graphMode": "Graph Mode",
|
||||||
"dragAndDropImage": "Drag and drop an image",
|
"dragAndDropImage": "Click to browse or drag an image",
|
||||||
"runCount": "Run count:",
|
"runCount": "Run count:",
|
||||||
"rerun": "Rerun",
|
"rerun": "Rerun",
|
||||||
"reuseParameters": "Reuse Parameters",
|
"reuseParameters": "Reuse Parameters",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { useCommandStore } from '@/stores/commandStore'
|
|||||||
import { useExecutionStore } from '@/stores/executionStore'
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
import { useQueueSettingsStore } from '@/stores/queueStore'
|
import { useQueueSettingsStore } from '@/stores/queueStore'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
const commandStore = useCommandStore()
|
const commandStore = useCommandStore()
|
||||||
const executionStore = useExecutionStore()
|
const executionStore = useExecutionStore()
|
||||||
@@ -55,13 +56,16 @@ function nodeToNodeData(node: LGraphNode) {
|
|||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
iconClass: 'icon-[lucide--image]',
|
iconClass: 'icon-[lucide--image]',
|
||||||
label: t('linearMode.dragAndDropImage')
|
label: t('linearMode.dragAndDropImage'),
|
||||||
|
onClick: () => node.widgets?.[1]?.callback?.(undefined)
|
||||||
}
|
}
|
||||||
const nodeData = extractVueNodeData(node)
|
const nodeData = extractVueNodeData(node)
|
||||||
for (const widget of nodeData.widgets ?? []) widget.slotMetadata = undefined
|
for (const widget of nodeData.widgets ?? []) widget.slotMetadata = undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...nodeData,
|
...nodeData,
|
||||||
|
//note lastNodeErrors uses exeuctionid, node.id is execution for root
|
||||||
|
hasErrors: !!executionStore.lastNodeErrors?.[node.id],
|
||||||
|
|
||||||
dropIndicator,
|
dropIndicator,
|
||||||
onDragDrop: node.onDragDrop,
|
onDragDrop: node.onDragDrop,
|
||||||
@@ -69,13 +73,18 @@ function nodeToNodeData(node: LGraphNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const partitionedNodes = computed(() => {
|
const partitionedNodes = computed(() => {
|
||||||
return partition(
|
const parts = partition(
|
||||||
graphNodes.value
|
graphNodes.value
|
||||||
.filter((node) => node.mode === 0 && node.widgets?.length)
|
.filter((node) => node.mode === 0 && node.widgets?.length)
|
||||||
.map(nodeToNodeData)
|
.map(nodeToNodeData)
|
||||||
.reverse(),
|
.reverse(),
|
||||||
(node) => ['MarkdownNote', 'Note'].includes(node.type)
|
(node) => ['MarkdownNote', 'Note'].includes(node.type)
|
||||||
)
|
)
|
||||||
|
for (const noteNode of parts[0]) {
|
||||||
|
for (const widget of noteNode.widgets ?? [])
|
||||||
|
widget.options = { ...widget.options, read_only: true }
|
||||||
|
}
|
||||||
|
return parts
|
||||||
})
|
})
|
||||||
|
|
||||||
const batchCountWidget: SimplifiedWidget<number> = {
|
const batchCountWidget: SimplifiedWidget<number> = {
|
||||||
@@ -165,7 +174,7 @@ defineExpose({ runButtonClick })
|
|||||||
<Popover
|
<Popover
|
||||||
v-if="partitionedNodes[0].length"
|
v-if="partitionedNodes[0].length"
|
||||||
align="start"
|
align="start"
|
||||||
class="overflow-y-auto overflow-x-clip max-h-(--reka-popover-content-available-height)"
|
class="overflow-y-auto overflow-x-clip max-h-(--reka-popover-content-available-height) z-100"
|
||||||
:reference="notesTo"
|
:reference="notesTo"
|
||||||
side="left"
|
side="left"
|
||||||
:to="notesTo"
|
:to="notesTo"
|
||||||
@@ -217,7 +226,13 @@ defineExpose({ runButtonClick })
|
|||||||
>
|
>
|
||||||
<NodeWidgets
|
<NodeWidgets
|
||||||
:node-data
|
:node-data
|
||||||
class="py-3 gap-y-4 **:[.col-span-2]:grid-cols-1 text-sm **:[.p-floatlabel]:h-35 rounded-lg"
|
:class="
|
||||||
|
cn(
|
||||||
|
'py-3 gap-y-3 **:[.col-span-2]:grid-cols-1 not-has-[textarea]:flex-0 rounded-lg',
|
||||||
|
nodeData.hasErrors &&
|
||||||
|
'ring-2 ring-inset ring-node-stroke-error'
|
||||||
|
)
|
||||||
|
"
|
||||||
:style="{ background: applyLightThemeColor(nodeData.bgcolor) }"
|
:style="{ background: applyLightThemeColor(nodeData.bgcolor) }"
|
||||||
/>
|
/>
|
||||||
</DropZone>
|
</DropZone>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { downloadFile } from '@/base/common/downloadUtil'
|
import { downloadFile } from '@/base/common/downloadUtil'
|
||||||
import Load3dViewerContent from '@/components/load3d/Load3dViewerContent.vue'
|
|
||||||
import Popover from '@/components/ui/Popover.vue'
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
import Button from '@/components/ui/button/Button.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { d, t } from '@/i18n'
|
import { d, t } from '@/i18n'
|
||||||
@@ -12,6 +11,7 @@ import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
|||||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||||
import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil'
|
import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil'
|
||||||
import ImagePreview from '@/renderer/extensions/linearMode/ImagePreview.vue'
|
import ImagePreview from '@/renderer/extensions/linearMode/ImagePreview.vue'
|
||||||
|
import Preview3d from '@/renderer/extensions/linearMode/Preview3d.vue'
|
||||||
import VideoPreview from '@/renderer/extensions/linearMode/VideoPreview.vue'
|
import VideoPreview from '@/renderer/extensions/linearMode/VideoPreview.vue'
|
||||||
import {
|
import {
|
||||||
getMediaType,
|
getMediaType,
|
||||||
@@ -172,7 +172,7 @@ async function rerun(e: Event) {
|
|||||||
class="w-full max-w-128 m-auto my-12 overflow-y-auto"
|
class="w-full max-w-128 m-auto my-12 overflow-y-auto"
|
||||||
v-text="selectedOutput!.url"
|
v-text="selectedOutput!.url"
|
||||||
/>
|
/>
|
||||||
<Load3dViewerContent
|
<Preview3d
|
||||||
v-else-if="getMediaType(selectedOutput) === '3d'"
|
v-else-if="getMediaType(selectedOutput) === '3d'"
|
||||||
:model-url="selectedOutput!.url"
|
:model-url="selectedOutput!.url"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ useEventListener(document.body, 'keydown', (e: KeyboardEvent) => {
|
|||||||
'border-2'
|
'border-2'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
@click="selectedIndex = [index, key]"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
:class="
|
:class="
|
||||||
|
|||||||
44
src/renderer/extensions/linearMode/Preview3d.vue
Normal file
44
src/renderer/extensions/linearMode/Preview3d.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useTemplateRef, watch } from 'vue'
|
||||||
|
|
||||||
|
import AnimationControls from '@/components/load3d/controls/AnimationControls.vue'
|
||||||
|
import { useLoad3dViewer } from '@/composables/useLoad3dViewer'
|
||||||
|
|
||||||
|
const { modelUrl } = defineProps<{
|
||||||
|
modelUrl: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const containerRef = useTemplateRef('containerRef')
|
||||||
|
|
||||||
|
const viewer = useLoad3dViewer()
|
||||||
|
|
||||||
|
watch([containerRef, () => modelUrl], async () => {
|
||||||
|
if (!containerRef.value || !modelUrl) return
|
||||||
|
|
||||||
|
await viewer.initializeStandaloneViewer(containerRef.value, modelUrl)
|
||||||
|
})
|
||||||
|
|
||||||
|
//TODO: refactor to add control buttons
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="containerRef"
|
||||||
|
class="relative w-full h-full"
|
||||||
|
@mouseenter="viewer.handleMouseEnter"
|
||||||
|
@mouseleave="viewer.handleMouseLeave"
|
||||||
|
@resize="viewer.handleResize"
|
||||||
|
>
|
||||||
|
<div class="pointer-events-none absolute top-0 left-0 size-full">
|
||||||
|
<AnimationControls
|
||||||
|
v-if="viewer.animations.value && viewer.animations.value.length > 0"
|
||||||
|
v-model:animations="viewer.animations.value"
|
||||||
|
v-model:playing="viewer.playing.value"
|
||||||
|
v-model:selected-speed="viewer.selectedSpeed.value"
|
||||||
|
v-model:selected-animation="viewer.selectedAnimation.value"
|
||||||
|
v-model:animation-progress="viewer.animationProgress.value"
|
||||||
|
v-model:animation-duration="viewer.animationDuration.value"
|
||||||
|
@seek="viewer.handleSeek"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -44,7 +44,10 @@ const bottomRightRef = useTemplateRef('bottomRightRef')
|
|||||||
const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
|
const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="absolute w-full h-full">
|
<div
|
||||||
|
class="absolute w-full h-full"
|
||||||
|
@wheel.capture="(e: WheelEvent) => outputHistoryRef?.onWheel(e)"
|
||||||
|
>
|
||||||
<div class="workflow-tabs-container pointer-events-auto h-9.5 w-full">
|
<div class="workflow-tabs-container pointer-events-auto h-9.5 w-full">
|
||||||
<div class="flex h-full items-center">
|
<div class="flex h-full items-center">
|
||||||
<WorkflowTabs />
|
<WorkflowTabs />
|
||||||
@@ -82,7 +85,10 @@ const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
|
|||||||
/>
|
/>
|
||||||
<LinearControls ref="linearWorkflowRef" mobile />
|
<LinearControls ref="linearWorkflowRef" mobile />
|
||||||
<div class="text-base-foreground flex items-center gap-4 justify-end m-4">
|
<div class="text-base-foreground flex items-center gap-4 justify-end m-4">
|
||||||
<div v-text="t('linearMode.beta')" />
|
<a
|
||||||
|
href="https://form.typeform.com/to/gmVqFi8l"
|
||||||
|
v-text="t('linearMode.beta')"
|
||||||
|
/>
|
||||||
<TypeformPopoverButton data-tf-widget="gmVqFi8l" />
|
<TypeformPopoverButton data-tf-widget="gmVqFi8l" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +128,6 @@ const linearWorkflowRef = useTemplateRef('linearWorkflowRef')
|
|||||||
id="linearCenterPanel"
|
id="linearCenterPanel"
|
||||||
:size="98"
|
: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"
|
class="flex flex-col min-w-min gap-4 mx-2 px-10 pt-8 pb-4 relative text-muted-foreground outline-none"
|
||||||
@wheel.capture="(e: WheelEvent) => outputHistoryRef?.onWheel(e)"
|
|
||||||
>
|
>
|
||||||
<LinearPreview
|
<LinearPreview
|
||||||
:latent-preview="
|
:latent-preview="
|
||||||
|
|||||||
Reference in New Issue
Block a user