diff --git a/src/services/nodeHelpService.ts b/src/services/nodeHelpService.ts index 88bb48d29b..3bbebfdda3 100644 --- a/src/services/nodeHelpService.ts +++ b/src/services/nodeHelpService.ts @@ -7,6 +7,10 @@ class NodeHelpService { async fetchNodeHelp(node: ComfyNodeDefImpl, locale: string): Promise { 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 { @@ -19,25 +23,24 @@ class NodeHelpService { locale: string ): Promise { 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` - let res = await fetch(api.fileURL(localePath)) + const localeDoc = await this.tryFetchMarkdown(localePath) + if (localeDoc.text) return localeDoc.text + lastError = localeDoc.errorText - if (!res.ok) { - // Fall back to non-locale path - const fallbackPath = `/extensions/${customNodeName}/docs/${node.name}.md` - res = await fetch(api.fileURL(fallbackPath)) - } - - if (!res.ok) { - throw new Error(res.statusText) - } + // 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 - return res.text() + throw new Error(lastError ?? 'Help not found') } private async fetchCoreNodeHelp( @@ -45,13 +48,35 @@ class NodeHelpService { locale: string ): Promise { const mdUrl = `/docs/${node.name}/${locale}.md` - const res = await fetch(api.fileURL(mdUrl)) + 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) { - throw new Error(res.statusText) + return { text: null, errorText: res.statusText } } - return res.text() + 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 } } } diff --git a/src/workbench/utils/nodeHelpUtil.ts b/src/workbench/utils/nodeHelpUtil.ts index 2bcd899614..2efec34a02 100644 --- a/src/workbench/utils/nodeHelpUtil.ts +++ b/src/workbench/utils/nodeHelpUtil.ts @@ -15,6 +15,9 @@ export function extractCustomNodeName( export function getNodeHelpBaseUrl(node: ComfyNodeDefImpl): string { const nodeSource = getNodeSource(node.python_module) + if (nodeSource.type === NodeSourceType.Blueprint) { + return '' + } if (nodeSource.type === NodeSourceType.CustomNodes) { const customNodeName = extractCustomNodeName(node.python_module) if (customNodeName) {