[TS] Fix ts-strict errors in Vue components (Part 2) (#3123)

Co-authored-by: Christian Byrne <cbyrne@comfy.org>
This commit is contained in:
Chenlei Hu
2025-03-18 10:51:23 -04:00
committed by GitHub
parent 0a6d3c0231
commit e8997a7653
19 changed files with 76 additions and 49 deletions

View File

@@ -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)
}

View File

@@ -15,7 +15,7 @@
scrollHeight="100%"
:optionDisabled="
(option: SettingTreeNode) =>
!queryIsEmpty && !searchResultsCategories.has(option.label)
!queryIsEmpty && !searchResultsCategories.has(option.label ?? '')
"
class="border-none w-full"
/>
@@ -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.

View File

@@ -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(
@@ -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
})
}

View File

@@ -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" />
@@ -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)
)
}
@@ -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 })[]
)
const selectedNodePacks = ref<components['schemas']['Node'][]>([])
const selectedNodePack = computed(() =>
const selectedNodePack = computed<components['schemas']['Node'] | null>(() =>
selectedNodePacks.value.length === 1 ? selectedNodePacks.value[0] : null
)

View File

@@ -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
})
}

View File

@@ -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<{
@@ -24,7 +25,8 @@ 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: {
@@ -32,8 +34,11 @@ const placeholderNodeDef = {
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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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)
}))
})
@@ -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) {
@@ -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)
}

View File

@@ -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

View File

@@ -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
@@ -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'
}
@@ -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 &&
@@ -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)
}
@@ -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)
}
@@ -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)

View File

@@ -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()
@@ -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
@@ -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)
}
})

View File

@@ -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
@@ -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
@@ -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(() => {
@@ -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()

View File

@@ -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 ?? ''
}
}
@@ -64,6 +64,7 @@ onUnmounted(() => {
const expandedKeys = inject(InjectKeyExpandedKeys)
const handleItemDrop = (node: RenderedTreeExplorerNode) => {
if (!expandedKeys) return
expandedKeys.value[node.key] = true
}
</script>

View File

@@ -20,7 +20,7 @@ import BaseThumbnail from '@/components/templates/thumbnails/BaseThumbnail.vue'
defineProps<{
src: string
alt: string
hoverZoom?: number
hoverZoom: number
isHovered?: boolean
}>()
</script>

View File

@@ -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;

View File

@@ -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
})
}
})

View File

@@ -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 => ({
@@ -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)
}

View File

@@ -396,7 +396,10 @@ const normalizeVersion = (version: string) =>
.map(Number)
.filter((part) => !Number.isNaN(part))
export function compareVersions(versionA: string, versionB: string): number {
export function compareVersions(
versionA: string | undefined,
versionB: string | undefined
): number {
versionA ??= '0.0.0'
versionB ??= '0.0.0'