Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,8 @@ const {
availableRunsOn,
filteredCount,
totalCount,
resetFilters
resetFilters,
loadFuseOptions
} = useTemplateFiltering(navigationFilteredTemplates)

/**
Expand Down Expand Up @@ -815,10 +816,10 @@ const pageTitle = computed(() => {
// Initialize templates loading with useAsyncState
const { isLoading } = useAsyncState(
async () => {
// Run all operations in parallel for better performance
await Promise.all([
loadTemplates(),
workflowTemplatesStore.loadWorkflowTemplates()
workflowTemplatesStore.loadWorkflowTemplates(),
loadFuseOptions()
])
return true
},
Expand Down
123 changes: 123 additions & 0 deletions src/composables/useTemplateFiltering.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createPinia, setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick, ref } from 'vue'
import type { IFuseOptions } from 'fuse.js'

import type { TemplateInfo } from '@/platform/workflow/templates/types/template'

Expand Down Expand Up @@ -42,13 +43,21 @@ vi.mock('@/platform/telemetry', () => ({
}))
}))

const mockGetFuseOptions = vi.hoisted(() => vi.fn())
vi.mock('@/scripts/api', () => ({
api: {
getFuseOptions: mockGetFuseOptions
}
}))
Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional: Instead of hoisting it separately, if you instantiate it within the mock module you can just import it like you would otherwise and get the same mock here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's easier to assert the fuseOptions with valid data, error state or null data, by hoisting it separately, since fuseOptions not be exported


const { useTemplateFiltering } =
await import('@/composables/useTemplateFiltering')

describe('useTemplateFiltering', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
mockGetFuseOptions.mockResolvedValue(null)
})

afterEach(() => {
Expand Down Expand Up @@ -272,4 +281,118 @@ describe('useTemplateFiltering', () => {
'beta-pro'
])
})

describe('loadFuseOptions', () => {
it('updates fuseOptions when getFuseOptions returns valid options', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'test-template',
description: 'Test template',
mediaType: 'image',
mediaSubtype: 'png'
}
])

const customFuseOptions: IFuseOptions<TemplateInfo> = {
keys: [
{ name: 'name', weight: 0.5 },
{ name: 'description', weight: 0.5 }
],
threshold: 0.4,
includeScore: true
}

mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)

const { loadFuseOptions, filteredTemplates } =
useTemplateFiltering(templates)

await loadFuseOptions()

expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
expect(filteredTemplates.value).toBeDefined()
})
Comment on lines +286 to +314
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Strengthen test assertion to verify options are applied.

The test only asserts that filteredTemplates.value is defined, which would pass even if the custom Fuse options are never applied. Consider also asserting that the options were actually updated or that search behavior reflects the new configuration.

💡 Suggested enhancement
  it('updates fuseOptions when getFuseOptions returns valid options', async () => {
    const templates = ref<TemplateInfo[]>([
      {
        name: 'test-template',
        description: 'Test template',
        mediaType: 'image',
        mediaSubtype: 'png'
      }
    ])

    const customFuseOptions: IFuseOptions<TemplateInfo> = {
      keys: [
        { name: 'name', weight: 0.5 },
        { name: 'description', weight: 0.5 }
      ],
      threshold: 0.4,
      includeScore: true
    }

    mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)

-   const { loadFuseOptions, filteredTemplates } =
+   const { loadFuseOptions, filteredTemplates, searchQuery } =
      useTemplateFiltering(templates)

    await loadFuseOptions()
+   
+   // Verify options are applied by testing search behavior
+   searchQuery.value = 'test'
+   await nextTick()

    expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
    expect(filteredTemplates.value).toBeDefined()
+   expect(filteredTemplates.value.length).toBeGreaterThan(0)
  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('updates fuseOptions when getFuseOptions returns valid options', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'test-template',
description: 'Test template',
mediaType: 'image',
mediaSubtype: 'png'
}
])
const customFuseOptions: IFuseOptions<TemplateInfo> = {
keys: [
{ name: 'name', weight: 0.5 },
{ name: 'description', weight: 0.5 }
],
threshold: 0.4,
includeScore: true
}
mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)
const { loadFuseOptions, filteredTemplates } =
useTemplateFiltering(templates)
await loadFuseOptions()
expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
expect(filteredTemplates.value).toBeDefined()
})
it('updates fuseOptions when getFuseOptions returns valid options', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'test-template',
description: 'Test template',
mediaType: 'image',
mediaSubtype: 'png'
}
])
const customFuseOptions: IFuseOptions<TemplateInfo> = {
keys: [
{ name: 'name', weight: 0.5 },
{ name: 'description', weight: 0.5 }
],
threshold: 0.4,
includeScore: true
}
mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)
const { loadFuseOptions, filteredTemplates, searchQuery } =
useTemplateFiltering(templates)
await loadFuseOptions()
// Verify options are applied by testing search behavior
searchQuery.value = 'test'
await nextTick()
expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
expect(filteredTemplates.value).toBeDefined()
expect(filteredTemplates.value.length).toBeGreaterThan(0)
})
🤖 Prompt for AI Agents
In @src/composables/useTemplateFiltering.test.ts around lines 286 - 314, The
test currently only checks filteredTemplates is defined; strengthen it to verify
the custom Fuse options are actually applied by either (A) asserting the
internal options were set if useTemplateFiltering exposes them (compare returned
fuseOptions to mockGetFuseOptions' value) or (B) verifying search behavior that
depends on those options: after await loadFuseOptions(), set a search query or
invoke the filtering function provided by useTemplateFiltering and assert
filteredTemplates.value contains the 'test-template' result for a fuzzy/partial
search that would only match when the custom IFuseOptions (mockGetFuseOptions)
are used; ensure you still assert mockGetFuseOptions was called once
(mockGetFuseOptions, loadFuseOptions, filteredTemplates, useTemplateFiltering).


it('does not update fuseOptions when getFuseOptions returns null', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'test-template',
description: 'Test template',
mediaType: 'image',
mediaSubtype: 'png'
}
])

mockGetFuseOptions.mockResolvedValueOnce(null)

const { loadFuseOptions, filteredTemplates } =
useTemplateFiltering(templates)

const initialResults = filteredTemplates.value

await loadFuseOptions()

expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
expect(filteredTemplates.value).toEqual(initialResults)
})

it('handles errors when getFuseOptions fails', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'test-template',
description: 'Test template',
mediaType: 'image',
mediaSubtype: 'png'
}
])

mockGetFuseOptions.mockRejectedValueOnce(new Error('Network error'))

const { loadFuseOptions, filteredTemplates } =
useTemplateFiltering(templates)

const initialResults = filteredTemplates.value

await expect(loadFuseOptions()).rejects.toThrow('Network error')
expect(filteredTemplates.value).toEqual(initialResults)
})
Comment on lines +339 to +358
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the implementation of loadFuseOptions in useTemplateFiltering.ts
rg -nA 20 'loadFuseOptions' src/composables/useTemplateFiltering.ts | head -60

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 904


🏁 Script executed:

#!/bin/bash
# Also check how loadFuseOptions is called in WorkflowTemplateSelectorDialog.vue
rg -nB 5 -A 5 'loadFuseOptions' src/components/WorkflowTemplateSelectorDialog.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Check if there's error handling setup around getFuseOptions calls
rg -nB 3 -A 10 'getFuseOptions' src/composables/useTemplateFiltering.ts

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 538


🏁 Script executed:

#!/bin/bash
# Search for imports/usages of useTemplateFiltering to find the component
rg -l 'useTemplateFiltering' src/components/

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 133


🏁 Script executed:

#!/bin/bash
# Look for loadFuseOptions usage across the codebase
rg -nB 5 -A 5 'loadFuseOptions\(\)' src/

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1078


🏁 Script executed:

#!/bin/bash
# Check the complete context around the Promise.all call for error handling
rg -nB 20 -A 10 'Promise.all' src/components/custom/widget/WorkflowTemplateSelectorDialog.vue | head -80

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 1047


Test correctly expects error propagation - but implementation lacks error handling.

The test is correct: loadFuseOptions() has no try-catch and will propagate errors from api.getFuseOptions(). However, the implementation violates the coding guideline requiring "Implement proper error handling in code" for TypeScript/Vue files.

The function should either:

  1. Catch errors and fall back gracefully (with appropriate error messaging), or
  2. Re-throw with context for the caller to handle

Currently, the function propagates errors without context, and callers in WorkflowTemplateSelectorDialog.vue rely on useAsyncState for error handling rather than explicit try-catch. While functional due to the wrapper, this lacks explicit error handling as required by guidelines.


it('recreates Fuse instance when fuseOptions change', async () => {
const templates = ref<TemplateInfo[]>([
{
name: 'searchable-template',
description: 'This is a searchable template',
mediaType: 'image',
mediaSubtype: 'png'
},
{
name: 'another-template',
description: 'Another template',
mediaType: 'image',
mediaSubtype: 'png'
}
])

const { loadFuseOptions, searchQuery, filteredTemplates } =
useTemplateFiltering(templates)

const customFuseOptions = {
keys: [{ name: 'name', weight: 1.0 }],
threshold: 0.2,
includeScore: true,
includeMatches: true
}

mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)

await loadFuseOptions()
await nextTick()

searchQuery.value = 'searchable'
await nextTick()

expect(filteredTemplates.value.length).toBeGreaterThan(0)
expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
})
Comment on lines +360 to +396
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Test assertion could be strengthened to verify Fuse options actually affect search behavior.

The test at line 380 only asserts filteredTemplates.value.length > 0, which would pass even if the new Fuse options aren't applied. A more robust test would capture search results before loading options, load new options with different threshold/weights, search again, and verify results differ.

🔎 Stronger test approach
  it('recreates Fuse instance when fuseOptions change', async () => {
    const templates = ref<TemplateInfo[]>([
      {
        name: 'searchable-template',
        description: 'This is a searchable template',
        mediaType: 'image',
        mediaSubtype: 'png'
      },
      {
        name: 'another-template',
        description: 'Another template',
        mediaType: 'image',
        mediaSubtype: 'png'
      }
    ])

    const { loadFuseOptions, searchQuery, filteredTemplates } =
      useTemplateFiltering(templates)

+   // Capture baseline results with default options
+   searchQuery.value = 'searchable'
+   await nextTick()
+   const resultsBeforeOptionsLoad = [...filteredTemplates.value]

    const customFuseOptions = {
-     keys: [{ name: 'name', weight: 1.0 }],
-     threshold: 0.2,
+     keys: [{ name: 'description', weight: 1.0 }], // Different key priority
+     threshold: 0.8, // More lenient threshold
      includeScore: true,
      includeMatches: true
    }

    mockGetFuseOptions.mockResolvedValueOnce(customFuseOptions)

    await loadFuseOptions()
    await nextTick()

-   searchQuery.value = 'searchable'
+   // Re-trigger search with new options
+   searchQuery.value = 'another'
    await nextTick()

-   expect(filteredTemplates.value.length).toBeGreaterThan(0)
+   // Verify results changed due to different options
+   expect(filteredTemplates.value).not.toEqual(resultsBeforeOptionsLoad)
+   expect(filteredTemplates.value.length).toBeGreaterThan(0)
    expect(mockGetFuseOptions).toHaveBeenCalledTimes(1)
  })
🤖 Prompt for AI Agents
In @src/composables/useTemplateFiltering.test.ts around lines 346 - 382, The
test 'recreates Fuse instance when fuseOptions change' should assert that new
Fuse options actually change search behavior: before calling loadFuseOptions(),
set searchQuery.value to a test term and capture filteredTemplates.value (or
assert it’s empty/not matching expected), then mockGetFuseOptions to return
customFuseOptions, call loadFuseOptions() and await nextTick(), set
searchQuery.value to the same test term again and assert filteredTemplates.value
now reflects the new options (e.g., includes the expected template), and finally
assert mockGetFuseOptions was called once; use the existing symbols
loadFuseOptions, searchQuery, filteredTemplates, mockGetFuseOptions, and
customFuseOptions to locate and modify the test.

})
})
44 changes: 28 additions & 16 deletions src/composables/useTemplateFiltering.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { refDebounced, watchDebounced } from '@vueuse/core'
import Fuse from 'fuse.js'
import type { IFuseOptions } from 'fuse.js'
import { computed, ref, watch } from 'vue'
import type { Ref } from 'vue'

Expand All @@ -8,6 +9,21 @@ import { useTelemetry } from '@/platform/telemetry'
import type { TemplateInfo } from '@/platform/workflow/templates/types/template'
import { useTemplateRankingStore } from '@/stores/templateRankingStore'
import { debounce } from 'es-toolkit/compat'
import { api } from '@/scripts/api'

// Fuse.js configuration for fuzzy search
const defaultFuseOptions: IFuseOptions<TemplateInfo> = {
keys: [
{ name: 'name', weight: 0.3 },
{ name: 'title', weight: 0.3 },
{ name: 'description', weight: 0.1 },
{ name: 'tags', weight: 0.2 },
{ name: 'models', weight: 0.3 }
],
threshold: 0.33,
includeScore: true,
includeMatches: true
}

export function useTemplateFiltering(
templates: Ref<TemplateInfo[]> | TemplateInfo[]
Expand Down Expand Up @@ -35,26 +51,14 @@ export function useTemplateFiltering(
| 'model-size-low-to-high'
>(settingStore.get('Comfy.Templates.SortBy'))

const fuseOptions = ref<IFuseOptions<TemplateInfo>>(defaultFuseOptions)

const templatesArray = computed(() => {
const templateData = 'value' in templates ? templates.value : templates
return Array.isArray(templateData) ? templateData : []
})

// Fuse.js configuration for fuzzy search
const fuseOptions = {
keys: [
{ name: 'name', weight: 0.3 },
{ name: 'title', weight: 0.3 },
{ name: 'description', weight: 0.1 },
{ name: 'tags', weight: 0.2 },
{ name: 'models', weight: 0.3 }
],
threshold: 0.33,
includeScore: true,
includeMatches: true
}

const fuse = computed(() => new Fuse(templatesArray.value, fuseOptions))
const fuse = computed(() => new Fuse(templatesArray.value, fuseOptions.value))

const availableModels = computed(() => {
const modelSet = new Set<string>()
Expand Down Expand Up @@ -272,6 +276,13 @@ export function useTemplateFiltering(
})
}, 500)

const loadFuseOptions = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Is this something that we could just bake into the composable?
How often do we explicitly re-fetch the options?
Do we need to explicitly defer the loading for some reason?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

plan to handle loadTemplates, loadFuseOptions, and loadWorkflowTemplates in a unified way to obtain a single loading state.
Only when the loading states of all three APIs are false should the page be rendered, in order to provide a better user experience.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By now, there is no scenario to re-fetch the fuse options.

const fetchedOptions = await api.getFuseOptions()
if (fetchedOptions) {
fuseOptions.value = fetchedOptions
}
}
Comment on lines +279 to +284
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: Missing error handling despite multiple reviews.

This function lacks try-catch error handling, causing errors to propagate and break template loading (see related comment on WorkflowTemplateSelectorDialog.vue). This issue was flagged in previous reviews and marked as addressed, but the implementation remains unchanged.

🔧 Required fix with graceful fallback
  const loadFuseOptions = async () => {
+   try {
      const fetchedOptions = await api.getFuseOptions()
      if (fetchedOptions) {
        fuseOptions.value = fetchedOptions
      }
+   } catch (error) {
+     console.warn('Failed to load Fuse options, using defaults:', error)
+     // fuseOptions.value remains at defaultFuseOptions
+   }
  }

Based on coding guidelines requiring proper error handling in TypeScript files and previous reviewer feedback.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const loadFuseOptions = async () => {
const fetchedOptions = await api.getFuseOptions()
if (fetchedOptions) {
fuseOptions.value = fetchedOptions
}
}
const loadFuseOptions = async () => {
try {
const fetchedOptions = await api.getFuseOptions()
if (fetchedOptions) {
fuseOptions.value = fetchedOptions
}
} catch (error: unknown) {
console.warn('Failed to load Fuse options, using defaults:', error)
// fuseOptions.value remains at defaultFuseOptions
}
}
🤖 Prompt for AI Agents
In @src/composables/useTemplateFiltering.ts around lines 279 - 284, The
loadFuseOptions function lacks error handling; wrap the await
api.getFuseOptions() call inside a try-catch in loadFuseOptions, call the
existing logger (or console.error) with the caught error and a clear context
message, and on failure perform a graceful fallback (leave fuseOptions.value
unchanged or assign an appropriate default such as an empty options
object/array) without rethrowing so template loading won’t break; reference:
loadFuseOptions, fuseOptions.value, and api.getFuseOptions().

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the try catch handle has been done in src/scripts/api.ts


// Watch for filter changes and track them
watch(
[searchQuery, selectedModels, selectedUseCases, selectedRunsOn, sortBy],
Expand Down Expand Up @@ -344,6 +355,7 @@ export function useTemplateFiltering(
resetFilters,
removeModelFilter,
removeUseCaseFilter,
removeRunsOnFilter
removeRunsOnFilter,
loadFuseOptions
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Fuse from 'fuse.js'
import { defineStore } from 'pinia'
import { computed, ref, shallowRef } from 'vue'

Expand Down Expand Up @@ -250,24 +249,6 @@ export const useWorkflowTemplatesStore = defineStore(
return filteredTemplates
})

/**
* Fuse.js instance for advanced template searching and filtering
*/
const templateFuse = computed(() => {
const fuseOptions = {
keys: [
{ name: 'searchableText', weight: 0.4 },
{ name: 'title', weight: 0.3 },
{ name: 'name', weight: 0.2 },
{ name: 'tags', weight: 0.1 }
],
threshold: 0.3,
includeScore: true
}

return new Fuse(enhancedTemplates.value, fuseOptions)
})

/**
* Filter templates by category ID using stored filter mappings
*/
Expand Down Expand Up @@ -548,7 +529,6 @@ export const useWorkflowTemplatesStore = defineStore(
groupedTemplates,
navGroupedTemplates,
enhancedTemplates,
templateFuse,
filterTemplatesByCategory,
isLoaded,
loadWorkflowTemplates,
Expand Down
29 changes: 28 additions & 1 deletion src/scripts/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import type {
} from '@/platform/assets/schemas/assetSchema'
import { isCloud } from '@/platform/distribution/types'
import { useToastStore } from '@/platform/updates/common/toastStore'
import { type WorkflowTemplates } from '@/platform/workflow/templates/types/template'
import {
type TemplateInfo,
type WorkflowTemplates
} from '@/platform/workflow/templates/types/template'
import type {
ComfyApiWorkflow,
ComfyWorkflowJSON,
Expand Down Expand Up @@ -51,6 +54,7 @@ import type { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
import type { AuthHeader } from '@/types/authTypes'
import type { NodeExecutionId } from '@/types/nodeIdentification'
import { fetchHistory } from '@/platform/remote/comfyui/history'
import type { IFuseOptions } from 'fuse.js'

interface QueuePromptRequestBody {
client_id: string
Expand Down Expand Up @@ -1269,6 +1273,29 @@ export class ComfyApi extends EventTarget {
}
}

/**
* Gets the Fuse options from the server.
*
* @returns The Fuse options, or null if not found or invalid
*/
async getFuseOptions(): Promise<IFuseOptions<TemplateInfo> | null> {
try {
const res = await axios.get(
this.fileURL('/templates/fuse_options.json'),
{
headers: {
'Content-Type': 'application/json'
}
Comment on lines +1286 to +1288
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove or replace Content-Type header in GET request.

The Content-Type request header is semantically incorrect for GET requests, as it describes the type of the request body, but GET requests don't have a body. If you want to indicate the expected response format, use the Accept header instead. Alternatively, since you're already validating the response content-type on line 1291, you could remove this header entirely.

♻️ Option 1: Use Accept header instead
      const res = await axios.get(
        this.fileURL('/templates/fuse_options.json'),
        {
          headers: {
-           'Content-Type': 'application/json'
+           'Accept': 'application/json'
          }
        }
      )
♻️ Option 2: Remove the headers entirely (recommended)

Since you're already validating the response content-type on line 1291, the request header is unnecessary:

      const res = await axios.get(
-       this.fileURL('/templates/fuse_options.json'),
-       {
-         headers: {
-           'Content-Type': 'application/json'
-         }
-       }
+       this.fileURL('/templates/fuse_options.json')
      )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
headers: {
'Content-Type': 'application/json'
}
async getFuseOptions(): Promise<IFuseOptions<TemplateInfo> | null> {
try {
const res = await axios.get(
this.fileURL('/templates/fuse_options.json')
)
const contentType = res.headers['content-type']
return contentType?.includes('application/json') ? res.data : null
} catch (error) {
console.error('Error loading fuse options:', error)
return null
}
}
🤖 Prompt for AI Agents
In @src/scripts/api.ts around lines 1286 - 1288, The GET request currently sets
a 'Content-Type' request header in the headers object which is incorrect for
GETs; remove the 'Content-Type' entry from that headers block (or if you intend
to signal expected response type replace it with an 'Accept' header) and leave
the existing response content-type validation in place so behavior remains
unchanged.

}
)
const contentType = res.headers['content-type']
return contentType?.includes('application/json') ? res.data : null
} catch (error) {
console.error('Error loading fuse options:', error)
return null
}
}

/**
* Gets the custom nodes i18n data from the server.
*
Expand Down