Skip to content

Decouple node help between sidebar and right panel#8110

Merged
christian-byrne merged 6 commits intomainfrom
pysssss/help-store-refactor
Jan 17, 2026
Merged

Decouple node help between sidebar and right panel#8110
christian-byrne merged 6 commits intomainfrom
pysssss/help-store-refactor

Conversation

@pythongosssss
Copy link
Member

@pythongosssss pythongosssss commented Jan 16, 2026

Summary

When the node library is open and you click on the node toolbar info button, this causes the node library info panel & right panel node info to show the same details.

Changes

  • Extract useNodeHelpContent composable so NodeHelpContent fetches its own content, allowing multiple panels to show help independently
  • Remove sync behavior from NodeHelpPage that caused left sidebar to change when selecting different graph nodes since we want to prioritise right panel for this behavior
  • Add telemetry tracking for node library help button to identify how frequently this is used

┆Issue is synchronized with this Notion page by Unito

  - Extract useNodeHelpContent composable so NodeHelpContent fetches its own
    content, allowing multiple panels to show help independently
  - Remove sync behavior from NodeHelpPage that caused left sidebar to change
    when selecting different graph nodes
  - Add telemetry tracking for node library help button
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jan 16, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

Extracted node-help fetching/rendering into a new composable useNodeHelpContent; simplified nodeHelpStore to only track open state and current node; components removed automatic selection-driven side effects or switched to the composable; tests updated to reflect per-component fetching and reduced store responsibilities.

Changes

Cohort / File(s) Summary
New composable & tests
src/composables/useNodeHelpContent.ts, src/composables/useNodeHelpContent.test.ts
Adds useNodeHelpContent that fetches and renders help per-instance (helpContent, isLoading, error, baseUrl, renderedHelpHtml). Includes comprehensive tests for markdown/media rendering, path prefixing, fallback and error behaviors.
Store simplification & tests
src/stores/workspace/nodeHelpStore.ts, src/stores/workspace/nodeHelpStore.test.ts
Removes fetch/render logic and reactive content fields from nodeHelpStore; retains only currentHelpNode, isHelpOpen, openHelp, and closeHelp. Tests reduced to basic initial/open/close behaviors.
Component migrations
src/components/node/NodeHelpContent.vue, src/components/rightSidePanel/info/TabInfo.vue, src/components/sidebar/tabs/nodeLibrary/NodeHelpPage.vue
NodeHelpContent now uses the new composable instead of store-derived refs. TabInfo and NodeHelpPage no longer auto-open node help on selection/nodeInfo changes; removed related watchers/side effects.
Info button tests & panel behavior
src/components/graph/selectionToolbox/InfoButton.test.ts, src/components/rightSidePanel/info/TabInfo.vue
Tests/components updated to call rightSidePanelStore.openPanel('info'); tests now mock useRightSidePanelStore.openPanel and assert invocation on click.
Removed tests for selection-sync help page
src/components/sidebar/tabs/nodeLibrary/NodeHelpPage.test.ts
Entire test file deleted; selection-driven help-sync tests removed to match removed watcher logic.
Telemetry on help clicks
src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue
Adds useTelemetry and wraps help-button click to emit node_library_help_button telemetry before invoking openNodeHelp.

Sequence Diagram(s)

sequenceDiagram
  participant Component as NodeHelpContent
  participant Composable as useNodeHelpContent
  participant Service as nodeHelpService
  participant Renderer as renderMarkdownToHtml

  Component->>Composable: provide node ref / node change
  Composable->>Service: fetchNodeHelp(url, locale)
  Service-->>Composable: markdown OR error
  alt fetch success
    Composable->>Renderer: renderMarkdownToHtml(markdown, baseUrl)
    Renderer-->>Composable: sanitized HTML
    Composable-->>Component: renderedHelpHtml, isLoading=false
  else fetch fails
    Composable-->>Component: fallback description or error, isLoading=false
  end
