Skip to content

Conversation

@brucew4yn3rp
Copy link
Collaborator

@brucew4yn3rp brucew4yn3rp commented Jan 4, 2026

Canvas Rotation and Mirroring

Overview

Adds rotation (90° left/right) and mirroring (horizontal/vertical) capabilities to the mask editor canvas. All three layers (image, mask, RGB) transform together. Redo and Undo respect transformations as new states. Keyboard shortcuts also added for all four functions in Keybinding settings.

Additionally, fixed the issue of ctrl+z and ctrl+y keyboard commands not restricting to the mask editor canvas while opened.

MaskEditor.Rotate.and.Mirror.mp4

What Changed

New Files

  • src/composables/maskeditor/useCanvasTransform.ts
    • Core transformation logic for rotation and mirroring
    • GPU texture recreation after transformations

Modified Files

src/composables/useCoreCommands.ts

  • Added check to see if Mask Editor is opened for undo and redo commands

src/stores/maskEditorStore.ts

  • Added GPU texture recreation signals

src/composables/maskeditor/useBrushDrawing.ts

  • Added watcher for gpuTexturesNeedRecreation signal
  • Handles GPU texture recreation when canvas dimensions change
  • Recreates textures with new dimensions after rotation
  • Updates preview canvas and readback buffers accordingly
  • Ensures proper ArrayBuffer backing for WebGPU compatibility

src/components/maskeditor/TopBarHeader.vue

  • Added 4 new transform buttons with icons:
    • Rotate Left (counter-clockwise)
    • Rotate Right (clockwise)
    • Mirror Horizontal
    • Mirror Vertical
  • Added visual separators between button groups

src/extensions/core/maskEditor.ts

  • Added keyboard shortcut settings for rotate and mirror

Translation Files (e.g., src/locales/en.json)

  • Added i18n keys:

┆Issue is synchronized with this Notion page by Unito

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jan 4, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

📝 Walkthrough

Walkthrough

Adds rotate (left/right) and mirror (horizontal/vertical) actions to the mask editor UI, a new useCanvasTransform composable to rotate/mirror mask/rgb/img canvases and signal GPU texture recreation, GPU-related store refs and watcher, three-layer canvas history updates, and localization keys.

Changes

Cohort / File(s) Summary
UI Toolbar Actions
src/components/maskeditor/dialog/TopBarHeader.vue, src/locales/en/main.json
Adds four toolbar buttons (rotate left/right, mirror horizontal/vertical) with SVG icons, tooltips, and localization keys; handlers call useCanvasTransform methods.
Canvas Transformation Composable
src/composables/maskeditor/useCanvasTransform.ts
New useCanvasTransform() exporting rotateAllLayers(clockwise) and mirrorAllLayers(horizontal); performs per-pixel rotate/mirror on mask, rgb, and img canvases, swaps dimensions on rotate, premultiplies alpha for GPU, stores pending GPU data, and pushes composite history states.
Canvas History & Undo/Redo Integration
src/composables/maskeditor/useCanvasHistory.ts, src/composables/useCoreCommands.ts, src/extensions/core/maskeditor.ts
History now captures three-layer states (mask,rgb,img) with updated save/restore/cleanup logic. Undo/Redo route to mask editor history when the editor dialog is open. Extension commands expose rotate/mirror operations.
GPU Texture State & Recreation
src/stores/maskEditorStore.ts, src/composables/maskeditor/useBrushDrawing.ts
Store adds refs: gpuTexturesNeedRecreation, gpuTextureWidth, gpuTextureHeight, pendingGPUMaskData, pendingGPURgbData and resets them in resetState(). Brush drawing adds a watcher that recreates GPU textures, uploads pending or canvas data, resizes preview canvas/readback buffers, and clears flags.
Minor UI/Event Change
src/components/maskeditor/MaskEditorContent.vue
Adds keydown.stop on root mask editor container to stop propagation of keydown events.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant TopBar as TopBarHeader
    participant Transform as useCanvasTransform
    participant Store as maskEditorStore
    participant Brush as useBrushDrawing
    participant GPU as GPU

    User->>TopBar: Click rotate/mirror button
    TopBar->>Transform: rotateAllLayers(clockwise) / mirrorAllLayers(horizontal)

    rect rgba(200,220,240,0.6)
        Note over Transform: Read pixels from mask/rgb/img\nApply per-pixel rotate/mirror\nSwap canvas dimensions (rotate)\nPremultiply alpha for GPU
    end

    Transform->>Store: set gpuTexturesNeedRecreation = true\nTransform->>Store: write pendingGPUMaskData / pendingGPURgbData
    Transform->>Store: push composite state to history

    rect rgba(240,220,200,0.6)
        Note over Brush: GPU recreation watcher triggers
        Brush->>GPU: create textures with new size
        Brush->>GPU: upload pending data or fallback canvas data
        Brush->>Brush: resize preview canvas & readback buffers
        Brush->>Store: clear gpu texture flags / pending data
    end

    Brush->>User: Display transformed canvases
Loading

Possibly related PRs


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 4, 2026

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 01/04/2026, 06:32:28 PM UTC

📈 Summary

  • Total Tests: 511
  • Passed: 500 ✅
  • Failed: 0
  • Flaky: 3 ⚠️
  • Skipped: 8 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 490 / ❌ 0 / ⚠️ 2 / ⏭️ 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

🎉 Click on the links above to view detailed test results for each browser configuration.

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

🎨 Storybook Build Status

loading Build is starting...

⏰ Started at: 01/04/2026, 06:25:38 PM UTC

🚀 Building Storybook

  • 📦 Installing dependencies...
  • 🔧 Building Storybook components...
  • 🌐 Preparing deployment to Cloudflare Pages...

⏱️ Please wait while the Storybook build is in progress...

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

Bundle Size Report

Summary

  • Raw size: 17.3 MB baseline 17.3 MB — 🔴 +16.9 kB
  • Gzip: 3.55 MB baseline 3.55 MB — 🔴 +2.84 kB
  • Brotli: 2.7 MB baseline 2.7 MB — 🔴 +1.79 kB
  • Bundles: 98 current • 98 baseline • 43 added / 43 removed

Category Glance
App Entry Points 🔴 +16.5 kB (3.24 MB) · Graph Workspace 🔴 +392 B (1 MB) · Vendor & Third-Party ⚪ 0 B (9.12 MB) · Other ⚪ 0 B (3.47 MB) · Panels & Settings ⚪ 0 B (300 kB) · UI Components ⚪ 0 B (193 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.24 MB (baseline 3.23 MB) • 🔴 +16.5 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-DpwDupZO.js (new) 3.05 MB 🔴 +3.05 MB 🔴 +639 kB 🔴 +485 kB
assets/index-Bbn6j0UD.js (removed) 3.03 MB 🟢 -3.03 MB 🟢 -637 kB 🟢 -484 kB
assets/index-vCLKk_js.js (new) 195 kB 🔴 +195 kB 🔴 +42.7 kB 🔴 +35.2 kB
assets/index-Cybtrczo.js (removed) 194 kB 🟢 -194 kB 🟢 -42.3 kB 🟢 -35.1 kB
assets/index-BHFabMbY.js (removed) 345 B 🟢 -345 B 🟢 -244 B 🟢 -201 B
assets/index-CeM6zO71.js (new) 345 B 🔴 +345 B 🔴 +245 B 🔴 +227 B

Status: 3 added / 3 removed

Graph Workspace — 1 MB (baseline 1000 kB) • 🔴 +392 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-B4bI7Jd5.js (new) 1 MB 🔴 +1 MB 🔴 +195 kB 🔴 +148 kB
assets/GraphView-DtR8e5sg.js (removed) 1000 kB 🟢 -1000 kB 🟢 -194 kB 🟢 -148 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-C8DNhcyR.js (new) 6.63 kB 🔴 +6.63 kB 🔴 +2.14 kB 🔴 +1.9 kB
assets/UserSelectView-CyLLd6LO.js (removed) 6.63 kB 🟢 -6.63 kB 🟢 -2.14 kB 🟢 -1.9 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-B3sxI6xn.js (new) 22.7 kB 🔴 +22.7 kB 🔴 +5.25 kB 🔴 +4.6 kB
assets/LegacyCreditsPanel-DGtj7O4z.js (removed) 22.7 kB 🟢 -22.7 kB 🟢 -5.26 kB 🟢 -4.6 kB
assets/KeybindingPanel-CSd8_3yu.js (removed) 14.8 kB 🟢 -14.8 kB 🟢 -3.57 kB 🟢 -3.12 kB
assets/KeybindingPanel-gSSwBtDC.js (new) 14.8 kB 🔴 +14.8 kB 🔴 +3.57 kB 🔴 +3.13 kB
assets/ExtensionPanel-BnJho85j.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +2.62 kB 🔴 +2.3 kB
assets/ExtensionPanel-DK1ljGgd.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -2.62 kB 🟢 -2.3 kB
assets/AboutPanel-DL80A574.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.2 kB
assets/AboutPanel-J92vh9XO.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.2 kB
assets/ServerConfigPanel-C5pF53A7.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -2.04 kB 🟢 -1.81 kB
assets/ServerConfigPanel-D3Ade2Vj.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +2.04 kB 🔴 +1.81 kB
assets/UserPanel-0nvBzFeh.js (new) 6.88 kB 🔴 +6.88 kB 🔴 +1.79 kB 🔴 +1.57 kB
assets/UserPanel-Bay-kw9Z.js (removed) 6.88 kB 🟢 -6.88 kB 🟢 -1.79 kB 🟢 -1.57 kB
assets/settings-AN2JfZVQ.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BIdKi-OT.js 26.2 kB 26.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Bu3OR-lX.js 24.6 kB 24.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-ByL6gy5c.js 25.4 kB 25.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CjlRFMdL.js 32.8 kB 32.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DkGwvylK.js 26.9 kB 26.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Dyd027Dx.js 24.7 kB 24.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-wwBxqLH5.js 21.3 kB 21.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-xx2Yb6R2.js 23.8 kB 23.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 193 kB (baseline 193 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LazyImage.vue_vue_type_script_setup_true_lang-BsVZtiUn.js (new) 60.1 kB 🔴 +60.1 kB 🔴 +12.4 kB 🔴 +10.8 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-CxyLIqfI.js (removed) 60.1 kB 🟢 -60.1 kB 🟢 -12.4 kB 🟢 -10.8 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-Ca65hC1V.js (new) 54.4 kB 🔴 +54.4 kB 🔴 +8.6 kB 🔴 +7.37 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-CPD6o0ti.js (removed) 54.4 kB 🟢 -54.4 kB 🟢 -8.6 kB 🟢 -7.39 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-C7Ho9aP3.js (removed) 49 kB 🟢 -49 kB 🟢 -10.5 kB 🟢 -9.14 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-DYTydeEF.js (new) 49 kB 🔴 +49 kB 🔴 +10.5 kB 🔴 +9.13 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CE6guJnm.js (removed) 10.9 kB 🟢 -10.9 kB 🟢 -2.89 kB 🟢 -2.55 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-CER-0054.js (new) 10.9 kB 🔴 +10.9 kB 🔴 +2.89 kB 🔴 +2.55 kB
assets/ComfyQueueButton-CwuhZzVz.js (new) 8.83 kB 🔴 +8.83 kB 🔴 +2.58 kB 🔴 +2.29 kB
assets/ComfyQueueButton-D6mqqPt3.js (removed) 8.83 kB 🟢 -8.83 kB 🟢 -2.57 kB 🟢 -2.29 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-BZ0eX2iS.js (new) 3.72 kB 🔴 +3.72 kB 🔴 +1.45 kB 🔴 +1.32 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-CbfXEDyF.js (removed) 3.72 kB 🟢 -3.72 kB 🟢 -1.46 kB 🟢 -1.32 kB
assets/WidgetButton-B73QWnPB.js (removed) 2.21 kB 🟢 -2.21 kB 🟢 -997 B 🟢 -893 B
assets/WidgetButton-DPMIF4ek.js (new) 2.21 kB 🔴 +2.21 kB 🔴 +997 B 🔴 +893 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-CXSOEcVp.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +890 B 🔴 +767 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-DYHvU_1-.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -890 B 🟢 -767 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-baZKuhG7.js (new) 897 B 🔴 +897 B 🔴 +500 B 🔴 +435 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-ljzk2w32.js (removed) 897 B 🟢 -897 B 🟢 -502 B 🟢 -432 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-CieNuc25.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 9 added / 9 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-9C-cPvxN.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/keybindingService-CBhjuTaL.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.59 kB
assets/audioService-BZlSF3KA.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -826 B
assets/audioService-D9nMzVQC.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +959 B 🔴 +820 B
assets/serverConfigStore-BWm_uxF_.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-BB-tdliC.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +546 B
assets/audioUtils-BWhowPt3.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -651 B 🟢 -547 B

Status: 1 added / 1 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-BiTiQvXD.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-QAjmpiSo.js 3.89 MB 3.89 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-BaL9qxPA.js 1.95 MB 1.95 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-BYiW32lN.js 2.02 MB 2.02 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-BkaRKIwe.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-DgYh9BF5.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BF8peZ5_.js 420 kB 420 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.47 MB (baseline 3.47 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscriptionRequiredDialogContent-B0oED_N9.js (removed) 29 kB 🟢 -29 kB 🟢 -6.42 kB 🟢 -5.57 kB
assets/SubscriptionRequiredDialogContent-DstewAVA.js (new) 29 kB 🔴 +29 kB 🔴 +6.42 kB 🔴 +5.58 kB
assets/WidgetRecordAudio-CqDNg3dl.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.64 kB
assets/WidgetRecordAudio-DVM2_dgc.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.63 kB
assets/AudioPreviewPlayer-CAa1QSC9.js (new) 13.3 kB 🔴 +13.3 kB 🔴 +3.35 kB 🔴 +2.99 kB
assets/AudioPreviewPlayer-e5LiPCZM.js (removed) 13.3 kB 🟢 -13.3 kB 🟢 -3.35 kB 🟢 -3 kB
assets/ValueControlPopover-B62K3PHA.js (removed) 5.49 kB 🟢 -5.49 kB 🟢 -1.71 kB 🟢 -1.52 kB
assets/ValueControlPopover-BRavKxaw.js (new) 5.49 kB 🔴 +5.49 kB 🔴 +1.71 kB 🔴 +1.52 kB
assets/WidgetGalleria-BvMbiIbS.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.31 kB
assets/WidgetGalleria-DnEOtuu5.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.44 kB 🟢 -1.3 kB
assets/WidgetColorPicker-B5DwYGMj.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetColorPicker-DOpnyK5O.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetTextarea-CV0d4wou.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.22 kB 🔴 +1.08 kB
assets/WidgetTextarea-DcVO9ONg.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.22 kB 🟢 -1.08 kB
assets/WidgetMarkdown-BMO8lD23.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.13 kB
assets/WidgetMarkdown-DLK-nMwP.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.13 kB
assets/WidgetAudioUI-B6ugdYQR.js (new) 2.89 kB 🔴 +2.89 kB 🔴 +1.17 kB 🔴 +1.06 kB
assets/WidgetAudioUI-Bc9pk1oE.js (removed) 2.89 kB 🟢 -2.89 kB 🟢 -1.17 kB 🟢 -1.05 kB
assets/WidgetInputText-Bnu09zmd.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -921 B 🟢 -834 B
assets/WidgetInputText-DoJtKF7J.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +921 B 🔴 +855 B
assets/WidgetToggleSwitch-BPr0nz4n.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +834 B 🔴 +736 B
assets/WidgetToggleSwitch-DLS2Bjhp.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -834 B 🟢 -735 B
assets/MediaImageBottom-Brhhjas2.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +731 B 🔴 +641 B
assets/MediaImageBottom-DcMvBukd.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -732 B 🟢 -645 B
assets/MediaAudioBottom-B5i5hai4.js (new) 1.51 kB 🔴 +1.51 kB 🔴 +733 B 🔴 +648 B
assets/MediaAudioBottom-cgx06SfY.js (removed) 1.51 kB 🟢 -1.51 kB 🟢 -734 B 🟢 -646 B
assets/Media3DBottom-BXUMGWI6.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -733 B 🟢 -650 B
assets/Media3DBottom-CzCkSWvc.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +732 B 🔴 +649 B
assets/MediaVideoBottom-BgVlNX3K.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +732 B 🔴 +650 B
assets/MediaVideoBottom-C1czouFJ.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -732 B 🟢 -647 B
assets/Media3DTop-DvtLO54U.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +768 B 🔴 +656 B
assets/Media3DTop-O5zMHsIB.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -769 B 🟢 -650 B
assets/WidgetSelect-BCiVsYP4.js (new) 733 B 🔴 +733 B 🔴 +361 B 🔴 +321 B
assets/WidgetSelect-BcX_RbIS.js (removed) 733 B 🟢 -733 B 🟢 -360 B 🟢 -303 B
assets/WidgetInputNumber-DiAcHGJ3.js (new) 673 B 🔴 +673 B 🔴 +350 B 🔴 +292 B
assets/WidgetInputNumber-DwCxiMIp.js (removed) 673 B 🟢 -673 B 🟢 -347 B 🟢 -287 B
assets/Load3D-BzL5jrv3.js (new) 424 B 🔴 +424 B 🔴 +267 B 🔴 +228 B
assets/Load3D-DCgNYtQy.js (removed) 424 B 🟢 -424 B 🟢 -268 B 🟢 -226 B
assets/WidgetLegacy-Ba58FmC-.js (new) 364 B 🔴 +364 B 🔴 +237 B 🔴 +196 B
assets/WidgetLegacy-Eh5K5RnT.js (removed) 364 B 🟢 -364 B 🟢 -238 B 🟢 -195 B
assets/commands-bTEY9Mp6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CcfGaui5.js 14.4 kB 14.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CisfgZf5.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CkU12Foh.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CoH2DJa6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-COSt-Bjx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DalfIW5f.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DfTl0eCm.js 13.5 kB 13.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwSJL865.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Bdc58rJq.js 97.1 kB 97.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BRKE6VDs.js 91 kB 91 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-C9ZJBRdI.js 81.5 kB 81.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CAL83XT3.js 84.6 kB 84.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CHLLfvpG.js 82.4 kB 82.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DDqR5EuX.js 71.3 kB 71.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DLHyaEcz.js 92.1 kB 92.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-O7KfJeMO.js 79.9 kB 79.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-OzGsrlqJ.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-DNcFZed8.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-DoyWuZ6A.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-BBrtuHMQ.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-aW9En70v.js 260 kB 260 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BIckSVgU.js 273 kB 273 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BiYpVi7D.js 263 kB 263 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CCEXtYfM.js 243 kB 243 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CvmVDWYd.js 323 kB 323 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-D_wreoPJ.js 267 kB 267 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DIAYlJwM.js 246 kB 246 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Dz-0ZIBN.js 297 kB 297 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-VZsNmhG7.js 264 kB 264 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-DSurjgrZ.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-CPasFD9R.js 3.18 kB 3.18 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 20 added / 20 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 8d1f8ed and 0d936a4.

📒 Files selected for processing (5)
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/locales/en/main.json
  • src/stores/maskEditorStore.ts
🧰 Additional context used
📓 Path-based instructions (18)
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/maskeditor/dialog/TopBarHeader.vue
src/**/*.{vue,ts}

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

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.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

Files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
**/*.{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/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.ts
  • src/locales/en/main.json
src/**/*.ts

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

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

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.ts
src/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores

Files:

  • src/stores/maskEditorStore.ts
**/*Store.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name Pinia stores using the pattern *Store.ts

Files:

  • src/stores/maskEditorStore.ts
🧠 Learnings (19)
📚 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/maskeditor/dialog/TopBarHeader.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 : Leverage VueUse functions for performance-enhancing styles in Vue components

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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 defineExpose only for imperative operations (such as form.validate(), modal.open())

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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 : Utilize Vue 3's Teleport component when needed

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
📚 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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/stores/maskEditorStore.ts
🧬 Code graph analysis (1)
src/composables/maskeditor/useCanvasTransform.ts (1)
src/stores/maskEditorStore.ts (1)
  • useMaskEditorStore (19-301)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: lint-and-format
  • GitHub Check: collect
🔇 Additional comments (4)
src/locales/en/main.json (1)

968-971: LGTM!

The new localization keys for rotation and mirroring operations are clear, well-named, and follow the existing conventions. The placement after the redo entry is logical.

src/stores/maskEditorStore.ts (1)

221-226: LGTM!

The reset logic for GPU-related state is comprehensive and properly clears all flags and pending data. This ensures clean state transitions and prevents memory leaks from retained ArrayBuffers.

src/composables/maskeditor/useCanvasTransform.ts (2)

108-115: Premultiplication logic is correct.

The alpha premultiplication implementation correctly converts from straight alpha (used by Canvas 2D) to premultiplied alpha (required by WebGPU). The formula color = color * alpha is applied per-channel with proper rounding.


221-253: LGTM! Mirror operations maintain canvas dimensions correctly.

The mirroring logic correctly:

  • Maintains original canvas dimensions (no swap needed)
  • Applies transformations to all three layers (mask, rgb, img)
  • Triggers GPU texture updates with unchanged dimensions
  • Saves the result to history for undo/redo support

The implementation is clean and follows the same pattern as rotation operations.

@brucew4yn3rp brucew4yn3rp requested a review from trsommer as a code owner January 4, 2026 14:13
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jan 4, 2026
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 0d936a4 and 2bcbe39.

📒 Files selected for processing (8)
  • src/components/maskeditor/MaskEditorContent.vue
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/useCoreCommands.ts
  • src/extensions/core/maskeditor.ts
  • src/stores/maskEditorStore.ts
🧰 Additional context used
📓 Path-based instructions (18)
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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
**/*.{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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.ts
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.vue
src/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

src/**/stores/**/*.{ts,tsx}: Maintain clear public interfaces and restrict extension access in stores
Use TypeScript for type safety in state management stores

Files:

  • src/stores/maskEditorStore.ts
**/*Store.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name Pinia stores using the pattern *Store.ts

Files:

  • src/stores/maskEditorStore.ts
🧠 Learnings (35)
📚 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/composables/maskeditor/useBrushDrawing.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 : Prefer `computed()` over `ref` with `watch` when deriving values

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.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 : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/maskeditor/useCanvasHistory.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 : Use watch and watchEffect for side effects

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.ts
📚 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/composables/maskeditor/useBrushDrawing.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement cleanup for async operations in Vue components

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.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/maskeditor/useBrushDrawing.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:34.324Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:34.324Z
Learning: Applies to src/**/{composables,components}/**/*.{ts,tsx,vue} : Clean up subscriptions in state management to prevent memory leaks

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.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/composables/maskeditor/useBrushDrawing.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

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

Applied to files:

  • src/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.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/composables/maskeditor/useBrushDrawing.ts
  • src/composables/useCoreCommands.ts
  • src/components/maskeditor/dialog/TopBarHeader.vue
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
  • src/components/maskeditor/MaskEditorContent.vue
  • src/stores/maskEditorStore.ts
  • src/extensions/core/maskeditor.ts
📚 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/maskeditor/dialog/TopBarHeader.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 **/*.{ts,tsx,vue} : Implement proper error handling in code

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Implement proper error handling

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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 defineExpose only for imperative operations (such as form.validate(), modal.open())

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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 : Utilize Vue 3's Teleport component when needed

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.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/maskeditor/dialog/TopBarHeader.vue
  • src/components/maskeditor/MaskEditorContent.vue
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Check tests-ui/README.md for test guidelines

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Write tests for new features

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.ts
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/lib/litegraph/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:56.371Z
Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{js,ts,jsx,tsx} : When adding features, always write vitest unit tests using cursor rules in @.cursor

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.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 **/*.test.ts : Aim for behavioral coverage of critical and new features in unit tests

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.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} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.ts
🧬 Code graph analysis (1)
src/extensions/core/maskeditor.ts (1)
src/composables/maskeditor/useCanvasTransform.ts (1)
  • useCanvasTransform (6-264)
⏰ 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). (14)
  • GitHub Check: playwright-tests-chromium-sharded (4, 8)
  • GitHub Check: playwright-tests-chromium-sharded (1, 8)
  • GitHub Check: playwright-tests-chromium-sharded (3, 8)
  • GitHub Check: playwright-tests-chromium-sharded (5, 8)
  • GitHub Check: playwright-tests-chromium-sharded (7, 8)
  • GitHub Check: playwright-tests-chromium-sharded (8, 8)
  • GitHub Check: playwright-tests-chromium-sharded (6, 8)
  • GitHub Check: playwright-tests-chromium-sharded (2, 8)
  • GitHub Check: playwright-tests (chromium-0.5x)
  • GitHub Check: playwright-tests (mobile-chrome)
  • GitHub Check: playwright-tests (chromium-2x)
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (17)
src/composables/useCoreCommands.ts (2)

63-65: LGTM! Clean integration with mask editor state.

The imports are correctly added following the repository's pattern of keeping type imports separate from regular imports.


204-223: LGTM! Proper conditional routing for undo/redo commands.

The implementation correctly routes undo/redo operations to the mask editor's history when the mask editor dialog is open, and falls back to the workflow change tracker otherwise. The pattern is clean and maintains separation of concerns.

src/composables/maskeditor/useBrushDrawing.ts (2)

1-1: LGTM! Proper WebGPU types reference.

The triple-slash directive correctly enables WebGPU type definitions for this file.


237-357: LGTM! Race condition guard properly implemented.

The isRecreatingTextures guard addresses the race condition concern from the previous review by preventing concurrent texture recreation. The implementation properly:

  1. Checks all preconditions including the guard flag
  2. Destroys old textures before creating new ones (addressing memory pressure concerns)
  3. Handles both pending data upload and canvas fallback scenarios
  4. Recreates readback buffers with correct dimensions
  5. Cleans up flags in a finally block ensuring proper state reset even on error

The fallback to updateGPUFromCanvas() when pending data is null is reasonable, though it assumes canvas state is current.

src/components/maskeditor/dialog/TopBarHeader.vue (2)

38-102: LGTM! Clean UI implementation with proper visual separation.

The four new transformation buttons (rotate left/right, mirror horizontal/vertical) are well-integrated with:

  • Consistent styling using iconButtonClass
  • Clear SVG icons for each action
  • Proper tooltips using i18n keys
  • Visual separators (dividers) between control groups for better UX

161-191: LGTM! Error handling properly implemented.

The transformation handlers correctly implement try-catch error handling as requested in the previous review. Each handler:

  • Awaits the async transformation operation
  • Logs errors to console with clear context
  • Prevents unhandled promise rejections

This addresses the past review comment about adding error handling to transformation handlers.

src/composables/maskeditor/useCanvasHistory.ts (4)

4-9: Well-structured interface for three-layer state.

The CanvasState interface cleanly captures all three canvas layers (mask, rgb, img) with appropriate union type supporting both ImageData and ImageBitmap. This enables efficient history management for rotation operations that produce ImageBitmap instances.


29-75: LGTM: Initialization logic correctly extended to three layers.

The function properly validates all three canvas contexts and their dimensions before capturing state. The requestAnimationFrame retry pattern handles async canvas initialization gracefully.


106-119: Verify the "all-or-nothing" requirement for provided data is intentional.

The condition if (providedMaskData && providedRgbData && providedImgData) requires all three data parameters to be provided together, otherwise fresh data is captured from all canvases. This ensures consistency but callers must supply all three or none.

This design is correct for the current use case (transforms always produce all three), but consider adding a comment documenting this requirement.


166-177: Good memory management for ImageBitmap resources.

The cleanupState function properly checks instanceof ImageBitmap before calling close(), ensuring GPU-backed resources are released. The clearStates function correctly iterates all states for cleanup before clearing the array.

src/stores/maskEditorStore.ts (2)

226-232: GPU state reset is complete and consistent.

All five GPU-related refs are properly reset to their initial values, ensuring clean state when the mask editor is reset.


279-284: GPU texture signals properly exposed for cross-module coordination.

The exported refs enable useCanvasTransform to signal texture recreation needs and provide data, while useBrushDrawing can watch these signals to perform the actual GPU texture recreation. This decoupled design is appropriate.

src/composables/maskeditor/useCanvasTransform.ts (5)

61-103: Mirror algorithm is correct.

The coordinate transformations for mirroring are correct:

  • Horizontal: (x,y) → (width-1-x, y)
  • Vertical: (x,y) → (x, height-1-y)

Dimensions remain unchanged, and the index calculation correctly uses width.


108-115: Premultiplication implementation is correct.

Standard premultiplied alpha calculation: R' = R * (A/255), etc. The in-place mutation is intentional for performance.


177-221: Rotation workflow is well-structured.

The function correctly:

  1. Validates all canvas/context dependencies
  2. Captures original dimensions before transformation
  3. Rotates all three layers consistently
  4. Swaps canvas dimensions to match rotated data
  5. Applies rotated ImageData to canvases
  6. Conditionally recreates GPU textures
  7. Saves state to history for undo/redo support

The async/await pattern with recreateGPUTextures ensures GPU operations complete before history save.


226-258: Mirror workflow correctly preserves dimensions.

The function follows the same pattern as rotateAllLayers but correctly omits dimension swapping since mirroring doesn't change canvas size. GPU texture recreation and history save are handled appropriately.


260-263: Minimal API surface exposed.

Only the two public operations are exported, keeping helper functions (rotateCanvas, mirrorCanvas, premultiplyData, recreateGPUTextures) as private implementation details. This follows the guideline to minimize exported surface area.

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

♻️ Duplicate comments (1)
src/composables/maskeditor/useCanvasTransform.ts (1)

160-165: Remove commented-out code.

Commented code should not remain in production. Either enable this defensive check or remove it entirely.

🔎 Option 1: Enable the check (recommended)
-   /*
-   if (maskData.buffer instanceof SharedArrayBuffer || rgbData.buffer instanceof SharedArrayBuffer) {
-     console.error('[useCanvasTransform] SharedArrayBuffer detected, WebGPU writeTexture will fail')
-     return
-   }
-   */
+   if (maskData.buffer instanceof SharedArrayBuffer || rgbData.buffer instanceof SharedArrayBuffer) {
+     console.error('[useCanvasTransform] SharedArrayBuffer detected, WebGPU writeTexture will fail')
+     return
+   }
🔎 Option 2: Remove commented code
-   /*
-   if (maskData.buffer instanceof SharedArrayBuffer || rgbData.buffer instanceof SharedArrayBuffer) {
-     console.error('[useCanvasTransform] SharedArrayBuffer detected, WebGPU writeTexture will fail')
-     return
-   }
-   */
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bcbe39 and 7f5ffae.

📒 Files selected for processing (2)
  • src/composables/maskeditor/useCanvasHistory.ts
  • src/composables/maskeditor/useCanvasTransform.ts
🧰 Additional context used
📓 Path-based instructions (11)
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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.ts
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.ts
**/**/use[A-Z]*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Name composables using the pattern useXyz.ts

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.ts
🧠 Learnings (8)
📚 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} : Take advantage of `TypedArray` `subarray` when appropriate

Applied to files:

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

Applied to files:

  • src/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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/composables/maskeditor/useCanvasTransform.ts
  • src/composables/maskeditor/useCanvasHistory.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 : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

  • src/composables/maskeditor/useCanvasHistory.ts
⏰ 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: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (14)
src/composables/maskeditor/useCanvasTransform.ts (8)

1-8: LGTM: Clean composable structure.

The composable setup follows proper conventions with the store access at the top level.


12-56: Rotation logic is mathematically correct.

The coordinate transformations are accurate:

  • Clockwise 90°: (x,y) → (height-1-y, x)
  • Counter-clockwise 90°: (x,y) → (y, width-1-x)

The destination index calculation correctly uses height as the new row width since dimensions are swapped.

Per coding guidelines, prefer function declarations over function expressions for pure functions. However, since these are internal helpers with consistent patterns throughout the file, this is acceptable.


61-103: Mirror logic is correct.

The pixel mirroring transformations are accurate:

  • Horizontal: (x,y) → (width-1-x, y)
  • Vertical: (x,y) → (x, height-1-y)

The implementation maintains dimensions correctly and copies RGBA values properly.


108-115: Premultiplication logic is correct for GPU upload.

Alpha premultiplication is properly applied in-place. The division by 255 and multiplication correctly converts straight alpha to premultiplied alpha format required by GPU pipelines.


121-133: Context validation is properly implemented.

The guard clause correctly validates all required resources including maskCtx and rgbCtx, addressing the previous concern about non-null assertions.


183-227: Rotation flow is well-structured.

The method correctly:

  1. Validates all required canvases and contexts
  2. Captures original dimensions before transformation
  3. Rotates all three layers consistently
  4. Swaps canvas dimensions appropriately
  5. Applies rotated data back to canvases
  6. Triggers GPU texture recreation when needed
  7. Saves state to history for undo/redo support

232-264: Mirror flow correctly preserves dimensions.

Unlike rotation, mirroring maintains the original dimensions while transforming pixel data. The implementation correctly handles GPU texture updates (data changes, not dimensions) and history recording.


266-269: Minimal public API surface.

Only rotateAllLayers and mirrorAllLayers are exported, keeping internal helpers private. This aligns with the coding guideline to minimize the surface area of each module.

src/composables/maskeditor/useCanvasHistory.ts (6)

4-9: Well-defined state interface.

The CanvasState interface clearly documents the three-layer structure and supports both ImageData (for direct pixel manipulation) and ImageBitmap (for efficient storage/rendering).


77-132: Three-layer state management is correct.

The saveState function properly:

  1. Validates all required contexts and canvases
  2. Handles both provided data and fresh captures
  3. Prunes redo history when new state is saved
  4. Maintains max history size with proper cleanup

134-144: Simplified undo/redo with early returns.

The refactored undo and redo functions use clean early returns instead of alert dialogs, improving UX and code clarity.


174-188: Layer restoration handles both ImageBitmap and ImageData.

The forEach loop correctly distinguishes between ImageBitmap (requires clearRect + drawImage) and ImageData (uses putImageData). This supports efficient storage while maintaining full restoration capability.


190-194: Proper ImageBitmap cleanup prevents memory leaks.

The cleanupState helper correctly calls .close() on ImageBitmap instances to release GPU resources. This aligns with the coding guideline to clean up subscriptions and resources.


196-201: clearStates properly cleans up all history.

Using forEach(cleanupState) ensures all ImageBitmap resources are released before clearing the array, preventing memory leaks.

trsommer
trsommer previously approved these changes Jan 4, 2026
Copy link
Collaborator

@trsommer trsommer left a comment

Choose a reason for hiding this comment

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

works as described. Only thing I can see is that rotation and undo/redo buttons are very similar in design. That could lead to some confusion

@brucew4yn3rp
Copy link
Collaborator Author

brucew4yn3rp commented Jan 4, 2026

Thank you @trsommer. Just updated the rotate buttons with a different SVG for ease of interpretation.

image

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

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f5ffae and 1b03bd0.

📒 Files selected for processing (1)
  • src/components/maskeditor/dialog/TopBarHeader.vue
🧰 Additional context used
📓 Path-based instructions (12)
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/maskeditor/dialog/TopBarHeader.vue
src/**/*.{vue,ts}

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

src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json

Files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
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/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/components/maskeditor/dialog/TopBarHeader.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/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
**/*.{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/components/maskeditor/dialog/TopBarHeader.vue
**/*.{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/components/maskeditor/dialog/TopBarHeader.vue
🧠 Learnings (18)
📚 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/maskeditor/dialog/TopBarHeader.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 **/*.{ts,tsx,vue} : Implement proper error handling in code

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.{vue,ts} : Implement proper error handling

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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 : Leverage VueUse functions for performance-enhancing styles in Vue components

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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 defineExpose only for imperative operations (such as form.validate(), modal.open())

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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 : Utilize Vue 3's Teleport component when needed

Applied to files:

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

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
📚 Learning: 2025-12-11T12:25:15.470Z
Learnt from: christian-byrne
Repo: Comfy-Org/ComfyUI_frontend PR: 7358
File: src/components/dialog/content/signin/SignUpForm.vue:45-54
Timestamp: 2025-12-11T12:25:15.470Z
Learning: This repository uses CI automation to format code (pnpm format). Do not include manual formatting suggestions in code reviews for Comfy-Org/ComfyUI_frontend. If formatting issues are detected, rely on the CI formatter or re-run pnpm format. Focus reviews on correctness, readability, performance, accessibility, and maintainability rather than style formatting.

Applied to files:

  • src/components/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.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/maskeditor/dialog/TopBarHeader.vue
⏰ 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: lint-and-format
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: collect
🔇 Additional comments (2)
src/components/maskeditor/dialog/TopBarHeader.vue (2)

134-134: LGTM! Proper composition API usage.

The import and instantiation of useCanvasTransform follows the established pattern used for useCanvasTools.

Also applies to: 143-143


38-105: LGTM! Clean implementation of transform UI controls.

The rotate and mirror buttons are well-integrated, following the existing patterns for undo/redo buttons. The visual separators provide clear grouping, and the inline SVG icons are consistent with the component's style. All required i18n keys (maskEditor.rotateLeft, maskEditor.rotateRight, maskEditor.mirrorHorizontal, maskEditor.mirrorVertical) are present in the translation files.

Note: A past review comment suggested adding keyboard shortcuts. Per the PR description, keyboard shortcuts for these transform actions were added in src/extensions/core/maskEditor.ts, so that concern has been addressed.

@DrJKL DrJKL added the Design Used to request Product feedback on design decisions label Jan 6, 2026
</svg>
</button>

<div class="h-5 w-px bg-[var(--p-form-field-border-color)]" />
Copy link
Contributor

Choose a reason for hiding this comment

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

I need to add a linter or something to check for these.
Can you use the direct utilities instead of the [var(...)] ones?

Comment on lines +6 to +8
mask: ImageData | ImageBitmap
rgb: ImageData | ImageBitmap
img: ImageData | ImageBitmap
Copy link
Contributor

Choose a reason for hiding this comment

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

Any chance we can consolidate on a single form for these?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessarily in this PR, more just asking because you'd probably know if it's feasible.

// Update canvas dimensions to match state (handles rotation undo/redo)
const refData = state.mask
const newWidth =
refData instanceof ImageBitmap ? refData.width : refData.width
Copy link
Contributor

Choose a reason for hiding this comment

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

Why a ternary with identical branches?

const rotateCanvas = (
ctx: CanvasRenderingContext2D,
canvas: HTMLCanvasElement,
clockwise: boolean
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a general aversion to positional boolean arguments (including the unary ones down below)...

Since these aren't exposed, it's probably okay for now, though I wouldn't mind using an options object with a default or a string enum (also with a default).

For the AllLayers ones though, given the way they're separately bound and not dependent on some other boolean that's passed through, what do you think about just splitting them into rotateCanvasClockwise, rotateCanvasCounterclockwise for the consumers of the composable?

That way they don't need to worry about the fact that there are multiple layers to transform and also don't have to dive in to see which state the boolean's truthiness represents.


// Store the premultiplied data for useBrushDrawing to pick up
// Cast to ensure ArrayBuffer backing (not SharedArrayBuffer)
store.pendingGPUMaskData = maskData as Uint8ClampedArray & {
Copy link
Contributor

Choose a reason for hiding this comment

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

The casting just tricks the type checker. Is there a way to guarantee the type further up the chain?

@DrJKL
Copy link
Contributor

DrJKL commented Jan 6, 2026

I left a few questions and an optional plea for a change in the public signature of the composable, but it looks good. Works for me locally.
Waiting to approve until the tests are fixed 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Design Used to request Product feedback on design decisions size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants