diff --git a/.gitattributes b/.gitattributes index 0f538ae762..39d7f722cd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,7 @@ *.json text eol=lf *.mjs text eol=lf *.mts text eol=lf +*.snap text eol=lf *.ts text eol=lf *.vue text eol=lf *.yaml text eol=lf diff --git a/.github/workflows/ci-lint-format.yaml b/.github/workflows/ci-lint-format.yaml index 71d5eed156..6ebfd8d386 100644 --- a/.github/workflows/ci-lint-format.yaml +++ b/.github/workflows/ci-lint-format.yaml @@ -39,6 +39,9 @@ jobs: - name: Run ESLint with auto-fix run: pnpm lint:fix + - name: Run Stylelint with auto-fix + run: pnpm stylelint:fix + - name: Run Prettier with auto-format run: pnpm format @@ -63,6 +66,7 @@ jobs: - name: Final validation run: | pnpm lint + pnpm stylelint pnpm format:check pnpm knip diff --git a/.github/workflows/size-data.yml b/.github/workflows/ci-size-data.yaml similarity index 97% rename from .github/workflows/size-data.yml rename to .github/workflows/ci-size-data.yaml index 8da55d0c22..a21c93110f 100644 --- a/.github/workflows/size-data.yml +++ b/.github/workflows/ci-size-data.yaml @@ -1,4 +1,4 @@ -name: size data +name: "CI: Size Data" on: push: diff --git a/.github/workflows/pr-backport.yaml b/.github/workflows/pr-backport.yaml index 1c9a292305..8729c5d570 100644 --- a/.github/workflows/pr-backport.yaml +++ b/.github/workflows/pr-backport.yaml @@ -69,34 +69,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Check if backports already exist - id: check-existing - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} - run: | - # Check for existing backport PRs for this PR number - EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName') - - if [ -z "$EXISTING_BACKPORTS" ]; then - echo "skip=false" >> $GITHUB_OUTPUT - exit 0 - fi - - # For manual triggers with force_rerun, proceed anyway - if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then - echo "skip=false" >> $GITHUB_OUTPUT - echo "::warning::Force rerun requested - existing backports will be updated" - exit 0 - fi - - echo "Found existing backport PRs:" - echo "$EXISTING_BACKPORTS" - echo "skip=true" >> $GITHUB_OUTPUT - echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates" - - name: Collect backport targets - if: steps.check-existing.outputs.skip != 'true' id: targets run: | TARGETS=() @@ -138,6 +111,14 @@ jobs: add_target "$label" "${BASH_REMATCH[1]}" elif [[ "$label" =~ ^backport:(.+)$ ]]; then add_target "$label" "${BASH_REMATCH[1]}" + elif [[ "$label" =~ ^core\/([0-9]+)\.([0-9]+)$ ]]; then + SAFE_MAJOR="${BASH_REMATCH[1]}" + SAFE_MINOR="${BASH_REMATCH[2]}" + add_target "$label" "core/${SAFE_MAJOR}.${SAFE_MINOR}" + elif [[ "$label" =~ ^cloud\/([0-9]+)\.([0-9]+)$ ]]; then + SAFE_MAJOR="${BASH_REMATCH[1]}" + SAFE_MINOR="${BASH_REMATCH[2]}" + add_target "$label" "cloud/${SAFE_MAJOR}.${SAFE_MINOR}" elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then add_target "$label" "core/${label}" fi @@ -151,8 +132,76 @@ jobs: echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT echo "Found backport targets: ${TARGETS[*]}" + - name: Filter already backported targets + id: filter-targets + env: + EVENT_NAME: ${{ github.event_name }} + FORCE_RERUN_INPUT: >- + ${{ github.event_name == 'workflow_dispatch' && inputs.force_rerun + || 'false' }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: >- + ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number + || github.event.pull_request.number }} + run: | + set -euo pipefail + + REQUESTED_TARGETS="${{ steps.targets.outputs.targets }}" + if [ -z "$REQUESTED_TARGETS" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + echo "pending-targets=" >> $GITHUB_OUTPUT + exit 0 + fi + + FORCE_RERUN=false + if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$FORCE_RERUN_INPUT" = "true" ]; then + FORCE_RERUN=true + fi + + mapfile -t EXISTING_BRANCHES < <( + git ls-remote --heads origin "backport-${PR_NUMBER}-to-*" || true + ) + + PENDING=() + SKIPPED=() + + for target in $REQUESTED_TARGETS; do + SAFE_TARGET=$(echo "$target" | tr '/' '-') + BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}" + + if [ "$FORCE_RERUN" = true ]; then + PENDING+=("$target") + continue + fi + + if printf '%s\n' "${EXISTING_BRANCHES[@]:-}" | + grep -Fq "refs/heads/${BACKPORT_BRANCH}"; then + SKIPPED+=("$target") + else + PENDING+=("$target") + fi + done + + SKIPPED_JOINED="${SKIPPED[*]:-}" + PENDING_JOINED="${PENDING[*]:-}" + + echo "already-exists=${SKIPPED_JOINED}" >> $GITHUB_OUTPUT + echo "pending-targets=${PENDING_JOINED}" >> $GITHUB_OUTPUT + + if [ -z "$PENDING_JOINED" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + if [ -n "$SKIPPED_JOINED" ]; then + echo "::warning::Backport branches already exist for: ${SKIPPED_JOINED}" + fi + else + echo "skip=false" >> $GITHUB_OUTPUT + if [ -n "$SKIPPED_JOINED" ]; then + echo "::notice::Skipping already backported targets: ${SKIPPED_JOINED}" + fi + fi + - name: Backport commits - if: steps.check-existing.outputs.skip != 'true' + if: steps.filter-targets.outputs.skip != 'true' id: backport env: PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} @@ -170,7 +219,7 @@ jobs: MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}" fi - for target in ${{ steps.targets.outputs.targets }}; do + for target in ${{ steps.filter-targets.outputs.pending-targets }}; do TARGET_BRANCH="${target}" SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-') BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}" @@ -185,6 +234,14 @@ jobs: continue fi + # Check if commit already exists on target branch + if git branch -r --contains "${MERGE_COMMIT}" | grep -q "origin/${TARGET_BRANCH}"; then + echo "::notice::Commit ${MERGE_COMMIT} already exists on ${TARGET_BRANCH}, skipping backport" + FAILED="${FAILED}${TARGET_BRANCH}:already-exists " + echo "::endgroup::" + continue + fi + # Create backport branch git checkout -b "${BACKPORT_BRANCH}" "origin/${TARGET_BRANCH}" @@ -219,7 +276,7 @@ jobs: fi - name: Create PR for each successful backport - if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success + if: steps.filter-targets.outputs.skip != 'true' && steps.backport.outputs.success env: GH_TOKEN: ${{ secrets.PR_GH_TOKEN }} PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} @@ -258,7 +315,7 @@ jobs: done - name: Comment on failures - if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed + if: steps.filter-targets.outputs.skip != 'true' && failure() && steps.backport.outputs.failed env: GH_TOKEN: ${{ github.token }} run: | @@ -279,6 +336,9 @@ jobs: if [ "${reason}" = "branch-missing" ]; then gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist" + elif [ "${reason}" = "already-exists" ]; then + gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed." + elif [ "${reason}" = "conflicts" ]; then # Convert comma-separated conflicts back to newlines for display CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /') @@ -287,3 +347,9 @@ jobs: gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" fi done + + - name: Remove needs-backport label + if: steps.filter-targets.outputs.skip != 'true' && success() + run: gh pr edit ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }} --remove-label "needs-backport" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-claude-review.yaml b/.github/workflows/pr-claude-review.yaml index b09fde14f0..16eaecfc1e 100644 --- a/.github/workflows/pr-claude-review.yaml +++ b/.github/workflows/pr-claude-review.yaml @@ -28,6 +28,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} check-regexp: '^(lint-and-format|test|playwright-tests)' + allowed-conclusions: success,skipped,failure,cancelled,neutral,action_required,timed_out,stale wait-interval: 30 repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/size-report.yml b/.github/workflows/pr-size-report.yaml similarity index 96% rename from .github/workflows/size-report.yml rename to .github/workflows/pr-size-report.yaml index caaafd30a3..968888aa91 100644 --- a/.github/workflows/size-report.yml +++ b/.github/workflows/pr-size-report.yaml @@ -1,8 +1,8 @@ -name: size report +name: "PR: Size Report" on: workflow_run: - workflows: ['size data'] + workflows: ['CI: Size Data'] types: - completed workflow_dispatch: @@ -22,7 +22,7 @@ permissions: issues: write jobs: - size-report: + comment: runs-on: ubuntu-latest if: > github.repository == 'Comfy-Org/ComfyUI_frontend' && @@ -78,7 +78,7 @@ jobs: uses: dawidd6/action-download-artifact@v11 with: branch: ${{ steps.pr-base.outputs.content }} - workflow: size-data.yml + workflow: ci-size-data.yaml event: push name: size-data path: temp/size-prev diff --git a/.github/workflows/pr-update-playwright-expectations.yaml b/.github/workflows/pr-update-playwright-expectations.yaml index f688c3250a..a013624668 100644 --- a/.github/workflows/pr-update-playwright-expectations.yaml +++ b/.github/workflows/pr-update-playwright-expectations.yaml @@ -12,11 +12,11 @@ concurrency: cancel-in-progress: true jobs: - test: + setup: runs-on: ubuntu-latest if: > ( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) || - ( github.event.issue.pull_request && + ( github.event.issue.pull_request && github.event_name == 'issue_comment' && ( github.event.comment.author_association == 'OWNER' || @@ -24,12 +24,25 @@ jobs: github.event.comment.author_association == 'COLLABORATOR' ) && startsWith(github.event.comment.body, '/update-playwright') ) + outputs: + cache-key: ${{ steps.cache-key.outputs.key }} + pr-number: ${{ steps.pr-info.outputs.pr-number }} + branch: ${{ steps.pr-info.outputs.branch }} + comment-id: ${{ steps.find-update-comment.outputs.comment-id }} steps: + - name: Get PR info + id: pr-info + run: | + echo "pr-number=${{ github.event.number || github.event.issue.number }}" >> $GITHUB_OUTPUT + echo "branch=$(gh pr view ${{ github.event.number || github.event.issue.number }} --repo ${{ github.repository }} --json headRefName --jq '.headRefName')" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Find Update Comment uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad id: "find-update-comment" with: - issue-number: ${{ github.event.number || github.event.issue.number }} + issue-number: ${{ steps.pr-info.outputs.pr-number }} comment-author: "github-actions[bot]" body-includes: "Updating Playwright Expectations" @@ -37,72 +50,260 @@ jobs: uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 with: comment-id: ${{ steps.find-update-comment.outputs.comment-id }} - issue-number: ${{ github.event.number || github.event.issue.number }} + issue-number: ${{ steps.pr-info.outputs.pr-number }} body: | Updating Playwright Expectations edit-mode: replace reactions: eyes - - name: Get Branch SHA - id: "get-branch" - run: echo ::set-output name=branch::$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName') - env: - REPO: ${{ github.repository }} - PR_NO: ${{ github.event.number || github.event.issue.number }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Initial Checkout + - name: Checkout repository uses: actions/checkout@v5 with: - ref: ${{ steps.get-branch.outputs.branch }} - - name: Setup Frontend + ref: ${{ steps.pr-info.outputs.branch }} + - name: Setup frontend uses: ./.github/actions/setup-frontend with: include_build_step: true - - name: Setup ComfyUI Server + # Save expensive build artifacts (Python env, built frontend, node_modules) + # Source code will be checked out fresh in sharded jobs + - name: Generate cache key + id: cache-key + run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT + - name: Save cache + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 + with: + path: | + ComfyUI + dist + key: comfyui-setup-${{ steps.cache-key.outputs.key }} + + # Sharded snapshot updates + update-snapshots-sharded: + needs: setup + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shardIndex: [1, 2, 3, 4] + shardTotal: [4] + steps: + # Checkout source code fresh (not cached) + - name: Checkout repository + uses: actions/checkout@v5 + with: + ref: ${{ needs.setup.outputs.branch }} + + # Restore expensive build artifacts from setup job + - name: Restore cached artifacts + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 + with: + fail-on-cache-miss: true + path: | + ComfyUI + dist + key: comfyui-setup-${{ needs.setup.outputs.cache-key }} + + - name: Setup ComfyUI server (from cache) uses: ./.github/actions/setup-comfyui-server with: launch_server: true + + - name: Setup nodejs, pnpm, reuse built frontend + uses: ./.github/actions/setup-frontend + - name: Setup Playwright uses: ./.github/actions/setup-playwright - - name: Run Playwright tests and update snapshots + + # Run sharded tests with snapshot updates + - name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) id: playwright-tests - run: pnpm exec playwright test --update-snapshots + run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} continue-on-error: true - - uses: actions/upload-artifact@v4 + + # Identify and stage only changed snapshot files + - name: Stage changed snapshot files + id: changed-snapshots + run: | + echo "==========================================" + echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})" + echo "==========================================" + + # Get list of changed snapshot files + changed_files=$(git diff --name-only browser_tests/ 2>/dev/null | grep -E '\-snapshots/' || echo "") + + if [ -z "$changed_files" ]; then + echo "No snapshot changes in this shard" + echo "has-changes=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "✓ Found changed files:" + echo "$changed_files" + file_count=$(echo "$changed_files" | wc -l) + echo "Count: $file_count" + echo "has-changes=true" >> $GITHUB_OUTPUT + echo "" + + # Create staging directory + mkdir -p /tmp/changed_snapshots_shard + + # Copy only changed files, preserving directory structure + # Strip 'browser_tests/' prefix to avoid double nesting + echo "Copying changed files to staging directory..." + while IFS= read -r file; do + # Remove 'browser_tests/' prefix + file_without_prefix="${file#browser_tests/}" + # Create parent directories + mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")" + # Copy file + cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix" + echo " → $file_without_prefix" + done <<< "$changed_files" + + echo "" + echo "Staged files for upload:" + find /tmp/changed_snapshots_shard -type f + + # Upload ONLY the changed files from this shard + - name: Upload changed snapshots + uses: actions/upload-artifact@v4 + if: steps.changed-snapshots.outputs.has-changes == 'true' + with: + name: snapshots-shard-${{ matrix.shardIndex }} + path: /tmp/changed_snapshots_shard/ + retention-days: 1 + + - name: Upload test report + uses: actions/upload-artifact@v4 if: always() with: - name: playwright-report + name: playwright-report-shard-${{ matrix.shardIndex }} path: ./playwright-report/ retention-days: 30 - - name: Debugging info + + # Merge snapshots and commit + merge-and-commit: + needs: [setup, update-snapshots-sharded] + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + ref: ${{ needs.setup.outputs.branch }} + + # Download all changed snapshot files from shards + - name: Download snapshot artifacts + uses: actions/download-artifact@v4 + with: + pattern: snapshots-shard-* + path: ./downloaded-snapshots + merge-multiple: false + + - name: List downloaded files run: | - echo "PR: ${{ github.event.issue.number }}" - echo "Branch: ${{ steps.get-branch.outputs.branch }}" - git status + echo "==========================================" + echo "DOWNLOADED SNAPSHOT FILES" + echo "==========================================" + find ./downloaded-snapshots -type f + echo "" + echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)" + + # Merge only the changed files into browser_tests + - name: Merge changed snapshots + run: | + set -euo pipefail + + echo "==========================================" + echo "MERGING CHANGED SNAPSHOTS" + echo "==========================================" + + # Verify target directory exists + if [ ! -d "browser_tests" ]; then + echo "::error::Target directory 'browser_tests' does not exist" + exit 1 + fi + + merged_count=0 + + # For each shard's changed files, copy them directly + for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do + if [ ! -d "$shard_dir" ]; then + continue + fi + + shard_name=$(basename "$shard_dir") + file_count=$(find "$shard_dir" -type f | wc -l) + + if [ "$file_count" -eq 0 ]; then + echo " $shard_name: no files" + continue + fi + + echo "Processing $shard_name ($file_count file(s))..." + + # Copy files directly, preserving directory structure + # Since files are already in correct structure (no browser_tests/ prefix), just copy them all + cp -v -r "$shard_dir"* browser_tests/ 2>&1 | sed 's/^/ /' + + merged_count=$((merged_count + 1)) + echo " ✓ Merged" + echo "" + done + + echo "==========================================" + echo "MERGE COMPLETE" + echo "==========================================" + echo "Shards merged: $merged_count" + + - name: Show changes + run: | + echo "==========================================" + echo "CHANGES SUMMARY" + echo "==========================================" + echo "" + echo "Changed files in browser_tests:" + git diff --name-only browser_tests/ | head -20 || echo "No changes" + echo "" + echo "Total changes:" + git diff --name-only browser_tests/ | wc -l || echo "0" + - name: Commit updated expectations + id: commit run: | git config --global user.name 'github-actions' git config --global user.email 'github-actions@github.com' - git add browser_tests - if git diff --cached --quiet; then + + if git diff --quiet browser_tests/; then echo "No changes to commit" - else - git commit -m "[automated] Update test expectations" - git push origin ${{ steps.get-branch.outputs.branch }} + echo "has-changes=false" >> $GITHUB_OUTPUT + exit 0 fi + + echo "==========================================" + echo "COMMITTING CHANGES" + echo "==========================================" + + echo "has-changes=true" >> $GITHUB_OUTPUT + + git add browser_tests/ + git commit -m "[automated] Update test expectations" + + echo "Pushing to ${{ needs.setup.outputs.branch }}..." + git push origin ${{ needs.setup.outputs.branch }} + + echo "✓ Commit and push successful" - name: Add Done Reaction uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 - if: github.event_name == 'issue_comment' + if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true' with: - comment-id: ${{ steps.find-update-comment.outputs.comment-id }} - issue-number: ${{ github.event.number || github.event.issue.number }} + comment-id: ${{ needs.setup.outputs.comment-id }} + issue-number: ${{ needs.setup.outputs.pr-number }} reactions: +1 reactions-edit-mode: replace - name: Remove New Browser Test Expectations label if: always() && github.event_name == 'pull_request' - run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "New Browser Test Expectations" + run: gh pr edit ${{ needs.setup.outputs.pr-number }} --remove-label "New Browser Test Expectations" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release-branch-create.yaml b/.github/workflows/release-branch-create.yaml index 992e779ddb..434100ff3f 100644 --- a/.github/workflows/release-branch-create.yaml +++ b/.github/workflows/release-branch-create.yaml @@ -69,6 +69,9 @@ jobs: echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT + BASE_COMMIT=$(git rev-parse HEAD) + echo "base_commit=$BASE_COMMIT" >> $GITHUB_OUTPUT + # Get previous major version for comparison PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1) @@ -87,13 +90,13 @@ jobs: elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then # Major version bump (e.g., 1.99.x → 2.0.0) echo "is_minor_bump=true" >> $GITHUB_OUTPUT - BRANCH_NAME="core/${PREV_MAJOR}.${PREV_MINOR}" - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT + BRANCH_BASE="${PREV_MAJOR}.${PREV_MINOR}" + echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then # Minor version bump (e.g., 1.23.x → 1.24.0) echo "is_minor_bump=true" >> $GITHUB_OUTPUT - BRANCH_NAME="core/${MAJOR}.${PREV_MINOR}" - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT + BRANCH_BASE="${MAJOR}.${PREV_MINOR}" + echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT else echo "is_minor_bump=false" >> $GITHUB_OUTPUT fi @@ -101,64 +104,97 @@ jobs: # Return to main branch git checkout main - - name: Create release branch + - name: Create release branches + id: create_branches if: steps.check_version.outputs.is_minor_bump == 'true' run: | - BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}" + BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}" + PREV_VERSION="${{ steps.check_version.outputs.prev_version }}" - # Check if branch already exists - if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then - echo "⚠️ Branch $BRANCH_NAME already exists, skipping creation" - echo "branch_exists=true" >> $GITHUB_ENV - exit 0 - else - echo "branch_exists=false" >> $GITHUB_ENV + if [[ -z "$BRANCH_BASE" ]]; then + echo "::error::Branch base not set; unable to determine release branches" + exit 1 fi - # Create branch from the commit BEFORE the version bump - # This ensures the release branch has the previous minor version - git checkout -b "$BRANCH_NAME" HEAD^1 + BASE_COMMIT="${{ steps.check_version.outputs.base_commit }}" - # Push the new branch - git push origin "$BRANCH_NAME" + if [[ -z "$BASE_COMMIT" ]]; then + echo "::error::Base commit not provided; cannot create release branches" + exit 1 + fi - echo "✅ Created release branch: $BRANCH_NAME" - echo "This branch is now in feature freeze and will only receive:" - echo "- Bug fixes" - echo "- Critical security patches" - echo "- Documentation updates" + RESULTS_FILE=$(mktemp) + trap 'rm -f "$RESULTS_FILE"' EXIT + for PREFIX in core cloud; do + BRANCH_NAME="${PREFIX}/${BRANCH_BASE}" + + if git ls-remote --exit-code --heads origin \ + "$BRANCH_NAME" >/dev/null 2>&1; then + echo "⚠️ Branch $BRANCH_NAME already exists" + echo "ℹ️ Skipping creation for $BRANCH_NAME" + STATUS="exists" + else + # Create branch from the commit BEFORE the version bump + if ! git push origin "$BASE_COMMIT:refs/heads/$BRANCH_NAME"; then + echo "::error::Failed to push release branch $BRANCH_NAME" + exit 1 + fi + echo "✅ Created release branch: $BRANCH_NAME" + STATUS="created" + fi + + echo "$BRANCH_NAME|$STATUS|$PREV_VERSION" >> "$RESULTS_FILE" + done + + { + echo "results<<'EOF'" + cat "$RESULTS_FILE" + echo "EOF" + } >> $GITHUB_OUTPUT - name: Post summary if: steps.check_version.outputs.is_minor_bump == 'true' run: | - BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}" - PREV_VERSION="${{ steps.check_version.outputs.prev_version }}" CURRENT_VERSION="${{ steps.check_version.outputs.current_version }}" + RESULTS="${{ steps.create_branches.outputs.results }}" - if [[ "${{ env.branch_exists }}" == "true" ]]; then + if [[ -z "$RESULTS" ]]; then cat >> $GITHUB_STEP_SUMMARY << EOF - ## 🌿 Release Branch Already Exists + ## 🌿 Release Branch Summary - The release branch for the previous minor version already exists: - EOF - else - cat >> $GITHUB_STEP_SUMMARY << EOF - ## 🌿 Release Branch Created - - A new release branch has been created for the previous minor version: + Release branch creation skipped; no eligible branches were found. EOF + exit 0 fi cat >> $GITHUB_STEP_SUMMARY << EOF + ## 🌿 Release Branch Summary - - **Branch**: \`$BRANCH_NAME\` - - **Version**: \`$PREV_VERSION\` (feature frozen) - **Main branch**: \`$CURRENT_VERSION\` (active development) + ### Branch Status + EOF + + while IFS='|' read -r BRANCH STATUS PREV_VERSION; do + if [[ "$STATUS" == "created" ]]; then + cat >> $GITHUB_STEP_SUMMARY << EOF + + - \`$BRANCH\` created from version \`$PREV_VERSION\` + EOF + else + cat >> $GITHUB_STEP_SUMMARY << EOF + + - \`$BRANCH\` already existed (based on version \`$PREV_VERSION\`) + EOF + fi + done <<< "$RESULTS" + + cat >> $GITHUB_STEP_SUMMARY << EOF + ### Branch Policy - The \`$BRANCH_NAME\` branch is now in **feature freeze** and will only accept: + Release branches are feature-frozen and only accept: - 🐛 Bug fixes - 🔒 Security patches - 📚 Documentation updates @@ -167,9 +203,9 @@ jobs: ### Backporting Changes - To backport a fix to this release branch: + To backport a fix: 1. Create your fix on \`main\` first - 2. Cherry-pick to \`$BRANCH_NAME\` - 3. Create a PR targeting \`$BRANCH_NAME\` - 4. Use the \`Release\` label on the PR + 2. Cherry-pick to the target release branch + 3. Create a PR targeting that branch + 4. Apply the matching \`core/x.y\` or \`cloud/x.y\` label EOF diff --git a/.github/workflows/weekly-docs-check.yaml b/.github/workflows/weekly-docs-check.yaml new file mode 100644 index 0000000000..33f0927153 --- /dev/null +++ b/.github/workflows/weekly-docs-check.yaml @@ -0,0 +1,145 @@ +name: "Weekly Documentation Check" +description: "Automated weekly documentation accuracy check and update via Claude" + +permissions: + contents: write + pull-requests: write + id-token: write + +on: + schedule: + # Run every Monday at 9 AM UTC + - cron: '0 9 * * 1' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + docs-check: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: main + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies for analysis tools + run: | + # Check if packages are already available locally + if ! pnpm list typescript @vue/compiler-sfc >/dev/null 2>&1; then + echo "Installing TypeScript and Vue compiler globally..." + pnpm install -g typescript @vue/compiler-sfc + else + echo "TypeScript and Vue compiler already available locally" + fi + + - name: Run Claude Documentation Review + uses: anthropics/claude-code-action@v1.0.6 + with: + prompt: | + Is all documentation still 100% accurate? + + INSTRUCTIONS: + 1. Fact-check all documentation against the current codebase + 2. Look for: + - Outdated API references + - Deprecated functions or components still documented + - Missing documentation for new features + - Incorrect code examples + - Broken internal references + - Configuration examples that no longer work + - Documentation that contradicts actual implementation + 3. Update any inaccurate or outdated documentation + 4. Add documentation for significant undocumented features + 5. Ensure all code examples are valid and tested against current code + + Focus on these key areas: + - docs/**/*.md (all documentation files) + - CLAUDE.md (project guidelines) + - README.md files throughout the repository + - .claude/commands/*.md (Claude command documentation) + + Make changes directly to the documentation files as needed. + DO NOT modify any source code files unless absolutely necessary for documentation accuracy. + + After making all changes, create a comprehensive PR message summary: + 1. Write a detailed PR body to /tmp/pr-body-${{ github.run_id }}.md in markdown format + 2. Include: + - ## Summary section with bullet points of what was changed + - ## Changes Made section with details organized by category + - ## Review Notes section with any important context + 3. Be specific about which files were updated and why + 4. If no changes were needed, write a brief message stating documentation is up to date + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_args: "--max-turns 256 --allowedTools 'Bash(git status),Bash(git diff),Bash(git log),Bash(pnpm:*),Bash(npm:*),Bash(node:*),Bash(tsc:*),Bash(echo:*),Read,Write,Edit,Glob,Grep'" + continue-on-error: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for changes + id: check_changes + run: | + if git diff --quiet && git diff --cached --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No documentation changes needed" + else + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "Documentation changes detected" + fi + + - name: Create default PR body if not generated + if: steps.check_changes.outputs.has_changes == 'true' + run: | + if [ ! -f /tmp/pr-body-${{ github.run_id }}.md ]; then + cat > /tmp/pr-body-${{ github.run_id }}.md <<'EOF' + ## Automated Documentation Review + + This PR contains documentation updates identified by the weekly automated review. + + ### Review Process + - Automated fact-checking against current codebase + - Verification of code examples and API references + - Detection of outdated or missing documentation + + ### What was checked + - All markdown documentation in `docs/` + - Project guidelines in `CLAUDE.md` + - README files throughout the repository + - Claude command documentation in `.claude/commands/` + + **Note**: This is an automated PR. Please review all changes carefully before merging. + + 🤖 Generated by weekly documentation check workflow + EOF + fi + + - name: Create or Update Pull Request + if: steps.check_changes.outputs.has_changes == 'true' + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.PR_GH_TOKEN }} + commit-message: 'docs: weekly documentation accuracy update' + branch: docs/weekly-update + delete-branch: true + title: 'docs: Weekly Documentation Update' + body-path: /tmp/pr-body-${{ github.run_id }}.md + labels: | + documentation + automated + draft: true + assignees: ${{ github.repository_owner }} diff --git a/CLAUDE.md b/CLAUDE.md index 0b187fbfcc..4866c2dce3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,6 +62,11 @@ Key Nx features: ## Project Philosophy +- Follow good software engineering principles + - YAGNI + - AHA + - DRY + - SOLID - Clean, stable public APIs - Domain-driven design - Thousands of users and extensions diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1c9341d54..6c84779ff9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ Have another idea? Drop into Discord or open an issue, and let's chat! ``` 3. Configure environment (optional): - Create a `.env` file in the project root based on the provided [.env.example](.env.example) file. + Create a `.env` file in the project root based on the provided [.env_example](.env_example) file. **Note about ports**: By default, the dev server expects the ComfyUI backend at `localhost:8188`. If your ComfyUI instance runs on a different port, update this in your `.env` file. @@ -325,4 +325,4 @@ If you have questions about contributing: - Ask in our [Discord](https://discord.com/invite/comfyorg) - Open a new issue for clarification -Thank you for contributing to ComfyUI Frontend! \ No newline at end of file +Thank you for contributing to ComfyUI Frontend! diff --git a/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png b/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png new file mode 100644 index 0000000000..4dc0b3f43d Binary files /dev/null and b/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png b/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png new file mode 100644 index 0000000000..2676f31eb3 Binary files /dev/null and b/browser_tests/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png new file mode 100644 index 0000000000..cea542a4f0 Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png new file mode 100644 index 0000000000..1b4771e319 Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png new file mode 100644 index 0000000000..ed2e63d047 Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png new file mode 100644 index 0000000000..391dd76372 Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png new file mode 100644 index 0000000000..4ed230307b Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png new file mode 100644 index 0000000000..077e34405e Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png new file mode 100644 index 0000000000..d0235f54bb Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png new file mode 100644 index 0000000000..e4155c01ff Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png new file mode 100644 index 0000000000..c5d0151f1c Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png new file mode 100644 index 0000000000..7b1d91b3d2 Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png new file mode 100644 index 0000000000..26eb49e5ce Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png new file mode 100644 index 0000000000..94d12be43b Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png new file mode 100644 index 0000000000..e91d3a5fd9 Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png b/browser_tests/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png new file mode 100644 index 0000000000..088ec907c6 Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png new file mode 100644 index 0000000000..6dabeb42ec Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png new file mode 100644 index 0000000000..e04cb92a72 Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png new file mode 100644 index 0000000000..6f5e35a1cc Binary files /dev/null and b/browser_tests/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/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png new file mode 100644 index 0000000000..9adde1602a Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts-snapshots/vue-node-muted-state-chromium-linux.png differ diff --git a/browser_tests/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png b/browser_tests/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png new file mode 100644 index 0000000000..4cc3ebe2c6 Binary files /dev/null and b/browser_tests/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png differ diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index b01de101e5..8059fb4cb4 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -1657,7 +1657,8 @@ export const comfyPageFixture = base.extend<{ 'Comfy.userId': userId, // Set tutorial completed to true to avoid loading the tutorial workflow. 'Comfy.TutorialCompleted': true, - 'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize + 'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize, + 'Comfy.VueNodes.AutoScaleLayout': false }) } catch (e) { console.error(e) diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts index 7459acf585..9ccd4cd67a 100644 --- a/browser_tests/tests/dialog.spec.ts +++ b/browser_tests/tests/dialog.spec.ts @@ -301,7 +301,9 @@ test.describe('Settings', () => { }) test.describe('Support', () => { - test('Should open external zendesk link', async ({ comfyPage }) => { + test('Should open external zendesk link with OSS tag', async ({ + comfyPage + }) => { await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') const pagePromise = comfyPage.page.context().waitForEvent('page') await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support']) @@ -309,6 +311,10 @@ test.describe('Support', () => { await newPage.waitForLoadState('networkidle') await expect(newPage).toHaveURL(/.*support\.comfy\.org.*/) + + const url = new URL(newPage.url()) + expect(url.searchParams.get('tf_42243568391700')).toBe('oss') + await newPage.close() }) }) diff --git a/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png b/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png index a8deb29a0b..ed1732e671 100644 Binary files a/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png and b/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-hidden-links-chromium-linux.png differ diff --git a/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png b/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png index 106cae69c7..16f7fcc647 100644 Binary files a/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png and b/browser_tests/tests/graphCanvasMenu.spec.ts-snapshots/canvas-with-visible-links-chromium-linux.png differ diff --git a/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/no-workflow-webp-chromium-linux.png b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/no-workflow-webp-chromium-linux.png index 8f347b607e..1d8908e175 100644 Binary files a/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/no-workflow-webp-chromium-linux.png and b/browser_tests/tests/loadWorkflowInMedia.spec.ts-snapshots/no-workflow-webp-chromium-linux.png differ diff --git a/browser_tests/tests/nodeBadge.spec.ts-snapshots/node-badge-unknown-color-palette-chromium-linux.png b/browser_tests/tests/nodeBadge.spec.ts-snapshots/node-badge-unknown-color-palette-chromium-linux.png index 6975bdeae5..d1617f5995 100644 Binary files a/browser_tests/tests/nodeBadge.spec.ts-snapshots/node-badge-unknown-color-palette-chromium-linux.png and b/browser_tests/tests/nodeBadge.spec.ts-snapshots/node-badge-unknown-color-palette-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png index ac2f2b0661..cffe23ed13 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png index 562ec32e6f..5cd2eacc48 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png index ac2f2b0661..cffe23ed13 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-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 2c3b7e7e37..aaa2b590a7 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 b364f6468f..b63ad44ee2 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 3fa6b8e845..ed2e63d047 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 13fa609adb..db3883b5bc 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 4d9074fc7e..e9a11910a2 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 4b8fc7f9c9..66b808e342 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 9f97e071c6..5fa283cee0 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 9051a32a33..5c08aded80 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 47608d0143..76946cbc04 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 c135ceb0a7..6a39534a22 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 63cd3edaa7..0b51591e7c 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 4099a672fd..a12651973e 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 1f7471e702..ff06bd6ad3 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 2703f76669..088ec907c6 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 b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts index 8b8e0cb259..7074bbc2d7 100644 --- a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts +++ b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts @@ -9,29 +9,28 @@ const BYPASS_CLASS = /before:bg-bypass\/60/ test.describe('Vue Node Bypass', () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.setSetting('Comfy.VueNodes.Enabled', true) + await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + await comfyPage.setSetting('Comfy.Minimap.Visible', false) + await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true) await comfyPage.vueNodes.waitForNodes() }) - test.fixme( - 'should allow toggling bypass on a selected node with hotkey', - async ({ comfyPage }) => { - await comfyPage.setup() - await comfyPage.page.getByText('Load Checkpoint').click() - await comfyPage.page.keyboard.press(BYPASS_HOTKEY) + test('should allow toggling bypass on a selected node with hotkey', async ({ + comfyPage + }) => { + await comfyPage.page.getByText('Load Checkpoint').click() + await comfyPage.page.keyboard.press(BYPASS_HOTKEY) - const checkpointNode = - comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') - await expect(checkpointNode).toHaveClass(BYPASS_CLASS) - await comfyPage.page.mouse.click(400, 300) - await comfyPage.page.waitForTimeout(128) - await expect(comfyPage.canvas).toHaveScreenshot( - 'vue-node-bypassed-state.png' - ) + const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') + await expect(checkpointNode).toHaveClass(BYPASS_CLASS) + await comfyPage.nextFrame() + await expect(comfyPage.canvas).toHaveScreenshot( + 'vue-node-bypassed-state.png' + ) - await comfyPage.page.keyboard.press(BYPASS_HOTKEY) - await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS) - } - ) + await comfyPage.page.keyboard.press(BYPASS_HOTKEY) + await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS) + }) test('should allow toggling bypass on multiple selected nodes with hotkey', async ({ comfyPage 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 87186d4670..91e59358a2 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 35e902262f..43e9ac1700 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 fe4d392387..c876d8af37 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 caeb1e1fc4..015c2479da 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/lod.spec.ts b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts index f8c94aba59..cfe0ba1b3b 100644 --- a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts +++ b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts @@ -23,10 +23,10 @@ test.describe('Vue Nodes - LOD', () => { const vueNodesContainer = comfyPage.vueNodes.nodes const textboxesInNodes = vueNodesContainer.getByRole('textbox') - const buttonsInNodes = vueNodesContainer.getByRole('button') + const comboboxesInNodes = vueNodesContainer.getByRole('combobox') await expect(textboxesInNodes.first()).toBeVisible() - await expect(buttonsInNodes.first()).toBeVisible() + await expect(comboboxesInNodes.first()).toBeVisible() await comfyPage.zoom(120, 10) await comfyPage.nextFrame() @@ -34,7 +34,7 @@ test.describe('Vue Nodes - LOD', () => { await expect(comfyPage.canvas).toHaveScreenshot('vue-nodes-lod-active.png') await expect(textboxesInNodes.first()).toBeHidden() - await expect(buttonsInNodes.first()).toBeHidden() + await expect(comboboxesInNodes.first()).toBeHidden() await comfyPage.zoom(-120, 10) await comfyPage.nextFrame() @@ -43,6 +43,6 @@ test.describe('Vue Nodes - LOD', () => { 'vue-nodes-lod-inactive.png' ) await expect(textboxesInNodes.first()).toBeVisible() - await expect(buttonsInNodes.first()).toBeVisible() + await expect(comboboxesInNodes.first()).toBeVisible() }) }) diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png index 76070e3971..77678b6b09 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png index 8e06dd0313..cdb700eb68 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png index 3ea3691405..4656299b23 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-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 ee7f27d289..ce83405d22 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 0bd33a3ff4..8318ff97bc 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/build/plugins/comfyAPIPlugin.ts b/build/plugins/comfyAPIPlugin.ts index 5b7f4bec42..3e9a9e5b98 100644 --- a/build/plugins/comfyAPIPlugin.ts +++ b/build/plugins/comfyAPIPlugin.ts @@ -6,6 +6,34 @@ interface ShimResult { exports: string[] } +const SKIP_WARNING_FILES = new Set(['scripts/app', 'scripts/api']) + +/** Files that will be removed in v1.34 */ +const DEPRECATED_FILES = [ + 'scripts/ui', + 'extensions/core/maskEditorOld', + 'extensions/core/groupNode' +] as const + +function getWarningMessage( + fileKey: string, + shimFileName: string +): string | null { + if (SKIP_WARNING_FILES.has(fileKey)) { + return null + } + + const isDeprecated = DEPRECATED_FILES.some((deprecatedPath) => + fileKey.startsWith(deprecatedPath) + ) + + if (isDeprecated) { + return `[ComfyUI Deprecated] Importing from "${shimFileName}" is deprecated and will be removed in v1.34.` + } + + return `[ComfyUI Notice] "${shimFileName}" is an internal module, not part of the public API. Future updates may break this import.` +} + function isLegacyFile(id: string): boolean { return ( id.endsWith('.ts') && @@ -63,12 +91,22 @@ export function comfyAPIPlugin(isDev: boolean): Plugin { const relativePath = path.relative(path.join(projectRoot, 'src'), id) const shimFileName = relativePath.replace(/\.ts$/, '.js') - const shimComment = `// Shim for ${relativePath}\n` + let shimContent = `// Shim for ${relativePath}\n` + + const fileKey = relativePath.replace(/\.ts$/, '').replace(/\\/g, '/') + const warningMessage = getWarningMessage(fileKey, shimFileName) + + if (warningMessage) { + // It will only display once because it is at the root of the file. + shimContent += `console.warn('${warningMessage}');\n` + } + + shimContent += result.exports.join('') this.emitFile({ type: 'asset', fileName: shimFileName, - source: shimComment + result.exports.join('') + source: shimContent }) } diff --git a/eslint.config.ts b/eslint.config.ts index b66674865e..6681d2126a 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -5,7 +5,6 @@ import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescrip import { importX } from 'eslint-plugin-import-x' import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import storybook from 'eslint-plugin-storybook' -import tailwind from 'eslint-plugin-tailwindcss' import unusedImports from 'eslint-plugin-unused-imports' import pluginVue from 'eslint-plugin-vue' import { defineConfig } from 'eslint/config' @@ -34,11 +33,7 @@ const settings = { ], noWarnOnMultipleProjects: true }) - ], - tailwindcss: { - config: `${import.meta.dirname}/packages/design-system/src/css/style.css`, - functions: ['cn', 'clsx', 'tw'] - } + ] } as const const commonParserOptions = { @@ -60,7 +55,6 @@ export default defineConfig([ '**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*', 'packages/registry-types/src/comfyRegistryTypes.ts', - 'public/auth-sw.js', 'src/extensions/core/*', 'src/scripts/*', 'src/types/generatedManagerTypes.ts', @@ -98,7 +92,6 @@ export default defineConfig([ // Difference in typecheck on CI vs Local // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Bad types in the plugin - tailwind.configs['flat/recommended'], pluginVue.configs['flat/recommended'], eslintPluginPrettierRecommended, storybook.configs['flat/recommended'], @@ -130,7 +123,6 @@ export default defineConfig([ 'import-x/no-relative-packages': 'error', 'unused-imports/no-unused-imports': 'error', 'no-console': ['error', { allow: ['warn', 'error'] }], - 'tailwindcss/no-custom-classname': 'off', // TODO: fix 'vue/no-v-html': 'off', // Enforce dark-theme: instead of dark: prefix 'vue/no-restricted-class': ['error', '/^dark:/'], diff --git a/global.d.ts b/global.d.ts index 5493cbb180..059e477324 100644 --- a/global.d.ts +++ b/global.d.ts @@ -4,12 +4,19 @@ declare const __SENTRY_DSN__: string declare const __ALGOLIA_APP_ID__: string declare const __ALGOLIA_API_KEY__: string declare const __USE_PROD_CONFIG__: boolean -declare const __MIXPANEL_TOKEN__: string -type BuildFeatureFlags = { - REQUIRE_SUBSCRIPTION: boolean +interface Window { + __CONFIG__: { + mixpanel_token?: string + subscription_required?: boolean + server_health_alert?: { + message: string + tooltip?: string + severity?: 'info' | 'warning' | 'error' + badge?: string + } + } } -declare const __BUILD_FLAGS__: BuildFeatureFlags interface Navigator { /** diff --git a/knip.config.ts b/knip.config.ts index 9284830609..a77574f97b 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -14,7 +14,7 @@ const config: KnipConfig = { }, 'apps/desktop-ui': { entry: ['src/main.ts', 'src/i18n.ts'], - project: ['src/**/*.{js,ts,vue}', '*.{js,ts,mts}'] + project: ['src/**/*.{js,ts,vue}'] }, 'packages/tailwind-utils': { project: ['src/**/*.{js,ts}'] @@ -41,9 +41,7 @@ const config: KnipConfig = { 'src/workbench/extensions/manager/types/generatedManagerTypes.ts', 'packages/registry-types/src/comfyRegistryTypes.ts', // Used by a custom node (that should move off of this) - 'src/scripts/ui/components/splitButton.ts', - // Service worker - registered at runtime via navigator.serviceWorker.register() - 'public/auth-sw.js' + 'src/scripts/ui/components/splitButton.ts' ], compilers: { // https://github.com/webpro-nl/knip/issues/1008#issuecomment-3207756199 diff --git a/package.json b/package.json index 55a55a7b1c..6c82e43b55 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@comfyorg/comfyui-frontend", "private": true, - "version": "1.30.2", + "version": "1.31.1", "type": "module", "repository": "https://github.com/Comfy-Org/ComfyUI_frontend", "homepage": "https://comfy.org", @@ -43,7 +43,8 @@ "test:browser": "pnpm exec nx e2e", "test:unit": "nx run test", "typecheck": "vue-tsc --noEmit", - "zipdist": "node scripts/zipdist.js" + "zipdist": "node scripts/zipdist.js", + "clean": "nx reset" }, "devDependencies": { "@eslint/js": "catalog:", @@ -61,7 +62,6 @@ "@storybook/vue3-vite": "catalog:", "@tailwindcss/vite": "catalog:", "@trivago/prettier-plugin-sort-imports": "catalog:", - "@types/eslint-plugin-tailwindcss": "catalog:", "@types/fs-extra": "catalog:", "@types/jsdom": "catalog:", "@types/node": "catalog:", @@ -78,7 +78,6 @@ "eslint-plugin-import-x": "catalog:", "eslint-plugin-prettier": "catalog:", "eslint-plugin-storybook": "catalog:", - "eslint-plugin-tailwindcss": "catalog:", "eslint-plugin-unused-imports": "catalog:", "eslint-plugin-vue": "catalog:", "fs-extra": "^11.2.0", diff --git a/packages/design-system/src/css/style.css b/packages/design-system/src/css/style.css index eee0c06589..21553a4398 100644 --- a/packages/design-system/src/css/style.css +++ b/packages/design-system/src/css/style.css @@ -9,29 +9,18 @@ @config '../../tailwind.config.ts'; -@media (prefers-color-scheme: dark) { - :root { - --fg-color: #fff; - --bg-color: #202020; - --content-bg: #4e4e4e; - --content-fg: #fff; - --content-hover-bg: #222; - --content-hover-fg: #fff; - } -} - @theme { --text-xxs: 0.625rem; --text-xxs--line-height: calc(1 / 0.625); - /* Spacing */ - --spacing-xs: 8px; + --text-xxxs: 0.5625rem; + --text-xxxs--line-height: calc(1 / 0.5625); /* Font Families */ --font-inter: 'Inter', sans-serif; /* Palette Colors */ - --color-charcoal-100: #171718; + --color-charcoal-100: #55565e; --color-charcoal-200: #494a50; --color-charcoal-300: #3c3d42; --color-charcoal-400: #313235; @@ -42,43 +31,45 @@ --color-neutral-550: #636363; - --color-stone-100: #828282; - --color-stone-200: #444444; - --color-stone-300: #bbbbbb; + --color-ash-300: #bbbbbb; + --color-ash-500: #828282; + --color-ash-800: #444444; --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-smoke-100: #f3f3f3; + --color-smoke-200: #e9e9e9; + --color-smoke-300: #e1e1e1; + --color-smoke-400: #d9d9d9; + --color-smoke-500: #c5c5c5; + --color-smoke-600: #b4b4b4; + --color-smoke-700: #a0a0a0; + --color-smoke-800: #8a8a8a; --color-sand-100: #e1ded5; --color-sand-200: #d6cfc2; --color-sand-300: #888682; - --color-pure-black: #000000; - --color-pure-white: #ffffff; - --color-slate-100: #9c9eab; --color-slate-200: #9fa2bd; --color-slate-300: #5b5e7d; - --color-brand-yellow: #f0ff41; - --color-brand-blue: #172dd7; + --color-electric-400: #f0ff41; + --color-sapphire-700: #172dd7; + --color-brand-yellow: var(--color-electric-400); + --color-brand-blue: var(--color-sapphire-700); + + --color-azure-400: #31b9f4; + --color-azure-600: #0b8ce9; + + --color-jade-400: #47e469; + --color-jade-600: #00cd72; + + --color-gold-400: #fcbf64; + --color-gold-600: #fd9903; - --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; @@ -90,26 +81,24 @@ --color-error: #962a2a; --color-comfy-menu-secondary: var(--comfy-menu-secondary-bg); - --text-xxxs: 0.5625rem; - --text-xxxs--line-height: calc(1 / 0.5625); - --color-blue-selection: rgb(from var(--color-blue-100) r g b / 0.3); - --color-node-hover-100: rgb(from var(--color-charcoal-100) r g b/ 0.15); - --color-node-hover-200: rgb(from var(--color-charcoal-100) r g b/ 0.1); - --color-modal-tag: rgb(from var(--color-gray-400) r g b/ 0.4); + --color-blue-selection: rgb(from var(--color-azure-600) 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-smoke-400) r g b/ 0.4); --color-alpha-charcoal-600-30: color-mix( in srgb, var(--color-charcoal-600) 30%, transparent ); - --color-alpha-stone-100-20: color-mix( + --color-alpha-ash-500-20: color-mix( in srgb, - var(--color-stone-100) 20%, + var(--color-ash-500) 20%, transparent ); - --color-alpha-gray-500-50: color-mix( + --color-alpha-smoke-500-50: color-mix( in srgb, - var(--color-gray-500) 50%, + var(--color-smoke-500) 50%, transparent ); @@ -145,8 +134,8 @@ --content-hover-bg: #adadad; --content-hover-fg: #000; - --button-surface: var(--color-pure-white); - --button-surface-contrast: var(--color-pure-black); + --button-surface: var(--color-white); + --button-surface-contrast: var(--color-black); /* Code styling colors for help menu*/ --code-text-color: rgb(0 122 255 / 1); @@ -157,31 +146,36 @@ --accent-primary: var(--color-charcoal-700); --backdrop: var(--color-white); - --button-hover-surface: var(--color-gray-200); - --button-active-surface: var(--color-gray-400); - --button-icon: var(--color-gray-600); + + --button-hover-surface: var(--color-smoke-200); + --button-active-surface: var(--color-smoke-400); + --button-icon: var(--color-smoke-600); + --dialog-surface: var(--color-neutral-200); - --interface-menu-component-surface-hovered: var(--color-gray-200); - --interface-menu-component-surface-selected: var(--color-gray-400); - --interface-menu-keybind-surface-default: var(--color-gray-500); - --interface-panel-surface: var(--color-pure-white); - --interface-stroke: var(--color-gray-300); - --nav-background: var(--color-pure-white); - --node-border: var(--color-gray-300); - --node-component-border: var(--color-gray-400); - --node-component-disabled: var(--color-alpha-stone-100-20); + + --interface-menu-component-surface-hovered: var(--color-smoke-200); + --interface-menu-component-surface-selected: var(--color-smoke-400); + --interface-menu-keybind-surface-default: var(--color-smoke-500); + --interface-panel-surface: var(--color-white); + --interface-stroke: var(--color-smoke-300); + + --nav-background: var(--color-white); + + --node-border: var(--color-smoke-300); + --node-component-border: var(--color-smoke-400); + --node-component-disabled: var(--color-alpha-ash-500-20); --node-component-executing: var(--color-blue-500); --node-component-header: var(--fg-color); - --node-component-header-icon: var(--color-stone-200); + --node-component-header-icon: var(--color-ash-800); --node-component-header-surface: var(--color-white); --node-component-outline: var(--color-black); - --node-component-ring: rgb(from var(--color-gray-500) r g b / 50%); + --node-component-ring: rgb(from var(--color-smoke-500) r g b / 50%); --node-component-slot-dot-outline-opacity-mult: 1; --node-component-slot-dot-outline-opacity: 5%; --node-component-slot-dot-outline: var(--color-black); - --node-component-slot-text: var(--color-stone-200); - --node-component-surface-highlight: var(--color-stone-100); - --node-component-surface-hovered: var(--color-gray-200); + --node-component-slot-text: var(--color-ash-800); + --node-component-surface-highlight: var(--color-ash-500); + --node-component-surface-hovered: var(--color-smoke-200); --node-component-surface-selected: var(--color-charcoal-200); --node-component-surface: var(--color-white); --node-component-tooltip: var(--color-charcoal-700); @@ -193,40 +187,53 @@ ); --node-component-widget-skeleton-surface: var(--color-zinc-300); --node-divider: var(--color-sand-100); - --node-icon-disabled: var(--color-alpha-gray-500-50); - --node-stroke: var(--color-gray-400); + --node-icon-disabled: var(--color-alpha-smoke-500-50); + --node-stroke: var(--color-smoke-400); --node-stroke-selected: var(--color-accent-primary); --node-stroke-error: var(--color-error); - --node-stroke-executing: var(--color-blue-100); - --text-secondary: var(--color-stone-100); + --node-stroke-executing: var(--color-azure-600); + + --text-secondary: var(--color-ash-500); --text-primary: var(--color-charcoal-700); - --input-surface: rgba(0, 0, 0, 0.15); + --input-surface: rgb(0 0 0 / 0.15); } .dark-theme { - --accent-primary: var(--color-pure-white); + --fg-color: #fff; + --bg-color: #202020; + --content-bg: #4e4e4e; + --content-fg: #fff; + --content-hover-bg: #222; + --content-hover-fg: #fff; + + --accent-primary: var(--color-white); --backdrop: var(--color-neutral-900); + --button-surface: var(--color-charcoal-600); - --button-surface-contrast: var(--color-pure-white); + --button-surface-contrast: var(--color-white); --button-hover-surface: var(--color-charcoal-600); --button-active-surface: var(--color-charcoal-600); - --button-icon: var(--color-gray-800); + --button-icon: var(--color-smoke-800); + --dialog-surface: var(--color-neutral-700); + --interface-menu-component-surface-hovered: var(--color-charcoal-400); --interface-menu-component-surface-selected: var(--color-charcoal-300); --interface-menu-keybind-surface-default: var(--color-charcoal-200); - --interface-panel-surface: var(--color-charcoal-100); + --interface-panel-surface: var(--color-charcoal-800); --interface-stroke: var(--color-charcoal-400); - --nav-background: var(--color-charcoal-100); + + --nav-background: var(--color-charcoal-800); + --node-border: var(--color-charcoal-500); - --node-component-border: var(--color-stone-200); + --node-component-border: var(--color-ash-800); --node-component-border-error: var(--color-danger-100); --node-component-border-executing: var(--color-blue-500); --node-component-border-selected: var(--color-charcoal-200); --node-component-header-icon: var(--color-slate-300); --node-component-header-surface: var(--color-charcoal-800); --node-component-outline: var(--color-white); - --node-component-ring: rgb(var(--color-gray-500) / 20%); + --node-component-ring: rgb(var(--color-smoke-500) / 20%); --node-component-slot-dot-outline-opacity: 10%; --node-component-slot-dot-outline: var(--color-white); --node-component-slot-text: var(--color-slate-200); @@ -240,14 +247,16 @@ --node-component-widget-skeleton-surface: var(--color-zinc-800); --node-component-disabled: var(--color-alpha-charcoal-600-30); --node-divider: var(--color-charcoal-500); - --node-icon-disabled: var(--color-alpha-stone-100-20); - --node-stroke: var(--color-stone-200); - --node-stroke-selected: var(--color-pure-white); + --node-icon-disabled: var(--color-alpha-ash-500-20); + --node-stroke: var(--color-ash-800); + --node-stroke-selected: var(--color-white); --node-stroke-error: var(--color-error); - --node-stroke-executing: var(--color-blue-100); + --node-stroke-executing: var(--color-azure-600); + --text-secondary: var(--color-slate-100); - --text-primary: var(--color-pure-white); - --input-surface: rgba(130, 130, 130, 0.1); + --text-primary: var(--color-white); + + --input-surface: rgb(130 130 130 / 0.1); } @theme inline { @@ -258,9 +267,15 @@ --color-button-surface: var(--button-surface); --color-button-surface-contrast: var(--button-surface-contrast); --color-dialog-surface: var(--dialog-surface); - --color-interface-menu-component-surface-hovered: var(--interface-menu-component-surface-hovered); - --color-interface-menu-component-surface-selected: var(--interface-menu-component-surface-selected); - --color-interface-menu-keybind-surface-default: var(--interface-menu-keybind-surface-default); + --color-interface-menu-component-surface-hovered: var( + --interface-menu-component-surface-hovered + ); + --color-interface-menu-component-surface-selected: var( + --interface-menu-component-surface-selected + ); + --color-interface-menu-keybind-surface-default: var( + --interface-menu-keybind-surface-default + ); --color-interface-panel-surface: var(--interface-panel-surface); --color-interface-stroke: var(--interface-stroke); --color-nav-background: var(--nav-background); @@ -324,7 +339,41 @@ } } -/* Everything below here to be cleaned up over time. */ +/* ===================== Scrollbar Utilities (Tailwind) ===================== + Usage: Add `scrollbar-custom` class to scrollable containers. + The scrollbar styling adapts to light/dark theme automatically. +============================================================================ */ + +@utility scrollbar-custom { + overflow-y: auto; + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: var(--dialog-surface) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 10px; + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track { + background: transparent; + } + &::-webkit-scrollbar-thumb { + background: var(--dialog-surface); + border-radius: 9999px; + border: 2px solid transparent; + } + &::-webkit-scrollbar-thumb:hover { + background: var(--dialog-surface); + } + &::-webkit-scrollbar-corner { + background: transparent; + } +} +/* =================== End Custom Scrollbar (cross-browser) =================== */ + +/* Everthing below here to be cleaned up over time. */ body { width: 100vw; @@ -1139,7 +1188,7 @@ audio.comfy-audio.empty-audio-widget { } .isLOD .lg-node-header { - border-radius: 0px; + border-radius: 0; pointer-events: none; } diff --git a/packages/design-system/src/icons/image-ai-edit.svg b/packages/design-system/src/icons/image-ai-edit.svg new file mode 100644 index 0000000000..437669d6da --- /dev/null +++ b/packages/design-system/src/icons/image-ai-edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/shared-frontend-utils/src/formatUtil.ts b/packages/shared-frontend-utils/src/formatUtil.ts index 658498f1fd..cb1a0d1a34 100644 --- a/packages/shared-frontend-utils/src/formatUtil.ts +++ b/packages/shared-frontend-utils/src/formatUtil.ts @@ -474,3 +474,68 @@ export function formatDuration(milliseconds: number): string { return parts.join(' ') } + +const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'] as const +const VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov', 'avi'] as const +const AUDIO_EXTENSIONS = ['mp3', 'wav', 'ogg', 'flac'] as const +const THREE_D_EXTENSIONS = ['obj', 'fbx', 'gltf', 'glb'] as const + +const MEDIA_TYPES = ['image', 'video', 'audio', '3D'] as const +type MediaType = (typeof MEDIA_TYPES)[number] + +// Type guard helper for checking array membership +type ImageExtension = (typeof IMAGE_EXTENSIONS)[number] +type VideoExtension = (typeof VIDEO_EXTENSIONS)[number] +type AudioExtension = (typeof AUDIO_EXTENSIONS)[number] +type ThreeDExtension = (typeof THREE_D_EXTENSIONS)[number] + +/** + * Truncates a filename while preserving the extension + * @param filename The filename to truncate + * @param maxLength Maximum length for the filename without extension + * @returns Truncated filename with extension preserved + */ +export function truncateFilename( + filename: string, + maxLength: number = 20 +): string { + if (!filename || filename.length <= maxLength) { + return filename + } + + const lastDotIndex = filename.lastIndexOf('.') + const nameWithoutExt = + lastDotIndex > -1 ? filename.substring(0, lastDotIndex) : filename + const extension = lastDotIndex > -1 ? filename.substring(lastDotIndex) : '' + + // If the name without extension is short enough, return as is + if (nameWithoutExt.length <= maxLength) { + return filename + } + + // Calculate how to split the truncation + const halfLength = Math.floor((maxLength - 3) / 2) // -3 for '...' + const start = nameWithoutExt.substring(0, halfLength) + const end = nameWithoutExt.substring(nameWithoutExt.length - halfLength) + + return `${start}...${end}${extension}` +} + +/** + * Determines the media type from a filename's extension (singular form) + * @param filename The filename to analyze + * @returns The media type: 'image', 'video', 'audio', or '3D' + */ +export function getMediaTypeFromFilename(filename: string): MediaType { + if (!filename) return 'image' + const ext = filename.split('.').pop()?.toLowerCase() + if (!ext) return 'image' + + // Type-safe array includes check using type assertion + if (IMAGE_EXTENSIONS.includes(ext as ImageExtension)) return 'image' + if (VIDEO_EXTENSIONS.includes(ext as VideoExtension)) return 'video' + if (AUDIO_EXTENSIONS.includes(ext as AudioExtension)) return 'audio' + if (THREE_D_EXTENSIONS.includes(ext as ThreeDExtension)) return '3D' + + return 'image' +} diff --git a/packages/tailwind-utils/README.md b/packages/tailwind-utils/README.md index 5f315600b2..35c725e881 100644 --- a/packages/tailwind-utils/README.md +++ b/packages/tailwind-utils/README.md @@ -14,7 +14,7 @@ import { cn } from '@comfyorg/tailwind-utils' // Use with conditional classes (ternary)