- kaleido: a field folded about the frame centre for symmetric compositions; octaves is the fold order
- temporal_rate socket on Direct, Walk and Source sets how fast a held pattern evolves across a clip
- drift with use_temporal_coherence no longer collapses to jump; each draw holds its own seed
- gaussian no longer shares its first channel's seed with the sampler's base noise
- verification/describe_field.py scores a shader type without rendering; analyze.py --bands
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A preset was applied at execution time, so the panel lied: select explore and
shader_strength kept showing its old value while the run used 0.30.
web/src/preset_widgets.ts wraps the preset widget's callback and writes the
bundle into the widgets it names, running each widget's own callback so the
shader preview follows. Editing one of those widgets to a value the preset does
not hold drops the preset back to custom -- otherwise the panel would just lie
the other way. The Walk node keeps the parameter it ramps, and a saved workflow is
never rewritten on load.
The table comes from GET /shader_noise_ksampler/presets rather than a copy in
TypeScript, because preset values get recalibrated and a copy would drift. The
Python override stays the authority: API submissions and the Walk node never run
the JS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The preview now attaches to ShaderNoiseKSamplerDirect as well. That node
already exposes every shader control as a real input, so instead of adding a
second, unconnected copy of each widget, the preview mirrors the node's own
inputs into the properties its WebGL uniforms read, and follows them as they
change.
Its four preview-only widgets (Show Shader, Show Tooltips, Animation Speed,
Pixel Resolution) are marked serialize: false there. Saved workflows map widget
values by position, so serializing them would shift every stored input when an
older workflow loads.
The deprecated display node keeps its own widgets and behaviour unchanged.
Also adds vitest coverage for the sampling-mode migration helpers.
Typecheck clean, 91 web tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Direct node now dispatches: "standard" (default) runs the new pipeline,
"legacy" runs the frozen pre-2.0 one. Measured through the node with a
recording sampler, 20 steps:
standard, 1 stage 1 call x 20 steps, from sigma 14.61
standard, 2 stages 2 calls x 10 steps, from 14.61 then 1.48
standard, denoise 0.6 1 call x 20 steps, from 2.23
standard, 3 injections 4 calls x 5 steps, 14.61 / 3.87 / 1.48 / 0.60
legacy, 2 stages 2 calls, each rebuilding a full schedule
The 1.48 is the fix: stages continue one trajectory instead of restarting at
maximum noise, where flow models discard the previous stage entirely. The 2.23
is denoise finally reaching the schedule.
Also on the node:
- sequential_distribution, injection_distribution and fast_high_channel_noise
become real optional inputs. As V1 `hidden` tuple inputs ComfyUI never
delivered them, so they were stuck at their defaults.
- the debug/visualisation hidden inputs are gone; they drove stub no-ops.
- IS_CHANGED is removed: it only restated widget values that are already part
of the cache key, and would have rejected the new input.
- new widgets are appended last, so saved workflows keep their widget order.
web/src/sampling_mode_migration.ts switches nodes loaded from pre-2.0
workflows to "legacy", recognising them by the absence of the snk_version
property, so existing seeds keep reproducing.
186 Python tests and 85 web tests pass; the 11 legacy goldens now exercise the
legacy branch through this dispatch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- direct_shader_ksampler: remove ParameterResponseMapper and its call site.
Its only trigger, target_attribute_changes, is a V1 `hidden` tuple input,
which ComfyUI never passes to a node, so the block was unreachable.
- advanced_comparer.py: an older duplicate of nodes/comparer.py that nothing
imported (the live class is exported through nodes/__init__.py).
- web/types/*.js(.map): compiled leftovers of the type-only .ts declarations.
ComfyUI loads web/**/*.js recursively, so these were served to the browser
as extensions for no reason. The .ts sources stay.
All 11 legacy goldens, 64 generator smoke tests and 85 web tests still pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Restore focus to main canvas when closing the matrix modal to support keyboard navigation
- Update aria-label to 'Copied' on copy button success to support screen readers
- Conditionally restore original aria-label to prevent overwriting on error
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
- Restore focus to main canvas when closing the matrix modal to support keyboard navigation
- Update aria-label to 'Copied' on copy button success to support screen readers
- Restore original aria-label after timeout
Co-authored-by: AEmotionStudio <163354043+AEmotionStudio@users.noreply.github.com>
- Add api_routes.py with POST /shader_noise_ksampler/save_params endpoint
- Update frontend to use fetch() API instead of browser download
- Register API routes with PromptServer in __init__.py
- Add CHANGELOG.md with version history
- Update README.md with navigation links and version 1.3.0
- Video comparer optimizations for memory efficiency
- Fix absolute imports in shaders/ to use relative paths
- Correct indentation in shader files
- Refactor video_comparer.py and nodes/video_comparer.py
- Add new example workflows
- Update gitignore and package.json
Remove export keyword from TITLE_GRADIENT_COLORS since it's only used
internally by createTitleGradient() and never imported elsewhere.
All 85 tests pass.
- Simplify redundant shimmer conditionals:
Both branches called calculateShimmerPosition(1.0) identically.
Now unconditionally compute shimmer, only cache lastTime conditionally.
- Use TITLE_CORNER_RADIUS constant:
Replaced local 'const cornerRadius = 8' in both golden_eyeball.ts and
gradient_title.ts with the shared TITLE_CORNER_RADIUS constant.
All 85 tests pass.
- Remove redundant Side-by-Side case in computeSize():
The case set height = Math.max(500, width) which was identical to the
default initialization, so it was effectively dead code.
- Extract resetButtonTimeout helper in copyCodeSection():
Both success and error handlers had identical timeout reset logic.
Consolidated into a single helper function, reducing ~25 lines.
All 85 tests pass.
- Restore getExtraMenuOptions context menu in advanced_comparer.ts:
- Layout Mode submenu with all 8 comparison modes
- Reset to Default Size option
- Consistent with video_comparer.ts implementation
- Create COMPARER_MODES constant to eliminate duplicate arrays:
- Used in @comparer_mode property definition
- Used in layoutWidget combo values
- Used in context menu submenu
- Share AnimationCache type in gradient_title.ts:
- Import AnimationCache from golden_eyeball.js
- Remove duplicate RenderCache interface definition
- Note: gradient_title.ts intentionally has its own drawGradientTitle
for equation-styled titles (different visual from golden eyeball)
All 85 tests pass.
- Add .js extension to local imports for browser ES module compatibility
(./golden_eyeball → ./golden_eyeball.js, ./comfy_utils → ./comfy_utils.js)
- Remove unused api import from advanced_comparer.ts and video_comparer.ts
(no longer needed since imageDataToUrl moved to comfy_utils)
- Remove unreachable dead code in gradient_title.ts
(else branch was never reached due to early return for collapsed state)
All 85 tests pass.
- Remove export from drawGoldenEyeball (internal function only)
Makes it a private implementation detail in golden_eyeball.ts
- Remove unused CACHE properties in video_comparer.ts:
- titleCanvas, titleCtx, lastWidth, lastHeight, collapsed
- Now only keeps frameCount, frameSkip, lastTime needed by AnimationCache
- Remove dead cleanup code in onRemoved
- Extract shared imageDataToUrl utility:
- Create new comfy_utils.ts module
- Update advanced_comparer.ts to import shared function
- Update video_comparer.ts to import shared function
- Eliminates duplicated URL building logic
All 85 tests pass.
- Add calculateScaledDimensions() private method to AdvancedImageComparerWidget
- Replace duplicated aspect ratio calculation in 5 draw methods:
- drawImage
- drawImageSideBySide
- drawImageStacked
- drawImageInCell
- drawImageInPair
The helper calculates scaled dimensions to fit an image within usable area
while maintaining aspect ratio, eliminating ~15 lines of repeated logic.
Addresses PR #41 review comment about code duplication.
- Remove unused 'last_y' property from AdvancedImageComparerWidget class
(property was set but never read)
- Remove unused 'declare const app' statement in gradient_title.ts
- Simplify import handling: use direct 'import { app }' instead of alias
Files modified:
- web/src/advanced_comparer.ts
- web/src/gradient_title.ts
- Remove unreachable else branch in drawGradientTitle that could never execute
after early return for collapsed state (line 301-303)
- Remove hidden clearCacheButton debug widget that served no functional purpose
(lines 768-771, also from interface)
Files modified:
- web/src/advanced_comparer.ts
💡 What:
- Exposed the `showToast` utility from `web/shader_params_save_button.js` as `window.showComfyToast`.
- Updated `web/matrix_button.js` to use `window.showComfyToast` for the "Copy Code" feedback.
- Added `:focus-visible` styles to the "Shader Matrix" modal buttons and tabs.
🎯 Why:
- To provide consistent and delightful feedback when copying code snippets.
- To improve accessibility for keyboard users by making focus states clearly visible.
📸 Before/After:
- Before: Copy button text changed to "Copied!". No visible focus ring on dark modal buttons.
- After: "Code copied to clipboard!" toast appears. Interactive elements have a bright purple focus ring.
♿ Accessibility:
- Added `:focus-visible` outline styles using the theme's accent color.
💡 What: Replaced subtle button text changes and console logs with a Toast notification system for the "Save Shader Parameters" button.
🎯 Why: Users need clear, accessible feedback when saving parameters to know if the action succeeded or failed. The previous method was easy to miss.
📸 Verification: Verified via Playwright script `verify_toast.py` with mock environment. Screenshot confirms visibility and styling.
♿ Accessibility: Included `role='alert'` and high-contrast styling for the notifications.
🔧 Fixes:
- Addressed potential zombie process in verification script.
- Added fallback cleanup for toast elements if CSS transitions are disabled.
- Fixed event listener memory leak in toast fallback.
- Standardized fallback logic in test environment.
💡 What: Replaced subtle button text changes and console logs with a Toast notification system for the "Save Shader Parameters" button.
🎯 Why: Users need clear, accessible feedback when saving parameters to know if the action succeeded or failed. The previous method was easy to miss.
📸 Verification: Verified via Playwright script `verify_toast.py` with mock environment. Screenshot confirms visibility and styling.
♿ Accessibility: Included `role='alert'` and high-contrast styling for the notifications.
🔧 Fixes:
- Addressed potential zombie process in verification script.
- Added fallback cleanup for toast elements if CSS transitions are disabled.
💡 What: Replaced subtle button text changes and console logs with a Toast notification system for the "Save Shader Parameters" button.
🎯 Why: Users need clear, accessible feedback when saving parameters to know if the action succeeded or failed. The previous method was easy to miss.
📸 Verification: Verified via Playwright script `verify_toast.py` with mock environment. Screenshot confirms visibility and styling.
♿ Accessibility: Included `role='alert'` and high-contrast styling for the notifications.
- Implements a `keydown` listener on the modal to intercept Tab and Shift+Tab key presses.
- Dynamically queries visible focusable elements (`button`, `input`, etc.) that have a valid `offsetParent`.
- Loops focus between the first and last focusable elements to prevent keyboard users from tabbing out of the modal into obscured content.
- Enhances accessibility for screen reader and keyboard-only users.
Updated the noise name generation logic to correctly capitalize 'FBM' and '3D' acronyms in aria-labels, ensuring consistency with the UI titles and better screen reader experience.
Enhance accessibility for the noise visualization modal by:
- Adding `role="img"` to all generated canvas elements.
- Adding descriptive `aria-label` attributes derived from the noise type ID (e.g., "Visualization of Perlin noise pattern").
- This ensures screen reader users perceive these visualizations as images with meaningful descriptions rather than empty, untagged content.
Verified with `verification/verify_canvas_a11y.js` which mocks the DOM environment.
- Added `window.copyCodeSection` helper in `web/matrix_button.js`.
- Updated documentation modal HTML to include "Copy" buttons next to "Show" buttons for all code examples.
- Added CSS for `.button-group` and `.copy-code-button` with hover and success states.
- Implemented robust error handling for clipboard access and success/error feedback loops.
- Fixed race condition in button state reset by using explicit string "Copy" and clearing timeouts.
- Restored `verification/verify_dos_fix.py` which was inadvertently deleted.
- Hardened error handler to clean up state and manage timeouts correctly.
- Added `window.copyCodeSection` helper in `web/matrix_button.js`.
- Updated documentation modal HTML to include "Copy" buttons next to "Show" buttons for all code examples.
- Added CSS for `.button-group` and `.copy-code-button` with hover and success states.
- Implemented robust error handling for clipboard access and success/error feedback loops.
- Fixed race condition in button state reset by using explicit string "Copy" and clearing timeouts.
- Restored `verification/verify_dos_fix.py` which was inadvertently deleted.
- Added `window.copyCodeSection` helper in `web/matrix_button.js`.
- Updated documentation modal HTML to include "Copy" buttons next to "Show" buttons for all code examples.
- Added CSS for `.button-group` and `.copy-code-button` with hover and success states.
- Implemented robust error handling for clipboard access and success/error feedback loops.
- Restored `verification/verify_dos_fix.py` which was inadvertently deleted.
- Add `aria-expanded` and `aria-controls` to code block toggle buttons in `web/matrix_button.js`.
- Add unique IDs to collapsible code `pre` blocks.
- Update `toggleCodeSection` to dynamically toggle `aria-expanded` state.
- Improve screen reader support for the Shader Matrix modal.
🚨 Severity: HIGH
💡 Vulnerability: User-controlled input in `subfolder` and `type` parameters was concatenated directly into API URLs without encoding in `web/video_comparer.js` and `web/advanced_comparer.js`.
🎯 Impact: This could allow parameter injection or result in broken URLs if filenames/subfolders contain special characters.
🔧 Fix: Wrapped `type` and `subfolder` parameters with `encodeURIComponent` when constructing the `view` API URL.
✅ Verification: Manual verification of the code.
Add full ARIA support and keyboard navigation (Arrow keys, Enter/Space) to the documentation tabs in the Shader Matrix modal.
- Add `role="tablist"`, `role="tab"`, `role="tabpanel"`.
- Implement roving `tabindex` and `aria-selected` management.
- Add `window.handleTabNavigation` to support Arrow Left/Right navigation between tabs.
- Ensure visual focus order matches logical tab order.
- Added `role="dialog"`, `aria-modal="true"`, and `aria-label` to the modal container.
- Added `aria-label` to the icon-only "Close" and "Scroll to top" buttons.
- Implemented initial focus management to set focus on the "Close" button when the modal opens.
These changes ensure screen reader users can correctly identify the modal and navigate its controls.