Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/dialog/content/PromptDialogContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ const onConfirm = () => {
useDialogStore().closeDialog()
}

const inputRef = ref(null)
const inputRef = ref<InstanceType<typeof InputText> | undefined>()
const selectAllText = () => {
if (!inputRef.value) return
// @ts-expect-error - $el is an internal property of the InputText component
const inputElement = inputRef.value.$el
inputElement.setSelectionRange(0, inputElement.value.length)
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/dialog/content/SettingDialogContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
scrollHeight="100%"
:optionDisabled="
(option: SettingTreeNode) =>
!queryIsEmpty && !searchResultsCategories.has(option.label)
!queryIsEmpty && !searchResultsCategories.has(option.label ?? '')
"
class="border-none w-full"
/>
Expand Down Expand Up @@ -266,8 +266,8 @@ const handleSearch = (query: string) => {

const queryIsEmpty = computed(() => searchQuery.value.length === 0)
const inSearch = computed(() => !queryIsEmpty.value && !searchInProgress.value)
const tabValue = computed(() =>
inSearch.value ? 'Search Results' : activeCategory.value?.label
const tabValue = computed<string>(() =>
inSearch.value ? 'Search Results' : activeCategory.value?.label ?? ''
)
// Don't allow null category to be set outside of search.
// In search mode, the active category can be null to show all search results.
Expand Down
4 changes: 2 additions & 2 deletions src/components/dialog/content/error/ReportIssuePanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ const createUser = (formData: IssueReportFormData): User => ({
})

const createExtraData = async (formData: IssueReportFormData) => {
const result = {}
const result: Record<string, unknown> = {}
const isChecked = (fieldValue: string) => formData[fieldValue]

await Promise.all(
Expand Down Expand Up @@ -243,7 +243,7 @@ const submit = async (event: FormSubmitEvent) => {
toast.add({
severity: 'error',
summary: t('g.error'),
detail: error.message,
detail: error instanceof Error ? error.message : String(error),
life: 3000
})
}
Expand Down
19 changes: 10 additions & 9 deletions src/components/dialog/content/manager/ManagerDialogContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
<ContentDivider orientation="vertical" :width="0.2" />
<div class="flex-1 flex flex-col isolate">
<InfoPanel
v-if="!hasMultipleSelections"
v-if="!hasMultipleSelections && selectedNodePack"
:node-pack="selectedNodePack"
/>
<InfoPanelMultiItem v-else :node-packs="selectedNodePacks" />
Expand Down Expand Up @@ -149,8 +149,8 @@ const isEmptySearch = computed(() => searchQuery.value === '')

const getInstalledSearchResults = async () => {
if (isEmptySearch.value) return getInstalledPacks()
return searchResults.value.filter((pack) =>
comfyManagerStore.installedPacksIds.has(pack.name)
return searchResults.value.filter(
(pack) => pack.name && comfyManagerStore.installedPacksIds.has(pack.name)
)
}

Expand All @@ -162,15 +162,16 @@ watchEffect(async () => {
}
})

const resultsWithKeys = computed(() =>
displayPacks.value.map((item) => ({
...item,
key: item.id || item.name
}))
const resultsWithKeys = computed(
() =>
displayPacks.value.map((item) => ({
...item,
key: item.id || item.name
})) as (components['schemas']['Node'] & { key: string })[]
)

Copy link
Contributor

@christian-byrne christian-byrne Mar 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this, since we only need the key for the purpose of rendering, maybe we can just generate a unique key if the name doesn't exist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to leave this change to you (in a separate PR) as it involves with larger scale logic change

const selectedNodePacks = ref<components['schemas']['Node'][]>([])
const selectedNodePack = computed(() =>
const selectedNodePack = computed<components['schemas']['Node'] | null>(() =>
selectedNodePacks.value.length === 1 ? selectedNodePacks.value[0] : null
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const getPackNodes = async (pack: components['schemas']['Node']) => {
if (!comfyRegistryService.packNodesAvailable(pack)) return []
return comfyRegistryService.getNodeDefs({
packId: pack.id,
versionId: pack.latest_version.id
versionId: pack.latest_version?.id
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

<script setup lang="ts">
import NodePreview from '@/components/node/NodePreview.vue'
import { ComfyNodeDef } from '@/schemas/nodeDef/nodeDefSchemaV2'
import { components } from '@/types/comfyRegistryTypes'

defineProps<{
Expand All @@ -24,16 +25,20 @@ defineProps<{
}>()

// TODO: when registry returns node defs, use them here
const placeholderNodeDef = {
const placeholderNodeDef: ComfyNodeDef = {
name: 'Sample Node',
display_name: 'Sample Node',
description: 'This is a sample node for preview purposes',
inputs: {
input1: { name: 'Input 1', type: 'IMAGE' },
input2: { name: 'Input 2', type: 'CONDITIONING' }
},
outputs: [
{ name: 'Output 1', type: 'IMAGE', index: 0 },
{ name: 'Output 2', type: 'MASK', index: 1 }
]
{ name: 'Output 1', type: 'IMAGE', index: 0, is_list: false },
{ name: 'Output 2', type: 'MASK', index: 1, is_list: false }
],
category: 'Utility',
output_node: false,
python_module: 'nodes'
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const isUpdateAvailable = computed(() => {
const latestVersion = nodePack.latest_version?.version
if (!latestVersion) return false

const installedVersion = getInstalledPackVersion(nodePack.id)
const installedVersion = getInstalledPackVersion(nodePack.id ?? '')

// Don't attempt to show update available for nightly GitHub packs
if (installedVersion && !isSemVer(installedVersion)) return false
Expand Down
2 changes: 1 addition & 1 deletion src/components/dialog/content/setting/ExtensionPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
icon="pi pi-ellipsis-h"
text
severity="secondary"
@click="menu.show($event)"
@click="menu?.show($event)"
/>
<ContextMenu ref="menu" :model="contextMenuItems" />
</template>
Expand Down
8 changes: 6 additions & 2 deletions src/components/dialog/content/setting/KeybindingPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ interface ICommandData {
const commandsData = computed<ICommandData[]>(() => {
return Object.values(commandStore.commands).map((command) => ({
id: command.id,
label: t(`commands.${normalizeI18nKey(command.id)}.label`, command.label),
label: t(
`commands.${normalizeI18nKey(command.id)}.label`,
command.label ?? ''
),
keybinding: keybindingStore.getKeybindingByCommandId(command.id)
}))
})
Expand All @@ -166,7 +169,7 @@ const selectedCommandData = ref<ICommandData | null>(null)
const editDialogVisible = ref(false)
const newBindingKeyCombo = ref<KeyComboImpl | null>(null)
const currentEditingCommand = ref<ICommandData | null>(null)
const keybindingInput = ref(null)
const keybindingInput = ref<InstanceType<typeof InputText> | null>(null)

const existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {
if (!currentEditingCommand.value) {
Expand Down Expand Up @@ -201,6 +204,7 @@ watchEffect(() => {
if (editDialogVisible.value) {
// nextTick doesn't work here, so we use a timeout instead
setTimeout(() => {
// @ts-expect-error - $el is an internal property of the InputText component
keybindingInput.value?.$el?.focus()
}, 300)
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ watchEffect(() => {

watchEffect(() => {
const spellcheckEnabled = settingStore.get('Comfy.TextareaWidget.Spellcheck')
const textareas = document.querySelectorAll('textarea.comfy-multiline-input')
const textareas = document.querySelectorAll<HTMLTextAreaElement>(
'textarea.comfy-multiline-input'
)

textareas.forEach((textarea: HTMLTextAreaElement) => {
textarea.spellcheck = spellcheckEnabled
Expand Down
22 changes: 12 additions & 10 deletions src/components/graph/NodeTooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ import { normalizeI18nKey } from '@/utils/formatUtil'
let idleTimeout: number
const nodeDefStore = useNodeDefStore()
const settingStore = useSettingStore()
const tooltipRef = ref<HTMLDivElement>()
const tooltipRef = ref<HTMLDivElement | undefined>()
const tooltipText = ref('')
const left = ref<string>()
const top = ref<string>()

const hideTooltip = () => (tooltipText.value = null)
const hideTooltip = () => (tooltipText.value = '')

const showTooltip = async (tooltip: string | null | undefined) => {
if (!tooltip) return
Expand All @@ -44,7 +44,9 @@ const showTooltip = async (tooltip: string | null | undefined) => {

await nextTick()

const rect = tooltipRef.value.getBoundingClientRect()
const rect = tooltipRef.value?.getBoundingClientRect()
if (!rect) return

if (rect.right > window.innerWidth) {
left.value = comfyApp.canvas.mouse[0] - rect.width + 'px'
}
Expand All @@ -60,7 +62,7 @@ const onIdle = () => {
if (!node) return

const ctor = node.constructor as { title_mode?: 0 | 1 | 2 | 3 }
const nodeDef = nodeDefStore.nodeDefsByName[node.type]
const nodeDef = nodeDefStore.nodeDefsByName[node.type ?? '']

if (
ctor.title_mode !== LiteGraph.NO_TITLE &&
Expand All @@ -80,8 +82,8 @@ const onIdle = () => {
if (inputSlot !== -1) {
const inputName = node.inputs[inputSlot].name
const translatedTooltip = st(
`nodeDefs.${normalizeI18nKey(node.type)}.inputs.${normalizeI18nKey(inputName)}.tooltip`,
nodeDef.inputs[inputName]?.tooltip
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.inputs.${normalizeI18nKey(inputName)}.tooltip`,
nodeDef.inputs[inputName]?.tooltip ?? ''
)
return showTooltip(translatedTooltip)
}
Expand All @@ -94,8 +96,8 @@ const onIdle = () => {
)
if (outputSlot !== -1) {
const translatedTooltip = st(
`nodeDefs.${normalizeI18nKey(node.type)}.outputs.${outputSlot}.tooltip`,
nodeDef.outputs[outputSlot]?.tooltip
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.outputs.${outputSlot}.tooltip`,
nodeDef.outputs[outputSlot]?.tooltip ?? ''
)
return showTooltip(translatedTooltip)
}
Expand All @@ -104,8 +106,8 @@ const onIdle = () => {
// Dont show for DOM widgets, these use native browser tooltips as we dont get proper mouse events on these
if (widget && !isDOMWidget(widget)) {
const translatedTooltip = st(
`nodeDefs.${normalizeI18nKey(node.type)}.inputs.${normalizeI18nKey(widget.name)}.tooltip`,
nodeDef.inputs[widget.name]?.tooltip
`nodeDefs.${normalizeI18nKey(node.type ?? '')}.inputs.${normalizeI18nKey(widget.name)}.tooltip`,
nodeDef.inputs[widget.name]?.tooltip ?? ''
)
// Widget tooltip can be set dynamically, current translation collection does not support this.
return showTooltip(widget.tooltip ?? translatedTooltip)
Expand Down
6 changes: 4 additions & 2 deletions src/components/graph/widgets/DomWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const emit = defineEmits<{
(e: 'update:widgetValue', value: string | object): void
}>()

const widgetElement = ref<HTMLElement>()
const widgetElement = ref<HTMLElement | undefined>()

const { style: positionStyle, updatePositionWithTransform } =
useAbsolutePosition()
Expand All @@ -61,6 +61,8 @@ const enableDomClipping = computed(() =>

const updateDomClipping = () => {
const lgCanvas = canvasStore.canvas
if (!lgCanvas || !widgetElement.value) return

const selectedNode = Object.values(
lgCanvas.selected_nodes ?? {}
)[0] as LGraphNode
Expand Down Expand Up @@ -130,7 +132,7 @@ const inputSpec = widget.node.constructor.nodeData
const tooltip = inputSpec?.inputs?.[widget.name]?.tooltip

onMounted(() => {
if (isDOMWidget(widget)) {
if (isDOMWidget(widget) && widgetElement.value) {
widgetElement.value.appendChild(widget.element)
}
})
Expand Down
14 changes: 9 additions & 5 deletions src/components/sidebar/tabs/modelLibrary/ModelTreeLeaf.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ const props = defineProps<{
node: RenderedTreeExplorerNode<ComfyModelDef>
}>()

const modelDef = computed(() => props.node.data)
// Note: The leaf node should always have a model definition on node.data.
const modelDef = computed<ComfyModelDef>(() => props.node.data!)

const modelPreviewUrl = computed(() => {
if (modelDef.value?.image) {
if (modelDef.value.image) {
return modelDef.value.image
}
const folder = modelDef.value.directory
Expand All @@ -69,6 +70,8 @@ const sidebarLocation = computed<'left' | 'right'>(() =>

const handleModelHover = async () => {
const hoverTarget = modelContentElement.value
if (!hoverTarget) return

const targetRect = hoverTarget.getBoundingClientRect()

const previewHeight = previewRef.value?.$el.offsetHeight || 0
Expand All @@ -87,8 +90,8 @@ const handleModelHover = async () => {
modelDef.value.load()
}

const container = ref<HTMLElement | null>(null)
const modelContentElement = ref<HTMLElement | null>(null)
const container = ref<HTMLElement | undefined>()
const modelContentElement = ref<HTMLElement | undefined>()
const isHovered = ref(false)

const showPreview = computed(() => {
Expand All @@ -114,7 +117,8 @@ const handleMouseLeave = () => {
isHovered.value = false
}
onMounted(() => {
modelContentElement.value = container.value?.closest('.p-tree-node-content')
modelContentElement.value =
container.value?.closest('.p-tree-node-content') ?? undefined
modelContentElement.value?.addEventListener('mouseenter', handleMouseEnter)
modelContentElement.value?.addEventListener('mouseleave', handleMouseLeave)
modelDef.value.load()
Expand Down
3 changes: 2 additions & 1 deletion src/components/sidebar/tabs/nodeLibrary/NodeTreeFolder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ onMounted(() => {

const updateIconColor = () => {
if (iconElement.value && customization.value) {
iconElement.value.style.color = customization.value.color
iconElement.value.style.color = customization.value.color ?? ''
}
}

Expand All @@ -64,6 +64,7 @@ onUnmounted(() => {

const expandedKeys = inject(InjectKeyExpandedKeys)
const handleItemDrop = (node: RenderedTreeExplorerNode) => {
if (!expandedKeys) return
expandedKeys.value[node.key] = true
}
</script>
2 changes: 1 addition & 1 deletion src/components/templates/thumbnails/DefaultThumbnail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import BaseThumbnail from '@/components/templates/thumbnails/BaseThumbnail.vue'
defineProps<{
src: string
alt: string
hoverZoom?: number
hoverZoom: number
isHovered?: boolean
}>()
</script>
4 changes: 3 additions & 1 deletion src/components/toast/GlobalToast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ function updateToastPosition() {
document.getElementById('dynamic-toast-style') || createStyleElement()
const rect = document
.querySelector('.graph-canvas-container')
.getBoundingClientRect()
?.getBoundingClientRect()
if (!rect) return

styleElement.textContent = `
.p-toast.p-component.p-toast-top-right {
top: ${rect.top + 20}px !important;
Expand Down
2 changes: 1 addition & 1 deletion src/components/topbar/TopMenubar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ eventBus.on((event: string, payload: any) => {
onMounted(() => {
if (isElectron()) {
electronAPI().changeTheme({
height: topMenuRef.value.getBoundingClientRect().height
height: topMenuRef.value?.getBoundingClientRect().height ?? 0
})
}
})
Expand Down
4 changes: 2 additions & 2 deletions src/components/topbar/WorkflowTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const workspaceStore = useWorkspaceStore()
const workflowStore = useWorkflowStore()
const workflowService = useWorkflowService()
const workflowBookmarkStore = useWorkflowBookmarkStore()
const rightClickedTab = ref<WorkflowOption>(null)
const rightClickedTab = ref<WorkflowOption | undefined>()
const menu = ref()

const workflowToOption = (workflow: ComfyWorkflow): WorkflowOption => ({
Expand Down Expand Up @@ -114,7 +114,7 @@ const onCloseWorkflow = (option: WorkflowOption) => {
closeWorkflows([option])
}

const showContextMenu = (event, option) => {
const showContextMenu = (event: MouseEvent, option: WorkflowOption) => {
rightClickedTab.value = option
menu.value.show(event)
}
Expand Down
Loading
Loading