Compare commits

..

4 Commits

Author SHA1 Message Date
Terry Jia
f1932631b6 export vue on Window 2025-05-13 22:04:29 -04:00
Chenlei Hu
e6d649b596 [Refactor] Convert NodeBadge.vue to composable (#3883) 2025-05-13 21:56:26 -04:00
Comfy Org PR Bot
b037ba84e3 1.20.0 (#3850)
Co-authored-by: huchenlei <20929282+huchenlei@users.noreply.github.com>
Co-authored-by: Chenlei Hu <huchenlei@proton.me>
2025-05-13 21:05:21 -04:00
杨必赞
7c5c47c105 expose user loggedin in extensionManager (#3871) 2025-05-13 21:04:27 -04:00
19 changed files with 165 additions and 195 deletions

6
global.d.ts vendored
View File

@@ -16,3 +16,9 @@ interface Navigator {
visible: boolean
}
}
interface Window {
Vue: typeof import('vue')
PrimeVue: typeof import('primevue')
VueI18n: typeof import('vue-i18n')
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@comfyorg/comfyui-frontend",
"version": "1.19.9",
"version": "1.20.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@comfyorg/comfyui-frontend",
"version": "1.19.9",
"version": "1.20.0",
"license": "GPL-3.0-only",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",

View File

@@ -1,7 +1,7 @@
{
"name": "@comfyorg/comfyui-frontend",
"private": true,
"version": "1.19.9",
"version": "1.20.0",
"type": "module",
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
"homepage": "https://comfy.org",

View File

@@ -27,7 +27,6 @@
class="w-full h-full touch-none"
/>
<NodeBadge />
<NodeTooltip v-if="tooltipEnabled" />
<NodeSearchboxPopover />
@@ -53,7 +52,6 @@ import BottomPanel from '@/components/bottomPanel/BottomPanel.vue'
import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue'
import DomWidgets from '@/components/graph/DomWidgets.vue'
import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue'
import NodeBadge from '@/components/graph/NodeBadge.vue'
import NodeTooltip from '@/components/graph/NodeTooltip.vue'
import SelectionOverlay from '@/components/graph/SelectionOverlay.vue'
import SelectionToolbox from '@/components/graph/SelectionToolbox.vue'
@@ -62,6 +60,7 @@ import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vu
import SideToolbar from '@/components/sidebar/SideToolbar.vue'
import SecondRowWorkflowTabs from '@/components/topbar/SecondRowWorkflowTabs.vue'
import { useChainCallback } from '@/composables/functional/useChainCallback'
import { useNodeBadge } from '@/composables/node/useNodeBadge'
import { useCanvasDrop } from '@/composables/useCanvasDrop'
import { useContextMenuTranslation } from '@/composables/useContextMenuTranslation'
import { useCopy } from '@/composables/useCopy'
@@ -254,6 +253,7 @@ const workflowPersistence = useWorkflowPersistence()
// @ts-expect-error fixme ts strict error
useCanvasDrop(canvasRef)
useLitegraphSettings()
useNodeBadge()
onMounted(async () => {
useGlobalLitegraph()

View File

@@ -1,114 +0,0 @@
<template>
<div>
<!-- This component does not render anything visible. -->
</div>
</template>
<script setup lang="ts">
import {
BadgePosition,
LGraphBadge,
type LGraphNode
} from '@comfyorg/litegraph'
import _ from 'lodash'
import { computed, onMounted, watch } from 'vue'
import { app } from '@/scripts/app'
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
import { useSettingStore } from '@/stores/settingStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { NodeBadgeMode } from '@/types/nodeSource'
const settingStore = useSettingStore()
const colorPaletteStore = useColorPaletteStore()
const nodeSourceBadgeMode = computed(
() => settingStore.get('Comfy.NodeBadge.NodeSourceBadgeMode') as NodeBadgeMode
)
const nodeIdBadgeMode = computed(
() => settingStore.get('Comfy.NodeBadge.NodeIdBadgeMode') as NodeBadgeMode
)
const nodeLifeCycleBadgeMode = computed(
() =>
settingStore.get('Comfy.NodeBadge.NodeLifeCycleBadgeMode') as NodeBadgeMode
)
watch([nodeSourceBadgeMode, nodeIdBadgeMode, nodeLifeCycleBadgeMode], () => {
app.graph?.setDirtyCanvas(true, true)
})
const nodeDefStore = useNodeDefStore()
function badgeTextVisible(
nodeDef: ComfyNodeDefImpl | null,
badgeMode: NodeBadgeMode
): boolean {
return !(
badgeMode === NodeBadgeMode.None ||
(nodeDef?.isCoreNode && badgeMode === NodeBadgeMode.HideBuiltIn)
)
}
onMounted(() => {
app.registerExtension({
name: 'Comfy.NodeBadge',
nodeCreated(node: LGraphNode) {
node.badgePosition = BadgePosition.TopRight
const badge = computed(() => {
const nodeDef = nodeDefStore.fromLGraphNode(node)
return new LGraphBadge({
text: _.truncate(
[
badgeTextVisible(nodeDef, nodeIdBadgeMode.value)
? `#${node.id}`
: '',
badgeTextVisible(nodeDef, nodeLifeCycleBadgeMode.value)
? nodeDef?.nodeLifeCycleBadgeText ?? ''
: '',
badgeTextVisible(nodeDef, nodeSourceBadgeMode.value)
? nodeDef?.nodeSource?.badgeText ?? ''
: ''
]
.filter((s) => s.length > 0)
.join(' '),
{
length: 31
}
),
fgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_FG_COLOR,
bgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_BG_COLOR
})
})
node.badges.push(() => badge.value)
if (node.constructor.nodeData?.api_node) {
const creditsBadge = computed(() => {
return new LGraphBadge({
text: '',
iconOptions: {
unicode: '\ue96b',
fontFamily: 'PrimeIcons',
color: '#FABC25',
bgColor: '#353535',
fontSize: 8
},
fgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_FG_COLOR,
bgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_BG_COLOR
})
})
node.badges.push(() => creditsBadge.value)
}
}
})
})
</script>

View File

@@ -0,0 +1,122 @@
import {
BadgePosition,
LGraphBadge,
type LGraphNode
} from '@comfyorg/litegraph'
import _ from 'lodash'
import { computed, onMounted, watch } from 'vue'
import { app } from '@/scripts/app'
import { useExtensionStore } from '@/stores/extensionStore'
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
import { useSettingStore } from '@/stores/settingStore'
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { NodeBadgeMode } from '@/types/nodeSource'
/**
* Add LGraphBadge to LGraphNode based on settings.
*
* Following badges are added:
* - Node ID badge
* - Node source badge
* - Node life cycle badge
* - API node credits badge
*/
export const useNodeBadge = () => {
const settingStore = useSettingStore()
const extensionStore = useExtensionStore()
const colorPaletteStore = useColorPaletteStore()
const nodeSourceBadgeMode = computed(
() =>
settingStore.get('Comfy.NodeBadge.NodeSourceBadgeMode') as NodeBadgeMode
)
const nodeIdBadgeMode = computed(
() => settingStore.get('Comfy.NodeBadge.NodeIdBadgeMode') as NodeBadgeMode
)
const nodeLifeCycleBadgeMode = computed(
() =>
settingStore.get(
'Comfy.NodeBadge.NodeLifeCycleBadgeMode'
) as NodeBadgeMode
)
watch([nodeSourceBadgeMode, nodeIdBadgeMode, nodeLifeCycleBadgeMode], () => {
app.graph?.setDirtyCanvas(true, true)
})
const nodeDefStore = useNodeDefStore()
function badgeTextVisible(
nodeDef: ComfyNodeDefImpl | null,
badgeMode: NodeBadgeMode
): boolean {
return !(
badgeMode === NodeBadgeMode.None ||
(nodeDef?.isCoreNode && badgeMode === NodeBadgeMode.HideBuiltIn)
)
}
onMounted(() => {
extensionStore.registerExtension({
name: 'Comfy.NodeBadge',
nodeCreated(node: LGraphNode) {
node.badgePosition = BadgePosition.TopRight
const badge = computed(() => {
const nodeDef = nodeDefStore.fromLGraphNode(node)
return new LGraphBadge({
text: _.truncate(
[
badgeTextVisible(nodeDef, nodeIdBadgeMode.value)
? `#${node.id}`
: '',
badgeTextVisible(nodeDef, nodeLifeCycleBadgeMode.value)
? nodeDef?.nodeLifeCycleBadgeText ?? ''
: '',
badgeTextVisible(nodeDef, nodeSourceBadgeMode.value)
? nodeDef?.nodeSource?.badgeText ?? ''
: ''
]
.filter((s) => s.length > 0)
.join(' '),
{
length: 31
}
),
fgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_FG_COLOR,
bgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_BG_COLOR
})
})
node.badges.push(() => badge.value)
if (node.constructor.nodeData?.api_node) {
const creditsBadge = computed(() => {
return new LGraphBadge({
text: '',
iconOptions: {
unicode: '\ue96b',
fontFamily: 'PrimeIcons',
color: '#FABC25',
bgColor: '#353535',
fontSize: 8
},
fgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_FG_COLOR,
bgColor:
colorPaletteStore.completedActivePalette.colors.litegraph_base
.BADGE_BG_COLOR
})
})
node.badges.push(() => creditsBadge.value)
}
}
})
})
}

View File

@@ -1,41 +1,23 @@
import { useFavicon } from '@vueuse/core'
import { type WatchHandle, ref, watch } from 'vue'
import { watch } from 'vue'
import { useExecutionStore } from '@/stores/executionStore'
import { useSettingStore } from '@/stores/settingStore'
export const useProgressFavicon = () => {
const defaultFavicon = '/assets/images/favicon_progress_16x16/frame_9.png'
const favicon = useFavicon(defaultFavicon)
const executionStore = useExecutionStore()
const settingsStore = useSettingStore()
const totalFrames = 10
const watchHandle = ref<WatchHandle>()
function watchProgress() {
watchHandle.value = watch(
[() => executionStore.executionProgress, () => executionStore.isIdle],
([progress, isIdle]) => {
if (isIdle) {
favicon.value = defaultFavicon
} else {
const frame = Math.floor(progress * totalFrames)
favicon.value = `/assets/images/favicon_progress_16x16/frame_${frame}.png`
}
}
)
}
watch(
() => settingsStore.get('Comfy.Window.TabIconProgress'),
(showProgress) => {
if (showProgress) {
watchProgress()
[() => executionStore.executionProgress, () => executionStore.isIdle],
([progress, isIdle]) => {
if (isIdle) {
favicon.value = defaultFavicon
} else {
watchHandle.value?.stop()
const frame = Math.floor(progress * totalFrames)
favicon.value = `/assets/images/favicon_progress_16x16/frame_${frame}.png`
}
},
{ immediate: true }
}
)
}

View File

@@ -828,14 +828,5 @@ export const CORE_SETTINGS: SettingParams[] = [
type: 'boolean',
defaultValue: false,
versionAdded: '1.19.1'
},
{
id: 'Comfy.Window.TabIconProgress',
name: 'Show progress in browser tab icon (favicon)',
tooltip:
'Updates the browser tab icon to show the progress of the current task.',
type: 'boolean',
defaultValue: true,
versionAdded: '1.20.0'
}
]

View File

@@ -332,10 +332,6 @@
"after": "after"
}
},
"Comfy_Window_TabIconProgress": {
"name": "Show progress in browser tab icon (favicon)",
"tooltip": "Updates the browser tab icon to show the progress of the current task."
},
"Comfy_Window_UnloadConfirmation": {
"name": "Show confirmation when closing window"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "Controla cuándo se actualizan los valores del widget (aleatorizar/incrementar/decrementar), ya sea antes de que se encole el aviso o después."
},
"Comfy_Window_TabIconProgress": {
"name": "Mostrar progreso en el icono de la pestaña del navegador (favicon)",
"tooltip": "Actualiza el icono de la pestaña del navegador para mostrar el progreso de la tarea actual."
},
"Comfy_Window_UnloadConfirmation": {
"name": "Mostrar confirmación al cerrar la ventana"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "Contrôle quand les valeurs du widget sont mises à jour (randomize/increment/decrement), soit avant que l'invite ne soit mise en file d'attente, soit après."
},
"Comfy_Window_TabIconProgress": {
"name": "Afficher la progression dans licône donglet du navigateur (favicon)",
"tooltip": "Met à jour licône de longlet du navigateur pour afficher la progression de la tâche en cours."
},
"Comfy_Window_UnloadConfirmation": {
"name": "Afficher une confirmation lors de la fermeture de la fenêtre"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "ウィジェットの値が更新されるタイミングを制御します(ランダム化/インクリメント/デクリメント)、プロンプトがキューに入れられる前または後のいずれかです。"
},
"Comfy_Window_TabIconProgress": {
"name": "ブラウザータブアイコン(ファビコン)に進行状況を表示",
"tooltip": "現在のタスクの進行状況をブラウザータブのアイコンに表示します。"
},
"Comfy_Window_UnloadConfirmation": {
"name": "ウィンドウを閉じるときに確認を表示"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "위젯 값이 업데이트되는 시점을 제어합니다 (무작위화/증가/감소), 프롬프트가 큐에 추가되기 전 또는 후입니다."
},
"Comfy_Window_TabIconProgress": {
"name": "브라우저 탭 아이콘(파비콘)에 진행 상황 표시",
"tooltip": "현재 작업의 진행 상황을 브라우저 탭 아이콘에 표시합니다."
},
"Comfy_Window_UnloadConfirmation": {
"name": "창 닫을 때 확인 표시"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "Управляет тем, когда обновляются значения виджета (случайные/увеличение/уменьшение), либо до того, как запрос будет поставлен в очередь, либо после."
},
"Comfy_Window_TabIconProgress": {
"name": "Показывать прогресс в значке вкладки браузера (favicon)",
"tooltip": "Обновляет значок вкладки браузера, чтобы показывать прогресс текущей задачи."
},
"Comfy_Window_UnloadConfirmation": {
"name": "Показать подтверждение при закрытии окна"
},

View File

@@ -332,10 +332,6 @@
},
"tooltip": "控制组件值的更新时机(随机/增加/减少),可以在执行工作流之前或之后。"
},
"Comfy_Window_TabIconProgress": {
"name": "在浏览器标签图标中显示进度",
"tooltip": "更新浏览器标签图标以显示当前任务的进度。"
},
"Comfy_Window_UnloadConfirmation": {
"name": "关闭窗口时显示确认"
},

View File

@@ -5,11 +5,14 @@ import * as Sentry from '@sentry/vue'
import { initializeApp } from 'firebase/app'
import { createPinia } from 'pinia'
import 'primeicons/primeicons.css'
import * as pv from 'primevue'
import PrimeVue from 'primevue/config'
import ConfirmationService from 'primevue/confirmationservice'
import ToastService from 'primevue/toastservice'
import Tooltip from 'primevue/tooltip'
import { createApp } from 'vue'
import * as Vue from 'vue'
import * as vueI18n from 'vue-i18n'
import { VueFire, VueFireAuth } from 'vuefire'
import '@/assets/css/style.css'
@@ -68,3 +71,7 @@ app
modules: [VueFireAuth()]
})
.mount('#vue-app')
window.Vue = Vue
window.PrimeVue = pv
window.VueI18n = vueI18n

