diff --git a/.env_example b/.env_example index 520521fe0..a6987ae26 100644 --- a/.env_example +++ b/.env_example @@ -33,3 +33,4 @@ DISABLE_VUE_PLUGINS=false # Algolia credentials required for developing with the new custom node manager. ALGOLIA_APP_ID=4E0RO38HS8 ALGOLIA_API_KEY=684d998c36b67a9a9fce8fc2d8860579 + diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml index 3ec61cb3c..d1eecafe3 100644 --- a/.github/workflows/claude-pr-review.yml +++ b/.github/workflows/claude-pr-review.yml @@ -47,6 +47,7 @@ jobs: needs: wait-for-ci if: needs.wait-for-ci.outputs.should-proceed == 'true' runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -69,19 +70,17 @@ jobs: pnpm install -g typescript @vue/compiler-sfc - name: Run Claude PR Review - uses: anthropics/claude-code-action@main + uses: anthropics/claude-code-action@v1.0.6 with: label_trigger: "claude-review" - direct_prompt: | + prompt: | Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly. CRITICAL: You must post individual inline comments using the gh api commands shown in the file. DO NOT create a summary comment. Each issue must be posted as a separate inline comment on the specific line of code. anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - max_turns: 256 - timeout_minutes: 30 - allowed_tools: "Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch" + claude_args: "--max-turns 256 --allowedTools 'Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch'" env: PR_NUMBER: ${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-playwright-deploy.yaml b/.github/workflows/pr-playwright-deploy.yaml index 12051fa99..19bb28253 100644 --- a/.github/workflows/pr-playwright-deploy.yaml +++ b/.github/workflows/pr-playwright-deploy.yaml @@ -1,4 +1,4 @@ -name: PR Playwright Deploy and Comment +name: PR Playwright Deploy (Forks) on: workflow_run: @@ -9,272 +9,84 @@ env: DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p' jobs: - deploy-reports: + deploy-and-comment-forked-pr: runs-on: ubuntu-latest - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'completed' + if: | + github.repository == 'Comfy-Org/ComfyUI_frontend' && + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.head_repository != null && + github.event.workflow_run.repository != null && + github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name permissions: + pull-requests: write actions: read - strategy: - fail-fast: false - matrix: - browser: [chromium, chromium-2x, chromium-0.5x, mobile-chrome] steps: - - name: Get PR info - id: pr-info + - name: Log workflow trigger info + run: | + echo "Repository: ${{ github.repository }}" + echo "Event: ${{ github.event.workflow_run.event }}" + echo "Head repo: ${{ github.event.workflow_run.head_repository.full_name || 'null' }}" + echo "Base repo: ${{ github.event.workflow_run.repository.full_name || 'null' }}" + echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}" + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get PR Number + id: pr uses: actions/github-script@v7 with: script: | - const { data: pullRequests } = await github.rest.pulls.list({ + const { data: prs } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, }); - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return { number: null, sanitized_branch: null }; + const pr = prs.find(p => p.head.sha === context.payload.workflow_run.head_sha); + + if (!pr) { + console.log('No PR found for SHA:', context.payload.workflow_run.head_sha); + return null; } - const pr = pullRequests[0]; - const branchName = context.payload.workflow_run.head_branch; - const sanitizedBranch = branchName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/--+/g, '-').replace(/^-|-$/g, ''); - - return { - number: pr.number, - sanitized_branch: sanitizedBranch - }; + console.log(`Found PR #${pr.number} from fork: ${context.payload.workflow_run.head_repository.full_name}`); + return pr.number; - - name: Set project name - if: fromJSON(steps.pr-info.outputs.result).number != null - id: project-name + - name: Handle Test Start + if: steps.pr.outputs.result != 'null' && github.event.action == 'requested' + env: + GITHUB_TOKEN: ${{ github.token }} run: | - if [ "${{ matrix.browser }}" = "chromium-0.5x" ]; then - echo "name=comfyui-playwright-chromium-0-5x" >> $GITHUB_OUTPUT - else - echo "name=comfyui-playwright-${{ matrix.browser }}" >> $GITHUB_OUTPUT - fi - echo "branch=${{ fromJSON(steps.pr-info.outputs.result).sanitized_branch }}" >> $GITHUB_OUTPUT + chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ steps.pr.outputs.result }}" \ + "${{ github.event.workflow_run.head_branch }}" \ + "starting" \ + "$(date -u '${{ env.DATE_FORMAT }}')" - - name: Download playwright report - if: fromJSON(steps.pr-info.outputs.result).number != null + - name: Download and Deploy Reports + if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' uses: actions/download-artifact@v4 with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - name: playwright-report-${{ matrix.browser }} - path: playwright-report - - - name: Install Wrangler - if: fromJSON(steps.pr-info.outputs.result).number != null - run: npm install -g wrangler - - - name: Deploy to Cloudflare Pages (${{ matrix.browser }}) - if: fromJSON(steps.pr-info.outputs.result).number != null - id: cloudflare-deploy - continue-on-error: true - run: | - # Retry logic for wrangler deploy (3 attempts) - RETRY_COUNT=0 - MAX_RETRIES=3 - SUCCESS=false + pattern: playwright-report-* + path: reports - while [ $RETRY_COUNT -lt $MAX_RETRIES ] && [ $SUCCESS = false ]; do - RETRY_COUNT=$((RETRY_COUNT + 1)) - echo "Deployment attempt $RETRY_COUNT of $MAX_RETRIES..." - - if npx wrangler pages deploy playwright-report --project-name=${{ steps.project-name.outputs.name }} --branch=${{ steps.project-name.outputs.branch }}; then - SUCCESS=true - echo "Deployment successful on attempt $RETRY_COUNT" - else - echo "Deployment failed on attempt $RETRY_COUNT" - if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then - echo "Retrying in 10 seconds..." - sleep 10 - fi - fi - done - - if [ $SUCCESS = false ]; then - echo "All deployment attempts failed" - exit 1 - fi + - name: Handle Test Completion + if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - - comment-tests-starting: - runs-on: ubuntu-latest - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'requested' - permissions: - pull-requests: write - actions: read - steps: - - name: Get PR number - id: pr - uses: actions/github-script@v7 - with: - script: | - const { data: pullRequests } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, - }); - - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return null; - } - - return pullRequests[0].number; - - - name: Get completion time - id: completion-time - run: echo "time=$(date -u '${{ env.DATE_FORMAT }}')" >> $GITHUB_OUTPUT - - - name: Generate comment body for start - if: steps.pr.outputs.result != 'null' - id: comment-body-start + GITHUB_TOKEN: ${{ github.token }} run: | - echo "" > comment.md - echo "## ๐ŸŽญ Playwright Test Results" >> comment.md - echo "" >> comment.md - echo "comfy-loading-gif **Tests are starting...** " >> comment.md - echo "" >> comment.md - echo "โฐ Started at: ${{ steps.completion-time.outputs.time }} UTC" >> comment.md - echo "" >> comment.md - echo "### ๐Ÿš€ Running Tests" >> comment.md - echo "- ๐Ÿงช **chromium**: Running tests..." >> comment.md - echo "- ๐Ÿงช **chromium-0.5x**: Running tests..." >> comment.md - echo "- ๐Ÿงช **chromium-2x**: Running tests..." >> comment.md - echo "- ๐Ÿงช **mobile-chrome**: Running tests..." >> comment.md - echo "" >> comment.md - echo "---" >> comment.md - echo "โฑ๏ธ Please wait while tests are running across all browsers..." >> comment.md - - - name: Comment PR - Tests Started - if: steps.pr.outputs.result != 'null' - uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0 - with: - issue-number: ${{ steps.pr.outputs.result }} - body-includes: '' - comment-author: 'github-actions[bot]' - edit-mode: replace - body-path: comment.md - - comment-tests-completed: - runs-on: ubuntu-latest - needs: deploy-reports - if: github.repository == 'Comfy-Org/ComfyUI_frontend' && github.event.workflow_run.event == 'pull_request' && github.event.action == 'completed' && always() - permissions: - pull-requests: write - actions: read - steps: - - name: Get PR number - id: pr - uses: actions/github-script@v7 - with: - script: | - const { data: pullRequests } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - head: `${context.repo.owner}:${context.payload.workflow_run.head_branch}`, - }); - - if (pullRequests.length === 0) { - console.log('No open PR found for this branch'); - return null; - } - - return pullRequests[0].number; - - - name: Download all deployment info - if: steps.pr.outputs.result != 'null' - uses: actions/download-artifact@v4 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - pattern: deployment-info-* - merge-multiple: true - path: deployment-info - - - name: Get completion time - id: completion-time - run: echo "time=$(date -u '${{ env.DATE_FORMAT }}')" >> $GITHUB_OUTPUT - - - name: Generate comment body for completion - if: steps.pr.outputs.result != 'null' - id: comment-body-completed - run: | - echo "" > comment.md - echo "## ๐ŸŽญ Playwright Test Results" >> comment.md - echo "" >> comment.md - - # Check if all tests passed - ALL_PASSED=true - for file in deployment-info/*.txt; do - if [ -f "$file" ]; then - browser=$(basename "$file" .txt) - info=$(cat "$file") - exit_code=$(echo "$info" | cut -d'|' -f2) - if [ "$exit_code" != "0" ]; then - ALL_PASSED=false - break - fi - fi - done - - if [ "$ALL_PASSED" = "true" ]; then - echo "โœ… **All tests passed across all browsers!**" >> comment.md - else - echo "โŒ **Some tests failed!**" >> comment.md - fi - - echo "" >> comment.md - echo "โฐ Completed at: ${{ steps.completion-time.outputs.time }} UTC" >> comment.md - echo "" >> comment.md - echo "### ๐Ÿ“Š Test Reports by Browser" >> comment.md - - for file in deployment-info/*.txt; do - if [ -f "$file" ]; then - browser=$(basename "$file" .txt) - info=$(cat "$file") - exit_code=$(echo "$info" | cut -d'|' -f2) - url=$(echo "$info" | cut -d'|' -f3) - - # Validate URLs before using them in comments - sanitized_url=$(echo "$url" | grep -E '^https://[a-z0-9.-]+\.pages\.dev(/.*)?$' || echo "INVALID_URL") - if [ "$sanitized_url" = "INVALID_URL" ]; then - echo "Invalid deployment URL detected: $url" - url="#" # Use safe fallback - fi - - if [ "$exit_code" = "0" ]; then - status="โœ…" - else - status="โŒ" - fi - - echo "- $status **$browser**: [View Report]($url)" >> comment.md - fi - done - - echo "" >> comment.md - echo "---" >> comment.md - if [ "$ALL_PASSED" = "true" ]; then - echo "๐ŸŽ‰ Your tests are passing across all browsers!" >> comment.md - else - echo "โš ๏ธ Please check the test reports for details on failures." >> comment.md - fi - - - name: Comment PR - Tests Complete - if: steps.pr.outputs.result != 'null' - uses: edumserrano/find-create-or-update-comment@82880b65c8a3a6e4c70aa05a204995b6c9696f53 # v3.0.0 - with: - issue-number: ${{ steps.pr.outputs.result }} - body-includes: '' - comment-author: 'github-actions[bot]' - edit-mode: replace - body-path: comment.md \ No newline at end of file + # Rename merged report if exists + [ -d "reports/playwright-report-chromium-merged" ] && \ + mv reports/playwright-report-chromium-merged reports/playwright-report-chromium + + chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ steps.pr.outputs.result }}" \ + "${{ github.event.workflow_run.head_branch }}" \ + "completed" \ No newline at end of file diff --git a/.github/workflows/test-ui.yaml b/.github/workflows/test-ui.yaml index 451c4b903..f8f6cf955 100644 --- a/.github/workflows/test-ui.yaml +++ b/.github/workflows/test-ui.yaml @@ -284,3 +284,65 @@ jobs: name: playwright-report-chromium path: ComfyUI_frontend/playwright-report/ retention-days: 30 + + #### BEGIN Deployment and commenting (non-forked PRs only) + # when using pull_request event, we have permission to comment directly + # if its a forked repo, we need to use workflow_run event in a separate workflow (pr-playwright-deploy.yaml) + + # Post starting comment for non-forked PRs + comment-on-pr-start: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get start time + id: start-time + run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT + + - name: Post starting comment + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ github.event.pull_request.number }}" \ + "${{ github.head_ref }}" \ + "starting" \ + "${{ steps.start-time.outputs.time }}" + + # Deploy and comment for non-forked PRs only + deploy-and-comment: + needs: [playwright-tests, merge-reports] + runs-on: ubuntu-latest + if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false + permissions: + pull-requests: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download all playwright reports + uses: actions/download-artifact@v4 + with: + pattern: playwright-report-* + path: reports + + - name: Make deployment script executable + run: chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh + + - name: Deploy reports and comment on PR + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + GITHUB_TOKEN: ${{ github.token }} + run: | + ./scripts/cicd/pr-playwright-deploy-and-comment.sh \ + "${{ github.event.pull_request.number }}" \ + "${{ github.head_ref }}" \ + "completed" + #### END Deployment and commenting (non-forked PRs only) \ No newline at end of file diff --git a/.gitignore b/.gitignore index db0b8454c..100bcd13e 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ tests-ui/workflows/examples /blob-report/ /playwright/.cache/ browser_tests/**/*-win32.png +browser-tests/local/ .env diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 76aca2401..05e082ef0 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -57,9 +57,8 @@ /* Override Storybook's problematic & selector styles */ /* Reset only the specific properties that Storybook injects */ - #storybook-root li+li, - #storybook-docs li+li { - margin: inherit; - padding: inherit; + li+li { + margin: 0; + padding: revert-layer; } \ No newline at end of file diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts index 8ac7449f4..cf2e5e6be 100644 --- a/browser_tests/tests/dialog.spec.ts +++ b/browser_tests/tests/dialog.spec.ts @@ -36,6 +36,10 @@ test('Does not report warning on undo/redo', async ({ comfyPage }) => { await comfyPage.loadWorkflow('missing/missing_nodes') await comfyPage.closeDialog() + // Wait for any async operations to complete after dialog closes + await comfyPage.nextFrame() + await comfyPage.page.waitForTimeout(100) + // Make a change to the graph await comfyPage.doubleClickCanvas() await comfyPage.searchBox.fillAndSelectFirstNode('KSampler') diff --git a/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png b/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png index 3acc073ff..90bc677fc 100644 Binary files a/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png and b/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png differ diff --git a/browser_tests/tests/rerouteNode.spec.ts-snapshots/native-reroute-chromium-linux.png b/browser_tests/tests/rerouteNode.spec.ts-snapshots/native-reroute-chromium-linux.png index 30b9a3894..11ecdabd0 100644 Binary files a/browser_tests/tests/rerouteNode.spec.ts-snapshots/native-reroute-chromium-linux.png and b/browser_tests/tests/rerouteNode.spec.ts-snapshots/native-reroute-chromium-linux.png differ diff --git a/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png b/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png index d14a6e132..6e63295b8 100644 Binary files a/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png and b/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts b/browser_tests/tests/selectionToolbox.spec.ts index ce45eb3fb..a9a5fc9c2 100644 --- a/browser_tests/tests/selectionToolbox.spec.ts +++ b/browser_tests/tests/selectionToolbox.spec.ts @@ -149,7 +149,7 @@ test.describe('Selection Toolbox', () => { // Node should have the selected color class/style // Note: Exact verification method depends on how color is applied to nodes const selectedNode = (await comfyPage.getNodeRefsByTitle('KSampler'))[0] - expect(selectedNode.getProperty('color')).not.toBeNull() + expect(await selectedNode.getProperty('color')).not.toBeNull() }) test('color picker shows current color of selected nodes', async ({ 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 3cec1c675..a9d9bafce 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/templates.spec.ts-snapshots/template-card-after-hover-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-card-after-hover-chromium-linux.png index 5e105418c..719ec65e4 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-card-after-hover-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-card-after-hover-chromium-linux.png differ diff --git a/browser_tests/tests/templates.spec.ts-snapshots/template-card-before-hover-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-card-before-hover-chromium-linux.png index 4bafa8784..b880fca84 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-card-before-hover-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-card-before-hover-chromium-linux.png differ diff --git a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-desktop-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-desktop-chromium-linux.png index ff6a6c017..0c34b0607 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-desktop-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-desktop-chromium-linux.png differ diff --git a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-mobile-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-mobile-chromium-linux.png index 604c23351..54f14ea0d 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-mobile-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-mobile-chromium-linux.png differ diff --git a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-tablet-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-tablet-chromium-linux.png index 1e2a1017c..a5cf133cb 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-tablet-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-tablet-chromium-linux.png differ diff --git a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-varying-content-chromium-linux.png b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-varying-content-chromium-linux.png index cabfe9a20..6c330c2f4 100644 Binary files a/browser_tests/tests/templates.spec.ts-snapshots/template-grid-varying-content-chromium-linux.png and b/browser_tests/tests/templates.spec.ts-snapshots/template-grid-varying-content-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png index 3f3d831bb..474fbb555 100644 Binary files a/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png and b/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png differ diff --git a/package.json b/package.json index 8e3876eb9..49884f34c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@comfyorg/comfyui-frontend", "private": true, - "version": "1.27.2", + "version": "1.27.3", "type": "module", "repository": "https://github.com/Comfy-Org/ComfyUI_frontend", "homepage": "https://comfy.org", @@ -25,10 +25,10 @@ "preinstall": "npx only-allow pnpm", "prepare": "husky || true && git config blame.ignoreRevsFile .git-blame-ignore-revs || true", "preview": "nx preview", - "lint": "eslint src --cache --concurrency=auto", - "lint:fix": "eslint src --cache --fix --concurrency=auto", - "lint:no-cache": "eslint src", - "lint:fix:no-cache": "eslint src --fix", + "lint": "eslint src --cache --concurrency=$npm_package_config_eslint_concurrency", + "lint:fix": "eslint src --fix --cache --concurrency=$npm_package_config_eslint_concurrency", + "lint:no-cache": "eslint src --concurrency=$npm_package_config_eslint_concurrency", + "lint:fix:no-cache": "eslint src --fix --concurrency=$npm_package_config_eslint_concurrency", "knip": "knip --cache", "knip:no-cache": "knip", "locale": "lobe-i18n locale", @@ -37,8 +37,12 @@ "storybook": "nx storybook -p 6006", "build-storybook": "storybook build" }, + "config": { + "eslint_concurrency": "4" + }, "devDependencies": { "@eslint/js": "^9.8.0", + "@iconify-json/lucide": "^1.2.66", "@iconify/tailwind": "^1.2.0", "@intlify/eslint-plugin-vue-i18n": "^3.2.0", "@lobehub/i18n-cli": "^1.25.1", @@ -76,7 +80,6 @@ "jsdom": "^26.1.0", "knip": "^5.62.0", "lint-staged": "^15.2.7", - "lucide-vue-next": "^0.540.0", "nx": "21.4.1", "prettier": "^3.3.2", "storybook": "^9.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbbfd7c1b..68fbf59b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,6 +171,9 @@ importers: '@eslint/js': specifier: ^9.8.0 version: 9.12.0 + '@iconify-json/lucide': + specifier: ^1.2.66 + version: 1.2.66 '@iconify/tailwind': specifier: ^1.2.0 version: 1.2.0 @@ -282,9 +285,6 @@ importers: lint-staged: specifier: ^15.2.7 version: 15.2.7 - lucide-vue-next: - specifier: ^0.540.0 - version: 0.540.0(vue@3.5.13(typescript@5.9.2)) nx: specifier: 21.4.1 version: 21.4.1 @@ -1595,6 +1595,9 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify-json/lucide@1.2.66': + resolution: {integrity: sha512-TrhmfThWY2FHJIckjz7g34gUx3+cmja61DcHNdmu0rVDBQHIjPMYO1O8mMjoDSqIXEllz9wDZxCqT3lFuI+f/A==} + '@iconify/json@2.2.380': resolution: {integrity: sha512-+Al/Q+mMB/nLz/tawmJEOkCs6+RKKVUS/Yg9I80h2yRpu0kIzxVLQRfF0NifXz/fH92vDVXbS399wio4lMVF4Q==} @@ -4736,11 +4739,6 @@ packages: resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} engines: {node: '>=16.14'} - lucide-vue-next@0.540.0: - resolution: {integrity: sha512-H7qhKVNKLyoFMo05pWcGSWBiLPiI3zJmWV65SuXWHlrIGIcvDer10xAyWcRJ0KLzIH5k5+yi7AGw/Xi1VF8Pbw==} - peerDependencies: - vue: '>=3.0.1' - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -8024,6 +8022,10 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify-json/lucide@1.2.66': + dependencies: + '@iconify/types': 2.0.0 + '@iconify/json@2.2.380': dependencies: '@iconify/types': 2.0.0 @@ -11563,10 +11565,6 @@ snapshots: lru-cache@8.0.5: {} - lucide-vue-next@0.540.0(vue@3.5.13(typescript@5.9.2)): - dependencies: - vue: 3.5.13(typescript@5.9.2) - lz-string@1.5.0: {} magic-string@0.30.17: diff --git a/scripts/cicd/pr-playwright-deploy-and-comment.sh b/scripts/cicd/pr-playwright-deploy-and-comment.sh new file mode 100755 index 000000000..767a7f514 --- /dev/null +++ b/scripts/cicd/pr-playwright-deploy-and-comment.sh @@ -0,0 +1,241 @@ +#!/bin/bash +set -e + +# Deploy Playwright test reports to Cloudflare Pages and comment on PR +# Usage: ./pr-playwright-deploy-and-comment.sh [start_time] + +# Input validation +# Validate PR number is numeric +case "$1" in + ''|*[!0-9]*) + echo "Error: PR_NUMBER must be numeric" >&2 + exit 1 + ;; +esac +PR_NUMBER="$1" + +# Sanitize and validate branch name (allow alphanumeric, dots, dashes, underscores, slashes) +BRANCH_NAME=$(echo "$2" | sed 's/[^a-zA-Z0-9._/-]//g') +if [ -z "$BRANCH_NAME" ]; then + echo "Error: Invalid or empty branch name" >&2 + exit 1 +fi + +# Validate status parameter +STATUS="${3:-completed}" +case "$STATUS" in + starting|completed) ;; + *) + echo "Error: STATUS must be 'starting' or 'completed'" >&2 + exit 1 + ;; +esac + +START_TIME="${4:-$(date -u '+%m/%d/%Y, %I:%M:%S %p')}" + +# Required environment variables +: "${GITHUB_TOKEN:?GITHUB_TOKEN is required}" +: "${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is required}" + +# Cloudflare variables only required for deployment +if [ "$STATUS" = "completed" ]; then + : "${CLOUDFLARE_API_TOKEN:?CLOUDFLARE_API_TOKEN is required for deployment}" + : "${CLOUDFLARE_ACCOUNT_ID:?CLOUDFLARE_ACCOUNT_ID is required for deployment}" +fi + +# Configuration +COMMENT_MARKER="" +# Use dot notation for artifact names (as Playwright creates them) +BROWSERS="chromium chromium-2x chromium-0.5x mobile-chrome" + +# Install wrangler if not available (output to stderr for debugging) +if ! command -v wrangler > /dev/null 2>&1; then + echo "Installing wrangler v4..." >&2 + npm install -g wrangler@^4.0.0 >&2 || { + echo "Failed to install wrangler" >&2 + echo "failed" + return + } +fi + +# Deploy a single browser report, WARN: ensure inputs are sanitized before calling this function +deploy_report() { + dir="$1" + browser="$2" + branch="$3" + + [ ! -d "$dir" ] && echo "failed" && return + + + # Project name with dots converted to dashes for Cloudflare + sanitized_browser=$(echo "$browser" | sed 's/\./-/g') + project="comfyui-playwright-${sanitized_browser}" + + echo "Deploying $browser to project $project on branch $branch..." >&2 + + # Try deployment up to 3 times + i=1 + while [ $i -le 3 ]; do + echo "Deployment attempt $i of 3..." >&2 + # Branch and project are already sanitized, use them directly + # Branch was sanitized at script start, project uses sanitized_browser + if output=$(wrangler pages deploy "$dir" \ + --project-name="$project" \ + --branch="$branch" 2>&1); then + + # Extract URL from output (improved regex for valid URL characters) + url=$(echo "$output" | grep -oE 'https://[a-zA-Z0-9.-]+\.pages\.dev\S*' | head -1) + result="${url:-https://${branch}.${project}.pages.dev}" + echo "Success! URL: $result" >&2 + echo "$result" # Only this goes to stdout for capture + return + else + echo "Deployment failed on attempt $i: $output" >&2 + fi + [ $i -lt 3 ] && sleep 10 + i=$((i + 1)) + done + + echo "failed" +} + +# Post or update GitHub comment +post_comment() { + body="$1" + temp_file=$(mktemp) + echo "$body" > "$temp_file" + + if command -v gh > /dev/null 2>&1; then + # Find existing comment ID + existing=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \ + --jq ".[] | select(.body | contains(\"$COMMENT_MARKER\")) | .id" | head -1) + + if [ -n "$existing" ]; then + # Update specific comment by ID + gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing" \ + --field body="$(cat "$temp_file")" + else + # Create new comment + gh pr comment "$PR_NUMBER" --body-file "$temp_file" + fi + else + echo "GitHub CLI not available, outputting comment:" + cat "$temp_file" + fi + + rm -f "$temp_file" +} + +# Main execution +if [ "$STATUS" = "starting" ]; then + # Post starting comment + comment=$(cat < **Tests are starting...** + +โฐ Started at: $START_TIME UTC + +### ๐Ÿš€ Running Tests +- ๐Ÿงช **chromium**: Running tests... +- ๐Ÿงช **chromium-0.5x**: Running tests... +- ๐Ÿงช **chromium-2x**: Running tests... +- ๐Ÿงช **mobile-chrome**: Running tests... + +--- +โฑ๏ธ Please wait while tests are running... +EOF +) + post_comment "$comment" + +else + # Deploy and post completion comment + # Convert branch name to Cloudflare-compatible format (lowercase, only alphanumeric and dashes) + cloudflare_branch=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | \ + sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g') + + echo "Looking for reports in: $(pwd)/reports" + echo "Available reports:" + ls -la reports/ 2>/dev/null || echo "Reports directory not found" + + # Deploy all reports in parallel and collect URLs + temp_dir=$(mktemp -d) + pids="" + i=0 + + # Start parallel deployments + for browser in $BROWSERS; do + if [ -d "reports/playwright-report-$browser" ]; then + echo "Found report for $browser, deploying in parallel..." + ( + url=$(deploy_report "reports/playwright-report-$browser" "$browser" "$cloudflare_branch") + echo "$url" > "$temp_dir/$i.url" + echo "Deployment result for $browser: $url" + ) & + pids="$pids $!" + else + echo "Report not found for $browser at reports/playwright-report-$browser" + echo "failed" > "$temp_dir/$i.url" + fi + i=$((i + 1)) + done + + # Wait for all deployments to complete + for pid in $pids; do + wait $pid + done + + # Collect URLs in order + urls="" + i=0 + for browser in $BROWSERS; do + if [ -f "$temp_dir/$i.url" ]; then + url=$(cat "$temp_dir/$i.url") + else + url="failed" + fi + if [ -z "$urls" ]; then + urls="$url" + else + urls="$urls $url" + fi + i=$((i + 1)) + done + + # Clean up temp directory + rm -rf "$temp_dir" + + # Generate completion comment + comment="$COMMENT_MARKER +## ๐ŸŽญ Playwright Test Results + +โœ… **Tests completed successfully!** + +โฐ Completed at: $(date -u '+%m/%d/%Y, %I:%M:%S %p') UTC + +### ๐Ÿ“Š Test Reports by Browser" + + # Add browser results + i=0 + for browser in $BROWSERS; do + # Get URL at position i + url=$(echo "$urls" | cut -d' ' -f$((i + 1))) + + if [ "$url" != "failed" ] && [ -n "$url" ]; then + comment="$comment +- โœ… **${browser}**: [View Report](${url})" + else + comment="$comment +- โŒ **${browser}**: Deployment failed" + fi + i=$((i + 1)) + done + + comment="$comment + +--- +๐ŸŽ‰ Click on the links above to view detailed test results for each browser configuration." + + post_comment "$comment" +fi \ No newline at end of file diff --git a/src/assets/css/style.css b/src/assets/css/style.css index ee6e697f0..3258cbba5 100644 --- a/src/assets/css/style.css +++ b/src/assets/css/style.css @@ -7,66 +7,6 @@ @config '../../../tailwind.config.ts'; -@layer tailwind-utilities { - /* Set default values to prevent some styles from not working properly. */ - *, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(66 153 225 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; - } - - @tailwind components; - @tailwind utilities; -} - :root { --fg-color: #000; --bg-color: #fff; @@ -107,6 +47,91 @@ } } +@theme { + --text-xxs: 0.625rem; + --text-xxs--line-height: calc(1 / 0.625); + + /* Palette Colors */ + --color-charcoal-100: #171718; + --color-charcoal-200: #202121; + --color-charcoal-300: #262729; + --color-charcoal-400: #2d2e32; + --color-charcoal-500: #313235; + --color-charcoal-600: #3c3d42; + --color-charcoal-700: #494a50; + --color-charcoal-800: #55565e; + + --color-stone-100: #444444; + --color-stone-200: #828282; + --color-stone-300: #bbbbbb; + + --color-ivory-100: #fdfbfa; + --color-ivory-200: #faf9f5; + --color-ivory-300: #f0eee6; + + --color-gray-100: #f3f3f3; + --color-gray-200: #e9e9e9; + --color-gray-300: #e1e1e1; + --color-gray-400: #d9d9d9; + --color-gray-500: #c5c5c5; + --color-gray-600: #b4b4b4; + --color-gray-700: #a0a0a0; + --color-gray-800: #8a8a8a; + + --color-sand-100: #e1ded5; + --color-sand-200: #d6cfc2; + --color-sand-300: #888682; + + --color-slate-100: #9c9eab; + --color-slate-200: #9fa2bd; + --color-slate-300: #5b5e7d; + + --color-brand-yellow: #f0ff41; + --color-brand-blue: #172dd7; + + --color-blue-100: #0b8ce9; + --color-blue-200: #31b9f4; + --color-success-100: #00cd72; + --color-success-200: #47e469; + --color-warning-100: #fd9903; + --color-warning-200: #fcbf64; + --color-danger-100: #c02323; + --color-danger-200: #d62952; + + --color-error: #962a2a; + + --color-blue-selection: rgb( from var(--color-blue-100) r g b / 0.3); + --color-node-hover-100: rgb( from var(--color-charcoal-800) r g b/ 0.15); + --color-node-hover-200: rgb(from var(--color-charcoal-800) r g b/ 0.1); + --color-modal-tag: rgb(from var(--color-gray-400) r g b/ 0.4); + + /* PrimeVue pulled colors */ + --color-muted: var(--p-text-muted-color); + --color-highlight: var(--p-primary-color); + + /* Special Colors (temporary) */ + --color-dark-elevation-1.5: rgba(from white r g b/ 0.015); + --color-dark-elevation-2: rgba(from white r g b / 0.03); +} + +@custom-variant dark-theme { + .dark-theme & { + @slot; + } +} + +@utility scrollbar-hide { + scrollbar-width: none; + &::-webkit-scrollbar { + width: 1px; + } + &::-webkit-scrollbar-thumb { + background-color: transparent; + } +} + +/* Everthing below here to be cleaned up over time. */ + body { width: 100vw; height: 100vh; @@ -849,7 +874,7 @@ audio.comfy-audio.empty-audio-widget { .comfy-load-3d, .comfy-load-3d-animation, .comfy-preview-3d, -.comfy-preview-3d-animation{ +.comfy-preview-3d-animation { display: flex; flex-direction: column; background: transparent; @@ -862,7 +887,7 @@ audio.comfy-audio.empty-audio-widget { .comfy-load-3d-animation canvas, .comfy-preview-3d canvas, .comfy-preview-3d-animation canvas, -.comfy-load-3d-viewer canvas{ +.comfy-load-3d-viewer canvas { display: flex; width: 100% !important; height: 100% !important; @@ -939,7 +964,9 @@ audio.comfy-audio.empty-audio-widget { .lg-node .lg-slot, .lg-node .lg-widget { - transition: opacity 0.1s ease, font-size 0.1s ease; + transition: + opacity 0.1s ease, + font-size 0.1s ease; } /* Performance optimization during canvas interaction */ @@ -971,4 +998,3 @@ audio.comfy-audio.empty-audio-widget { /* Use solid colors only */ background-image: none !important; } - diff --git a/src/components/button/IconButton.stories.ts b/src/components/button/IconButton.stories.ts index 7caf298e9..d774c0e5b 100644 --- a/src/components/button/IconButton.stories.ts +++ b/src/components/button/IconButton.stories.ts @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { Bell, Download, Heart, Settings, Trophy, X } from 'lucide-vue-next' import IconButton from './IconButton.vue' @@ -33,13 +32,13 @@ type Story = StoryObj export const Primary: Story = { render: (args) => ({ - components: { IconButton, Trophy }, + components: { IconButton }, setup() { return { args } }, template: ` - + ` }), @@ -51,13 +50,13 @@ export const Primary: Story = { export const Secondary: Story = { render: (args) => ({ - components: { IconButton, Settings }, + components: { IconButton }, setup() { return { args } }, template: ` - + ` }), @@ -69,13 +68,13 @@ export const Secondary: Story = { export const Transparent: Story = { render: (args) => ({ - components: { IconButton, X }, + components: { IconButton }, setup() { return { args } }, template: ` - + ` }), @@ -87,13 +86,13 @@ export const Transparent: Story = { export const Small: Story = { render: (args) => ({ - components: { IconButton, Bell }, + components: { IconButton }, setup() { return { args } }, template: ` - + ` }), @@ -105,42 +104,42 @@ export const Small: Story = { export const AllVariants: Story = { render: () => ({ - components: { IconButton, Trophy, Settings, X, Bell, Heart, Download }, + components: { IconButton }, template: `
- + - +
- + - +
- + - +
- + - + - +
diff --git a/src/components/button/IconButton.vue b/src/components/button/IconButton.vue index 1f5b24bac..5de7adfcd 100644 --- a/src/components/button/IconButton.vue +++ b/src/components/button/IconButton.vue @@ -1,5 +1,11 @@ @@ -15,11 +21,16 @@ import { getButtonTypeClasses, getIconButtonSizeClasses } from '@/types/buttonTypes' +import { cn } from '@/utils/tailwindUtil' interface IconButtonProps extends BaseButtonProps { onClick: (event: Event) => void } +defineOptions({ + inheritAttrs: false +}) + const { size = 'md', type = 'secondary', @@ -36,8 +47,6 @@ const buttonStyle = computed(() => { ? getBorderButtonTypeClasses(type) : getButtonTypeClasses(type) - return [baseClasses, sizeClasses, typeClasses, className] - .filter(Boolean) - .join(' ') + return cn(baseClasses, sizeClasses, typeClasses, className) }) diff --git a/src/components/button/IconGroup.stories.ts b/src/components/button/IconGroup.stories.ts index 1eb0d6e0a..c2fa1b96d 100644 --- a/src/components/button/IconGroup.stories.ts +++ b/src/components/button/IconGroup.stories.ts @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { Download, ExternalLink, Heart } from 'lucide-vue-next' import IconButton from './IconButton.vue' import IconGroup from './IconGroup.vue' @@ -17,17 +16,17 @@ type Story = StoryObj export const Basic: Story = { render: () => ({ - components: { IconGroup, IconButton, Download, ExternalLink, Heart }, + components: { IconGroup, IconButton }, template: ` - + - + - + ` diff --git a/src/components/button/IconGroup.vue b/src/components/button/IconGroup.vue index 784a945eb..9c5bbd40c 100644 --- a/src/components/button/IconGroup.vue +++ b/src/components/button/IconGroup.vue @@ -1,7 +1,17 @@ + + diff --git a/src/components/button/IconTextButton.stories.ts b/src/components/button/IconTextButton.stories.ts index da07d9a66..0139b9bd6 100644 --- a/src/components/button/IconTextButton.stories.ts +++ b/src/components/button/IconTextButton.stories.ts @@ -1,14 +1,4 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { - ChevronLeft, - ChevronRight, - Download, - Package, - Save, - Settings, - Trash2, - X -} from 'lucide-vue-next' import IconTextButton from './IconTextButton.vue' @@ -49,14 +39,14 @@ type Story = StoryObj export const Primary: Story = { render: (args) => ({ - components: { IconTextButton, Package }, + components: { IconTextButton }, setup() { return { args } }, template: ` ` @@ -70,14 +60,14 @@ export const Primary: Story = { export const Secondary: Story = { render: (args) => ({ - components: { IconTextButton, Settings }, + components: { IconTextButton }, setup() { return { args } }, template: ` ` @@ -91,14 +81,14 @@ export const Secondary: Story = { export const Transparent: Story = { render: (args) => ({ - components: { IconTextButton, X }, + components: { IconTextButton }, setup() { return { args } }, template: ` ` @@ -112,14 +102,14 @@ export const Transparent: Story = { export const WithIconRight: Story = { render: (args) => ({ - components: { IconTextButton, ChevronRight }, + components: { IconTextButton }, setup() { return { args } }, template: ` ` @@ -134,14 +124,14 @@ export const WithIconRight: Story = { export const Small: Story = { render: (args) => ({ - components: { IconTextButton, Save }, + components: { IconTextButton }, setup() { return { args } }, template: ` ` @@ -156,66 +146,60 @@ export const Small: Story = { export const AllVariants: Story = { render: () => ({ components: { - IconTextButton, - Download, - Settings, - Trash2, - ChevronRight, - ChevronLeft, - Save + IconTextButton }, template: `
diff --git a/src/components/button/IconTextButton.vue b/src/components/button/IconTextButton.vue index 656761757..a62aab08b 100644 --- a/src/components/button/IconTextButton.vue +++ b/src/components/button/IconTextButton.vue @@ -1,5 +1,11 @@ diff --git a/src/components/button/MoreButton.vue b/src/components/button/MoreButton.vue index 5f4f81607..0ea645c7b 100644 --- a/src/components/button/MoreButton.vue +++ b/src/components/button/MoreButton.vue @@ -14,7 +14,7 @@ unstyled :pt="pt" > -
+
@@ -25,6 +25,8 @@ import Popover from 'primevue/popover' import { computed, ref } from 'vue' +import { cn } from '@/utils/tailwindUtil' + import IconButton from './IconButton.vue' const popover = ref>() @@ -39,13 +41,16 @@ const hide = () => { const pt = computed(() => ({ root: { - class: 'absolute z-50' + class: cn('absolute z-50') }, content: { - class: [ - 'mt-2 bg-white dark-theme:bg-zinc-800 text-neutral dark-theme:text-white rounded-lg', - 'shadow-lg border border-zinc-200 dark-theme:border-zinc-700' - ] + class: cn( + 'mt-2 rounded-lg', + 'bg-white dark-theme:bg-zinc-800', + 'text-neutral dark-theme:text-white', + 'shadow-lg', + 'border border-zinc-200 dark-theme:border-zinc-700' + ) } })) diff --git a/src/components/button/TextButton.vue b/src/components/button/TextButton.vue index a183ea788..4dbe53b9c 100644 --- a/src/components/button/TextButton.vue +++ b/src/components/button/TextButton.vue @@ -1,5 +1,11 @@ @@ -15,12 +21,17 @@ import { getButtonSizeClasses, getButtonTypeClasses } from '@/types/buttonTypes' +import { cn } from '@/utils/tailwindUtil' interface TextButtonProps extends BaseButtonProps { label: string onClick: () => void } +defineOptions({ + inheritAttrs: false +}) + const { size = 'md', type = 'primary', @@ -38,8 +49,6 @@ const buttonStyle = computed(() => { ? getBorderButtonTypeClasses(type) : getButtonTypeClasses(type) - return [baseClasses, sizeClasses, typeClasses, className] - .filter(Boolean) - .join(' ') + return cn(baseClasses, sizeClasses, typeClasses, className) }) diff --git a/src/components/card/Card.stories.ts b/src/components/card/Card.stories.ts index 0d8bf4385..f4252644e 100644 --- a/src/components/card/Card.stories.ts +++ b/src/components/card/Card.stories.ts @@ -1,13 +1,4 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { - Download, - Folder, - Heart, - Info, - MoreVertical, - Star, - Upload -} from 'lucide-vue-next' import { ref } from 'vue' import IconButton from '../button/IconButton.vue' @@ -58,14 +49,6 @@ const meta: Meta = { options: ['square', 'portrait', 'tallPortrait'], description: 'Card container aspect ratio' }, - maxWidth: { - control: { type: 'range', min: 200, max: 600, step: 10 }, - description: 'Maximum width in pixels' - }, - minWidth: { - control: { type: 'range', min: 150, max: 400, step: 10 }, - description: 'Minimum width in pixels' - }, topRatio: { control: 'select', options: ['square', 'landscape'], @@ -149,14 +132,7 @@ const createCardTemplate = (args: CardStoryArgs) => ({ CardTitle, CardDescription, IconButton, - SquareChip, - Info, - Folder, - Heart, - Download, - Star, - Upload, - MoreVertical + SquareChip }, setup() { const favorited = ref(false) @@ -171,11 +147,10 @@ const createCardTemplate = (args: CardStoryArgs) => ({ } }, template: ` -
+
@@ -222,7 +197,7 @@ const createCardTemplate = (args: CardStoryArgs) => ({ @@ -230,7 +205,7 @@ const createCardTemplate = (args: CardStoryArgs) => ({