From 8df0a3885de7aa8df2dceebb5e9e25da81c727d4 Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Sun, 2 Nov 2025 16:35:42 -0800 Subject: [PATCH] [feat] Rename license filter to 'Runs On' filter in template selector (#6543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Renamed the templates license filter to better reflect its actual purpose - showing where a template executes (locally in ComfyUI vs external/remote API). The current "License" filter has been causing confusion with model licensing terms (e.g., Apache vs flux-dev licensing). This PR clarifies the filter's purpose by renaming it to "Runs On" and updating the options to be more descriptive of inference location. image image ## Changes - **Filter name**: "License" → "Runs On" - **Filter options**: - "Open Source" → "ComfyUI" - "Closed Source (API Nodes)" → "External or Remote API" - **Icon**: Changed from `file-text` to `server` for better visual representation - **Variable naming**: Updated all related variables, types, and tests to use `runsOn` naming convention - **Telemetry**: Updated metadata to track `selected_runs_on` instead of `selected_licenses` ## Why "Runs On"? - **Clear intent**: Users want to know if a template runs locally or requires an API call - **Avoids confusion**: Separates the concept from model licensing terms - **Inclusive wording**: "Remote" is included alongside "API" to help users who may not be familiar with API terminology - **Cloud-agnostic**: "Runs On" works whether the app itself is running locally or in the cloud ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6543-feat-Rename-license-filter-to-Runs-On-filter-in-template-selector-29f6d73d3650811f935bc1f3fce7d7ad) by [Unito](https://www.unito.io) --- .../widget/WorkflowTemplateSelectorDialog.vue | 48 +++++++++---------- src/composables/useTemplateFiltering.ts | 38 +++++++-------- src/locales/en/main.json | 2 +- src/platform/telemetry/types.ts | 2 +- .../composables/useTemplateFiltering.test.ts | 11 ++--- 5 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue b/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue index eab22c01d..84e50bf62 100644 --- a/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue +++ b/src/components/custom/widget/WorkflowTemplateSelectorDialog.vue @@ -68,17 +68,17 @@ - + @@ -528,12 +528,12 @@ const { searchQuery, selectedModels, selectedUseCases, - selectedLicenses, + selectedRunsOn, sortBy, filteredTemplates, availableModels, availableUseCases, - availableLicenses, + availableRunsOn, filteredCount, totalCount, resetFilters @@ -561,15 +561,15 @@ const selectedUseCaseObjects = computed({ } }) -const selectedLicenseObjects = computed({ +const selectedRunsOnObjects = computed({ get() { - return selectedLicenses.value.map((license) => ({ - name: license, - value: license + return selectedRunsOn.value.map((runsOn) => ({ + name: runsOn, + value: runsOn })) }, set(value: { name: string; value: string }[]) { - selectedLicenses.value = value.map((item) => item.value) + selectedRunsOn.value = value.map((item) => item.value) } }) @@ -602,10 +602,10 @@ const useCaseOptions = computed(() => })) ) -const licenseOptions = computed(() => - availableLicenses.value.map((license) => ({ - name: license, - value: license +const runsOnOptions = computed(() => + availableRunsOn.value.map((runsOn) => ({ + name: runsOn, + value: runsOn })) ) @@ -634,14 +634,14 @@ const useCaseFilterLabel = computed(() => { } }) -const licenseFilterLabel = computed(() => { - if (selectedLicenseObjects.value.length === 0) { - return t('templateWorkflows.licenseFilter', 'License') - } else if (selectedLicenseObjects.value.length === 1) { - return selectedLicenseObjects.value[0].name +const runsOnFilterLabel = computed(() => { + if (selectedRunsOnObjects.value.length === 0) { + return t('templateWorkflows.runsOnFilter', 'Runs On') + } else if (selectedRunsOnObjects.value.length === 1) { + return selectedRunsOnObjects.value[0].name } else { - return t('templateWorkflows.licensesSelected', { - count: selectedLicenseObjects.value.length + return t('templateWorkflows.runsOnSelected', { + count: selectedRunsOnObjects.value.length }) } }) @@ -708,7 +708,7 @@ watch( sortBy, selectedModels, selectedUseCases, - selectedLicenses + selectedRunsOn ], () => { resetPagination() diff --git a/src/composables/useTemplateFiltering.ts b/src/composables/useTemplateFiltering.ts index f4e60a449..7c724e2d4 100644 --- a/src/composables/useTemplateFiltering.ts +++ b/src/composables/useTemplateFiltering.ts @@ -13,7 +13,7 @@ export function useTemplateFiltering( const searchQuery = ref('') const selectedModels = ref([]) const selectedUseCases = ref([]) - const selectedLicenses = ref([]) + const selectedRunsOn = ref([]) const sortBy = ref< | 'default' | 'alphabetical' @@ -63,8 +63,8 @@ export function useTemplateFiltering( return Array.from(tagSet).sort() }) - const availableLicenses = computed(() => { - return ['Open Source', 'Closed Source (API Nodes)'] + const availableRunsOn = computed(() => { + return ['ComfyUI', 'External or Remote API'] }) const debouncedSearchQuery = refDebounced(searchQuery, 50) @@ -108,21 +108,21 @@ export function useTemplateFiltering( }) }) - const filteredByLicenses = computed(() => { - if (selectedLicenses.value.length === 0) { + const filteredByRunsOn = computed(() => { + if (selectedRunsOn.value.length === 0) { return filteredByUseCases.value } return filteredByUseCases.value.filter((template) => { - // Check if template has API in its tags or name (indicating it's a closed source API node) + // Check if template has API in its tags or name (indicating it runs on external/remote API) const isApiTemplate = template.tags?.includes('API') || template.name?.toLowerCase().includes('api_') - return selectedLicenses.value.some((selectedLicense) => { - if (selectedLicense === 'Closed Source (API Nodes)') { + return selectedRunsOn.value.some((selectedRunsOn) => { + if (selectedRunsOn === 'External or Remote API') { return isApiTemplate - } else if (selectedLicense === 'Open Source') { + } else if (selectedRunsOn === 'ComfyUI') { return !isApiTemplate } return false @@ -142,7 +142,7 @@ export function useTemplateFiltering( } const sortedTemplates = computed(() => { - const templates = [...filteredByLicenses.value] + const templates = [...filteredByRunsOn.value] switch (sortBy.value) { case 'alphabetical': @@ -195,7 +195,7 @@ export function useTemplateFiltering( searchQuery.value = '' selectedModels.value = [] selectedUseCases.value = [] - selectedLicenses.value = [] + selectedRunsOn.value = [] sortBy.value = 'default' } @@ -207,8 +207,8 @@ export function useTemplateFiltering( selectedUseCases.value = selectedUseCases.value.filter((t) => t !== tag) } - const removeLicenseFilter = (license: string) => { - selectedLicenses.value = selectedLicenses.value.filter((l) => l !== license) + const removeRunsOnFilter = (runsOn: string) => { + selectedRunsOn.value = selectedRunsOn.value.filter((r) => r !== runsOn) } const filteredCount = computed(() => filteredTemplates.value.length) @@ -220,7 +220,7 @@ export function useTemplateFiltering( search_query: searchQuery.value || undefined, selected_models: selectedModels.value, selected_use_cases: selectedUseCases.value, - selected_licenses: selectedLicenses.value, + selected_runs_on: selectedRunsOn.value, sort_by: sortBy.value, filtered_count: filteredCount.value, total_count: totalCount.value @@ -229,14 +229,14 @@ export function useTemplateFiltering( // Watch for filter changes and track them watch( - [searchQuery, selectedModels, selectedUseCases, selectedLicenses, sortBy], + [searchQuery, selectedModels, selectedUseCases, selectedRunsOn, sortBy], () => { // Only track if at least one filter is active (to avoid tracking initial state) const hasActiveFilters = searchQuery.value.trim() !== '' || selectedModels.value.length > 0 || selectedUseCases.value.length > 0 || - selectedLicenses.value.length > 0 || + selectedRunsOn.value.length > 0 || sortBy.value !== 'default' if (hasActiveFilters) { @@ -251,14 +251,14 @@ export function useTemplateFiltering( searchQuery, selectedModels, selectedUseCases, - selectedLicenses, + selectedRunsOn, sortBy, // Computed filteredTemplates, availableModels, availableUseCases, - availableLicenses, + availableRunsOn, filteredCount, totalCount, @@ -266,6 +266,6 @@ export function useTemplateFiltering( resetFilters, removeModelFilter, removeUseCaseFilter, - removeLicenseFilter + removeRunsOnFilter } } diff --git a/src/locales/en/main.json b/src/locales/en/main.json index b89d46720..dc0c73ec6 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -766,7 +766,7 @@ "modelFilter": "Model Filter", "modelsSelected": "{count} Models", "useCasesSelected": "{count} Use Cases", - "licensesSelected": "{count} Licenses", + "runsOnSelected": "{count} Runs On", "resultsCount": "Showing {count} of {total} templates", "sort": { "recommended": "Recommended", diff --git a/src/platform/telemetry/types.ts b/src/platform/telemetry/types.ts index abe517bca..27253bddd 100644 --- a/src/platform/telemetry/types.ts +++ b/src/platform/telemetry/types.ts @@ -190,7 +190,7 @@ export interface TemplateFilterMetadata { search_query?: string selected_models: string[] selected_use_cases: string[] - selected_licenses: string[] + selected_runs_on: string[] sort_by: | 'default' | 'alphabetical' diff --git a/tests-ui/tests/composables/useTemplateFiltering.test.ts b/tests-ui/tests/composables/useTemplateFiltering.test.ts index 8fbdf59fd..4a83aa9f8 100644 --- a/tests-ui/tests/composables/useTemplateFiltering.test.ts +++ b/tests-ui/tests/composables/useTemplateFiltering.test.ts @@ -101,11 +101,11 @@ describe('useTemplateFiltering', () => { searchQuery, selectedModels, selectedUseCases, - selectedLicenses, + selectedRunsOn, filteredTemplates, availableModels, availableUseCases, - availableLicenses, + availableRunsOn, filteredCount, totalCount, removeUseCaseFilter, @@ -120,10 +120,7 @@ describe('useTemplateFiltering', () => { 'Portrait', 'Video' ]) - expect(availableLicenses.value).toEqual([ - 'Open Source', - 'Closed Source (API Nodes)' - ]) + expect(availableRunsOn.value).toEqual(['ComfyUI', 'External or Remote API']) searchQuery.value = 'enterprise' await nextTick() @@ -133,7 +130,7 @@ describe('useTemplateFiltering', () => { 'api-template' ]) - selectedLicenses.value = ['Closed Source (API Nodes)'] + selectedRunsOn.value = ['External or Remote API'] await nextTick() expect(filteredTemplates.value.map((template) => template.name)).toEqual([ 'api-template'