View File

@@ -463,8 +463,7 @@ const zSettings = z.object({
'main.sub.setting.name': z.any(),
'single.setting': z.any(),
'LiteGraph.Node.DefaultPadding': z.boolean(),
'LiteGraph.Pointer.TrackpadGestures': z.boolean(),
'Comfy.Window.TabIconProgress': z.boolean()
'LiteGraph.Pointer.TrackpadGestures': z.boolean()
})
export type EmbeddingsResponse = z.infer<typeof zEmbeddingsResponse>

View File

@@ -6,7 +6,9 @@ import { useColorPaletteService } from '@/services/colorPaletteService'
import { useDialogService } from '@/services/dialogService'
import type { SidebarTabExtension, ToastManager } from '@/types/extensionTypes'
import { useApiKeyAuthStore } from './apiKeyAuthStore'
import { useCommandStore } from './commandStore'
import { useFirebaseAuthStore } from './firebaseAuthStore'
import { useQueueSettingsStore } from './queueStore'
import { useSettingStore } from './settingStore'
import { useToastStore } from './toastStore'
@@ -43,6 +45,18 @@ export const useWorkspaceStore = defineStore('workspace', () => {
const dialog = useDialogService()
const bottomPanel = useBottomPanelStore()
const authStore = useFirebaseAuthStore()
const apiKeyStore = useApiKeyAuthStore()
const firebaseUser = computed(() => authStore.currentUser)
const isApiKeyLogin = computed(() => apiKeyStore.isAuthenticated)
const isLoggedIn = computed(
() => !!isApiKeyLogin.value || firebaseUser.value !== null
)
const partialUserStore = {
isLoggedIn
}
/**
* Registers a sidebar tab.
* @param tab The sidebar tab to register.
@@ -86,6 +100,7 @@ export const useWorkspaceStore = defineStore('workspace', () => {
colorPalette,
dialog,
bottomPanel,
user: partialUserStore,
registerSidebarTab,
unregisterSidebarTab,

View File

@@ -6,11 +6,7 @@ import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import type { UserConfigExport } from 'vitest/config'
import {
addElementVnodeExportPlugin,
comfyAPIPlugin,
generateImportMapPlugin
} from './build/plugins'
import { comfyAPIPlugin } from './build/plugins'
dotenv.config()
@@ -71,12 +67,6 @@ export default defineConfig({
plugins: [
vue(),
comfyAPIPlugin(IS_DEV),
generateImportMapPlugin([
{ name: 'vue', pattern: /[\\/]node_modules[\\/]vue[\\/]/ },
{ name: 'primevue', pattern: /[\\/]node_modules[\\/]primevue[\\/]/ },
{ name: 'vue-i18n', pattern: /[\\/]node_modules[\\/]vue-i18n[\\/]/ }
]),
addElementVnodeExportPlugin(),
Icons({
compiler: 'vue3'