-
Notifications
You must be signed in to change notification settings - Fork 426
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
📝 WalkthroughWalkthroughAdds a number-control system: new types, composables, UI components, a registry and global seed store; wires control widgets into widget creation and renderer payloads; executes registered controls around prompt queuing. Changes
Sequence Diagram(s)sequenceDiagram
participant User as UI
participant Widget as Widget Component
participant Control as useStepperControl
participant Store as GlobalSeedStore
participant Registry as NumberControlRegistry
User->>Widget: change number input
Widget->>Control: applyControl / onChange
Control->>Control: determine NumberControlMode
alt RANDOMIZE
Control->>Store: read globalSeed
Store-->>Control: seed
Control->>Control: compute random value (min..max)
else LINK_TO_GLOBAL
Control->>Store: read and clamp globalSeed
Store-->>Control: clamped value
else INCREMENT/DECREMENT
Control->>Control: add/subtract step and clamp
else FIXED
Control->>Control: no change
end
Control-->>Widget: return updated value
Widget-->>User: render updated value
sequenceDiagram
participant App as App (queuePrompt)
participant Registry as NumberControlRegistry
participant Setting as Setting Store
participant Controls as Registered Controls
App->>Registry: executeNumberControls('before')
Registry->>Setting: get('Comfy.WidgetControlMode')
Setting-->>Registry: mode
alt mode == 'before'
Registry->>Controls: invoke all callbacks
end
App->>App: process queued prompt(s)
App->>Registry: executeNumberControls('after')
Registry->>Setting: get('Comfy.WidgetControlMode')
Setting-->>Registry: mode
alt mode == 'after'
Registry->>Controls: invoke all callbacks
end
Possibly related PRs
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 11/30/2025, 09:58:32 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results❌ Some tests failed ⏰ Completed at: 11/30/2025, 10:10:14 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.18 MB (baseline 3.18 MB) • 🔴 +2.77 kBMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 951 kB (baseline 950 kB) • 🔴 +729 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 — 147 kB (baseline 139 kB) • 🔴 +7.35 kBReusable component library chunks
Status: 9 added / 9 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 3 added / 3 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.6 MB (baseline 8.56 MB) • 🔴 +43.2 kBExternal libraries and shared vendor chunks
Status: 5 added / 5 removed Other — 3.85 MB (baseline 3.84 MB) • 🔴 +7.51 kBBundles that do not match a named category
Status: 24 added / 23 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
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: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
70-75: Localize tooltip text via vue‑i18n instead of hard‑coding
buttonTooltipreturns a hard‑coded, user‑visible English string, which conflicts with the “use vue‑i18n for ALL user‑facing strings” rule forsrc/**/componentsfiles.Please move this into i18n, e.g.:
import { useI18n } from 'vue-i18n' const { t } = useI18n() const buttonTooltip = computed(() => buttonsDisabled.value ? t('widget.numberInput.precisionLimitTooltip') : null )and add
widget.numberInput.precisionLimitTooltiptosrc/locales/en/main.json.src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts (1)
297-315: Avoidas anyin tests; widen component model type to acceptundefinedor use proper castingThe test intentionally exercises an
undefinedmodel value to validate graceful handling (line 63 of the component usesmodelValue.value ?? 0to handle falsy values). However,modelValue: undefined as anyviolates the "never useany/as any" guideline. Since the component's runtime code demonstrates it handlesundefineddefensibly:Option 1 (preferred): Widen the component model type:
// In WidgetInputNumberInput.vue line 24 const modelValue = defineModel<number | undefined>({ default: 0 })Then pass
undefinedwithout casting:modelValue: undefinedOption 2 (if component changes aren't feasible): Avoid
any:- modelValue: undefined as any + modelValue: undefined as unknown as numberOption 1 is cleaner since
undefinedis clearly a realistic runtime value the component already handles.
♻️ Duplicate comments (1)
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
96-119: Remove debugconsole.logand unused template refThe increment button still has a debug handler:
<NumberFieldIncrement v-if="!buttonsDisabled" :class="incrementClass" :disabled="!canIncrement" @mouseup="console.log('up')" />and
ref="numberFieldRoot"is declared but never read in the script.
- Drop the
console.logor replace it with the intended handler/emit.- If you don’t need to access the root instance, remove the
refto avoid confusion.Example:
- v-bind="filteredProps" - ref="numberFieldRoot" + v-bind="filteredProps" @@ - :disabled="!canIncrement" - @mouseup="console.log('up')" + :disabled="!canIncrement" />
🧹 Nitpick comments (7)
src/composables/graph/useGraphNodeManager.ts (1)
99-108: Good progress on the fix—use strict equality (===) to complete it.The assignment operator (
=) has been corrected to a comparison operator (==), which resolves the critical bug from the previous review. However, best practice is to use strict equality (===) instead of loose equality (==).Apply this final refinement:
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)) } }tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts (4)
10-30: Mocks look good; consider asserting registry behavior or simplifying surfaceThe store and registry mocks are reasonable and match the composable’s dependencies. Right now, none of the tests assert
numberControlRegistry.register/unregister, so either:
- add expectations around registration/unregistration to verify lifecycle wiring, or
- trim the mock to only what’s actually needed to keep the suite lean.
133-160: RANDOMIZE test never actually fails; prefer deterministicMath.randommockingThe loop from Line 149 to Line 159 is intended to detect lack of randomness, but it always ends with
expect(true).toBe(true), so the test will pass even if the value never changes.To make this non-flaky but meaningful, mock
Math.randominstead of relying on real randomness, e.g.:- it('should randomize value in RANDOMIZE mode', () => { - const modelValue = ref(100) - const options = { min: 0, max: 10, step: 1 } - - const { controlMode, applyControl } = useStepperControl( - modelValue, - options - ) - controlMode.value = NumberControlMode.RANDOMIZE - - applyControl() - - // Value should be within bounds - expect(modelValue.value).toBeGreaterThanOrEqual(0) - expect(modelValue.value).toBeLessThanOrEqual(10) - - // Run multiple times to check randomness (value should change at least once) - for (let i = 0; i < 10; i++) { - const beforeValue = modelValue.value - applyControl() - if (modelValue.value !== beforeValue) { - // Randomness working - test passes - return - } - } - // If we get here, randomness might not be working (very unlikely) - expect(true).toBe(true) // Still pass the test - }) + it('should randomize value in RANDOMIZE mode', () => { + const modelValue = ref(100) + const options = { min: 10, max: 20, step: 1 } + const randomSpy = vi + .spyOn(Math, 'random') + .mockReturnValueOnce(0) // -> min + .mockReturnValueOnce(0.999999) // -> max + + const { controlMode, applyControl } = useStepperControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.RANDOMIZE + + applyControl() + expect(modelValue.value).toBe(10) + + applyControl() + expect(modelValue.value).toBe(20) + + randomSpy.mockRestore() + })This keeps the test deterministic while actually validating the RANDOMIZE behavior. Based on learnings, tests should mock external dependencies rather than rely on real randomness.
176-188: Optional: also cover clamping when global seed exceedsmaxYou already test clamping when the global seed is below
min. For completeness, consider adding a second test where mockedglobalSeedis abovemaxto assert the upper clamp path as well.
191-222: Consider adding coverage forstep2precedence overstepThe composable uses
step2when present and falls back tostep. The default-options tests are good, but they don’t currently assert this precedence. A small additional test like:it('uses step2 when provided', () => { const modelValue = ref(0) const options = { step: 1, step2: 5 } const { controlMode, applyControl } = useStepperControl(modelValue, options) controlMode.value = NumberControlMode.INCREMENT applyControl() expect(modelValue.value).toBe(5) })would lock in that behavior.
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (2)
77-85: GuardcanIncrement/canDecrementwhenmin/maxare undefinedRight now:
const canDecrement = computed(() => modelValue.value > filteredProps.value.min) const canIncrement = computed(() => modelValue.value < filteredProps.value.max)If
minormaxare absent fromfilteredProps, these comparisons becomevalue > undefined/value < undefined, which evaluate tofalse, so both buttons end up disabled even though there is no actual bound.Consider treating missing bounds as unbounded, e.g.:
const canDecrement = computed(() => { const min = filteredProps.value.min if (typeof min !== 'number') return true return modelValue.value > min }) const canIncrement = computed(() => { const max = filteredProps.value.max if (typeof max !== 'number') return true return modelValue.value < max })This more closely matches typical numeric input behavior when
min/maxare omitted.
123-129: Consider removing legacy.p-inputnumber-inputstylesThe scoped style still targets
.p-inputnumber-input, which was specific to PrimeVueInputNumber. Now that the template usesNumberFieldRoot/NumberFieldInputfromreka-ui, this selector is likely dead and can be removed unless you know those components still emit that class.Cleaning it up will reduce confusion for future maintainers.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (18)
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/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/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.pngis excluded by!**/*.pngbrowser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-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 (4)
src/composables/graph/useGraphNodeManager.ts(4 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts(11 hunks)src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue(2 hunks)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (20)
**/*.{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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuetests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuetests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuetests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuetests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
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/components/WidgetInputNumberInput.test.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
**/*.{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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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/composables/graph/useGraphNodeManager.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/WidgetInputNumberInput.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.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.vuetests-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 mocksFiles:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.tsFiles:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts🧠 Learnings (18)
📚 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.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/components/WidgetInputNumberInput.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vuetests-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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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 : 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.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.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 : Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Applied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.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/WidgetInputNumberInput.test.tssrc/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/lib/litegraph/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:56.371Z Learning: Applies to src/lib/litegraph/**/*.{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 InlineMessage component with MessageApplied 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 : Replace PrimeVue Calendar component with DatePickerApplied 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 : Replace PrimeVue Chips component with AutoComplete with multiple enabledApplied to files:
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:34.324Z Learning: Applies to src/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guideApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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} : 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/useStepperControl.test.ts🧬 Code graph analysis (1)
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts (2)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (1)
useStepperControl(39-96)src/renderer/core/layout/store/layoutStore.ts (1)
onChange(922-925)🪛 ESLint
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts
[error] 7-7: Unable to resolve path to module 'reka-ui'.
(import-x/no-unresolved)
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
[error] 7-7: Unable to resolve path to module 'reka-ui'.
(import-x/no-unresolved)
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/LWRRXZfkEC'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at ExportMap.get (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/export-map.js:88:22)
at processBodyStatement (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:9:31)
at Program (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/namespace.js:100:21)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/namespace)
[error] 1-1: Resolve error: EACCES: permission denied, open '/sTejqfgbMS'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-unresolved.js:31:34)
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/no-unresolved)
[error] 1-1: Resolve error: EACCES: permission denied, open '/oJPDLzInWr'
at Object.writeFileSync (node:fs:2409:20)
at l (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:13670)
at createFilesMatcher (file:///home/jailuser/git/node_modules/.pnpm/[email protected]/node_modules/get-tsconfig/dist/index.mjs:7:14422)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:70:65)
at Object.resolve (file:///home/jailuser/git/node_modules/.pnpm/[email protected][email protected]_@typescript-eslin_da4796079dab5a32abf73f9910d12370/node_modules/eslint-import-resolver-typescript/lib/index.js:147:20)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:69
at setRuleContext (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint-import-context/lib/index.js:23:20)
at fullResolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:170:30)
at relative (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:215:12)
at resolve (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/resolve.js:220:16)
at importType (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/import-type.js:126:63)
at checkImportForRelativePackage (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:15:38)
at file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/rules/no-relative-packages.js:59:40
at checkSourceValue (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:14:9)
at checkSource (file:///home/jailuser/git/node_modules/.pnpm/eslint-plugin-import-x@4.16.1_@typescript-eslint[email protected]_eslint@[email protected]__d4b6b79e6f12f59d34d55ebbf27dc73f/node_modules/eslint-plugin-import-x/lib/utils/module-visitor.js:17:9)
at ruleErrorHandler (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1173:33)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:46
at Array.forEach ()
at SourceCodeVisitor.callSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-visitor.js:76:30)
at /home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:291:18
at Array.forEach ()
at SourceCodeTraverser.traverseSync (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/source-code-traverser.js:290:10)
at runRules (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1214:12)
at #flatVerifyWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2101:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2189:43)
at Linter._verifyWithFlatConfigArray (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2292:15)
at Linter.verify (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:1677:10)
at Linter.verifyAndFix (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/linter/linter.js:2557:20)
at verifyText (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1179:45)
at readAndVerifyFile (/home/jailuser/git/node_modules/.pnpm/[email protected][email protected]/node_modules/eslint/lib/eslint/eslint-helpers.js:1320:10)(import-x/no-relative-packages)
[error] 1-1: Unable to resolve path to module 'pinia'.
(import-x/no-unresolved)
[error] 2-2: Unable to resolve path to module 'vitest'.
(import-x/no-unresolved)
[error] 3-3: Unable to resolve path to module 'vue'.
(import-x/no-unresolved)
[error] 8-8: Unable to resolve path to module '@/renderer/extensions/vueNodes/widgets/composables/useStepperControl'.
(import-x/no-unresolved)
🔇 Additional comments (6)
src/composables/graph/useGraphNodeManager.ts (2)
142-143: LGTM: Clean integration of control widget support.The control widget is correctly wired into the safe widget data extraction flow, following the same pattern as
isDOMWidget. The optional nature ofcontrolWidgetis properly handled.
87-98: The review comment contains a factual error:'link_to_global'is not a validControlWidgetOptionsvalue.The
ControlWidgetOptionstype definition contains only four values:'fixed','increment','decrement', and'randomize'. The suggested diff adding a case for'link_to_global'would violate TypeScript type safety and fail compilation.The current implementation is correct. The function:
- Explicitly handles the three most common cases (
'fixed','increment','decrement')- Defaults to
'randomize'for any unrecognized value- Returns a valid
ControlWidgetOptionstype in all pathsMinor improvements could include making
'randomize'an explicit case and adding a warning for truly invalid values, but the core suggestion in the review is invalid.Likely an incorrect or invalid review comment.
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useStepperControl.test.ts (2)
32-60: Solid initialization coverageThese tests cleanly verify the default mode (
RANDOMIZE) and thatapplyControlis exposed, which aligns with the composable API.
224-271: Good coverage ofonChangevs direct assignment pathsThese tests nicely exercise both the callback path and the direct assignment fallback, and the FIXED-mode suppression of
onChange, which matches the composable’s switch logic.src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue (1)
2-7: I'll help you verify the review comment for the WidgetInputNumberInput.vue file. Let me start by checking thereka-uidependency and examining the component file.
<function_calls>
#!/bin/bashVerify dependency declaration
echo "=== Checking package.json for reka-ui ==="
cat package.json | jq '.dependencies["reka-ui"], .devDependencies["reka-ui"]'echo ""
echo "=== Confirming reka-ui references in codebase ==="
rg -n "from 'reka-ui'" -S
</function_calls>Now let me examine the full WidgetInputNumberInput.vue file to check for the other issues mentioned in the scratchpad:
<function_calls>
#!/bin/bashFind and examine the WidgetInputNumberInput.vue file
echo "=== Finding WidgetInputNumberInput.vue ==="
fd -type f "WidgetInputNumberInput.vue"echo ""
echo "=== Full content of WidgetInputNumberInput.vue ==="
cat -n src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
</function_calls><function_calls>
#!/bin/bashCheck for i18n usage and console.log/debug in the file
echo "=== Checking for i18n and console patterns ==="
rg -n "buttonTooltip|console.|i18n|useI18n" src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue
</function_calls>src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts (1)
3-7: Reka-ui is a legitimate TypeScript-supported package; verify it's in your project dependencies.
Reka-uiis a published npm package providing unstyled Vue UI primitives with TypeScript support. The imports (NumberFieldRoot,NumberFieldIncrement,NumberFieldDecrement) follow the library's standard component export pattern.The ESLint
import-x/no-unresolvedwarning suggests either:
reka-uiis missing fromdependencies/devDependenciesinpackage.json- The unplugin resolver (
reka-ui/resolver) is not configured in your ESLint/TypeScript config- A configuration issue preventing import resolution
Confirm that
reka-uiis declared in yourpackage.jsonand that your ESLint/TypeScript tooling is properly configured to resolve it. If the package is present but ESLint still warns, check for missing resolver configuration.
Personally as a random SDXL user, I do think the "control before generate" setting should be made a lot more obvious (or better yet, the default), since it's more intuitive for users wanting to quickly retry a seed. In before-mode, when I get a promising-but-not-perfect result from a random seed, I can just switch to "fixed" mode, tweak my prompt, and rerun. In after-mode, by contrast, the seed has already changed, and I have to retrieve the old value by digging into the image's metadata in a separate program and manually copy-pasting it back into the seed field. I can't even copy seeds from the job history, only a useless* job ID. *: I'm sure it's useful for something, just like control-after-generate must be useful for something, but I can't fathom why after-mode is the default. So I'm glad there's a before/after setting, but I wish its existence and lifesaving practical effect were made more obvious. |
|
@AustinMroz I can fix these merge conflicts right now, let me know if you are currently also doing this and I'll avoid pushing. If not, please ignore. |
This is great feedback, what would you say would be most intuitive or obvious to you? |
|
I think "control before generate" would be the more intuitive default. With "control after generate", it's confusing for the seed to change at the end of a gen, since it no longer corresponds with the output on display, and there's no way within ComfyUI to retrieve the old seed for a retry with different prompts/settings, which is a common use case. The one downside I can think of for "control before generate" is that increment/decrement would also change the starting seed for the first gen, which would be unexpected. Maybe it can skip changing the seed when the seed value has just been manually changed by the user. (This exception would also make sense for randomize mode.) |
|
Cleaning a few things up |
src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDropdown.vue
Show resolved
Hide resolved
Without these, randomized values can exceed the maximum safe integer range which results in the increment and decrement buttons being 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: 2
🧹 Nitpick comments (2)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (2)
50-51: Magic number should be a named constant.The value
1125899906842624(2^50) is used to define safe bounds but is not documented. This should be extracted to a named constant with a comment explaining why this specific bound was chosen instead of JavaScript'sNumber.MAX_SAFE_INTEGER(2^53 - 1).Apply this diff to improve maintainability:
+// Safe bounds for numeric controls to prevent precision loss +// Uses 2^50 instead of MAX_SAFE_INTEGER for [reason - e.g., ComfyUI backend limitation] +const SAFE_NUMERIC_BOUND = 1125899906842624 + export function useStepperControl( modelValue: Ref<number>, options: StepperControlOptions, defaultValue?: ControlWidgetOptions ) { const controlMode = ref<NumberControlMode>(convertToEnum(defaultValue)) const controlId = Symbol('numberControl') const globalSeedStore = useGlobalSeedStore() const applyControl = () => { const { min = 0, max = 1000000, step2, step = 1, onChange } = options - const safeMax = Math.min(1125899906842624, max) - const safeMin = Math.max(-1125899906842624, min) + const safeMax = Math.min(SAFE_NUMERIC_BOUND, max) + const safeMin = Math.max(-SAFE_NUMERIC_BOUND, min)
49-49: Consider validating min ≤ max constraint.The code destructures
minandmaxwith defaults but does not validate thatmin ≤ max. If a caller providesmin > max, the behavior could be unexpected (e.g.,safeMin > safeMax, making clamping logic produce incorrect results).Consider adding a defensive check:
const applyControl = () => { const { min = 0, max = 1000000, step2, step = 1, onChange } = options + if (min > max) { + console.warn('useStepperControl: min should not exceed max', { min, max }) + return + } const safeMax = Math.min(SAFE_NUMERIC_BOUND, max) const safeMin = Math.max(-SAFE_NUMERIC_BOUND, min)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
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 (1)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{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/useStepperControl.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.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/useStepperControl.ts
**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.ts
Files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts
🧠 Learnings (1)
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels
Applied to files:
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts
🔇 Additional comments (4)
src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts (4)
1-15: LGTM! Enum and imports are well-structured.The imports follow Vue 3 Composition API conventions, and the enum correctly uses camelCase naming.
17-23: LGTM! Interface is properly typed.The interface follows TypeScript best practices with clear, optional properties.
66-68: Verify: Does RANDOMIZE mode need to respect the step increment?The random value generation in RANDOMIZE mode produces any integer within
[safeMin, safeMax]but does not align tostepincrements. For example, withmin=0, max=100, step=10, this could generate47instead of multiples of 10 like0, 10, 20, ..., 100.Is this intentional, or should random values be step-aligned?
If step-aligned randomization is desired, consider this approach:
case NumberControlMode.RANDOMIZE: - newValue = Math.floor(Math.random() * (safeMax - safeMin + 1)) + safeMin + { + const range = safeMax - safeMin + const steps = Math.floor(range / actualStep) + const randomStep = Math.floor(Math.random() * (steps + 1)) + newValue = safeMin + randomStep * actualStep + } break
84-98: LGTM! Proper lifecycle management and cleanup.The composable correctly registers with the
numberControlRegistryon mount and unregisters on unmount, preventing memory leaks. The return values are appropriate for the composable pattern.
| 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 | ||
| } | ||
| return NumberControlMode.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 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 return RANDOMIZE instead of LINK_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
}🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts
around lines 25 to 37, the convertToEnum function is missing a case for
'linkToGlobal' so that value falls through to RANDOMIZE; add a case clause for
'linkToGlobal' that returns NumberControlMode.LINK_TO_GLOBAL (keeping the
existing default return as RANDOMIZE) so the function maps that string to the
correct enum.
| break | ||
| case NumberControlMode.LINK_TO_GLOBAL: | ||
| // Use global seed value, constrained by min/max | ||
| newValue = Math.max(min, Math.min(safeMax, globalSeedStore.globalSeed)) |
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.
Inconsistent boundary constraint in LINK_TO_GLOBAL mode.
Line 71 uses min for the lower bound while using safeMax for the upper bound. All other control modes consistently use safeMin and safeMax. This inconsistency could allow values outside the safe integer range if min < -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))
break📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| newValue = Math.max(min, Math.min(safeMax, globalSeedStore.globalSeed)) | |
| case NumberControlMode.LINK_TO_GLOBAL: | |
| // Use global seed value, constrained by min/max | |
| newValue = Math.max(safeMin, Math.min(safeMax, globalSeedStore.globalSeed)) | |
| break |
🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts
around line 71, the LINK_TO_GLOBAL branch uses `min` for the lower bound but
should use `safeMin` to match other modes; update the expression `newValue =
Math.max(min, Math.min(safeMax, globalSeedStore.globalSeed))` to use `safeMin`
instead of `min`, ensuring the code consistently constrains values with
`safeMin` and `safeMax`.
I know; that's how I can articulate the concept now. But until recently, I was just suffering through the default mode with no idea it could be changed, until someone else pointed out this setting existed and did something useful. I bet many users follow the same path, or never find out. I think putting a toggle right in the KSampler node would help a lot with discoverability, but since it's a shared setting, maybe it's hard to keep the UI state synced across multiple nodes? Barring that, maybe the simplest improvement would be changing the terminology from "control before/after generate" to "update seed before/after generate", which makes the practical effect much clearer. Similarly, the setting could be relabeled from "widget control mode: before/after" to "update seed fields: before generate / after generate". (Or is the current wording a vague "control" because it also affects things other than seed RNGs?) |

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