Loading

Possibly related PRs

Suggested reviewers

  • christian-byrne
  • DrJKL
  • Myestery
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

🎭 Playwright Tests: ✅ Passed

Results: 507 passed, 0 failed, 0 flaky, 8 skipped (Total: 515)

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

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

🎨 Storybook Build Status

Build completed successfully!

⏰ Completed at: 01/16/2026, 10:00:20 PM UTC

🔗 Links


🎉 Your Storybook is ready for review!

@github-actions
Copy link

github-actions bot commented Jan 16, 2026

Bundle Size Report

Summary

  • Raw size: 19.8 MB baseline 19.8 MB — 🔴 +458 B
  • Gzip: 4.04 MB baseline 4.04 MB — 🔴 +298 B
  • Brotli: 3.08 MB baseline 3.08 MB — 🔴 +101 B
  • Bundles: 100 current • 99 baseline • 39 added / 38 removed

Category Glance
App Entry Points 🟢 -3.52 kB (3.36 MB) · Graph Workspace 🔴 +3.35 kB (1.14 MB) · Utilities & Hooks 🔴 +460 B (1.86 kB) · Other 🔴 +131 B (5.38 MB) · UI Components 🔴 +39 B (203 kB) · Vendor & Third-Party ⚪ 0 B (9.34 MB) · + 3 more

Per-category breakdown
App Entry Points — 3.36 MB (baseline 3.36 MB) • 🟢 -3.52 kB

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-B5aWirBx.js (removed) 3.16 MB 🟢 -3.16 MB 🟢 -662 kB 🟢 -503 kB
assets/index-D-sARKQs.js (new) 3.16 MB 🔴 +3.16 MB 🔴 +662 kB 🔴 +502 kB
assets/index-CCkGePKJ.js (new) 200 kB 🔴 +200 kB 🔴 +44.1 kB 🔴 +36.4 kB
assets/index-D_ncDpdZ.js (removed) 200 kB 🟢 -200 kB 🟢 -44.1 kB 🟢 -36.4 kB
assets/index-CLFQLsAJ.js (new) 345 B 🔴 +345 B 🔴 +244 B 🔴 +207 B
assets/index-Dyn0978C.js (removed) 345 B 🟢 -345 B 🟢 -245 B 🟢 -240 B

Status: 3 added / 3 removed

Graph Workspace — 1.14 MB (baseline 1.14 MB) • 🔴 +3.35 kB

Graph editor runtime, canvas, workflow orchestration

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

Status: 1 added / 1 removed

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

Top-level views, pages, and routed surfaces

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

