mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
Yet further app fixes (#9523)
- Prevent selection of note nodes - Prevents selection of nodes with errors - A bit broader than the reported "can select missing nodes". A node with an error is a node that can not execute and thus can not be used in an app. - Updates the typeform survey - Add a collapsible list of all api nodes(/prices) contained in an app. - Needs to be prettied up for mobile still. <img width="322" height="751" alt="image" src="https://github.com/user-attachments/assets/ebfeeada-9b80-488e-88d6-feaa8bd53629" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9523-Yet-further-app-fixes-31c6d73d365081de9150fbf2d3ec54dd) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -27,7 +27,7 @@ import { app } from '@/scripts/app'
|
|||||||
import { DOMWidgetImpl } from '@/scripts/domWidget'
|
import { DOMWidgetImpl } from '@/scripts/domWidget'
|
||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useAppMode } from '@/composables/useAppMode'
|
import { useAppMode } from '@/composables/useAppMode'
|
||||||
import { useAppModeStore } from '@/stores/appModeStore'
|
import { nodeTypeValidForApp, useAppModeStore } from '@/stores/appModeStore'
|
||||||
import { resolveNode } from '@/utils/litegraphUtil'
|
import { resolveNode } from '@/utils/litegraphUtil'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
import { HideLayoutFieldKey } from '@/types/widgetTypes'
|
import { HideLayoutFieldKey } from '@/types/widgetTypes'
|
||||||
@@ -162,7 +162,11 @@ function handleDown(e: MouseEvent) {
|
|||||||
}
|
}
|
||||||
function handleClick(e: MouseEvent) {
|
function handleClick(e: MouseEvent) {
|
||||||
const [node, widget] = getHovered(e) ?? []
|
const [node, widget] = getHovered(e) ?? []
|
||||||
if (node?.mode !== LGraphEventMode.ALWAYS)
|
if (
|
||||||
|
node?.mode !== LGraphEventMode.ALWAYS ||
|
||||||
|
!nodeTypeValidForApp(node.type) ||
|
||||||
|
node.has_errors
|
||||||
|
)
|
||||||
return canvasInteractions.forwardEventToCanvas(e)
|
return canvasInteractions.forwardEventToCanvas(e)
|
||||||
|
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
@@ -198,7 +202,9 @@ const renderedOutputs = computed(() => {
|
|||||||
return canvas
|
return canvas
|
||||||
.graph!.nodes.filter(
|
.graph!.nodes.filter(
|
||||||
(n) =>
|
(n) =>
|
||||||
n.constructor.nodeData?.output_node && n.mode === LGraphEventMode.ALWAYS
|
n.constructor.nodeData?.output_node &&
|
||||||
|
n.mode === LGraphEventMode.ALWAYS &&
|
||||||
|
!n.has_errors
|
||||||
)
|
)
|
||||||
.map(nodeToDisplayTuple)
|
.map(nodeToDisplayTuple)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3179,6 +3179,7 @@
|
|||||||
"loadTemplate": "Load a template",
|
"loadTemplate": "Load a template",
|
||||||
"cancelThisRun": "Cancel this run",
|
"cancelThisRun": "Cancel this run",
|
||||||
"deleteAllAssets": "Delete all assets from this run",
|
"deleteAllAssets": "Delete all assets from this run",
|
||||||
|
"hasCreditCost": "Requires additional credits",
|
||||||
"welcome": {
|
"welcome": {
|
||||||
"title": "App Mode",
|
"title": "App Mode",
|
||||||
"message": "A simplified view that hides the node graph so you can focus on creating.",
|
"message": "A simplified view that hides the node graph so you can focus on creating.",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function togglePromotion() {
|
|||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'pointer-events-auto absolute size-full rounded-2xl ring-5 ring-warning-background/50',
|
'pointer-events-auto absolute z-1 size-full rounded-2xl ring-5 ring-warning-background/50',
|
||||||
isPromoted && 'ring-warning-background'
|
isPromoted && 'ring-warning-background'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useEventListener, useTimeout } from '@vueuse/core'
|
import { useEventListener, useTimeout } from '@vueuse/core'
|
||||||
import { partition, remove, takeWhile } from 'es-toolkit'
|
import { remove, takeWhile } from 'es-toolkit'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { computed, ref, shallowRef } from 'vue'
|
import { computed, ref, shallowRef } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@@ -19,6 +19,7 @@ import { useSettingStore } from '@/platform/settings/settingStore'
|
|||||||
import { useTelemetry } from '@/platform/telemetry'
|
import { useTelemetry } from '@/platform/telemetry'
|
||||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||||
import DropZone from '@/renderer/extensions/linearMode/DropZone.vue'
|
import DropZone from '@/renderer/extensions/linearMode/DropZone.vue'
|
||||||
|
import PartnerNodesList from '@/renderer/extensions/linearMode/PartnerNodesList.vue'
|
||||||
import NodeWidgets from '@/renderer/extensions/vueNodes/components/NodeWidgets.vue'
|
import NodeWidgets from '@/renderer/extensions/vueNodes/components/NodeWidgets.vue'
|
||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
@@ -119,20 +120,6 @@ function nodeToNodeData(node: LGraphNode) {
|
|||||||
onDragOver: node.onDragOver
|
onDragOver: node.onDragOver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const partitionedNodes = computed(() => {
|
|
||||||
const parts = partition(
|
|
||||||
graphNodes.value
|
|
||||||
.filter((node) => node.mode === 0 && node.widgets?.length)
|
|
||||||
.map(nodeToNodeData)
|
|
||||||
.reverse(),
|
|
||||||
(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
|
|
||||||
})
|
|
||||||
|
|
||||||
//TODO: refactor out of this file.
|
//TODO: refactor out of this file.
|
||||||
//code length is small, but changes should propagate
|
//code length is small, but changes should propagate
|
||||||
@@ -180,34 +167,6 @@ defineExpose({ runButtonClick })
|
|||||||
v-text="workflowStore.activeWorkflow?.filename"
|
v-text="workflowStore.activeWorkflow?.filename"
|
||||||
/>
|
/>
|
||||||
<div class="flex-1" />
|
<div class="flex-1" />
|
||||||
<Popover
|
|
||||||
v-if="partitionedNodes[0].length"
|
|
||||||
align="end"
|
|
||||||
class="z-100 max-h-(--reka-popover-content-available-height) overflow-x-clip overflow-y-auto"
|
|
||||||
side="bottom"
|
|
||||||
:side-offset="-8"
|
|
||||||
>
|
|
||||||
<template #button>
|
|
||||||
<Button variant="muted-textonly">
|
|
||||||
<i class="icon-[lucide--info]" />
|
|
||||||
</Button>
|
|
||||||
</template>
|
|
||||||
<div>
|
|
||||||
<template
|
|
||||||
v-for="(nodeData, index) in partitionedNodes[0]"
|
|
||||||
:key="nodeData.id"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="index !== 0"
|
|
||||||
class="w-full border-t border-border-subtle"
|
|
||||||
/>
|
|
||||||
<NodeWidgets
|
|
||||||
:node-data
|
|
||||||
class="max-w-100 gap-y-3 rounded-lg py-3 *:has-[textarea]:h-50 **:[.col-span-2]:grid-cols-1"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</Popover>
|
|
||||||
<Button v-if="false"> {{ t('menuLabels.publish') }} </Button>
|
<Button v-if="false"> {{ t('menuLabels.publish') }} </Button>
|
||||||
</section>
|
</section>
|
||||||
<div
|
<div
|
||||||
@@ -218,9 +177,7 @@ defineExpose({ runButtonClick })
|
|||||||
class="grow overflow-y-auto contain-size"
|
class="grow overflow-y-auto contain-size"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(nodeData, index) of appModeStore.selectedInputs.length
|
v-for="(nodeData, index) of mappedSelections"
|
||||||
? mappedSelections
|
|
||||||
: partitionedNodes[0]"
|
|
||||||
:key="nodeData.id"
|
:key="nodeData.id"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -274,6 +231,7 @@ defineExpose({ runButtonClick })
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
<PartnerNodesList v-if="!mobile" />
|
||||||
<section
|
<section
|
||||||
v-if="mobile"
|
v-if="mobile"
|
||||||
data-testid="linear-run-button"
|
data-testid="linear-run-button"
|
||||||
@@ -284,6 +242,7 @@ defineExpose({ runButtonClick })
|
|||||||
class="mt-4 w-full"
|
class="mt-4 w-full"
|
||||||
/>
|
/>
|
||||||
<div v-else class="mt-4 flex">
|
<div v-else class="mt-4 flex">
|
||||||
|
<PartnerNodesList mobile />
|
||||||
<Popover side="top" @open-auto-focus.prevent>
|
<Popover side="top" @open-auto-focus.prevent>
|
||||||
<template #button>
|
<template #button>
|
||||||
<Button size="lg" class="-mr-3 pr-7">
|
<Button size="lg" class="-mr-3 pr-7">
|
||||||
|
|||||||
14
src/renderer/extensions/linearMode/PartnerNodeItem.vue
Normal file
14
src/renderer/extensions/linearMode/PartnerNodeItem.vue
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ title: string; price: string }>()
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="not-last:mb-4">
|
||||||
|
<div class="text-muted-foreground" v-text="title" />
|
||||||
|
<span
|
||||||
|
class="mt-2 flex h-5 max-w-max items-center rounded-full bg-component-node-widget-background p-2 py-3"
|
||||||
|
>
|
||||||
|
<i class="mr-1 icon-[lucide--component] h-4 bg-amber-400" />
|
||||||
|
{{ price }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
85
src/renderer/extensions/linearMode/PartnerNodesList.vue
Normal file
85
src/renderer/extensions/linearMode/PartnerNodesList.vue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleRoot,
|
||||||
|
CollapsibleTrigger
|
||||||
|
} from 'reka-ui'
|
||||||
|
import { computed, toValue } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
import Popover from '@/components/ui/Popover.vue'
|
||||||
|
import { usePriceBadge } from '@/composables/node/usePriceBadge'
|
||||||
|
import PartnerNodeItem from '@/renderer/extensions/linearMode/PartnerNodeItem.vue'
|
||||||
|
import { trackNodePrice } from '@/renderer/extensions/vueNodes/composables/usePartitionedBadges'
|
||||||
|
import { app } from '@/scripts/app'
|
||||||
|
import { mapAllNodes } from '@/utils/graphTraversalUtil'
|
||||||
|
|
||||||
|
defineProps<{ mobile?: boolean }>()
|
||||||
|
|
||||||
|
const { isCreditsBadge } = usePriceBadge()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const creditsBadges = computed(() =>
|
||||||
|
mapAllNodes(app.graph, (node) => {
|
||||||
|
if (node.isSubgraphNode()) return
|
||||||
|
|
||||||
|
const priceBadge = node.badges.find(isCreditsBadge)
|
||||||
|
if (!priceBadge) return
|
||||||
|
|
||||||
|
trackNodePrice(node)
|
||||||
|
return [node.title, toValue(priceBadge).text, node.id] as const
|
||||||
|
})
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Popover v-if="mobile && creditsBadges.length" side="top">
|
||||||
|
<template #button>
|
||||||
|
<Button class="mr-2 size-10">
|
||||||
|
<i class="icon-[comfy--credits] bg-amber-400" />
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<section
|
||||||
|
class="max-h-(--reka-popover-content-available-height) overflow-y-auto"
|
||||||
|
>
|
||||||
|
<PartnerNodeItem
|
||||||
|
v-for="[title, price, key] in creditsBadges"
|
||||||
|
:key
|
||||||
|
:title
|
||||||
|
:price
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</Popover>
|
||||||
|
<div v-else-if="creditsBadges.length === 1">
|
||||||
|
<PartnerNodeItem
|
||||||
|
v-for="[title, price, key] in creditsBadges"
|
||||||
|
:key
|
||||||
|
:title
|
||||||
|
:price
|
||||||
|
class="border-t border-border-subtle pt-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<CollapsibleRoot
|
||||||
|
v-else-if="creditsBadges.length"
|
||||||
|
v-slot="{ open }"
|
||||||
|
class="flex max-h-1/2 w-full flex-col"
|
||||||
|
>
|
||||||
|
<div class="mb-1 w-full border-b border-border-subtle" />
|
||||||
|
<CollapsibleTrigger as-child>
|
||||||
|
<Button variant="textonly" class="w-full text-sm">
|
||||||
|
<i class="icon-[comfy--credits] size-4 bg-amber-400" />
|
||||||
|
{{ t('linearMode.hasCreditCost') }}
|
||||||
|
<i v-if="open" class="ml-auto icon-[lucide--chevron-up]" />
|
||||||
|
<i v-else class="ml-auto icon-[lucide--chevron-down]" />
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<CollapsibleContent class="overflow-y-auto">
|
||||||
|
<PartnerNodeItem
|
||||||
|
v-for="[title, price, key] in creditsBadges"
|
||||||
|
:key
|
||||||
|
:title
|
||||||
|
:price
|
||||||
|
/>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</CollapsibleRoot>
|
||||||
|
</template>
|
||||||
@@ -39,7 +39,8 @@
|
|||||||
v-if="
|
v-if="
|
||||||
lgraphNode?.constructor?.nodeData?.output_node &&
|
lgraphNode?.constructor?.nodeData?.output_node &&
|
||||||
isSelectOutputsMode &&
|
isSelectOutputsMode &&
|
||||||
nodeData.mode === LGraphEventMode.ALWAYS
|
nodeData.mode === LGraphEventMode.ALWAYS &&
|
||||||
|
!nodeData.hasErrors
|
||||||
"
|
"
|
||||||
:id="nodeData.id"
|
:id="nodeData.id"
|
||||||
/>
|
/>
|
||||||
@@ -91,10 +92,6 @@
|
|||||||
backgroundColor: applyLightThemeColor(nodeData?.color)
|
backgroundColor: applyLightThemeColor(nodeData?.color)
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<AppOutput
|
|
||||||
v-if="lgraphNode?.constructor?.nodeData?.output_node && isSelectMode"
|
|
||||||
:id="nodeData.id"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
v-if="displayHeader"
|
v-if="displayHeader"
|
||||||
class="relative flex flex-col items-center justify-center"
|
class="relative flex flex-col items-center justify-center"
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ import {
|
|||||||
shouldExpand,
|
shouldExpand,
|
||||||
shouldRenderAsVue
|
shouldRenderAsVue
|
||||||
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
|
} from '@/renderer/extensions/vueNodes/widgets/registry/widgetRegistry'
|
||||||
|
import { nodeTypeValidForApp } from '@/stores/appModeStore'
|
||||||
import {
|
import {
|
||||||
stripGraphPrefix,
|
stripGraphPrefix,
|
||||||
useWidgetValueStore
|
useWidgetValueStore
|
||||||
@@ -160,7 +161,11 @@ onErrorCaptured((error) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const canSelectInputs = computed(
|
const canSelectInputs = computed(
|
||||||
() => isSelectInputsMode.value && nodeData?.mode === LGraphEventMode.ALWAYS
|
() =>
|
||||||
|
isSelectInputsMode.value &&
|
||||||
|
nodeData?.mode === LGraphEventMode.ALWAYS &&
|
||||||
|
nodeTypeValidForApp(nodeData.type) &&
|
||||||
|
!nodeData.hasErrors
|
||||||
)
|
)
|
||||||
const nodeType = computed(() => nodeData?.type || '')
|
const nodeType = computed(() => nodeData?.type || '')
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { computed, toValue } from 'vue'
|
|||||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||||
import { useNodePricing } from '@/composables/node/useNodePricing'
|
import { useNodePricing } from '@/composables/node/useNodePricing'
|
||||||
import { usePriceBadge } from '@/composables/node/usePriceBadge'
|
import { usePriceBadge } from '@/composables/node/usePriceBadge'
|
||||||
|
import type { NodeId } from '@/lib/litegraph/src/LGraphNode'
|
||||||
|
import type { INodeInputSlot } from '@/lib/litegraph/src/interfaces'
|
||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import type { NodeBadgeProps } from '@/renderer/extensions/vueNodes/components/NodeBadge.vue'
|
import type { NodeBadgeProps } from '@/renderer/extensions/vueNodes/components/NodeBadge.vue'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
@@ -17,6 +19,56 @@ function splitAroundFirstSpace(text: string): [string, string | undefined] {
|
|||||||
return [text.slice(0, index), text.slice(index + 1)]
|
return [text.slice(0, index), text.slice(index + 1)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrackableNode = {
|
||||||
|
id: NodeId
|
||||||
|
type: string
|
||||||
|
inputs?: INodeInputSlot[]
|
||||||
|
}
|
||||||
|
//TODO deduplicate reactivity tracking once more thoroughly tested
|
||||||
|
export function trackNodePrice(node: TrackableNode) {
|
||||||
|
const {
|
||||||
|
getRelevantWidgetNames,
|
||||||
|
hasDynamicPricing,
|
||||||
|
getInputGroupPrefixes,
|
||||||
|
getInputNames,
|
||||||
|
getNodeRevisionRef
|
||||||
|
} = useNodePricing()
|
||||||
|
// Access per-node revision ref to establish dependency (each node has its own ref)
|
||||||
|
void getNodeRevisionRef(node.id).value
|
||||||
|
|
||||||
|
if (!hasDynamicPricing(node.type)) return
|
||||||
|
|
||||||
|
// Access only the widget values that affect pricing (from widgetValueStore)
|
||||||
|
const relevantNames = getRelevantWidgetNames(node.type)
|
||||||
|
const widgetStore = useWidgetValueStore()
|
||||||
|
const graphId = app.canvas?.graph?.rootGraph.id
|
||||||
|
if (relevantNames.length > 0 && node.id != null) {
|
||||||
|
for (const name of relevantNames) {
|
||||||
|
// Access value from store to create reactive dependency
|
||||||
|
if (!graphId) continue
|
||||||
|
void widgetStore.getWidget(graphId, node.id, name)?.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Access input connections for regular inputs
|
||||||
|
const inputNames = getInputNames(node.type)
|
||||||
|
if (inputNames.length > 0) {
|
||||||
|
node?.inputs?.forEach((inp) => {
|
||||||
|
if (inp.name && inputNames.includes(inp.name)) {
|
||||||
|
void inp.link // Access link to create reactive dependency
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Access input connections for input_groups (e.g., autogrow inputs)
|
||||||
|
const groupPrefixes = getInputGroupPrefixes(node.type)
|
||||||
|
if (groupPrefixes.length > 0) {
|
||||||
|
node?.inputs?.forEach((inp) => {
|
||||||
|
if (groupPrefixes.some((prefix) => inp.name?.startsWith(prefix + '.'))) {
|
||||||
|
void inp.link // Access link to create reactive dependency
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function usePartitionedBadges(nodeData: VueNodeData) {
|
export function usePartitionedBadges(nodeData: VueNodeData) {
|
||||||
// Use per-node pricing revision to re-compute badges only when this node's pricing updates
|
// Use per-node pricing revision to re-compute badges only when this node's pricing updates
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import { app } from '@/scripts/app'
|
|||||||
import { ChangeTracker } from '@/scripts/changeTracker'
|
import { ChangeTracker } from '@/scripts/changeTracker'
|
||||||
import { resolveNode } from '@/utils/litegraphUtil'
|
import { resolveNode } from '@/utils/litegraphUtil'
|
||||||
|
|
||||||
|
export function nodeTypeValidForApp(type: string) {
|
||||||
|
return !['Note', 'MarkdownNote'].includes(type)
|
||||||
|
}
|
||||||
|
|
||||||
export const useAppModeStore = defineStore('appMode', () => {
|
export const useAppModeStore = defineStore('appMode', () => {
|
||||||
const { getCanvas } = useCanvasStore()
|
const { getCanvas } = useCanvasStore()
|
||||||
const workflowStore = useWorkflowStore()
|
const workflowStore = useWorkflowStore()
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const { onResizeEnd } = useStablePrimeVueSplitterSizer(
|
|||||||
[activeTab, splitterKey]
|
[activeTab, splitterKey]
|
||||||
)
|
)
|
||||||
|
|
||||||
const TYPEFORM_WIDGET_ID = 'gmVqFi8l'
|
const TYPEFORM_WIDGET_ID = 'jmmzmlKw'
|
||||||
|
|
||||||
const bottomLeftRef = useTemplateRef('bottomLeftRef')
|
const bottomLeftRef = useTemplateRef('bottomLeftRef')
|
||||||
const bottomRightRef = useTemplateRef('bottomRightRef')
|
const bottomRightRef = useTemplateRef('bottomRightRef')
|
||||||
|
|||||||
Reference in New Issue
Block a user