Files
ComfyUI_frontend/src/services/nodeHelpService.ts
Benjamin Lu d94e0720f3 Handle HTML fallbacks for node help and skip blueprint docs (#7021)
explicitly prevents subgraphs from making an api call since they don't
have docs, this was previously reliant on a non-ok resolution

also doesn't try returning anything that has contenttype of text/html to
prevent the markdown renderer from crashing

## Summary
- short-circuit blueprint/subgraph nodes in help: skip doc fetch and
return the node description, avoiding SPA fallback responses
- guard node help fetch against HTML/SPA fallbacks using content-type
checks; treat them as missing and trigger the existing description
fallback
- keep base URL logic unchanged for non-blueprint nodes

## Testing
- pnpm typecheck
- pnpm lint:fix
- pnpm test:unit
2025-11-28 10:09:09 -08:00

85 lines
2.6 KiB
TypeScript

import { api } from '@/scripts/api'
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { NodeSourceType, getNodeSource } from '@/types/nodeSource'
import { extractCustomNodeName } from '@/workbench/utils/nodeHelpUtil'
class NodeHelpService {
async fetchNodeHelp(node: ComfyNodeDefImpl, locale: string): Promise<string> {
const nodeSource = getNodeSource(node.python_module)
if (nodeSource.type === NodeSourceType.Blueprint) {
return node.description || ''
}
if (nodeSource.type === NodeSourceType.CustomNodes) {
return this.fetchCustomNodeHelp(node, locale)
} else {
return this.fetchCoreNodeHelp(node, locale)
}
}
private async fetchCustomNodeHelp(
node: ComfyNodeDefImpl,
locale: string
): Promise<string> {
const customNodeName = extractCustomNodeName(node.python_module)
let lastError: string | undefined
if (!customNodeName) {
throw new Error('Invalid custom node module')
}
// Try locale-specific path first
const localePath = `/extensions/${customNodeName}/docs/${node.name}/${locale}.md`
const localeDoc = await this.tryFetchMarkdown(localePath)
if (localeDoc.text) return localeDoc.text
lastError = localeDoc.errorText
// Fall back to non-locale path
const fallbackPath = `/extensions/${customNodeName}/docs/${node.name}.md`
const fallbackDoc = await this.tryFetchMarkdown(fallbackPath)
if (fallbackDoc.text) return fallbackDoc.text
lastError = fallbackDoc.errorText ?? lastError
throw new Error(lastError ?? 'Help not found')
}
private async fetchCoreNodeHelp(
node: ComfyNodeDefImpl,
locale: string
): Promise<string> {
const mdUrl = `/docs/${node.name}/${locale}.md`
const doc = await this.tryFetchMarkdown(mdUrl)
if (!doc.text) {
throw new Error(doc.errorText ?? 'Help not found')
}
return doc.text
}
/**
* Fetch a markdown file and return its text, guarding against HTML/SPA fallbacks.
* Returns null when not OK or when the content type indicates HTML.
*/
private async tryFetchMarkdown(
path: string
): Promise<{ text: string | null; errorText?: string }> {
const res = await fetch(api.fileURL(path))
if (!res.ok) {
return { text: null, errorText: res.statusText }
}
const contentType = res.headers?.get?.('content-type') ?? ''
const text = await res.text()
const isHtmlContentType = contentType.includes('text/html')
if (isHtmlContentType) return { text: null, errorText: res.statusText }
return { text }
}
}
// Export singleton instance
export const nodeHelpService = new NodeHelpService()