Status: 1 added / 1 removed

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

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LegacyCreditsPanel-CpRF0gFA.js (removed) 25.2 kB 🟢 -25.2 kB 🟢 -5.76 kB 🟢 -5.02 kB
assets/LegacyCreditsPanel-qeW-ExPo.js (new) 25.2 kB 🔴 +25.2 kB 🔴 +5.75 kB 🔴 +5.01 kB
assets/KeybindingPanel-BxSkz-BR.js (new) 14.9 kB 🔴 +14.9 kB 🔴 +3.59 kB 🔴 +3.14 kB
assets/KeybindingPanel-IxdO_fbJ.js (removed) 14.9 kB 🟢 -14.9 kB 🟢 -3.59 kB 🟢 -3.14 kB
assets/ExtensionPanel-D_mbrkTw.js (removed) 11.1 kB 🟢 -11.1 kB 🟢 -2.63 kB 🟢 -2.31 kB
assets/ExtensionPanel-qLbtGxbq.js (new) 11.1 kB 🔴 +11.1 kB 🔴 +2.63 kB 🔴 +2.31 kB
assets/AboutPanel-D6f8biV1.js (removed) 9.2 kB 🟢 -9.2 kB 🟢 -2.48 kB 🟢 -2.22 kB
assets/AboutPanel-DWG3x-bG.js (new) 9.2 kB 🔴 +9.2 kB 🔴 +2.48 kB 🔴 +2.23 kB
assets/ServerConfigPanel-CbkEMHTR.js (removed) 7.55 kB 🟢 -7.55 kB 🟢 -2.06 kB 🟢 -1.82 kB
assets/ServerConfigPanel-wAv3AymD.js (new) 7.55 kB 🔴 +7.55 kB 🔴 +2.06 kB 🔴 +1.83 kB
assets/UserPanel-k0nZPTJx.js (removed) 6.92 kB 🟢 -6.92 kB 🟢 -1.8 kB 🟢 -1.58 kB
assets/UserPanel-vPMQ3ZQ3.js (new) 6.92 kB 🔴 +6.92 kB 🔴 +1.8 kB 🔴 +1.58 kB
assets/settings-BGQfQzTx.js 25.6 kB 25.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BVE4KHTw.js 22.7 kB 22.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-BVtpJmlU.js 30.9 kB 30.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-C2aO00Dz.js 28.6 kB 28.6 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-Cm3ieBXR.js 27.8 kB 27.8 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CzQKMdK3.js 26.2 kB 26.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-CzYUIUnL.js 27.1 kB 27.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DwKpL7jw.js 26.3 kB 26.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-DX8feV4n.js 25.3 kB 25.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-mWzYycGc.js 22 kB 22 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/settings-U4AdZ8Rl.js 34.9 kB 34.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 6 added / 6 removed

UI Components — 203 kB (baseline 203 kB) • 🔴 +39 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/LazyImage.vue_vue_type_script_setup_true_lang-CxccdtIB.js (new) 63.9 kB 🔴 +63.9 kB 🔴 +12.8 kB 🔴 +11.2 kB
assets/LazyImage.vue_vue_type_script_setup_true_lang-DNLAABKg.js (removed) 63.9 kB 🟢 -63.9 kB 🟢 -12.8 kB 🟢 -11.2 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-6-kfU7MK.js (removed) 56.4 kB 🟢 -56.4 kB 🟢 -8.78 kB 🟢 -7.53 kB
assets/Load3D.vue_vue_type_script_setup_true_lang-DLhx4Oop.js (new) 56.4 kB 🔴 +56.4 kB 🔴 +8.78 kB 🔴 +7.54 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-CoYZKqee.js (new) 48.1 kB 🔴 +48.1 kB 🔴 +10.4 kB 🔴 +9 kB
assets/WidgetSelect.vue_vue_type_script_setup_true_lang-Dtto2CDM.js (removed) 48.1 kB 🟢 -48.1 kB 🟢 -10.4 kB 🟢 -9 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-DG5iq1ld.js (removed) 16.4 kB 🟢 -16.4 kB 🟢 -4.11 kB 🟢 -3.64 kB
assets/WidgetInputNumber.vue_vue_type_script_setup_true_lang-Dxz_n42b.js (new) 16.4 kB 🔴 +16.4 kB 🔴 +4.11 kB 🔴 +3.64 kB
assets/ComfyQueueButton-khFlj68l.js (new) 8.91 kB 🔴 +8.91 kB 🔴 +2.62 kB 🔴 +2.33 kB
assets/ComfyQueueButton-BmL2oehY.js (removed) 8.87 kB 🟢 -8.87 kB 🟢 -2.6 kB 🟢 -2.32 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-BI-Drhfz.js (removed) 3.75 kB 🟢 -3.75 kB 🟢 -1.47 kB 🟢 -1.33 kB
assets/WidgetWithControl.vue_vue_type_script_setup_true_lang-D6m6EEZc.js (new) 3.75 kB 🔴 +3.75 kB 🔴 +1.47 kB 🔴 +1.33 kB
assets/WidgetButton-CeEZfgiT.js (removed) 2.25 kB 🟢 -2.25 kB 🟢 -1.01 kB 🟢 -912 B
assets/WidgetButton-Datmacfk.js (new) 2.25 kB 🔴 +2.25 kB 🔴 +1.01 kB 🔴 +908 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-B-8nX1tR.js (removed) 2.14 kB 🟢 -2.14 kB 🟢 -891 B 🟢 -771 B
assets/WidgetLayoutField.vue_vue_type_script_setup_true_lang-C-gyJ1gH.js (new) 2.14 kB 🔴 +2.14 kB 🔴 +892 B 🔴 +771 B
assets/UserAvatar.vue_vue_type_script_setup_true_lang-DYuISk5v.js 1.34 kB 1.34 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 8 added / 8 removed

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

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/keybindingService-DHkNQppA.js (new) 7.51 kB 🔴 +7.51 kB 🔴 +1.83 kB 🔴 +1.57 kB
assets/keybindingService-DwjBAmGn.js (removed) 7.51 kB 🟢 -7.51 kB 🟢 -1.83 kB 🟢 -1.57 kB
assets/audioService-BOmqt4EO.js (new) 2.2 kB 🔴 +2.2 kB 🔴 +961 B 🔴 +829 B
assets/audioService-BYw3CZ3m.js (removed) 2.2 kB 🟢 -2.2 kB 🟢 -963 B 🟢 -823 B
assets/serverConfigStore-UAfw346M.js 2.83 kB 2.83 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 2 added / 2 removed

