Feat: Add preview as plaintext toggle for Preview As Text#7102
Conversation
📝 WalkthroughWalkthroughTightens widget typings and runtime validation, changes SafeWidgetData.options to IWidgetOptions, assigns widget.options directly, excludes hidden widgets from layout/size calculations, refactors per-widget rendering and preview widgets (Markdown + plaintext toggle), and updates a test to use SafeWidgetData typing. Changes
Possibly related PRs
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 12/02/2025, 10:30:26 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results⏰ Completed at: 12/02/2025, 10:39:43 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.19 MB (baseline 3.19 MB) • 🔴 +1.63 kBMain entry bundles and manifests
Status: 3 added / 3 removed Graph Workspace — 928 kB (baseline 928 kB) • 🟢 -33 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 6.54 kB (baseline 6.54 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 1 added / 1 removed Panels & Settings — 298 kB (baseline 298 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 6 added / 6 removed UI Components — 169 kB (baseline 169 kB) • ⚪ 0 BReusable component library chunks
Status: 6 added / 6 removed Data & Services — 12.5 kB (baseline 12.5 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 2 added / 2 removed Utilities & Hooks — 2.94 kB (baseline 2.94 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed Vendor & Third-Party — 8.56 MB (baseline 8.56 MB) • ⚪ 0 BExternal libraries and shared vendor chunks
Other — 3.84 MB (baseline 3.84 MB) • ⚪ 0 BBundles that do not match a named category
Status: 17 added / 17 removed |
|
I know it should be Heading not Header. Don't @ me. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/extensions/core/previewAny.ts (1)
27-32: Add i18n for user-facing strings.The
label_on: 'Markdown'andlabel_off: 'Plaintext'strings on lines 39-40 should usevue-i18nfor localization. Per coding guidelines, user-facing strings should be added tosrc/locales/en/main.json.Consider:
+ import { t } from '@/i18n' const showAsPlaintextWidget = ComfyWidgets['BOOLEAN']( this, 'previewMode', [ 'BOOLEAN', - { label_on: 'Markdown', label_off: 'Plaintext', default: false } + { label_on: t('previewAny.markdown'), label_off: t('previewAny.plaintext'), default: false } ], app )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/composables/graph/useGraphNodeManager.ts(3 hunks)src/extensions/core/previewAny.ts(2 hunks)src/extensions/core/widgetInputs.ts(2 hunks)src/lib/litegraph/src/LGraphNode.ts(2 hunks)src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue(0 hunks)src/renderer/extensions/vueNodes/components/NodeWidgets.vue(2 hunks)src/scripts/widgets.ts(2 hunks)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts(2 hunks)
💤 Files with no reviewable changes (1)
- src/renderer/extensions/vueNodes/components/LGraphNodePreview.vue
🧰 Additional context used
📓 Path-based instructions (22)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
src/**/*.{vue,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tssrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/scripts/widgets.tssrc/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/src/LGraphNode.tssrc/extensions/core/previewAny.tssrc/extensions/core/widgetInputs.ts
src/**/{services,composables}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/{services,composables}/**/*.{ts,tsx}: Useapi.apiURL()for backend endpoints instead of constructing URLs directly
Useapi.fileURL()for static file access instead of constructing URLs directly
Files:
src/composables/graph/useGraphNodeManager.ts
src/**/{composables,components}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Clean up subscriptions in state management to prevent memory leaks
Files:
src/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vue
src/**/{components,composables}/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Use vue-i18n for ALL user-facing strings by adding them to
src/locales/en/main.json
Files:
src/composables/graph/useGraphNodeManager.tssrc/renderer/extensions/vueNodes/components/NodeWidgets.vue
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/widgets/composables/useWidgetRenderer.test.ts
**/*.{test,spec}.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Unit and component tests should be located in
tests-ui/or co-located with components assrc/components/**/*.{test,spec}.ts; E2E tests should be inbrowser_tests/
Files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts
**/composables/use*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Name composables in the format
useXyz.ts
Files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventionsFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/lib/litegraph/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)
src/lib/litegraph/**/*.{js,ts,jsx,tsx}: Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns using thepnpm lint:fixcommand
Take advantage ofTypedArraysubarraywhen appropriate
Thesizeandposproperties ofRectangleshare the same array buffer (subarray); they may be used to set the rectangle's size and position
Prefer single lineifsyntax over adding curly braces, when the statement has a very concise expression and concise, single line statement
Do not replace&&=or||=with=when there is no reason to do so. If you do find a reason to remove either&&=or||=, leave a comment explaining why the removal occurred
When writing methods, prefer returning idiomatic JavaScriptundefinedovernullFiles:
src/lib/litegraph/src/LGraphNode.tssrc/lib/litegraph/**/*.{ts,tsx}
📄 CodeRabbit inference engine (src/lib/litegraph/CLAUDE.md)
Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code
Files:
src/lib/litegraph/src/LGraphNode.ts🧠 Learnings (6)
📚 Learning: 2025-11-24T19:47:56.371Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/lib/litegraph/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:56.371Z Learning: Applies to src/lib/litegraph/**/*.{test,spec}.{ts,tsx} : Use provided test helpers `createTestSubgraph` and `createTestSubgraphNode` from `./fixtures/subgraphHelpers` for consistent subgraph test setupApplied to files:
src/composables/graph/useGraphNodeManager.tstests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.tssrc/extensions/core/widgetInputs.ts📚 Learning: 2025-11-24T19:48:03.270Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: tests-ui/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:48:03.270Z Learning: Applies to tests-ui/**/*.test.{js,ts,jsx,tsx} : Use existing test utilities rather than writing custom utilitiesApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts📚 Learning: 2025-11-24T19:48:09.318Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursor/rules/unit-test.mdc:0-0 Timestamp: 2025-11-24T19:48:09.318Z Learning: Applies to test/**/*.{test,spec}.{js,ts,jsx,tsx} : Prefer the use of `test.extend` over loose variables; import `test as baseTest` from `vitest`Applied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.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 featuresApplied to files:
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.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 ToggleSwitchApplied to files:
src/renderer/extensions/vueNodes/components/NodeWidgets.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/**/*.{ts,tsx,vue} : Implement proper TypeScript types throughout the codebaseApplied to files:
src/extensions/core/widgetInputs.ts🧬 Code graph analysis (4)
src/composables/graph/useGraphNodeManager.ts (1)
src/lib/litegraph/src/types/widgets.ts (1)
IWidgetOptions(5-36)tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts (1)
src/composables/graph/useGraphNodeManager.ts (1)
SafeWidgetData(40-50)src/extensions/core/previewAny.ts (3)
src/scripts/widgets.ts (1)
ComfyWidgets(288-302)src/scripts/domWidget.ts (1)
DOMWidget(40-49)src/types/index.ts (1)
DOMWidget(43-43)src/extensions/core/widgetInputs.ts (1)
src/scripts/widgets.ts (1)
isValidWidgetType(304-308)🔇 Additional comments (17)
src/scripts/widgets.ts (2)
288-302: LGTM - Good type refinement withas const.Using
as constprovides better type inference and allowskeyof typeof ComfyWidgetsto be a union of literal types rather than juststring, enabling stronger compile-time checks.
304-308: Type guard implementation is correct.The
isValidWidgetTypefunction properly narrowsunknownto valid widget keys. The implementation correctly handles the runtime check using property lookup.src/lib/litegraph/src/LGraphNode.ts (2)
4030-4032: Good addition of hidden widget filtering for layout calculations.Filtering out hidden widgets prevents them from contributing to
fixedWidgetHeight, which is the correct behavior. The filtering is applied consistently in both the height computation loop and the positioning loop (line 4071).
4071-4074: Consistent use ofvisibleWidgetsfor positioning.The same filtered set is used for both height calculation and y-position assignment, ensuring hidden widgets don't create gaps in the layout.
tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts (2)
9-9: Good type import for test alignment.Importing
SafeWidgetDataensures the test widget object aligns with the updated interface, particularly theoptionsfield type change fromRecord<string, unknown>toIWidgetOptions<unknown>.
125-128: Proper typing for test widget object.Using
Partial<SafeWidgetData>correctly types the test widget with only the properties needed for this specific test case.src/extensions/core/previewAny.ts (2)
34-49: Consider clarifying toggle semantics.The toggle logic works correctly, but the naming is counterintuitive:
default: falsewithlabel_off: 'Plaintext'means the default shows Plaintext. When the toggle is ON (value=true), Markdown is shown.The callback correctly inverts visibility:
value=true→ Markdown visible, Plaintext hiddenvalue=false→ Markdown hidden, Plaintext visibleThis matches the initial state (lines 51-63) where Markdown is hidden by default.
73-80: Filter correctly retrieves both preview widgets for updating, but violates system-wide widget naming assumption.The code uses
filter()intentionally to update both the Markdown and plaintext preview widgets that share the name'preview'. This is necessary because one widget is shown/hidden dynamically based on thepreviewModetoggle. However, the broader codebase extensively uses.find()(60+ locations) which returns only the first match, assuming unique widget names per node. While this extension's internal usage is self-contained and works correctly, the pattern creates architectural inconsistency. Consider documenting this exception or using distinct widget names ('previewMarkdown','previewPlaintext') to align with system-wide design assumptions.src/extensions/core/widgetInputs.ts (2)
17-21: Good centralization of widget type validation.Importing
isValidWidgetTypefrom the widgets module instead of doing an inlinetype in ComfyWidgetscheck improves maintainability and provides consistent type narrowing across the codebase.
230-236: Type narrowing works correctly with the guard.After
isValidWidgetType(type)returns true,typeis narrowed tokeyof typeof ComfyWidgets, allowing safe indexing intoComfyWidgets[type]. The else branch appropriately falls back toaddWidgetfor unknown types.src/composables/graph/useGraphNodeManager.ts (3)
13-16: Good type refinement withIWidgetOptions.Importing
IWidgetOptionsallows theSafeWidgetData.optionsfield to have a more specific type than the genericRecord<string, unknown>, providing better autocompletion and type checking for known widget option properties.
45-45: Type improvement for options field.Changing from
Record<string, unknown>toIWidgetOptions<unknown>provides more accurate typing. The<unknown>generic parameter correctly indicates that thevaluesarray type is not known at this level.
113-113: Direct assignment is correct.Assigning
widget.optionsdirectly (instead of spreading) is appropriate sinceIWidgetOptionsis already the correct type. Spreading would have been necessary only if the source type was incompatible.src/renderer/extensions/vueNodes/components/NodeWidgets.vue (4)
19-26: Good template restructuring for hidden widget support.Using a
<template>wrapper withv-ifon the inner div correctly filters out hidden widgets from the DOM while maintaining the loop structure. Thedata-is-hiddenattribute is useful for debugging.
30-50: InputSlot integration for widget connection dots.Adding the InputSlot component with
dot-onlymode correctly renders connection points for widgets that have associated input slots. The conditional rendering based onwidget.slotMetadatais appropriate.
165-174: Good refactor to function-based update handler.Converting
updateHandlerfrom an object method to a standalone function is cleaner and avoids potentialthisbinding issues. The logic correctly updates the widget value and conditionally calls the callback.
146-153: Correct conditional disabling for linked widgets.When a widget's slot is linked (
slotMetadata?.linked), the widget should be disabled to prevent conflicting inputs. The spread withdisabled: truecorrectly achieves this without mutating the original options.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/extensions/core/previewAny.ts (1)
51-63: Consider initializing visibility state via the callback.The visibility initialization (lines 51-52, 58-59) duplicates the logic in the callback (lines 44-49), violating DRY principles. If the callback logic changes, these lines must be updated separately.
Apply this diff to avoid duplication:
showAsPlaintextWidget.widget.callback = (value) => { showValueWidget.hidden = !value showValueWidget.options.hidden = !value showValueWidgetPlain.hidden = value showValueWidgetPlain.options.hidden = value } - showValueWidget.hidden = true - showValueWidget.options.hidden = true showValueWidget.options.read_only = true showValueWidget.element.readOnly = true showValueWidget.element.disabled = true showValueWidget.serialize = false - showValueWidgetPlain.hidden = false - showValueWidgetPlain.options.hidden = false showValueWidgetPlain.options.read_only = true showValueWidgetPlain.element.readOnly = true showValueWidgetPlain.element.disabled = true showValueWidgetPlain.serialize = false + + // Initialize visibility based on default value + showAsPlaintextWidget.widget.callback(showAsPlaintextWidget.widget.value) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/extensions/core/previewAny.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.json
Files:
src/extensions/core/previewAny.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Use es-toolkit for utility functions
Files:
src/extensions/core/previewAny.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
Use TypeScript for type safety
**/*.{ts,tsx}: Never useanytype - use proper TypeScript types
Never useas anytype assertions - fix the underlying type issue
Files:
src/extensions/core/previewAny.ts
**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committing
Files:
src/extensions/core/previewAny.ts
src/**/*.{vue,ts}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.{vue,ts}: Leverage VueUse functions for performance-enhancing styles
Implement proper error handling
Use vue-i18n in composition API for any string literals. Place new translation entries in src/locales/en/main.json
Files:
src/extensions/core/previewAny.ts
src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.ts: Use es-toolkit for utility functions
Use TypeScript for type safety
Files:
src/extensions/core/previewAny.ts
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/extensions/core/previewAny.ts
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/extensions/core/previewAny.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Define dynamic setting defaults using runtime context with functions in settings configuration
UsedefaultsByInstallVersionproperty for gradual feature rollout based on version in settings configuration
Files:
src/extensions/core/previewAny.ts
src/**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (src/CLAUDE.md)
src/**/*.{ts,tsx,vue}: Sanitize HTML with DOMPurify to prevent XSS attacks
Avoid using @ts-expect-error; use proper TypeScript types instead
Use es-toolkit for utility functions instead of other utility libraries
Implement proper TypeScript types throughout the codebase
Files:
src/extensions/core/previewAny.ts
src/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/extensions/core/previewAny.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: playwright-tests-chromium-sharded (7, 8)
- GitHub Check: playwright-tests-chromium-sharded (8, 8)
- GitHub Check: playwright-tests-chromium-sharded (6, 8)
- GitHub Check: playwright-tests-chromium-sharded (2, 8)
- GitHub Check: playwright-tests-chromium-sharded (5, 8)
- GitHub Check: playwright-tests-chromium-sharded (3, 8)
- GitHub Check: playwright-tests-chromium-sharded (4, 8)
- GitHub Check: playwright-tests-chromium-sharded (1, 8)
- GitHub Check: playwright-tests (chromium-0.5x)
- GitHub Check: playwright-tests (mobile-chrome)
- GitHub Check: playwright-tests (chromium-2x)
🔇 Additional comments (3)
src/extensions/core/previewAny.ts (3)
44-49: LGTM! Toggle logic is correct.The callback correctly manages visibility for both preview widgets. When Markdown mode is active (
value = true), the Markdown widget is shown and plaintext is hidden, and vice versa. Both thehiddenproperty andoptions.hiddenare consistently updated.
73-78: LGTM! Correctly handles multiple preview widgets.The updated logic properly supports both preview widgets by filtering all widgets named 'preview' and updating each one with the preview text. This ensures both the Markdown and plaintext widgets stay synchronized with the node's output.
39-39: Internationalize user-facing label strings.The hardcoded strings
'Markdown'and'Plaintext'violate the i18n coding guideline. User-facing strings should use vue-i18n.As per coding guidelines, use vue-i18n for string literals and add entries to
src/locales/en/main.json. Apply this diff:- { label_on: 'Markdown', label_off: 'Plaintext', default: false } + { label_on: t('previewAny.labelMarkdown'), label_off: t('previewAny.labelPlaintext'), default: false }Then add to
src/locales/en/main.json:{ "previewAny": { "labelMarkdown": "Markdown", "labelPlaintext": "Plaintext" } }You'll also need to import
useI18nat the top and callconst { t } = useI18n()within the function scope.⛔ Skipped due to learnings
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: src/CLAUDE.md:0-0 Timestamp: 2025-11-24T19:47:34.324Z Learning: Applies to src/**/{components,composables}/**/*.{ts,tsx,vue} : Use vue-i18n for ALL user-facing strings by adding them to `src/locales/en/main.json`
AustinMroz
left a comment
There was a problem hiding this comment.
Functionality works well.
Some passing concern on the change to how hidden functions. There's a fairly lengthy tail of recommending custom node authors add a widget.computeSize to set the height to -4 when hidden. (This wouldn't quite work here due to the sizing on textAreas). However, I'm reasonably confident this change is safe. I can't think of a time you'd want a widget to be hidden, but still take up space.
Very worth calling out. I'll keep an eye out. |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/renderer/extensions/vueNodes/components/NodeWidgets.vue (1)
19-63: Per-widget template structure and hidden-widget handling look solid; minor optional tweak for data attributeThe move to a
<template v-for>with a single guarded wrapper per widget, plus the InputSlot column and widget component in the same grid row, looks correct and matches the grid semantics. Thev-if="!widget.simplified.options?.hidden"cleanly removes hidden widgets from layout calculations, which aligns with the PR intent.If you only need
data-is-hiddenfor debugging or CSS hooks, you might simplify it to a plain boolean instead of a formatted string, e.g.:- :data-is-hidden="`hidden: ${widget.simplified.options?.hidden}`" + :data-is-hidden="Boolean(widget.simplified.options?.hidden)"This keeps the attribute value predictable (
"true"/"false") while preserving current behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/renderer/extensions/vueNodes/components/NodeWidgets.vue(3 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.vue
📄 CodeRabbit inference engine (.cursorrules)
**/*.vue: Use setup() function for component logic in Vue 3 Composition API
Utilize ref and reactive for reactive state in Vue 3
Implement computed properties with computed() function
Use watch and watchEffect for side effects in Vue 3
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection in Vue 3
Use Vue 3.5 style of default prop declaration with defineProps()
Organize Vue components in <script> <style> order
Use Tailwind CSS for styling Vue components
Implement responsive design with Tailwind CSS
Do not use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage). Use replacements: Select, Popover, DatePicker, ToggleSwitch, Drawer, AutoComplete, Tabs, Stepper, Message respectively
Implement proper props and emits definitions in Vue components
Utilize Vue 3's Teleport component when needed
Use Suspense for async components in Vue 3
Follow Vue 3 style guide and naming conventions
Never use deprecated PrimeVue components (Dropdown, OverlayPanel, Calendar, InputSwitch, Sidebar, Chips, TabMenu, Steps, InlineMessage)Never use
:class="[]"to merge class names - always useimport { cn } from '@/utils/tailwindUtil'for class merging in Vue templates
**/*.vue: Use TypeScript with Vue 3 Single File Components (.vuefiles)
Name Vue components in PascalCase (e.g.,MenuHamburger.vue)Files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{vue,ts,tsx}: Leverage VueUse functions for performance-enhancing utilities
Use vue-i18n in Composition API for any string literals and place new translation entries in src/locales/en/main.jsonFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{ts,tsx,js,vue}
📄 CodeRabbit inference engine (.cursorrules)
Implement proper error handling in components and services
**/*.{ts,tsx,js,vue}: Use 2-space indentation, single quotes, no semicolons, and maintain 80-character line width as configured in.prettierrc
Organize imports by sorting and grouping by plugin, and runpnpm formatbefore committingFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use the Vue 3 Composition API instead of the Options API when writing Vue components (exception: when overriding or extending PrimeVue components for compatibility)
Use setup() function for component logic
Utilize ref and reactive for reactive state
Implement computed properties with computed()
Use watch and watchEffect for side effects
Implement lifecycle hooks with onMounted, onUpdated, etc.
Utilize provide/inject for dependency injection
Use vue 3.5 style of default prop declaration
Use Tailwind CSS for styling
Implement proper props and emits definitions
Utilize Vue 3's Teleport component when needed
Use Suspense for async components
Follow Vue 3 style guide and naming conventionsFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/*.{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.jsonFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for variable and setting names in TypeScript/Vue files
Files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{vue,html}
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
dark:ordark-theme:Tailwind variants - instead use semantic values fromstyle.csstheme, e.g.bg-node-component-surfaceFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,vue}: Useconst settingStore = useSettingStore()andsettingStore.get('Comfy.SomeSetting')to retrieve settings in TypeScript/Vue files
Useawait settingStore.set('Comfy.SomeSetting', newValue)to update settings in TypeScript/Vue files
Check server capabilities usingapi.serverSupportsFeature('feature_name')before using enhanced features
Useapi.getServerFeature('config_name', defaultValue)to retrieve server feature configurationEnforce ESLint rules for Vue + TypeScript including: no floating promises, no unused imports, and i18n raw text restrictions in templates
Files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/*.{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 codebaseFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/{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/NodeWidgets.vuesrc/**/*.{vue,ts,tsx}
📄 CodeRabbit inference engine (src/CLAUDE.md)
Follow Vue 3 composition API style guide
Files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vuesrc/**/{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.jsonFiles:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue🧠 Learnings (4)
📚 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 stateApplied to files:
src/renderer/extensions/vueNodes/components/NodeWidgets.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 : Implement computed properties with computed()Applied to files:
src/renderer/extensions/vueNodes/components/NodeWidgets.vue📚 Learning: 2025-11-24T19:46:52.279Z
Learnt from: CR Repo: Comfy-Org/ComfyUI_frontend PR: 0 File: .cursorrules:0-0 Timestamp: 2025-11-24T19:46:52.279Z Learning: Applies to **/*.vue : Utilize ref and reactive for reactive state in Vue 3Applied to files:
src/renderer/extensions/vueNodes/components/NodeWidgets.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 APIApplied to files:
src/renderer/extensions/vueNodes/components/NodeWidgets.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: collect
- GitHub Check: test
- GitHub Check: lint-and-format
🔇 Additional comments (1)
src/renderer/extensions/vueNodes/components/NodeWidgets.vue (1)
139-188: Widget options/slot metadata processing and update handler look correctThe
processedWidgetscomputed mapping looks good:
- Using
shouldRenderAsVue(widget)as a gate and resolvingvueComponentviagetComponentwith a WidgetDOM/WidgetLegacy fallback is consistent with the existing pattern.- Destructuring
slotMetadataandoptions, then derivingwidgetOptionswithdisabled: truewhenslotMetadata?.linkedavoids mutating the originalwidget.optionswhile still enforcing read-only behavior for linked slots.- The
simplifiedobject construction is straightforward and no longer wrapped inreactive, which matches previous feedback and keeps reactivity anchored on the originalwidgetandwidgetOptions.- The
updateHandlerclosure mutatingwidget.valueand skipping callbacks forassetwidgets preserves the prior behavior.No changes requested here. This is a clean refinement of the widget processing pipeline.
## Summary Adds a toggle to conditionally render the text as Markdown. ## Also... Fixes some type issues across our myriad Widget types. We should probably clean those up. ## Example https://github.com/user-attachments/assets/24fed943-1e79-4ea4-a962-826b06d68761 This could be a good minimal testcase for dynamic widgets @AustinMroz ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7102-WIP-Feat-Add-preview-as-plaintext-toggle-for-Preview-As-Text-2bd6d73d3650810c8b25c84866c8875c) by [Unito](https://www.unito.io)
Summary
Adds a toggle to conditionally render the text as Markdown.
Also...
Fixes some type issues across our myriad Widget types. We should probably clean those up.
Example
Screen.Recording.2025-12-02.140823.mp4
This could be a good minimal testcase for dynamic widgets @AustinMroz
┆Issue is synchronized with this Notion page by Unito