-
Notifications
You must be signed in to change notification settings - Fork 433
Support "control after generate" in vue #6985
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
The @PointerUp.stop was breaking reka ui NumberFields. IIRC, this was added to allow selecting text without dragging nodes. Current testing suggests this isn't required for pointerup This reduces the margins some on number inputs. It's trivial to add a px-2.5, but helps with information density
📝 WalkthroughWalkthroughThis pull request introduces a number control widget system that enables managing number inputs with multiple control modes (fixed, increment, decrement, randomize, link-to-global). It adds composables, services, UI components, a global seed store, and integrates control execution into the queue prompt flow with comprehensive test coverage and localization. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as NumberInput +<br/>ControlButton
participant Popover as NumberControlPopover
participant Stepper as useStepperControl
participant Registry as NumberControlRegistry
participant App as app.queuePrompt()
participant Store as globalSeedStore
User->>UI: Click control button
activate UI
UI->>Popover: toggle()
activate Popover
Popover->>User: Show mode options
deactivate UI
User->>Popover: Select mode<br/>(increment/randomize/etc)
Popover->>Stepper: emit update:controlMode
deactivate Popover
activate Stepper
Stepper->>Stepper: setControlMode(mode)
Stepper->>UI: widget.controlWidget.update(mode)
deactivate Stepper
User->>App: Queue prompt
activate App
App->>Registry: executeNumberControls('before')
activate Registry
Registry->>Registry: Get all registered<br/>applyControl callbacks
Registry->>Stepper: applyControl()
activate Stepper
alt mode = RANDOMIZE
Stepper->>Stepper: value = random(min, max)
else mode = INCREMENT
Stepper->>Stepper: value = current + step
else mode = LINK_TO_GLOBAL
Stepper->>Store: Get globalSeed
activate Store
Store-->>Stepper: seed value
deactivate Store
Stepper->>Stepper: value = clamp(seed, min, max)
else mode = FIXED
Stepper->>Stepper: (no change)
end
Stepper->>UI: onChange(newValue)
deactivate Stepper
deactivate Registry
App->>App: Process queued prompt
App->>Registry: executeNumberControls('after')
deactivate App
✨ Finishing touches
🧪 Generate unit tests (beta)
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: 12/05/2025, 09:38:31 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results⏰ Completed at: 12/05/2025, 09:46:54 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.2 MB (baseline 3.2 MB) • 🔴 +2.75 kBMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 975 kB (baseline 975 kB) • 🔴 +684 BGraph 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 — 180 kB (baseline 173 kB) • 🔴 +6.88 kBReusable component library chunks
Status: 7 added / 7 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.82 MB (baseline 3.81 MB) • 🔴 +7.49 kBBundles that do not match a named category
Status: 19 added / 18 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)
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue (2)
148-165: UnifieddropdownItemslogic looks correctThe refactor to have asset mode always use
allItemsand non-asset “all” explicitly concatenateinputItemsandoutputItemsis consistent with the surrounding data flow; no functional issues stand out. If you want to reduce duplication, the'all'branch could just returnallItems.value, but that’s purely optional.
70-81: Localize user-facing strings and tighten upload error messagingSeveral strings are user-visible but not run through vue-i18n, which conflicts with the repo guidelines:
- Filter option labels:
'All','Inputs','Outputs'(Lines 76–79).- Toast messages:
File upload failed,Upload failed: ${error}(Lines 296, 320).Consider:
- Replacing these literals with
t(...)calls and adding appropriate entries insrc/locales/en/main.json.- For the upload error toast, deriving a readable message (e.g.
error instanceof Error ? error.message : String(error)) rather than stringifying the raw error object.This keeps UX consistent and ready for localization without changing behavior.
Also applies to: 263-264, 295-321
tests-ui/tests/stores/globalSeedStore.test.ts (1)
21-28: Consider replacing the probabilistic assertion.This test relies on random seed generation to be different across store instances, which introduces non-determinism. While the 1-in-1,000,000 chance is low, flaky tests can cause false failures in CI/CD pipelines.
Consider one of these alternatives:
- Mock
Math.random()to return predictable values- Test that the seed is within the valid range instead of comparing uniqueness
- Accept the low flakiness risk and document it clearly
Example with mocking:
it('should create different seeds for different store instances', () => { + const mockRandom = vi.spyOn(Math, 'random') + mockRandom.mockReturnValueOnce(0.5) const store1 = useGlobalSeedStore() setActivePinia(createPinia()) // Reset pinia + mockRandom.mockReturnValueOnce(0.7) const store2 = useGlobalSeedStore() - // Very unlikely to be the same (1 in 1,000,000 chance) - expect(store1.globalSeed).not.toBe(store2.globalSeed) + expect(store1.globalSeed).toBe(500000) + expect(store2.globalSeed).toBe(700000) + mockRandom.mockRestore() })src/types/simplifiedWidget.ts (1)
18-26: Consider adding 'link-to-global' to ControlWidgetOptions type.The
NumberControlPopover.vuecomponent references aLINK_TO_GLOBALmode (currently disabled via feature flag), but this option is missing from theControlWidgetOptionstype union. While the feature is disabled now, including it in the type definition would ensure type safety when the feature is enabled.export type ControlWidgetOptions = | 'fixed' | 'increment' | 'decrement' | 'randomize' + | 'link-to-global'src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue (1)
29-29: Document the LINK_TO_GLOBAL feature flag.The
ENABLE_LINK_TO_GLOBALconstant is hardcoded tofalse, but there's no comment explaining why this feature is disabled or what would be required to enable it. Consider adding a comment to guide future development.+// TODO: Enable LINK_TO_GLOBAL once global seed synchronization is fully implemented const ENABLE_LINK_TO_GLOBAL = falsesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue (1)
57-64: Consider usingcn()utility for class merging.Per coding guidelines, prefer using the
cn()utility from@/utils/tailwindUtilfor class merging instead of template string interpolation.- <i :class="`${controlButtonIcon} text-blue-100 text-xs`" /> + <i :class="cn(controlButtonIcon, 'text-blue-100 text-xs')" />You'll need to import
cnfrom@/utils/tailwindUtil.src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
123-128: Remove potentially dead PrimeVue CSS.This scoped CSS targets
.p-inputnumber-input, a PrimeVue class. Since the component now uses Reka UI'sNumberFieldRoot/NumberFieldInput, this CSS may no longer apply and could be removed.src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
27-34: Consider wrapping callback execution in try-catch for resilience.If one registered
applyFnthrows an error, it will prevent subsequent controls from being applied. Consider wrapping each callback in a try-catch to ensure all controls are executed.executeControls(phase: 'before' | 'after'): void { const settingStore = useSettingStore() if (settingStore.get('Comfy.WidgetControlMode') === phase) { for (const applyFn of this.controls.values()) { - applyFn() + try { + applyFn() + } catch (error) { + console.error('Error executing number control:', error) + } } } }
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
Outdated
Show resolved
Hide resolved
| if (typeof window !== 'undefined') { | ||
| import('@/base/common/downloadUtil') | ||
| .then((module) => { | ||
| const fn = ( | ||
| module as { | ||
| downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob | ||
| } | ||
| ).downloadBlob | ||
| if (typeof fn === 'function') { | ||
| ;(window as any).downloadBlob = fn | ||
| } | ||
| }) | ||
| .catch(() => {}) | ||
| } |
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 TypeScript violations: avoid as any and improve error handling.
This code violates two explicit coding guidelines:
- Line 63 uses
as anytype assertion - Line 66 silently swallows errors
As per coding guidelines, never use as any type assertions and implement proper error handling.
Apply this diff to fix both issues:
+declare global {
+ interface Window {
+ downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob
+ }
+}
+
if (typeof window !== 'undefined') {
import('@/base/common/downloadUtil')
.then((module) => {
const fn = (
module as {
downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob
}
).downloadBlob
if (typeof fn === 'function') {
- ;(window as any).downloadBlob = fn
+ window.downloadBlob = fn
}
})
- .catch(() => {})
+ .catch((error) => {
+ console.error('Failed to load downloadBlob utility:', error)
+ })
}📝 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.
| if (typeof window !== 'undefined') { | |
| import('@/base/common/downloadUtil') | |
| .then((module) => { | |
| const fn = ( | |
| module as { | |
| downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob | |
| } | |
| ).downloadBlob | |
| if (typeof fn === 'function') { | |
| ;(window as any).downloadBlob = fn | |
| } | |
| }) | |
| .catch(() => {}) | |
| } | |
| declare global { | |
| interface Window { | |
| downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob | |
| } | |
| } | |
| if (typeof window !== 'undefined') { | |
| import('@/base/common/downloadUtil') | |
| .then((module) => { | |
| const fn = ( | |
| module as { | |
| downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob | |
| } | |
| ).downloadBlob | |
| if (typeof fn === 'function') { | |
| window.downloadBlob = fn | |
| } | |
| }) | |
| .catch((error) => { | |
| console.error('Failed to load downloadBlob utility:', error) | |
| }) | |
| } |
|
Updating Playwright Expectations |
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: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (11)
browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.pngis excluded by!**/*.png
📒 Files selected for processing (3)
src/locales/en/main.json(1 hunks)src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue(0 hunks)src/scripts/app.ts(3 hunks)
💤 Files with no reviewable changes (1)
- src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{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/scripts/app.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/scripts/app.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/scripts/app.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/scripts/app.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/scripts/app.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/scripts/app.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/scripts/app.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/scripts/app.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/scripts/app.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/scripts/app.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/scripts/app.ts
🧬 Code graph analysis (1)
src/scripts/app.ts (1)
src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
executeNumberControls(57-59)
⏰ 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). (6)
- GitHub Check: deploy-and-comment
- GitHub Check: setup
- GitHub Check: setup
- GitHub Check: lint-and-format
- GitHub Check: collect
- GitHub Check: test
🔇 Additional comments (3)
src/scripts/app.ts (3)
34-34: Import placement and dependency layering look goodThe new
executeNumberControlsimport follows the existing pattern of pulling renderer/vueNodes utilities intoapp.ts(we already importensureCorrectLayoutScalefrom the same area), so this dependency direction is consistent and there’s no unused import issue here.
1345-1358: Confirm intended scope and timing ofexecuteNumberControls('before')
executeNumberControls('before')now runs once per batch iteration, after allbeforeQueuedwidget callbacks (main graph + subgraphs) and immediately beforegraphToPrompt. That means:
- Controls see the post‑widget state of the graph and affect the prompt that is just about to be queued.
- The hook runs regardless of whether any number controls are actually registered, assuming the registry is a safe no‑op in that case.
- For partial executions (
queueNodeIds), the hook still runs globally and doesn’t filter by the nodes being executed.Please double‑check that this ordering and global scope match the intended design for number controls (especially for control‑before‑generate vs control‑after‑generate modes and partial execution flows). If you want controls to be able to key off which nodes will run, they may need access to
porqueueNodeIdshere.
1396-1408: ClarifyexecuteNumberControls('after')behavior on errors and partial failures
executeNumberControls('after')is invoked only whenapi.queuePromptresolves successfully; it is skipped when an exception is thrown because thebreakinside thecatchexits the loop before reaching this call. It still runs whenres.node_errorsis non‑empty.This seems reasonable (don’t run “after” controls when the prompt wasn’t queued at all), but it does mean:
- Any “after‑generate” behavior will not run on
PromptExecutionErroreven if prior hooks (e.g.before) have already adjusted state.- “After” hooks will still run when the backend reports node‑level errors in
res.node_errors.Please confirm that this matches the intended UX for control‑after‑generate, especially around failed/partial executions; if you expect symmetrical cleanup or state updates on certain error paths, we may want to call
executeNumberControls('after')in a narrowerfinallyaroundqueuePromptor gate it explicitly onres.prompt_id/lastNodeErrors.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
browser_tests/fixtures/VueNodeHelpers.ts (1)
159-164: Selector now matches current DOM order but is still structurally brittleSwitching
decrementButtontonth(1)makes sense if the DOM order is[increment, decrement, control...]and the last button is no longer the decrement control.Long‑term, this helper will be more stable if the underlying widget exposes semantic hooks (e.g.
data-testid="decrement", or accessible labels like “Decrease value”) and the selector keys off those instead of raw button order:decrementButton: widget.getByRole('button', { name: /decrease/i })or a data‑attribute locator. Not blocking, but worth considering to align with the “prefer specific selectors in browser tests” guidance.
♻️ Duplicate comments (6)
src/scripts/utils.ts (1)
54-67: These TypeScript violations remain unresolved from the previous review.The code still contains the same two guideline violations that were flagged previously:
- Line 63:
(window as any).downloadBlob = fnuses a forbiddenas anytype assertion- Line 66:
.catch(() => {})silently swallows errorsBased on learnings and coding guidelines, these must be fixed.
The previously suggested fix is still applicable:
+declare global { + interface Window { + downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob + } +} + if (typeof window !== 'undefined') { import('@/base/common/downloadUtil') .then((module) => { const fn = ( module as { downloadBlob?: typeof import('@/base/common/downloadUtil').downloadBlob } ).downloadBlob if (typeof fn === 'function') { - ;(window as any).downloadBlob = fn + window.downloadBlob = fn } }) - .catch(() => {}) + .catch((error) => { + console.error('Failed to load downloadBlob utility:', error) + }) }src/locales/en/main.json (1)
1914-1929: Clarify header key naming and capitalization for number control copyThe new
widgets.numberControlstrings work functionally, but the header keys/copy are still a bit confusing:
controlHeaderBeforevscontrolHeaderBefore2rely on a numeric suffix to distinguish fragments.controlHeaderBeforeis sentence‑case (“Automatically update the value”), whilecontrolHeaderAfter/controlHeaderBefore2are ALL CAPS (“AFTER”, “BEFORE”).For readability and future maintenance, consider in a follow‑up:
- Renaming these keys to reflect their role (e.g.
headerPrefix,headerModeAfter,headerModeBefore,headerSuffix).- Standardizing capitalization (e.g. ALL CAPS for mode labels only, or consistent sentence case) and then adjusting the consuming component to join them into a single clear sentence.
Since this was already raised in an earlier review on this PR, treating it as a non‑blocking polish item is reasonable.
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
119-121: Conditionally render slot wrapper to avoid invisible hitbox.The absolutely positioned wrapper div renders unconditionally, creating an invisible hitbox when no slot content is provided. This should be guarded with
v-if="slots.default"to stay consistent with theinputClasspadding logic.Apply this diff:
</InputNumber> - <div class="absolute top-5 right-8 h-4 w-7 -translate-y-4/5"> + <div + v-if="slots.default" + class="absolute top-5 right-8 h-4 w-7 -translate-y-4/5" + > <slot /> </div> </WidgetLayoutField>This was previously flagged in past reviews but appears to still be present.
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (2)
25-37: Missing case for 'linkToGlobal' in convertToEnum.The function handles 'fixed', 'increment', 'decrement', and 'randomize', but does not handle 'linkToGlobal'. If a user passes 'linkToGlobal' as the
defaultValue, it will incorrectly fall through to the default case and returnRANDOMIZEinstead ofLINK_TO_GLOBAL.Apply this diff to add the missing case:
function convertToEnum(str?: ControlWidgetOptions): NumberControlMode { switch (str) { case 'fixed': return NumberControlMode.FIXED case 'increment': return NumberControlMode.INCREMENT case 'decrement': return NumberControlMode.DECREMENT case 'randomize': return NumberControlMode.RANDOMIZE + case 'linkToGlobal': + return NumberControlMode.LINK_TO_GLOBAL } return NumberControlMode.RANDOMIZE }
69-72: Inconsistent boundary constraint in LINK_TO_GLOBAL mode.Line 71 uses
minfor the lower bound while usingsafeMaxfor the upper bound. All other control modes consistently usesafeMinandsafeMax. This inconsistency could allow values outside the safe integer range ifmin < -1125899906842624.Apply this diff for consistency:
case NumberControlMode.LINK_TO_GLOBAL: // Use global seed value, constrained by min/max - newValue = Math.max(min, Math.min(safeMax, globalSeedStore.globalSeed)) + newValue = Math.max(safeMin, Math.min(safeMax, globalSeedStore.globalSeed)) breaksrc/composables/graph/useGraphNodeManager.ts (1)
102-111: Critical: Assignment operator used instead of comparison in find callback.Line 104 uses
=(assignment) instead of===(comparison). This will:
- Mutate every linked widget's
nameproperty to'control_after_generate'- Always return truthy, so it finds the first linked widget regardless of actual name
- Cause incorrect behavior and data corruption
Apply this diff to fix:
function getControlWidget(widget: IBaseWidget): SafeControlWidget | undefined { const cagWidget = widget.linkedWidgets?.find( - (w) => w.name == 'control_after_generate' + (w) => w.name === 'control_after_generate' ) if (!cagWidget) return return { value: validateControlWidgetValue(cagWidget.value), update: (value) => (cagWidget.value = validateControlWidgetValue(value)) } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (17)
browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.pngis excluded by!**/*.png
📒 Files selected for processing (27)
browser_tests/fixtures/VueNodeHelpers.ts(1 hunks)browser_tests/tests/vueNodes/widgets/int/integerWidget.spec.ts(1 hunks)src/composables/graph/useGraphNodeManager.ts(4 hunks)src/locales/en/main.json(1 hunks)src/renderer/extensions/vueNodes/components/NodeWidgets.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue(4 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vue(3 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue(2 hunks)src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue(0 hunks)src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts(2 hunks)src/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts(1 hunks)src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts(3 hunks)src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts(2 hunks)src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.ts(1 hunks)src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts(1 hunks)src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts(1 hunks)src/scripts/app.ts(3 hunks)src/scripts/utils.ts(1 hunks)src/stores/globalSeedStore.ts(1 hunks)src/types/simplifiedWidget.ts(2 hunks)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts(1 hunks)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts(2 hunks)tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.ts(1 hunks)tests-ui/tests/stores/globalSeedStore.test.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/renderer/extensions/vueNodes/widgets/components/layout/WidgetLayoutField.vue
🧰 Additional context used
📓 Path-based instructions (24)
**/*.{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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tsbrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tsbrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/scripts/utils.tssrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/scripts/utils.tstests-ui/tests/stores/globalSeedStore.test.tssrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tsbrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts
src/**/{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 directly
Files:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/scripts/app.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/scripts/utils.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/stores/globalSeedStore.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts
**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.ts
Files:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useComboWidget.tssrc/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.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/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts
browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (browser_tests/CLAUDE.md)
browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx}: Test user workflows in browser tests
Use Playwright fixtures for browser tests
Follow naming conventions for browser tests
Check assets/ directory for test data when writing tests
Prefer specific selectors in browser tests
Test across multiple viewports
Files:
browser_tests/tests/vueNodes/widgets/int/integerWidget.spec.ts
browser_tests/**/*.{test,spec}.ts
📄 CodeRabbit inference engine (AGENTS.md)
Playwright E2E tests can use optional tags like
@mobileand@2xwhich are respected by the Playwright configuration
Files:
browser_tests/tests/vueNodes/widgets/int/integerWidget.spec.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/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 storesFiles:
src/stores/globalSeedStore.ts**/stores/*Store.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name Pinia stores with the
*Store.tssuffixFiles:
src/stores/globalSeedStore.ts🧠 Learnings (35)
📚 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:
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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} : Write tests for new featuresApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tsbrowser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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} : Mock external dependencies in testsApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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} : Use existing test utilities rather than writing custom utilitiesApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.ts📚 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 possibleApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.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: Check tests-ui/README.md for test guidelinesApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: browser_tests/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:22.909Z Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Prefer specific selectors in browser testsApplied to files:
browser_tests/tests/vueNodes/widgets/int/integerWidget.spec.tsbrowser_tests/fixtures/VueNodeHelpers.ts📚 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} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`Applied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: browser_tests/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:22.909Z Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Use Playwright fixtures for browser testsApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts📚 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} : Use `vitest` for unit testing in this projectApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.ts📚 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} : Use `test` instead of `it` for defining test cases in vitestApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tstests-ui/tests/stores/globalSeedStore.test.ts📚 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 panelsApplied to files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberSlider.vuetests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.tssrc/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 ToggleSwitchApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumber.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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} : Never use `as any` type assertions - fix the underlying type issueApplied to files:
src/scripts/utils.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} : Never use `any` type - use proper TypeScript typesApplied to files:
src/scripts/utils.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/**/*.{ts,tsx,vue} : Avoid using ts-expect-error; use proper TypeScript types insteadApplied to files:
src/scripts/utils.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/**/{services,composables}/**/*.{ts,tsx} : Use `api.fileURL()` for static file access instead of constructing URLs directlyApplied to files:
src/scripts/utils.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/tests/stores/globalSeedStore.test.tssrc/stores/globalSeedStore.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/tests/stores/globalSeedStore.test.tssrc/stores/globalSeedStore.ts📚 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 PopoverApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue📚 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 **/*.vue : 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 respectivelyApplied to files:
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuebrowser_tests/fixtures/VueNodeHelpers.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 SelectApplied to files:
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue📚 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/globalSeedStore.tssrc/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurredApplied to files:
src/composables/graph/useGraphNodeManager.ts📚 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 computed() for derived state in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/composables/useNumberStepCalculation.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 : Use setup() function in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Implement computed properties with computed() functionApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 : Implement computed properties with computed()Applied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 stylesApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useControlButtonIcon.ts📚 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 **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilitiesApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Use setup() function for component logic in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue🧬 Code graph analysis (9)
src/renderer/extensions/vueNodes/widgets/composables/useIntWidget.ts (3)
src/scripts/widgets.ts (1)
addValueControlWidget(79-102)src/lib/litegraph/src/LGraphNode.ts (1)
widget(3955-3958)src/schemas/nodeDef/migration.ts (1)
transformInputSpecV2ToV1(139-143)tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.ts (1)
src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
NumberControlRegistry(7-49)src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts (2)
src/scripts/widgets.ts (1)
addValueControlWidgets(104-286)src/schemas/nodeDef/migration.ts (1)
transformInputSpecV2ToV1(139-143)src/renderer/extensions/vueNodes/widgets/composables/useFloatWidget.ts (2)
src/scripts/widgets.ts (1)
addValueControlWidget(79-102)src/schemas/nodeDef/migration.ts (1)
transformInputSpecV2ToV1(139-143)src/scripts/app.ts (1)
src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
executeNumberControls(57-59)src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (4)
src/types/simplifiedWidget.ts (1)
ControlWidgetOptions(18-22)src/stores/globalSeedStore.ts (1)
useGlobalSeedStore(4-16)src/renderer/core/layout/store/layoutStore.ts (1)
onChange(926-929)src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
numberControlRegistry(52-52)tests-ui/tests/stores/globalSeedStore.test.ts (1)
src/stores/globalSeedStore.ts (1)
useGlobalSeedStore(4-16)src/composables/graph/useGraphNodeManager.ts (2)
src/types/simplifiedWidget.ts (2)
SafeControlWidget(23-26)ControlWidgetOptions(18-22)src/scripts/domWidget.ts (3)
value(138-140)value(142-145)isDOMWidget(107-109)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts (2)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (1)
useStepperControl(39-98)src/renderer/core/layout/store/layoutStore.ts (1)
onChange(926-929)
| const { controlMode } = useStepperControl( | ||
| modelValue, | ||
| { | ||
| ...props.widget.options, | ||
| onChange: handleControlChange | ||
| }, | ||
| props.widget.controlWidget!.value | ||
| ) |
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.
Unsafe non-null assertions on controlWidget.
Lines 34 and 41 use non-null assertions (widget.controlWidget!) without defensive checks. If controlWidget is undefined, this will cause a runtime error. While the parent component may conditionally render this component only when controlWidget exists, TypeScript cannot verify this.
Consider adding a runtime guard or updating the prop type to guarantee controlWidget is defined:
const props = defineProps<{
- widget: SimplifiedWidget<number>
+ widget: SimplifiedWidget<number> & { controlWidget: SafeControlWidget }
readonly?: boolean
}>()Or add a defensive check:
if (!props.widget.controlWidget) {
throw new Error('WidgetInputNumberWithControl requires a widget with controlWidget')
}🤖 Prompt for AI Agents
In
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue
around lines 28 to 35 (and usages at line 41), non-null assertions are used on
props.widget.controlWidget which can throw at runtime if undefined; add a
runtime guard at the top of the setup (or component entry) that checks for
props.widget.controlWidget and throws a clear error (or return early) if absent,
or else tighten the prop type to require controlWidget so TypeScript guarantees
presence; ensure all subsequent uses remove the `!` after you’ve validated or
updated the prop type.
| import { isComboInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' | ||
| import { transformInputSpecV2ToV1 } from '@/schemas/nodeDef/migration' | ||
| import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget' | ||
| import type { BaseDOMWidget } from '@/scripts/domWidget' | ||
| import { addValueControlWidgets } from '@/scripts/widgets' | ||
| import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets' | ||
| import { addValueControlWidgets } from '@/scripts/widgets' | ||
| import { useAssetsStore } from '@/stores/assetsStore' |
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.
🧹 Nitpick | 🔵 Trivial
Make MultiSelect control wiring explicit and aligned with other combo widgets
Wiring addValueControlWidgets into addMultiSelectWidget is consistent with how combo widgets handle control_after_generate, but here we rely on ComponentWidgetImpl being treated as a combo inside addValueControlWidgets.
To avoid silent misbehavior if the MultiSelect DOM widget is ever not recognized as a combo (e.g., isComboWidget(widget) returning false and falling into the “number” path), consider mirroring the defensive guard used in createInputMappingWidget / addComboWidget:
const addMultiSelectWidget = (
node: LGraphNode,
inputSpec: ComboInputSpec
): IBaseWidget => {
@@
addWidget(node, widget as BaseDOMWidget<object | string>)
// TODO: Add remote support to multi-select widget
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/3003
if (inputSpec.control_after_generate) {
+ if (!isComboWidget(widget)) {
+ throw new Error(`Expected combo widget but received ${widget.type}`)
+ }
widget.linkedWidgets = addValueControlWidgets(
node,
widget,
undefined,
undefined,
transformInputSpecV2ToV1(inputSpec)
)
}Also, please double‑check that isComboWidget(widget) returns true for this MultiSelect wrapper so the control logic follows the combo path rather than the numeric path in addValueControlWidgets.
Also applies to: 72-80
🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/composables/useComboWidget.ts around
lines 20-26 and 72-80, the MultiSelect widget is wired into
addValueControlWidgets without an explicit guard that it is treated as a combo,
which can cause controls to follow the numeric path if isComboWidget(widget)
becomes false; update the code to mirror the defensive pattern used in
createInputMappingWidget/addComboWidget by checking isComboWidget(widget) (or
otherwise ensuring the ComponentWidgetImpl is flagged/constructed as a combo)
before calling addValueControlWidgets and, if it’s not a combo, fall back to the
intended combo wiring path (or throw/log an explicit error), and add a runtime
assertion or unit-check to confirm isComboWidget returns true for this
MultiSelect wrapper.
| executeControls(phase: 'before' | 'after'): void { | ||
| const settingStore = useSettingStore() | ||
| if (settingStore.get('Comfy.WidgetControlMode') === phase) { | ||
| for (const applyFn of this.controls.values()) { | ||
| applyFn() | ||
| } | ||
| } | ||
| } |
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.
Add error handling when executing registered controls
Right now, if any applyFn throws, it will stop execution of subsequent controls and bubble the error up to the caller. For a shared registry invoked around generate, that can make a single bad control break the whole flow and conflicts with the guideline to add error handling in services.
Consider catching errors per callback so the rest can still run, and log via your preferred logger (or console.error as a fallback), e.g.:
executeControls(phase: 'before' | 'after'): void {
const settingStore = useSettingStore()
if (settingStore.get('Comfy.WidgetControlMode') === phase) {
- for (const applyFn of this.controls.values()) {
- applyFn()
- }
+ for (const applyFn of this.controls.values()) {
+ try {
+ applyFn()
+ } catch (error) {
+ // Replace with shared logging utility if available
+ console.error('Failed to execute number control', error)
+ }
+ }
}
}As per coding guidelines, services here should include basic error handling.
🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts
around lines 27 to 34, the executeControls method currently calls each applyFn
directly which lets a thrown error stop the loop; update it to wrap each applyFn
call in a try/catch so a failing control does not prevent subsequent controls
from running, and log the error inside the catch (use the project logger if
available, otherwise console.error) along with identifying context (which phase
and which control key or index) so failures are recorded but execution
continues.
| for (const subgraph of this.graph.subgraphs.values()) { | ||
| executeWidgetsCallback(subgraph.nodes, 'beforeQueued') | ||
| } | ||
| executeNumberControls('before') |
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.
Add error handling around executeNumberControls to prevent queue processing failures.
If executeNumberControls throws an exception, it will halt the queue processing flow and prevent prompts from being queued. Consider wrapping these calls in try-catch blocks to log errors without breaking the queue.
Apply this diff to add error handling:
executeWidgetsCallback(this.graph.nodes, 'beforeQueued')
for (const subgraph of this.graph.subgraphs.values()) {
executeWidgetsCallback(subgraph.nodes, 'beforeQueued')
}
- executeNumberControls('before')
+ try {
+ executeNumberControls('before')
+ } catch (error) {
+ console.error('Error executing number controls (before):', error)
+ }
const p = await this.graphToPrompt(this.graph) executeWidgetsCallback(
p.workflow.nodes
.map((n) => this.graph.getNodeById(n.id))
.filter((n) => !!n),
'afterQueued'
)
for (const subgraph of this.graph.subgraphs.values()) {
executeWidgetsCallback(subgraph.nodes, 'afterQueued')
}
- executeNumberControls('after')
+ try {
+ executeNumberControls('after')
+ } catch (error) {
+ console.error('Error executing number controls (after):', error)
+ }
this.canvas.draw(true, true)Also applies to: 1407-1407
🤖 Prompt for AI Agents
In src/scripts/app.ts around lines 1356 and 1407, calls to
executeNumberControls('before') and the similar call at 1407 are unguarded and
can throw, halting queue processing; wrap each call in a try-catch that catches
any exception, logs the error with contextual information (including which
call/location and the error object) using the module's logger (or processLogger)
and then continues without rethrowing so the queue processing isn't interrupted.
src/types/simplifiedWidget.ts
Outdated
| export type ControlWidgetOptions = | ||
| | 'fixed' | ||
| | 'increment' | ||
| | 'decrement' | ||
| | 'randomize' |
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 'linkToGlobal' option in ControlWidgetOptions type.
The ControlWidgetOptions union type does not include 'linkToGlobal', but NumberControlMode.LINK_TO_GLOBAL exists in useStepperControl.ts (line 14). While ENABLE_LINK_TO_GLOBAL is currently false in NumberControlPopover.vue (line 29), the type should be complete to support the full enum when the feature is enabled.
Apply this diff to add the missing option:
export type ControlWidgetOptions =
| 'fixed'
| 'increment'
| 'decrement'
| 'randomize'
+ | 'linkToGlobal'🤖 Prompt for AI Agents
In src/types/simplifiedWidget.ts around lines 18 to 22, the ControlWidgetOptions
union type is missing the 'linkToGlobal' member referenced by
NumberControlMode.LINK_TO_GLOBAL; add 'linkToGlobal' to the union so the type
covers that option (i.e., extend the existing union to include 'linkToGlobal'),
keeping formatting consistent with the other string literals.
| import { createPinia, setActivePinia } from 'pinia' | ||
| import { beforeEach, describe, expect, it } from 'vitest' | ||
|
|
||
| import { useGlobalSeedStore } from '@/stores/globalSeedStore' | ||
|
|
||
| describe('useGlobalSeedStore', () => { | ||
| let store: ReturnType<typeof useGlobalSeedStore> | ||
|
|
||
| beforeEach(() => { | ||
| setActivePinia(createPinia()) | ||
| store = useGlobalSeedStore() | ||
| }) | ||
|
|
||
| describe('initialization', () => { | ||
| it('should initialize with a random global seed', () => { | ||
| expect(typeof store.globalSeed).toBe('number') | ||
| expect(store.globalSeed).toBeGreaterThanOrEqual(0) | ||
| expect(store.globalSeed).toBeLessThan(1000000) | ||
| }) | ||
|
|
||
| it('should create different seeds for different store instances', () => { | ||
| const store1 = useGlobalSeedStore() | ||
| setActivePinia(createPinia()) // Reset pinia | ||
| const store2 = useGlobalSeedStore() | ||
|
|
||
| // Very unlikely to be the same (1 in 1,000,000 chance) | ||
| expect(store1.globalSeed).not.toBe(store2.globalSeed) | ||
| }) | ||
| }) | ||
|
|
||
| describe('setGlobalSeed', () => { | ||
| it('should update the global seed value', () => { | ||
| const newSeed = 42 | ||
|
|
||
| store.setGlobalSeed(newSeed) | ||
|
|
||
| expect(store.globalSeed).toBe(newSeed) | ||
| }) | ||
|
|
||
| it('should accept any number value', () => { | ||
| const testValues = [0, 1, 999999, 1000000, -1, 123.456] | ||
|
|
||
| for (const value of testValues) { | ||
| store.setGlobalSeed(value) | ||
| expect(store.globalSeed).toBe(value) | ||
| } | ||
| }) | ||
| }) | ||
|
|
||
| describe('reactivity', () => { | ||
| it('should be reactive when global seed changes', () => { | ||
| const initialSeed = store.globalSeed | ||
| const newSeed = initialSeed + 100 | ||
|
|
||
| store.setGlobalSeed(newSeed) | ||
|
|
||
| expect(store.globalSeed).toBe(newSeed) | ||
| expect(store.globalSeed).not.toBe(initialSeed) | ||
| }) | ||
| }) |
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.
🧹 Nitpick | 🔵 Trivial
Make tests deterministic and align with vitest style (test vs it)
The coverage here is solid, but two improvements would make these tests more robust and consistent with the repo’s unit‑test rules:
- Avoid relying on real randomness
'should create different seeds for different store instances' can (very rarely) fail if Math.random() returns the same value twice. You can make this deterministic by mocking Math.random:
-import { beforeEach, describe, expect, it } from 'vitest'
+import { beforeEach, describe, expect, test, vi } from 'vitest'
@@
- describe('initialization', () => {
- it('should initialize with a random global seed', () => {
+ describe('initialization', () => {
+ test('should initialize with a random global seed', () => {
@@
- it('should create different seeds for different store instances', () => {
- const store1 = useGlobalSeedStore()
- setActivePinia(createPinia()) // Reset pinia
- const store2 = useGlobalSeedStore()
-
- // Very unlikely to be the same (1 in 1,000,000 chance)
- expect(store1.globalSeed).not.toBe(store2.globalSeed)
- })
+ test('should create different seeds for different store instances', () => {
+ const randomSpy = vi
+ .spyOn(Math, 'random')
+ .mockReturnValueOnce(0.1)
+ .mockReturnValueOnce(0.9)
+
+ const store1 = useGlobalSeedStore()
+ setActivePinia(createPinia()) // Reset pinia
+ const store2 = useGlobalSeedStore()
+
+ expect(store1.globalSeed).not.toBe(store2.globalSeed)
+
+ randomSpy.mockRestore()
+ })
@@
- describe('setGlobalSeed', () => {
- it('should update the global seed value', () => {
+ describe('setGlobalSeed', () => {
+ test('should update the global seed value', () => {
@@
- it('should accept any number value', () => {
+ test('should accept any number value', () => {
@@
- describe('reactivity', () => {
- it('should be reactive when global seed changes', () => {
+ describe('reactivity', () => {
+ test('should be reactive when global seed changes', () => {This keeps the semantics the same while removing flakiness and matching the test‑preferred style for Vitest in this repo.
| } | ||
|
|
||
| function validateControlWidgetValue(val: unknown): ControlWidgetOptions { | ||
| //TODO: Is there a way to do this without repeating? |
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 do something like I did with the expandables.
const CONTROL_OPTIONS = [ 'fixed', 'increment', 'decrement', 'randomize' ] as const
export function validateControlOption(maybeOption: string): ControlOption {
if (CONTROL_OPTIONS.includes(maybeOption as ControlOption)) return maybeOption
return 'randomize'
}You could also use a type guard in there.
src/locales/en/main.json
Outdated
| "controlHeaderBefore": "Automatically update the value", | ||
| "controlHeaderAfter": "AFTER", | ||
| "controlHeaderBefore2": "BEFORE", | ||
| "controlHeaderEnd": "running the workflow:", |
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.
Optional: You could nest these
| "controlHeaderBefore": "Automatically update the value", | |
| "controlHeaderAfter": "AFTER", | |
| "controlHeaderBefore2": "BEFORE", | |
| "controlHeaderEnd": "running the workflow:", | |
| "controlHeader": { | |
| "Before": "Automatically update the value", | |
| "After": "AFTER", | |
| "Before2": "BEFORE", | |
| "End": "running the workflow:", | |
| } |
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: 1
♻️ Duplicate comments (4)
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
115-117: Add conditional rendering to prevent invisible hitbox.The absolutely positioned wrapper renders unconditionally, creating an invisible overlay that can block input interaction when no slot is provided. This should only render when a default slot exists, matching the conditional padding logic.
Apply this diff:
- <div class="absolute top-5 right-8 h-4 w-7 -translate-y-4/5"> + <div + v-if="slots.default" + class="absolute top-5 right-8 h-4 w-7 -translate-y-4/5" + > <slot /> </div>src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (1)
65-99: Fix inconsistent boundary constraint in LINK_TO_GLOBAL mode.Line 88 uses
minfor the lower bound while all other modes consistently usesafeMin. This inconsistency could allow values outside the safe integer range ifmin < -1125899906842624.Apply this diff for consistency:
case NumberControlMode.LINK_TO_GLOBAL: // Use global seed value, constrained by min/max - newValue = Math.max(min, Math.min(safeMax, globalSeedStore.globalSeed)) + newValue = Math.max(safeMin, Math.min(safeMax, globalSeedStore.globalSeed)) breaksrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue (1)
26-37: Add runtime guard or tighten prop type to prevent unsafe non-null assertions.Lines 32 and 37 use non-null assertions (
widget.controlWidget!) without defensive checks. IfcontrolWidgetis undefined, this will cause a runtime error. While the parent component may conditionally render this component only whencontrolWidgetexists, TypeScript cannot verify this guarantee.Consider one of these solutions:
Option 1: Add a runtime guard at component entry:
const props = defineProps<{ widget: SimplifiedWidget<number> }>() if (!props.widget.controlWidget) { throw new Error('WidgetInputNumberWithControl requires a widget with controlWidget') } // Then remove the `!` assertions belowOption 2: Tighten the prop type to require controlWidget:
const props = defineProps<{ widget: SimplifiedWidget<number> & { controlWidget: SafeControlWidget } }>() // Then remove the `!` assertions belowOption 2 is preferred as it provides compile-time safety.
src/composables/graph/useGraphNodeManager.ts (1)
87-96: Use strict equality operator.Line 89 uses loose equality (
==) instead of strict equality (===). While this works for string comparison, TypeScript best practices recommend using===for type safety and consistency.Apply this diff:
const cagWidget = widget.linkedWidgets?.find( - (w) => w.name == 'control_after_generate' + (w) => w.name === 'control_after_generate' )Based on coding guidelines, TypeScript should use strict equality for type safety.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
src/composables/graph/useGraphNodeManager.ts(4 hunks)src/locales/en/main.json(1 hunks)src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue(4 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue(1 hunks)src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts(1 hunks)src/types/simplifiedWidget.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (18)
**/*.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue**/*.{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.jsonFiles:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue**/*.{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 committingFiles:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/**/*.{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.jsonFiles:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue**/*.{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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/**/*.{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 codebaseFiles:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/**/{composables,components}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Clean up subscriptions in state management to prevent memory leaks
Files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/types/simplifiedWidget.tssrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vuesrc/**/{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/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/types/simplifiedWidget.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 issueFiles:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/types/simplifiedWidget.tssrc/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safetyFiles:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/types/simplifiedWidget.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 configurationFiles:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.tssrc/types/simplifiedWidget.tssrc/**/{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/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/composables/graph/useGraphNodeManager.ts**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.tsFiles:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts🧠 Learnings (17)
📚 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 ToggleSwitchApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue📚 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 **/*.vue : 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 respectivelyApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue📚 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 **/*.vue : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.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 panelsApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuesrc/renderer/extensions/vueNodes/widgets/composables/useStepperControl.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.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 : Use setup() function in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 computed() for derived state in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Implement computed properties with computed() functionApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 : Implement computed properties with computed()Applied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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 stylesApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.{vue,ts,tsx} : Leverage VueUse functions for performance-enhancing utilitiesApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Use setup() function for component logic in Vue 3 Composition APIApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/*.vue : Never use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'` for class merging in Vue templatesApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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 **/composables/use*.ts : Name composables in the format `useXyz.ts`Applied to files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/**/*.{js,ts,jsx,tsx} : Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurredApplied to files:
src/composables/graph/useGraphNodeManager.ts📚 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 PopoverApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vuesrc/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.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 SelectApplied to files:
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.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 Calendar component with DatePickerApplied to files:
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue🧬 Code graph analysis (1)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (3)
src/types/simplifiedWidget.ts (1)
ControlOptions(24-24)src/stores/globalSeedStore.ts (1)
useGlobalSeedStore(4-16)src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts (1)
numberControlRegistry(52-52)⏰ 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). (5)
- GitHub Check: deploy-and-comment
- GitHub Check: collect
- GitHub Check: lint-and-format
- GitHub Check: test
- GitHub Check: setup
🔇 Additional comments (12)
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (2)
3-3: LGTM!The
useSlotsimport is correctly added and used to detect the presence of a default slot for conditional styling.
72-72: LGTM!The slot detection and conditional padding logic is well-implemented. The right padding is correctly applied only when a default slot is present.
Also applies to: 94-98
src/types/simplifiedWidget.ts (2)
18-30: LGTM!The control options validation is well-implemented. The exclusion of 'linkToGlobal' is intentional since the feature is currently disabled (ENABLE_LINK_TO_GLOBAL = false in NumberControlPopover.vue). The default fallback to 'randomize' provides safe behavior for unknown values.
32-35: LGTM!The
SafeControlWidgettype provides a clean API for control interaction with a value getter and update callback. The optionalcontrolWidgetfield inSimplifiedWidgetcorrectly enables the control feature without breaking existing widgets.Also applies to: 68-68
src/composables/graph/useGraphNodeManager.ts (2)
23-24: LGTM!The imports are correctly added for the new control widget functionality.
130-131: LGTM!The
controlWidgetis correctly populated in the widget mapper using the helper function. The integration is clean and follows the existing pattern.src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (3)
9-23: LGTM!The
NumberControlModeenum andStepperControlOptionsinterface are well-defined. IncludingLINK_TO_GLOBALin the enum despite the feature being currently disabled is good forward-planning for future enablement.
25-37: LGTM!The
convertToEnumfunction correctly handles all enabled control options. The absence of 'linkToGlobal' is intentional since the feature is currently disabled (ENABLE_LINK_TO_GLOBAL = false).
39-54: LGTM!The icon mapping is comprehensive and uses appropriate icons for each control mode.
src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue (2)
1-87: LGTM!The component is well-structured using Vue 3 Composition API. The feature flag for
LINK_TO_GLOBALis appropriately placed, and the component API (props, emits, expose) is clean and well-defined.As per coding guidelines, the component correctly uses PrimeVue Popover (not the deprecated OverlayPanel) and ToggleSwitch (not the deprecated InputSwitch).
89-171: LGTM!The template is well-structured with proper use of semantic CSS classes, i18n for all user-facing text, and clean conditional rendering. The layout is responsive and accessible.
As per coding guidelines, all user-facing strings are correctly internationalized using vue-i18n.
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue (1)
45-67: LGTM!The template is well-structured with proper component composition and grid layout. The button and popover integration is clean.
| "numberControl": { | ||
| "header" : { | ||
| "prefix": "Automatically update the value", | ||
| "after": "AFTER", | ||
| "before": "BEFORE", | ||
| "postfix": "running the workflow:" | ||
| }, | ||
| "linkToGlobal": "Link to", | ||
| "linkToGlobalSeed": "Global Value", | ||
| "linkToGlobalDesc": "Unique value linked to the Global Value's control setting", | ||
| "randomize": "Randomize Value", | ||
| "randomizeDesc": "Shuffles the value randomly after each generation", | ||
| "increment": "Increment Value", | ||
| "incrementDesc": "Adds 1 to the value number", | ||
| "decrement": "Decrement Value", | ||
| "decrementDesc": "Subtracts 1 from the value number", | ||
| "editSettings": "Edit control settings" | ||
| } |
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.
🧹 Nitpick | 🔵 Trivial
Verify intentional ALL CAPS usage for mode labels.
Lines 1917-1918 use ALL CAPS for "AFTER" and "BEFORE" while surrounding text uses mixed case. This creates visual emphasis but is inconsistent with typical UI text conventions.
Please confirm this capitalization is intentional for design/UX reasons. If the goal is to emphasize the control mode, consider alternative approaches like bold styling or a separate UI treatment rather than ALL CAPS in the localization string.
Example alternative structure:
"header": {
"prefix": "Automatically update the value",
"afterMode": "after",
"beforeMode": "before",
"postfix": "running the workflow:"
}Then apply emphasis via CSS in the component rather than hardcoding capitalization.
🤖 Prompt for AI Agents
In src/locales/en/main.json around lines 1914 to 1931, the header labels "AFTER"
and "BEFORE" are in ALL CAPS while surrounding text is mixed case; confirm
whether ALL CAPS is intentional for UX, and if not replace those values with
normal-cased alternatives (e.g., "after"/"before" or "After"/"Before") and
optionally rename the keys to semantic names like afterMode/beforeMode so
emphasis is applied in the component via styling/CSS instead of hardcoded
capitalization.
| // precision 1 → 0.1, precision 2 → 0.01, etc. | ||
| return 1 / Math.pow(10, precision.value) | ||
| }) | ||
| const stepValue = useNumberStepCalculation(widget.options, precision, true) |
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 think this could be a utility function instead of a composable for now.
| */ | ||
| export function useNumberStepCalculation( | ||
| options: NumberWidgetOptions | undefined, | ||
| precision: Ref<number | undefined>, |
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.
https://vuejs.org/api/reactivity-utilities.html#tovalue
This is a good place for MaybeRefOrGetter
| export enum NumberControlMode { | ||
| FIXED = 'fixed', | ||
| INCREMENT = 'increment', | ||
| DECREMENT = 'decrement', | ||
| RANDOMIZE = 'randomize', | ||
| LINK_TO_GLOBAL = 'linkToGlobal' | ||
| } |
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.
| export enum NumberControlMode { | |
| FIXED = 'fixed', | |
| INCREMENT = 'increment', | |
| DECREMENT = 'decrement', | |
| RANDOMIZE = 'randomize', | |
| LINK_TO_GLOBAL = 'linkToGlobal' | |
| } | |
| export const NumberControlMode = { | |
| FIXED: 'fixed', | |
| INCREMENT: 'increment', | |
| DECREMENT: 'decrement', | |
| RANDOMIZE: 'randomize', | |
| LINK_TO_GLOBAL: 'linkToGlobal' | |
| } as const |
That thing we talked about
| type ControlOption = { | ||
| description: string | ||
| icon?: string | ||
| mode: NumberControlMode | ||
| text?: string | ||
| title: string | ||
| } |
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.
Alphabetical is great, but required before optional please <3
|
|
||
| const applyControl = () => { | ||
| const { min = 0, max = 1000000, step2, step = 1, onChange } = options | ||
| const safeMax = Math.min(1125899906842624, max) |
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.
|
|
||
| // Register with singleton registry | ||
| onMounted(() => { | ||
| numberControlRegistry.register(controlId, applyControl) |
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.
Double check to see if you need to be DOM dependent here.
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.
Followup: sorta. It's being used as a convenient way to ensure it gets unregistered at some point. Since I'm proposing removal in a future PR, it's probably not worth rewriting the implementation here.
| 'randomize' | ||
| ] as const | ||
| export type ControlOptions = (typeof CONTROL_OPTIONS)[number] | ||
|
|
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.
| function isValidControlOption(optionMaybe: string): optionMaybe is ControlOption { | |
| return CONTROL_OPTIONS.includes(optionMaybe as ControlOptions) | |
| } |
| ] as const | ||
| export type ControlOptions = (typeof CONTROL_OPTIONS)[number] | ||
|
|
||
| export function validateControlOption(val: unknown): ControlOptions { |
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.
Also, this is technically normalizing, right?
|
|
||
| describe('useStepperControl', () => { | ||
| beforeEach(() => { | ||
| setActivePinia(createPinia()) |
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.
createTestingPinia please
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 think this piece could be pruned for now, it's a deserving feature and should be treated with such deference.
Continuation of #6034 with
Several issues from original PR have not (yet) been addressed, but are likely better moved to future PR
┆Issue is synchronized with this Notion page by Unito