Utilities & Hooks — 1.86 kB (baseline 1.41 kB) • 🔴 +460 B

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/audioUtils-BI4mFmVX.js (new) 1.41 kB 🔴 +1.41 kB 🔴 +652 B 🔴 +544 B
assets/audioUtils-C089N6tx.js (removed) 1.41 kB 🟢 -1.41 kB 🟢 -650 B 🟢 -540 B
assets/nodeFilterUtil-CXKCRJ-m.js (new) 460 B 🔴 +460 B 🔴 +275 B 🔴 +227 B

Status: 2 added / 1 removed

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

External libraries and shared vendor chunks

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

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SubscriptionRequiredDialogContent-Ylaqsxpw.js (new) 29.4 kB 🔴 +29.4 kB 🔴 +6.52 kB 🔴 +5.67 kB
assets/SubscriptionRequiredDialogContent-CK-l-B9H.js (removed) 29.3 kB 🟢 -29.3 kB 🟢 -6.51 kB 🟢 -5.66 kB
assets/WidgetRecordAudio-CGNzsro0.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -5.24 kB 🟢 -4.64 kB
assets/WidgetRecordAudio-DQt2iRxB.js (new) 20.5 kB 🔴 +20.5 kB 🔴 +5.24 kB 🔴 +4.64 kB
assets/AudioPreviewPlayer-BqEvu8pD.js (new) 13.4 kB 🔴 +13.4 kB 🔴 +3.38 kB 🔴 +3.04 kB
assets/AudioPreviewPlayer-BzJWtA2e.js (removed) 13.4 kB 🟢 -13.4 kB 🟢 -3.36 kB 🟢 -3.01 kB
assets/ValueControlPopover-BZ-r9CCX.js (new) 5.53 kB 🔴 +5.53 kB 🔴 +1.72 kB 🔴 +1.53 kB
assets/ValueControlPopover-C44JR4r_.js (removed) 5.53 kB 🟢 -5.53 kB 🟢 -1.72 kB 🟢 -1.53 kB
assets/WidgetGalleria-D210isIC.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.46 kB 🟢 -1.32 kB
assets/WidgetGalleria-DaYEVIXR.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.46 kB 🔴 +1.32 kB
assets/WidgetColorPicker-oJKZq5jR.js (removed) 3.44 kB 🟢 -3.44 kB 🟢 -1.4 kB 🟢 -1.25 kB
assets/WidgetColorPicker-X32EpGR4.js (new) 3.44 kB 🔴 +3.44 kB 🔴 +1.4 kB 🔴 +1.25 kB
assets/WidgetTextarea-R7c71Bv1.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.23 kB 🔴 +1.11 kB
assets/WidgetTextarea-UZ5YOekd.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.23 kB 🟢 -1.11 kB
assets/WidgetMarkdown-ClFk6HS-.js (new) 3.12 kB 🔴 +3.12 kB 🔴 +1.29 kB 🔴 +1.14 kB
assets/WidgetMarkdown-DxCIq0QJ.js (removed) 3.12 kB 🟢 -3.12 kB 🟢 -1.29 kB 🟢 -1.14 kB
assets/WidgetAudioUI-B9pPAQ7s.js (new) 2.98 kB 🔴 +2.98 kB 🔴 +1.2 kB 🔴 +1.09 kB
assets/WidgetAudioUI-C8zTbzjx.js (removed) 2.93 kB 🟢 -2.93 kB 🟢 -1.18 kB 🟢 -1.07 kB
assets/WidgetToggleSwitch-BIFVOOHQ.js (new) 2.7 kB 🔴 +2.7 kB 🔴 +1.14 kB 🔴 +1.04 kB
assets/WidgetToggleSwitch-s5n7HngL.js (removed) 2.7 kB 🟢 -2.7 kB 🟢 -1.14 kB 🟢 -1.05 kB
assets/WidgetInputText-B7iBfu0t.js (new) 2.03 kB 🔴 +2.03 kB 🔴 +936 B 🔴 +859 B
assets/WidgetInputText-PNHa3yqo.js (removed) 2.03 kB 🟢 -2.03 kB 🟢 -936 B 🟢 -865 B
assets/Media3DTop-BKuPUozK.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +782 B 🔴 +669 B
assets/Media3DTop-D263Hz5A.js (removed) 1.53 kB 🟢 -1.53 kB 🟢 -784 B 🟢 -667 B
assets/WidgetSelect-CTWZO0PS.js (new) 772 B 🔴 +772 B 🔴 +375 B 🔴 +328 B
assets/WidgetSelect-DFNE4zy7.js (removed) 772 B 🟢 -772 B 🟢 -375 B 🟢 -344 B
assets/WidgetInputNumber-BW2zp-nu.js (removed) 712 B 🟢 -712 B 🟢 -361 B 🟢 -331 B
assets/WidgetInputNumber-eJihrZD6.js (new) 712 B 🔴 +712 B 🔴 +361 B 🔴 +329 B
assets/Load3D-BIqGt7rt.js (removed) 463 B 🟢 -463 B 🟢 -281 B 🟢 -237 B
assets/Load3D-Wvz-Ax_r.js (new) 463 B 🔴 +463 B 🔴 +281 B 🔴 +236 B
assets/WidgetLegacy-BGogCs2S.js (removed) 403 B 🟢 -403 B 🟢 -250 B 🟢 -210 B
assets/WidgetLegacy-C2hGs7zT.js (new) 403 B 🔴 +403 B 🔴 +252 B 🔴 +210 B
assets/commands-B32ZbeYu.js 16.5 kB 16.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-B7wQT83I.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-BK3JVjMG.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-C6twMpaZ.js 15.5 kB 15.5 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CDUWpEwM.js 18.3 kB 18.3 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CfZ6FPZ-.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-COIPP_pv.js 17 kB 17 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CoPn_77e.js 14.7 kB 14.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-CuRNS4XD.js 14.9 kB 14.9 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DBHjCSPA.js 16.2 kB 16.2 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/commands-DZJaRLKH.js 15.7 kB 15.7 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-8BNo1weg.js 110 kB 110 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-BPRfdNvb.js 127 kB 127 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-CezbcLlR.js 98.1 kB 98.1 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-COBPG1am.js 134 kB 134 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-COodTO5z.js 112 kB 112 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-D2h56mz4.js 97.4 kB 97.4 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DkeDO8xU.js 110 kB 110 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DNecFfxQ.js 138 kB 138 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-DPtAQ-XV.js 114 kB 114 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-R06XJOVT.js 116 kB 116 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/main-yFuDcHL9.js 154 kB 154 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaAudioTop-BbasJLNf.js 1.46 kB 1.46 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaImageTop-Cg8GhF1C.js 1.75 kB 1.75 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/MediaVideoTop-Blp7MPtz.js 2.65 kB 2.65 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-2h3z0SXa.js 324 kB 324 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-4bBZvQjV.js 298 kB 298 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-B1OXtv5-.js 295 kB 295 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-C_xG1nNM.js 324 kB 324 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-CAgbt7pL.js 365 kB 365 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-Cghy43iq.js 399 kB 399 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DHrs5gc0.js 328 kB 328 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DPFlfVHx.js 321 kB 321 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DsMfXcAJ.js 366 kB 366 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-DWdQhd19.js 337 kB 337 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/nodeDefs-mAfdiTdx.js 317 kB 317 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/OBJLoader2WorkerModule-DTMpvldF.js 109 kB 109 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetChart-BicTiHSA.js 2.48 kB 2.48 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/WidgetImageCompare-DUGS9EoD.js 3.21 kB 3.21 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B
assets/widgetPropFilter-BIbGSUAt.js 1.28 kB 1.28 kB ⚪ 0 B ⚪ 0 B ⚪ 0 B

Status: 16 added / 16 removed

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@src/components/graph/selectionToolbox/InfoButton.test.ts`:
- Around line 60-66: Remove the style-only assertions in the test: delete the
expectations that check CSS classes and the 'severity' attribute and keep only
behavioral assertions (e.g., the click test). In the test file
InfoButton.test.ts, update the case using mountComponent() and
wrapper.find('button') to remove
expect(button.classes()).toContain('help-button') and
expect(button.attributes('severity')).toBe('secondary'), ensuring the test
verifies behavior (click handling) only.
- Around line 10-15: The test defines openPanelMock at module scope and closes
it over the vi.mock factory; change this to use vi.hoisted() so the mock is
hoisted and not module-level mutable state: replace the top-level openPanelMock
with a hoisted mock via vi.hoisted() and have the mocked useRightSidePanelStore
factory return { openPanel: hoistedMock } (referencing openPanelMock and
useRightSidePanelStore in the test so the mock instance is created via
vi.hoisted before vi.mock is called).
- Around line 42-47: Replace the Button stub in the InfoButton test with the
real component import and registration: import Button from
'@/components/ui/button/Button.vue' and register it in the mount/shallowMount
options (components: { Button }) instead of the stubbed template; remove the
stub block that declares props 'severity'/'text' and ensure any test
interactions use the real Button API (variant, size, disabled, loading) so the
test exercises production behavior of the InfoButton component.

In `@src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue`:
- Around line 117-122: The click handler onHelpClick calls useTelemetry() each
time, which should instead be invoked once at component setup; move the
composable call to the top-level of the <script setup> (e.g., const telemetry =
useTelemetry()), then update onHelpClick to call
telemetry.trackUiButtonClicked(...) and keep the existing
props.openNodeHelp(nodeDef.value) call; this ensures useTelemetry is
instantiated once and reused.

In `@src/composables/useNodeHelpContent.test.ts`:
- Around line 93-95: Import the ComfyNodeDefImpl type from
"@/stores/nodeDefStore" and create a small helper makeNodeRef that casts a
Partial<ComfyNodeDefImpl> to ComfyNodeDefImpl and returns ref(...) so you stop
using `as any`; update the test to replace each `ref(mockCoreNode as any)` and
`ref(mockCustomNode as any)` with `makeNodeRef(mockCoreNode)` and
`makeNodeRef(mockCustomNode)` respectively (affecting the occurrences referenced
in the comment), ensuring all node refs use `Partial<ComfyNodeDefImpl> as
ComfyNodeDefImpl` inside the helper.
- Around line 85-91: The test currently assigns global.fetch = mockFetch at
module scope which leaks the stub; instead, in the test file replace that global
assignment by creating the mock inside beforeEach and use vi.stubGlobal('fetch',
mockFetch) there, call mockFetch.mockReset() in beforeEach, and restore/unstub
in afterEach (or use vi.restoreAllMocks()/vi.unstubAllGlobals()) to ensure the
global fetch stub is cleaned up; update references to the mockFetch variable and
lifecycle hooks (beforeEach/afterEach) in useNodeHelpContent.test.ts
accordingly.

In `@src/composables/useNodeHelpContent.ts`:
- Around line 38-60: The watcher for toValue(nodeRef) can let earlier
fetchNodeHelp calls overwrite newer results; inside the async watcher callback
(around the call to nodeHelpService.fetchNodeHelp) add a "current request"
guard: maintain a locally scoped increasing requestId (or token) that you
increment before calling fetchNodeHelp, capture it in a local variable, then
after the await only assign helpContent.value / error.value if the captured id
equals the latest id; alternatively, if fetchNodeHelp supports AbortController
pass an abort signal and cancel the previous request before starting a new one.
Ensure this logic references the existing symbols nodeRef,
nodeHelpService.fetchNodeHelp, helpContent, isLoading, error, and locale so
stale responses are ignored and loading state remains correct.

Comment on lines +117 to +122
const onHelpClick = () => {
useTelemetry()?.trackUiButtonClicked({
button_id: 'node_library_help_button'
})
props.openNodeHelp(nodeDef.value)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider calling useTelemetry() at the component setup level.

Calling useTelemetry() inside the click handler instantiates the composable on every click. While it likely works due to internal caching, the idiomatic Vue pattern is to call composables at the top level of <script setup> and reuse the returned instance.

♻️ Suggested refactor
 const settingStore = useSettingStore()
+const telemetry = useTelemetry()
 const sidebarLocation = computed<'left' | 'right'>(() =>
   settingStore.get('Comfy.Sidebar.Location')
 )

 const toggleBookmark = async () => {
   await nodeBookmarkStore.toggleBookmark(nodeDef.value)
 }

 const onHelpClick = () => {
-  useTelemetry()?.trackUiButtonClicked({
+  telemetry?.trackUiButtonClicked({
     button_id: 'node_library_help_button'
   })
   props.openNodeHelp(nodeDef.value)
 }
🤖 Prompt for AI Agents
In `@src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue` around lines 117 -
122, The click handler onHelpClick calls useTelemetry() each time, which should
instead be invoked once at component setup; move the composable call to the
top-level of the <script setup> (e.g., const telemetry = useTelemetry()), then
update onHelpClick to call telemetry.trackUiButtonClicked(...) and keep the
existing props.openNodeHelp(nodeDef.value) call; this ensures useTelemetry is
instantiated once and reused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/composables/useNodeHelpContent.test.ts`:
- Around line 32-63: The test currently fully mocks dompurify and marked via
vi.mock (mocking default.sanitize, marked.parse and the Renderer.image/link),
which prevents exercising real rendering/sanitization; change the test to import
the real dompurify and marked modules and replace the full vi.mock with
spies/partial mocks instead (use vi.spyOn on dompurify.default.sanitize and
marked.marked.parse or wrap those libraries in a thin adapter you control and
mock that adapter), remove the custom Renderer class mock so the real
marked.Renderer is used, and update assertions to verify calls to the spy/mocked
adapter rather than validate the mocked implementation.
♻️ Duplicate comments (1)
src/composables/useNodeHelpContent.test.ts (1)

96-110: Replace as any node refs with a typed helper.

Line 96 (and repeated usages below) use ref(mock… as any), which violates the repo’s “no any” rule and hides the intended node shape. Import the real type and use a helper that casts Partial<ComfyNodeDefImpl> to keep intent explicit and type-safe.

✅ Suggested refactor
 import { nextTick, ref } from 'vue'
+import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
 
+function makeNodeRef(node: Partial<ComfyNodeDefImpl>) {
+  return ref(node as ComfyNodeDefImpl)
+}
@@
-    const nodeRef = ref(mockCoreNode as any)
+    const nodeRef = makeNodeRef(mockCoreNode)
@@
-    const nodeRef = ref(mockCustomNode as any)
+    const nodeRef = makeNodeRef(mockCustomNode)

Please apply the same replacement to all other ref(mock… as any) occurrences in this file. Based on learnings, also keep the import type on its own line.

#!/bin/bash
# Verify no remaining `as any` in this test file
rg -n "as any" src/composables/useNodeHelpContent.test.ts

Also applies to: 122-137, 149-164, 177-204, 226-243, 258-292, 304-323

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/composables/useNodeHelpContent.test.ts`:
- Around line 349-380: In the "should ignore stale requests when node changes"
test, avoid the non-null assertion on resolveFirst by giving resolveFirst a safe
initial value and proper typing so it's always defined; for example declare
resolveFirst as let resolveFirst: (value: unknown) => void = () => {} and then
reassign it inside the Promise executor (the existing firstRequest/new Promise
code) so resolveFirst can be called later without using resolveFirst!; update
references to resolveFirst and firstRequest in the test accordingly to remove
the non-null assertion while preserving the same race-condition behavior.

Comment on lines +349 to +380
it('should ignore stale requests when node changes', async () => {
const nodeRef = ref(mockCoreNode)
let resolveFirst: (value: unknown) => void
const firstRequest = new Promise((resolve) => {
resolveFirst = resolve
})

mockFetch
.mockImplementationOnce(() => firstRequest)
.mockResolvedValueOnce({
ok: true,
text: async () => '# Second node content'
})

const { helpContent } = useNodeHelpContent(nodeRef)
await nextTick()

// Change node before first request completes
nodeRef.value = mockCustomNode
await nextTick()
await flushPromises()

// Now resolve the first (stale) request
resolveFirst!({
ok: true,
text: async () => '# First node content'
})
await flushPromises()

// Should have second node's content, not first
expect(helpContent.value).toBe('# Second node content')
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Good race condition coverage; minor pattern suggestion.

The stale request test correctly verifies that old responses are ignored when the node changes mid-flight. The resolveFirst! non-null assertion (line 372) is safe here since the Promise executor runs synchronously, but could be slightly more explicit:

♻️ Optional: Avoid late-bound assertion
   it('should ignore stale requests when node changes', async () => {
     const nodeRef = ref(mockCoreNode)
-    let resolveFirst: (value: unknown) => void
+    let resolveFirst: (value: unknown) => void = () => {}
     const firstRequest = new Promise((resolve) => {
       resolveFirst = resolve
     })
     // ... rest unchanged
-    resolveFirst!({
+    resolveFirst({
🤖 Prompt for AI Agents
In `@src/composables/useNodeHelpContent.test.ts` around lines 349 - 380, In the
"should ignore stale requests when node changes" test, avoid the non-null
assertion on resolveFirst by giving resolveFirst a safe initial value and proper
typing so it's always defined; for example declare resolveFirst as let
resolveFirst: (value: unknown) => void = () => {} and then reassign it inside
the Promise executor (the existing firstRequest/new Promise code) so
resolveFirst can be called later without using resolveFirst!; update references
to resolveFirst and firstRequest in the test accordingly to remove the non-null
assertion while preserving the same race-condition behavior.

@christian-byrne christian-byrne merged commit 3bbae61 into main Jan 17, 2026
27 checks passed
@christian-byrne christian-byrne deleted the pysssss/help-store-refactor branch January 17, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants