Skip to content

Conversation

@jtydhr88
Copy link
Collaborator

@jtydhr88 jtydhr88 commented Jan 2, 2026

Summary

Another implementation for image crop node, alternative for #7014
As discussed with @christian-byrne and @DrJKL we could have single widget - IMAGECROP with 4 ints and UI preview.

However, this solution requires changing the definition of image crop node in BE (sent here), which will break the exsiting workflow, also it would not allow connect separate int node as input, I am not sure it is a good idea.

So I keep two PRs openned for references

Screenshots

2025-12-31.23-16-23.mp4

┆Issue is synchronized with this Notion page by Unito

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jan 2, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

📝 Walkthrough

Walkthrough

Adds image-cropping and bounding-box editing: new Vue components, composables, litegraph widget types and classes, widget registry and scripts, schema and locale keys, a core extension enforcing minimum node sizes, and BaseWidget canvas placeholder support.

Changes

Cohort / File(s) Summary
Vue components
src/components/imagecrop/WidgetImageCrop.vue, src/components/boundingbox/WidgetBoundingBox.vue
New SFCs: WidgetImageCrop (image preview, draggable/resizable crop overlay, v-model CropRegion) and WidgetBoundingBox (numeric x/y/width/height, v-model Bounds).
Composables / widget wiring
src/composables/useImageCrop.ts, src/renderer/extensions/vueNodes/widgets/composables/useBoundingBoxWidget.ts
New composables: useImageCrop manages image URL resolution, load state, display vs natural scale, pointer drag/resize, bounds/min-size enforcement and event handlers; useBoundingBoxWidget constructs main widget plus 4 numeric sub-widgets and syncs values.
Litegraph types & widgets
src/lib/litegraph/src/types/widgets.ts, src/lib/litegraph/src/widgets/*.ts, src/lib/litegraph/src/widgets/widgetMap.ts
New exports/types: BoundingBoxValue, IImageCropWidget, IBoundingBoxWidget, IWidget union extended. New widget classes ImageCropWidget, BoundingBoxWidget; widgetMap updated to map 'imagecrop' and 'boundingbox'.
Canvas placeholder & widget updates
src/lib/litegraph/src/widgets/BaseWidget.ts, src/lib/litegraph/src/widgets/TextareaWidget.ts, src/lib/litegraph/src/widgets/TreeSelectWidget.ts
Added drawVueOnlyWarning to BaseWidget and replaced manual canvas rendering in some widgets with Vue-only warning placeholders; removed some implements clauses.
Registry & scripts
src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts, src/scripts/widgets.ts
Registered async components for WidgetImageCrop and WidgetBoundingBox; added imagecrop and boundingbox entries to core registry; added BOUNDINGBOX to ComfyWidgets.
Core extension
src/extensions/core/imageCrop.ts, src/extensions/core/index.ts
New core extension registering nodeCreated hook for ImageCrop nodes that enforces minimum dimensions (at least 300×450); core index imports the module.
Schema & inputs
src/schemas/nodeDef/nodeDefSchemaV2.ts
Added zBoundingBoxInputSpec (type BOUNDINGBOX) and exported BoundingBoxInputSpec; included in zInputSpec union.
Localization
src/locales/en/main.json
Added imageCrop keys (loading, noInputImage, cropPreviewAlt) and boundingBox keys (x, y, width, height).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant WidgetImageCrop as WidgetImageCrop
    participant Composable as useImageCrop
    participant Store as NodeOutputStore

    User->>WidgetImageCrop: open node panel / interact
    WidgetImageCrop->>Composable: init(nodeId) / bind v-model
    Composable->>Store: resolve image URL for nodeId
    Store-->>Composable: imageUrl
    Composable-->>WidgetImageCrop: imageUrl, isLoading, styles
    User->>WidgetImageCrop: drag/resize crop box or edit inputs
    WidgetImageCrop->>Composable: handleDragStart/Move/End or handleResizeStart/Move/End or handleInputChange
    Composable->>Composable: enforce bounds/scale, update crop values
    Composable-->>WidgetImageCrop: updated crop region (v-model)
Loading

Suggested reviewers

  • shinshin86
  • KarryCharon
  • christian-byrne
  • Yorha4D
✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Jan 2, 2026

🎭 Playwright Tests: ⚠️ Passed with flaky tests

Results: 503 passed, 0 failed, 2 flaky, 8 skipped (Total: 513)

❌ Failed Tests

📊 Browser Reports
  • chromium: View Report (✅ 493 / ❌ 0 / ⚠️ 1 / ⏭️ 8)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 7 / ❌ 0 / ⚠️ 1 / ⏭️ 0)

@github-actions
Copy link

github-actions bot commented Jan 2, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/17/2026, 07:46:59 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 2, 2026

Bundle Size Report

Summary

  • Raw size: 19.9 MB baseline 19.8 MB — 🔴 +28.1 kB
  • Gzip: 4.05 MB baseline 4.04 MB — 🔴 +6.24 kB
  • Brotli: 3.09 MB baseline 3.08 MB — 🔴 +5.67 kB
  • Bundles: 103 current • 100 baseline • 41 added / 38 removed

Category Glance
Other 🔴 +19.4 kB (5.4 MB) · UI Components 🔴 +4.7 kB (208 kB) · App Entry Points 🔴 +4 kB (3.36 MB) · Vendor & Third-Party ⚪ 0 B (9.34 MB) · Graph Workspace ⚪ 0 B (1.14 MB) · Panels & Settings ⚪ 0 B (376 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.36 MB (baseline 3.36 MB) • 🔴 +4 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-ByBo3c6g.js (new) 3.16 MB 🔴 +3.16 MB 🔴 +663 kB 🔴 +503 kB
assets/index-CnPfnqSU.js (removed) 3.16 MB 🟢 -3.16 MB 🟢 -662 kB 🟢 -502 kB
assets/index-C2QEmv0T.js (new) 201 kB 🔴 +201 kB 🔴 +44.1 kB 🔴 +36.4 kB
assets/index-Bi-5wNBS.js (removed) 200 kB 🟢 -200 kB 🟢 -44.1 kB 🟢 -36.4 kB
assets/index-C3ahwfBj.js (removed) 345 B 🟢 -345 B 🟢 -243 B 🟢 -237 B
assets/index-CSqQ201H.js (new) 345 B 🔴 +345 B 🔴 +247 B 🔴 +202 B

Status: 3 added / 3 removed

Graph Workspace — 1.14 MB (baseline 1.14 MB) • ⚪ 0 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-COdpVXxP.js (new) 1.14 MB 🔴 +1.14 MB 🔴 +218 kB 🔴 +164 kB
assets/GraphView-Vr494u2J.js (removed) 1.14 MB 🟢 -1.14 MB 🟢 -218 kB 🟢 -164 kB

Status: 1 added / 1 removed

Views & Navigation — 6.66 kB (baseline 6.66 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BlZCQ6JD.js (removed) 6.66 kB 🟢 -6.66 kB 🟢 -2.16 kB 🟢 -1.92 kB
assets/UserSelectView-BnHN59WA.js (new) 6.66 kB 🔴 +6.66 kB 🔴 +2.16 kB 🔴 +1.91 kB

Status: 1 added / 1 removed

Panels & Settings — 376 kB (baseline 376 kB) • ⚪ 0 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-BUlEYqE8.js (new) 25.2 kB 🔴 +25.2 kB 🔴 +5.75 kB 🔴 +5.02 kB
assets/LegacyCreditsPanel-Cae8lKK9.js (removed) 25.2 kB 🟢 -25.2 kB 🟢 -5.75 kB 🟢 -5.02 kB
assets/KeybindingPanel-Dp6pvClH.js (new) 14.9 kB 🔴 +14.9 kB 🔴 +3.59 kB 🔴 +3.14 kB
assets/KeybindingPanel-gHjnIa2C.js (removed) 14.9 kB 🟢 -14.9 kB 🟢 -3.59 kB 🟢 -3.14 kB
assets/ExtensionPanel-Bmn2r31M.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -2.63 kB 🟢 -2.31 kB
assets/ExtensionPanel-Cm9m2b0N.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +2.63 kB 🔴 +2.31 kB
assets/AboutPanel-DMWyXt2b.js (new) 9.2 kB 🔴 +9.2 kB 🔴 +2.48 kB 🔴 +2.23 kB
assets/AboutPanel-fLHdqPhx.js (removed) 9.2 kB 🟢 -9.2 kB 🟢 -2.48 kB 🟢 -2.23 kB
assets/ServerConfigPanel-DpBDjhus.js (removed) 7.55 kB 🟢 -7.55 kB 🟢 -2.06 kB 🟢 -1.83 kB
assets/ServerConfigPanel-DUwCWoG5.js (new) 7.55 kB 🔴 +7.55 kB 🔴 +2.06 kB 🔴 +1.83 kB
assets/UserPanel-BSQG7vlt.js (new) 6.92 kB 🔴 +6.92 kB 🔴 +1.8 kB 🔴 +1.58 kB
assets/UserPanel-D_fD2I_D.js (removed) 6.92 kB 🟢 -6.92 kB 🟢 -1.8 kB 🟢 -1.58 kB
assets/settings-B53OczP6.js 25.7 kB 25.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BejNis3-.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BNWtakbr.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BsEzQMdC.js 26.5 kB 26.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-ByY8kzq6.js 31.3 kB 31.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CbpLSdP-.js 22.3 kB 22.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Ce8QD7eh.js 27.5 kB 27.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CJPoNRmh.js 28.2 kB 28.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CLwRfrGb.js 35.4 kB 35.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DoSVO6sE.js 22.9 kB 22.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-UbrAIzeo.js 29.1 kB 29.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 208 kB (baseline 203 kB) • 🔴 +4.7 kB

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LazyImage.vue_vue_type_script_setup_true_lang-D7P30Lni.js (removed) 63.9 kB 🟢 -63.9 kB 🟢 -12.8 kB 🟢 -11.2 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-DsfsEZFw.js (new) 63.9 kB 🔴 +63.9 kB 🔴 +12.8 kB 🔴 +11.2 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-DO5i0LqB.js (new) 56.4 kB 🔴 +56.4 kB 🔴 +8.78 kB 🔴 +7.55 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-TpbKHqXK.js (removed) 56.4 kB 🟢 -56.4 kB 🟢 -8.78 kB 🟢 -7.55 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-D4d_om4v.js (new) 48.1 kB 🔴 +48.1 kB 🔴 +10.4 kB 🔴 +9 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-DsosfOYv.js (removed) 48.1 kB 🟢 -48.1 kB 🟢 -10.4 kB 🟢 -9 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-B8JJyQ47.js (removed) 16.4 kB 🟢 -16.4 kB 🟢 -4.11 kB 🟢 -3.64 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-Wf5XRNWX.js (new) 16.4 kB 🔴 +16.4 kB 🔴 +4.12 kB 🔴 +3.64 kB
assets/ComfyQueueButton-BhkQFSpD.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.62 kB 🔴 +2.33 kB
assets/ComfyQueueButton-qqCj2Pcv.js (removed) 8.91 kB 🟢 -8.91 kB 🟢 -2.62 kB 🟢 -2.33 kB
assets/WidgetBoundingBox.vue_vue_type_script_setup_true_lang-CLpxEJIv.js (new) 4.7 kB 🔴 +4.7 kB 🔴 +1.09 kB 🔴 +952 B
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-JkkgRFhL.js (removed) 3.75 kB 🟢 -3.75 kB 🟢 -1.47 kB 🟢 -1.33 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-VxpNygdQ.js (new) 3.75 kB 🔴 +3.75 kB 🔴 +1.47 kB 🔴 +1.33 kB
assets/WidgetButton-C78W8Vwe.js (new) 2.25 kB 🔴 +2.25 kB 🔴 +1.01 kB 🔴 +917 B
assets/WidgetButton-CcWm_0yJ.js (removed) 2.25 kB 🟢 -2.25 kB 🟢 -1.01 kB 🟢 -910 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-oZg1SGKu.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +891 B 🔴 +769 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-W0lx4vZb.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -890 B 🟢 -764 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-DYuISk5v.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 8 removed

Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-CCUeZZvb.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.57 kB
assets/keybindingService-Do2PTkEf.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.57 kB
assets/audioService-7OVKzNXE.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +964 B 🔴 +828 B
assets/audioService-CtNg1GVm.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -961 B 🟢 -824 B
assets/serverConfigStore-UAfw346M.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

Utilities & Hooks — 1.86 kB (baseline 1.86 kB) • ⚪ 0 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-CN12rX0O.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +547 B
assets/audioUtils-TBBuquBt.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -649 B 🟢 -543 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

Vendor & Third-Party — 9.34 MB (baseline 9.34 MB) • ⚪ 0 B

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-B6cS_vC9.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-qQ0OqwZI.js 3.93 MB 3.93 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-C6JjJDK2.js 1.95 MB 1.95 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-reka-ui-DDrshcJ8.js 111 kB 111 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-y3pikHUU.js 2.08 MB 2.08 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-CsIL_yPi.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-8yQe0SeB.js 165 kB 165 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BF8peZ5_.js 420 kB 420 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 5.4 MB (baseline 5.38 MB) • 🔴 +19.4 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscriptionRequiredDialogContent-CbpelBEo.js (new) 29.4 kB 🔴 +29.4 kB 🔴 +6.53 kB 🔴 +5.68 kB
assets/SubscriptionRequiredDialogContent-CP-yXjTL.js (removed) 29.4 kB 🟢 -29.4 kB 🟢 -6.53 kB 🟢 -5.68 kB
assets/WidgetRecordAudio-DKsJ0V9z.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -5.24 kB 🟢 -4.64 kB
assets/WidgetRecordAudio-DuJ2bVTb.js (new) 20.5 kB 🔴 +20.5 kB 🔴 +5.24 kB 🔴 +4.64 kB
assets/WidgetImageCrop-DhKQxbKt.js (new) 19.2 kB 🔴 +19.2 kB 🔴 +4.28 kB 🔴 +3.74 kB
assets/AudioPreviewPlayer-C0--on7v.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.38 kB 🔴 +3.03 kB
assets/AudioPreviewPlayer-CbPWZnyf.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.38 kB 🟢 -3.03 kB
assets/ValueControlPopover-Cnc-fLt8.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -1.72 kB 🟢 -1.53 kB
assets/ValueControlPopover-DshWNoJx.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +1.72 kB 🔴 +1.53 kB
assets/WidgetGalleria-B4vQQQcT.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.46 kB 🔴 +1.32 kB
assets/WidgetGalleria-vEIACrFl.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.46 kB 🟢 -1.32 kB
assets/WidgetColorPicker-Cw5L1Q1X.js (new) 3.44 kB 🔴 +3.44 kB 🔴 +1.4 kB 🔴 +1.25 kB
assets/WidgetColorPicker-DfaSTOXV.js (removed) 3.44 kB 🟢 -3.44 kB 🟢 -1.4 kB 🟢 -1.25 kB
assets/WidgetTextarea-DNsDO-Hq.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.23 kB 🔴 +1.09 kB
assets/WidgetTextarea-kL0X3Vmk.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.23 kB 🟢 -1.11 kB
assets/WidgetMarkdown-D0ZN4e7T.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.3 kB 🟢 -1.14 kB
assets/WidgetMarkdown-D99hNN4y.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/WidgetAudioUI-9M6Rm6_r.js (new) 2.98 kB 🔴 +2.98 kB 🔴 +1.2 kB 🔴 +1.09 kB
assets/WidgetAudioUI-C5NClewj.js (removed) 2.98 kB 🟢 -2.98 kB 🟢 -1.2 kB 🟢 -1.09 kB
assets/WidgetToggleSwitch-7eDCTTgE.js (new) 2.7 kB 🔴 +2.7 kB 🔴 +1.14 kB 🔴 +1.03 kB
assets/WidgetToggleSwitch-DafXWpm-.js (removed) 2.7 kB 🟢 -2.7 kB 🟢 -1.14 kB 🟢 -1.02 kB
assets/WidgetInputText-BaUE7AHB.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -935 B 🟢 -841 B
assets/WidgetInputText-BU-MlREE.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +937 B 🔴 +865 B
assets/Media3DTop-BndqClaj.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -780 B 🟢 -663 B
assets/Media3DTop-DSROZabZ.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +781 B 🔴 +667 B
assets/WidgetSelect-3rRPZ0bW.js (new) 772 B 🔴 +772 B 🔴 +375 B 🔴 +350 B
assets/WidgetSelect-emIErdKI.js (removed) 772 B 🟢 -772 B 🟢 -375 B 🟢 -345 B
assets/WidgetInputNumber--1WKdaoH.js (new) 712 B 🔴 +712 B 🔴 +361 B 🔴 +341 B
assets/WidgetInputNumber-BYQg-k_4.js (removed) 712 B 🟢 -712 B 🟢 -361 B 🟢 -330 B
assets/Load3D-DGxxPWBk.js (removed) 463 B 🟢 -463 B 🟢 -283 B 🟢 -237 B
assets/Load3D-ydpeaFd9.js (new) 463 B 🔴 +463 B 🔴 +279 B 🔴 +239 B
assets/WidgetLegacy-BJzG5gol.js (new) 403 B 🔴 +403 B 🔴 +251 B 🔴 +210 B
assets/WidgetLegacy-BqHKB4Wy.js (removed) 403 B 🟢 -403 B 🟢 -252 B 🟢 -211 B
assets/WidgetBoundingBox-C5xbpn6U.js (new) 229 B 🔴 +229 B 🔴 +196 B 🔴 +172 B
assets/commands-B32ZbeYu.js 16.5 kB 16.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B7wQT83I.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BK3JVjMG.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C6twMpaZ.js 15.5 kB 15.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CDUWpEwM.js 18.3 kB 18.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CfZ6FPZ-.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-COIPP_pv.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CoPn_77e.js 14.7 kB 14.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CuRNS4XD.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DBHjCSPA.js 16.2 kB 16.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DZJaRLKH.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-8BNo1weg.js 110 kB 110 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BPRfdNvb.js 127 kB 127 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CezbcLlR.js 98.1 kB 98.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-COBPG1am.js 134 kB 134 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-COodTO5z.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D2h56mz4.js 97.4 kB 97.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DkeDO8xU.js 110 kB 110 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DNecFfxQ.js 138 kB 138 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DPtAQ-XV.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-R06XJOVT.js 116 kB 116 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-yFuDcHL9.js 154 kB 154 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-BbasJLNf.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-Cg8GhF1C.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-Blp7MPtz.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-B5PoiJVp.js 328 kB 328 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BcF8ipfS.js 337 kB 337 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-C0iimyu4.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-C17UoBYT.js 321 kB 321 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Ci79V0N3.js 318 kB 318 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DaO1V-C-.js 324 kB 324 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DC9n3E5P.js 324 kB 324 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DhNrm-MX.js 400 kB 400 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dqlfw7kR.js 298 kB 298 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DzQz59ZA.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-W17mnyV4.js 295 kB 295 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BicTiHSA.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DUGS9EoD.js 3.21 kB 3.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 18 added / 16 removed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e932bb and 91fc236.

📒 Files selected for processing (12)
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/extensions/core/imageCrop.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
🧰 Additional context used
📓 Path-based instructions (19)
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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
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 occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/locales/en/main.json
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Minimize the surface area (exported values) of each module and composable

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.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/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/components/imagecrop/WidgetImageCrop.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/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/components/imagecrop/WidgetImageCrop.vue
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
src/**/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use barrel files (e.g., /some/package/index.ts) to re-export within /src

Files:

  • src/extensions/core/index.ts
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
🧠 Learnings (20)
📓 Common learnings
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.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,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • src/composables/useImageCrop.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/composables/useImageCrop.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Leverage VueUse functions for performance-enhancing styles in Vue components

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
🧬 Code graph analysis (5)
src/extensions/core/imageCrop.ts (1)
src/services/extensionService.ts (1)
  • useExtensionService (16-215)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (3)
src/scripts/widgets.ts (1)
  • ComfyWidgetConstructorV2 (31-34)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (266-272)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)
  • ImageCropInputSpec (180-180)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (3)
src/lib/litegraph/src/litegraph.ts (1)
  • BaseWidget (147-147)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (266-272)
src/lib/litegraph/src/widgets/BaseWidget.ts (2)
  • DrawWidgetOptions (14-19)
  • WidgetEventOptions (30-34)
src/scripts/widgets.ts (1)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (1)
  • useImageCropWidget (9-26)
src/lib/litegraph/src/widgets/widgetMap.ts (2)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)
  • ImageCropWidget (9-47)
src/lib/litegraph/src/utils/type.ts (1)
  • toClass (13-18)
⏰ 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: test
  • GitHub Check: lint-and-format
  • GitHub Check: setup
  • GitHub Check: collect
🔇 Additional comments (29)
src/locales/en/main.json (1)

1679-1683: LGTM!

The new locale entries follow the established pattern and provide appropriate i18n keys for the image crop widget. Good inclusion of cropPreviewAlt for accessibility.

src/composables/useImageCrop.ts (11)

1-28: LGTM!

Good use of useResizeObserver from VueUse as per coding guidelines. Type imports are properly separated, and the interface is well-defined.


30-66: LGTM!

State management is well-organized with clear separation between image metrics, crop region, drag state, and resize state.


90-108: LGTM!

The image URL retrieval logic correctly uses optional chaining and safely handles missing nodes or URLs by returning null.


110-147: LGTM!

The dimension calculation correctly handles aspect ratio fitting with appropriate guards against division by zero.


149-165: LGTM!

Good handling of CSS transforms by comparing getBoundingClientRect to clientWidth for accurate scale calculation during drag operations.


196-281: LGTM!

Resize handles are well-structured with appropriate cursor classes and positioning. Good use of Math.max(0, ...) to prevent negative dimensions when crop area is small.


283-295: LGTM!

Event handlers correctly manage loading state and error recovery.


297-340: LGTM!

Drag implementation correctly uses pointer capture for reliable tracking and properly clamps coordinates to image bounds.


358-414: LGTM!

The resize logic correctly handles all 8 directions with proper bounds clamping and minimum size enforcement. The left/top edge handling that adjusts both position and size is implemented correctly.


454-477: LGTM!

The composable exposes a clean, well-organized API with all necessary reactive values and event handlers for the component.


174-183: No action needed. The imageUrl is properly constructed using api.apiURL(), which is the recommended approach for backend endpoints per coding guidelines. Image data originates from internal node outputs through URLSearchParams and the trusted API utility, not from user input, making CSS injection a non-issue.

Likely an incorrect or invalid review comment.

src/lib/litegraph/src/widgets/widgetMap.ts (3)

21-21: LGTM!

Import follows alphabetical ordering and existing patterns.


54-54: LGTM!

Type mapping entry follows the established pattern.


125-126: LGTM!

Switch case follows the same pattern as other widget types (e.g., imagecompare, asset).

src/extensions/core/index.ts (1)

13-13: LGTM!

Extension import follows the established alphabetical ordering pattern.

src/schemas/nodeDef/nodeDefSchemaV2.ts (3)

67-72: LGTM!

The schema follows the established pattern (matching zImageCompareInputSpec) and uses valid Zod v3 syntax.


136-136: LGTM!

Union membership correctly placed.


180-180: LGTM!

Type export follows the established pattern.

src/scripts/widgets.ts (2)

17-17: LGTM!

Import follows alphabetical ordering and existing patterns.


299-299: LGTM!

Widget registration follows the established pattern, using transformWidgetConstructorV2ToV1 consistently with other V2 widgets like IMAGECOMPARE.

src/extensions/core/imageCrop.ts (1)

1-11: The extension correctly enforces minimum node dimensions for the ImageCrop widget to ensure adequate space for the crop UI. The pattern matches other core extensions.

The 'ImageCrop' comfyClass name is correct and matches the backend node definition, as confirmed by node definition entries across multiple locales.

src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts (2)

57-59: LGTM!

The async component import follows the established pattern used by other widgets in this registry.


163-171: LGTM!

The widget registration follows the existing convention with appropriate type, alias, and essential: false setting for this specialized widget.

src/lib/litegraph/src/types/widgets.ts (2)

257-272: LGTM!

The CropRegionValue interface and IImageCropWidget type definition follow the established patterns for widget types in this file. The interface properly extends IBaseWidget with appropriate generic parameters.


85-85: LGTM!

The IImageCropWidget is correctly added to the discriminated union type.

src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

44-46: LGTM!

The no-op onClick is appropriate for a Vue-only widget where interactions are handled by the Vue component.

src/components/imagecrop/WidgetImageCrop.vue (2)

81-101: LGTM!

The crop box and resize handle implementation with pointer event handlers is well-structured. The conditional rendering with v-if and v-show is appropriate for the different elements.


52-79: LGTM!

Good use of i18n for user-facing strings in loading, empty, and alt text states. The image element properly prevents default drag behavior and handles load/error events.

@Tekaiguy
Copy link

Tekaiguy commented Jan 2, 2026

If possible, I would request adding a ratio input which accepts values like "1.00" or "16:9" to lock the aspect ratio while resizing/dragging. I do a lot of crop work and it is a huge time saver.

The current best custom implementation of visual crop is here, if you want to take a look. Yours is less visually cluttered though, which I like!

@RandomGuyWithIssues
Copy link

Lets say i detect a face in some image and get bbox location, i want to crop that location from the image, do some sampling and return that crop back to the original image. How can i do it with this node if only input and output are image and no other data?

@jtydhr88 jtydhr88 force-pushed the imageCrop-crop-box-new branch from 91fc236 to bc6c542 Compare January 6, 2026 03:10
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Fix all issues with AI Agents
In @src/components/imagecrop/WidgetImageCrop.vue:
- Around line 81-90: Make the crop box keyboard-accessible: add tabindex="0" and
a focus-visible style to the crop box div (the same element using
:style="cropBoxStyle") and wire an @keydown handler that calls a new
handleKeyboardMove function; implement handleKeyboardMove in the composable that
responds to ArrowUp/Down/Left/Right (preventDefault), computes a small nudge
delta (e.g., 1px or 1 unit, shift for larger step), and updates the same
state/update logic used by handleDragMove/handleDragEnd so keyboard moves behave
identically and remain constrained within image bounds; ensure focus cleanup if
needed and keep naming consistent with
handleDragStart/handleDragMove/handleDragEnd.
- Around line 112-117: The CropRegion type declared in WidgetImageCrop.vue
duplicates the existing CropRegionValue type from
'@/lib/litegraph/src/types/widgets'; instead of a local declaration, import
CropRegionValue and re-export or alias it (e.g., type CropRegion =
CropRegionValue) so there is a single source of truth; update any local
references to use the imported/aliased symbol and remove the duplicate
CropRegion declaration.
- Around line 119-121: Replace the current props object with Vue 3.5 reactive
destructuring: instead of const props = defineProps<{ nodeId: NodeId }>(), call
defineProps with the same type and destructure nodeId directly (i.e., use the
destructured nodeId from defineProps) so the component uses the reactive prop
binding; update all usages that reference props.nodeId to use nodeId.

In @src/composables/useImageCrop.ts:
- Around line 434-444: The deep watchers are monitoring entire stores
(nodeOutputStore.nodeOutputs and nodeOutputStore.nodePreviewImages), causing
unnecessary updates; change the watches to observe only the current node's data
by using a getter that returns nodeOutputStore.nodeOutputs[currentNodeId] and
nodeOutputStore.nodePreviewImages[currentNodeId] (or a computed that derives the
specific output/preview for the active node) and remove deep: true where not
needed, keeping the callback to call updateImageUrl() so only relevant changes
trigger the update.

In @src/extensions/core/imageCrop.ts:
- Around line 1-12: Extract the hard-coded minimum dimensions 300 and 450 into
named constants to improve maintainability: define constants (e.g.,
MIN_IMAGE_CROP_WIDTH and MIN_IMAGE_CROP_HEIGHT) near the top of the module and
replace the literal 300 and 450 in the nodeCreated handler where
node.setSize([Math.max(oldWidth, 300), Math.max(oldHeight, 450)]) is called;
keep the early return and comfyClass check in the async nodeCreated function
unchanged.

In @src/lib/litegraph/src/widgets/ImageCropWidget.ts:
- Around line 15-42: The drawWidget method in ImageCropWidget.ts manually saves
and restores canvas state via destructuring and Object.assign; replace that with
the standard canvas pattern by calling ctx.save() at the start of state changes
in drawWidget (before setting fillStyle, strokeStyle, font, textAlign,
textBaseline) and call ctx.restore() at the end (after drawing the text and
rectangles) to restore all canvas state properties; keep the same drawing logic
and use the existing method name drawWidget and class ImageCropWidget.

In @src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts:
- Around line 9-25: The exported arrow assigned to useImageCropWidget should be
a function declaration per guidelines; change "export const useImageCropWidget =
(): ComfyWidgetConstructorV2 => { ... }" to a named function declaration "export
function useImageCropWidget(): ComfyWidgetConstructorV2 { ... }" and likewise
replace the inner anonymous arrow returned (the (node: LGraphNode, inputSpec:
InputSpecV2) => { ... }) with a named function (e.g., createImageCropWidget) so
both useImageCropWidget and the returned constructor are declared as functions
rather than function expressions; keep the same parameter names, types, body,
and return value IImageCropWidget.

In @src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts:
- Around line 163-171: The registry currently marks 'load3D' as expandable but
not 'imagecrop', causing the WidgetImageCrop component (registered as
'imagecrop' with component WidgetImageCrop) to not get the EXPANDING_TYPES
treatment; update EXPANDING_TYPES to include the 'imagecrop' key so the image
crop widget uses the same expanded/flex layout behavior as Load3D, ensuring the
image preview container can fill available vertical space and provide consistent
interaction area.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91fc236 and bc6c542.

📒 Files selected for processing (12)
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/extensions/core/imageCrop.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
🧰 Additional context used
📓 Path-based instructions (19)
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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
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 occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/locales/en/main.json
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Minimize the surface area (exported values) of each module and composable

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
src/**/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use barrel files (e.g., /some/package/index.ts) to re-export within /src

Files:

  • src/extensions/core/index.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.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/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.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/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
🧠 Learnings (31)
📓 Common learnings
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/extensions/core/imageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.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,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • src/composables/useImageCrop.ts
  • src/scripts/widgets.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 occurred

Applied to files:

  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Use function declarations instead of function expressions when possible

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Keep functions short and functional; minimize nesting and follow the arrow anti-pattern

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-04T21:43:49.363Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/TabParameters.vue:10-0
Timestamp: 2025-12-04T21:43:49.363Z
Learning: Vue 3.5+ supports reactive props destructure in <script setup>. Destructuring props directly (e.g., `const { nodes } = defineProps<{ nodes: LGraphNode[] }>()`) maintains reactivity through compiler transformation. This is the recommended modern approach and does not require using `props.x` or `toRef`/`toRefs`.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use `withDefaults` or runtime props declaration

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.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 ref/reactive for state management in Vue 3 Composition API

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Prefer `useModel` over separately defining prop and emit for two-way binding

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
  • src/scripts/widgets.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Implement proper props and emits definitions

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Leverage VueUse functions for performance-enhancing styles in Vue components

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/scripts/widgets.ts
🧬 Code graph analysis (7)
src/composables/useImageCrop.ts (2)
src/lib/litegraph/src/types/widgets.ts (1)
  • CropRegionValue (258-263)
src/scripts/app.ts (1)
  • app (1772-1772)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (3)
src/scripts/widgets.ts (1)
  • ComfyWidgetConstructorV2 (31-34)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (266-272)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)
  • ImageCropInputSpec (180-180)
src/lib/litegraph/src/widgets/widgetMap.ts (2)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)
  • ImageCropWidget (9-47)
src/lib/litegraph/src/utils/type.ts (1)
  • toClass (13-18)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (2)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (266-272)
src/lib/litegraph/src/widgets/BaseWidget.ts (2)
  • DrawWidgetOptions (14-19)
  • WidgetEventOptions (30-34)
src/extensions/core/imageCrop.ts (1)
src/services/extensionService.ts (1)
  • useExtensionService (16-215)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)
src/schemas/nodeDefSchema.ts (1)
  • zBaseInputOptions (22-37)
src/scripts/widgets.ts (1)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (1)
  • useImageCropWidget (9-26)
⏰ 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: playwright-tests-chromium-sharded (3, 8)
  • GitHub Check: playwright-tests-chromium-sharded (5, 8)
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (13)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)

67-72: LGTM! Schema definition follows existing patterns.

The new zImageCropInputSpec schema is consistent with similar input specs (IMAGE, IMAGECOMPARE) and properly integrated into the type system. The use of z.record(z.unknown()).optional() for options is Zod v3 compatible and matches the pattern used by other image-related widgets.

Also applies to: 136-136, 180-180

src/extensions/core/index.ts (1)

13-13: LGTM! Import properly added.

The imageCrop extension import is correctly placed in alphabetical order and follows the established pattern for core extensions.

src/lib/litegraph/src/widgets/widgetMap.ts (1)

21-21: LGTM! Widget integration follows established patterns.

The ImageCropWidget is properly integrated into the widget type system:

  • Import is correctly ordered
  • Added to WidgetTypeMap with proper typing
  • Switch case uses the toClass helper consistently with other widgets

Also applies to: 54-54, 125-126

src/scripts/widgets.ts (1)

17-17: LGTM! Widget constructor properly registered.

The IMAGECROP widget is correctly integrated into ComfyWidgets:

  • Import follows alphabetical ordering convention
  • Uses the V2-to-V1 transformation wrapper consistently with other modern widgets
  • Placement in ComfyWidgets object maintains logical grouping with other image-related widgets

