feat: add HoneyToast component for persistent progress notifications#7902
feat: add HoneyToast component for persistent progress notifications#7902
Conversation
📝 WalkthroughWalkthroughReplaces the manager progress dialog with a HoneyToast-based toast UI, adds HoneyToast and ProgressToastItem stories/tests, removes dialog-based manager progress APIs and related components/tests, updates GraphView and ModelImportProgressDialog to use the toast, and enables Storybook MCP addon. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ManagerToast as ManagerProgressToast
participant Settings as SettingsStore
participant Manager as ComfyManagerService
participant Workflow as WorkflowService
participant Events as UserEvents
User->>ManagerToast: Click "Apply Changes" (restart)
ManagerToast->>Settings: setToastSuppressReconnect(true)
ManagerToast->>Manager: requestReboot()
Manager->>Events: emit "reconnecting"
Note right of Manager: Comfy manager restarts...
Manager->>Events: emit "reconnected"
Events->>ManagerToast: onReconnected
ManagerToast->>Workflow: refreshNodeDefinitions()
ManagerToast->>Workflow: reloadCurrentWorkflow()
ManagerToast->>Manager: runConflictAnalysis()
ManagerToast->>Settings: setToastSuppressReconnect(false)
ManagerToast->>User: show success / update status
Possibly related PRs
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 01/08/2026, 10:57:55 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results⏰ Completed at: 01/08/2026, 11:03:47 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.23 MB (baseline 3.25 MB) • 🟢 -17.9 kBMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 1.05 MB (baseline 1.03 MB) • 🔴 +14.7 kBGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.63 kB (baseline 6.63 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 300 kB (baseline 300 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 197 kB (baseline 197 kB) • ⚪ 0 BReusable component library chunks
Status: 9 added / 9 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 1.41 kB (baseline 1.41 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 9.19 MB (baseline 9.19 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Status: 1 added / 1 removed Other — 3.6 MB (baseline 3.6 MB) • ⚪ 0 BBundles that do not match a named category
Status: 17 added / 17 removed |
dcce2e1 to
2179ffb
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
gentle nudge to test this functionality before merge
| :close-on-escape="true" | ||
| unstyled | ||
| :pt="{ | ||
| root: { class: 'absolute z-50' }, |
There was a problem hiding this comment.
should we read the z-index from a globals file to understand where this sits in the application stack?
There was a problem hiding this comment.
If only it were that easy.
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Positioned_layout/Stacking_context
| </span> | ||
| </div> | ||
| </div> | ||
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> |
There was a problem hiding this comment.
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> | |
| <div class="relative max-h-[17.5rem] overflow-y-auto px-4 py-4"> |
Is this better?
Amp-Thread-ID: https://ampcode.com/threads/T-019b9c4f-05d4-76b8-8db4-c6eb924b5bdf Co-authored-by: Amp <[email protected]>
…st.vue Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
🔧 Auto-fixes AppliedThis PR has been automatically updated to fix linting and formatting issues.
Changes made:
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Fix all issues with AI agents
In @src/platform/assets/components/ModelImportProgressDialog.vue:
- Around line 144-147: The static decorative scrollbar indicator rendered with
the v-if check on filteredJobs.length in ModelImportProgressDialog.vue can
mislead users; either remove that element entirely or make it functional:
replace the v-if="filteredJobs.length > 3" block with a reactive indicator tied
to the scroll position of the jobs container (use VueUse's useScroll on the
container ref), compute visibility from the container's scrollHeight vs
clientHeight (instead of item count), and bind the indicator's position/height
to the scroll progress so it accurately reflects scrollTop and scrollable range;
update any refs used (e.g., the jobs container ref and filteredJobs) and ensure
cleanup of watchers.
- Around line 95-104: The chevron icon on the filter Button (currently hardcoded
as "icon-[lucide--chevron-down]") doesn't reflect the popover state; add a
reactive boolean (e.g., popoverOpen), update it when the popover opens/closes
(or toggle it inside onFilterClick if that controls visibility), and replace the
static chevron with a conditional render or class toggle (e.g., show
"icon-[lucide--chevron-up]" or apply a rotation class when popoverOpen is true)
so the Button (referenced by Button, onFilterClick, activeFilterLabel) correctly
indicates open vs closed state.
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue:
- Line 302: The template in ManagerProgressToast.vue contains hardcoded emoji
spans (e.g., the <span class="mr-2">🎉</span> and the similar check emoji) which
should be moved into i18n strings or replaced with the app's icon components for
consistency; update the template to use either
$t('managerProgress.successEmoji') / $t('managerProgress.doneEmoji') or swap the
spans for the existing icon component (e.g., <Icon name="celebrate" /> and <Icon
name="check" />) wherever the emojis appear, and add corresponding i18n keys or
icon imports so the visual indicators are configurable and consistent with the
rest of the UI.
- Around line 3-4: ManagerProgressToast.vue currently imports PrimeVue
components Panel and TabMenu; remove these imports and any local
registration/usage of the symbols Panel and TabMenu, and replace them with
design-system or Tailwind-based alternatives: implement a simple
disclosure/collapsible panel pattern for the Panel usage and a small custom tab
component (or remove tabbing if unnecessary) for TabMenu usage, updating the
template markup and component registration (e.g., the components object or setup
imports) to reference the new implementations instead of Panel/TabMenu while
preserving existing props/events used by the current template.
- Line 251: Replace hardcoded Tailwind color utility classes in
ManagerProgressToast.vue: change the element using the class text-neutral-300 to
the semantic token text-muted-foreground, and change the element using bg-black
to the appropriate theme semantic token such as bg-base-background or
bg-node-component-surface (per your theme) so components use style.css semantic
colors; ensure no dark: variant is introduced and update any occurrences of
these class names in the template of the ManagerProgressToast component.
- Around line 257-259: In ManagerProgressToast.vue replace PrimeVue icon class
strings with the project's Lucide icon utility classes: change 'pi
pi-chevron-right' -> 'icon-[lucide--chevron-right]', 'pi pi-chevron-down' ->
'icon-[lucide--chevron-down]', 'pi pi-chevron-up' -> 'icon-[lucide--chevron-up]'
and 'pi pi-times' -> 'icon-[lucide--x]'; update the occurrences referenced in
the component (the conditional icon around the chevron at the ternary, the
chevron at the second occurrence, and the close icon) so the component uses the
consistent Lucide class pattern used elsewhere (e.g., HoneyToast).
- Around line 208-217: Remove the PrimeVue-specific :pt prop block from the
TabMenu usage and replace it with Tailwind utility classes; specifically, keep
the existing TabMenu element (v-model:active-index="activeTabIndex"
:model="tabs") but delete the :pt object and instead apply Tailwind classes
directly (e.g., to the TabMenu root via its class prop and to tab/menu item
elements by adding utility classes or by embedding class names in the tabs model
items). Update any styling previously set in :pt (menu border-none, menuitem
font-medium, action px-4 py-2) to equivalent Tailwind classes on the TabMenu
root, on individual tab items (via the tabs data or slot templates), or via a
small scoped CSS/Tailwind token helper so styling follows the Tailwind-first
guideline.
- Around line 223-226: The inline scrollbar style on the container in
ManagerProgressToast.vue should be replaced with Tailwind classes: remove the
:style object and add appropriate class names like "scrollbar-thin", a thumb
color utility (e.g. "scrollbar-thumb-[rgba(156,163,175,0.5)]" or your
design-system equivalent) and a transparent track class (e.g.
"scrollbar-track-transparent") to the same element; ensure the Tailwind
scrollbar plugin or custom utilities are enabled in your Tailwind config so
those classes resolve.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
AGENTS.mdsrc/components/honeyToast/HoneyToast.stories.tssrc/platform/assets/components/ModelImportProgressDialog.vuesrc/workbench/extensions/manager/components/ManagerProgressToast.vue
🧰 Additional context used
📓 Path-based instructions (13)
src/**/*.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 conventions
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
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/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.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
src/**/*.{ts,tsx,vue}: TypeScript files must use exclusive TypeScript; no new JavaScript files
Use VueUse functions for performance-enhancing styles
Use es-toolkit for utility functions
Implement proper error handling in Vue components and async code
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Clean up subscriptions in state management to prevent memory leaks
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
src/**/{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.json
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
**/*.vue: Vue 3.5+ components must use TypeScript with Composition API and <script setup lang="ts">
Use Tailwind 4 for styling; avoid <style> blocks in Vue components
Avoid new usage of PrimeVue components
Never usedark:Tailwind variant; use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from '@/utils/tailwindUtil' for merging class names; never use :class="[]"
Never use !important or ! prefix for Tailwind classes; find and fix interfering !important classes instead
Use default prop declaration with destructuring in Vue 3.5 TypeScript style; avoid withDefaults and runtime props
Prefer useModel over separate prop and emit definitions in Vue components
Be judicious with addition of new refs and state in Vue; use props directly, computed instead of watch, useModel instead of prop+emit when appropriate
Use provide/inject for dependency injection only when appropriate; prefer Store or shared composable for simplicity
Utilize Vue 3 Teleport component when needed for portal-style rendering
Use Suspense for async components in Vue 3
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,vue}: Use separateimport typestatements; avoid inline type imports mixed with value imports
ESLint rules must enforce Vue + TypeScript rules, no floating promises, no unused imports, and i18n raw text restrictions in templates
Never useanytype oras anytype assertions; use proper TypeScript types
Keep functions short and functional; minimize nesting with early returns and reduce arrow anti-patterns
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions over function expressions; use function declarations when possible
Write expressive, self-documenting code; minimize unnecessary comments and avoid redundant comments
Ask 'is there a simpler way?' when writing code; use refactoring to make complex code simpler
Watch out for Code Smells and refactor to avoid them
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
**/*.{ts,tsx,vue,js,jsx,json,css,md}
📄 CodeRabbit inference engine (AGENTS.md)
Format code with Prettier using 2-space indent, single quotes, no trailing semicolons, 80-character width
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.tsAGENTS.md
{src/**/*.{ts,tsx,vue},**/*.test.ts}
📄 CodeRabbit inference engine (AGENTS.md)
Use vue-i18n in Composition API for string literals; place translation entries in src/locales/en/main.json and use i18n plurals system
Files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.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/components/honeyToast/HoneyToast.stories.ts
src/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 strings
Files:
src/components/honeyToast/HoneyToast.stories.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Minimize surface area (exported values) of each module and composable
Files:
src/components/honeyToast/HoneyToast.stories.ts
🧠 Learnings (59)
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 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/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue TabMenu component with Tabs without panels
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Dropdown component with Select
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InlineMessage component with Message
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to 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)
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vueAGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to {src/**/*.{ts,tsx,vue},**/*.test.ts} : Use vue-i18n in Composition API for string literals; place translation entries in src/locales/en/main.json and use i18n plurals system
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-12-05T20:35:28.330Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/locales/en/main.json:2257-2272
Timestamp: 2025-12-05T20:35:28.330Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, vue-i18n pluralization uses a two-parameter pattern: `t('key', { count: value })` where the `count` property in the interpolation object automatically triggers plural selection from pipe-separated locale strings (e.g., "no items | 1 item | {count} items"). This is the correct pattern used throughout the codebase, as validated by Playwright tests.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use vue-i18n for ALL UI strings
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.{ts,tsx,vue} : Ask 'is there a simpler way?' when writing code; use refactoring to make complex code simpler
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement cleanup for async operations in Vue components
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2026-01-08T21:43:58.213Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7897
File: src/stores/assetDownloadStore.ts:117-117
Timestamp: 2026-01-08T21:43:58.213Z
Learning: In src/stores/assetDownloadStore.ts, the api.addEventListener('asset_download', handleAssetDownload) call at module level is intentional and should persist for the application lifetime since it's a singleton Pinia store managing global WebSocket events.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Never use `dark:` Tailwind variant; use semantic values from style.css theme (e.g., bg-node-component-surface)
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vueAGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,css} : Use the correct tokens from style.css in the design system package
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.
Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vuesrc/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.
Applied to files:
src/workbench/extensions/manager/components/ManagerProgressToast.vuesrc/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles
Applied to files:
src/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Vue 3.5+ components must use TypeScript with Composition API and <script setup lang="ts">
Applied to files:
src/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Use VueUse functions for performance-enhancing styles
Applied to files:
src/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed
Applied to files:
src/platform/assets/components/ModelImportProgressDialog.vue
📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .cursor/rules/unit-test.mdc:0-0
Timestamp: 2025-11-24T19:48:09.318Z
Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Mocks should be cleanly written and easy to understand, with reusable mocks where possible
Applied to files:
src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.
Applied to files:
src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.
Applied to files:
src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).
Applied to files:
src/components/honeyToast/HoneyToast.stories.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().
Applied to files:
src/components/honeyToast/HoneyToast.stories.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/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Follow Vue 3 style guide and naming conventions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Be judicious with addition of new refs and state in Vue; use props directly, computed instead of watch, useModel instead of prop+emit when appropriate
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Use Tailwind 4 for styling; avoid <style> blocks in Vue components
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Prefer useModel over separate prop and emit definitions in Vue components
Applied to files:
AGENTS.md
📚 Learning: 2025-12-01T23:45:28.610Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7084
File: src/renderer/extensions/vueNodes/components/SlotConnectionDot.vue:23-26
Timestamp: 2025-12-01T23:45:28.610Z
Learning: Tailwind CSS supports arbitrary fractional width utilities beyond the common predefined ones. For example, `w-5/2` is valid syntax that represents 250% width (5÷2 = 2.5 = 250%). This applies to all utility variants including pseudo-elements like `after:w-5/2`.
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Define proper props and emits definitions in Vue components
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Use default prop declaration with destructuring in Vue 3.5 TypeScript style; avoid withDefaults and runtime props
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.{ts,tsx,vue} : Watch out for Code Smells and refactor to avoid them
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Destructure props using Vue 3.5 style in Vue components
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Never use !important or ! prefix for Tailwind classes; find and fix interfering !important classes instead
Applied to files:
AGENTS.md
📚 Learning: 2025-12-09T21:40:19.792Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:19.792Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, inline Tailwind CSS class strings, even when long, are acceptable and preferred over extracting them to computed properties when the classes are static. This is a common Tailwind pattern and doesn't need to be flagged as a readability issue.
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T22:30:03.686Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Use cn() utility from '@/utils/tailwindUtil' for merging class names; never use :class="[]"
Applied to files:
AGENTS.md
📚 Learning: 2025-12-18T02:07:44.374Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:44.374Z
Learning: Comfy-Org/ComfyUI_frontend uses Tailwind CSS v4 utilities, including the new “safe” overflow-alignment classes. Do not flag items-center-safe, justify-*-safe, or place-*-safe utilities as invalid in src/**/*.vue or stories.
Applied to files:
AGENTS.md
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In Comfy-Org/ComfyUI_frontend, the `cn` utility function from `@/utils/tailwindUtil` uses `tailwind-merge`, which intelligently resolves conflicting Tailwind classes by keeping the last one. When a component uses `cn('base-classes bg-default', propClass)`, if `propClass` contains a conflicting background class, `tailwind-merge` will correctly override `bg-default` with the value from `propClass`. This additive pattern is correct and intentional, following the shadcn-ui convention.
</learning]
Applied to files:
AGENTS.md
📚 Learning: 2025-12-18T20:39:30.137Z
Learnt from: jtydhr88
Repo: Comfy-Org/ComfyUI_frontend PR: 7621
File: src/components/load3d/Load3DScene.vue:4-4
Timestamp: 2025-12-18T20:39:30.137Z
Learning: In src/components/load3d/Load3DScene.vue, the scoped `<style>` block with `!important` declarations for the canvas element is necessary because Three.js dynamically creates the canvas with inline styles, preventing direct application of Tailwind classes. This is a valid exception to the Tailwind-only styling guideline.
Applied to files:
AGENTS.md
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,css} : Use Tailwind CSS only for styling (no custom CSS)
Applied to files:
AGENTS.md
📚 Learning: 2025-12-22T21:36:16.031Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:16.031Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, avoid creating single-use variants for the Button component (and other UI components). Variants should be reusable across multiple use cases. It's acceptable to use a mix of variant props and manual Tailwind classes temporarily when a specific styling need exists in only one place, with consolidation deferred to a later phase when patterns emerge.
Applied to files:
AGENTS.md
🧬 Code graph analysis (1)
src/components/honeyToast/HoneyToast.stories.ts (2)
src/stores/assetDownloadStore.ts (1)
AssetDownload(7-16)src/components/toast/ProgressToastItem.stories.ts (1)
Completed(65-73)
🔇 Additional comments (5)
src/platform/assets/components/ModelImportProgressDialog.vue (1)
172-248: > Likely an incorrect or invalid review comment.AGENTS.md (2)
125-128: LGTM! Clear guidance on Vue 3.5+ best practices.The updated guidelines promote better patterns:
defineModelsimplifies v-model bindings- Template-based slots are more intuitive than
defineSlots- Same-name shorthand reduces verbosity
- Type helpers from
vue-component-type-helpersimprove maintainability
277-278: LGTM! Good guidance to prefer Tailwind fraction utilities.Using
w-4/5instead ofw-[80%]leverages Tailwind's built-in utilities and improves consistency across the codebase.src/workbench/extensions/manager/components/ManagerProgressToast.vue (2)
150-193: Good error handling and cleanup in restart flow.The
handleRestartfunction properly:
- Saves and restores the original toast setting
- Uses try-catch-finally for error handling
- Cleans up state on error
- Uses
useEventListenerwith{ once: true }for automatic cleanupThis demonstrates good practices for managing async operations and preventing memory leaks.
111-143: Well-designed auto-scroll behavior.The scroll management logic (lines 111-143) thoughtfully handles:
- Detecting when user manually scrolls (
isAtBottom,handleScroll)- Pausing auto-scroll during user interaction (
isUserScrolling)- Resuming auto-scroll on collapse/expand
- Using VueUse
wheneverwatchers with proper timing (flush: 'post')This creates a good user experience that respects user intent while maintaining helpful auto-scroll behavior.
| export const Default: Story = { | ||
| render: () => ({ | ||
| components: { HoneyToast, Button, ProgressToastItem }, | ||
| setup() { | ||
| const isExpanded = ref(false) | ||
| const jobs = [ | ||
| createMockJob({ | ||
| taskId: 'task-1', | ||
| assetName: 'model-v1.safetensors', | ||
| status: 'completed', | ||
| progress: 1 | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-2', | ||
| assetName: 'lora-style.safetensors', | ||
| status: 'running', | ||
| progress: 0.45 | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-3', | ||
| assetName: 'vae-decoder.safetensors', | ||
| status: 'created' | ||
| }) | ||
| ] | ||
| return { isExpanded, cn, jobs } | ||
| }, | ||
| template: ` | ||
| <HoneyToast v-model:expanded="isExpanded" :visible="true"> | ||
| <template #default> | ||
| <div class="flex h-12 items-center justify-between border-b border-border-default px-4"> | ||
| <h3 class="text-sm font-bold text-base-foreground">Download Queue</h3> | ||
| </div> | ||
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> | ||
| <div class="flex flex-col gap-2"> | ||
| <ProgressToastItem v-for="job in jobs" :key="job.taskId" :job="job" /> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <template #footer="{ toggle }"> | ||
| <div class="flex h-12 items-center justify-between border-t border-border-default px-4"> | ||
| <div class="flex items-center gap-2 text-sm"> | ||
| <i class="icon-[lucide--loader-circle] size-4 animate-spin text-muted-foreground" /> | ||
| <span class="font-bold text-base-foreground">lora-style.safetensors</span> | ||
| </div> | ||
| <div class="flex items-center gap-2"> | ||
| <span class="text-sm text-muted-foreground">1 of 3</span> | ||
| <div class="flex items-center"> | ||
| <Button variant="muted-textonly" size="icon" @click.stop="toggle"> | ||
| <i :class="cn('size-4', isExpanded ? 'icon-[lucide--chevron-down]' : 'icon-[lucide--chevron-up]')" /> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </template> | ||
| </HoneyToast> | ||
| ` | ||
| }) | ||
| } | ||
|
|
||
| export const Expanded: Story = { | ||
| render: () => ({ | ||
| components: { HoneyToast, Button, ProgressToastItem }, | ||
| setup() { | ||
| const isExpanded = ref(true) | ||
| const jobs = [ | ||
| createMockJob({ | ||
| taskId: 'task-1', | ||
| assetName: 'model-v1.safetensors', | ||
| status: 'completed', | ||
| progress: 1 | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-2', | ||
| assetName: 'lora-style.safetensors', | ||
| status: 'running', | ||
| progress: 0.45 | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-3', | ||
| assetName: 'vae-decoder.safetensors', | ||
| status: 'created' | ||
| }) | ||
| ] | ||
| return { isExpanded, cn, jobs } | ||
| }, | ||
| template: ` | ||
| <HoneyToast v-model:expanded="isExpanded" :visible="true"> | ||
| <template #default> | ||
| <div class="flex h-12 items-center justify-between border-b border-border-default px-4"> | ||
| <h3 class="text-sm font-bold text-base-foreground">Download Queue</h3> | ||
| </div> | ||
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> | ||
| <div class="flex flex-col gap-2"> | ||
| <ProgressToastItem v-for="job in jobs" :key="job.taskId" :job="job" /> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <template #footer="{ toggle }"> | ||
| <div class="flex h-12 items-center justify-between border-t border-border-default px-4"> | ||
| <div class="flex items-center gap-2 text-sm"> | ||
| <i class="icon-[lucide--loader-circle] size-4 animate-spin text-muted-foreground" /> | ||
| <span class="font-bold text-base-foreground">lora-style.safetensors</span> | ||
| </div> | ||
| <div class="flex items-center gap-2"> | ||
| <span class="text-sm text-muted-foreground">1 of 3</span> | ||
| <div class="flex items-center"> | ||
| <Button variant="muted-textonly" size="icon" @click.stop="toggle"> | ||
| <i :class="cn('size-4', isExpanded ? 'icon-[lucide--chevron-down]' : 'icon-[lucide--chevron-up]')" /> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </template> | ||
| </HoneyToast> | ||
| ` | ||
| }) | ||
| } | ||
|
|
||
| export const Completed: Story = { | ||
| render: () => ({ | ||
| components: { HoneyToast, Button, ProgressToastItem }, | ||
| setup() { | ||
| const isExpanded = ref(false) | ||
| const jobs = [ | ||
| createMockJob({ | ||
| taskId: 'task-1', | ||
| assetName: 'model-v1.safetensors', | ||
| bytesDownloaded: 1000000, | ||
| progress: 1, | ||
| status: 'completed' | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-2', | ||
| assetId: 'asset-2', | ||
| assetName: 'lora-style.safetensors', | ||
| bytesTotal: 500000, | ||
| bytesDownloaded: 500000, | ||
| progress: 1, | ||
| status: 'completed' | ||
| }) | ||
| ] | ||
| return { isExpanded, cn, jobs } | ||
| }, | ||
| template: ` | ||
| <HoneyToast v-model:expanded="isExpanded" :visible="true"> | ||
| <template #default> | ||
| <div class="flex h-12 items-center justify-between border-b border-border-default px-4"> | ||
| <h3 class="text-sm font-bold text-base-foreground">Download Queue</h3> | ||
| </div> | ||
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> | ||
| <div class="flex flex-col gap-2"> | ||
| <ProgressToastItem v-for="job in jobs" :key="job.taskId" :job="job" /> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <template #footer="{ toggle }"> | ||
| <div class="flex h-12 items-center justify-between border-t border-border-default px-4"> | ||
| <div class="flex items-center gap-2 text-sm"> | ||
| <i class="icon-[lucide--check-circle] size-4 text-jade-600" /> | ||
| <span class="font-bold text-base-foreground">All downloads completed</span> | ||
| </div> | ||
| <div class="flex items-center"> | ||
| <Button variant="muted-textonly" size="icon" @click.stop="toggle"> | ||
| <i :class="cn('size-4', isExpanded ? 'icon-[lucide--chevron-down]' : 'icon-[lucide--chevron-up]')" /> | ||
| </Button> | ||
| <Button variant="muted-textonly" size="icon"> | ||
| <i class="icon-[lucide--x] size-4" /> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </template> | ||
| </HoneyToast> | ||
| ` | ||
| }) | ||
| } | ||
|
|
||
| export const WithError: Story = { | ||
| render: () => ({ | ||
| components: { HoneyToast, Button, ProgressToastItem }, | ||
| setup() { | ||
| const isExpanded = ref(true) | ||
| const jobs = [ | ||
| createMockJob({ | ||
| taskId: 'task-1', | ||
| assetName: 'model-v1.safetensors', | ||
| status: 'failed', | ||
| progress: 0.23 | ||
| }), | ||
| createMockJob({ | ||
| taskId: 'task-2', | ||
| assetName: 'lora-style.safetensors', | ||
| status: 'completed', | ||
| progress: 1 | ||
| }) | ||
| ] | ||
| return { isExpanded, cn, jobs } | ||
| }, | ||
| template: ` | ||
| <HoneyToast v-model:expanded="isExpanded" :visible="true"> | ||
| <template #default> | ||
| <div class="flex h-12 items-center justify-between border-b border-border-default px-4"> | ||
| <h3 class="text-sm font-bold text-base-foreground">Download Queue</h3> | ||
| </div> | ||
| <div class="relative max-h-[300px] overflow-y-auto px-4 py-4"> | ||
| <div class="flex flex-col gap-2"> | ||
| <ProgressToastItem v-for="job in jobs" :key="job.taskId" :job="job" /> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <template #footer="{ toggle }"> | ||
| <div class="flex h-12 items-center justify-between border-t border-border-default px-4"> | ||
| <div class="flex items-center gap-2 text-sm"> | ||
| <i class="icon-[lucide--circle-alert] size-4 text-destructive-background" /> | ||
| <span class="font-bold text-base-foreground">1 download failed</span> | ||
| </div> | ||
| <div class="flex items-center"> | ||
| <Button variant="muted-textonly" size="icon" @click.stop="toggle"> | ||
| <i :class="cn('size-4', isExpanded ? 'icon-[lucide--chevron-down]' : 'icon-[lucide--chevron-up]')" /> | ||
| </Button> | ||
| <Button variant="muted-textonly" size="icon"> | ||
| <i class="icon-[lucide--x] size-4" /> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </template> | ||
| </HoneyToast> | ||
| ` | ||
| }) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Suggest consolidating duplicated story templates.
The Default, Expanded, Completed, and WithError stories share nearly identical template code (lines 67-96, 127-156, 186-214, 241-269). The only variations are the job data and isExpanded initial value.
Consider extracting a shared render function:
♻️ Proposed refactor to reduce duplication
function createStoryRender(
jobs: AssetDownload[],
expanded = false,
footerContent?: string
) {
return () => ({
components: { HoneyToast, Button, ProgressToastItem },
setup() {
const isExpanded = ref(expanded)
return { isExpanded, cn, jobs }
},
template: `
<HoneyToast v-model:expanded="isExpanded" :visible="true">
<template #default>
<div class="flex h-12 items-center justify-between border-b border-border-default px-4">
<h3 class="text-sm font-bold text-base-foreground">Download Queue</h3>
</div>
<div class="relative max-h-[300px] overflow-y-auto px-4 py-4">
<div class="flex flex-col gap-2">
<ProgressToastItem v-for="job in jobs" :key="job.taskId" :job="job" />
</div>
</div>
</template>
<template #footer="{ toggle }">
${footerContent || '<!-- default footer -->'}
</template>
</HoneyToast>
`
})
}
export const Default: Story = {
render: createStoryRender([
createMockJob({ taskId: 'task-1', assetName: 'model-v1.safetensors', status: 'completed', progress: 1 }),
createMockJob({ taskId: 'task-2', assetName: 'lora-style.safetensors', status: 'running', progress: 0.45 }),
createMockJob({ taskId: 'task-3', assetName: 'vae-decoder.safetensors', status: 'created' })
])
}This maintains story clarity while eliminating ~180 lines of duplication.
Based on coding guidelines: DRY principle, "ask 'is there a simpler way?'", and refactoring to make code simpler.
| <Button | ||
| variant="secondary" | ||
| size="md" | ||
| class="gap-1.5 px-2" | ||
| @click="onFilterClick" | ||
| > | ||
| <i class="icon-[lucide--list-filter] size-4" /> | ||
| <span>{{ activeFilterLabel }}</span> | ||
| <i class="icon-[lucide--chevron-down] size-3" /> | ||
| </Button> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider making filter button chevron direction reflect popover state.
The chevron icon (line 103) is always chevron-down, regardless of whether the popover is open or closed. This is a minor UX inconsistency—typically, dropdown buttons rotate or change their chevron to indicate open/closed state.
💡 Suggested enhancement to toggle chevron direction
Track popover visibility and conditionally render the chevron:
+const isFilterPopoverOpen = ref(false)
+
function onFilterClick(event: Event) {
+ isFilterPopoverOpen.value = !filterPopoverRef.value?.visible
filterPopoverRef.value?.toggle(event)
}
function setFilter(filter: typeof activeFilter.value) {
activeFilter.value = filter
filterPopoverRef.value?.hide()
+ isFilterPopoverOpen.value = false
}Then update the chevron in the template:
- <i class="icon-[lucide--chevron-down] size-3" />
+ <i
+ :class="
+ cn(
+ 'size-3',
+ isFilterPopoverOpen
+ ? 'icon-[lucide--chevron-up]'
+ : 'icon-[lucide--chevron-down]'
+ )
+ "
+ />Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @src/platform/assets/components/ModelImportProgressDialog.vue around lines 95
- 104, The chevron icon on the filter Button (currently hardcoded as
"icon-[lucide--chevron-down]") doesn't reflect the popover state; add a reactive
boolean (e.g., popoverOpen), update it when the popover opens/closes (or toggle
it inside onFilterClick if that controls visibility), and replace the static
chevron with a conditional render or class toggle (e.g., show
"icon-[lucide--chevron-up]" or apply a rotation class when popoverOpen is true)
so the Button (referenced by Button, onFilterClick, activeFilterLabel) correctly
indicates open vs closed state.
There was a problem hiding this comment.
This (above comment from coderabbit) seems valid, but not blocking by any means
There was a problem hiding this comment.
@christian-byrne, understood! Since it's valid but not blocking, feel free to tackle it in a future iteration if the team wants to polish that UX detail. The component works fine as-is for now. 👍
🧠 Learnings used
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
| <div | ||
| v-if="filteredJobs.length > 3" | ||
| class="absolute right-1 top-4 h-12 w-1 rounded-full bg-muted-foreground" | ||
| /> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Custom scrollbar indicator might not accurately reflect scroll position.
The visual scrollbar indicator is shown when filteredJobs.length > 3 and remains static (lines 144-147). This is a decorative element rather than a functional scrollbar, and the threshold seems arbitrary. Consider whether this enhances or confuses the UX, especially since the container already has native scrolling.
Alternatives to consider:
- Remove the indicator and rely on native scroll UI
- Implement a functional scrollbar indicator that actually reflects scroll position using VueUse's
useScroll - Adjust the visibility threshold based on actual content height rather than item count
🤖 Prompt for AI Agents
In @src/platform/assets/components/ModelImportProgressDialog.vue around lines
144 - 147, The static decorative scrollbar indicator rendered with the v-if
check on filteredJobs.length in ModelImportProgressDialog.vue can mislead users;
either remove that element entirely or make it functional: replace the
v-if="filteredJobs.length > 3" block with a reactive indicator tied to the
scroll position of the jobs container (use VueUse's useScroll on the container
ref), compute visibility from the container's scrollHeight vs clientHeight
(instead of item count), and bind the indicator's position/height to the scroll
progress so it accurately reflects scrollTop and scrollable range; update any
refs used (e.g., the jobs container ref and filteredJobs) and ensure cleanup of
watchers.
| import Panel from 'primevue/panel' | ||
| import TabMenu from 'primevue/tabmenu' |
There was a problem hiding this comment.
Avoid new usage of PrimeVue components.
Lines 3-4 import PrimeVue Panel and TabMenu components. Per coding guidelines, new PrimeVue usage should be avoided. Consider using alternative components from the design system or creating custom implementations with Tailwind.
Alternative approaches:
- For collapsible panels: Use a custom implementation with disclosure pattern
- For tab menu: Use a custom tab component or consider if the tabbed interface is necessary
As per coding guidelines: "Avoid new usage of PrimeVue components"
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue around
lines 3 - 4, ManagerProgressToast.vue currently imports PrimeVue components
Panel and TabMenu; remove these imports and any local registration/usage of the
symbols Panel and TabMenu, and replace them with design-system or Tailwind-based
alternatives: implement a simple disclosure/collapsible panel pattern for the
Panel usage and a small custom tab component (or remove tabbing if unnecessary)
for TabMenu usage, updating the template markup and component registration
(e.g., the components object or setup imports) to reference the new
implementations instead of Panel/TabMenu while preserving existing props/events
used by the current template.
| <TabMenu | ||
| v-model:active-index="activeTabIndex" | ||
| :model="tabs" | ||
| class="w-full border-none" | ||
| :pt="{ | ||
| menu: { class: 'border-none' }, | ||
| menuitem: { class: 'font-medium' }, | ||
| action: { class: 'px-4 py-2' } | ||
| }" | ||
| /> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Replace PrimeVue TabMenu styling with Tailwind classes.
Using PrimeVue's :pt prop (lines 212-216) for styling goes against the Tailwind-first approach. If continuing with TabMenu temporarily, the styling should be handled through Tailwind utility classes or semantic design tokens.
As per coding guidelines: Use Tailwind CSS for styling.
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue around
lines 208 - 217, Remove the PrimeVue-specific :pt prop block from the TabMenu
usage and replace it with Tailwind utility classes; specifically, keep the
existing TabMenu element (v-model:active-index="activeTabIndex" :model="tabs")
but delete the :pt object and instead apply Tailwind classes directly (e.g., to
the TabMenu root via its class prop and to tab/menu item elements by adding
utility classes or by embedding class names in the tabs model items). Update any
styling previously set in :pt (menu border-none, menuitem font-medium, action
px-4 py-2) to equivalent Tailwind classes on the TabMenu root, on individual tab
items (via the tabs data or slot templates), or via a small scoped CSS/Tailwind
token helper so styling follows the Tailwind-first guideline.
| :style="{ | ||
| scrollbarWidth: 'thin', | ||
| scrollbarColor: 'rgba(156, 163, 175, 0.5) transparent' | ||
| }" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Replace inline scrollbar styles with Tailwind utilities.
The inline style object for scrollbar styling should use Tailwind utilities instead:
♻️ Proposed fix
<div
ref="sectionsContainerRef"
- class="scroll-container max-h-[450px] overflow-y-auto px-6 py-4"
- :style="{
- scrollbarWidth: 'thin',
- scrollbarColor: 'rgba(156, 163, 175, 0.5) transparent'
- }"
+ class="scroll-container max-h-[450px] overflow-y-auto px-6 py-4 scrollbar-thin"
>Note: Ensure scrollbar-thin utility is configured in your Tailwind setup, or use available scrollbar utilities from the design system.
As per coding guidelines: Use Tailwind CSS for styling; avoid <style> blocks and inline styles.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue around
lines 223 - 226, The inline scrollbar style on the container in
ManagerProgressToast.vue should be replaced with Tailwind classes: remove the
:style object and add appropriate class names like "scrollbar-thin", a thumb
color utility (e.g. "scrollbar-thumb-[rgba(156,163,175,0.5)]" or your
design-system equivalent) and a transparent track class (e.g.
"scrollbar-track-transparent") to the same element; ensure the Tailwind
scrollbar plugin or custom utilities are enabled in your Tailwind config so
those classes resolve.
| <template #toggleicon> | ||
| <Button | ||
| variant="textonly" | ||
| class="text-neutral-300" |
There was a problem hiding this comment.
Use semantic color tokens instead of hardcoded colors.
Lines 251 and 269 use hardcoded color values:
- Line 251:
text-neutral-300 - Line 269:
bg-black
These should use semantic tokens from style.css theme (e.g., text-muted-foreground, bg-base-background).
♻️ Proposed fix
<Button
variant="textonly"
- class="text-neutral-300"
+ class="text-muted-foreground"
@click="togglePanel(index)"
> <div
...
- class="h-64 overflow-y-auto rounded-lg bg-black"
+ class="h-64 overflow-y-auto rounded-lg bg-base-background"
:class="{As per coding guidelines: "Never use dark: Tailwind variant; use semantic values from style.css theme (e.g., bg-node-component-surface)"
Also applies to: 269-269
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue at line
251, Replace hardcoded Tailwind color utility classes in
ManagerProgressToast.vue: change the element using the class text-neutral-300 to
the semantic token text-muted-foreground, and change the element using bg-black
to the appropriate theme semantic token such as bg-base-background or
bg-node-component-surface (per your theme) so components use style.css semantic
colors; ensure no dark: variant is introduced and update any occurrences of
these class names in the template of the ManagerProgressToast component.
| ? 'pi pi-chevron-right' | ||
| : 'pi pi-chevron-down' | ||
| " |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Replace PrimeVue icons with Lucide icons.
The component uses PrimeVue icons (pi pi-chevron-right, pi pi-chevron-down, pi pi-chevron-up, pi pi-times) on lines 257-259, 336, and 346. The codebase should use Lucide icons consistently.
♻️ Proposed fix
- :class="
- collapsedPanels[index]
- ? 'pi pi-chevron-right'
- : 'pi pi-chevron-down'
- "
+ :class="
+ collapsedPanels[index]
+ ? 'icon-[lucide--chevron-right]'
+ : 'icon-[lucide--chevron-down]'
+ " <i
- :class="isExpanded ? 'pi pi-chevron-up' : 'pi pi-chevron-down'"
+ :class="isExpanded ? 'icon-[lucide--chevron-up]' : 'icon-[lucide--chevron-down]'"
/>- <i class="pi pi-times" />
+ <i class="icon-[lucide--x]" />Based on codebase patterns using Lucide icons (e.g., HoneyToast.stories.ts uses icon-[lucide--*]).
Also applies to: 336-336, 346-346
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue around
lines 257 - 259, In ManagerProgressToast.vue replace PrimeVue icon class strings
with the project's Lucide icon utility classes: change 'pi pi-chevron-right' ->
'icon-[lucide--chevron-right]', 'pi pi-chevron-down' ->
'icon-[lucide--chevron-down]', 'pi pi-chevron-up' -> 'icon-[lucide--chevron-up]'
and 'pi pi-times' -> 'icon-[lucide--x]'; update the occurrences referenced in
the component (the conditional icon around the chevron at the ternary, the
chevron at the second occurrence, and the close icon) so the component uses the
consistent Lucide class pattern used elsewhere (e.g., HoneyToast).
| <span>{{ currentTaskName }}</span> | ||
| </template> | ||
| <template v-else-if="isRestartCompleted"> | ||
| <span class="mr-2">🎉</span> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider internationalizing hardcoded emojis.
Lines 302 and 306 use hardcoded emojis ("🎉" and "✅"). While emojis are generally Unicode-safe, consider whether these visual indicators should be part of the i18n strings for cultural consistency, or if icon components would be more appropriate.
If keeping emojis, this is acceptable. If preferring icons for consistency with the rest of the UI:
Alternative with icons
<template v-else-if="isRestartCompleted">
- <span class="mr-2">🎉</span>
+ <i class="icon-[lucide--party-popper] mr-2 size-4" />
<span>{{ currentTaskName }}</span>
</template>
<template v-else>
- <span class="mr-2">✅</span>
+ <i class="icon-[lucide--check-circle] mr-2 size-4 text-jade-600" />
<span>{{ t('manager.restartToApplyChanges') }}</span>
</template>Also applies to: 306-306
🤖 Prompt for AI Agents
In @src/workbench/extensions/manager/components/ManagerProgressToast.vue at line
302, The template in ManagerProgressToast.vue contains hardcoded emoji spans
(e.g., the <span class="mr-2">🎉</span> and the similar check emoji) which
should be moved into i18n strings or replaced with the app's icon components for
consistency; update the template to use either
$t('managerProgress.successEmoji') / $t('managerProgress.doneEmoji') or swap the
spans for the existing icon component (e.g., <Icon name="celebrate" /> and <Icon
name="check" />) wherever the emojis appear, and add corresponding i18n keys or
icon imports so the visual indicators are configurable and consistent with the
rest of the UI.
| <Button | ||
| variant="secondary" | ||
| size="md" | ||
| class="gap-1.5 px-2" | ||
| @click="onFilterClick" | ||
| > | ||
| <i class="icon-[lucide--list-filter] size-4" /> | ||
| <span>{{ activeFilterLabel }}</span> | ||
| <i class="icon-[lucide--chevron-down] size-3" /> | ||
| </Button> |
There was a problem hiding this comment.
This (above comment from coderabbit) seems valid, but not blocking by any means
| updateTaskState(event.detail.state) | ||
|
|
||
| // Show progress dialog when a task starts | ||
| showManagerProgressDialog() |
There was a problem hiding this comment.
Note: now the toast is alive whenever in the graph view so it manages its own visbility based on the manager stores.
| <GlobalToast /> | ||
| <RerouteMigrationToast /> | ||
| <ModelImportProgressDialog /> | ||
| <ManagerProgressToast /> |
There was a problem hiding this comment.
Oh, the toast is always alive when in this view?
Is that a better pattern than a store managing the component? I know it's actually very common, but seems strange for the component to act as a global store of state like that.
| // Show progress dialog immediately when task is queued | ||
| showManagerProgressDialog() |
There was a problem hiding this comment.
note: See other comment. Good that this is getting centralized now, but that could have been done either way to be fair.
| - Prefer `useModel` to separately defining a prop and emit | ||
| - Prefer `defineModel` to separately defining a prop and emit for v-model bindings | ||
| - Define slots via template usage, not `defineSlots` | ||
| - Use same-name shorthand for slot prop bindings: `:isExpanded` instead of `:is-expanded="isExpanded"` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the ESLint configuration file
cat eslint.config.ts
Length of output: 7757
🏁 Script executed:
#!/bin/bash
# Search for any existing rules related to defineModel, defineSlots, or prop shorthand
rg -i "defineModel|defineSlots|shorthand" eslint.config.ts
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Check if there are Vue-specific ESLint rules configured
rg -A5 "plugin.*vue|vue.*plugin" eslint.config.ts
Length of output: 759
🏁 Script executed:
#!/bin/bash
# Check available vue ESLint rules in node_modules to see if rules exist for these guidelines
fd -t f "index.js" node_modules/eslint-plugin-vue/lib/rules --exec head -1 {} \; | head -20
Length of output: 127
🏁 Script executed:
#!/bin/bash
# List all available vue eslint rules
fd -t f ".js$" node_modules/eslint-plugin-vue/lib/rules -x basename {} .js | sort
Length of output: 127
🏁 Script executed:
#!/bin/bash
# Search for defineModel-related rules in the vue plugin
rg -i "defineModel|define-model" node_modules/eslint-plugin-vue/lib/rules/ --type js -l
Length of output: 81
🏁 Script executed:
#!/bin/bash
# Search for slot-related rules
rg -i "slot.*shorthand|v-slot.*shorthand" node_modules/eslint-plugin-vue/lib/rules/ --type js -l
Length of output: 81
🌐 Web query:
eslint-plugin-vue defineModel rule
💡 Result:
There’s no standalone “defineModel” rule; handling of defineModel (the compiler macro) is covered by the vue/define-macros-order rule (which lets you enforce the order of compiler macros in <script setup>) and by deprecation/validation rules for model definitions. Example:
-
rule: vue/define-macros-order — enforces order of macros and accepts a custom order array (you can include "defineModel" in that order). Configure like:
{
"vue/define-macros-order": ["error", { "order": ["defineOptions","defineModel","defineProps","defineEmits","defineSlots"] }]
}
[1] -
rule: vue/no-deprecated-model-definition — flags use of the deprecated component model option (Vue 3) and has an allowVue3Compat option. [2]
Sources:
[1] eslint-plugin-vue — vue/define-macros-order documentation.
[2] eslint-plugin-vue — vue/no-deprecated-model-definition documentation.
🌐 Web query:
eslint-plugin-vue v-slot shorthand directive rule
💡 Result:
Rule: vue/v-slot-style — enforces v-slot directive style (shorthand #, longform v-slot:, or plain v-slot). Options: atComponent, default, named (each "shorthand" | "longform" | "v-slot") or a single "shorthand"/"longform" preset. Introduced in eslint-plugin-vue v6.0.0. Example: use <template #default> or per your option. [1][2]
Sources:
[1] eslint-plugin-vue — vue/v-slot-style (rule docs).
[2] eslint-plugin-vue — rules list (index).
🌐 Web query:
eslint-plugin-vue defineSlots rule enforce
💡 Result:
Short answer: eslint-plugin-vue enforces defineSlots usage via multiple rules:
- Order in <script setup>: vue/define-macros-order — can require defineSlots to appear (and where) among macros (configurable "order"). [1]
- Macro variable name: vue/require-macro-variable-name — can require the return of defineSlots be named (e.g. slots). [2]
- Feature availability: vue/no-unsupported-features includes "define-slots" so you can flag usage if your target Vue version doesn’t support it. [3]
- There is also a rule added to require explicit slots: vue/require-explicit-slots (introduced v9.21.0) which requires slots to be explicitly defined with defineSlots or the slots option. [4]
Examples / config pointers:
- Enforce macros order (default):
{ "vue/define-macros-order": ["error", { "order": ["defineProps","defineEmits","defineSlots"] }] }. [1] - Enforce macro variable name:
{ "vue/require-macro-variable-name": ["error", { "defineSlots":"slots" }] }. [2]
Sources: eslint-plugin-vue docs for vue/define-macros-order, vue/require-macro-variable-name, vue/no-unsupported-features, and changelog noting require-explicit-slots. [1][2][3][4]
🌐 Web query:
eslint-plugin-vue attribute shorthand same name binding rule
💡 Result:
There’s a dedicated rule in eslint-plugin-vue: vue/v-bind-style — it enforces v-bind shorthand vs longform and (since recent releases) includes a sameNameShorthand option to control the Vue 3.4+ same-name shorthand (:foo when the variable is named foo).
- Rule docs (options): "shorthand" | "longform" and sameNameShorthand: "ignore" | "always" | "never". [1]
- If you need to allow or disallow the Vue 3.4 same-name shorthand globally, set sameNameShorthand accordingly. Example:
{
"vue/v-bind-style": ["error", "shorthand", { "sameNameShorthand": "always" }]
}
[1] - If using features from Vue 3.4 you may also need to allow them in vue/no-unsupported-features (the rule’s "ignores" can include "v-bind-same-name-shorthand"). [2]
- The option was added in recent plugin releases (see changelog). [3]
Sources:
[1] eslint-plugin-vue — vue/v-bind-style docs.
[2] eslint-plugin-vue — vue/no-unsupported-features docs.
[3] eslint-plugin-vue changelog / release notes.
@christian-byrne Unfortunately, only one of the three guidelines has a linter rule available:
1. ✅ Same-name shorthand (:isExpanded instead of :is-expanded="isExpanded")
Rule: vue/v-bind-style with sameNameShorthand: "always"
This can be added to your eslint.config.ts:
rules: {
'vue/v-bind-style': ['error', 'shorthand', { sameNameShorthand: 'always' }],
// ... other rules
}2. ❌ Prefer defineModel over separate prop and emit
No direct rule exists. The vue/define-macros-order rule can enforce the order of macros if defineModel is used, but it won't flag cases where you use separate defineProps + defineEmits instead of defineModel.
3. ❌ Define slots via template usage, not defineSlots
No rule to enforce this. Interestingly, vue/require-explicit-slots does the opposite—it requires explicit defineSlots declarations. Your guideline intentionally goes against this pattern.
Recommendation: Add the vue/v-bind-style rule to automatically enforce the same-name shorthand. The other two guidelines will need to be caught during code review.
🧠 Learnings used
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Prefer useModel over separate prop and emit definitions in Vue components
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Be judicious with addition of new refs and state in Vue; use props directly, computed instead of watch, useModel instead of prop+emit when appropriate
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/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Define proper props and emits definitions in Vue components
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Follow Vue 3 style guide and naming conventions
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Use VueUse functions for performance-enhancing styles
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Use default prop declaration with destructuring in Vue 3.5 TypeScript style; avoid withDefaults and runtime props
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.{ts,tsx,vue} : Watch out for Code Smells and refactor to avoid them
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to 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)
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T22:30:03.686Z
Learning: Applies to **/*.vue : Use Tailwind 4 for styling; avoid <style> blocks in Vue components
|
|
|
…7902) Add HoneyToast, a persistent bottom-anchored notification component for long-running task progress, and migrate existing progress dialogs to use it. - **What**: - New `HoneyToast` component with slot-based API, Teleport, transitions, and accessibility - Migrated `ModelImportProgressDialog` to use HoneyToast - Created `ManagerProgressToast` combining the old Header/Content/Footer components - Deleted deprecated `ManagerProgressDialogContent`, `ManagerProgressHeader`, `ManagerProgressFooter`, and `useManagerProgressDialogStore` - Removed no-op `showManagerProgressDialog`/`toggleManagerProgressDialog` functions - Added Storybook stories for HoneyToast and ProgressToastItem - HoneyToast component design and slot API - ManagerProgressToast self-contained state management (auto-shows when `comfyManagerStore.taskLogs.length > 0`) - Accessibility attributes on the toast component ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7902-feat-add-HoneyToast-component-for-persistent-progress-notifications-2e26d73d365081c78ae6edc5accb326e) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: sno <[email protected]> Co-authored-by: github-actions <[email protected]> Co-authored-by: GitHub Action <[email protected]>
…rogress notifications (#7918) Backport of #7902 to cloud/1.36 Original PR: #7902 Cherry-picked merge commit e26e1f0. ## Conflicts resolved - **pnpm-lock.yaml**: Regenerated with `pnpm install` - **tests-ui/tests/components/dialog/footer/ManagerProgressFooter.test.ts**: Removed (PR deletes this file along with the component it tested) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7918-backport-cloud-1-36-feat-add-HoneyToast-component-for-persistent-progress-notification-2e36d73d3650811a9f57f26c56b84c97) by [Unito](https://www.unito.io) Co-authored-by: Alexander Brown <[email protected]> Co-authored-by: Amp <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: sno <[email protected]> Co-authored-by: github-actions <[email protected]> Co-authored-by: GitHub Action <[email protected]>
Summary
Add HoneyToast, a persistent bottom-anchored notification component for long-running task progress, and migrate existing progress dialogs to use it.
Changes
HoneyToastcomponent with slot-based API, Teleport, transitions, and accessibilityModelImportProgressDialogto use HoneyToastManagerProgressToastcombining the old Header/Content/Footer componentsManagerProgressDialogContent,ManagerProgressHeader,ManagerProgressFooter, anduseManagerProgressDialogStoreshowManagerProgressDialog/toggleManagerProgressDialogfunctionsReview Focus
comfyManagerStore.taskLogs.length > 0)┆Issue is synchronized with this Notion page by Unito