[feat] Add Storybook setup and NodePreview story (#4861)

* [feat] Add Storybook setup and NodePreview story

- Install and configure Storybook v9.1.1 for Vue 3
- Set up Storybook configuration with Vite integration
- Add Pinia store support for Storybook environment
- Create comprehensive NodePreview.stories.ts with multiple node examples:
  - KSampler node (complex node with multiple inputs/outputs)
  - CLIP Text Encode node (simple text input node)
  - VAE Decode node (image processing node)
  - Example with long markdown description
- Configure project paths and aliases for Storybook
- Stories demonstrate various ComfyUI node types with realistic mock data
- Update tsconfig.eslint.json to include Storybook files
- Fix ESLint issues with imports and number precision
- Add Storybook ESLint plugin configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [feat] Improve Storybook configuration and setup

- Add comprehensive PrimeVue theme setup with ComfyUI preset
- Configure proper Vue app setup with Pinia stores, i18n, and services
- Remove unused onboarding addon from Storybook dependencies
- Improve Vite configuration with better chunking and alias resolution
- Add proper CSS imports and styling for ComfyUI components

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [docs] Add comprehensive Storybook documentation

- Add README.md explaining Storybook usage, benefits, and comparison with other tools
- Add CLAUDE.md with development guidelines for working with Storybook
- Include best practices, troubleshooting tips, and integration notes
- Address PR review feedback for better developer onboarding

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [refactor] Remove ts-expect-error comment from Storybook preview

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [bugfix] Fix TypeScript errors in Load3D components and GLTF test

- Fix type mismatches in Load3DScene eventConfig by casting string values to proper enum types (MaterialMode, CameraType, UpDirection)
- Fix Uint8Array vs ArrayBuffer type issues in GLTF test by using .buffer property
- Remove unused @ts-expect-error comment in Rectangle.ts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [feat] Add Chromatic GitHub Action for Storybook visual testing

- Add automated visual regression testing for Storybook components
- Configure workflow to run on main branch and PRs
- Auto-accept changes on main branch for baseline updates
- Uses build-storybook script for optimized builds

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [docs] Add Chromatic documentation to Storybook README

- Document Chromatic visual testing integration
- Add information about automated testing workflow
- Include best practices for visual regression testing
- Explain how to view and manage test results

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* chore(chromatic.yaml): restrict push branches to main only for better workflow management

* [feat] Rebase branch onto main and update Storybook configuration

- Rebase sno-storybook branch onto origin/main with latest changes
- Update .storybook/main.ts with additional plugins and component configuration
- Add icons and component resolvers for Storybook support
- Update .gitignore with new entries
- Regenerate package-lock.json after rebase conflicts

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [bugfix] Fix TypeScript errors in SubgraphNode type checking

Add proper type validation for subgraph node selection before calling
SubgraphNode-specific methods. This prevents undefined values from being
passed to functions expecting SubgraphNode parameters.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(vite.config.mts): correct path alias for src directory to ensure proper resolution in the project
refactor(vite.config.mts): adjust templates proxy configuration for better readability and maintainability

* [feat] Remove bun.lock as it's now ignored

* [bugfix] Fix Storybook builder require() error by converting main.ts to main.mjs

- Convert .storybook/main.ts to main.mjs to resolve ES module compatibility
- Use dynamic imports instead of static imports to avoid require() errors
- Add .storybook directory to tsconfig.json includes
- Storybook build and dev server now work correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* chore(storybook): replace main.mjs with main.ts for improved type safety and maintainability
fix(storybook): remove unused import map plugins in Storybook configuration to prevent potential issues
fix(storybook): update color palette store initialization to streamline code and improve readability

* [feat] Optimize Chromatic workflow with automated PR status comments

- Replace complex GitHub Script actions with edumserrano/find-create-or-update-comment@v3
- Add comprehensive PR comments showing Storybook build progress and results
- Include build metrics: components, stories, visual changes, and errors
- Add direct links to Chromatic builds and Storybook previews
- Reduce workflow complexity by ~60 lines while maintaining functionality
- Use native GitHub Actions expressions for cleaner maintainability

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* chore(chromatic.yaml): move permissions section inside the chromatic-deployment job for better organization and clarity

* [fix] Resolve Vite CJS deprecation warning in Storybook config

- Use dynamic import for mergeConfig to avoid CJS build warning
- Replace static import with dynamic import in viteFinal function
- Maintain type safety with separate type import
- Fixes "The CJS build of Vite's Node API is deprecated" warning

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(chromatic.yaml): change edit-mode from replace to append to preserve existing comments in pull request

* [fix] Replace __dirname with process.cwd() in Storybook config

__dirname is not available in all environments. Using process.cwd()
provides better compatibility and resolves path issues in Storybook.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feature: storybook-setting (#5088)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jin Yi <jin12cc@gmail.com>
This commit is contained in:
snomiao
2025-08-19 11:05:28 +08:00
committed by GitHub
parent 727a3494e0
commit 451ef24ea6
19 changed files with 2459 additions and 608 deletions

View File

@@ -0,0 +1,261 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import type { ComfyNodeDef as ComfyNodeDefV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
import NodePreview from './NodePreview.vue'
// Mock node definition data for a typical ComfyUI node
const mockNodeDef: ComfyNodeDefV2 = {
name: 'KSampler',
display_name: 'KSampler',
description:
'The main sampler node for generating images. Controls the denoising process using various samplers and schedulers.',
category: 'sampling',
output_node: false,
python_module: 'nodes',
inputs: {
model: {
type: 'MODEL',
name: 'model',
isOptional: false
},
positive: {
type: 'CONDITIONING',
name: 'positive',
isOptional: false
},
negative: {
type: 'CONDITIONING',
name: 'negative',
isOptional: false
},
latent_image: {
type: 'LATENT',
name: 'latent_image',
isOptional: false
},
seed: {
type: 'INT',
name: 'seed',
default: 0,
min: 0,
max: Number.MAX_SAFE_INTEGER,
isOptional: false
},
steps: {
type: 'INT',
name: 'steps',
default: 20,
min: 1,
max: 10000,
isOptional: false
},
cfg: {
type: 'FLOAT',
name: 'cfg',
default: 8.0,
min: 0.0,
max: 100.0,
step: 0.1,
isOptional: false
},
sampler_name: {
type: 'COMBO',
name: 'sampler_name',
options: [
'euler',
'euler_ancestral',
'heun',
'dpm_2',
'dpm_2_ancestral',
'lms',
'dpm_fast',
'dpm_adaptive',
'dpmpp_2s_ancestral',
'dpmpp_sde',
'dpmpp_2m'
],
default: 'euler',
isOptional: false
},
scheduler: {
type: 'COMBO',
name: 'scheduler',
options: [
'normal',
'karras',
'exponential',
'sgm_uniform',
'simple',
'ddim_uniform'
],
default: 'normal',
isOptional: false
},
denoise: {
type: 'FLOAT',
name: 'denoise',
default: 1.0,
min: 0.0,
max: 1.0,
step: 0.01,
isOptional: false
}
},
outputs: [
{
index: 0,
name: 'LATENT',
type: 'LATENT',
is_list: false
}
]
}
// Simpler text node for comparison
const mockTextNodeDef: ComfyNodeDefV2 = {
name: 'CLIPTextEncode',
display_name: 'CLIP Text Encode',
description:
'Encode text using CLIP to create conditioning for image generation.',
category: 'conditioning',
output_node: false,
python_module: 'nodes',
inputs: {
clip: {
type: 'CLIP',
name: 'clip',
isOptional: false
},
text: {
type: 'STRING',
name: 'text',
multiline: true,
default: '',
isOptional: false
}
},
outputs: [
{
index: 0,
name: 'CONDITIONING',
type: 'CONDITIONING',
is_list: false
}
]
}
// Image processing node with multiple outputs
const mockImageNodeDef: ComfyNodeDefV2 = {
name: 'VAEDecode',
display_name: 'VAE Decode',
description:
'Decode latent representation back to image using a Variational Autoencoder.',
category: 'latent',
output_node: false,
python_module: 'nodes',
inputs: {
samples: {
type: 'LATENT',
name: 'samples',
isOptional: false
},
vae: {
type: 'VAE',
name: 'vae',
isOptional: false
}
},
outputs: [
{
index: 0,
name: 'IMAGE',
type: 'IMAGE',
is_list: false
}
]
}
const meta: Meta<typeof NodePreview> = {
title: 'Components/Node/NodePreview',
component: NodePreview,
parameters: {
layout: 'centered',
docs: {
description: {
component:
'NodePreview displays a preview of a ComfyUI node with its inputs, outputs, and parameters. This component is used to show node information in sidebars and tooltips.'
}
}
},
argTypes: {
nodeDef: {
description: 'Node definition object containing all node metadata',
control: { type: 'object' }
}
},
tags: ['autodocs']
}
export default meta
type Story = StoryObj<typeof NodePreview>
export const KSamplerNode: Story = {
args: {
nodeDef: mockNodeDef
},
parameters: {
docs: {
description: {
story:
'KSampler node - the main sampling node with multiple inputs including model, conditioning, and sampling parameters.'
}
}
}
}
export const TextEncodeNode: Story = {
args: {
nodeDef: mockTextNodeDef
},
parameters: {
docs: {
description: {
story:
'CLIP Text Encode node - simple text input node for creating conditioning.'
}
}
}
}
export const ImageProcessingNode: Story = {
args: {
nodeDef: mockImageNodeDef
},
parameters: {
docs: {
description: {
story:
'VAE Decode node - converts latent representation back to images.'
}
}
}
}
export const WithLongDescription: Story = {
args: {
nodeDef: {
...mockNodeDef,
description:
'This is an example of a node with a very long description that should wrap properly and display in the description area below the node preview. It demonstrates how the component handles extensive documentation text and formatting. The description supports **markdown** formatting including *italics* and other styling elements.'
}
},
parameters: {
docs: {
description: {
story:
'Node preview with a longer, markdown-formatted description to test text wrapping and styling.'
}
}
}
}