Also applies to: 299-299

src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts (1)

57-59: LGTM!

The async component registration follows the established pattern used by other widgets in this file.

src/locales/en/main.json (1)

1688-1692: LGTM!

The locale entries are well-structured and follow the established patterns. All keys are consumed by the WidgetImageCrop.vue component.

src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

44-46: LGTM!

The no-op onClick with clear documentation that this is a Vue-only widget is appropriate.

src/lib/litegraph/src/types/widgets.ts (2)

85-85: LGTM!

The IImageCropWidget is correctly added to the discriminated union, maintaining consistency with other widget types.


257-272: LGTM!

The CropRegionValue interface and IImageCropWidget are well-defined and follow the established patterns for widget types in this file.

src/composables/useImageCrop.ts (3)

30-31: LGTM!

The composable is well-structured with clear separation of concerns. The onMounted initialization and reactive state management follow Vue 3 Composition API best practices.

Also applies to: 452-452


73-79: Consider using nullish coalescing for explicit intent.

Using || for defaults treats 0 as falsy. While 0 is an invalid crop size (below MIN_CROP_SIZE), using ?? would make the intent clearer that you're defaulting only for undefined/null.

🔎 Proposed change
 const syncFromModelValue = () => {
   const value = modelValue.value
   cropX.value = value.x
   cropY.value = value.y
-  cropWidth.value = value.width || 512
-  cropHeight.value = value.height || 512
+  cropWidth.value = value.width ?? 512
+  cropHeight.value = value.height ?? 512
 }

Likely an incorrect or invalid review comment.


149-165: Potential division by zero not fully guarded.

getEffectiveScale checks for zero values but the check at line 159 happens after rect.width is already used in a potential division context. The guard is correct, but consider returning early before accessing rect.width if clientWidth is falsy.

🔎 Proposed fix
  const getEffectiveScale = (): number => {
    const container = containerEl.value

    if (!container || naturalWidth.value <= 0 || displayedWidth.value <= 0) {
      return 1
    }

    const rect = container.getBoundingClientRect()
    const clientWidth = container.clientWidth

-   if (!clientWidth || !rect.width) return 1
+   if (!clientWidth || rect.width <= 0) return 1

    const renderedDisplayedWidth =
      (displayedWidth.value / clientWidth) * rect.width

    return renderedDisplayedWidth / naturalWidth.value
  }

Likely an incorrect or invalid review comment.

src/components/imagecrop/WidgetImageCrop.vue (1)

11-48: Improve accessibility by associating labels with inputs.

The <label> elements are not programmatically associated with their corresponding inputs. Use id and for attributes for proper accessibility.

🔎 Proposed fix
       <div class="flex flex-1 items-center gap-1">
-        <label class="w-6 text-xs text-muted">X</label>
+        <label for="crop-x" class="w-6 text-xs text-muted">X</label>
         <input
+          id="crop-x"
           v-model.number="cropX"
           type="number"
           :min="0"
           class="h-6 w-full rounded border border-border bg-input px-1 text-xs"
           @change="handleInputChange"
         />
       </div>
       <div class="flex flex-1 items-center gap-1">
-        <label class="w-6 text-xs text-muted">Y</label>
+        <label for="crop-y" class="w-6 text-xs text-muted">Y</label>
         <input
+          id="crop-y"
           v-model.number="cropY"
           ...
       </div>
       <div class="flex flex-1 items-center gap-1">
-        <label class="w-6 text-xs text-muted">W</label>
+        <label for="crop-width" class="w-6 text-xs text-muted">W</label>
         <input
+          id="crop-width"
           v-model.number="cropWidth"
           ...
       </div>
       <div class="flex flex-1 items-center gap-1">
-        <label class="w-6 text-xs text-muted">H</label>
+        <label for="crop-height" class="w-6 text-xs text-muted">H</label>
         <input
+          id="crop-height"
           v-model.number="cropHeight"
           ...

Note: If multiple instances of this widget can exist on the same page, consider using unique IDs by incorporating nodeId.

⛔ Skipped due to learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:54.349Z
Learning: For Vue components in Comfy-Org/ComfyUI_frontend: Do not suggest adding aria-label to buttons that already have visible text content (e.g., <span> elements with labels). The visible text automatically provides the accessible name. aria-label should only be used for elements without visible labels, such as icon-only buttons.
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.

@jtydhr88 jtydhr88 force-pushed the imageCrop-crop-box-new branch from bc6c542 to 567a771 Compare January 6, 2026 03:20
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Fix all issues with AI Agents
In @src/components/imagecrop/WidgetImageCrop.vue:
- Line 96: The Vue template uses array class binding :class="['absolute',
handle.class]" in WidgetImageCrop.vue; replace this with the cn() utility from
@/utils/tailwindUtil: import cn in the script (or setup) and change the binding
to use :class="cn('absolute', handle.class)" so Tailwind class merging follows
project conventions; ensure the import name is cn and update any other similar
bindings in this component to use cn() for consistency.
- Around line 113-115: The current use of defineProps should be converted to Vue
3.5 reactive props destructuring: keep the typed defineProps call (defineProps<{
nodeId: NodeId }>()), then call toRefs on the returned props and extract nodeId
(e.g., const { nodeId } = toRefs(props)), and update usages to read nodeId.value
where needed; change references to the original props object to use the
destructured ref instead.

In @src/composables/useImageCrop.ts:
- Around line 73-79: In syncFromModelValue replace the `||` fallbacks with
nullish coalescing so defaults only apply for null/undefined: read
modelValue.value and set cropWidth.value = value.width ?? 512 and
cropHeight.value = value.height ?? 512 (leave cropX/cropY assignment as-is);
this clarifies intent that 0 is a deliberate value rather than treated as
missing.
- Around line 434-444: The current deep watchers on nodeOutputStore.nodeOutputs
and nodeOutputStore.nodePreviewImages fire on any mutation across all nodes;
change them to watch only the specific node's outputs/preview for the node being
edited (e.g., replace watch(() => nodeOutputStore.nodeOutputs, ...) with
watch(() => nodeOutputStore.nodeOutputs[nodeId] or a store getter like
nodeOutputStore.getNodeOutputs(nodeId), and similarly for nodePreviewImages) and
remove deep: true unless nested mutation detection for that specific node is
required; call updateImageUrl() in those targeted watchers so unrelated node
changes no longer trigger updates.

In @src/extensions/core/imageCrop.ts:
- Around line 9-10: Extract the magic numbers used when constraining image crop
sizes into named constants to improve readability and maintainability: define
MIN_IMAGECROP_WIDTH and MIN_IMAGECROP_HEIGHT near the top of the module and
replace the literal 300 and 450 in the node.setSize call (the code that reads
node.size and calls node.setSize([Math.max(oldWidth, 300), Math.max(oldHeight,
450)]) ) with those constants so the intent is clear and easy to update.

In @src/lib/litegraph/src/types/widgets.ts:
- Around line 257-263: CropRegionValue currently only enforces numeric types at
compile time; add runtime validation in the ImageCrop widget implementation to
ensure CropRegionValue.width and CropRegionValue.height are positive (>0) and
x/y are within expected bounds (e.g., non-negative and not outside image
dimensions if available). Validate inputs when constructing/updating the crop
region (e.g., in the widget's setValue/updateValue/handleChange functions) and
reject or normalize invalid values (throw error or clamp to valid range and log)
so downstream code always receives a valid CropRegionValue.

In @src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts:
- Line 22: The code uses a type assertion "as IImageCropWidget" on the result of
node.addWidget; remove the assertion by fixing typings so the call infers the
correct widget type — either call node.addWidget with a generic parameter (e.g.,
node.addWidget<IImageCropWidget>(...)) or update the widget API signature so
addWidget's return type is strongly typed for this widget. If changing the API
isn't possible now, keep the assertion but add a brief comment above it
explaining why the assertion is unavoidable and reference the related typing
limitation (IImageCropWidget / node.addWidget) so future refactors can remove
it.

In @src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts:
- Around line 163-171: The EXPANDING_TYPES set should include the 'imagecrop'
widget so it can expand vertically like 'load3D'; update the EXPANDING_TYPES
declaration (referencing the EXPANDING_TYPES symbol in widgetRegistry.ts) to add
the 'imagecrop' key (matching the widget id used in the registry entry for
component WidgetImageCrop) so the image crop preview can use available vertical
space and improve UX.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc6c542 and 567a771.

📒 Files selected for processing (12)
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/extensions/core/imageCrop.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/scripts/widgets.ts
🧰 Additional context used
📓 Path-based instructions (19)
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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.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/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
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 occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use TypeScript exclusively; do not write new JavaScript code
Use sorted and grouped imports organized by plugin/source
Enforce ESLint rules including Vue + TypeScript rules, disallow floating promises, disallow unused imports, and restrict i18n raw text in templates
Do not use any type or as any type assertions; fix the underlying type issue instead
Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Use function declarations instead of function expressions when possible
Use es-toolkit for utility functions
Implement proper error handling in code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
**/*.{ts,tsx,vue,js,jsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, and 80-character line width

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/locales/en/main.json
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Minimize the surface area (exported values) of each module and composable

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
src/**/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not use barrel files (e.g., /some/package/index.ts) to re-export within /src

Files:

  • src/extensions/core/index.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.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/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.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/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3.5+ with TypeScript in .vue files, exclusively using Composition API with <script setup lang="ts"> syntax
Use Tailwind 4 for styling in Vue components; avoid <style> blocks
Name Vue components using PascalCase (e.g., MenuHamburger.vue)
Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer computed() over ref with watch when deriving values
Prefer useModel over separately defining prop and emit for two-way binding
Use vue-i18n in composition API for string literals; place new translation entries in src/locales/en/main.json
Use cn() utility function from @/utils/tailwindUtil for merging Tailwind class names; do not use :class="[]" syntax
Do not use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Do not use !important or the ! important prefix for Tailwind classes; find and correct interfering !important classes instead
Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead
Leverage VueUse functions for performance-enhancing styles in Vue components
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
🧠 Learnings (27)
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/scripts/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/schemas/nodeDef/nodeDefSchemaV2.ts
  • src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners

Applied to files:

  • src/composables/useImageCrop.ts
  • src/scripts/widgets.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 occurred

Applied to files:

  • src/composables/useImageCrop.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied to files:

  • src/composables/useImageCrop.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Use function declarations instead of function expressions when possible

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Keep functions short and functional; minimize nesting and follow the arrow anti-pattern

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Leverage VueUse functions for performance-enhancing styles in Vue components

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Leverage VueUse functions for performance-enhancing styles

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components; use VueUse, shadcn/vue, or Reka UI instead

Applied to files:

  • src/scripts/widgets.ts
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-11T03:55:57.926Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-04T21:43:49.363Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/TabParameters.vue:10-0
Timestamp: 2025-12-04T21:43:49.363Z
Learning: Vue 3.5+ supports reactive props destructure in <script setup>. Destructuring props directly (e.g., `const { nodes } = defineProps<{ nodes: LGraphNode[] }>()`) maintains reactivity through compiler transformation. This is the recommended modern approach and does not require using `props.x` or `toRef`/`toRefs`.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T06:04:12.562Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.vue : Use Vue 3.5 TypeScript-style default prop declaration with reactive props destructuring; do not use `withDefaults` or runtime props declaration

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
🧬 Code graph analysis (5)
src/lib/litegraph/src/widgets/widgetMap.ts (2)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)
  • ImageCropWidget (9-47)
src/lib/litegraph/src/utils/type.ts (1)
  • toClass (13-18)
src/composables/useImageCrop.ts (2)
src/lib/litegraph/src/types/widgets.ts (1)
  • CropRegionValue (258-263)
src/scripts/app.ts (1)
  • app (1772-1772)
src/scripts/widgets.ts (1)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (1)
  • useImageCropWidget (9-26)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (2)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (266-272)
src/lib/litegraph/src/widgets/BaseWidget.ts (2)
  • DrawWidgetOptions (14-19)
  • WidgetEventOptions (30-34)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)
src/schemas/nodeDefSchema.ts (1)
  • zBaseInputOptions (22-37)
⏰ 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). (3)
  • GitHub Check: test
  • GitHub Check: collect
  • GitHub Check: lint-and-format
🔇 Additional comments (10)
src/extensions/core/index.ts (1)

13-13: LGTM! Extension import is correct.

The imageCrop extension is properly imported and follows the established pattern of side-effect imports for core extensions.

src/locales/en/main.json (1)

1688-1692: LGTM! Locale additions are appropriate.

The new imageCrop locale object provides clear, user-facing text for the image cropping widget UI. The strings cover loading state, missing input, and accessibility text for the crop preview.

src/lib/litegraph/src/widgets/widgetMap.ts (1)

21-21: LGTM! ImageCropWidget integration is consistent.

The ImageCropWidget is properly integrated into the widget mapping system:

  • Import follows alphabetical ordering
  • Type mapping added to WidgetTypeMap
  • Switch case uses the established toClass pattern

The implementation is clean and follows existing conventions.

Also applies to: 54-54, 125-126

src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts (1)

57-59: LGTM! Widget registration is correctly implemented.

The WidgetImageCrop component is properly registered:

  • Async component definition follows the established pattern
  • Widget definition includes appropriate component, aliases, and essential flag
  • Structure is consistent with other widget registrations like Load3D

Also applies to: 163-171

src/scripts/widgets.ts (1)

17-17: LGTM!

The import and widget registration follow the established pattern used by other widgets in the system. The integration is consistent and correct.

Also applies to: 299-299

src/lib/litegraph/src/types/widgets.ts (1)

257-272: LGTM! Type definitions are well-structured.

The CropRegionValue interface and IImageCropWidget type follow the established widget pattern and are properly integrated into the type system.

src/schemas/nodeDef/nodeDefSchemaV2.ts (1)

67-72: LGTM! Schema definition follows established patterns.

The zImageCropInputSpec schema is consistent with other input specifications (like zImageCompareInputSpec) and properly integrated into the union type and exports.

Also applies to: 136-136, 180-180

src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (1)

9-26: Consider using function declarations per coding guidelines.

Per repository guidelines, prefer pure function declarations over function expressions for better hoisting clarity and consistency.

🔎 Proposed refactor
-export const useImageCropWidget = (): ComfyWidgetConstructorV2 => {
-  return (node: LGraphNode, inputSpec: InputSpecV2): IImageCropWidget => {
+export function useImageCropWidget(): ComfyWidgetConstructorV2 {
+  return function (node: LGraphNode, inputSpec: InputSpecV2): IImageCropWidget {
     const { name, options = {} } = inputSpec as ImageCropInputSpec

     const widget = node.addWidget(
       'imagecrop',
       name,
       { x: 0, y: 0, width: 512, height: 512 },
       () => {},
       {
         serialize: true,
         ...options
       }
     ) as IImageCropWidget

     return widget
   }
 }

Based on learnings: Prefer pure function declarations over function expressions.

⛔ Skipped due to learnings
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:17.361Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, prefer pure function declarations (e.g., `function foo() { ... }`) over function expressions (e.g., `const foo = () => { ... }`) for pure functions, as function declarations are considered more functional than function expressions.
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Use function declarations instead of function expressions when possible
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Keep functions short and functional; minimize nesting and follow the arrow anti-pattern
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/*.{vue,ts,tsx} : Follow Vue 3 composition API style guide
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T06:04:12.562Z
Learning: Applies to **/*.{ts,tsx,vue} : Write code that is expressive and self-documenting; avoid redundant comments and clean as you go
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

15-42: Prefer ctx.save()/ctx.restore() for canvas state management.

The manual destructuring and Object.assign approach works but is verbose. The standard canvas idiom is more concise and handles all state properties automatically.

🔎 Proposed refactor
  drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {
    const { width } = options
    const { y, height } = this

-   const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx
+   ctx.save()

    ctx.fillStyle = this.background_color
    ctx.fillRect(15, y, width - 30, height)

    ctx.strokeStyle = this.outline_color
    ctx.strokeRect(15, y, width - 30, height)

    ctx.fillStyle = this.text_color
    ctx.font = '11px monospace'
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'

    const text = 'ImageCrop: Vue-only'
    ctx.fillText(text, width / 2, y + height / 2)

-   Object.assign(ctx, {
-     fillStyle,
-     strokeStyle,
-     textAlign,
-     textBaseline,
-     font
-   })
+   ctx.restore()
  }

Likely an incorrect or invalid review comment.

src/components/imagecrop/WidgetImageCrop.vue (1)

81-90: Consider adding keyboard accessibility for the crop box.

The crop box only responds to pointer events. Consider adding keyboard support (arrow keys for fine-grained movement) for accessibility, especially since users may want precise control.

🔎 Proposed enhancement

Add tabindex="0" to make the crop box focusable, then handle @keydown events for arrow key navigation:

<div
  v-if="imageUrl && !isLoading"
  tabindex="0"
  class="absolute box-border cursor-move overflow-hidden border-2 border-white focus:ring-2 focus:ring-primary"
  :style="cropBoxStyle"
  @pointerdown="handleDragStart"
  @pointermove="handleDragMove"
  @pointerup="handleDragEnd"
  @keydown="handleKeyboardMove"
>

This would require adding a handleKeyboardMove function in the composable to support arrow key movement.

⛔ Skipped due to learnings
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@src/composables/useImageCrop.ts`:
- Around line 429-435: The initialize function currently sets node.value from
app.rootGraph?.getNodeById(nodeId) and falls back to null if the graph isn't
ready; add a retry/watcher so initialization re-runs when app.rootGraph becomes
available: keep the current initialize function but register a watcher (e.g.,
watch or watchEffect on app.rootGraph) that calls initialize when app.rootGraph
transitions from undefined/null to a value, and ensure you cleanup the watcher
when the composable is destroyed; reference initialize, nodeId, node.value,
app.rootGraph, and updateImageUrl so the watcher re-attempts node lookup and
then calls updateImageUrl once the graph is ready.
- Around line 303-307: The pointer helpers capturePointer and releasePointer
currently cast e.target to HTMLElement which can be null; update both to guard
that e.target is non-null and is an HTMLElement (or use e.currentTarget typed as
HTMLElement) before calling setPointerCapture/releasePointerCapture, and skip or
no-op if the target check fails to avoid runtime errors.
♻️ Duplicate comments (11)
src/extensions/core/imageCrop.ts (1)

1-12: LGTM!

The extension registration follows the established pattern correctly:

  • Uses early return for non-ImageCrop nodes
  • Preserves existing dimensions if larger than minimum using Math.max
  • Clean and concise implementation

Note: Previous reviews suggested extracting the magic numbers (300, 450) to named constants for maintainability - this remains a valid optional improvement.

src/lib/litegraph/src/types/widgets.ts (1)

259-274: LGTM!

The type definitions are well-structured and follow established patterns:

  • CropRegionValue clearly defines the crop rectangle shape with x, y, width, height
  • IImageCropWidget extends IBaseWidget correctly with the appropriate type discriminator

Note: A previous review suggested adding runtime validation for logical constraints (positive dimensions, valid coordinates) in the widget implementation. This remains valid but is an implementation concern rather than a type definition issue.

src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

17-44: Prefer ctx.save()/ctx.restore() for canvas state management.

The manual destructuring and Object.assign approach is verbose and error-prone if additional context properties are modified. The standard canvas idiom handles all state automatically.

Proposed refactor
  drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {
    const { width } = options
    const { y, height } = this

-   const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx
+   ctx.save()

    ctx.fillStyle = this.background_color
    ctx.fillRect(15, y, width - 30, height)

    ctx.strokeStyle = this.outline_color
    ctx.strokeRect(15, y, width - 30, height)

    ctx.fillStyle = this.text_color
    ctx.font = '11px monospace'
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'

    const text = `ImageCrop: ${t('widgets.node2only')}`
    ctx.fillText(text, width / 2, y + height / 2)

-   Object.assign(ctx, {
-     fillStyle,
-     strokeStyle,
-     textAlign,
-     textBaseline,
-     font
-   })
+   ctx.restore()
  }
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (2)

9-25: Prefer function declarations per coding guidelines.

Per repository guidelines, prefer pure function declarations over function expressions for better hoisting clarity and consistency.

Proposed refactor
-export const useImageCropWidget = (): ComfyWidgetConstructorV2 => {
-  return (node: LGraphNode, inputSpec: InputSpecV2): IImageCropWidget => {
+export function useImageCropWidget(): ComfyWidgetConstructorV2 {
+  return function (node: LGraphNode, inputSpec: InputSpecV2): IImageCropWidget {
     const { name, options = {} } = inputSpec as ImageCropInputSpec

     const widget = node.addWidget(
       'imagecrop',
       name,
       { x: 0, y: 0, width: 512, height: 512 },
       () => {},
       {
         serialize: true,
         ...options
       }
     ) as IImageCropWidget

     return widget
   }
 }

Based on learnings: Prefer pure function declarations over function expressions.


16-16: Add bounds validation when image loads to constrain the default crop region.

The default { x: 0, y: 0, width: 512, height: 512 } is applied without validation. If the input image is smaller than 512×512, the crop region will extend beyond image bounds until user interaction triggers the boundary enforcement in handleResizeMove/handleDragMove.

Consider clamping the initial crop to image dimensions in handleImageLoad or when the image URL updates.

src/composables/useImageCrop.ts (2)

61-73: Use nullish coalescing for explicit intent.

Using || for defaults treats 0 as falsy. While 0 is below MIN_CROP_SIZE, using ?? clarifies intent that you're defaulting only for undefined/null.

Proposed change
  const cropWidth = computed({
-   get: () => modelValue.value.width || 512,
+   get: () => modelValue.value.width ?? 512,
    set: (v: number) => {
      modelValue.value.width = v
    }
  })

  const cropHeight = computed({
-   get: () => modelValue.value.height || 512,
+   get: () => modelValue.value.height ?? 512,
    set: (v: number) => {
      modelValue.value.height = v
    }
  })

437-447: Consider optimizing deep watchers on store state.

Deep watching nodeOutputStore.nodeOutputs and nodePreviewImages triggers on any change to these objects, even for unrelated nodes. Consider watching only the specific node's outputs if performance becomes an issue with many nodes.

Proposed optimization
+ const nodeOutputs = computed(() => {
+   if (!node.value) return null
+   return nodeOutputStore.nodeOutputs[node.value.id]
+ })
+
+ const nodePreviewImages = computed(() => {
+   if (!node.value) return null
+   const inputNode = node.value.getInputNode(0)
+   return inputNode ? nodeOutputStore.nodePreviewImages[inputNode.id] : null
+ })

  watch(
-   () => nodeOutputStore.nodeOutputs,
+   nodeOutputs,
    () => updateImageUrl(),
-   { deep: true }
  )

  watch(
-   () => nodeOutputStore.nodePreviewImages,
+   nodePreviewImages,
    () => updateImageUrl(),
-   { deep: true }
  )
src/components/imagecrop/WidgetImageCrop.vue (4)

62-97: Improve accessibility by associating labels with inputs.

The <label> elements are not programmatically associated with their corresponding inputs. Use id and for attributes for proper accessibility and screen reader support.

Proposed fix (apply pattern to all inputs)
-     <label class="content-center text-xs text-node-component-slot-text">
+     <label for="crop-x" class="content-center text-xs text-node-component-slot-text">
        {{ $t('imageCrop.x') }}
      </label>
      <input
+       id="crop-x"
        v-model.number="cropX"
        type="number"
        :min="0"
        class="h-7 rounded-lg border-none bg-component-node-widget-background px-2 text-xs text-component-node-foreground focus:outline-0"
      />

Apply the same pattern for Y (id="crop-y"), W (id="crop-width"), and H (id="crop-height").


52-52: Use cn() utility instead of array class syntax.

Per coding guidelines, use the cn() utility function from @/utils/tailwindUtil for merging Tailwind class names.

Proposed refactor

Import the utility:

import { cn } from '@/utils/tailwindUtil'

Update the binding:

-        :class="['absolute', handle.class]"
+        :class="cn('absolute', handle.class)"

109-111: Use Vue 3.5 style props destructuring.

Per coding guidelines, prefer reactive props destructuring.

Proposed refactor
-const props = defineProps<{
+const { nodeId } = defineProps<{
   nodeId: NodeId
 }>()

 ...

-} = useImageCrop(props.nodeId, { imageEl, containerEl, modelValue })
+} = useImageCrop(nodeId, { imageEl, containerEl, modelValue })

37-46: Consider adding keyboard accessibility for the crop box.

The crop box only responds to pointer events. Consider adding keyboard support (arrow keys for fine-grained movement) for accessibility.

Add tabindex="0" to make the crop box focusable and handle @keydown for arrow key navigation.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 567a771 and d489481.

📒 Files selected for processing (9)
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/extensions/core/imageCrop.ts
  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/locales/en/main.json
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
🧰 Additional context used
📓 Path-based instructions (17)
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/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety

src/**/*.ts: Derive component types using vue-component-type-helpers (ComponentProps, ComponentSlots) instead of separate type files
Use es-toolkit for utility functions
Minimize the surface area (exported values) of each module and composable
Favor pure functions, especially testable ones

Files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase

src/**/*.{ts,tsx,vue}: Use separate import type statements instead of inline type in mixed imports
Apply Prettier formatting with 2-space indentation, single quotes, no trailing semicolons, 80-character width
Sort and group imports by plugin, run pnpm format before committing
Never use any type - use proper TypeScript types
Never use as any type assertions - fix the underlying type issue
Write code that is expressive and self-documenting - avoid unnecessary comments
Do not add or retain redundant comments - clean as you go
Avoid mutable state - prefer immutability and assignment at point of declaration
Watch out for Code Smells and refactor to avoid them

Files:

  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,vue}: Use ref for reactive state, computed() for derived values, and watch/watchEffect for side effects in Composition API
Avoid using ref with watch if a computed would suffice - minimize refs and derived state
Use provide/inject for dependency injection only when simpler alternatives (Store or shared composable) won't work
Leverage VueUse functions for performance-enhancing composables
Use VueUse function for useI18n in composition API for string literals

Files:

  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

Don't use barrel files (e.g., /some/package/index.ts) to re-export within /src

Files:

  • src/extensions/core/index.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Keep functions short and functional
Minimize nesting (if statements, for loops, etc.)
Use function declarations instead of function expressions when possible

Files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
src/**/*.vue

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventions

src/**/*.vue: Use Vue 3 Single File Components (SFCs) with Composition API only
Use <script setup lang="ts"> for component logic in Vue SFCs
Avoid <style> blocks in Vue components - use Tailwind 4 styling instead
Use vue-i18n for all string literals in Vue components - place translation entries in src/locales/en/main.json
Use Tailwind utility classes instead of dark: variant - use semantic values from style.css theme (e.g., bg-node-component-surface)
Use cn() utility from @/utils/tailwindUtil for merging Tailwind class names instead of :class="[]" or hardcoding
Never use !important or ! Tailwind prefix - fix interfering classes instead
Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., w-4/5 instead of w-[80%])
Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid withDefaults or runtime props
Prefer defineModel over separately defining a prop and emit for v-model bindings
Define slots via template usage, not via defineSlots
Use same-name shorthand for slot prop bindings (e.g., :isExpanded instead of :is-expanded="isExpanded")
Do not import Vue macros unnecessarily
Avoid new usage of PrimeVue components
Use Tailwind's plurals system via i18n instead of hardcoding ...

Files:

  • src/components/imagecrop/WidgetImageCrop.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/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.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/components/imagecrop/WidgetImageCrop.vue
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
src/components/**/*.vue

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.vue: Use setup() function in Vue 3 Composition API
Destructure props using Vue 3.5 style in Vue components
Use ref/reactive for state management in Vue 3 Composition API
Implement computed() for derived state in Vue 3 Composition API
Use provide/inject for dependency injection in Vue components
Prefer emit/@event-name for state changes over other communication patterns
Use defineExpose only for imperative operations (such as form.validate(), modal.open())
Replace PrimeVue Dropdown component with Select
Replace PrimeVue OverlayPanel component with Popover
Replace PrimeVue Calendar component with DatePicker
Replace PrimeVue InputSwitch component with ToggleSwitch
Replace PrimeVue Sidebar component with Drawer
Replace PrimeVue Chips component with AutoComplete with multiple enabled
Replace PrimeVue TabMenu component with Tabs without panels
Replace PrimeVue Steps component with Stepper without panels
Replace PrimeVue InlineMessage component with Message
Extract complex conditionals to computed properties
Implement cleanup for async operations in Vue components
Use lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API
Use Teleport/Suspense when needed for component rendering
Define proper props and emits definitions in Vue components

Name Vue components in PascalCase (e.g., MenuHamburger.vue)

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,css}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,css}: Use Tailwind CSS only for styling (no custom CSS)
Use the correct tokens from style.css in the design system package

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/components/**/*.{vue,ts,js}

📄 CodeRabbit inference engine (src/components/CLAUDE.md)

src/components/**/*.{vue,ts,js}: Use existing VueUse composables (such as useElementHover) instead of manually managing event listeners
Use useIntersectionObserver for visibility detection instead of custom scroll handlers
Use vue-i18n for ALL UI strings

Files:

  • src/components/imagecrop/WidgetImageCrop.vue
src/lib/litegraph/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using the pnpm lint:fix command
Take advantage of TypedArray subarray when appropriate
The size and pos properties of Rectangle share the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single line if syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
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 occurred
When writing methods, prefer returning idiomatic JavaScript undefined over null

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
src/lib/litegraph/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)

Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Files:

  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
src/**/{services,composables}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/{services,composables}/**/*.{ts,tsx}: Use api.apiURL() for backend endpoints instead of constructing URLs directly
Use api.fileURL() for static file access instead of constructing URLs directly

Files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
src/composables/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables as useXyz.ts (e.g., useForm.ts)

Files:

  • src/composables/useImageCrop.ts
🧠 Learnings (47)
📚 Learning: 2025-12-09T03:39:54.501Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7169
File: src/platform/remote/comfyui/jobs/jobTypes.ts:1-107
Timestamp: 2025-12-09T03:39:54.501Z
Learning: In the ComfyUI_frontend project, Zod is on v3.x. Do not suggest Zod v4 standalone validators (z.uuid, z.ulid, z.cuid2, z.nanoid) until an upgrade to Zod 4 is performed. When reviewing TypeScript files (e.g., src/platform/remote/comfyui/jobs/jobTypes.ts) validate against Zod 3 capabilities and avoid introducing v4-specific features; flag any proposal to upgrade or incorporate v4-only validators and propose staying with compatible 3.x patterns.

Applied to files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-13T11:03:11.264Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7416
File: src/stores/imagePreviewStore.ts:5-7
Timestamp: 2025-12-13T11:03:11.264Z
Learning: In the ComfyUI_frontend repository, lint rules require keeping 'import type' statements separate from non-type imports, even if importing from the same module. Do not suggest consolidating them into a single import statement. Ensure type imports remain on their own line (import type { ... } from 'module') and regular imports stay on separate lines.

Applied to files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-17T00:40:09.635Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.stories.ts:45-55
Timestamp: 2025-12-17T00:40:09.635Z
Learning: Prefer pure function declarations over function expressions (e.g., use function foo() { ... } instead of const foo = () => { ... }) for pure functions in the repository. Function declarations are more functional-leaning, offer better hoisting clarity, and can improve readability and tooling consistency. Apply this guideline across TypeScript files in Comfy-Org/ComfyUI_frontend, including story and UI component code, except where a function expression is semantically required (e.g., callbacks, higher-order functions with closures).

Applied to files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-30T22:22:33.836Z
Learnt from: kaili-yang
Repo: Comfy-Org/ComfyUI_frontend PR: 7805
File: src/composables/useCoreCommands.ts:439-439
Timestamp: 2025-12-30T22:22:33.836Z
Learning: When accessing reactive properties from Pinia stores in TypeScript files, avoid using .value on direct property access (e.g., useStore().isOverlayExpanded). Pinia auto-wraps refs when accessed directly, returning the primitive value. The .value accessor is only needed when destructuring store properties or when using storeToRefs().

Applied to files:

  • src/extensions/core/index.ts
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2026-01-12T17:39:27.738Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7906
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:545-552
Timestamp: 2026-01-12T17:39:27.738Z
Learning: In Vue/TypeScript files (src/**/*.{ts,tsx,vue}), prefer if/else statements over ternary operators when performing side effects or actions (e.g., mutating state, calling methods with side effects). Ternaries should be reserved for computing and returning values.

Applied to files:

  • src/extensions/core/index.ts
  • src/components/imagecrop/WidgetImageCrop.vue
  • src/lib/litegraph/src/types/widgets.ts
  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/lib/litegraph/src/widgets/widgetMap.ts
  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
  • src/extensions/core/imageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Vue 3 Single File Components (SFCs) with Composition API only

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-11T03:55:57.926Z
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:57.926Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-08T02:40:22.621Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7894
File: src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts:11-14
Timestamp: 2026-01-08T02:40:22.621Z
Learning: In the Comfy-Org/ComfyUI_frontend repository test files: When testing components, import the real type definitions from the component files instead of duplicating interface definitions in the test files. This prevents type drift and maintains consistency.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Use separate `import type` statements instead of inline `type` in mixed imports

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T21:15:46.862Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7603
File: src/components/queue/QueueOverlayHeader.vue:49-59
Timestamp: 2025-12-18T21:15:46.862Z
Learning: In the ComfyUI_frontend repository, for Vue components, do not add aria-label to buttons that have visible text content (e.g., buttons containing <span> text). The visible text provides the accessible name. Use aria-label only for elements without visible labels (e.g., icon-only buttons). If a button has no visible label, provide a clear aria-label or associate with an aria-labelledby describing its action.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use `cn()` utility from `@/utils/tailwindUtil` for merging Tailwind class names instead of `:class="[]"` or hardcoding

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-08T02:26:18.357Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7893
File: src/components/button/IconGroup.vue:5-6
Timestamp: 2026-01-08T02:26:18.357Z
Learning: In components that use the cn utility from '@/utils/tailwindUtil' with tailwind-merge, rely on the behavior that conflicting Tailwind classes are resolved by keeping the last one. For example, cn('base-classes bg-default', propClass) will have any conflicting background class from propClass override bg-default. This additive pattern is intentional and aligns with the shadcn-ui convention; ensure you document or review expectations accordingly in Vue components.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Never use `!important` or `!` Tailwind prefix - fix interfering classes instead

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind utility classes instead of `dark:` variant - use semantic values from `style.css` theme (e.g., `bg-node-component-surface`)

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T21:40:12.361Z
Learnt from: benceruleanlu
Repo: Comfy-Org/ComfyUI_frontend PR: 7297
File: src/components/actionbar/ComfyActionbar.vue:33-43
Timestamp: 2025-12-09T21:40:12.361Z
Learning: In Vue single-file components, allow inline Tailwind CSS class strings for static classes and avoid extracting them into computed properties solely for readability. Prefer keeping static class names inline for simplicity and performance. For dynamic or conditional classes, use Vue bindings (e.g., :class) to compose classes.

Applies to all Vue files in the repository (e.g., src/**/*.vue) where Tailwind utilities are used for static styling.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use Tailwind fraction utilities instead of arbitrary percentage values (e.g., `w-4/5` instead of `w-[80%]`)

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Avoid `<style>` blocks in Vue components - use Tailwind 4 styling instead

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T02:07:38.870Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7598
File: src/components/sidebar/tabs/AssetsSidebarTab.vue:131-131
Timestamp: 2025-12-18T02:07:38.870Z
Learning: Tailwind CSS v4 safe utilities (e.g., items-center-safe, justify-*-safe, place-*-safe) are allowed in Vue components under src/ and in story files. Do not flag these specific safe variants as invalid when reviewing code in src/**/*.vue or related stories.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-04T21:43:49.363Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/parameters/TabParameters.vue:10-0
Timestamp: 2025-12-04T21:43:49.363Z
Learning: Vue 3.5+ supports reactive props destructure in <script setup>. Destructuring props directly (e.g., `const { nodes } = defineProps<{ nodes: LGraphNode[] }>()`) maintains reactivity through compiler transformation. This is the recommended modern approach and does not require using `props.x` or `toRef`/`toRefs`.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.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 : Destructure props using Vue 3.5 style in Vue components

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Use TypeScript Vue 3.5 style default prop declaration with reactive props destructuring - avoid `withDefaults` or runtime props

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-16T22:26:49.463Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7537
File: src/components/ui/button/Button.vue:17-17
Timestamp: 2025-12-16T22:26:49.463Z
Learning: In Vue 3.5+ with <script setup>, when using defineProps<Props>() with partial destructuring (e.g., const { as = 'button', class: customClass = '' } = defineProps<Props>() ), props that are not destructured (e.g., variant, size) stay accessible by name in the template scope. This pattern is valid: you can destructure only a subset of props for convenience while referencing the remaining props directly in template expressions. Apply this guideline to Vue components across the codebase (all .vue files).

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use vue 3.5 style of default prop declaration

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.vue : Prefer `defineModel` over separately defining a prop and emit for v-model bindings

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize ref and reactive for reactive state

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-09T03:49:52.828Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/platform/updates/components/WhatsNewPopup.vue:5-13
Timestamp: 2025-12-09T03:49:52.828Z
Learning: In Vue files across the ComfyUI_frontend repo, when a button is needed, prefer the repo's common button components from src/components/button/ (IconButton.vue, TextButton.vue, IconTextButton.vue) over plain HTML <button> elements. These components wrap PrimeVue with the project’s design system styling. Use only the common button components for consistency and theming, and import them from src/components/button/ as needed.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-22T21:36:08.369Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/platform/cloud/subscription/components/PricingTable.vue:185-201
Timestamp: 2025-12-22T21:36:08.369Z
Learning: In Vue components, avoid creating single-use variants for common UI components (e.g., Button and other shared components). Aim for reusable variants that cover multiple use cases. It’s acceptable to temporarily mix variant props with inline Tailwind classes when a styling need is unique to one place, but plan and consolidate into shared, reusable variants as patterns emerge across the codebase.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-21T01:06:02.786Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7649
File: src/components/graph/selectionToolbox/ColorPickerButton.vue:15-18
Timestamp: 2025-12-21T01:06:02.786Z
Learning: In Comfy-Org/ComfyUI_frontend, in Vue component files, when a filled icon is required (e.g., 'pi pi-circle-fill'), you may mix PrimeIcons with Lucide icons since Lucide lacks filled variants. This mixed usage is acceptable when one icon library does not provide an equivalent filled icon. Apply consistently across Vue components in the src directory where icons are used, and document the rationale when a mixed approach is chosen.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 Learning: 2025-12-18T16:03:02.066Z
Learnt from: henrikvilhelmberglund
Repo: Comfy-Org/ComfyUI_frontend PR: 7617
File: src/components/actionbar/ComfyActionbar.vue:301-308
Timestamp: 2025-12-18T16:03:02.066Z
Learning: In the ComfyUI frontend queue system, useQueuePendingTaskCountStore().count indicates the number of tasks in the queue, where count = 1 means a single active/running task and count > 1 means there are pending tasks in addition to the active task. Therefore, in src/components/actionbar/ComfyActionbar.vue, enable the 'Clear Pending Tasks' button only when count > 1 to avoid clearing the currently running task. The active task should be canceled using the 'Cancel current run' button instead. This rule should be enforced via a conditional check on the queue count, with appropriate disabled/aria-disabled states for accessibility, and tests should verify behavior for count = 1 and count > 1.

Applied to files:

  • src/components/imagecrop/WidgetImageCrop.vue
📚 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 occurred

Applied to files:

  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
  • src/composables/useImageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Avoid mutable state - prefer immutability and assignment at point of declaration

Applied to files:

  • src/lib/litegraph/src/widgets/ImageCropWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Leverage VueUse functions for performance-enhancing composables

Applied to files:

  • src/composables/useImageCrop.ts
  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/composables/**/*.ts : Name composables as `useXyz.ts` (e.g., `useForm.ts`)

Applied to files:

  • src/composables/useImageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Avoid using `ref` with `watch` if a `computed` would suffice - minimize refs and derived state

Applied to files:

  • src/composables/useImageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Watch out for Code Smells and refactor to avoid them

Applied to files:

  • src/composables/useImageCrop.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/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied to files:

  • src/composables/useImageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,vue} : Use `ref` for reactive state, `computed()` for derived values, and `watch`/`watchEffect` for side effects in Composition API

Applied to files:

  • src/composables/useImageCrop.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx} : Use function declarations instead of function expressions when possible

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx} : Keep functions short and functional

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.ts : Favor pure functions, especially testable ones

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2026-01-10T00:24:17.695Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-10T00:24:17.695Z
Learning: Applies to src/**/*.{ts,tsx,vue} : Never use `as any` type assertions - fix the underlying type issue

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.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/**/*.{ts,tsx} : Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.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 instead

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2026-01-09T02:07:59.035Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7898
File: src/composables/usePaste.test.ts:248-248
Timestamp: 2026-01-09T02:07:59.035Z
Learning: In test files at src/**/*.test.ts, when creating mock objects that partially implement an interface (e.g., LGraphNode), use `as Partial<InterfaceType> as InterfaceType` instead of `as any` or `as unknown as InterfaceType` to explicitly acknowledge the incomplete implementation while maintaining type safety.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-06T02:11:00.385Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7137
File: src/components/rightSidePanel/RightSidePanel.vue:174-180
Timestamp: 2025-12-06T02:11:00.385Z
Learning: PrimeVue components have poor TypeScript typing, so type assertions (like `as RightSidePanelTab`) may be necessary when handling emitted events or prop values from PrimeVue components like TabList.

Applied to files:

  • src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts
📚 Learning: 2025-12-09T04:35:43.971Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 6300
File: src/locales/en/main.json:774-780
Timestamp: 2025-12-09T04:35:43.971Z
Learning: In the Comfy-Org/ComfyUI_frontend repository, locale files other than `src/locales/en/main.json` are generated automatically on every release. Developers only need to add English (en) key/values in `src/locales/en/main.json` when making PRs; manual updates to other locale files (fr, ja, ko, ru, zh, zh-TW, es, ar, tr, etc.) are not required and should not be suggested in reviews.

Applied to files:

  • src/locales/en/main.json
🧬 Code graph analysis (4)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (3)
src/lib/litegraph/src/litegraph.ts (1)
  • BaseWidget (147-147)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (268-274)
src/lib/litegraph/src/widgets/BaseWidget.ts (2)
  • DrawWidgetOptions (14-19)
  • WidgetEventOptions (30-34)
src/lib/litegraph/src/widgets/widgetMap.ts (2)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)
  • ImageCropWidget (11-49)
src/lib/litegraph/src/utils/type.ts (1)
  • toClass (13-18)
src/composables/useImageCrop.ts (2)
src/lib/litegraph/src/types/widgets.ts (1)
  • CropRegionValue (260-265)
src/scripts/app.ts (1)
  • app (1744-1744)
src/renderer/extensions/vueNodes/widgets/composables/useImageCropWidget.ts (4)
src/scripts/widgets.ts (1)
  • ComfyWidgetConstructorV2 (31-34)
src/lib/litegraph/src/types/widgets.ts (1)
  • IImageCropWidget (268-274)
src/schemas/nodeDef/nodeDefSchemaV2.ts (1)
  • ImageCropInputSpec (180-180)
src/lib/litegraph/src/LGraphNode.ts (1)
  • widget (3972-3975)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: deploy-and-comment
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (9)
src/extensions/core/index.ts (1)

13-13: LGTM!

The import is correctly placed in alphabetical order among other core extension imports, following the established pattern for side-effect extension registration.

src/locales/en/main.json (1)

1713-1721: LGTM!

The localization entries are well-structured:

  • Keys follow camelCase naming convention consistent with the codebase
  • Text values are clear and appropriate for the crop UI
  • Placement maintains logical ordering in the file

Based on learnings, only English locale updates are required; other locales are auto-generated on release.

src/lib/litegraph/src/widgets/widgetMap.ts (3)

21-21: LGTM!

Import is correctly placed in alphabetical order after ImageCompareWidget.


54-54: LGTM!

The type map entry follows the established pattern for widget type registration.


125-126: LGTM!

The switch case correctly uses the toClass helper to convert the widget, consistent with all other widget type cases in this function.

src/lib/litegraph/src/types/widgets.ts (1)

87-87: LGTM!

The IImageCropWidget type is correctly added to the IWidget discriminated union.

src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

46-48: LGTM - intentional no-op for Vue-only widget.

The empty onClick handler with the explanatory comment is appropriate since this widget delegates all interaction to the Vue component layer.

src/components/imagecrop/WidgetImageCrop.vue (1)

1-7: LGTM - Well-structured component following Vue 3 patterns.

The component correctly:

  • Uses <script setup lang="ts"> with Composition API
  • Leverages defineModel for v-model binding
  • Uses useTemplateRef for element refs
  • Applies proper i18n via $t()
  • Uses Tailwind classes without <style> block
  • Stops pointer event propagation appropriately to prevent canvas interference

Also applies to: 102-142

src/composables/useImageCrop.ts (1)

184-193: api.apiURL() is already used for safe URL construction.

The imageUrl comes from getNodeImageUrls() in the image preview store. For output images, the URLs are properly constructed with api.apiURL() and parameter encoding via URLSearchParams at line 120-121 of imagePreviewStore.ts. For preview images, they are returned directly from the app's internal state. Both paths are safe from XSS injection in the CSS background-image context since the URLs originate from the backend or app, not user input.

@jtydhr88 jtydhr88 force-pushed the imageCrop-crop-box-new branch from e9bf36c to 21e2da1 Compare January 17, 2026 04:14
@coderabbitai coderabbitai bot requested a review from DrJKL January 17, 2026 04:14
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@src/components/boundingbox/WidgetBoundingBox.vue`:
- Around line 51-76: The computed setters for x, y, width, and height currently
write raw values into modelValue and should instead normalize inputs: in the set
handlers for the computed properties (x, y, width, height) clamp values to
allowed ranges (e.g., x,y >= 0; width,height >= 1) and coerce to integers
(Math.round or equivalent) before assigning to modelValue.value; also ensure
when updating width/height you respect any image bounds or relationships by
computing the clamped value first and then setting modelValue.value = {
...modelValue.value, <prop>: normalizedValue } so invalid negative or decimal
numbers cannot leak into the model.
- Around line 1-38: The template currently uses the Options API $t() global
helper (e.g. $t('boundingBox.x'), $t('boundingBox.y'), $t('boundingBox.width'),
$t('boundingBox.height')) but this component should use the Composition API i18n
helper; import and call useI18n() in the script (or script setup) and extract
const { t } = useI18n(), then replace all template occurrences of $t(...) with
t(...), ensuring t is returned/exposed to the template so the labels render
correctly.

In
`@src/renderer/extensions/vueNodes/widgets/composables/useBoundingBoxWidget.ts`:
- Around line 25-37: The code unsafely asserts inputSpec as BoundingBoxInputSpec
inside useBoundingBoxWidget; replace the type assertion with a runtime-safe
check: add a type predicate function (e.g., isBoundingBoxInputSpec(input:
InputSpecV2): input is BoundingBoxInputSpec) or validate with Zod, then use that
predicate to narrow inputSpec before accessing name/component/default and
constructing defaultValue; update useBoundingBoxWidget to call the predicate (or
parse with Zod) and handle the failure branch (throw or fallback) so you no
longer rely on "as BoundingBoxInputSpec".
♻️ Duplicate comments (9)
src/lib/litegraph/src/widgets/ImageCropWidget.ts (1)

17-44: Prefer ctx.save()/ctx.restore() for canvas state management.

The manual destructuring and Object.assign approach is verbose. The standard canvas idiom handles all state properties automatically.

♻️ Proposed refactor
  drawWidget(ctx: CanvasRenderingContext2D, options: DrawWidgetOptions): void {
    const { width } = options
    const { y, height } = this

-   const { fillStyle, strokeStyle, textAlign, textBaseline, font } = ctx
+   ctx.save()

    ctx.fillStyle = this.background_color
    ctx.fillRect(15, y, width - 30, height)

    ctx.strokeStyle = this.outline_color
    ctx.strokeRect(15, y, width - 30, height)

    ctx.fillStyle = this.text_color
    ctx.font = '11px monospace'
    ctx.textAlign = 'center'
    ctx.textBaseline = 'middle'

    const text = `ImageCrop: ${t('widgets.node2only')}`
    ctx.fillText(text, width / 2, y + height / 2)

-   Object.assign(ctx, {
-     fillStyle,
-     strokeStyle,
-     textAlign,
-     textBaseline,
-     font
-   })
+   ctx.restore()
  }
src/renderer/extensions/vueNodes/widgets/registry/widgetRegistry.ts (1)

167-182: Consider adding 'imagecrop' to EXPANDING_TYPES for better UX.

The imagecrop widget includes an interactive image preview with crop visualization. Adding it to EXPANDING_TYPES (Line 223) would allow the widget to expand and utilize available vertical space, similar to load3D.

♻️ Proposed refactor
-const EXPANDING_TYPES = ['textarea', 'markdown', 'load3D'] as const
+const EXPANDING_TYPES = ['textarea', 'markdown', 'load3D', 'imagecrop'] as const
src/composables/useImageCrop.ts (4)

62-74: Consider using nullish coalescing for explicit intent.

Using || for defaults treats 0 as falsy. While 0 would be clamped by MIN_CROP_SIZE constraints, using ?? makes the intent clearer.

♻️ Proposed change
  const cropWidth = computed({
-   get: () => modelValue.value.width || 512,
+   get: () => modelValue.value.width ?? 512,
    set: (v: number) => {
      modelValue.value.width = v
    }
  })

  const cropHeight = computed({
-   get: () => modelValue.value.height || 512,
+   get: () => modelValue.value.height ?? 512,
    set: (v: number) => {
      modelValue.value.height = v
    }
  })

299-303: Guard against null target in pointer capture helpers.

The type assertions (e.target as HTMLElement) could fail if e.target is null. Add a guard to prevent potential runtime errors.

🐛 Proposed fix
-const capturePointer = (e: PointerEvent) =>
-  (e.target as HTMLElement).setPointerCapture(e.pointerId)
+const capturePointer = (e: PointerEvent) => {
+  if (e.target instanceof HTMLElement) {
+    e.target.setPointerCapture(e.pointerId)
+  }
+}

-const releasePointer = (e: PointerEvent) =>
-  (e.target as HTMLElement).releasePointerCapture(e.pointerId)
+const releasePointer = (e: PointerEvent) => {
+  if (e.target instanceof HTMLElement) {
+    e.target.releasePointerCapture(e.pointerId)
+  }
+}

433-443: Consider optimizing deep watchers on store state.

Deep watching nodeOutputStore.nodeOutputs and nodePreviewImages triggers on any change to these objects, even for unrelated nodes. This could cause performance issues with many nodes.

♻️ Proposed optimization
+ const nodeOutputs = computed(() => {
+   if (!node.value) return null
+   return nodeOutputStore.nodeOutputs[node.value.id]
+ })
+
+ const nodePreviewImages = computed(() => {
+   if (!node.value) return null
+   const inputNode = node.value.getInputNode(0)
+   return inputNode ? nodeOutputStore.nodePreviewImages[inputNode.id] : null
+ })

  watch(
-   () => nodeOutputStore.nodeOutputs,
+   nodeOutputs,
    () => updateImageUrl(),
-   { deep: true }
  )

  watch(
-   () => nodeOutputStore.nodePreviewImages,
+   nodePreviewImages,
    () => updateImageUrl(),
-   { deep: true }
  )

425-431: Handle case when rootGraph is not yet available.

The app.rootGraph?.getNodeById(nodeId) call may return null if the graph hasn't been initialized. Consider adding a watcher to retry initialization when the graph becomes available.

♻️ Proposed enhancement
  const initialize = () => {
    if (nodeId != null) {
      node.value = app.rootGraph?.getNodeById(nodeId) || null
    }

    updateImageUrl()
  }

+ // Retry initialization when rootGraph becomes available
+ watch(
+   () => app.rootGraph,
+   (graph) => {
+     if (graph && !node.value && nodeId != null) {
+       node.value = graph.getNodeById(nodeId) || null
+       updateImageUrl()
+     }
+   }
+ )

  onMounted(initialize)
src/components/imagecrop/WidgetImageCrop.vue (3)

37-46: Add keyboard support to the crop box for accessibility.

The crop box is pointer-only, which limits keyboard users. Make it focusable and wire Arrow-key nudges to the same movement logic used for dragging (in useImageCrop).

♿️ Suggested template enhancement
-      <div
-        v-if="imageUrl && !isLoading"
-        class="absolute box-content cursor-move overflow-hidden border-2 border-white"
-        :style="cropBoxStyle"
-        `@pointerdown`="handleDragStart"
-        `@pointermove`="handleDragMove"
-        `@pointerup`="handleDragEnd"
-      >
+      <div
+        v-if="imageUrl && !isLoading"
+        tabindex="0"
+        class="absolute box-content cursor-move overflow-hidden border-2 border-white focus-visible:ring-2 focus-visible:ring-primary"
+        :style="cropBoxStyle"
+        `@pointerdown`="handleDragStart"
+        `@pointermove`="handleDragMove"
+        `@pointerup`="handleDragEnd"
+        `@keydown`="handleKeyboardMove"
+      >

48-57: Use cn() for class merging instead of array syntax.

♻️ Suggested refactor
<script setup lang="ts">
-import { useTemplateRef } from 'vue'
+import { useTemplateRef } from 'vue'
+import { cn } from '@/utils/tailwindUtil'
...
-        :class="['absolute', handle.class]"
+        :class="cn('absolute', handle.class)"
As per coding guidelines.

72-99: Prefer Vue 3.5 reactive props destructuring for nodeId.

♻️ Suggested refactor
-const props = defineProps<{
-  nodeId: NodeId
-}>()
+const { nodeId } = defineProps<{
+  nodeId: NodeId
+}>()

...
-} = useImageCrop(props.nodeId, { imageEl, containerEl, modelValue })
+} = useImageCrop(nodeId, { imageEl, containerEl, modelValue })
As per coding guidelines.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@src/lib/litegraph/src/widgets/BaseWidget.ts`:
- Around line 237-264: Replace the magic margin numbers in drawVueOnlyWarning
with the BaseWidget.margin constant: use BaseWidget.margin instead of 15 and
compute width minus 2 * BaseWidget.margin instead of width - 30; update the
fillRect, strokeRect and fillText x-position calculations (and any other uses of
15 or 30 in that method) to reference BaseWidget.margin so the widget respects
the class-level margin constant (method: drawVueOnlyWarning, class: BaseWidget).

@DrJKL DrJKL removed their assignment Jan 17, 2026
@jtydhr88 jtydhr88 merged commit be8916b into main Jan 17, 2026
28 checks passed
@jtydhr88 jtydhr88 deleted the imageCrop-crop-box-new branch January 17, 2026 22:09
godwiniheuwa pushed a commit to godwiniheuwa/ComfyUI_frontend that referenced this pull request Jan 22, 2026
…eCrop (Comfy-Org#7825)

## Summary

Another implementation for image crop node, alternative for
Comfy-Org#7014
As discussed with @christian-byrne and @DrJKL we could have single
widget - IMAGECROP with 4 ints and UI preview.

However, this solution requires changing the definition of image crop
node in BE (sent
[here](Comfy-Org/ComfyUI#11594)), which
will break the exsiting workflow, also it would not allow connect
separate int node as input, I am not sure it is a good idea.

So I keep two PRs openned for references

## Screenshots


https://github.com/user-attachments/assets/fde6938c-4395-48f6-ac05-6282c5eb8157

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7825-feat-Add-visual-crop-preview-widget-for-ImageCrop-node-widget-ImageCrop-2dc6d73d3650812bb8a2cdff4615032b)
by [Unito](https://www.unito.io)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants