Skip to content

Conversation

@simula-r
Copy link
Contributor

@simula-r simula-r commented Dec 9, 2025

Summary

Make image preview keyboard accessible, set the key listener on the node itself for more robust and intuitive handling, also add better aria labels.

Follow up PR: same on Video preview.

Changes

  • What: LGraphNode.vue, ImagePreview.vue
  • Breaking:
  • Dependencies:

┆Issue is synchronized with this Notion page by Unito

@simula-r simula-r requested a review from a team as a code owner December 9, 2025 00:04
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Dec 9, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

📝 Walkthrough

Walkthrough

LGraphNode now provides a reactive keyEvent shallowRef and captures keydown; ImagePreview injects and watches that ref, moves tabindex/role/ARIA and mouse handlers to an inner role="img" wrapper, adds focus state, aria-busy/aria-current, and hover/focus visibility for action buttons; tests updated accordingly.

Changes

Cohort / File(s) Summary
Keyboard & accessibility wiring
src/renderer/extensions/vueNodes/components/LGraphNode.vue, src/renderer/extensions/vueNodes/components/ImagePreview.vue
LGraphNode.vue adds tabindex="0", a keydown handler, and provides a shallowRef keyEvent. ImagePreview.vue injects and watches keyEvent to call handleKeyDown.
Template & interaction adjustments
src/renderer/extensions/vueNodes/components/ImagePreview.vue
Moved tabindex, role, ARIA, and mouse handlers to an inner image wrapper (ref="imageWrapperEl", role="img"); added aria-busy, role="alert" for errors, aria-current on navigation dots, isFocused state with focusin/focusout, and show action buttons on hover OR focus.
Tests updated
tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
Tests now target the inner image wrapper via [role="img"] and trigger mouseenter/mouseleave and focusin/focusout; added focus-related assertions and adjusted hover triggers.
sequenceDiagram
    autonumber
    participant User as User (keyboard / mouse)
    participant Node as LGraphNode
    participant Preview as ImagePreview

    User->>Node: keydown
    Node->>Node: handleNodeKeydown (update `keyEvent` shallowRef)
    Node-->>Preview: provided `keyEvent` updates (reactive)
    Preview->>Preview: watch(keyEvent) -> handleKeyDown (navigate images)

    User->>Preview: mouseenter / focusin on inner `role="img"` wrapper
    Preview->>Preview: set isHovered / isFocused -> show action buttons
    User->>Preview: mouseleave / focusout
    Preview->>Preview: clear isHovered / isFocused -> hide action buttons
Loading

Possibly related PRs

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/vue-nodes-image-preview-a11y

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

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 12/12/2025, 05:49:15 AM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

🎭 Playwright Test Results

⚠️ Tests passed with flaky tests

⏰ Completed at: 12/12/2025, 06:00:29 AM UTC

📈 Summary

  • Total Tests: 500
  • Passed: 488 ✅
  • Failed: 0
  • Flaky: 3 ⚠️
  • Skipped: 9 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 479 / ❌ 0 / ⚠️ 3 / ⏭️ 9
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 6 / ❌ 0 / ⚠️ 0 / ⏭️ 0

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

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

Bundle Size Report

Summary

  • Raw size: 17.1 MB baseline 17.1 MB — 🔴 +1.12 kB
  • Gzip: 3.39 MB baseline 3.39 MB — 🔴 +261 B
  • Brotli: 2.6 MB baseline 2.6 MB — 🔴 +189 B
  • Bundles: 97 current • 97 baseline • 39 added / 39 removed

Category Glance
Graph Workspace 🔴 +1.12 kB (983 kB) · Vendor & Third-Party ⚪ 0 B (8.56 MB) · Other ⚪ 0 B (3.81 MB) · App Entry Points ⚪ 0 B (3.24 MB) · Panels & Settings ⚪ 0 B (298 kB) · UI Components ⚪ 0 B (178 kB) · + 3 more

Per-category breakdown
App Entry Points — 3.24 MB (baseline 3.24 MB) • ⚪ 0 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-Cdc5UUz7.js (removed) 3.01 MB 🟢 -3.01 MB 🟢 -626 kB 🟢 -476 kB
assets/index-Dd2aFwPK.js (new) 3.01 MB 🔴 +3.01 MB 🔴 +626 kB 🔴 +476 kB
assets/index-BjatpvTM.js (removed) 224 kB 🟢 -224 kB 🟢 -47.8 kB 🟢 -39.4 kB
assets/index-HkNzVHJC.js (new) 224 kB 🔴 +224 kB 🔴 +47.8 kB 🔴 +39.5 kB
assets/index-BBMxpk-1.js (removed) 345 B 🟢 -345 B 🟢 -245 B 🟢 -234 B
assets/index-DdDFQxW0.js (new) 345 B 🔴 +345 B 🔴 +243 B 🔴 +200 B

Status: 3 added / 3 removed

Graph Workspace — 983 kB (baseline 982 kB) • 🔴 +1.12 kB

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-Bk8MkP6Z.js (new) 983 kB 🔴 +983 kB 🔴 +190 kB 🔴 +145 kB
assets/GraphView-INv5AJAg.js (removed) 982 kB 🟢 -982 kB 🟢 -190 kB 🟢 -145 kB

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/UserSelectView-BEkFz3cA.js (new) 6.54 kB 🔴 +6.54 kB 🔴 +2.14 kB 🔴 +1.9 kB
assets/UserSelectView-Dt0O6bbR.js (removed) 6.54 kB 🟢 -6.54 kB 🟢 -2.14 kB 🟢 -1.89 kB

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-DTBIta49.js (removed) 21.4 kB 🟢 -21.4 kB 🟢 -5.15 kB 🟢 -4.5 kB
assets/LegacyCreditsPanel-Khx5l0g0.js (new) 21.4 kB 🔴 +21.4 kB 🔴 +5.15 kB 🔴 +4.5 kB
assets/KeybindingPanel-BRRl0pto.js (removed) 13.6 kB 🟢 -13.6 kB 🟢 -3.42 kB 🟢 -3 kB
assets/KeybindingPanel-DO2kiguI.js (new) 13.6 kB 🔴 +13.6 kB 🔴 +3.42 kB 🔴 +3.01 kB
assets/ExtensionPanel-1wtCUVbX.js (removed) 10.8 kB 🟢 -10.8 kB 🟢 -2.57 kB 🟢 -2.26 kB
assets/ExtensionPanel-D7kGw00C.js (new) 10.8 kB 🔴 +10.8 kB 🔴 +2.57 kB 🔴 +2.25 kB
assets/AboutPanel-CaVR7cXJ.js (new) 9.16 kB 🔴 +9.16 kB 🔴 +2.46 kB 🔴 +2.21 kB
assets/AboutPanel-Cq0jsoqI.js (removed) 9.16 kB 🟢 -9.16 kB 🟢 -2.46 kB 🟢 -2.21 kB
assets/ServerConfigPanel-BMyDoUMA.js (new) 6.56 kB 🔴 +6.56 kB 🔴 +1.83 kB 🔴 +1.63 kB
assets/ServerConfigPanel-C72Jafwp.js (removed) 6.56 kB 🟢 -6.56 kB 🟢 -1.83 kB 🟢 -1.63 kB
assets/UserPanel-BJ-6d_OG.js (new) 6.23 kB 🔴 +6.23 kB 🔴 +1.72 kB 🔴 +1.5 kB
assets/UserPanel-CHXNvDk1.js (removed) 6.23 kB 🟢 -6.23 kB 🟢 -1.72 kB 🟢 -1.5 kB
assets/settings-BhbWhsRg.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BXTtSH4O.js 33.3 kB 33.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C9Pzn-NG.js 25.2 kB 25.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CCy2fA_h.js 27.3 kB 27.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CQpqEFfl.js 26.6 kB 26.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DHcnxypw.js 21.7 kB 21.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DhFTK9fY.js 25.1 kB 25.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DlT4t_ui.js 25.9 kB 25.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DRgSrIdD.js 24.2 kB 24.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-tjkeqiZq.js 21.1 kB 21.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

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

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/Load3D.vue_vue_type_script_setup_true_lang-B9zI02UZ.js (new) 53.7 kB 🔴 +53.7 kB 🔴 +8.48 kB 🔴 +7.29 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-CwYazDRM.js (removed) 53.7 kB 🟢 -53.7 kB 🟢 -8.48 kB 🟢 -7.29 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-Dzmy73Ih.js (removed) 48.1 kB 🟢 -48.1 kB 🟢 -10.4 kB 🟢 -8.98 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-xFMs2DJY.js (new) 48.1 kB 🔴 +48.1 kB 🔴 +10.4 kB 🔴 +8.99 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-B7VboNlp.js (new) 48 kB 🔴 +48 kB 🔴 +10.6 kB 🔴 +9.32 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-Crwf_bOG.js (removed) 48 kB 🟢 -48 kB 🟢 -10.6 kB 🟢 -9.32 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-BZKXaFLb.js (removed) 12.9 kB 🟢 -12.9 kB 🟢 -3.37 kB 🟢 -2.97 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-Eu0Y8gIf.js (new) 12.9 kB 🔴 +12.9 kB 🔴 +3.37 kB 🔴 +2.98 kB
assets/ComfyQueueButton-BkYq58pL.js (removed) 8.44 kB 🟢 -8.44 kB 🟢 -2.48 kB 🟢 -2.22 kB
assets/ComfyQueueButton-h9zPHujO.js (new) 8.44 kB 🔴 +8.44 kB 🔴 +2.48 kB 🔴 +2.21 kB
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-aNdkKFgo.js (new) 2.15 kB 🔴 +2.15 kB 🔴 +892 B 🔴 +769 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-BMFqR1lj.js (removed) 2.15 kB 🟢 -2.15 kB 🟢 -895 B 🟢 -766 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-CmjqdPx_.js (new) 897 B 🔴 +897 B 🔴 +499 B 🔴 +434 B
assets/MediaTitle.vue_vue_type_script_setup_true_lang-CQJbV6Pa.js (removed) 897 B 🟢 -897 B 🟢 -502 B 🟢 -435 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-HWlLovsu.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetButton-CMNwFzv0.js 2.04 kB 2.04 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 7 added / 7 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-CbpuMHRF.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/keybindingService-DhzJNufm.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/audioService-aGijY9cy.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -960 B 🟢 -821 B
assets/audioService-N6IM-kVg.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +960 B 🔴 +820 B
assets/serverConfigStore-bHPLOH2-.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

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

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-D30gfQsy.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -650 B 🟢 -547 B
assets/audioUtils-DtAVtuxc.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +650 B 🔴 +549 B
assets/mathUtil-CD4DsosH.js 1.32 kB 1.32 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeFilterUtil-CXKCRJ-m.js 460 B 460 B ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 1 added / 1 removed

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

External libraries and shared vendor chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/vendor-chart-VUOgEeCi.js 452 kB 452 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-other-Deurr33o.js 3.98 MB 3.98 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-primevue-DO99B366.js 1.96 MB 1.96 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-three-aR6ntw5X.js 1.37 MB 1.37 MB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-tiptap-jlp9Zr6M.js 232 kB 232 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-vue-DQYuPKbl.js 160 kB 160 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/vendor-xterm-BZLod3g9.js 407 kB 407 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
Other — 3.81 MB (baseline 3.81 MB) • ⚪ 0 B

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/WidgetRecordAudio-CR2cmLSx.js (new) 20.4 kB 🔴 +20.4 kB 🔴 +5.23 kB 🔴 +4.62 kB
assets/WidgetRecordAudio-ty8r-_DZ.js (removed) 20.4 kB 🟢 -20.4 kB 🟢 -5.23 kB 🟢 -4.62 kB
assets/AudioPreviewPlayer-BFZV2QIt.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.37 kB 🔴 +3.01 kB
assets/AudioPreviewPlayer-DsD5zsit.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.37 kB 🟢 -3.01 kB
assets/WidgetGalleria-CH07UMyj.js (removed) 4.1 kB 🟢 -4.1 kB 🟢 -1.45 kB 🟢 -1.3 kB
assets/WidgetGalleria-Dc3NBAU7.js (new) 4.1 kB 🔴 +4.1 kB 🔴 +1.45 kB 🔴 +1.3 kB
assets/WidgetColorPicker-BCbSsG62.js (new) 3.41 kB 🔴 +3.41 kB 🔴 +1.38 kB 🔴 +1.23 kB
assets/WidgetColorPicker-QfokDQEA.js (removed) 3.41 kB 🟢 -3.41 kB 🟢 -1.38 kB 🟢 -1.23 kB
assets/WidgetTextarea-D8acsjQ7.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.21 kB 🟢 -1.07 kB
assets/WidgetTextarea-SQM8eYrv.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.21 kB 🔴 +1.08 kB
assets/WidgetMarkdown-D4OGh65w.js (removed) 3.08 kB 🟢 -3.08 kB 🟢 -1.28 kB 🟢 -1.12 kB
assets/WidgetMarkdown-sZwOOHOU.js (new) 3.08 kB 🔴 +3.08 kB 🔴 +1.28 kB 🔴 +1.12 kB
assets/WidgetAudioUI-BrSLqxvX.js (new) 2.85 kB 🔴 +2.85 kB 🔴 +1.16 kB 🔴 +1.05 kB
assets/WidgetAudioUI-D41jSkHn.js (removed) 2.85 kB 🟢 -2.85 kB 🟢 -1.16 kB 🟢 -1.05 kB
assets/WidgetInputText-Dv1Ruqv3.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -918 B 🟢 -844 B
assets/WidgetInputText-qecsbltP.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +915 B 🔴 +844 B
assets/WidgetToggleSwitch-BNoXNrDO.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +831 B 🔴 +731 B
assets/WidgetToggleSwitch-CIg58V3I.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -834 B 🟢 -734 B
assets/MediaImageBottom-C62w19on.js (new) 1.55 kB 🔴 +1.55 kB 🔴 +733 B 🔴 +640 B
assets/MediaImageBottom-RAa7bHLZ.js (removed) 1.55 kB 🟢 -1.55 kB 🟢 -737 B 🟢 -642 B
assets/MediaAudioBottom-B1NE13xM.js (removed) 1.51 kB 🟢 -1.51 kB 🟢 -731 B 🟢 -649 B
assets/MediaAudioBottom-C0QoD83E.js (new) 1.51 kB 🔴 +1.51 kB 🔴 +732 B 🔴 +649 B
assets/Media3DBottom-BmpYMigk.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +728 B 🔴 +649 B
assets/Media3DBottom-Dv_meZCH.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -731 B 🟢 -654 B
assets/MediaVideoBottom-B4naITkI.js (removed) 1.5 kB 🟢 -1.5 kB 🟢 -732 B 🟢 -645 B
assets/MediaVideoBottom-BwF6ALeJ.js (new) 1.5 kB 🔴 +1.5 kB 🔴 +730 B 🔴 +647 B
assets/Media3DTop-2wcJXnYT.js (new) 1.49 kB 🔴 +1.49 kB 🔴 +762 B 🔴 +650 B
assets/Media3DTop-BHEFIE5J.js (removed) 1.49 kB 🟢 -1.49 kB 🟢 -764 B 🟢 -648 B
assets/WidgetSelect-f_7PEq5s.js (removed) 655 B 🟢 -655 B 🟢 -341 B 🟢 -288 B
assets/WidgetSelect-gKLdfQ9n.js (new) 655 B 🔴 +655 B 🔴 +340 B 🔴 +285 B
assets/WidgetInputNumber-BAbibVnZ.js (removed) 595 B 🟢 -595 B 🟢 -328 B 🟢 -273 B
assets/WidgetInputNumber-DkQuHRAQ.js (new) 595 B 🔴 +595 B 🔴 +327 B 🔴 +273 B
assets/Load3D-_YlH1OQT.js (new) 424 B 🔴 +424 B 🔴 +266 B 🔴 +222 B
assets/Load3D-B-8gKuNy.js (removed) 424 B 🟢 -424 B 🟢 -264 B 🟢 -222 B
assets/WidgetLegacy-D3FtwQpy.js (removed) 364 B 🟢 -364 B 🟢 -234 B 🟢 -195 B
assets/WidgetLegacy-DPSHZRGY.js (new) 364 B 🔴 +364 B 🔴 +235 B 🔴 +194 B
assets/commands-_s-RvhJR.js 13.6 kB 13.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BuUILW6P.js 13 kB 13 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BV4R6fLx.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BWp4HdfU.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CLwPdnT6.js 14.2 kB 14.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CWMchBmd.js 15.9 kB 15.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DazTQhtc.js 12.9 kB 12.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DmWrOe93.js 13.7 kB 13.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DwiH7Kr6.js 13.8 kB 13.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-mS3LCNPn.js 14.5 kB 14.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BMi-Aksj.js 99 kB 99 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CqR8skJT.js 73.1 kB 73.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Cw9RZWRY.js 89 B 89 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DcRHAFEy.js 81.7 kB 81.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DdFdLxku.js 72.2 kB 72.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DJAtuVu5.js 84.3 kB 84.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DK8I9Rk3.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-gP_ssnMb.js 83.4 kB 83.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-nxXY9vGp.js 94 kB 94 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-Ycd3gqkA.js 86.5 kB 86.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-D1WKsNUf.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-B2BN_vpA.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-B8jmaGRE.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BC3OlaIn.js 342 kB 342 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-BsqN8-W1.js 285 kB 285 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Bw_Jitw_.js 101 B 101 B ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CK2saYDx.js 307 kB 307 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cm5kR4Hi.js 306 kB 306 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CMrh-uxB.js 310 kB 310 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DlUIOit1.js 369 kB 369 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DNu_xoP2.js 282 kB 282 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DpcvlpZe.js 303 kB 303 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-IyjOYIl-.js 317 kB 317 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BH0eLeWx.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-mYznCAm6.js 2.21 kB 2.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 18 added / 18 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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/extensions/vueNodes/components/ImagePreview.vue (1)

268-297: Keyboard navigation works well, but action buttons lack keyboard access.

The arrow key and Home/End navigation is implemented correctly. However, the floating action buttons (Download, Edit/Mask, Remove) only appear on hover and have no keyboard shortcuts or focus management. This creates an accessibility barrier for keyboard-only users.

Consider one of these solutions:

  1. Always show action buttons when the image wrapper is focused (not just on hover):
-    <div v-if="isHovered" class="actions absolute top-2 right-2 flex gap-2.5">
+    <div v-if="isHovered || isFocused" class="actions absolute top-2 right-2 flex gap-2.5">

And track focus state:

const isFocused = ref(false)
// In template on wrapper: @focus="isFocused = true" @blur="isFocused = false"
  1. Add keyboard shortcuts for common actions (e.g., 'd' for download, 'Delete' for remove) in handleKeyDown.

  2. Make action buttons always visible but styled less prominently, becoming more prominent on hover/focus.

Option 1 is the most straightforward and aligns with accessibility best practices.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 418f8ff and 5127b3f.

📒 Files selected for processing (3)
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue (4 hunks)
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue (6 hunks)
  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
tests-ui/**/*.test.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)

tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively; no new JavaScript code

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,vue}: Use camelCase for variable and function names
Indent with 2 spaces (see .prettierrc)
Use single quotes for strings (see .prettierrc)
No trailing semicolons (see .prettierrc)
Maximum line width of 80 characters (see .prettierrc)
Sort and group imports by plugin (run pnpm format before committing)
Never use any type; use proper TypeScript types instead
Never use as any type assertions; fix the underlying type issue instead
Avoid code comments unless absolutely necessary; write expressive, self-documenting code instead
When writing new code, ask if there is a simpler way to introduce the same functionality; if yes, choose the simpler approach
Use refactoring to make complex code simpler
Use es-toolkit for utility functions
Use Vite for fast development and building
Implement proper error handling
Write tests for all changes, especially bug fixes to catch future regressions

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Avoid writing change detector tests that just assert default values
Avoid writing tests dependent on non-behavioral features like utility classes or styles
Avoid writing redundant tests

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3 SFCs with Composition API only (use <script setup lang="ts">, not Options API)
Avoid using <style> blocks; use Tailwind 4 for all styling
Use defineProps with TypeScript style default declaration; do not use withDefaults or runtime props declaration
Prefer useModel over separately defining a prop and emit
Use computed instead of a ref and watch if possible
Avoid using ref if a prop would accomplish the design goals; avoid using computed if a ref or prop directly would work
Do not import Vue macros unnecessarily
Never use the dark: Tailwind variant; use semantic values from the style.css theme instead (e.g., bg-node-component-surface)
Never use :class="[]" to merge class names; always use cn() from @/utils/tailwindUtil (e.g., <div :class="cn('text-node-component-header-icon', hasError && 'text-danger')" />)
Leverage VueUse functions for performance-enhancing styles
Avoid new usage of PrimeVue components
Use Vue 3 Teleport component when needed
Use Vue 3 Suspense for async components

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/components/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
🧠 Learnings (26)
📓 Common learnings
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 provide/inject for dependency injection in Vue components
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
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 provide/inject for dependency injection
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 : Prefer emit/event-name for state changes over other communication patterns
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch
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
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Define proper props and emits definitions in Vue components
📚 Learning: 2025-12-06T00:52:35.750Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T00:52:35.750Z
Learning: Applies to **/*.{ts,vue} : Write tests for all changes, especially bug fixes to catch future regressions

Applied to files:

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

Applied to files:

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

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Prefer specific selectors in browser tests

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilities

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.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/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-06T00:52:35.750Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T00:52:35.750Z
Learning: Applies to **/*.vue : Use `computed` instead of a `ref` and `watch` if possible

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use setup() function for component logic

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.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 lifecycle hooks: onMounted, onUpdated in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-06T00:52:35.750Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T00:52:35.750Z
Learning: Applies to **/*.vue : Avoid using `ref` if a prop would accomplish the design goals; avoid using `computed` if a `ref` or prop directly would work

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.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 provide/inject for dependency injection in Vue components

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Sidebar component with Drawer

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-06T00:52:35.750Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T00:52:35.750Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/ImagePreview.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 Teleport/Suspense when needed for component rendering

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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). (5)
  • GitHub Check: deploy-and-comment
  • GitHub Check: test
  • GitHub Check: setup
  • GitHub Check: lint-and-format
  • GitHub Check: collect
🔇 Additional comments (9)
src/renderer/extensions/vueNodes/components/LGraphNode.vue (5)

8-8: LGTM! Good accessibility enhancement.

Adding tabindex="0" makes nodes keyboard-navigable, and using focus-visible ensures the outline only appears for keyboard focus, not mouse clicks. This follows accessibility best practices.

Also applies to: 20-20


286-286: LGTM! Good UX pattern.

Focusing the node container on pointer down enables immediate keyboard interaction after clicking, which improves the user experience and accessibility.


52-52: LGTM! Correctly wired keyboard event handler.

The @keydown listener is appropriately placed on the focusable container, and the handler propagates events to descendants via the provided keyEvent ref.


135-144: LGTM! Necessary imports for keyboard accessibility.

The provide and shallowRef imports are required for the new keyboard event propagation mechanism and are correctly imported from Vue.


211-216: Consider resetting keyEvent to null after processing for clearer lifecycle management.

The keyEvent ref is set on each keydown but never reset. While ImagePreview.vue (the consumer of this injected value) defensively checks if (!e) return before handling the event, the pattern would be more predictable if keyEvent explicitly reset to null after each event. This would clarify that the ref represents the "current" event rather than a persistent reference, and would prevent potential confusion if additional consumers are added in the future.

tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts (1)

110-112: LGTM! Tests correctly updated to match component changes.

The tests now target the inner element with role="img" for hover interactions, which accurately reflects the component's new structure where hover handlers were moved to the inner wrapper. The explanatory comment on line 110 is helpful for maintainability.

Also applies to: 127-127, 130-130, 135-135, 143-143, 155-155, 169-169, 188-188

src/renderer/extensions/vueNodes/components/ImagePreview.vue (3)

19-19: LGTM! Proper use of ARIA alert role.

Using role="alert" for the error state ensures screen readers announce the error immediately, which is the correct accessibility pattern for critical error messages.


108-108: LGTM! Correct aria-current usage.

The conditional aria-current attribute properly indicates the active navigation dot to screen readers, using 'true' when active and undefined (omitting the attribute) when inactive.


124-171: LGTM! Correct keyboard event injection pattern.

The component properly injects the keyEvent from the parent node and watches for changes to handle keyboard navigation. The defensive check for keyEvent existence and null values is appropriate for cases where the component might be used outside of LGraphNode.

Based on learnings, use provide/inject for dependency injection in Vue components.

@simula-r simula-r force-pushed the fix/vue-nodes-image-preview-a11y branch from 5127b3f to 5b3bbee Compare December 9, 2025 02:47
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Dec 9, 2025
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/extensions/vueNodes/components/ImagePreview.vue (1)

287-316: Add stopPropagation() to handle keydown events consistently with the codebase pattern.

The handleKeyDown function calls preventDefault() on arrow key, Home, and End events but not stopPropagation(). This inconsistency should be fixed—other event handlers throughout the codebase (e.g., useMarkdownWidget.ts, useNodeResize.ts) consistently call both methods together to prevent unintended side effects from event bubbling. Add event.stopPropagation() alongside each event.preventDefault() call.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5127b3f and 1cce524.

📒 Files selected for processing (2)
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue (6 hunks)
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue (5 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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

src/**/*.{ts,tsx,vue}: Write expressive and self-documenting code to reduce the need for comments; clean redundant comments as you go
Consider if there is a simpler way to introduce functionality before writing code; choose the simpler approach when possible
Use refactoring to make complex code simpler
Keep functions short and functional
Minimize nesting in code (e.g., if () { ... } or for () { ... }) to avoid arrow anti-pattern
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions (especially testable ones)
Watch out for code smells and refactor to avoid them
Implement proper error handling in code
If a complex type definition is inlined in multiple related places, extract and name it for reuse

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.{js,ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript exclusively (no new JavaScript)

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3 SFCs with Composition API only (.vue files)
Use Tailwind 4 styling and avoid <style> blocks in Vue components
Use <script setup lang="ts"> for component logic in Vue components
Use Vue 3.5 TypeScript style of default prop declaration with reactive props destructuring
Do not use withDefaults or runtime props declaration in Vue components
Prefer useModel to separately defining a prop and emit in Vue components
Use ref for reactive state in Vue components
Implement computed properties with computed() instead of using ref and watch
Use watch and watchEffect for side effects in Vue components
Use provide/inject for dependency injection only when simpler alternatives (Store or composable) are not suitable
Do not use the dark: Tailwind variant; use semantic values from style.css theme instead (e.g., bg-node-component-surface)
Use import { cn } from '@/utils/tailwindUtil' to merge class names instead of :class="[]"
Avoid new usage of PrimeVue components
Use VueUse functions for performance-enhancing styles in Vue components
Do not import Vue macros unnecessarily in components
Be judicious with addition of new refs or other state; prefer using props or composables when possible
Do not add a computed if it's possible to use a ref or prop directly
Do not use a watch if a computed would work instead
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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Do not use any type in TypeScript code
Do not use as any type assertions in TypeScript code; fix the underlying type issue
Style formatting: 2 space indent, single quotes, no trailing semicolons, 80 character width
Import statements should be sorted and grouped by plugin; run pnpm format before committing
ESLint rules: no floating promises, no unused imports, i18n raw text restrictions in templates

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use vue-i18n in Composition API for string literals and place new translation entries in src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
🧠 Learnings (18)
📚 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/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Use `<script setup lang="ts">` for component logic in Vue components

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Use `ref` for reactive state in Vue components

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to src/stores/*Store.ts : Name Pinia stores as `*Store.ts`

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Implement computed properties with `computed()` instead of using `ref` and `watch`

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Be judicious with addition of new refs or other state; prefer using props or composables when possible

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue Steps component with Stepper without panels

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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 Teleport/Suspense when needed for component rendering

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T02:44:38.234Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T02:44:38.234Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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). (3)
  • GitHub Check: setup
  • GitHub Check: test
  • GitHub Check: lint-and-format
🔇 Additional comments (11)
src/renderer/extensions/vueNodes/components/LGraphNode.vue (4)

8-8: LGTM!

Adding tabindex="0" makes the node container keyboard-focusable, which is essential for accessibility.


20-20: LGTM!

The updated focus-visible styling using outline-node-component-outline follows the semantic design token approach. As per coding guidelines, this correctly avoids the dark: Tailwind variant.


52-52: LGTM!

Wiring the @keydown handler to propagate keyboard events to descendants via the provided keyEvent is a clean approach for accessibility.


135-144: LGTM!

Imports are correctly updated to include provide and shallowRef for the new keyboard event propagation mechanism.

src/renderer/extensions/vueNodes/components/ImagePreview.vue (7)

7-18: Good accessibility improvements on the image wrapper.

Adding role="img", aria-label, aria-busy, and focus event handlers significantly improves keyboard accessibility. The focusin/focusout events (which bubble) are correctly used instead of focus/blur to handle focus on nested elements.


20-23: LGTM!

Adding role="alert" to the error state ensures screen readers announce errors appropriately.


58-62: LGTM!

Showing action buttons on both hover and focus ensures keyboard users can access them when the element is focused.


114-114: LGTM!

Using aria-current="true" on the active navigation dot follows WAI-ARIA best practices for indicating the current item in a set.


130-131: LGTM!

Imports correctly updated for the new inject-based keyboard event handling.


267-276: LGTM!

The focus handling correctly distinguishes between focus moving within the wrapper vs. leaving entirely using contains() check on relatedTarget. This prevents premature hiding of action buttons when focus moves between buttons.


172-179: The current implementation is correct; no changes needed.

Each keyboard event from the DOM is a unique object instance, so the watch will fire once per distinct event. The null guard properly handles the initial state. There is no scenario in this codebase where the same KeyboardEvent object reference is assigned twice, making the suggested duplicate-processing concern unfounded.

Likely an incorrect or invalid review comment.

@christian-byrne christian-byrne added preview area:vue-migration area:accessibility Accessibility (a11y) issues or improvements labels Dec 9, 2025
Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

LGTM. There's also these but not sure if they work well for this case

// from vueuse
useFocus(imageWrapperEl, { /* track inside */ })
useMouseInElement(imageWrapperEl)

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/renderer/extensions/vueNodes/components/LGraphNode.vue (1)

8-22: Keyboard focus + keyEvent provide pattern looks good; clearing keyEvent remains optional

Making the node container focusable with tabindex="0", adding a focus-visible outline, and wiring @keydown="handleNodeKeydown" to update a provided shallowRef<KeyboardEvent | null> is a solid way to expose keyboard events to descendants like ImagePreview.

One minor nuance (already raised in a prior review): keyEvent is set but never cleared. It’s not incorrect, but if you ever need to avoid reprocessing stale events or want to make GC behavior more explicit, you could clear it after consumers run (e.g., via queueMicrotask or by exposing a “consume” helper). Since this was discussed before and is low impact, it’s fine to leave as-is unless you start seeing duplicated handling downstream.

Also applies to: 53-53, 212-217

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cce524 and e674108.

📒 Files selected for processing (3)
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue (7 hunks)
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue (5 hunks)
  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
tests-ui/**/*.test.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (tests-ui/CLAUDE.md)

tests-ui/**/*.test.{js,ts,jsx,tsx}: Write tests for new features
Follow existing test patterns in the codebase
Use existing test utilities rather than writing custom utilities
Mock external dependencies in tests
Always prefer vitest mock functions over writing verbose manual mocks

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
**/*.{ts,tsx,js,jsx,vue,json}

📄 CodeRabbit inference engine (AGENTS.md)

Code style: Use 2-space indentation, single quotes, no trailing semicolons, and 80-character line width (see .prettierrc)

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; run pnpm format before committing
Use TypeScript for type safety; never use any type - use proper TypeScript types
Never use as any type assertions; fix the underlying type issue instead
Use es-toolkit for utility functions
Write code that is expressive and self-documenting; avoid comments unless absolutely necessary; do not add or retain redundant comments
Keep functions short and functional
Minimize nesting in code (e.g., deeply nested if or for statements); apply the Arrow Anti-Pattern principle
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.ts: Write tests for all changes, especially bug fixes to catch future regressions
Unit/component test files must be named **/*.test.ts or in tests-ui/ directory
Do not write change detector tests that just assert default values
Do not write tests dependent on non-behavioral features like utility classes or styles
Be parsimonious in testing; do not write redundant tests; see composable tests approach
Follow 'Don't Mock What You Don't Own' principle - avoid mocking external dependencies
Do not write tests that just test the mocks; ensure tests fail when code behaves unexpectedly
Leverage Vitest's mocking utilities where possible for test mocking
Keep module mocks contained in test files; do not use global mutable state within test files; use vi.hoisted() if necessary
For Component testing, use Vue Test Utils and follow advice about making components easy to test
Aim for behavioral coverage of critical and new features in unit tests

Files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3 SFCs (Single File Components) with Composition API only; do not use Options API
Vue components must use <script setup lang="ts"> for component logic
Use Vue 3.5 TypeScript style for default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer useModel to separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using <style> blocks in Vue components
Use semantic Tailwind values from style.css theme instead of the dark: variant; for example, use bg-node-component-surface instead of dark: prefixes
Always use cn() utility from @/utils/tailwindUtil to merge Tailwind class names; do not use :class="[]" syntax
Use ref for reactive state in Vue Composition API components
Implement computed properties with computed() from Vue; avoid using a ref with a watch if a computed would work instead
Use watch and watchEffect for side effects in Vue components
Implement lifecycle hooks using onMounted, onUpdated, and other Vue lifecycle functions
Use provide/inject for dependency injection; do not use dependency injection if a Store or shared composable would be simpler
Do not import Vue macros unnecessarily; only use when needed
Be judicious with addition of new refs or other state: prefer props, avoid redundant computed, and prefer computed over watch
Use VueUse functions for performance-enhancing styles
In Vue Components, implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Implement proper error handling in Vue components
Follow Vue 3 style guide and naming conventions
Use vue-i18n in composition API for any string literals; place new translation entries in src/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
🧠 Learnings (32)
📚 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:

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

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:22.909Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: browser_tests/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:22.909Z
Learning: Applies to browser_tests/**/*.{e2e,spec}.{ts,tsx,js,jsx} : Prefer specific selectors in browser tests

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Mock external dependencies in tests

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.{vue,ts,js} : Use useIntersectionObserver for visibility detection instead of custom scroll handlers

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Follow existing test patterns in the codebase

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : For Component testing, use Vue Test Utils and follow advice about making components easy to test

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.test.ts : Keep module mocks contained in test files; do not use global mutable state within test files; use `vi.hoisted()` if necessary

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue InputSwitch component with ToggleSwitch

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: tests-ui/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:48:03.270Z
Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Always prefer vitest mock functions over writing verbose manual mocks

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.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:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-12-10T03:09:13.807Z
Learnt from: DrJKL
Repo: Comfy-Org/ComfyUI_frontend PR: 7303
File: src/components/topbar/CurrentUserPopover.test.ts:199-205
Timestamp: 2025-12-10T03:09:13.807Z
Learning: In test files, prefer selecting or asserting on accessible properties (text content, aria-label, role, accessible name) over data-testid attributes. This ensures tests validate actual user-facing behavior and accessibility, reducing reliance on implementation details like test IDs.

Applied to files:

  • tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Be judicious with addition of new refs or other state: prefer props, avoid redundant `computed`, and prefer `computed` over `watch`

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use `ref` for reactive state in Vue Composition API components

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use `provide`/`inject` for dependency injection; do not use dependency injection if a Store or shared composable would be simpler

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Vue components must use `<script setup lang="ts">` for component logic

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Implement computed properties with `computed()` from Vue; avoid using a `ref` with a `watch` if a `computed` would work instead

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/LGraphNode.vue
  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Replace PrimeVue OverlayPanel component with Popover

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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 Teleport/Suspense when needed for component rendering

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Utilize Vue 3's Teleport component when needed

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.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/renderer/extensions/vueNodes/components/ImagePreview.vue
📚 Learning: 2025-11-24T19:47:02.860Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-24T19:47:02.860Z
Learning: Applies to src/**/*.vue : Use Suspense for async components

Applied to files:

  • src/renderer/extensions/vueNodes/components/ImagePreview.vue
🪛 GitHub Actions: CI: Size Data
src/renderer/extensions/vueNodes/components/ImagePreview.vue

[error] 13-13: TS2339: Property 'isLoading' does not exist on type 'CreateComponentPublicInstanceWithMixins<ToResolvedProps<ImagePreviewProps, {}>, { Skeleton: DefineComponent<SkeletonProps, SkeletonSlots, (e: string, ...args: any[]) => void>; ... 23 more ...; getImageFilename: (url: string) => string; }, ... 23 more ..., {}>'.

🪛 GitHub Check: collect
src/renderer/extensions/vueNodes/components/ImagePreview.vue

[failure] 13-13:
Property 'isLoading' does not exist on type 'CreateComponentPublicInstanceWithMixins<ToResolvedProps<ImagePreviewProps, {}>, { Skeleton: DefineComponent<SkeletonProps, SkeletonSlots, (e: string, ...args: any[]) => void>; ... 23 more ...; getImageFilename: (url: string) => string; }, ... 23 more ..., {}>'.

⏰ 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: setup
  • GitHub Check: test
🔇 Additional comments (2)
tests-ui/tests/renderer/extensions/vueNodes/components/ImagePreview.test.ts (1)

104-138: Tests correctly target the image wrapper and cover hover/focus behavior

Updating the tests to interact with the inner element via [role="img"] and adding focusin / focusout coverage for showing/hiding the action buttons aligns well with the new a11y model and uses accessible selectors instead of brittle structural hooks. The hover and focus flows being exercised here match the component’s behavior and should guard against regressions.

Also applies to: 140-168, 170-186, 194-200, 213-219

src/renderer/extensions/vueNodes/components/ImagePreview.vue (1)

8-17: A11y improvements on wrapper, error state, and navigation dots look solid

Binding the inner wrapper as the primary interactive surface (tabindex="0", role="img", i18n-based aria-label, and aria-busy) plus using role="alert" for the error block and aria-current on the active navigation dot significantly improves screen-reader semantics. Tying the action buttons’ visibility to isHovered || isFocused also correctly supports both mouse and keyboard users.

Also applies to: 50-54, 88-99, 100-107

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/extensions/vueNodes/components/LGraphNode.vue (1)

5-53: Avoid making every node tabbable; consider roving tabindex (selected/focused-only) + add an accessible role/label.
tabindex="0" on each node can create a huge tab-stop list on complex graphs, and it’s unclear what SR users will hear when landing on this div.

Suggested direction:

  • Make only the “active/selected” node tabbable (others tabindex="-1"), and/or focus programmatically on selection.
  • Add a semantic role + label (e.g., role="group" and an aria-label derived from the node title, via i18n).

Example (shape only; adjust to your selection/focus model and existing i18n keys):

-    tabindex="0"
+    :tabindex="isSelected ? 0 : -1"
+    role="group"
+    :aria-label="/* i18n-backed node label, e.g. node title */"
♻️ Duplicate comments (1)
src/renderer/extensions/vueNodes/components/LGraphNode.vue (1)

211-216: (Duplicate) Clear/consume keyEvent and use a typed InjectionKey instead of a string key.

  • The “clear after dispatch” point was raised before; still worth addressing if consumers watch() it.
  • Also, provide('keyEvent', ...) is collision-prone and untyped across files; prefer InjectionKey (Symbol) for safer DI.

If you keep the ref approach, consider clearing after a microtask (or expose a consumeKeyEvent() via provide).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28380fc and 85bf28d.

📒 Files selected for processing (1)
  • src/renderer/extensions/vueNodes/components/LGraphNode.vue (5 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
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/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/{composables,components}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Clean up subscriptions in state management to prevent memory leaks

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Follow Vue 3 composition API style guide

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (src/CLAUDE.md)

Use vue-i18n for ALL user-facing strings by adding them to src/locales/en/main.json

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use Vue 3 SFCs (Single File Components) with Composition API only; do not use Options API
Vue components must use <script setup lang="ts"> for component logic
Use Vue 3.5 TypeScript style for default prop declaration with reactive props destructuring; do not use withDefaults or runtime props declaration
Prefer useModel to separately defining a prop and emit
Use Tailwind 4 utility classes for styling; avoid using <style> blocks in Vue components
Use semantic Tailwind values from style.css theme instead of the dark: variant; for example, use bg-node-component-surface instead of dark: prefixes
Always use cn() utility from @/utils/tailwindUtil to merge Tailwind class names; do not use :class="[]" syntax
Use ref for reactive state in Vue Composition API components
Implement computed properties with computed() from Vue; avoid using a ref with a watch if a computed would work instead
Use watch and watchEffect for side effects in Vue components
Implement lifecycle hooks using onMounted, onUpdated, and other Vue lifecycle functions
Use provide/inject for dependency injection; do not use dependency injection if a Store or shared composable would be simpler
Do not import Vue macros unnecessarily; only use when needed
Be judicious with addition of new refs or other state: prefer props, avoid redundant computed, and prefer computed over watch
Use VueUse functions for performance-enhancing styles
In Vue Components, implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Implement proper error handling in Vue components
Follow Vue 3 style guide and naming conventions
Use vue-i18n in composition API for any string literals; place new translation entries in src/locales/en/main.json
Avoid new usage of PrimeVue components; prefer shadcn/vue or Reka UI instead

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
**/*.{ts,tsx,js,jsx,vue,json}

📄 CodeRabbit inference engine (AGENTS.md)

Code style: Use 2-space indentation, single quotes, no trailing semicolons, and 80-character line width (see .prettierrc)

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Imports must be sorted and grouped by plugin; run pnpm format before committing
Use TypeScript for type safety; never use any type - use proper TypeScript types
Never use as any type assertions; fix the underlying type issue instead
Use es-toolkit for utility functions
Write code that is expressive and self-documenting; avoid comments unless absolutely necessary; do not add or retain redundant comments
Keep functions short and functional
Minimize nesting in code (e.g., deeply nested if or for statements); apply the Arrow Anti-Pattern principle
Avoid mutable state; prefer immutability and assignment at point of declaration
Favor pure functions, especially testable ones

Files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
🧠 Learnings (16)
📓 Common learnings
Learnt from: simula-r
Repo: Comfy-Org/ComfyUI_frontend PR: 7252
File: src/renderer/extensions/vueNodes/components/ImagePreview.vue:151-158
Timestamp: 2025-12-11T03:55:51.755Z
Learning: In src/renderer/extensions/vueNodes/components/ImagePreview.vue and LGraphNode.vue, keyboard navigation for image galleries should respond to node-level focus (via keyEvent injection from LGraphNode), not require focus within the image preview wrapper itself. This allows users to navigate the gallery with arrow keys immediately when the node is focused/selected.
📚 Learning: 2025-12-11T03:55:51.755Z
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:51.755Z
Learning: In Vue components under src/renderer/extensions/vueNodes (e.g., ImagePreview.vue and LGraphNode.vue), implement image gallery keyboard navigation so that it responds to the node's focus state rather than requiring focus inside the image preview wrapper. Achieve this by wiring keyEvent handling at the node focus level and injecting or propagating key events (arrow keys) to the gallery when the node is focused/selected. This improves accessibility and aligns navigation with node-level focus behavior.

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Be judicious with addition of new refs or other state: prefer props, avoid redundant `computed`, and prefer `computed` over `watch`

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use `ref` for reactive state in Vue Composition API components

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use ref/reactive for state management in Vue 3 Composition API

Applied to files:

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

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Use setup() function in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Use `provide`/`inject` for dependency injection; do not use dependency injection if a Store or shared composable would be simpler

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Vue components must use `<script setup lang="ts">` for component logic

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-12-09T20:22:23.620Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-09T20:22:23.620Z
Learning: Applies to **/*.vue : Implement computed properties with `computed()` from Vue; avoid using a `ref` with a `watch` if a `computed` would work instead

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.vue
📚 Learning: 2025-11-24T19:47:45.616Z
Learnt from: CR
Repo: Comfy-Org/ComfyUI_frontend PR: 0
File: src/components/CLAUDE.md:0-0
Timestamp: 2025-11-24T19:47:45.616Z
Learning: Applies to src/components/**/*.vue : Implement computed() for derived state in Vue 3 Composition API

Applied to files:

  • src/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.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/renderer/extensions/vueNodes/components/LGraphNode.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: setup
  • GitHub Check: lint-and-format
  • GitHub Check: test
  • GitHub Check: collect

Comment on lines +52 to 53
@keydown="handleNodeKeydown"
>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard node-level key forwarding so arrow keys in inputs/widgets don’t trigger gallery navigation.
Because keydown bubbles, typing/caret navigation inside inputs within the node can still set keyEvent, which descendants may treat as “navigate preview”.

Concrete guard (also reduces pointless updates for keys nobody consumes):

 const handleNodeKeydown = (event: KeyboardEvent) => {
-  keyEvent.value = event
+  // Don’t steal cursor keys from inputs / editable content inside the node
+  const target = event.target as HTMLElement | null
+  if (
+    target?.closest(
+      'input, textarea, select, [contenteditable="true"], [role="textbox"]'
+    )
+  ) {
+    return
+  }
+
+  // Optional: only forward keys preview cares about
+  // if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return
+
+  keyEvent.value = event
 }

Based on learnings, this still preserves the intended “node-focused navigation” behavior while avoiding accidental interception.

Also applies to: 214-216

🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/components/LGraphNode.vue around lines 52-53
(also apply same change to lines 214-216), the node-level keydown handler
forwards all key events and unintentionally treats input/caret navigation inside
descendant inputs/widgets as navigation; modify handleNodeKeydown so it first
inspects event.target (or the first non-shadow composedPath entry) and returns
early if the target is an editable element (input, textarea, select,
[contenteditable="true"], or role="textbox") or if event.defaultPrevented; then
only set/forward keyEvent for non-editable targets (and still restrict to the
navigation keys you care about) to avoid needless updates and prevent arrow keys
inside inputs from triggering gallery navigation.

@christian-byrne christian-byrne merged commit 88bdc60 into main Dec 12, 2025
27 checks passed
@christian-byrne christian-byrne deleted the fix/vue-nodes-image-preview-a11y branch December 12, 2025 06:31
jtydhr88 pushed a commit that referenced this pull request Dec 16, 2025
## Summary

Applies appropriate logic from below PRs (which affected image
outputs/previews) to video previews

- #7268: don't port
the 250ms logic for videos, as it is not as relevant - videos typically
take longer than 250ms and most browsers have a built-in loading state
that will be displayed between component mount and video onloaded that
we don't want to flash. Use the native video loaded event instead
- #7252: apply to
videos 1-for-1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7558-feat-improve-vue-node-video-preview-loading-and-a11y-2cb6d73d365081eab4dcfeb1e62c553b)
by [Unito](https://www.unito.io)
github-actions bot pushed a commit that referenced this pull request Dec 16, 2025
## Summary

Applies appropriate logic from below PRs (which affected image
outputs/previews) to video previews

- #7268: don't port
the 250ms logic for videos, as it is not as relevant - videos typically
take longer than 250ms and most browsers have a built-in loading state
that will be displayed between component mount and video onloaded that
we don't want to flash. Use the native video loaded event instead
- #7252: apply to
videos 1-for-1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7558-feat-improve-vue-node-video-preview-loading-and-a11y-2cb6d73d365081eab4dcfeb1e62c553b)
by [Unito](https://www.unito.io)
github-actions bot pushed a commit that referenced this pull request Dec 16, 2025
## Summary

Applies appropriate logic from below PRs (which affected image
outputs/previews) to video previews

- #7268: don't port
the 250ms logic for videos, as it is not as relevant - videos typically
take longer than 250ms and most browsers have a built-in loading state
that will be displayed between component mount and video onloaded that
we don't want to flash. Use the native video loaded event instead
- #7252: apply to
videos 1-for-1

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7558-feat-improve-vue-node-video-preview-loading-and-a11y-2cb6d73d365081eab4dcfeb1e62c553b)
by [Unito](https://www.unito.io)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:accessibility Accessibility (a11y) issues or improvements area:vue-migration preview size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants