diff --git a/.cursor/rules/unit-test.mdc b/.cursor/rules/unit-test.mdc deleted file mode 100644 index 2c6704f3e..000000000 --- a/.cursor/rules/unit-test.mdc +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Creating unit tests -globs: -alwaysApply: false ---- - -# Creating unit tests - -- This project uses `vitest` for unit testing -- Tests are stored in the `test/` directory -- Tests should be cross-platform compatible; able to run on Windows, macOS, and linux - - e.g. the use of `path.resolve`, or `path.join` and `path.sep` to ensure that tests work the same on all platforms -- Tests should be mocked properly - - Mocks should be cleanly written and easy to understand - - Mocks should be re-usable where possible - -## Unit test style - -- Prefer the use of `test.extend` over loose variables - - To achieve this, import `test as baseTest` from `vitest` -- Never use `it`; `test` should be used in place of this \ No newline at end of file diff --git a/.github/AGENTS.md b/.github/AGENTS.md new file mode 100644 index 000000000..0b64ac21b --- /dev/null +++ b/.github/AGENTS.md @@ -0,0 +1,14 @@ +# PR Review Context + +Context for automated PR review system. + +## Review Scope + +This automated review performs comprehensive analysis: +- Architecture and design patterns +- Security vulnerabilities +- Performance implications +- Code quality and maintainability +- Integration concerns + +For implementation details, see `.claude/commands/comprehensive-pr-review.md`. diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index 9a95d8cd0..3c928db39 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -1,36 +1,3 @@ -# ComfyUI Frontend - Claude Review Context - -This file provides additional context for the automated PR review system. - -## Quick Reference - -### PrimeVue Component Migrations - -When reviewing, flag these deprecated components: -- `Dropdown` → Use `Select` from 'primevue/select' -- `OverlayPanel` → Use `Popover` from 'primevue/popover' -- `Calendar` → Use `DatePicker` from 'primevue/datepicker' -- `InputSwitch` → Use `ToggleSwitch` from 'primevue/toggleswitch' -- `Sidebar` → Use `Drawer` from 'primevue/drawer' -- `Chips` → Use `AutoComplete` with multiple enabled and typeahead disabled -- `TabMenu` → Use `Tabs` without panels -- `Steps` → Use `Stepper` without panels -- `InlineMessage` → Use `Message` component - -### API Utilities Reference - -- `api.apiURL()` - Backend API calls (/prompt, /queue, /view, etc.) -- `api.fileURL()` - Static file access (templates, extensions) -- `$t()` / `i18n.global.t()` - Internationalization -- `DOMPurify.sanitize()` - HTML sanitization - -## Review Scope - -This automated review performs comprehensive analysis including: -- Architecture and design patterns -- Security vulnerabilities -- Performance implications -- Code quality and maintainability -- Integration concerns - -For implementation details, see `.claude/commands/comprehensive-pr-review.md`. \ No newline at end of file + +@AGENTS.md diff --git a/.storybook/AGENTS.md b/.storybook/AGENTS.md new file mode 100644 index 000000000..5f6373f4b --- /dev/null +++ b/.storybook/AGENTS.md @@ -0,0 +1,17 @@ +# Storybook Guidelines + +See `@docs/guidance/storybook.md` for story patterns (auto-loaded for `*.stories.ts`). + +## Available Context + +Stories have access to: +- All ComfyUI stores +- PrimeVue with ComfyUI theming +- i18n system +- CSS variables and styling + +## Troubleshooting + +1. **Import Errors**: Verify `@/` alias works +2. **Missing Styles**: Check CSS imports in `preview.ts` +3. **Store Errors**: Check store initialization in setup diff --git a/.storybook/CLAUDE.md b/.storybook/CLAUDE.md index ca8248784..320b78815 100644 --- a/.storybook/CLAUDE.md +++ b/.storybook/CLAUDE.md @@ -1,197 +1,3 @@ -# Storybook Development Guidelines for Claude - -## Quick Commands - -- `pnpm storybook`: Start Storybook development server -- `pnpm build-storybook`: Build static Storybook -- `pnpm test:unit`: Run unit tests (includes Storybook components) - -## Development Workflow for Storybook - -1. **Creating New Stories**: - - Place `*.stories.ts` files alongside components - - Follow the naming pattern: `ComponentName.stories.ts` - - Use realistic mock data that matches ComfyUI schemas - -2. **Testing Stories**: - - Verify stories render correctly in Storybook UI - - Test different component states and edge cases - - Ensure proper theming and styling - -3. **Code Quality**: - - Run `pnpm typecheck` to verify TypeScript - - Run `pnpm lint` to check for linting issues - - Follow existing story patterns and conventions - -## Story Creation Guidelines - -### Basic Story Structure - -```typescript -import type { Meta, StoryObj } from '@storybook/vue3' -import ComponentName from './ComponentName.vue' - -const meta: Meta = { - title: 'Category/ComponentName', - component: ComponentName, - parameters: { - layout: 'centered' // or 'fullscreen', 'padded' - } -} - -export default meta -type Story = StoryObj - -export const Default: Story = { - args: { - // Component props - } -} -``` - -### Mock Data Patterns - -For ComfyUI components, use realistic mock data: - -```typescript -// Node definition mock -const mockNodeDef = { - input: { - required: { - prompt: ["STRING", { multiline: true }] - } - }, - output: ["CONDITIONING"], - output_is_list: [false], - category: "conditioning" -} - -// Component instance mock -const mockComponent = { - id: "1", - type: "CLIPTextEncode", - // ... other properties -} -``` - -### Common Story Variants - -Always include these story variants when applicable: - -- **Default**: Basic component with minimal props -- **WithData**: Component with realistic data -- **Loading**: Component in loading state -- **Error**: Component with error state -- **LongContent**: Component with edge case content -- **Empty**: Component with no data - -### Storybook-Specific Code Patterns - -#### Store Access -```typescript -// In stories, access stores through the setup function -export const WithStore: Story = { - render: () => ({ - setup() { - const store = useMyStore() - return { store } - }, - template: '' - }) -} -``` - -#### Event Testing -```typescript -export const WithEvents: Story = { - args: { - onUpdate: fn() // Use Storybook's fn() for action logging - } -} -``` - -## Configuration Notes - -### Vue App Setup -The Storybook preview is configured with: -- Pinia stores initialized -- PrimeVue with ComfyUI theme -- i18n internationalization -- All necessary CSS imports - -### Build Configuration -- Vite integration with proper alias resolution -- Manual chunking for better performance -- TypeScript support with strict checking -- CSS processing for Vue components - -## Troubleshooting - -### Common Issues - -1. **Import Errors**: Verify `@/` alias is working correctly -2. **Missing Styles**: Ensure CSS imports are in `preview.ts` -3. **Store Errors**: Check store initialization in setup -4. **Type Errors**: Use proper TypeScript types for story args - -### Debug Commands - -```bash -# Check TypeScript issues -pnpm typecheck - -# Lint Storybook files -pnpm lint .storybook/ - -# Build to check for production issues -pnpm build-storybook -``` - -## File Organization - -``` -.storybook/ -├── main.ts # Core configuration -├── preview.ts # Global setup and decorators -├── README.md # User documentation -└── CLAUDE.md # This file - Claude guidelines - -src/ -├── components/ -│ └── MyComponent/ -│ ├── MyComponent.vue -│ └── MyComponent.stories.ts -``` - -## Integration with ComfyUI - -### Available Context - -Stories have access to: -- All ComfyUI stores (widgetStore, colorPaletteStore, etc.) -- PrimeVue components with ComfyUI theming -- Internationalization system -- ComfyUI CSS variables and styling - -### Testing Components - -When testing ComfyUI-specific components: -1. Use realistic node definitions and data structures -2. Test with different node types (sampling, conditioning, etc.) -3. Verify proper CSS theming and dark/light modes -4. Check component behavior with various input combinations - -### Performance Considerations - -- Use manual chunking for large dependencies -- Minimize bundle size by avoiding unnecessary imports -- Leverage Storybook's lazy loading capabilities -- Profile build times and optimize as needed - -## Best Practices - -1. **Keep Stories Focused**: Each story should demonstrate one specific use case -2. **Use Descriptive Names**: Story names should clearly indicate what they show -3. **Document Complex Props**: Use JSDoc comments for complex prop types -4. **Test Edge Cases**: Create stories for unusual but valid use cases -5. **Maintain Consistency**: Follow established patterns in existing stories \ No newline at end of file + +@AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index 743572be3..da2953783 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,7 @@ # Repository Guidelines +See @docs/guidance/*.md for file-type-specific conventions (auto-loaded by glob). + ## Project Structure & Module Organization - Source: `src/` @@ -46,6 +48,21 @@ The project uses **Nx** for build orchestration and task management - `pnpm lint` / `pnpm lint:fix`: Lint (ESLint) - `pnpm format` / `pnpm format:check`: Prettier - `pnpm typecheck`: Vue TSC type checking +- `pnpm storybook`: Start Storybook development server + +## Development Workflow + +1. Make code changes +2. Run relevant tests +3. Run `pnpm typecheck`, `pnpm lint`, `pnpm format` +4. Check if README updates are needed +5. Suggest docs.comfy.org updates for user-facing changes + +## Git Conventions + +- Use `prefix:` format: `feat:`, `fix:`, `test:` +- Add "Fixes #n" to PR descriptions +- Never mention Claude/AI in commits ## Coding Style & Naming Conventions diff --git a/CLAUDE.md b/CLAUDE.md index 7ceadf85c..43c994c2d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,30 +1 @@ -# Claude Code specific instructions - -@Agents.md - -## Repository Setup - -For first-time setup, use the Claude command: - -```sh -/setup_repo -``` - -This bootstraps the monorepo with dependencies, builds, tests, and dev server verification. - -**Prerequisites:** Node.js >= 24, Git repository, available ports for dev server, storybook, etc. - -## Development Workflow - -1. **First-time setup**: Run `/setup_repo` Claude command -2. Make code changes -3. Run tests (see subdirectory CLAUDE.md files) -4. Run typecheck, lint, format -5. Check README updates -6. Consider docs.comfy.org updates - -## Git Conventions - -- Use `prefix:` format: `feat:`, `fix:`, `test:` -- Add "Fixes #n" to PR descriptions -- Never mention Claude/AI in commits +@AGENTS.md diff --git a/CODEOWNERS b/CODEOWNERS index fcba1e400..2612e3f22 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -37,7 +37,7 @@ /src/components/graph/selectionToolbox/ @Myestery # Minimap -/src/renderer/extensions/minimap/ @jtydhr88 @Myestery +/src/renderer/extensions/minimap/ @jtydhr88 @Myestery # Workflow Templates /src/platform/workflow/templates/ @Myestery @christian-byrne @comfyui-wiki @@ -55,8 +55,7 @@ /src/workbench/extensions/manager/ @viva-jinyi @christian-byrne @ltdrdata # Translations -/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer @Comfy-org/comfy_frontend_devs -/src/locales/pt-BR/ @JonatanAtila @Yorha4D @KarryCharon @shinshin86 +/src/locales/ @Comfy-Org/comfy_maintainer @Comfy-org/comfy_frontend_devs # LLM Instructions (blank on purpose) .claude/ diff --git a/browser_tests/AGENTS.md b/browser_tests/AGENTS.md new file mode 100644 index 000000000..177529ee1 --- /dev/null +++ b/browser_tests/AGENTS.md @@ -0,0 +1,8 @@ +# E2E Testing Guidelines + +See `@docs/guidance/playwright.md` for Playwright best practices (auto-loaded for `*.spec.ts`). + +## Directory Structure + +- `assets/` - Test data (JSON workflows, fixtures) +- Tests use premade JSON workflows to load desired graph state diff --git a/browser_tests/CLAUDE.md b/browser_tests/CLAUDE.md index cc42b392b..62a682148 100644 --- a/browser_tests/CLAUDE.md +++ b/browser_tests/CLAUDE.md @@ -1,17 +1,3 @@ -# E2E Testing Guidelines - -## Browser Tests -- Test user workflows -- Use Playwright fixtures -- Follow naming conventions - -## Best Practices -- Check assets/ for test data -- Prefer specific selectors -- Test across viewports - -## Testing Process -After code changes: -1. Create browser tests as appropriate -2. Run tests until passing -3. Then run typecheck, lint, format \ No newline at end of file + +@AGENTS.md diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 2f51533ce..fe439af20 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -21,7 +21,6 @@ import { import { Topbar } from './components/Topbar' import type { Position, Size } from './types' import { NodeReference, SubgraphSlotReference } from './utils/litegraphUtils' -import TaskHistory from './utils/taskHistory' dotenv.config() @@ -146,8 +145,6 @@ class ConfirmDialog { } export class ComfyPage { - private _history: TaskHistory | null = null - public readonly url: string // All canvas position operations are based on default view of canvas. public readonly canvas: Locator @@ -301,11 +298,6 @@ export class ComfyPage { } } - setupHistory(): TaskHistory { - this._history ??= new TaskHistory(this) - return this._history - } - async setup({ clearStorage = true, mockReleases = true diff --git a/browser_tests/fixtures/utils/taskHistory.ts b/browser_tests/fixtures/utils/taskHistory.ts deleted file mode 100644 index 01dfb1a4a..000000000 --- a/browser_tests/fixtures/utils/taskHistory.ts +++ /dev/null @@ -1,164 +0,0 @@ -import type { Request, Route } from '@playwright/test' -import _ from 'es-toolkit/compat' -import fs from 'fs' -import path from 'path' -import { v4 as uuidv4 } from 'uuid' - -import type { - HistoryTaskItem, - TaskItem, - TaskOutput -} from '../../../src/schemas/apiSchema' -import type { ComfyPage } from '../ComfyPage' - -/** keyof TaskOutput[string] */ -type OutputFileType = 'images' | 'audio' | 'animated' - -const DEFAULT_IMAGE = 'example.webp' - -const getFilenameParam = (request: Request) => { - const url = new URL(request.url()) - return url.searchParams.get('filename') || DEFAULT_IMAGE -} - -const getContentType = (filename: string, fileType: OutputFileType) => { - const subtype = path.extname(filename).slice(1) - switch (fileType) { - case 'images': - return `image/${subtype}` - case 'audio': - return `audio/${subtype}` - case 'animated': - return `video/${subtype}` - } -} - -const setQueueIndex = (task: TaskItem) => { - task.prompt[0] = TaskHistory.queueIndex++ -} - -const setPromptId = (task: TaskItem) => { - task.prompt[1] = uuidv4() -} - -export default class TaskHistory { - static queueIndex = 0 - static readonly defaultTask: Readonly = { - prompt: [0, 'prompt-id', {}, { client_id: uuidv4() }, []], - outputs: {}, - status: { - status_str: 'success', - completed: true, - messages: [] - }, - taskType: 'History' - } - private tasks: HistoryTaskItem[] = [] - private outputContentTypes: Map = new Map() - - constructor(readonly comfyPage: ComfyPage) {} - - private loadAsset: (filename: string) => Buffer = _.memoize( - (filename: string) => { - const filePath = this.comfyPage.assetPath(filename) - return fs.readFileSync(filePath) - } - ) - - private async handleGetHistory(route: Route) { - return route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify(this.tasks) - }) - } - - private async handleGetView(route: Route) { - const fileName = getFilenameParam(route.request()) - if (!this.outputContentTypes.has(fileName)) { - return route.continue() - } - - const asset = this.loadAsset(fileName) - return route.fulfill({ - status: 200, - contentType: this.outputContentTypes.get(fileName), - body: asset, - headers: { - 'Cache-Control': 'public, max-age=31536000', - 'Content-Length': asset.byteLength.toString() - } - }) - } - - async setupRoutes() { - return this.comfyPage.page.route( - /.*\/api\/(view|history)(\?.*)?$/, - async (route) => { - const request = route.request() - const method = request.method() - - const isViewReq = request.url().includes('view') && method === 'GET' - if (isViewReq) return this.handleGetView(route) - - const isHistoryPath = request.url().includes('history') - const isGetHistoryReq = isHistoryPath && method === 'GET' - if (isGetHistoryReq) return this.handleGetHistory(route) - - const isClearReq = - method === 'POST' && - isHistoryPath && - request.postDataJSON()?.clear === true - if (isClearReq) return this.clearTasks() - - return route.continue() - } - ) - } - - private createOutputs( - filenames: string[], - filetype: OutputFileType - ): TaskOutput { - return filenames.reduce((outputs, filename, i) => { - const nodeId = `${i + 1}` - outputs[nodeId] = { - [filetype]: [{ filename, subfolder: '', type: 'output' }] - } - const contentType = getContentType(filename, filetype) - this.outputContentTypes.set(filename, contentType) - return outputs - }, {}) - } - - private addTask(task: HistoryTaskItem) { - setPromptId(task) - setQueueIndex(task) - this.tasks.unshift(task) // Tasks are added to the front of the queue - } - - clearTasks(): this { - this.tasks = [] - return this - } - - withTask( - outputFilenames: string[], - outputFiletype: OutputFileType = 'images', - overrides: Partial = {} - ): this { - this.addTask({ - ...TaskHistory.defaultTask, - outputs: this.createOutputs(outputFilenames, outputFiletype), - ...overrides - }) - return this - } - - /** Repeats the last task in the task history a specified number of times. */ - repeat(n: number): this { - for (let i = 0; i < n; i++) - this.addTask(structuredClone(this.tasks.at(0)) as HistoryTaskItem) - return this - } -} diff --git a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png index a7fef0346..c21094f81 100644 Binary files a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png and b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png index 6b617420d..1c9232428 100644 Binary files a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png and b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png index 80cd43050..5de44c6e2 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png index e405605b0..f5a44f64f 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png index d0bc96022..93bcf0b1b 100644 Binary files a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png index d60314ef3..6db83b57b 100644 Binary files a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png index 5cd966a4e..d01357755 100644 Binary files a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png differ diff --git a/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png b/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png index c20ae5f89..694f33c40 100644 Binary files a/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png and b/browser_tests/tests/viewport.spec.ts-snapshots/viewport-fits-when-saved-offscreen-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png index 0dcab673a..f11f5c2c0 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png index 7ac84c627..d00dcf630 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png index 607efc16c..b509651cf 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png index a642edb35..866ef2172 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png index 01d5994ad..a846faf0b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png index 2e52a3827..ff0eae0d7 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png index f637fce10..d7592a0f7 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png index 16a48ace3..708d21f68 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png index 56f48867c..77971fa12 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png index 51ff6727b..1252ea723 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png index a8645017b..667b7c139 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png index 2a29eb590..efcb9ff07 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png index 06d0e7c84..6b28c14bc 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png index ac178e4a2..fc9e06620 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png index 8fe5cd25b..b7e4c6e8b 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts-snapshots/vue-node-bypassed-state-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png index 44c99675b..9329a1a6a 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png index 04790555d..e0f627695 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png index 759e50ca7..64ae5e68b 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png index 89194527f..a50a6c88f 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png index 97085e9a3..0af0e7447 100644 Binary files a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png and b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png differ diff --git a/docs/guidance/playwright.md b/docs/guidance/playwright.md new file mode 100644 index 000000000..f8ec8eabf --- /dev/null +++ b/docs/guidance/playwright.md @@ -0,0 +1,33 @@ +--- +globs: + - '**/*.spec.ts' +--- + +# Playwright E2E Test Conventions + +See `docs/testing/*.md` for detailed patterns. + +## Best Practices + +- Follow [Playwright Best Practices](https://playwright.dev/docs/best-practices) +- Do NOT use `waitForTimeout` - use Locator actions and retrying assertions +- Prefer specific selectors (role, label, test-id) +- Test across viewports + +## Test Tags + +Tags are respected by config: +- `@mobile` - Mobile viewport tests +- `@2x` - High DPI tests + +## Test Data + +- Check `browser_tests/assets/` for test data and fixtures +- Use realistic ComfyUI workflows for E2E tests + +## Running Tests + +```bash +pnpm test:browser # Run all E2E tests +pnpm test:browser -- --ui # Interactive UI mode +``` diff --git a/docs/guidance/storybook.md b/docs/guidance/storybook.md new file mode 100644 index 000000000..70bdb6c53 --- /dev/null +++ b/docs/guidance/storybook.md @@ -0,0 +1,55 @@ +--- +globs: + - '**/*.stories.ts' +--- + +# Storybook Conventions + +## File Placement + +Place `*.stories.ts` files alongside their components: +``` +src/components/MyComponent/ +├── MyComponent.vue +└── MyComponent.stories.ts +``` + +## Story Structure + +```typescript +import type { Meta, StoryObj } from '@storybook/vue3' +import ComponentName from './ComponentName.vue' + +const meta: Meta = { + title: 'Category/ComponentName', + component: ComponentName, + parameters: { layout: 'centered' } +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { /* props */ } +} +``` + +## Required Story Variants + +Include when applicable: +- **Default** - Minimal props +- **WithData** - Realistic data +- **Loading** - Loading state +- **Error** - Error state +- **Empty** - No data + +## Mock Data + +Use realistic ComfyUI schemas for mocks (node definitions, components). + +## Running Storybook + +```bash +pnpm storybook # Development server +pnpm build-storybook # Production build +``` diff --git a/docs/guidance/typescript.md b/docs/guidance/typescript.md new file mode 100644 index 000000000..9a6dd102b --- /dev/null +++ b/docs/guidance/typescript.md @@ -0,0 +1,37 @@ +--- +globs: + - '**/*.ts' + - '**/*.tsx' + - '**/*.vue' +--- + +# TypeScript Conventions + +## Type Safety + +- Never use `any` type - use proper TypeScript types +- Never use `as any` type assertions - fix the underlying type issue +- Type assertions are a last resort; they lead to brittle code +- Avoid `@ts-expect-error` - fix the underlying issue instead + +## Utility Libraries + +- Use `es-toolkit` for utility functions (not lodash) + +## API Utilities + +When making API calls in `src/`: + +```typescript +// ✅ Correct - use api helpers +const response = await api.get(api.apiURL('/prompt')) +const template = await fetch(api.fileURL('/templates/default.json')) + +// ❌ Wrong - direct URL construction +const response = await fetch('/api/prompt') +``` + +## Security + +- Sanitize HTML with `DOMPurify.sanitize()` +- Never log secrets or sensitive data diff --git a/docs/guidance/vitest.md b/docs/guidance/vitest.md new file mode 100644 index 000000000..90fe19673 --- /dev/null +++ b/docs/guidance/vitest.md @@ -0,0 +1,36 @@ +--- +globs: + - '**/*.test.ts' +--- + +# Vitest Unit Test Conventions + +See `docs/testing/*.md` for detailed patterns. + +## Test Quality + +- Do not write change detector tests (tests that just assert defaults) +- Do not write tests dependent on non-behavioral features (styles, classes) +- Do not write tests that just test mocks - ensure real code is exercised +- Be parsimonious; avoid redundant tests + +## Mocking + +- Use Vitest's mocking utilities (`vi.mock`, `vi.spyOn`) +- Keep module mocks contained - no global mutable state +- Use `vi.hoisted()` for per-test mock manipulation +- Don't mock what you don't own + +## Component Testing + +- Use Vue Test Utils for component tests +- Follow advice about making components easy to test +- Wait for reactivity with `await nextTick()` after state changes + +## Running Tests + +```bash +pnpm test:unit # Run all unit tests +pnpm test:unit -- path/to/file # Run specific test +pnpm test:unit -- --watch # Watch mode +``` diff --git a/docs/guidance/vue-components.md b/docs/guidance/vue-components.md new file mode 100644 index 000000000..24bcf22fe --- /dev/null +++ b/docs/guidance/vue-components.md @@ -0,0 +1,46 @@ +--- +globs: + - '**/*.vue' +--- + +# Vue Component Conventions + +Applies to all `.vue` files anywhere in the codebase. + +## Vue 3 Composition API + +- Use ` @@ -217,7 +235,13 @@ defineExpose({ runButtonClick }) > diff --git a/src/renderer/extensions/linearMode/LinearPreview.vue b/src/renderer/extensions/linearMode/LinearPreview.vue index 2e466b80e..4dacf93a7 100644 --- a/src/renderer/extensions/linearMode/LinearPreview.vue +++ b/src/renderer/extensions/linearMode/LinearPreview.vue @@ -2,7 +2,6 @@ import { computed } from 'vue' import { downloadFile } from '@/base/common/downloadUtil' -import Load3dViewerContent from '@/components/load3d/Load3dViewerContent.vue' import Popover from '@/components/ui/Popover.vue' import Button from '@/components/ui/button/Button.vue' import { d, t } from '@/i18n' @@ -12,6 +11,7 @@ import type { AssetItem } from '@/platform/assets/schemas/assetSchema' import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil' import ImagePreview from '@/renderer/extensions/linearMode/ImagePreview.vue' +import Preview3d from '@/renderer/extensions/linearMode/Preview3d.vue' import VideoPreview from '@/renderer/extensions/linearMode/VideoPreview.vue' import { getMediaType, @@ -159,7 +159,7 @@ async function rerun(e: Event) {