-
Notifications
You must be signed in to change notification settings - Fork 428
feat: usage-based template ordering #7062
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add usage-based ordering to workflow templates with position bias correction, manual ranking (searchRank), and freshness boost. New sort modes: - "Recommended" (default): usage × 0.5 + searchRank × 0.3 + freshness × 0.2 - "Popular": usage × 0.9 + freshness × 0.1 Includes generation script for refreshing scores from Mixpanel data.
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 11/30/2025, 10:11:21 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
📝 WalkthroughWalkthroughIntroduces a usage-based template ranking system consisting of a CLI script to generate scores from Mixpanel data, a pre-computed scores asset, a new store to manage ranking computations, and integration into the template filtering and UI components. Default sort behavior changed to "Recommended" with new "Popular" sort option. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as WorkflowTemplateSelectorDialog
participant Filter as useTemplateFiltering
participant Store as templateRankingStore
participant Asset as template-usage-scores.json
UI->>Store: loadScores() (parallel init)
activate Store
Store->>Asset: fetch JSON
Asset-->>Store: usage scores loaded
Store-->>UI: scores ready
deactivate Store
UI->>Filter: render with sortBy='default'
activate Filter
alt Sorting by 'default' (Recommended)
Filter->>Store: computeDefaultScore(name, date, searchRank)
activate Store
Store->>Store: score = 0.5×usage + 0.3×internal + 0.2×freshness
Store-->>Filter: composite score
deactivate Store
else Sorting by 'popular'
Filter->>Store: computePopularScore(name, date)
activate Store
Store->>Store: score = 0.9×usage + 0.1×freshness
Store-->>Filter: composite score
deactivate Store
end
Filter->>Filter: sort templates by scores
Filter-->>UI: sorted template list
deactivate Filter
UI->>UI: render ranked templates
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
🎭 Playwright Test Results❌ Some tests failed ⏰ Completed at: 11/30/2025, 10:19:53 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.18 MB (baseline 3.18 MB) • 🔴 +43 BMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 952 kB (baseline 950 kB) • 🔴 +2.58 kBGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 139 kB (baseline 139 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 BBundles that do not match a named category
Status: 17 added / 17 removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (8)
scripts/generate-template-scores.ts (3)
246-253: Handle edge case whencorrectedScoresis empty.If
rawDatais empty (which shouldn't happen given the earlier check inparseCSV, but could occur ifcomputeNormalizedScoresis called directly),Math.max(...correctedScores.map(...))returns-Infinity, causing all normalized scores to becomeNaN.Consider a defensive check:
// Normalize to 0-1 const maxScore = Math.max(...correctedScores.map((s) => s.score)) + if (maxScore <= 0 || !Number.isFinite(maxScore)) { + console.warn('No valid scores to normalize') + return {} + } const normalized: Record<string, number> = {}
157-176: CSV parsing doesn't handle escaped quotes.The
parseCSVLinefunction doesn't handle RFC 4180-compliant escaped quotes (e.g.,"value with ""escaped"" quotes"). This may cause parsing errors with template names containing quotes.For a dev tool, this is likely acceptable. If robustness is needed later, consider using a CSV parsing library or handling doubled quotes:
for (const char of line) { - if (char === '"' && !inQuotes) { + if (char === '"' && !inQuotes) { inQuotes = true - } else if (char === '"' && inQuotes) { - inQuotes = false + } else if (char === '"' && inQuotes) { + // Check for escaped quote "" + const nextIdx = line.indexOf(char) + 1 + if (line[nextIdx] === '"') { + current += '"' + // Skip next quote (would need different iteration approach) + } else { + inQuotes = false + }
275-283: Minor: Negative count displayed when fewer than 20 scores.In dry-run mode, if there are fewer than 20 templates, line 281 displays a negative number (e.g., "... and -5 more").
- console.log(` ... and ${Object.keys(usageScores).length - 20} more`) + const remaining = Object.keys(usageScores).length - 20 + if (remaining > 0) { + console.log(` ... and ${remaining} more`) + }docs/TEMPLATE_RANKING.md (2)
22-24: Add language specifier to fenced code block.Per markdownlint, fenced code blocks should have a language specified for proper rendering and syntax highlighting.
-``` +```text public/assets/template-usage-scores.json # { "template_name": 0.95, ... } normalized 0-1 -``` +```
47-50: Add language specifier to fenced code block.Same issue as above - add a language specifier for the formula block.
-``` +```text correction = 1 + (position - 1) / (maxPosition - 1) normalizedUsage = rawUsage × correction -``` +```tests-ui/stores/templateRankingStore.test.ts (1)
91-97: Test could be more meaningful.This test calls
computePopularScoretwice with identical arguments and verifies they're equal, which is trivially true. Consider comparing againstcomputeDefaultScoreto demonstrate thatpopulartruly ignoressearchRank.describe('computePopularScore', () => { it('does not use searchRank', () => { const store = useTemplateRankingStore() - // Popular score ignores searchRank - just usage + freshness - const score1 = store.computePopularScore('template', '2024-01-01') - const score2 = store.computePopularScore('template', '2024-01-01') - expect(score1).toBe(score2) + // Popular score ignores searchRank - verify same result regardless of what + // searchRank would be if it were used + const popularScore = store.computePopularScore('template', '2024-01-01') + const defaultScoreLowRank = store.computeDefaultScore('template', '2024-01-01', 1) + const defaultScoreHighRank = store.computeDefaultScore('template', '2024-01-01', 10) + // Default scores differ due to searchRank, but popular is consistent + expect(defaultScoreHighRank).toBeGreaterThan(defaultScoreLowRank) + // Popular score should be deterministic (no searchRank influence) + expect(store.computePopularScore('template', '2024-01-01')).toBe(popularScore) })src/stores/templateRankingStore.ts (1)
53-63: Consider documenting or clamping out-of-range searchRank values.The normalization
(searchRank ?? 5) / 10allows values outside the documented 1-10 range (e.g., 0 or 15), resulting in scores outside 0-1. If this is intentional flexibility, add a comment; otherwise, consider clamping:const internal = Math.max(0, Math.min(1, (searchRank ?? 5) / 10))src/components/custom/widget/WorkflowTemplateSelectorDialog.vue (1)
649-658: Validate new sort options (default/popular) and i18n entriesThe new sort options:
'default'→t('templateWorkflows.sort.default', 'Recommended')'popular'→t('templateWorkflows.sort.popular', 'Popular')'newest'→ unchanged but now listed after the new modesLook good, but please double-check that:
useTemplateFiltering(and any related telemetry) supports the newsortByvalues'default'and'popular'consistently, including default initialization.- The i18n keys
templateWorkflows.sort.defaultandtemplateWorkflows.sort.popularare defined insrc/locales/en/main.jsonso we don’t rely on the fallback English strings at runtime. As per coding guidelines, new user-facing strings should be registered in the locale JSON.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
docs/TEMPLATE_RANKING.md(1 hunks)package.json(1 hunks)public/assets/template-usage-scores.json(1 hunks)scripts/generate-template-scores.ts(1 hunks)src/components/custom/widget/WorkflowTemplateSelectorDialog.vue(4 hunks)src/composables/useTemplateFiltering.ts(5 hunks)src/locales/en/main.json(1 hunks)src/platform/settings/constants/coreSettings.ts(1 hunks)src/platform/telemetry/types.ts(1 hunks)src/platform/workflow/templates/types/template.ts(1 hunks)src/schemas/apiSchema.ts(1 hunks)src/stores/templateRankingStore.ts(1 hunks)tests-ui/stores/templateRankingStore.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (25)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
src/**/*.{vue,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.ts
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/composables/useTemplateFiltering.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tstests-ui/stores/templateRankingStore.test.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/composables/useTemplateFiltering.tsscripts/generate-template-scores.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/platform/workflow/templates/types/template.tssrc/platform/settings/constants/coreSettings.tssrc/platform/telemetry/types.tssrc/schemas/apiSchema.tssrc/stores/templateRankingStore.tssrc/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.ts
tests-ui/**/*.test.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)
tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks
Files:
tests-ui/stores/templateRankingStore.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/stores/templateRankingStore.test.ts
src/**/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores
Files:
src/stores/templateRankingStore.ts
**/stores/*Store.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name Pinia stores with the
*Store.tssuffix
Files:
src/stores/templateRankingStore.ts
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventionsFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vue**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/**/{composables,components}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Clean up subscriptions in state management to prevent memory leaks
Files:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tssrc/**/{components,composables}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Use vue-i18n for ALL user-facing strings by adding them to
src/locales/en/main.jsonFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/composables/useTemplateFiltering.tssrc/components/**/*.vue
📄 CodeRabbit inference engine (src/components/CLAUDE.md)
src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue componentsFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/components/**/*.{vue,css}
📄 CodeRabbit inference engine (src/components/CLAUDE.md)
src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system packageFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/components/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (src/components/CLAUDE.md)
src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI stringsFiles:
src/components/custom/widget/WorkflowTemplateSelectorDialog.vuesrc/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directlyFiles:
src/composables/useTemplateFiltering.ts**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.tsFiles:
src/composables/useTemplateFiltering.ts🧠 Learnings (16)
📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.{ts,tsx,js} : Use es-toolkit for utility functionsApplied to files:
package.json📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/*.{ts,tsx,js,vue} : Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in `.prettierrc`Applied to files:
package.json📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/*.{ts,tsx,js,vue} : Organize imports by sorting and grouping by plugin, and run `pnpm format` before committingApplied to files:
package.json📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/lib/litegraph/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:56.371Z Learning: Applies to src/lib/litegraph/**/*.{js,ts,jsx,tsx} : Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the `pnpm lint:fix` commandApplied to files:
package.json📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/*.{ts,tsx,vue} : Enforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templatesApplied to files:
package.json📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:14.779Z Learning: Applies to **/*.{ts,tsx,vue} : Use `await settingStore.set('Comfy.SomeSetting', newValue)` to update settings in TypeScript/Vue filesApplied to files:
src/platform/settings/constants/coreSettings.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new featuresApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebaseApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocksApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/stores/*Store.ts : Name Pinia stores with the `*Store.ts` suffixApplied to files:
tests-ui/stores/templateRankingStore.test.tssrc/stores/templateRankingStore.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in testsApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/lib/litegraph/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:56.371Z Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setupApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:34.324Z Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in storesApplied to files:
tests-ui/stores/templateRankingStore.test.tssrc/stores/templateRankingStore.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilitiesApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:48:23.088Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Use Vitest (with happy-dom) for unit and component tests, and Playwright for E2E testsApplied to files:
tests-ui/stores/templateRankingStore.test.ts📚 Learning: 2025-11-24T19:47:14.779Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:14.779Z Learning: Applies to **/*.{ts,tsx,vue} : Use `const settingStore = useSettingStore()` and `settingStore.get('Comfy.SomeSetting')` to retrieve settings in TypeScript/Vue filesApplied to files:
src/stores/templateRankingStore.tssrc/composables/useTemplateFiltering.ts🧬 Code graph analysis (3)
tests-ui/stores/templateRankingStore.test.ts (1)
src/stores/templateRankingStore.ts (1)
useTemplateRankingStore(12-87)src/composables/useTemplateFiltering.ts (2)
src/platform/workflow/templates/types/template.ts (1)
TemplateInfo(1-40)src/stores/templateRankingStore.ts (1)
useTemplateRankingStore(12-87)scripts/generate-template-scores.ts (1)
src/platform/workflow/templates/types/template.ts (2)
TemplateInfo(1-40)WorkflowTemplates(42-51)🪛 ESLint
tests-ui/stores/templateRankingStore.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/fPPvRFUvUi'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)(import-x/namespace)
[error] 1-1: Resolve error: EACCES: permission denied, open '/slcyuLISSN'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)(import-x/no-unresolved)
[error] 1-1: Resolve error: EACCES: permission denied, open '/DVbwyMrubx'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)(import-x/no-relative-packages)
[error] 1-1: Unable to resolve path to module 'pinia'.
(import-x/no-unresolved)
[error] 2-2: Unable to resolve path to module 'vitest'.
(import-x/no-unresolved)
[error] 4-4: Unable to resolve path to module '@/stores/templateRankingStore'.
(import-x/no-unresolved)
src/stores/templateRankingStore.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/isJetaxmCl'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/namespace)
[error] 1-1: Resolve error: EACCES: permission denied, open '/qdlfAaOIvB'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/no-unresolved)
[error] 1-1: Resolve error: EACCES: permission denied, open '/RqqBNdcyHj'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at checkDefault (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/default.js:22:39)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/default)
[error] 1-1: Resolve error: EACCES: permission denied, open '/JtSqhijzTP'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/no-relative-packages)
[error] 8-8: Unable to resolve path to module 'axios'.
(import-x/no-unresolved)
[error] 9-9: Unable to resolve path to module 'pinia'.
(import-x/no-unresolved)
[error] 10-10: Unable to resolve path to module 'vue'.
(import-x/no-unresolved)
src/components/custom/widget/WorkflowTemplateSelectorDialog.vue
[error] 408-408: Unable to resolve path to module '@/stores/templateRankingStore'.
(import-x/no-unresolved)
src/composables/useTemplateFiltering.ts
[error] 9-9: Unable to resolve path to module '@/stores/templateRankingStore'.
(import-x/no-unresolved)
[error] 10-10: Unable to resolve path to module 'es-toolkit/compat'.
(import-x/no-unresolved)
🪛 markdownlint-cli2 (0.18.1)
docs/TEMPLATE_RANKING.md
22-22: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
47-47: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: setup
- GitHub Check: lint-and-format
- GitHub Check: test
- GitHub Check: collect
🔇 Additional comments (17)
package.json (1)
28-28: LGTM!The new script follows the existing pattern of using
tsxto run TypeScript scripts directly, consistent with other scripts likejson-schema.src/schemas/apiSchema.ts (1)
514-521: LGTM!The enum additions for
'popular'and'model-size-low-to-high'sort options are well-placed and align with the new ranking-based sorting feature. The schema correctly reflects the expanded sort options used by the UI and ranking logic.src/platform/workflow/templates/types/template.ts (1)
35-39: LGTM!The
searchRankproperty is well-documented with clear scale semantics (1-10, default 5). The optional typing is appropriate since not all templates will have manual ranking overrides.scripts/generate-template-scores.ts (1)
178-209: LGTM!The
loadUIOrderfunction handles both local file and remote GitHub fetch cleanly, with proper error handling for HTTP failures. Supporting bothWorkflowTemplates[]and{ modules: [...] }formats provides good flexibility.src/platform/settings/constants/coreSettings.ts (1)
1079-1084: LGTM! Default sort changed to "Recommended".The default value change from
'newest'to'default'(which maps to "Recommended" in the UI) aligns with the PR objectives. Existing users who have previously saved a preference will retain their choice since this only affects the initial default.src/locales/en/main.json (1)
853-859: LGTM!The new "popular" sort option and the renaming of "default" to "Recommended" are consistent with the PR objectives. The labels clearly communicate the sorting behavior to users.
public/assets/template-usage-scores.json (1)
1-188: LGTM!The pre-computed usage scores are properly normalized (0-1 range) and structured as expected by
templateRankingStore. The data aligns with the documented position bias correction methodology.src/platform/telemetry/types.ts (1)
196-203: LGTM!The
sort_byunion type correctly includes the new'popular'option, maintaining type safety for telemetry tracking.docs/TEMPLATE_RANKING.md (1)
1-95: Documentation is comprehensive and well-structured.The ranking system documentation clearly explains the formulas, data files, position bias correction, and update workflow. This will help future maintainers understand and update the ranking system.
tests-ui/stores/templateRankingStore.test.ts (2)
122-127: Consider adding a note about unclamped searchRank behavior.The test documents that values above 10 aren't clamped. If this is intentional (allowing power users to boost templates beyond the documented scale), consider adding a comment in the store. If unintentional, the store should clamp values to 1-10.
1-136: Good test coverage for the new ranking store.The tests comprehensively cover freshness calculations, default/popular scoring, usage score retrieval, and edge cases. Following learnings, the tests use Vitest mocks appropriately and follow existing patterns.
src/stores/templateRankingStore.ts (2)
1-87: Well-structured Pinia store with clear public interface.The store follows project conventions (naming, TypeScript types, composition API pattern). The scoring functions are well-documented with formulas matching the documentation.
19-26: Useapi.fileURL()for static asset access.Replace the direct URL string with
api.fileURL('assets/template-usage-scores.json')to ensure assets resolve correctly across all deployment contexts. This aligns with the pattern used throughout the codebase in services and composables for static file access.try { const response = await axios.get(api.fileURL('assets/template-usage-scores.json')) usageScores.value = response.data isLoaded.value = true } catch (error) { console.error('Error loading template ranking scores:', error) } }⛔ Skipped due to learnings
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-24T19:48:23.088Z Learning: Applies to **/stores/*Store.ts : Name Pinia stores with the `*Store.ts` suffixLearnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:34.324Z Learning: Applies to src/**/stores/**/*.{ts,tsx} : Maintain clear public interfaces and restrict extension access in storesLearnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:34.324Z Learning: Applies to src/**/{services,composables}/**/*.{ts,tsx} : Use `api.fileURL()` for static file access instead of constructing URLs directlysrc/composables/useTemplateFiltering.ts (4)
161-182: LGTM!The new
'default'and'popular'sort cases correctly integrate with the ranking store, usingcomputeDefaultScoreandcomputePopularScorerespectively. The descending sort order (scoreB - scoreA) correctly ranks higher scores first.
212-212: Good improvement - removed implicit any.Removing the explicit
(a: any, b: any)type annotation improves type safety. TypeScript will now correctly inferTemplateInfotypes for the sort callback parameters.
232-232: Default sort reset aligns with new "Recommended" behavior.Changing
resetFiltersto setsortBy.value = 'default'is consistent with making "Recommended" the default sorting mode.
16-16: VerifyloadScores()is called before filtering.The composable uses
rankingStorebut doesn't callloadScores(). Ensure the scores are loaded before this composable computes sorted templates, otherwise all usage scores will be 0.
| for (let i = 0; i < args.length; i++) { | ||
| const arg = args[i] | ||
| switch (arg) { | ||
| case '--input': | ||
| case '-i': | ||
| config.inputPath = args[++i] | ||
| break | ||
| case '--ui-order': | ||
| case '-u': | ||
| config.uiOrderPath = args[++i] | ||
| break | ||
| case '--output': | ||
| case '-o': | ||
| config.outputDir = args[++i] | ||
| break | ||
| case '--dry-run': | ||
| config.dryRun = true | ||
| break | ||
| case '--help': | ||
| case '-h': | ||
| printHelp() | ||
| process.exit(0) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing bounds check when accessing argument values.
When parsing --input, --ui-order, or --output, the code increments i and accesses args[i] without verifying the value exists. If a user runs pnpm generate:template-scores --input without a path, this returns undefined silently.
Consider adding validation:
case '--input':
case '-i':
+ if (!args[i + 1] || args[i + 1].startsWith('-')) {
+ console.error('Error: --input requires a path argument')
+ process.exit(1)
+ }
config.inputPath = args[++i]
break📝 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.
| for (let i = 0; i < args.length; i++) { | |
| const arg = args[i] | |
| switch (arg) { | |
| case '--input': | |
| case '-i': | |
| config.inputPath = args[++i] | |
| break | |
| case '--ui-order': | |
| case '-u': | |
| config.uiOrderPath = args[++i] | |
| break | |
| case '--output': | |
| case '-o': | |
| config.outputDir = args[++i] | |
| break | |
| case '--dry-run': | |
| config.dryRun = true | |
| break | |
| case '--help': | |
| case '-h': | |
| printHelp() | |
| process.exit(0) | |
| } | |
| for (let i = 0; i < args.length; i++) { | |
| const arg = args[i] | |
| switch (arg) { | |
| case '--input': | |
| case '-i': | |
| if (!args[i + 1] || args[i + 1].startsWith('-')) { | |
| console.error('Error: --input requires a path argument') | |
| process.exit(1) | |
| } | |
| config.inputPath = args[++i] | |
| break | |
| case '--ui-order': | |
| case '-u': | |
| config.uiOrderPath = args[++i] | |
| break | |
| case '--output': | |
| case '-o': | |
| config.outputDir = args[++i] | |
| break | |
| case '--dry-run': | |
| config.dryRun = true | |
| break | |
| case '--help': | |
| case '-h': | |
| printHelp() | |
| process.exit(0) | |
| } |
🤖 Prompt for AI Agents
In scripts/generate-template-scores.ts around lines 56 to 78, the switch
increments i and reads args[i] for --input, --ui-order and --output without
checking bounds; add a check after incrementing (or before assigning) to ensure
args[i] exists and is not an empty string, and if missing log a clear error
(include which flag is missing its value) and exit with a non‑zero code (or call
printHelp()) so the script doesn't silently set undefined; update tests or usage
message accordingly.
| function computePositionCorrection( | ||
| position: number, | ||
| maxPosition: number | ||
| ): number { | ||
| // Linear interpolation: position 1 = 1.0×, position max = 2.0× | ||
| // Buried templates get proportionally stronger boost | ||
| return 1 + (position - 1) / (maxPosition - 1) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential division by zero when maxPosition is 1.
If there's only one template in the UI order, maxPosition - 1 equals zero, causing division by zero.
Consider adding a guard:
function computePositionCorrection(
position: number,
maxPosition: number
): number {
// Linear interpolation: position 1 = 1.0×, position max = 2.0×
// Buried templates get proportionally stronger boost
+ if (maxPosition <= 1) return 1
return 1 + (position - 1) / (maxPosition - 1)
}🤖 Prompt for AI Agents
In scripts/generate-template-scores.ts around lines 215 to 222,
computePositionCorrection divides by (maxPosition - 1) which will be zero when
maxPosition is 1; add a guard at the top that returns 1 (no boost) when
maxPosition <= 1 (or when maxPosition - 1 === 0) to avoid the division by zero,
otherwise proceed with the existing linear interpolation; ensure the function
signature and any callers still expect a number and update any unit tests if
present.
| import { useTemplateWorkflows } from '@/platform/workflow/templates/composables/useTemplateWorkflows' | ||
| import { useWorkflowTemplatesStore } from '@/platform/workflow/templates/repositories/workflowTemplatesStore' | ||
| import type { TemplateInfo } from '@/platform/workflow/templates/types/template' | ||
| import { useTemplateRankingStore } from '@/stores/templateRankingStore' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix unresolved useTemplateRankingStore import and consider robustness of loadScores
- ESLint reports
import-x/no-unresolvedon@/stores/templateRankingStore. That will break lint/build. Please verify the store file path (e.g.src/stores/templateRankingStore.ts) and either:- Adjust the import to match the actual location/name, or
- Move/rename the store file so it matches this path.
templateRankingStore.loadScores()is now part of thePromise.allinuseAsyncState. If score loading is non-critical for showing the dialog, consider making this more defensive so a failure to fetch ranking data doesn’t block the whole template selector (e.g. wraploadScoresin an internal try/catch or switch toPromise.allSettledand only treat template-loading failures as fatal).
Also applies to: 447-447, 759-764
🧰 Tools
🪛 ESLint
[error] 408-408: Unable to resolve path to module '@/stores/templateRankingStore'.
(import-x/no-unresolved)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like this to rather be a github workflow on the templates repo and the usage scores to also come from there.
If we want to have it here, then it will be nice to have this workflow communicate with mixpanel on some regular basis and update the json like we have with snapshots
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, good point - most of this should go in Comfy-Org/workflow_templates
Add usage-based ordering to workflow templates with position bias correction, manual ranking (searchRank), and freshness boost.
New sort modes:
Includes generation script for refreshing scores from Mixpanel data.
New default ordering:
┆Issue is synchronized with this Notion page by Unito