Compare commits
6 Commits
v1.31.0
...
bl-selecti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebd20a815e | ||
|
|
9616b91700 | ||
|
|
939d1a0e44 | ||
|
|
df6723415b | ||
|
|
83ff415815 | ||
|
|
87d3111d5c |
@@ -458,15 +458,15 @@ echo "Workflow triggered. Waiting for PR creation..."
|
||||
3. **IMMEDIATELY CHECK**: Did release workflow trigger?
|
||||
```bash
|
||||
sleep 10
|
||||
gh run list --workflow=release-draft-create.yaml --limit=1
|
||||
gh run list --workflow=release.yaml --limit=1
|
||||
```
|
||||
4. **For Minor/Major Version Releases**: The release-branch-create workflow will automatically:
|
||||
4. **For Minor/Major Version Releases**: The create-release-candidate-branch workflow will automatically:
|
||||
- Create a `core/x.yy` branch for the PREVIOUS minor version
|
||||
- Apply branch protection rules
|
||||
- Document the feature freeze policy
|
||||
```bash
|
||||
# Monitor branch creation (for minor/major releases)
|
||||
gh run list --workflow=release-branch-create.yaml --limit=1
|
||||
gh run list --workflow=create-release-candidate-branch.yaml --limit=1
|
||||
```
|
||||
4. If workflow didn't trigger due to [skip ci]:
|
||||
```bash
|
||||
@@ -477,7 +477,7 @@ echo "Workflow triggered. Waiting for PR creation..."
|
||||
```
|
||||
5. If workflow triggered, monitor execution:
|
||||
```bash
|
||||
WORKFLOW_RUN_ID=$(gh run list --workflow=release-draft-create.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
|
||||
WORKFLOW_RUN_ID=$(gh run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[0].databaseId')
|
||||
gh run watch ${WORKFLOW_RUN_ID}
|
||||
```
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ For each commit:
|
||||
3. Merge the PR: `gh pr merge --merge`
|
||||
4. Monitor release workflow:
|
||||
```bash
|
||||
gh run list --workflow=release-draft-create.yaml --limit=1
|
||||
gh run list --workflow=release.yaml --limit=1
|
||||
gh run watch
|
||||
```
|
||||
5. Track progress:
|
||||
|
||||
@@ -5,10 +5,6 @@ PLAYWRIGHT_TEST_URL=http://localhost:5173
|
||||
|
||||
# Proxy target of the local development server
|
||||
# Note: localhost:8188 does not work.
|
||||
# Cloud auto-detection: Setting this to any *.comfy.org URL automatically enables
|
||||
# cloud mode (DISTRIBUTION=cloud) without needing to set DISTRIBUTION separately.
|
||||
# Examples: https://testcloud.comfy.org/, https://stagingcloud.comfy.org/,
|
||||
# https://pr-123.testenvs.comfy.org/, https://cloud.comfy.org/
|
||||
DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188
|
||||
|
||||
# Allow dev server access from remote IP addresses.
|
||||
|
||||
21
.github/workflows/README.md
vendored
@@ -1,21 +0,0 @@
|
||||
# GitHub Workflows
|
||||
|
||||
## Naming Convention
|
||||
|
||||
Workflow files follow a consistent naming pattern: `<prefix>-<descriptive-name>.yaml`
|
||||
|
||||
### Category Prefixes
|
||||
|
||||
| Prefix | Purpose | Example |
|
||||
| ---------- | ----------------------------------- | ------------------------------------ |
|
||||
| `ci-` | Testing, linting, validation | `ci-tests-e2e.yaml` |
|
||||
| `release-` | Version management, publishing | `release-version-bump.yaml` |
|
||||
| `pr-` | PR automation (triggered by labels) | `pr-claude-review.yaml` |
|
||||
| `api-` | External Api type generation | `api-update-registry-api-types.yaml` |
|
||||
| `i18n-` | Internationalization updates | `i18n-update-core.yaml` |
|
||||
|
||||
## Documentation
|
||||
|
||||
Each workflow file contains comments explaining its purpose, triggers, and behavior. For specific details about what each workflow does, refer to the comments at the top of each `.yaml` file.
|
||||
|
||||
For GitHub Actions documentation, see [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows).
|
||||
@@ -1,4 +1,4 @@
|
||||
name: PR Backport
|
||||
name: Auto Backport
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
@@ -95,61 +95,41 @@ jobs:
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
|
||||
|
||||
- name: Collect backport targets
|
||||
- name: Extract version labels
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
id: targets
|
||||
id: versions
|
||||
run: |
|
||||
TARGETS=()
|
||||
declare -A SEEN=()
|
||||
|
||||
# Extract version labels (e.g., "1.24", "1.22")
|
||||
VERSIONS=""
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
# For manual triggers, get labels from the PR
|
||||
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
|
||||
else
|
||||
# For automatic triggers, extract from PR event
|
||||
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
||||
LABELS=$(echo "$LABELS" | jq -r '.[].name')
|
||||
fi
|
||||
|
||||
add_target() {
|
||||
local label="$1"
|
||||
local target="$2"
|
||||
|
||||
if [ -z "$target" ]; then
|
||||
return
|
||||
|
||||
for label in $LABELS; do
|
||||
# Match version labels like "1.24" (major.minor only)
|
||||
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
# Validate the branch exists before adding to list
|
||||
if git ls-remote --exit-code origin "core/${label}" >/dev/null 2>&1; then
|
||||
VERSIONS="${VERSIONS}${label} "
|
||||
else
|
||||
echo "::warning::Label '${label}' found but branch 'core/${label}' does not exist"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
target=$(echo "$target" | xargs)
|
||||
|
||||
if [ -z "$target" ] || [ -n "${SEEN[$target]}" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code origin "$target" >/dev/null 2>&1; then
|
||||
TARGETS+=("$target")
|
||||
SEEN["$target"]=1
|
||||
else
|
||||
echo "::warning::Label '${label}' references missing branch '${target}'"
|
||||
fi
|
||||
}
|
||||
|
||||
while IFS= read -r label; do
|
||||
[ -z "$label" ] && continue
|
||||
|
||||
if [[ "$label" =~ ^branch:(.+)$ ]]; then
|
||||
add_target "$label" "${BASH_REMATCH[1]}"
|
||||
elif [[ "$label" =~ ^backport:(.+)$ ]]; then
|
||||
add_target "$label" "${BASH_REMATCH[1]}"
|
||||
elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||
add_target "$label" "core/${label}"
|
||||
fi
|
||||
done <<< "$LABELS"
|
||||
|
||||
if [ "${#TARGETS[@]}" -eq 0 ]; then
|
||||
echo "::error::No backport targets found (use labels like '1.24' or 'branch:release/hotfix')"
|
||||
if [ -z "$VERSIONS" ]; then
|
||||
echo "::error::No version labels found (e.g., 1.24, 1.22)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT
|
||||
echo "Found backport targets: ${TARGETS[*]}"
|
||||
echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT
|
||||
echo "Found version labels: ${VERSIONS}"
|
||||
|
||||
- name: Backport commits
|
||||
if: steps.check-existing.outputs.skip != 'true'
|
||||
@@ -170,17 +150,16 @@ jobs:
|
||||
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
fi
|
||||
|
||||
for target in ${{ steps.targets.outputs.targets }}; do
|
||||
TARGET_BRANCH="${target}"
|
||||
SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-')
|
||||
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}"
|
||||
for version in ${{ steps.versions.outputs.versions }}; do
|
||||
echo "::group::Backporting to core/${version}"
|
||||
|
||||
echo "::group::Backporting to ${TARGET_BRANCH}"
|
||||
TARGET_BRANCH="core/${version}"
|
||||
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}"
|
||||
|
||||
# Fetch target branch (fail if doesn't exist)
|
||||
if ! git fetch origin "${TARGET_BRANCH}"; then
|
||||
echo "::error::Target branch ${TARGET_BRANCH} does not exist"
|
||||
FAILED="${FAILED}${TARGET_BRANCH}:branch-missing "
|
||||
FAILED="${FAILED}${version}:branch-missing "
|
||||
echo "::endgroup::"
|
||||
continue
|
||||
fi
|
||||
@@ -191,7 +170,7 @@ jobs:
|
||||
# Try cherry-pick
|
||||
if git cherry-pick "${MERGE_COMMIT}"; then
|
||||
git push origin "${BACKPORT_BRANCH}"
|
||||
SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} "
|
||||
SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} "
|
||||
echo "Successfully created backport branch: ${BACKPORT_BRANCH}"
|
||||
# Return to main (keep the branch, we need it for PR)
|
||||
git checkout main
|
||||
@@ -201,7 +180,7 @@ jobs:
|
||||
git cherry-pick --abort
|
||||
|
||||
echo "::error::Cherry-pick failed due to conflicts"
|
||||
FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} "
|
||||
FAILED="${FAILED}${version}:conflicts:${CONFLICTS} "
|
||||
|
||||
# Clean up the failed branch
|
||||
git checkout main
|
||||
@@ -235,13 +214,13 @@ jobs:
|
||||
fi
|
||||
|
||||
for backport in ${{ steps.backport.outputs.success }}; do
|
||||
IFS=':' read -r target branch <<< "${backport}"
|
||||
IFS=':' read -r version branch <<< "${backport}"
|
||||
|
||||
if PR_URL=$(gh pr create \
|
||||
--base "${target}" \
|
||||
--base "core/${version}" \
|
||||
--head "${branch}" \
|
||||
--title "[backport ${target}] ${PR_TITLE}" \
|
||||
--body "Backport of #${PR_NUMBER} to \`${target}\`"$'\n\n'"Automatically created by backport workflow." \
|
||||
--title "[backport ${version}] ${PR_TITLE}" \
|
||||
--body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \
|
||||
--label "backport" 2>&1); then
|
||||
|
||||
# Extract PR number from URL
|
||||
@@ -251,9 +230,9 @@ jobs:
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Successfully backported to #${PR_NUM}"
|
||||
fi
|
||||
else
|
||||
echo "::error::Failed to create PR for ${target}: ${PR_URL}"
|
||||
echo "::error::Failed to create PR for ${version}: ${PR_URL}"
|
||||
# Still try to comment on the original PR about the failure
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`${target}\`. Please create the PR manually from branch \`${branch}\`"
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`core/${version}\`. Please create the PR manually from branch \`${branch}\`"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -274,16 +253,16 @@ jobs:
|
||||
fi
|
||||
|
||||
for failure in ${{ steps.backport.outputs.failed }}; do
|
||||
IFS=':' read -r target reason conflicts <<< "${failure}"
|
||||
IFS=':' read -r version reason conflicts <<< "${failure}"
|
||||
|
||||
if [ "${reason}" = "branch-missing" ]; then
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist"
|
||||
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`core/${version}\` does not exist"
|
||||
|
||||
elif [ "${reason}" = "conflicts" ]; then
|
||||
# Convert comma-separated conflicts back to newlines for display
|
||||
CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
|
||||
|
||||
COMMENT_BODY="@${PR_AUTHOR} Backport to \`${target}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`${target}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
|
||||
COMMENT_BODY="@${PR_AUTHOR} Backport to \`core/${version}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`core/${version}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
|
||||
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
|
||||
fi
|
||||
done
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "PR: Claude Review"
|
||||
description: "AI-powered code review triggered by adding the 'claude-review' label to a PR"
|
||||
name: Claude PR Review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release PyPI Dev
|
||||
name: Create Dev PyPI Package
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -25,6 +25,17 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
dist
|
||||
tsconfig.tsbuildinfo
|
||||
key: dev-release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
dev-release-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Get current version
|
||||
id: current_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
@@ -33,7 +44,6 @@ jobs:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
||||
ENABLE_MINIFY: 'true'
|
||||
USE_PROD_CONFIG: 'true'
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release Branch Create
|
||||
name: Create Release Branch
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release Draft Create
|
||||
name: Create Release Draft
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -28,6 +28,16 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
tsconfig.tsbuildinfo
|
||||
key: release-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
release-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Get current version
|
||||
id: current_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
@@ -45,7 +55,6 @@ jobs:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
|
||||
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
||||
ENABLE_MINIFY: 'true'
|
||||
USE_PROD_CONFIG: 'true'
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
@@ -117,7 +126,7 @@ jobs:
|
||||
|
||||
publish_types:
|
||||
needs: build
|
||||
uses: ./.github/workflows/release-npm-types.yaml
|
||||
uses: ./.github/workflows/publish-frontend-types.yaml
|
||||
with:
|
||||
version: ${{ needs.build.outputs.version }}
|
||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "CI: Python Validation"
|
||||
description: "Validates Python code in tools/devtools directory"
|
||||
name: Devtools Python Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "CI: Lint Format"
|
||||
description: "Linting and code formatting validation for pull requests"
|
||||
name: Lint and Format
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -33,15 +32,27 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
.eslintcache
|
||||
tsconfig.tsbuildinfo
|
||||
.prettierCache
|
||||
.knip-cache
|
||||
key: lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js,mts}', '*.config.*', '.eslintrc.*', '.prettierrc.*', 'tsconfig.json') }}
|
||||
restore-keys: |
|
||||
lint-format-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
lint-format-cache-${{ runner.os }}-
|
||||
ci-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- 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
|
||||
|
||||
@@ -66,7 +77,6 @@ jobs:
|
||||
- name: Final validation
|
||||
run: |
|
||||
pnpm lint
|
||||
pnpm stylelint
|
||||
pnpm format:check
|
||||
pnpm knip
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
name: "CI: Tests E2E (Deploy for Forks)"
|
||||
description: "Deploys test results from forked PRs (forks can't access deployment secrets)"
|
||||
name: PR Playwright Deploy (Forks)
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["CI: Tests E2E"]
|
||||
workflows: ["Tests CI"]
|
||||
types: [requested, completed]
|
||||
|
||||
env:
|
||||
@@ -1,9 +1,8 @@
|
||||
name: "CI: Tests Storybook (Deploy for Forks)"
|
||||
description: "Deploys Storybook previews from forked PRs (forks can't access deployment secrets)"
|
||||
name: PR Storybook Deploy (Forks)
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["CI: Tests Storybook"]
|
||||
workflows: ['Storybook and Chromatic CI']
|
||||
types: [requested, completed]
|
||||
|
||||
env:
|
||||
@@ -1,309 +0,0 @@
|
||||
# Setting test expectation screenshots for Playwright
|
||||
name: "PR: Update Playwright Expectations"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
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_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
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: ${{ steps.pr-info.outputs.pr-number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: "Updating Playwright Expectations"
|
||||
|
||||
- name: Add Starting Reaction
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
||||
with:
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||
body: |
|
||||
Updating Playwright Expectations
|
||||
edit-mode: replace
|
||||
reactions: eyes
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ steps.pr-info.outputs.branch }}
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
# 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
|
||||
|
||||
# Run sharded tests with snapshot updates
|
||||
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||
id: playwright-tests
|
||||
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||
continue-on-error: true
|
||||
|
||||
# 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-shard-${{ matrix.shardIndex }}
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
# 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 "=========================================="
|
||||
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'
|
||||
|
||||
if git diff --quiet browser_tests/; then
|
||||
echo "No changes to commit"
|
||||
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' && steps.commit.outputs.has-changes == 'true'
|
||||
with:
|
||||
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 ${{ needs.setup.outputs.pr-number }} --remove-label "New Browser Test Expectations"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
15
.github/workflows/publish-desktop-ui.yaml
vendored
@@ -44,7 +44,6 @@ jobs:
|
||||
contents: read
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
ENABLE_MINIFY: 'true'
|
||||
steps:
|
||||
- name: Validate inputs
|
||||
env:
|
||||
@@ -161,6 +160,20 @@ jobs:
|
||||
echo "publish_dir=$PUBLISH_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pack (preview only)
|
||||
shell: bash
|
||||
working-directory: ${{ steps.pkg.outputs.publish_dir }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm pack --json | tee pack-result.json
|
||||
|
||||
- name: Upload package tarball artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: desktop-ui-npm-tarball-${{ inputs.version }}
|
||||
path: ${{ steps.pkg.outputs.publish_dir }}/*.tgz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Check if version already on npm
|
||||
id: check_npm
|
||||
env:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Release NPM Types
|
||||
name: Publish Frontend Types
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
52
.github/workflows/size-data.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: size data
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
collect:
|
||||
if: github.repository == 'Comfy-Org/ComfyUI_frontend'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: '24.x'
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build project
|
||||
run: pnpm build
|
||||
|
||||
- name: Collect size data
|
||||
run: node scripts/size-collect.js
|
||||
|
||||
- name: Save PR number & base branch
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo ${{ github.event.number }} > ./temp/size/number.txt
|
||||
echo ${{ github.base_ref }} > ./temp/size/base.txt
|
||||
|
||||
- name: Upload size data
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: size-data
|
||||
path: temp/size
|
||||
104
.github/workflows/size-report.yml
vendored
@@ -1,104 +0,0 @@
|
||||
name: size report
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['size data']
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to report on'
|
||||
required: true
|
||||
type: number
|
||||
run_id:
|
||||
description: 'Size data workflow run ID'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
size-report:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
|
||||
(
|
||||
(github.event_name == 'workflow_run' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success') ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
)
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: '24.x'
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Download size data
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
name: size-data
|
||||
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
|
||||
path: temp/size
|
||||
|
||||
- name: Set PR number
|
||||
id: pr-number
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "content=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "content=$(cat temp/size/number.txt)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Set base branch
|
||||
id: pr-base
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
echo "content=main" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "content=$(cat temp/size/base.txt)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Download previous size data
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
branch: ${{ steps.pr-base.outputs.content }}
|
||||
workflow: size-data.yml
|
||||
event: push
|
||||
name: size-data
|
||||
path: temp/size-prev
|
||||
if_no_artifact_found: warn
|
||||
|
||||
- name: Generate size report
|
||||
run: node scripts/size-report.js > size-report.md
|
||||
|
||||
- name: Read size report
|
||||
id: size-report
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./size-report.md
|
||||
|
||||
- name: Create or update PR comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ steps.pr-number.outputs.content }}
|
||||
body: |
|
||||
${{ steps.size-report.outputs.content }}
|
||||
<!-- COMFYUI_FRONTEND_SIZE -->
|
||||
body-include: '<!-- COMFYUI_FRONTEND_SIZE -->'
|
||||
@@ -1,5 +1,6 @@
|
||||
name: "CI: Tests Storybook"
|
||||
description: "Builds Storybook and runs visual regression testing via Chromatic, deploys previews to Cloudflare Pages"
|
||||
name: Storybook and Chromatic CI
|
||||
|
||||
# - [Automate Chromatic with GitHub Actions • Chromatic docs]( https://www.chromatic.com/docs/github-actions/ )
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manual triggering
|
||||
@@ -50,6 +51,19 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
storybook-static
|
||||
tsconfig.tsbuildinfo
|
||||
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
|
||||
restore-keys: |
|
||||
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
storybook-cache-${{ runner.os }}-
|
||||
storybook-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -102,6 +116,19 @@ jobs:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
storybook-static
|
||||
tsconfig.tsbuildinfo
|
||||
key: storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', '*.config.*', '.storybook/**/*') }}
|
||||
restore-keys: |
|
||||
storybook-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
storybook-cache-${{ runner.os }}-
|
||||
storybook-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "CI: Tests E2E"
|
||||
description: "End-to-end testing with Playwright across multiple browsers, deploys test reports to Cloudflare Pages"
|
||||
name: Tests CI
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -143,7 +142,7 @@ jobs:
|
||||
merge-reports:
|
||||
needs: [playwright-tests-chromium-sharded]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !cancelled() }}
|
||||
if: ${{ always() && !cancelled() }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
@@ -169,6 +168,26 @@ jobs:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
|
||||
|
||||
- name: Build failed screenshot manifest
|
||||
if: ${{ needs.playwright-tests-chromium-sharded.result == 'failure' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if ! pnpm tsx scripts/cicd/build-failed-screenshot-manifest.ts; then
|
||||
echo "ERROR: Failed to generate screenshot manifest"
|
||||
echo "This may indicate an issue with the Playwright JSON report or the manifest script"
|
||||
exit 1
|
||||
fi
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Upload failed screenshot manifest
|
||||
if: ${{ needs.playwright-tests-chromium-sharded.result == 'failure' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: failed-screenshot-tests
|
||||
path: ComfyUI_frontend/ci-rerun/*.txt
|
||||
retention-days: 7
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -1,5 +1,4 @@
|
||||
name: 'Api: Update Registry API Types'
|
||||
description: 'When upstream comfy-api is updated, click dispatch to update the TypeScript type definitions in this repo'
|
||||
name: Update Comfy Registry API Types
|
||||
|
||||
on:
|
||||
# Manual trigger
|
||||
@@ -30,9 +29,26 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: update-registry-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
update-registry-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache comfy-api repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: comfy-api
|
||||
key: comfy-api-repo-${{ runner.os }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
comfy-api-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout comfy-api repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
@@ -1,5 +1,4 @@
|
||||
name: 'Api: Update Manager API Types'
|
||||
description: 'When upstream ComfyUI-Manager API is updated, click dispatch to update the TypeScript type definitions in this repo'
|
||||
name: Update ComfyUI-Manager API Types
|
||||
|
||||
on:
|
||||
# Manual trigger
|
||||
@@ -31,9 +30,26 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: update-manager-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
update-manager-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Cache ComfyUI-Manager repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ComfyUI-Manager
|
||||
key: comfyui-manager-repo-${{ runner.os }}-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
comfyui-manager-repo-${{ runner.os }}-
|
||||
|
||||
- name: Checkout ComfyUI-Manager repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
@@ -1,5 +1,4 @@
|
||||
name: 'Api: Update Electron API Types'
|
||||
description: 'When upstream electron API is updated, click dispatch to update the TypeScript type definitions in this repo'
|
||||
name: Update Electron Types
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -26,6 +25,15 @@ jobs:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
key: electron-types-tools-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
electron-types-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Update electron types
|
||||
run: pnpm install --workspace-root @comfyorg/comfyui-electron-types@latest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: i18n Update Custom Nodes
|
||||
name: Update Locales for given custom node repository
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "i18n: Update Core"
|
||||
description: "Generates and updates translations for core ComfyUI components using OpenAI"
|
||||
name: Update Locales
|
||||
|
||||
on:
|
||||
# Manual dispatch for urgent translation updates
|
||||
@@ -55,5 +54,5 @@ jobs:
|
||||
# Apply the stashed changes if any
|
||||
git stash pop || true
|
||||
git add src/locales/
|
||||
git diff --staged --quiet || git commit -m "Update locales"
|
||||
git diff --staged --quiet || git commit -m "Update locales [skip ci]"
|
||||
git push origin HEAD:${{ github.head_ref }}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: i18n Update Nodes
|
||||
name: Update Node Definitions Locales
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
284
.github/workflows/update-playwright-expectations.yaml
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
# Setting test expectation screenshots for Playwright
|
||||
#
|
||||
# This workflow uses a selective snapshot update strategy:
|
||||
# 1. When tests fail in CI, they generate a manifest of failed test locations (file:line)
|
||||
# 2. This workflow downloads that manifest from the failed test run artifacts
|
||||
# 3. Only the failed tests are re-run with --update-snapshots (much faster than running all tests)
|
||||
# 4. Updated snapshots are committed back to the PR branch
|
||||
#
|
||||
# Trigger: Add label "New Browser Test Expectations" OR comment "/update-playwright" on PR
|
||||
name: Update Playwright Expectations
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
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_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR'
|
||||
) &&
|
||||
startsWith(github.event.comment.body, '/update-playwright') )
|
||||
steps:
|
||||
- name: Find Update Comment
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
|
||||
id: "find-update-comment"
|
||||
with:
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: "Updating Playwright Expectations"
|
||||
|
||||
- name: Add Starting Reaction
|
||||
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 }}
|
||||
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
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ steps.get-branch.outputs.branch }}
|
||||
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
|
||||
- name: Setup ComfyUI Server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
launch_server: true
|
||||
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
- name: Locate failed screenshot manifest artifact
|
||||
id: locate-manifest
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo
|
||||
let headSha = ''
|
||||
if (context.eventName === 'pull_request') {
|
||||
headSha = context.payload.pull_request.head.sha
|
||||
} else if (context.eventName === 'issue_comment') {
|
||||
const prNumber = context.payload.issue.number
|
||||
const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber })
|
||||
headSha = pr.data.head.sha
|
||||
}
|
||||
|
||||
if (!headSha) {
|
||||
core.setOutput('run_id', '')
|
||||
core.setOutput('has_manifest', 'false')
|
||||
return
|
||||
}
|
||||
|
||||
const { data } = await github.rest.actions.listWorkflowRuns({
|
||||
owner,
|
||||
repo,
|
||||
workflow_id: 'tests-ci.yaml',
|
||||
head_sha: headSha,
|
||||
event: 'pull_request',
|
||||
per_page: 1,
|
||||
})
|
||||
const run = data.workflow_runs?.[0]
|
||||
|
||||
let has = 'false'
|
||||
let runId = ''
|
||||
if (run) {
|
||||
runId = String(run.id)
|
||||
const { data: { artifacts = [] } } = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner,
|
||||
repo,
|
||||
run_id: run.id,
|
||||
per_page: 100,
|
||||
})
|
||||
if (artifacts.some(a => a.name === 'failed-screenshot-tests' && !a.expired)) has = 'true'
|
||||
}
|
||||
core.setOutput('run_id', runId)
|
||||
core.setOutput('has_manifest', has)
|
||||
|
||||
- name: Download failed screenshot manifest
|
||||
if: steps.locate-manifest.outputs.has_manifest == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
run-id: ${{ steps.locate-manifest.outputs.run_id }}
|
||||
name: failed-screenshot-tests
|
||||
path: ci-rerun
|
||||
|
||||
- name: Re-run failed screenshot tests and update snapshots
|
||||
id: playwright-tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Selective Snapshot Update"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Check if manifest exists
|
||||
if [ ! -d ci-rerun ]; then
|
||||
echo "ERROR: No manifest found in ci-rerun/ directory"
|
||||
echo " This means no failed screenshot tests were detected in the latest CI run."
|
||||
echo " Please ensure tests have been run and failures were recorded."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
files=(ci-rerun/*.txt)
|
||||
|
||||
if [ ${#files[@]} -eq 0 ]; then
|
||||
echo "ERROR: No manifest files found in ci-rerun/"
|
||||
echo " Expected files like: chromium.txt, chromium-2x.txt, mobile-chrome.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found ${#files[@]} project manifest(s):"
|
||||
for f in "${files[@]}"; do
|
||||
project="$(basename "$f" .txt)"
|
||||
count=$(grep -c . "$f" 2>/dev/null || echo "0")
|
||||
echo " - $project: $count failed test(s)"
|
||||
done
|
||||
echo ""
|
||||
|
||||
# Re-run tests per project
|
||||
total_tests=0
|
||||
for f in "${files[@]}"; do
|
||||
project="$(basename "$f" .txt)"
|
||||
mapfile -t lines < "$f"
|
||||
filtered=( )
|
||||
|
||||
# Validate and sanitize test paths to prevent command injection
|
||||
for l in "${lines[@]}"; do
|
||||
# Skip empty lines
|
||||
[ -z "$l" ] && continue
|
||||
|
||||
# Validate format: must be browser_tests/...spec.ts:number
|
||||
if [[ "$l" =~ ^browser_tests/.+\.spec\.ts:[0-9]+$ ]]; then
|
||||
filtered+=("$l")
|
||||
else
|
||||
echo "WARNING: Skipping invalid test path: $l"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#filtered[@]} -eq 0 ]; then
|
||||
echo "WARNING: Skipping $project (no valid tests in manifest)"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Updating snapshots for project: $project"
|
||||
echo " Re-running ${#filtered[@]} failed test(s)..."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright test --project="$project" --update-snapshots \
|
||||
--reporter=line --reporter=html \
|
||||
"${filtered[@]}"
|
||||
|
||||
total_tests=$((total_tests + ${#filtered[@]}))
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Completed snapshot updates for $total_tests test(s)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
- name: Debugging info
|
||||
run: |
|
||||
echo "PR: ${{ github.event.issue.number }}"
|
||||
echo "Branch: ${{ steps.get-branch.outputs.branch }}"
|
||||
git status
|
||||
|
||||
- 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
|
||||
echo "No expectation updates detected; skipping commit."
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
# Count changed snapshots
|
||||
changed_count=$(git diff --cached --name-only browser_tests | wc -l)
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
echo "count=$changed_count" >> $GITHUB_OUTPUT
|
||||
|
||||
git commit -m "[automated] Update test expectations"
|
||||
git push origin ${{ steps.get-branch.outputs.branch }}
|
||||
fi
|
||||
|
||||
- name: Generate workflow summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Snapshot Update Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ steps.commit.outputs.changed }}" = "true" ]; then
|
||||
echo "**${{ steps.commit.outputs.count }} snapshot(s) updated**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "<details>" >> $GITHUB_STEP_SUMMARY
|
||||
echo "<summary>View updated files</summary>" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
git diff HEAD~1 --name-only browser_tests 2>/dev/null || echo "No git history available" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "${{ steps.commit.outputs.changed }}" = "false" ]; then
|
||||
echo "No snapshot changes detected" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "WARNING: Snapshot update may have failed - check logs above" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Strategy:** Selective snapshot update (only failed tests re-run)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Add Done Reaction
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
||||
if: github.event_name == 'issue_comment'
|
||||
with:
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.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"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "CI: JSON Validation"
|
||||
description: "Validates JSON syntax in all tracked .json files (excluding tsconfig*.json) using jq"
|
||||
name: Validate JSON
|
||||
|
||||
on:
|
||||
push:
|
||||
26
.github/workflows/version-bump-desktop-ui.yaml
vendored
@@ -14,11 +14,6 @@ on:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
branch:
|
||||
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
bump-version-desktop-ui:
|
||||
@@ -31,25 +26,8 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate branch exists
|
||||
run: |
|
||||
BRANCH="${{ github.event.inputs.branch }}"
|
||||
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
|
||||
echo "❌ Branch '$BRANCH' does not exist"
|
||||
echo ""
|
||||
echo "Available core branches:"
|
||||
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
|
||||
echo ""
|
||||
echo "Main branch:"
|
||||
echo " - main"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Branch '$BRANCH' exists"
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -86,10 +64,8 @@ jobs:
|
||||
title: desktop-ui ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
body: |
|
||||
${{ steps.capitalised.outputs.capitalised }} version increment for @comfyorg/desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
|
||||
**Base branch:** `${{ github.event.inputs.branch }}`
|
||||
branch: desktop-ui-version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
base: ${{ github.event.inputs.branch }}
|
||||
base: main
|
||||
labels: |
|
||||
Release
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "Release: Version Bump"
|
||||
description: "Manual workflow to increment package version with semantic versioning support"
|
||||
name: Version Bump
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -15,11 +14,6 @@ on:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
branch:
|
||||
description: 'Base branch to bump (e.g., main, core/1.29, core/1.30)'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
bump-version:
|
||||
@@ -31,24 +25,6 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Validate branch exists
|
||||
run: |
|
||||
BRANCH="${{ github.event.inputs.branch }}"
|
||||
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" && ! git show-ref --verify --quiet "refs/remotes/origin/$BRANCH"; then
|
||||
echo "❌ Branch '$BRANCH' does not exist"
|
||||
echo ""
|
||||
echo "Available core branches:"
|
||||
git branch -r | grep 'origin/core/' | sed 's/.*origin\// - /' || echo " (none found)"
|
||||
echo ""
|
||||
echo "Main branch:"
|
||||
echo " - main"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Branch '$BRANCH' exists"
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -59,6 +35,7 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Bump version
|
||||
id: bump-version
|
||||
@@ -81,9 +58,7 @@ jobs:
|
||||
title: ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
body: |
|
||||
${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
|
||||
**Base branch:** `${{ github.event.inputs.branch }}`
|
||||
branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
|
||||
base: ${{ github.event.inputs.branch }}
|
||||
base: main
|
||||
labels: |
|
||||
Release
|
||||
@@ -1,5 +1,4 @@
|
||||
name: "CI: Tests Unit"
|
||||
description: "Unit and component testing with Vitest"
|
||||
name: Vitest Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -29,6 +28,19 @@ jobs:
|
||||
node-version: "lts/*"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
.cache
|
||||
coverage
|
||||
.vitest-cache
|
||||
key: vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('src/**/*.{ts,vue,js}', 'vitest.config.*', 'tsconfig.json') }}
|
||||
restore-keys: |
|
||||
vitest-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
|
||||
vitest-cache-${{ runner.os }}-
|
||||
test-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
2
.gitignore
vendored
@@ -78,7 +78,7 @@ templates_repo/
|
||||
vite.config.mts.timestamp-*.mjs
|
||||
|
||||
# Linux core dumps
|
||||
/core
|
||||
./core
|
||||
|
||||
*storybook.log
|
||||
storybook-static
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Run Knip with cache via package script
|
||||
pnpm knip 1>&2
|
||||
pnpm knip
|
||||
|
||||
|
||||
@@ -7,5 +7,12 @@
|
||||
"importOrder": ["^@core/(.*)$", "<THIRD_PARTY_MODULES>", "^@/(.*)$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"]
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.{js,cjs,mjs,ts,cts,mts,tsx,vue}",
|
||||
"options": {
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
Thank you for contributing to ComfyUI Frontend!
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>ComfyUI</title>
|
||||
<title>ComfyUI Desktop</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@comfyorg/desktop-ui",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"nx": {
|
||||
"tags": [
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div
|
||||
class="mx-auto grid h-[40rem] w-full max-w-3xl grid-rows-[1fr_auto_auto_1fr] select-none"
|
||||
class="grid grid-rows-[1fr_auto_auto_1fr] w-full max-w-3xl mx-auto h-[40rem] select-none"
|
||||
>
|
||||
<h2 class="text-center font-inter text-3xl font-bold text-neutral-100">
|
||||
<h2 class="font-inter font-bold text-3xl text-neutral-100 text-center">
|
||||
{{ $t('install.gpuPicker.title') }}
|
||||
</h2>
|
||||
|
||||
<!-- GPU Selection buttons - takes up remaining space and centers content -->
|
||||
<div class="flex flex-1 items-center justify-center gap-8">
|
||||
<div class="flex-1 flex gap-8 justify-center items-center">
|
||||
<!-- Apple Metal / NVIDIA -->
|
||||
<HardwareOption
|
||||
v-if="platform === 'darwin'"
|
||||
:image-path="'./assets/images/apple-mps-logo.png'"
|
||||
:image-path="'/assets/images/apple-mps-logo.png'"
|
||||
placeholder-text="Apple Metal"
|
||||
subtitle="Apple Metal"
|
||||
:value="'mps'"
|
||||
@@ -21,7 +21,7 @@
|
||||
/>
|
||||
<HardwareOption
|
||||
v-else
|
||||
:image-path="'./assets/images/nvidia-logo-square.jpg'"
|
||||
:image-path="'/assets/images/nvidia-logo-square.jpg'"
|
||||
placeholder-text="NVIDIA"
|
||||
:subtitle="$t('install.gpuPicker.nvidiaSubtitle')"
|
||||
:value="'nvidia'"
|
||||
@@ -47,17 +47,17 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="h-16 px-24 pt-12">
|
||||
<div class="pt-12 px-24 h-16">
|
||||
<div v-show="showRecommendedBadge" class="flex items-center gap-2">
|
||||
<Tag
|
||||
:value="$t('install.gpuPicker.recommended')"
|
||||
class="rounded-full bg-neutral-300 px-2 py-[1px] text-sm font-bold text-neutral-900"
|
||||
class="bg-neutral-300 text-neutral-900 rounded-full text-sm font-bold px-2 py-[1px]"
|
||||
/>
|
||||
<i class="icon-[lucide--badge-check] text-lg text-neutral-300" />
|
||||
<i class="icon-[lucide--badge-check] text-neutral-300 text-lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-24 text-neutral-300">
|
||||
<div class="text-neutral-300 px-24">
|
||||
<p v-show="descriptionText" class="leading-relaxed">
|
||||
{{ descriptionText }}
|
||||
</p>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="task-div relative grid min-h-52 max-w-48"
|
||||
class="task-div max-w-48 min-h-52 grid relative"
|
||||
:class="{ 'opacity-75': isLoading }"
|
||||
>
|
||||
<Card
|
||||
class="relative h-full max-w-48 overflow-hidden"
|
||||
class="max-w-48 relative h-full overflow-hidden"
|
||||
:class="{ 'opacity-65': runner.state !== 'error' }"
|
||||
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
|
||||
>
|
||||
<template #header>
|
||||
<i
|
||||
v-if="runner.state === 'error'"
|
||||
class="pi pi-exclamation-triangle absolute top-0 -right-14 m-2 text-red-500 opacity-15"
|
||||
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
|
||||
style="font-size: 10rem"
|
||||
/>
|
||||
<img
|
||||
v-if="task.headerImg"
|
||||
:src="task.headerImg"
|
||||
class="h-full w-full object-contain px-4 pt-4 opacity-25"
|
||||
class="object-contain w-full h-full opacity-25 pt-4 px-4"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
@@ -27,7 +27,7 @@
|
||||
{{ description }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="mt-1 flex gap-4">
|
||||
<div class="flex gap-4 mt-1">
|
||||
<Button
|
||||
:icon="task.button?.icon"
|
||||
:label="task.button?.text"
|
||||
@@ -73,7 +73,7 @@ defineEmits<{
|
||||
// Bindings
|
||||
const description = computed(() =>
|
||||
runner.value.state === 'error'
|
||||
? (props.task.errorDescription ?? props.task.shortDescription)
|
||||
? props.task.errorDescription ?? props.task.shortDescription
|
||||
: props.task.shortDescription
|
||||
)
|
||||
|
||||
|
||||
@@ -1,163 +1,67 @@
|
||||
// Import only English locale eagerly as the default/fallback
|
||||
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
|
||||
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' }
|
||||
import ar from '@frontend-locales/ar/main.json' with { type: 'json' }
|
||||
import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' }
|
||||
import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' }
|
||||
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import en from '@frontend-locales/en/main.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
|
||||
import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' }
|
||||
import es from '@frontend-locales/es/main.json' with { type: 'json' }
|
||||
import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' }
|
||||
import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' }
|
||||
import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' }
|
||||
import fr from '@frontend-locales/fr/main.json' with { type: 'json' }
|
||||
import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' }
|
||||
import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' }
|
||||
import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' }
|
||||
import ja from '@frontend-locales/ja/main.json' with { type: 'json' }
|
||||
import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' }
|
||||
import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' }
|
||||
import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' }
|
||||
import ko from '@frontend-locales/ko/main.json' with { type: 'json' }
|
||||
import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' }
|
||||
import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' }
|
||||
import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' }
|
||||
import ru from '@frontend-locales/ru/main.json' with { type: 'json' }
|
||||
import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' }
|
||||
import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' }
|
||||
import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' }
|
||||
import tr from '@frontend-locales/tr/main.json' with { type: 'json' }
|
||||
import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' }
|
||||
import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' }
|
||||
import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' }
|
||||
import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' }
|
||||
import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' }
|
||||
import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' }
|
||||
import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' }
|
||||
import zh from '@frontend-locales/zh/main.json' with { type: 'json' }
|
||||
import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' }
|
||||
import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' }
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
function buildLocale<
|
||||
M extends Record<string, unknown>,
|
||||
N extends Record<string, unknown>,
|
||||
C extends Record<string, unknown>,
|
||||
S extends Record<string, unknown>
|
||||
>(main: M, nodes: N, commands: C, settings: S) {
|
||||
function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) {
|
||||
return {
|
||||
...main,
|
||||
nodeDefs: nodes,
|
||||
commands: commands,
|
||||
settings: settings
|
||||
} as M & { nodeDefs: N; commands: C; settings: S }
|
||||
}
|
||||
|
||||
// Locale loader map - dynamically import locales only when needed
|
||||
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
|
||||
/* eslint-disable import-x/no-unresolved */
|
||||
const localeLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/main.json'),
|
||||
es: () => import('@frontend-locales/es/main.json'),
|
||||
fr: () => import('@frontend-locales/fr/main.json'),
|
||||
ja: () => import('@frontend-locales/ja/main.json'),
|
||||
ko: () => import('@frontend-locales/ko/main.json'),
|
||||
ru: () => import('@frontend-locales/ru/main.json'),
|
||||
tr: () => import('@frontend-locales/tr/main.json'),
|
||||
zh: () => import('@frontend-locales/zh/main.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
|
||||
}
|
||||
|
||||
const nodeDefsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
|
||||
es: () => import('@frontend-locales/es/nodeDefs.json'),
|
||||
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
|
||||
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
|
||||
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
|
||||
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
|
||||
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
|
||||
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
|
||||
}
|
||||
|
||||
const commandsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/commands.json'),
|
||||
es: () => import('@frontend-locales/es/commands.json'),
|
||||
fr: () => import('@frontend-locales/fr/commands.json'),
|
||||
ja: () => import('@frontend-locales/ja/commands.json'),
|
||||
ko: () => import('@frontend-locales/ko/commands.json'),
|
||||
ru: () => import('@frontend-locales/ru/commands.json'),
|
||||
tr: () => import('@frontend-locales/tr/commands.json'),
|
||||
zh: () => import('@frontend-locales/zh/commands.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
|
||||
}
|
||||
|
||||
const settingsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/settings.json'),
|
||||
es: () => import('@frontend-locales/es/settings.json'),
|
||||
fr: () => import('@frontend-locales/fr/settings.json'),
|
||||
ja: () => import('@frontend-locales/ja/settings.json'),
|
||||
ko: () => import('@frontend-locales/ko/settings.json'),
|
||||
ru: () => import('@frontend-locales/ru/settings.json'),
|
||||
tr: () => import('@frontend-locales/tr/settings.json'),
|
||||
zh: () => import('@frontend-locales/zh/settings.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
|
||||
}
|
||||
|
||||
// Track which locales have been loaded
|
||||
const loadedLocales = new Set<string>(['en'])
|
||||
|
||||
// Track locales currently being loaded to prevent race conditions
|
||||
const loadingLocales = new Map<string, Promise<void>>()
|
||||
|
||||
/**
|
||||
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
|
||||
*/
|
||||
export async function loadLocale(locale: string): Promise<void> {
|
||||
if (loadedLocales.has(locale)) {
|
||||
return
|
||||
}
|
||||
|
||||
// If already loading, return the existing promise to prevent duplicate loads
|
||||
const existingLoad = loadingLocales.get(locale)
|
||||
if (existingLoad) {
|
||||
return existingLoad
|
||||
}
|
||||
|
||||
const loader = localeLoaders[locale]
|
||||
const nodeDefsLoader = nodeDefsLoaders[locale]
|
||||
const commandsLoader = commandsLoaders[locale]
|
||||
const settingsLoader = settingsLoaders[locale]
|
||||
|
||||
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
|
||||
console.warn(`Locale "${locale}" is not supported`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and track the loading promise
|
||||
const loadPromise = (async () => {
|
||||
try {
|
||||
const [main, nodes, commands, settings] = await Promise.all([
|
||||
loader(),
|
||||
nodeDefsLoader(),
|
||||
commandsLoader(),
|
||||
settingsLoader()
|
||||
])
|
||||
|
||||
const messages = buildLocale(
|
||||
main.default,
|
||||
nodes.default,
|
||||
commands.default,
|
||||
settings.default
|
||||
)
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
|
||||
loadedLocales.add(locale)
|
||||
} catch (error) {
|
||||
console.error(`Failed to load locale "${locale}":`, error)
|
||||
throw error
|
||||
} finally {
|
||||
// Clean up the loading promise once complete
|
||||
loadingLocales.delete(locale)
|
||||
}
|
||||
})()
|
||||
|
||||
loadingLocales.set(locale, loadPromise)
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
// Only include English in the initial bundle
|
||||
const messages = {
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings)
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings),
|
||||
zh: buildLocale(zh, zhNodes, zhCommands, zhSettings),
|
||||
'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings),
|
||||
ru: buildLocale(ru, ruNodes, ruCommands, ruSettings),
|
||||
ja: buildLocale(ja, jaNodes, jaCommands, jaSettings),
|
||||
ko: buildLocale(ko, koNodes, koCommands, koSettings),
|
||||
fr: buildLocale(fr, frNodes, frCommands, frSettings),
|
||||
es: buildLocale(es, esNodes, esCommands, esSettings),
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings),
|
||||
tr: buildLocale(tr, trNodes, trCommands, trSettings)
|
||||
}
|
||||
|
||||
// Type for locale messages - inferred from the English locale structure
|
||||
type LocaleMessages = typeof messages.en
|
||||
|
||||
export const i18n = createI18n({
|
||||
// Must set `false`, as Vue I18n Legacy API is for Vue 2
|
||||
legacy: false,
|
||||
|
||||
@@ -66,6 +66,17 @@
|
||||
@click="troubleshoot"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<button
|
||||
v-if="!terminalVisible"
|
||||
class="text-sm text-neutral-500 hover:text-neutral-300 transition-colors flex items-center gap-2 mx-auto"
|
||||
@click="terminalVisible = true"
|
||||
>
|
||||
<i class="pi pi-search"></i>
|
||||
{{ $t('serverStart.showTerminal') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Terminal Output (positioned at bottom when manually toggled in error state) -->
|
||||
|
||||
@@ -46,10 +46,6 @@ class ComfyMenu {
|
||||
.nth(0)
|
||||
}
|
||||
|
||||
get buttons() {
|
||||
return this.sideToolbar.locator('.side-bar-button')
|
||||
}
|
||||
|
||||
get nodeLibraryTab() {
|
||||
this._nodeLibraryTab ??= new NodeLibrarySidebarTab(this.page)
|
||||
return this._nodeLibraryTab
|
||||
|
||||
@@ -7,7 +7,7 @@ export class Topbar {
|
||||
|
||||
constructor(public readonly page: Page) {
|
||||
this.menuLocator = page.locator('.comfy-command-menu')
|
||||
this.menuTrigger = page.locator('.comfy-menu-button-wrapper')
|
||||
this.menuTrigger = page.locator('.comfyui-logo-wrapper')
|
||||
}
|
||||
|
||||
async getTabNames(): Promise<string[]> {
|
||||
@@ -105,7 +105,7 @@ export class Topbar {
|
||||
* Close the topbar menu by clicking outside
|
||||
*/
|
||||
async closeTopbarMenu() {
|
||||
await this.page.locator('body').click({ position: { x: 300, y: 10 } })
|
||||
await this.page.locator('body').click({ position: { x: 10, y: 10 } })
|
||||
await expect(this.menuLocator).not.toBeVisible()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ComfyActionbar {
|
||||
|
||||
async isDocked() {
|
||||
const className = await this.root.getAttribute('class')
|
||||
return className?.includes('static') ?? false
|
||||
return className?.includes('is-docked') ?? false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,10 +116,9 @@ test.describe('Actionbar', () => {
|
||||
test('Can dock actionbar into top menu', async ({ comfyPage }) => {
|
||||
await comfyPage.page.dragAndDrop(
|
||||
'.actionbar .drag-handle',
|
||||
'.actionbar-container',
|
||||
'.comfyui-menu',
|
||||
{
|
||||
targetPosition: { x: 50, y: 20 },
|
||||
force: true
|
||||
targetPosition: { x: 0, y: 0 }
|
||||
}
|
||||
)
|
||||
expect(await comfyPage.actionbar.isDocked()).toBe(true)
|
||||
|
||||
@@ -301,9 +301,7 @@ test.describe('Settings', () => {
|
||||
})
|
||||
|
||||
test.describe('Support', () => {
|
||||
test('Should open external zendesk link with OSS tag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should open external zendesk link', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
const pagePromise = comfyPage.page.context().waitForEvent('page')
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
|
||||
@@ -311,10 +309,6 @@ 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()
|
||||
})
|
||||
})
|
||||
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 88 KiB |
@@ -39,15 +39,15 @@ test.describe('Graph Canvas Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Toggle minimap button is clickable and has correct test id', async ({
|
||||
test('Focus mode button is clickable and has correct test id', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const minimapButton = comfyPage.page.getByTestId('toggle-minimap-button')
|
||||
await expect(minimapButton).toBeVisible()
|
||||
await expect(minimapButton).toBeEnabled()
|
||||
const focusButton = comfyPage.page.getByTestId('focus-mode-button')
|
||||
await expect(focusButton).toBeVisible()
|
||||
await expect(focusButton).toBeEnabled()
|
||||
|
||||
// Test that the button can be clicked without error
|
||||
await minimapButton.click()
|
||||
await focusButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
})
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
@@ -233,7 +233,6 @@ test.describe('Group Node', () => {
|
||||
}
|
||||
|
||||
const isRegisteredNodeDefStore = async (comfyPage: ComfyPage) => {
|
||||
await comfyPage.menu.nodeLibraryTab.open()
|
||||
const groupNodesFolderCt = await comfyPage.menu.nodeLibraryTab
|
||||
.getFolder(GROUP_NODE_CATEGORY)
|
||||
.count()
|
||||
@@ -254,6 +253,8 @@ test.describe('Group Node', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.loadWorkflow(WORKFLOW_NAME)
|
||||
await comfyPage.menu.nodeLibraryTab.open()
|
||||
|
||||
groupNode = await comfyPage.getFirstNodeRef()
|
||||
if (!groupNode)
|
||||
throw new Error(`Group node not found in workflow ${WORKFLOW_NAME}`)
|
||||
|
||||
@@ -3,10 +3,10 @@ import { expect } from '@playwright/test'
|
||||
import type { Position } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyPageFixture as test,
|
||||
testComfySnapToGridGridSize
|
||||
} from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
@@ -786,25 +786,24 @@ test.describe('Viewport settings', () => {
|
||||
// Screenshot the canvas element
|
||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
||||
|
||||
// Open zoom controls dropdown first
|
||||
const zoomControlsButton = comfyPage.page.getByTestId(
|
||||
'zoom-controls-button'
|
||||
)
|
||||
await zoomControlsButton.click()
|
||||
|
||||
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
|
||||
await toggleButton.click()
|
||||
// close zoom menu
|
||||
await zoomControlsButton.click()
|
||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', false)
|
||||
|
||||
await comfyPage.menu.topbar.saveWorkflow('Workflow A')
|
||||
await comfyPage.nextFrame()
|
||||
const screenshotA = (await comfyPage.canvas.screenshot()).toString('base64')
|
||||
|
||||
// Save workflow as a new file, then zoom out before screen shot
|
||||
await comfyPage.menu.topbar.saveWorkflowAs('Workflow B')
|
||||
|
||||
await comfyPage.nextFrame()
|
||||
const tabA = comfyPage.menu.topbar.getWorkflowTab('Workflow A')
|
||||
await changeTab(tabA)
|
||||
|
||||
const screenshotA = (await comfyPage.canvas.screenshot()).toString('base64')
|
||||
|
||||
const tabB = comfyPage.menu.topbar.getWorkflowTab('Workflow B')
|
||||
await changeTab(tabB)
|
||||
|
||||
await comfyMouse.move(comfyPage.emptySpace)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await comfyMouse.wheel(0, 60)
|
||||
@@ -816,6 +815,9 @@ test.describe('Viewport settings', () => {
|
||||
// Ensure that the screenshots are different due to zoom level
|
||||
expect(screenshotB).not.toBe(screenshotA)
|
||||
|
||||
const tabA = comfyPage.menu.topbar.getWorkflowTab('Workflow A')
|
||||
const tabB = comfyPage.menu.topbar.getWorkflowTab('Workflow B')
|
||||
|
||||
// Go back to Workflow A
|
||||
await changeTab(tabA)
|
||||
expect((await comfyPage.canvas.screenshot()).toString('base64')).toBe(
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
@@ -8,7 +8,9 @@ test.describe('Menu', () => {
|
||||
})
|
||||
|
||||
test('Can register sidebar tab', async ({ comfyPage }) => {
|
||||
const initialChildrenCount = await comfyPage.menu.buttons.count()
|
||||
const initialChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
|
||||
(el) => el.children.length
|
||||
)
|
||||
|
||||
await comfyPage.page.evaluate(async () => {
|
||||
window['app'].extensionManager.registerSidebarTab({
|
||||
@@ -24,7 +26,9 @@ test.describe('Menu', () => {
|
||||
})
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
const newChildrenCount = await comfyPage.menu.buttons.count()
|
||||
const newChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
|
||||
(el) => el.children.length
|
||||
)
|
||||
expect(newChildrenCount).toBe(initialChildrenCount + 1)
|
||||
})
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ test.describe('Minimap', () => {
|
||||
})
|
||||
|
||||
test('Validate minimap toggle button state', async ({ comfyPage }) => {
|
||||
// Open zoom controls dropdown first
|
||||
const zoomControlsButton = comfyPage.page.getByTestId(
|
||||
'zoom-controls-button'
|
||||
)
|
||||
await zoomControlsButton.click()
|
||||
|
||||
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
|
||||
|
||||
await expect(toggleButton).toBeVisible()
|
||||
@@ -45,6 +51,13 @@ test.describe('Minimap', () => {
|
||||
|
||||
test('Validate minimap can be toggled off and on', async ({ comfyPage }) => {
|
||||
const minimapContainer = comfyPage.page.locator('.litegraph-minimap')
|
||||
|
||||
// Open zoom controls dropdown first
|
||||
const zoomControlsButton = comfyPage.page.getByTestId(
|
||||
'zoom-controls-button'
|
||||
)
|
||||
await zoomControlsButton.click()
|
||||
|
||||
const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button')
|
||||
|
||||
await expect(minimapContainer).toBeVisible()
|
||||
@@ -54,10 +67,22 @@ test.describe('Minimap', () => {
|
||||
|
||||
await expect(minimapContainer).not.toBeVisible()
|
||||
|
||||
// Open zoom controls dropdown again
|
||||
await zoomControlsButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(toggleButton).toContainText('Show Minimap')
|
||||
|
||||
await toggleButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(minimapContainer).toBeVisible()
|
||||
|
||||
// Open zoom controls dropdown again to verify button text
|
||||
await zoomControlsButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(toggleButton).toContainText('Hide Minimap')
|
||||
})
|
||||
|
||||
test('Validate minimap keyboard shortcut Alt+M', async ({ comfyPage }) => {
|
||||
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
@@ -1,28 +0,0 @@
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||
})
|
||||
|
||||
test.describe('Record Audio Node', () => {
|
||||
test('should add a record audio node and take a screenshot', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open the search box by double clicking on the canvas
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
|
||||
// Search for and add the RecordAudio node
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('RecordAudio')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Verify the RecordAudio node was added
|
||||
const recordAudioNodes = await comfyPage.getNodeRefsByType('RecordAudio')
|
||||
expect(recordAudioNodes.length).toBe(1)
|
||||
|
||||
// Take a screenshot of the canvas with the RecordAudio node
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('record_audio_node.png')
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -9,9 +9,6 @@ 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()
|
||||
})
|
||||
|
||||
@@ -23,7 +20,6 @@ test.describe('Vue Node Bypass', () => {
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 76 KiB |
@@ -23,10 +23,10 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
|
||||
const vueNodesContainer = comfyPage.vueNodes.nodes
|
||||
const textboxesInNodes = vueNodesContainer.getByRole('textbox')
|
||||
const comboboxesInNodes = vueNodesContainer.getByRole('combobox')
|
||||
const buttonsInNodes = vueNodesContainer.getByRole('button')
|
||||
|
||||
await expect(textboxesInNodes.first()).toBeVisible()
|
||||
await expect(comboboxesInNodes.first()).toBeVisible()
|
||||
await expect(buttonsInNodes.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(comboboxesInNodes.first()).toBeHidden()
|
||||
await expect(buttonsInNodes.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(comboboxesInNodes.first()).toBeVisible()
|
||||
await expect(buttonsInNodes.first()).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 66 KiB |
@@ -6,34 +6,6 @@ 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') &&
|
||||
@@ -91,22 +63,12 @@ export function comfyAPIPlugin(isDev: boolean): Plugin {
|
||||
const relativePath = path.relative(path.join(projectRoot, 'src'), id)
|
||||
const shimFileName = relativePath.replace(/\.ts$/, '.js')
|
||||
|
||||
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('')
|
||||
const shimComment = `// Shim for ${relativePath}\n`
|
||||
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: shimFileName,
|
||||
source: shimContent
|
||||
source: shimComment + result.exports.join('')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
755
docs/PLAYWRIGHT_SELECTIVE_RERUN_ALTERNATIVES.md
Normal file
@@ -0,0 +1,755 @@
|
||||
# Playwright Selective Test Rerun Alternatives
|
||||
|
||||
This document analyzes alternatives for selectively re-running only failed Playwright tests for snapshot updates, comparing native Playwright features with the current custom manifest approach used in this project.
|
||||
|
||||
## Table of Contents
|
||||
- [Current Approach](#current-approach)
|
||||
- [Native Playwright Features](#native-playwright-features)
|
||||
- [Playwright Reporter Options](#playwright-reporter-options)
|
||||
- [GitHub Actions Integration Patterns](#github-actions-integration-patterns)
|
||||
- [Third-Party Solutions](#third-party-solutions)
|
||||
- [Comparison and Recommendations](#comparison-and-recommendations)
|
||||
|
||||
---
|
||||
|
||||
## Current Approach
|
||||
|
||||
### Implementation
|
||||
The project currently uses a **custom manifest-based approach** that:
|
||||
|
||||
1. **Generates a manifest** of failed screenshot tests after CI runs
|
||||
- Script: `scripts/cicd/build-failed-screenshot-manifest.ts`
|
||||
- Parses JSON report to find tests with failed screenshot assertions
|
||||
- Creates per-project text files: `ci-rerun/{project}.txt`
|
||||
- Format: `file_path:line_number` (e.g., `browser_tests/menu.test.ts:42`)
|
||||
|
||||
2. **Stores manifest as GitHub artifact**
|
||||
- Artifact name: `failed-screenshot-tests`
|
||||
- Retention: 7 days
|
||||
- Only uploaded when chromium sharded tests fail
|
||||
|
||||
3. **Downloads manifest in update workflow**
|
||||
- Workflow: `.github/workflows/update-playwright-expectations.yaml`
|
||||
- Triggered by: PR label "New Browser Test Expectations" or `/update-playwright` comment
|
||||
- Falls back to full test suite if manifest not found
|
||||
|
||||
4. **Re-runs only failed tests**
|
||||
```bash
|
||||
for f in ci-rerun/*.txt; do
|
||||
project="$(basename "$f" .txt)"
|
||||
mapfile -t lines < "$f"
|
||||
# Filter empty lines
|
||||
pnpm exec playwright test --project="$project" --update-snapshots "${filtered[@]}"
|
||||
done
|
||||
```
|
||||
|
||||
### Advantages
|
||||
- ✅ Works across workflow runs and different trigger mechanisms
|
||||
- ✅ Survives beyond single workflow execution
|
||||
- ✅ Precise control over which tests to re-run
|
||||
- ✅ Supports multiple projects with separate manifests
|
||||
- ✅ Works with sharded test runs (merged report)
|
||||
- ✅ Platform-agnostic approach (works on any CI/CD platform)
|
||||
|
||||
### Disadvantages
|
||||
- ❌ Custom implementation requires maintenance
|
||||
- ❌ Requires parsing JSON report format (could break with Playwright updates)
|
||||
- ❌ Additional artifact storage needed
|
||||
- ❌ More complex than native solutions
|
||||
|
||||
---
|
||||
|
||||
## Native Playwright Features
|
||||
|
||||
### 1. `--last-failed` CLI Flag
|
||||
|
||||
**Availability:** Playwright v1.44.0+ (May 2024)
|
||||
|
||||
#### How It Works
|
||||
```bash
|
||||
# First run - execute all tests
|
||||
npx playwright test
|
||||
|
||||
# Second run - only re-run failed tests
|
||||
npx playwright test --last-failed
|
||||
```
|
||||
|
||||
Playwright maintains a `.last-run.json` file in the `test-results/` directory that tracks failed tests.
|
||||
|
||||
#### CLI Examples
|
||||
```bash
|
||||
# Run only failed tests from last run
|
||||
npx playwright test --last-failed
|
||||
|
||||
# Update snapshots for only failed tests
|
||||
npx playwright test --last-failed --update-snapshots
|
||||
|
||||
# Combine with project filtering
|
||||
npx playwright test --last-failed --project=chromium
|
||||
|
||||
# Debug failed tests
|
||||
npx playwright test --last-failed --debug
|
||||
```
|
||||
|
||||
#### File Location and Format
|
||||
- **Location:** `test-results/.last-run.json`
|
||||
- **Format:** JSON object containing failed test information
|
||||
- **Structure:** Contains a `failedTests: []` array with test identifiers
|
||||
- **Persistence:** Cleared when all tests pass on subsequent run
|
||||
|
||||
#### Advantages
|
||||
- ✅ Built into Playwright (no custom code)
|
||||
- ✅ Simple CLI flag
|
||||
- ✅ Automatically maintained by Playwright
|
||||
- ✅ Works with all Playwright features (debug, UI mode, etc.)
|
||||
|
||||
#### Limitations
|
||||
- ❌ **Not designed for CI/CD distributed testing** (per Playwright maintainers)
|
||||
- ❌ **Intended for local development only** ("inner loop scenario")
|
||||
- ❌ Cleared on new test runs (doesn't persist across clean environments)
|
||||
- ❌ **GitHub Actions starts with clean environment** - `.last-run.json` not available on retry
|
||||
- ❌ **Doesn't work with sharded tests** - each shard creates its own `.last-run.json`
|
||||
- ❌ No native way to merge `.last-run.json` across shards
|
||||
- ❌ Not designed for cross-workflow persistence
|
||||
|
||||
#### CI/CD Workaround (Not Recommended)
|
||||
To use `--last-failed` in GitHub Actions, you would need to:
|
||||
|
||||
```yaml
|
||||
- name: Run Playwright tests
|
||||
id: playwright-test
|
||||
run: npx playwright test
|
||||
|
||||
- name: Upload last run state
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: last-run-state
|
||||
path: test-results/.last-run.json
|
||||
|
||||
# In retry workflow:
|
||||
- name: Download last run state
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: last-run-state
|
||||
path: test-results/
|
||||
|
||||
- name: Rerun failed tests
|
||||
run: npx playwright test --last-failed --update-snapshots
|
||||
```
|
||||
|
||||
**Why This Isn't Ideal:**
|
||||
- Playwright maintainers explicitly state this is not the intended use case
|
||||
- Doesn't work well with sharded tests (multiple `.last-run.json` files)
|
||||
- Requires manual artifact management
|
||||
- More complex than the current custom approach for this use case
|
||||
|
||||
### 2. File:Line Syntax for Specific Tests
|
||||
|
||||
Playwright supports running tests at specific line numbers:
|
||||
|
||||
```bash
|
||||
# Run a specific test at line 42
|
||||
npx playwright test tests/example.spec.ts:42
|
||||
|
||||
# Multiple tests
|
||||
npx playwright test tests/file1.spec.ts:10 tests/file2.spec.ts:25
|
||||
|
||||
# With snapshot updates
|
||||
npx playwright test tests/example.spec.ts:42 --update-snapshots
|
||||
|
||||
# With project selection
|
||||
npx playwright test --project=chromium tests/example.spec.ts:42
|
||||
```
|
||||
|
||||
This is **exactly the format** the current custom manifest uses, making it compatible with Playwright's native CLI.
|
||||
|
||||
### 3. Test Filtering Options
|
||||
|
||||
```bash
|
||||
# Filter by grep pattern
|
||||
npx playwright test -g "screenshot"
|
||||
|
||||
# Inverse grep
|
||||
npx playwright test --grep-invert "mobile"
|
||||
|
||||
# By project
|
||||
npx playwright test --project=chromium
|
||||
|
||||
# Multiple projects
|
||||
npx playwright test --project=chromium --project=firefox
|
||||
|
||||
# Specific directory
|
||||
npx playwright test tests/screenshots/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Playwright Reporter Options
|
||||
|
||||
### 1. JSON Reporter
|
||||
|
||||
**Purpose:** Machine-readable test results
|
||||
|
||||
#### Configuration
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
export default defineConfig({
|
||||
reporter: [
|
||||
['json', { outputFile: 'results.json' }]
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
Or via environment variable:
|
||||
```bash
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=results.json npx playwright test --reporter=json
|
||||
```
|
||||
|
||||
#### Output Structure
|
||||
```json
|
||||
{
|
||||
"stats": {
|
||||
"expected": 100,
|
||||
"unexpected": 5,
|
||||
"flaky": 2,
|
||||
"skipped": 3
|
||||
},
|
||||
"suites": [
|
||||
{
|
||||
"title": "Test Suite",
|
||||
"specs": [
|
||||
{
|
||||
"file": "browser_tests/example.test.ts",
|
||||
"line": 42,
|
||||
"tests": [
|
||||
{
|
||||
"projectId": "chromium",
|
||||
"results": [
|
||||
{
|
||||
"status": "failed",
|
||||
"attachments": [
|
||||
{ "contentType": "image/png" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**This is the format** the current `build-failed-screenshot-manifest.ts` script parses.
|
||||
|
||||
#### Advantages
|
||||
- ✅ Stable, documented JSON schema (`@playwright/test/reporter`)
|
||||
- ✅ Includes all test metadata (file, line, project, status, attachments)
|
||||
- ✅ Can be used programmatically
|
||||
- ✅ Supports multiple reporters simultaneously
|
||||
|
||||
#### Current Project Usage
|
||||
```yaml
|
||||
# In tests-ci.yaml
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright test --project=${{ matrix.browser }} \
|
||||
--reporter=list \
|
||||
--reporter=html \
|
||||
--reporter=json
|
||||
```
|
||||
|
||||
### 2. Blob Reporter
|
||||
|
||||
**Purpose:** Merging sharded test reports
|
||||
|
||||
#### Configuration
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
export default defineConfig({
|
||||
reporter: process.env.CI ? 'blob' : 'html'
|
||||
})
|
||||
```
|
||||
|
||||
#### Usage with Sharding
|
||||
```bash
|
||||
# Run sharded test with blob output
|
||||
npx playwright test --shard=1/4 --reporter=blob
|
||||
|
||||
# Merge blob reports
|
||||
npx playwright merge-reports --reporter=html ./all-blob-reports
|
||||
npx playwright merge-reports --reporter=json ./all-blob-reports
|
||||
```
|
||||
|
||||
#### Current Project Usage
|
||||
```yaml
|
||||
# Sharded chromium tests
|
||||
- run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
||||
env:
|
||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ../blob-report
|
||||
|
||||
# Merge reports job
|
||||
- run: |
|
||||
pnpm exec playwright merge-reports --reporter=html ./all-blob-reports
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
|
||||
```
|
||||
|
||||
#### Advantages
|
||||
- ✅ Designed for distributed testing
|
||||
- ✅ Can merge into any reporter format (HTML, JSON, etc.)
|
||||
- ✅ Preserves all test information across shards
|
||||
|
||||
#### Blob Reporter and `--last-failed`
|
||||
- ❌ Blob reports **do not contain** a merged `.last-run.json`
|
||||
- ❌ Each shard creates its own `.last-run.json` that isn't included in blob
|
||||
- ❌ GitHub issue [#30924](https://github.com/microsoft/playwright/issues/30924) requests this feature (currently unsupported)
|
||||
|
||||
### 3. Multiple Reporters
|
||||
|
||||
You can use multiple reporters simultaneously:
|
||||
|
||||
```typescript
|
||||
export default defineConfig({
|
||||
reporter: [
|
||||
['list'], // Terminal output
|
||||
['html'], // Browse results
|
||||
['json', { outputFile: 'results.json' }], // Programmatic parsing
|
||||
['junit', { outputFile: 'results.xml' }] // CI integration
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
Or via CLI:
|
||||
```bash
|
||||
npx playwright test --reporter=list --reporter=html --reporter=json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions Integration Patterns
|
||||
|
||||
### Pattern 1: Comment-Triggered Workflow (JupyterLab Approach)
|
||||
|
||||
**Example:** [jupyterlab/jupyterlab-git](https://github.com/jupyterlab/jupyterlab-git/blob/main/.github/workflows/update-integration-tests.yml)
|
||||
|
||||
```yaml
|
||||
name: Update Playwright Snapshots
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-snapshots:
|
||||
# Only run for authorized users on PRs with specific comment
|
||||
if: >
|
||||
(github.event.issue.author_association == 'OWNER' ||
|
||||
github.event.issue.author_association == 'COLLABORATOR' ||
|
||||
github.event.issue.author_association == 'MEMBER'
|
||||
) && github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, 'please update snapshots')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: React to the triggering comment
|
||||
run: gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions --raw-field 'content=+1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Checkout PR branch
|
||||
run: gh pr checkout ${{ github.event.issue.number }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup and run tests
|
||||
run: |
|
||||
npm ci
|
||||
npx playwright install --with-deps
|
||||
npx playwright test --update-snapshots
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name 'github-actions'
|
||||
git config user.email 'github-actions@github.com'
|
||||
git add .
|
||||
git diff --cached --quiet || git commit -m "Update snapshots"
|
||||
git push
|
||||
```
|
||||
|
||||
#### Advantages
|
||||
- ✅ Simple comment-based trigger
|
||||
- ✅ Visual feedback (reaction on comment)
|
||||
- ✅ Authorization checks built-in
|
||||
- ✅ Auto-commits to PR branch
|
||||
|
||||
#### Limitations
|
||||
- ❌ Runs **all** tests with `--update-snapshots` (not selective)
|
||||
- ❌ No integration with failed test information from CI
|
||||
|
||||
### Pattern 2: Label-Based Trigger + Manifest (Current Approach)
|
||||
|
||||
```yaml
|
||||
name: Update Playwright Expectations
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
if: >
|
||||
( github.event_name == 'pull_request' &&
|
||||
github.event.label.name == 'New Browser Test Expectations' ) ||
|
||||
( github.event.issue.pull_request &&
|
||||
startsWith(github.event.comment.body, '/update-playwright') )
|
||||
|
||||
steps:
|
||||
# ... setup steps ...
|
||||
|
||||
- name: Locate failed screenshot manifest artifact
|
||||
id: locate-manifest
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo
|
||||
let headSha = ''
|
||||
if (context.eventName === 'pull_request') {
|
||||
headSha = context.payload.pull_request.head.sha
|
||||
} else if (context.eventName === 'issue_comment') {
|
||||
const prNumber = context.payload.issue.number
|
||||
const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber })
|
||||
headSha = pr.data.head.sha
|
||||
}
|
||||
|
||||
const { data } = await github.rest.actions.listWorkflowRuns({
|
||||
owner, repo,
|
||||
workflow_id: 'tests-ci.yaml',
|
||||
head_sha: headSha,
|
||||
per_page: 1,
|
||||
})
|
||||
const run = data.workflow_runs?.[0]
|
||||
|
||||
let has = 'false'
|
||||
if (run) {
|
||||
const { data: { artifacts = [] } } = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner, repo, run_id: run.id
|
||||
})
|
||||
if (artifacts.some(a => a.name === 'failed-screenshot-tests' && !a.expired))
|
||||
has = 'true'
|
||||
}
|
||||
core.setOutput('has_manifest', has)
|
||||
|
||||
- name: Download failed screenshot manifest
|
||||
if: steps.locate-manifest.outputs.has_manifest == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
run-id: ${{ steps.locate-manifest.outputs.run_id }}
|
||||
name: failed-screenshot-tests
|
||||
path: ComfyUI_frontend/ci-rerun
|
||||
|
||||
- name: Re-run failed screenshot tests
|
||||
run: |
|
||||
if [ ! -d ci-rerun ]; then
|
||||
echo "No manifest found; running full suite"
|
||||
pnpm exec playwright test --update-snapshots
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for f in ci-rerun/*.txt; do
|
||||
project="$(basename "$f" .txt)"
|
||||
mapfile -t lines < "$f"
|
||||
filtered=()
|
||||
for l in "${lines[@]}"; do
|
||||
[ -n "$l" ] && filtered+=("$l")
|
||||
done
|
||||
|
||||
if [ ${#filtered[@]} -gt 0 ]; then
|
||||
echo "Re-running ${#filtered[@]} tests for project $project"
|
||||
pnpm exec playwright test --project="$project" --update-snapshots "${filtered[@]}"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
#### Advantages
|
||||
- ✅ **Selective** - only re-runs failed screenshot tests
|
||||
- ✅ Works across different trigger mechanisms (label or comment)
|
||||
- ✅ Fallback to full suite if manifest not found
|
||||
- ✅ Per-project manifests support multiple browser configurations
|
||||
- ✅ Handles sharded tests via merged report
|
||||
|
||||
### Pattern 3: WordPress/Openverse Approach (Always Update)
|
||||
|
||||
Proposed pattern (not fully implemented):
|
||||
1. CI always runs with `--update-snapshots` flag
|
||||
2. If snapshots change, create/update a secondary branch
|
||||
3. Open PR targeting the original PR branch
|
||||
4. Developer reviews snapshot changes before merging
|
||||
|
||||
#### Advantages
|
||||
- ✅ Always generates correct snapshots
|
||||
- ✅ Snapshot changes are visible in separate PR
|
||||
- ✅ No test failures due to mismatched snapshots
|
||||
|
||||
#### Limitations
|
||||
- ❌ Creates multiple PRs
|
||||
- ❌ More complex merge workflow
|
||||
- ❌ Potential for snapshot changes to mask real issues
|
||||
|
||||
### Pattern 4: Manual Workflow Dispatch
|
||||
|
||||
```yaml
|
||||
name: Update Snapshots
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
update-snapshots:
|
||||
description: 'Update snapshots'
|
||||
type: boolean
|
||||
default: false
|
||||
test-pattern:
|
||||
description: 'Test pattern (optional)'
|
||||
type: string
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
npm ci
|
||||
npx playwright install --with-deps
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
if [ "${{ inputs.update-snapshots }}" = "true" ]; then
|
||||
FLAGS="--update-snapshots"
|
||||
fi
|
||||
|
||||
PATTERN="${{ inputs.test-pattern }}"
|
||||
npx playwright test ${PATTERN} ${FLAGS}
|
||||
```
|
||||
|
||||
#### Advantages
|
||||
- ✅ Full manual control
|
||||
- ✅ Can specify test patterns
|
||||
- ✅ Simple to understand
|
||||
|
||||
#### Limitations
|
||||
- ❌ Requires manual triggering
|
||||
- ❌ Not integrated with CI failures
|
||||
|
||||
---
|
||||
|
||||
## Third-Party Solutions
|
||||
|
||||
### Currents.dev - Last Failed GitHub Action
|
||||
|
||||
**Repository:** [currents-dev/playwright-last-failed](https://github.com/currents-dev/playwright-last-failed)
|
||||
|
||||
#### Purpose
|
||||
Helps run last failed Playwright tests using Currents' cloud-based caching service.
|
||||
|
||||
#### Usage
|
||||
```yaml
|
||||
- name: Playwright Last Failed action
|
||||
id: last-failed-action
|
||||
uses: currents-dev/playwright-last-failed@v1
|
||||
with:
|
||||
pw-output-dir: test-results
|
||||
matrix-index: ${{ matrix.shard }}
|
||||
matrix-total: ${{ strategy.job-total }}
|
||||
```
|
||||
|
||||
#### How It Works
|
||||
- Uses Currents' cloud service to persist failed test information
|
||||
- Supports sharded tests via matrix parameters
|
||||
- Enables selective rerun of failed tests across workflow retries
|
||||
|
||||
#### Advantages
|
||||
- ✅ Works with sharded tests
|
||||
- ✅ Persists across workflow runs
|
||||
- ✅ Supports GitHub Actions retry mechanism
|
||||
- ✅ Handles distributed testing
|
||||
|
||||
#### Limitations
|
||||
- ❌ **Requires Currents subscription** (third-party paid service)
|
||||
- ❌ Dependency on external service
|
||||
- ❌ Data sent to third-party cloud
|
||||
- ❌ Additional cost
|
||||
- ❌ Vendor lock-in
|
||||
|
||||
#### Recommendation
|
||||
**Not suitable for this project** due to:
|
||||
- External service dependency
|
||||
- Cost implications
|
||||
- The current custom solution is already working well
|
||||
|
||||
---
|
||||
|
||||
## Comparison and Recommendations
|
||||
|
||||
### Feature Matrix
|
||||
|
||||
| Feature | Current Approach | `--last-failed` | Currents | Comment Trigger Only |
|
||||
|---------|-----------------|-----------------|----------|---------------------|
|
||||
| Works with sharded tests | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes |
|
||||
| Persists across workflows | ✅ Yes | ❌ No | ✅ Yes | N/A |
|
||||
| Selective reruns | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No (runs all) |
|
||||
| No external dependencies | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
|
||||
| Simple implementation | ⚠️ Medium | ✅ Simple | ✅ Simple | ✅ Simple |
|
||||
| Maintenance overhead | ⚠️ Medium | ✅ Low | ✅ Low | ✅ Low |
|
||||
| Works in CI/CD | ✅ Yes | ⚠️ Workaround | ✅ Yes | ✅ Yes |
|
||||
| Cost | ✅ Free | ✅ Free | ❌ Paid | ✅ Free |
|
||||
| Supports multiple projects | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
||||
|
||||
### Why `--last-failed` Isn't Suitable (Currently)
|
||||
|
||||
1. **Not designed for CI/CD:** Playwright maintainers explicitly state it's for "inner loop scenario (local development)"
|
||||
2. **Doesn't work with sharded tests:** Each shard creates its own `.last-run.json` with no native merge
|
||||
3. **Clean environment issue:** GitHub Actions starts fresh, losing `.last-run.json`
|
||||
4. **Feature request pending:** GitHub issue [#30924](https://github.com/microsoft/playwright/issues/30924) requests blob report integration (not yet implemented)
|
||||
|
||||
### Recommendations
|
||||
|
||||
#### Short Term: Keep Current Approach
|
||||
**Verdict: The current custom manifest approach is the best solution for this project's needs.**
|
||||
|
||||
**Reasons:**
|
||||
1. ✅ **Works perfectly with sharded tests** - merges results across 8 shards
|
||||
2. ✅ **Persists across workflows** - artifact storage for 7 days
|
||||
3. ✅ **Selective reruns** - only failed screenshot tests
|
||||
4. ✅ **No external dependencies** - fully self-contained
|
||||
5. ✅ **Uses stable Playwright JSON format** - typed via `@playwright/test/reporter`
|
||||
6. ✅ **Already working well** - proven in production
|
||||
|
||||
**Minor Improvements:**
|
||||
```typescript
|
||||
// Add version check to warn if JSON schema changes
|
||||
import { version } from '@playwright/test/package.json'
|
||||
if (major(version) !== 1) {
|
||||
console.warn('Playwright major version changed - verify JSON schema compatibility')
|
||||
}
|
||||
|
||||
// Add more robust error handling
|
||||
try {
|
||||
const report: JSONReport = JSON.parse(raw)
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse Playwright JSON report: ${error.message}`)
|
||||
}
|
||||
|
||||
// Consider adding tests for the manifest builder
|
||||
// e.g., tests/cicd/build-failed-screenshot-manifest.test.ts
|
||||
```
|
||||
|
||||
#### Long Term: Monitor Playwright Development
|
||||
|
||||
**Watch for these features:**
|
||||
1. **Blob report + `.last-run.json` merge** - GitHub issue [#30924](https://github.com/microsoft/playwright/issues/30924)
|
||||
2. **Native CI/CD support for `--last-failed`** - may never happen (by design)
|
||||
3. **Report merging improvements** - GitHub issue [#33094](https://github.com/microsoft/playwright/issues/33094)
|
||||
|
||||
**Migration path if native support improves:**
|
||||
```yaml
|
||||
# Future potential approach (if Playwright adds this feature)
|
||||
- name: Merge reports with last-run
|
||||
run: |
|
||||
npx playwright merge-reports --reporter=html ./all-blob-reports
|
||||
npx playwright merge-reports --reporter=last-failed ./all-blob-reports
|
||||
|
||||
- name: Upload merged last-run
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: last-run-state
|
||||
path: test-results/.last-run.json
|
||||
|
||||
# In update workflow
|
||||
- name: Download last-run state
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: last-run-state
|
||||
path: test-results/
|
||||
|
||||
- name: Update snapshots for failed tests
|
||||
run: npx playwright test --last-failed --update-snapshots
|
||||
```
|
||||
|
||||
**However, this is speculative** - Playwright maintainers have indicated `--last-failed` is not intended for CI/CD.
|
||||
|
||||
#### Alternative: Simplify to Full Suite Reruns
|
||||
|
||||
If the custom manifest becomes too complex to maintain, consider:
|
||||
|
||||
```yaml
|
||||
- name: Re-run ALL screenshot tests
|
||||
run: |
|
||||
# Simple grep-based filtering for screenshot tests
|
||||
npx playwright test -g "screenshot" --update-snapshots
|
||||
```
|
||||
|
||||
**Trade-offs:**
|
||||
- ✅ Much simpler
|
||||
- ✅ No custom scripts
|
||||
- ❌ Slower (runs all screenshot tests, not just failed ones)
|
||||
- ❌ Potentially updates snapshots that weren't actually failing
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The current custom manifest approach is **well-designed** and **appropriate** for this project's requirements:
|
||||
|
||||
1. **Handles sharded tests** - critical for CI performance
|
||||
2. **Selective reruns** - saves time and resources
|
||||
3. **Stable implementation** - uses documented Playwright JSON schema
|
||||
4. **No external dependencies** - fully controlled
|
||||
|
||||
While `--last-failed` is a nice feature for **local development**, Playwright's own documentation and maintainer comments confirm it's **not suitable for distributed CI/CD testing**, which is exactly what this project needs.
|
||||
|
||||
The only potentially better solution (Currents) requires a paid external service, which adds cost and complexity without significant benefits over the current approach.
|
||||
|
||||
**Recommendation: Keep the current implementation**, with minor improvements to error handling and documentation. Monitor Playwright development for native improvements, but don't expect `--last-failed` to become a viable alternative for this use case.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### Official Playwright Documentation
|
||||
- [Command Line](https://playwright.dev/docs/test-cli)
|
||||
- [Reporters](https://playwright.dev/docs/test-reporters)
|
||||
- [Test Sharding](https://playwright.dev/docs/test-sharding)
|
||||
- [CI/CD Setup](https://playwright.dev/docs/ci-intro)
|
||||
|
||||
### Community Resources
|
||||
- [Playwright Solutions: How to Run Failures Only](https://playwrightsolutions.com/how-to-run-failures-only-from-the-last-playwright-run/)
|
||||
- [Medium: How to Run Only Last Failed Tests](https://medium.com/@testerstalk/how-to-run-only-last-failed-tests-in-playwright-e5e41472594a)
|
||||
- [Medium: Streamlining Visual Regression Testing](https://medium.com/@haleywardo/streamlining-playwright-visual-regression-testing-with-github-actions-e077fd33c27c)
|
||||
|
||||
### GitHub Issues
|
||||
- [#30924 - Last-failed with blob reports](https://github.com/microsoft/playwright/issues/30924)
|
||||
- [#33094 - Merging main run with --last-failed](https://github.com/microsoft/playwright/issues/33094)
|
||||
- [#28254 - Feature request for --last-failed](https://github.com/microsoft/playwright/issues/28254)
|
||||
|
||||
### Example Implementations
|
||||
- [JupyterLab Git - Update Integration Tests](https://github.com/jupyterlab/jupyterlab-git/blob/main/.github/workflows/update-integration-tests.yml)
|
||||
- [WordPress Openverse - Discussion #4535](https://github.com/WordPress/openverse/issues/4535)
|
||||
|
||||
### Third-Party Tools
|
||||
- [Currents - Playwright Last Failed Action](https://github.com/currents-dev/playwright-last-failed)
|
||||
- [Currents - Re-run Only Failed Tests](https://docs.currents.dev/guides/re-run-only-failed-tests)
|
||||
482
docs/SNAPSHOT_UPDATE_FROM_ACTUALS.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# Snapshot Update from Actual Files (Fast Approach)
|
||||
|
||||
**Date:** 2025-10-08
|
||||
**Status:** Proposed Optimization
|
||||
|
||||
## Overview
|
||||
|
||||
When Playwright snapshot tests fail, Playwright **already generates the new ("actual") snapshots**. Instead of re-running tests with `--update-snapshots`, we can extract these actual snapshots from the `test-results/` directory and copy them to overwrite the expected snapshots.
|
||||
|
||||
**Performance improvement:** ~1-2 minutes → **~10-30 seconds**
|
||||
|
||||
## How Playwright Stores Snapshots
|
||||
|
||||
### Expected (Baseline) Snapshots
|
||||
|
||||
Stored in: `<test-file>-snapshots/<snapshot-name>-<project>-<platform>.png`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
browser_tests/tests/interaction.spec.ts-snapshots/default-chromium-linux.png
|
||||
```
|
||||
|
||||
### Failed Test Artifacts
|
||||
|
||||
When a snapshot test fails, Playwright creates:
|
||||
|
||||
```
|
||||
test-results/<test-hash>/
|
||||
├── <snapshot-name>-actual.png # The NEW screenshot
|
||||
├── <snapshot-name>-expected.png # Copy of baseline
|
||||
└── <snapshot-name>-diff.png # Visual diff
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```
|
||||
test-results/interaction-default-chromium-67af3c/
|
||||
├── default-1-actual.png
|
||||
├── default-1-expected.png
|
||||
└── default-1-diff.png
|
||||
```
|
||||
|
||||
## Current Approach vs. Proposed Approach
|
||||
|
||||
### Current: Re-run Tests with `--update-snapshots`
|
||||
|
||||
```yaml
|
||||
# Current workflow (.github/workflows/update-playwright-expectations.yaml)
|
||||
- name: Re-run failed screenshot tests and update snapshots
|
||||
run: |
|
||||
# Download manifest of failed tests
|
||||
# For each project: chromium, chromium-2x, etc.
|
||||
# Run: playwright test --project="$project" --update-snapshots test1.spec.ts:42 test2.spec.ts:87 ...
|
||||
```
|
||||
|
||||
**Time:** ~2-5 minutes (depends on # of failed tests)
|
||||
|
||||
**Why slow:**
|
||||
- Re-executes tests (browser startup, navigation, interactions)
|
||||
- Waits for elements, animations, etc.
|
||||
- Generates HTML report
|
||||
- Each test takes 5-15 seconds
|
||||
|
||||
### Proposed: Copy Actual → Expected
|
||||
|
||||
```yaml
|
||||
# Proposed workflow
|
||||
- name: Download test artifacts (includes test-results/)
|
||||
- name: Copy actual snapshots to expected locations
|
||||
run: pnpm tsx scripts/cicd/update-snapshots-from-actuals.ts
|
||||
- name: Commit and push
|
||||
```
|
||||
|
||||
**Time:** ~10-30 seconds (just file operations)
|
||||
|
||||
**Why fast:**
|
||||
- No test execution
|
||||
- No browser startup
|
||||
- Just file copying
|
||||
- Parallel file operations
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Step 1: Modify tests-ci.yaml
|
||||
|
||||
Currently, test artifacts upload only the `playwright-report/` directory.
|
||||
|
||||
**Add test-results/ to artifacts:**
|
||||
|
||||
```yaml
|
||||
# .github/workflows/tests-ci.yaml
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-results-${{ matrix.browser }} # New artifact
|
||||
path: |
|
||||
ComfyUI_frontend/test-results/**/*-actual.png
|
||||
ComfyUI_frontend/test-results/**/*-expected.png
|
||||
ComfyUI_frontend/test-results/**/*-diff.png
|
||||
retention-days: 7
|
||||
```
|
||||
|
||||
**Optimization:** Only upload actual snapshots for failed tests (saves artifact storage)
|
||||
|
||||
### Step 2: Create Script to Map Actuals → Expected
|
||||
|
||||
**File:** `scripts/cicd/update-snapshots-from-actuals.ts`
|
||||
|
||||
```typescript
|
||||
import type { JSONReport, JSONReportTestResult } from '@playwright/test/reporter'
|
||||
import fs from 'node:fs'
|
||||
import fsp from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
interface SnapshotMapping {
|
||||
actualPath: string // test-results/.../snapshot-1-actual.png
|
||||
expectedPath: string // browser_tests/tests/foo.spec.ts-snapshots/snapshot-chromium-linux.png
|
||||
testFile: string
|
||||
testName: string
|
||||
project: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const reportPath = path.join('playwright-report', 'report.json')
|
||||
|
||||
if (!fs.existsSync(reportPath)) {
|
||||
console.log('No report.json found - no failed tests to update')
|
||||
return
|
||||
}
|
||||
|
||||
const raw = await fsp.readFile(reportPath, 'utf8')
|
||||
const report: JSONReport = JSON.parse(raw)
|
||||
|
||||
const mappings: SnapshotMapping[] = []
|
||||
|
||||
// Parse JSON report to extract snapshot paths
|
||||
function collectFailedSnapshots(suite: any) {
|
||||
if (!suite) return
|
||||
|
||||
for (const childSuite of suite.suites ?? []) {
|
||||
collectFailedSnapshots(childSuite)
|
||||
}
|
||||
|
||||
for (const spec of suite.specs ?? []) {
|
||||
for (const test of spec.tests) {
|
||||
const lastResult = test.results[test.results.length - 1]
|
||||
|
||||
if (lastResult?.status !== 'failed') continue
|
||||
|
||||
// Check if test has image attachments (indicates screenshot test)
|
||||
const imageAttachments = lastResult.attachments.filter(
|
||||
(att: any) => att?.contentType?.startsWith('image/')
|
||||
)
|
||||
|
||||
if (imageAttachments.length === 0) continue
|
||||
|
||||
// Extract snapshot mapping from attachments
|
||||
for (const attachment of imageAttachments) {
|
||||
const attachmentPath = attachment.path
|
||||
|
||||
if (!attachmentPath || !attachmentPath.includes('-actual.png')) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse test-results path to determine expected location
|
||||
// test-results/interaction-default-chromium-67af3c/default-1-actual.png
|
||||
// → browser_tests/tests/interaction.spec.ts-snapshots/default-chromium-linux.png
|
||||
|
||||
const actualPath = attachmentPath
|
||||
const expectedPath = inferExpectedPath(actualPath, spec.file, test.projectId)
|
||||
|
||||
if (expectedPath) {
|
||||
mappings.push({
|
||||
actualPath,
|
||||
expectedPath,
|
||||
testFile: spec.file,
|
||||
testName: test.annotations[0]?.description || test.title,
|
||||
project: test.projectId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectFailedSnapshots(report)
|
||||
|
||||
if (mappings.length === 0) {
|
||||
console.log('No failed snapshot tests found')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`Found ${mappings.length} snapshots to update`)
|
||||
|
||||
// Copy actual → expected
|
||||
let successCount = 0
|
||||
let errorCount = 0
|
||||
|
||||
for (const mapping of mappings) {
|
||||
try {
|
||||
if (!fs.existsSync(mapping.actualPath)) {
|
||||
console.warn(`⚠️ Actual file not found: ${mapping.actualPath}`)
|
||||
errorCount++
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure expected directory exists
|
||||
const expectedDir = path.dirname(mapping.expectedPath)
|
||||
await fsp.mkdir(expectedDir, { recursive: true })
|
||||
|
||||
// Copy actual → expected
|
||||
await fsp.copyFile(mapping.actualPath, mapping.expectedPath)
|
||||
|
||||
console.log(`✓ Updated: ${path.basename(mapping.expectedPath)}`)
|
||||
successCount++
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to update ${mapping.expectedPath}:`, error)
|
||||
errorCount++
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Successfully updated ${successCount} snapshots`)
|
||||
if (errorCount > 0) {
|
||||
console.log(`⚠️ Failed to update ${errorCount} snapshots`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer the expected snapshot path from the actual path
|
||||
*
|
||||
* Actual: test-results/interaction-default-chromium-67af3c/default-1-actual.png
|
||||
* Expected: browser_tests/tests/interaction.spec.ts-snapshots/default-chromium-linux.png
|
||||
*/
|
||||
function inferExpectedPath(actualPath: string, testFile: string, projectId: string): string | null {
|
||||
try {
|
||||
// Extract snapshot name from actual path
|
||||
// "default-1-actual.png" → "default"
|
||||
const actualFilename = path.basename(actualPath)
|
||||
const snapshotName = actualFilename.replace(/-\d+-actual\.png$/, '')
|
||||
|
||||
// Determine platform (linux, darwin, win32)
|
||||
const platform = process.platform === 'linux' ? 'linux'
|
||||
: process.platform === 'darwin' ? 'darwin'
|
||||
: 'win32'
|
||||
|
||||
// Build expected path
|
||||
const testDir = path.dirname(testFile)
|
||||
const testBasename = path.basename(testFile)
|
||||
const snapshotsDir = path.join(testDir, `${testBasename}-snapshots`)
|
||||
const expectedFilename = `${snapshotName}-${projectId}-${platform}.png`
|
||||
|
||||
return path.join(snapshotsDir, expectedFilename)
|
||||
} catch (error) {
|
||||
console.error(`Failed to infer expected path for ${actualPath}:`, error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('Failed to update snapshots:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
```
|
||||
|
||||
### Step 3: Better Approach - Use Playwright's Attachment Metadata
|
||||
|
||||
The JSON reporter actually includes the **expected snapshot path** in the attachments!
|
||||
|
||||
**Simplified script:**
|
||||
|
||||
```typescript
|
||||
async function main() {
|
||||
const report: JSONReport = JSON.parse(await fsp.readFile('playwright-report/report.json', 'utf8'))
|
||||
|
||||
const updates: Array<{ actual: string; expected: string }> = []
|
||||
|
||||
for (const result of getAllTestResults(report)) {
|
||||
if (result.status !== 'failed') continue
|
||||
|
||||
for (const attachment of result.attachments) {
|
||||
// Playwright includes both actual and expected in attachments
|
||||
if (attachment.name?.includes('-actual') && attachment.path) {
|
||||
const actualPath = attachment.path
|
||||
|
||||
// Find corresponding expected attachment
|
||||
const expectedAttachment = result.attachments.find(
|
||||
att => att.name === attachment.name.replace('-actual', '-expected')
|
||||
)
|
||||
|
||||
if (expectedAttachment?.path) {
|
||||
// The expected path in attachment points to the test-results copy
|
||||
// But we can infer the real expected path from the attachment metadata
|
||||
const expectedPath = inferRealExpectedPath(expectedAttachment)
|
||||
updates.push({ actual: actualPath, expected: expectedPath })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy files
|
||||
for (const { actual, expected } of updates) {
|
||||
await fsp.copyFile(actual, expected)
|
||||
console.log(`✓ Updated: ${path.relative(process.cwd(), expected)}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Update GitHub Actions Workflow
|
||||
|
||||
```yaml
|
||||
# .github/workflows/update-playwright-expectations.yaml
|
||||
name: Update Playwright Expectations
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
update:
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/update-snapshots') &&
|
||||
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'),
|
||||
github.event.comment.author_association)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: React to comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
github.rest.reactions.createForIssueComment({
|
||||
comment_id: context.payload.comment.id,
|
||||
content: '+1'
|
||||
})
|
||||
|
||||
- name: Checkout PR
|
||||
run: gh pr checkout ${{ github.event.issue.number }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
|
||||
- name: Get latest failed test run
|
||||
id: get-run
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.issue.number
|
||||
})
|
||||
|
||||
const runs = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'tests-ci.yaml',
|
||||
head_sha: pr.data.head.sha,
|
||||
per_page: 1
|
||||
})
|
||||
|
||||
core.setOutput('run_id', runs.data.workflow_runs[0]?.id || '')
|
||||
|
||||
- name: Download test results
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
run-id: ${{ steps.get-run.outputs.run_id }}
|
||||
pattern: playwright-results-*
|
||||
path: ComfyUI_frontend/test-results
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download JSON report
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
run-id: ${{ steps.get-run.outputs.run_id }}
|
||||
pattern: playwright-report-*
|
||||
path: ComfyUI_frontend/playwright-report
|
||||
merge-multiple: true
|
||||
|
||||
- name: Update snapshots from actuals
|
||||
working-directory: ComfyUI_frontend
|
||||
run: pnpm tsx scripts/cicd/update-snapshots-from-actuals.ts
|
||||
|
||||
- name: Commit and push
|
||||
working-directory: ComfyUI_frontend
|
||||
run: |
|
||||
git config user.name 'github-actions'
|
||||
git config user.email 'github-actions@github.com'
|
||||
git add browser_tests/**/*-snapshots/*.png
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No snapshot changes detected"
|
||||
else
|
||||
git commit -m "[automated] Update test expectations"
|
||||
git push
|
||||
fi
|
||||
```
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
### Current Approach: Re-run Tests
|
||||
|
||||
| Step | Time |
|
||||
|------|------|
|
||||
| Download manifest | 5s |
|
||||
| Install Playwright browsers | 20s |
|
||||
| Re-run 50 failed tests | 2-3 min |
|
||||
| Generate report | 10s |
|
||||
| Commit and push | 10s |
|
||||
| **Total** | **~3-4 min** |
|
||||
|
||||
### Proposed Approach: Copy Actuals
|
||||
|
||||
| Step | Time |
|
||||
|------|------|
|
||||
| Download test-results artifacts | 10s |
|
||||
| Download JSON report | 2s |
|
||||
| Run copy script | 5s |
|
||||
| Commit and push | 10s |
|
||||
| **Total** | **~30s** |
|
||||
|
||||
**Speedup: 6-8x faster** ⚡
|
||||
|
||||
## Advantages
|
||||
|
||||
✅ **Much faster** - No test re-execution
|
||||
✅ **Simpler** - No need for manifest generation
|
||||
✅ **Fewer dependencies** - No Playwright browser install needed
|
||||
✅ **Less resource usage** - No ComfyUI server, no browser processes
|
||||
✅ **More reliable** - File operations are deterministic
|
||||
✅ **Already tested** - The snapshots were generated during the actual test run
|
||||
|
||||
## Disadvantages / Edge Cases
|
||||
|
||||
❌ **New snapshots** - If a test creates a snapshot for the first time, there's no existing expected file. This is rare and can be handled by fallback to re-running.
|
||||
|
||||
❌ **Deleted tests** - Old snapshots won't be cleaned up automatically. Could add a cleanup step.
|
||||
|
||||
❌ **Multiple projects** - Each project (chromium, chromium-2x, mobile-chrome) generates separate actuals. The script needs to handle all of them.
|
||||
|
||||
❌ **Artifact storage** - Storing test-results/ increases artifact size. Mitigation: Only upload `-actual.png` files, not traces/videos.
|
||||
|
||||
## Hybrid Approach (Recommended)
|
||||
|
||||
Use the fast copy approach **with fallback**:
|
||||
|
||||
```yaml
|
||||
- name: Update snapshots
|
||||
run: |
|
||||
# Try fast approach first
|
||||
if pnpm tsx scripts/cicd/update-snapshots-from-actuals.ts; then
|
||||
echo "✓ Updated snapshots from actuals"
|
||||
else
|
||||
echo "⚠ Fast update failed, falling back to re-running tests"
|
||||
# Fallback to current approach
|
||||
pnpm exec playwright test --update-snapshots --project=chromium ...
|
||||
fi
|
||||
```
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] Create `scripts/cicd/update-snapshots-from-actuals.ts`
|
||||
- [ ] Update `tests-ci.yaml` to upload `test-results/` artifacts
|
||||
- [ ] Update `update-playwright-expectations.yaml` to use new script
|
||||
- [ ] Add fallback logic for edge cases
|
||||
- [ ] Test with actual PR
|
||||
- [ ] Update documentation
|
||||
- [ ] Consider switching from label trigger → comment trigger (`/update-snapshots`)
|
||||
|
||||
## Related Links
|
||||
|
||||
- **Playwright snapshot docs:** https://playwright.dev/docs/test-snapshots
|
||||
- **JSON reporter types:** `@playwright/test/reporter`
|
||||
- **GitHub Actions artifacts:** https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts
|
||||
- **Issue #22064:** Playwright feature request for better snapshot file alignment
|
||||
|
||||
## Conclusion
|
||||
|
||||
This approach is **significantly faster** and **simpler** than re-running tests. The main trade-off is artifact storage size, but this can be mitigated by only uploading actual snapshots (not traces/videos).
|
||||
|
||||
**Recommendation:** Implement this as the primary approach with fallback to re-running tests for edge cases.
|
||||
@@ -1,7 +1,6 @@
|
||||
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
|
||||
import pluginJs from '@eslint/js'
|
||||
import pluginI18n from '@intlify/eslint-plugin-vue-i18n'
|
||||
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
|
||||
import { importX } from 'eslint-plugin-import-x'
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
|
||||
import storybook from 'eslint-plugin-storybook'
|
||||
@@ -24,17 +23,10 @@ const commonGlobals = {
|
||||
} as const
|
||||
|
||||
const settings = {
|
||||
'import-x/resolver-next': [
|
||||
createTypeScriptImportResolver({
|
||||
alwaysTryTypes: true,
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./apps/*/tsconfig.json',
|
||||
'./packages/*/tsconfig.json'
|
||||
],
|
||||
noWarnOnMultipleProjects: true
|
||||
})
|
||||
],
|
||||
'import/resolver': {
|
||||
typescript: true,
|
||||
node: true
|
||||
},
|
||||
tailwindcss: {
|
||||
config: `${import.meta.dirname}/packages/design-system/src/css/style.css`,
|
||||
functions: ['cn', 'clsx', 'tw']
|
||||
@@ -76,7 +68,9 @@ export default defineConfig([
|
||||
projectService: {
|
||||
allowDefaultProject: [
|
||||
'vite.electron.config.mts',
|
||||
'vite.types.config.mts'
|
||||
'vite.types.config.mts',
|
||||
'playwright.config.ts',
|
||||
'playwright.i18n.config.ts'
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -254,17 +248,5 @@ export default defineConfig([
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['scripts/**/*.js'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'no-console': 'off'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
13
global.d.ts
vendored
@@ -5,19 +5,6 @@ declare const __ALGOLIA_APP_ID__: string
|
||||
declare const __ALGOLIA_API_KEY__: string
|
||||
declare const __USE_PROD_CONFIG__: boolean
|
||||
|
||||
interface Window {
|
||||
__CONFIG__: {
|
||||
mixpanel_token?: string
|
||||
subscription_required?: boolean
|
||||
server_health_alert?: {
|
||||
message: string
|
||||
tooltip?: string
|
||||
severity?: 'info' | 'warning' | 'error'
|
||||
badge?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
/**
|
||||
* Used by the electron API. This is a WICG non-standard API, but is guaranteed to exist in Electron.
|
||||
|
||||
@@ -12,10 +12,6 @@ const config: KnipConfig = {
|
||||
],
|
||||
project: ['**/*.{js,ts,vue}', '*.{js,ts,mts}']
|
||||
},
|
||||
'apps/desktop-ui': {
|
||||
entry: ['src/main.ts', 'src/i18n.ts'],
|
||||
project: ['src/**/*.{js,ts,vue}', '*.{js,ts,mts}']
|
||||
},
|
||||
'packages/tailwind-utils': {
|
||||
project: ['src/**/*.{js,ts}']
|
||||
},
|
||||
@@ -34,7 +30,9 @@ const config: KnipConfig = {
|
||||
'@primeuix/forms',
|
||||
'@primeuix/styled',
|
||||
'@primeuix/utils',
|
||||
'@primevue/icons'
|
||||
'@primevue/icons',
|
||||
// Dev
|
||||
'@trivago/prettier-plugin-sort-imports'
|
||||
],
|
||||
ignore: [
|
||||
// Auto generated manager types
|
||||
|
||||
@@ -8,10 +8,8 @@ export default {
|
||||
}
|
||||
|
||||
function formatAndEslint(fileNames) {
|
||||
// Convert absolute paths to relative paths for better ESLint resolution
|
||||
const relativePaths = fileNames.map((f) => f.replace(process.cwd() + '/', ''))
|
||||
return [
|
||||
`pnpm exec eslint --cache --fix ${relativePaths.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${relativePaths.join(' ')}`
|
||||
`pnpm exec eslint --cache --fix ${fileNames.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${fileNames.join(' ')}`
|
||||
]
|
||||
}
|
||||
|
||||
11
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.31.0",
|
||||
"version": "1.29.2",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
@@ -11,10 +11,7 @@
|
||||
"build:desktop": "nx build @comfyorg/desktop-ui",
|
||||
"build-storybook": "storybook build",
|
||||
"build:types": "nx build --config vite.types.config.mts && node scripts/prepare-types.js",
|
||||
"build:analyze": "cross-env ANALYZE_BUNDLE=true pnpm build",
|
||||
"build": "cross-env NODE_OPTIONS='--max-old-space-size=8192' pnpm typecheck && nx build",
|
||||
"size:collect": "node scripts/size-collect.js",
|
||||
"size:report": "node scripts/size-report.js",
|
||||
"collect-i18n": "pnpm exec playwright test --config=playwright.i18n.config.ts",
|
||||
"dev:desktop": "nx dev @comfyorg/desktop-ui",
|
||||
"dev:electron": "nx serve --config vite.electron.config.mts",
|
||||
@@ -55,7 +52,6 @@
|
||||
"@nx/vite": "catalog:",
|
||||
"@pinia/testing": "catalog:",
|
||||
"@playwright/test": "catalog:",
|
||||
"@prettier/plugin-oxc": "catalog:",
|
||||
"@storybook/addon-docs": "catalog:",
|
||||
"@storybook/vue3": "catalog:",
|
||||
"@storybook/vue3-vite": "catalog:",
|
||||
@@ -89,14 +85,9 @@
|
||||
"jsdom": "catalog:",
|
||||
"knip": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"markdown-table": "catalog:",
|
||||
"mixpanel-browser": "catalog:",
|
||||
"nx": "catalog:",
|
||||
"picocolors": "catalog:",
|
||||
"postcss-html": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"pretty-bytes": "catalog:",
|
||||
"rollup-plugin-visualizer": "catalog:",
|
||||
"storybook": "catalog:",
|
||||
"stylelint": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
|
||||
@@ -24,14 +24,11 @@
|
||||
--text-xxs: 0.625rem;
|
||||
--text-xxs--line-height: calc(1 / 0.625);
|
||||
|
||||
/* Spacing */
|
||||
--spacing-xs: 8px;
|
||||
|
||||
/* 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;
|
||||
@@ -63,7 +60,6 @@
|
||||
--color-sand-200: #d6cfc2;
|
||||
--color-sand-300: #888682;
|
||||
|
||||
--color-pure-black: #000000;
|
||||
--color-pure-white: #ffffff;
|
||||
|
||||
--color-slate-100: #9c9eab;
|
||||
@@ -89,10 +85,6 @@
|
||||
--color-bypass: #6a246a;
|
||||
--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);
|
||||
@@ -145,9 +137,6 @@
|
||||
--content-hover-bg: #adadad;
|
||||
--content-hover-fg: #000;
|
||||
|
||||
--button-surface: var(--color-pure-white);
|
||||
--button-surface-contrast: var(--color-pure-black);
|
||||
|
||||
/* Code styling colors for help menu*/
|
||||
--code-text-color: rgb(0 122 255 / 1);
|
||||
--code-bg-color: rgb(96 165 250 / 0.2);
|
||||
@@ -157,19 +146,8 @@
|
||||
|
||||
--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);
|
||||
--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);
|
||||
--node-component-executing: var(--color-blue-500);
|
||||
--node-component-header: var(--fg-color);
|
||||
--node-component-header-icon: var(--color-stone-200);
|
||||
@@ -181,7 +159,7 @@
|
||||
--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-surface-hovered: var(--color-charcoal-400);
|
||||
--node-component-surface-selected: var(--color-charcoal-200);
|
||||
--node-component-surface: var(--color-white);
|
||||
--node-component-tooltip: var(--color-charcoal-700);
|
||||
@@ -192,33 +170,18 @@
|
||||
from var(--color-zinc-500) r g b / 10%
|
||||
);
|
||||
--node-component-widget-skeleton-surface: var(--color-zinc-300);
|
||||
--node-divider: var(--color-sand-100);
|
||||
--node-component-disabled: var(--color-alpha-stone-100-20);
|
||||
--node-icon-disabled: var(--color-alpha-gray-500-50);
|
||||
--node-stroke: var(--color-gray-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);
|
||||
--text-primary: var(--color-charcoal-700);
|
||||
--input-surface: rgb(0 0 0 / 0.15);
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
--accent-primary: var(--color-pure-white);
|
||||
--backdrop: var(--color-neutral-900);
|
||||
--button-surface: var(--color-charcoal-600);
|
||||
--button-surface-contrast: var(--color-pure-white);
|
||||
--button-hover-surface: var(--color-charcoal-600);
|
||||
--button-active-surface: var(--color-charcoal-600);
|
||||
--button-icon: var(--color-gray-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-stroke: var(--color-charcoal-400);
|
||||
--nav-background: var(--color-charcoal-100);
|
||||
--node-border: var(--color-charcoal-500);
|
||||
--node-component-border: var(--color-stone-200);
|
||||
--node-component-border-error: var(--color-danger-100);
|
||||
--node-component-border-executing: var(--color-blue-500);
|
||||
@@ -231,7 +194,7 @@
|
||||
--node-component-slot-dot-outline: var(--color-white);
|
||||
--node-component-slot-text: var(--color-slate-200);
|
||||
--node-component-surface-highlight: var(--color-slate-100);
|
||||
--node-component-surface-hovered: var(--color-charcoal-600);
|
||||
--node-component-surface-hovered: var(--color-charcoal-400);
|
||||
--node-component-surface-selected: var(--color-charcoal-200);
|
||||
--node-component-surface: var(--color-charcoal-800);
|
||||
--node-component-tooltip: var(--color-white);
|
||||
@@ -239,38 +202,16 @@
|
||||
--node-component-tooltip-surface: var(--color-charcoal-800);
|
||||
--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-stroke-error: var(--color-error);
|
||||
--node-stroke-executing: var(--color-blue-100);
|
||||
--text-secondary: var(--color-slate-100);
|
||||
--text-primary: var(--color-pure-white);
|
||||
--input-surface: rgb(130 130 130 / 0.1);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-backdrop: var(--backdrop);
|
||||
--color-button-active-surface: var(--button-active-surface);
|
||||
--color-button-hover-surface: var(--button-hover-surface);
|
||||
--color-button-icon: var(--button-icon);
|
||||
--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-panel-surface: var(--interface-panel-surface);
|
||||
--color-interface-stroke: var(--interface-stroke);
|
||||
--color-nav-background: var(--nav-background);
|
||||
--color-node-border: var(--node-border);
|
||||
--color-node-component-border: var(--node-component-border);
|
||||
--color-node-component-executing: var(--node-component-executing);
|
||||
--color-node-component-header: var(--node-component-header);
|
||||
@@ -303,15 +244,11 @@
|
||||
--node-component-widget-skeleton-surface
|
||||
);
|
||||
--color-node-component-disabled: var(--node-component-disabled);
|
||||
--color-node-divider: var(--node-divider);
|
||||
--color-node-icon-disabled: var(--node-icon-disabled);
|
||||
--color-node-stroke: var(--node-stroke);
|
||||
--color-node-stroke-selected: var(--node-stroke-selected);
|
||||
--color-node-stroke-error: var(--node-stroke-error);
|
||||
--color-node-stroke-executing: var(--node-stroke-executing);
|
||||
--color-text-secondary: var(--text-secondary);
|
||||
--color-text-primary: var(--text-primary);
|
||||
--color-input-surface: var(--input-surface);
|
||||
}
|
||||
|
||||
@custom-variant dark-theme {
|
||||
@@ -330,42 +267,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ===================== 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. */
|
||||
/* Everything below here to be cleaned up over time. */
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
@@ -1180,7 +1082,7 @@ audio.comfy-audio.empty-audio-widget {
|
||||
}
|
||||
|
||||
.isLOD .lg-node-header {
|
||||
border-radius: 0;
|
||||
border-radius: 0px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 16 16" fill="none">
|
||||
<g clip-path="url(#clip0_704_2695)">
|
||||
<path d="M6.05048 2C5.52055 7.29512 9.23033 10.4722 14 9.94267" stroke="currentColor" stroke-width="1.3"/>
|
||||
<path d="M6.5 5.5L10 2" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M8 8L12.5 3.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="square"/>
|
||||
<path d="M10.5 9.5L14 6" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M7.99992 14.6667C11.6818 14.6667 14.6666 11.6819 14.6666 8.00004C14.6666 4.31814 11.6818 1.33337 7.99992 1.33337C4.31802 1.33337 1.33325 4.31814 1.33325 8.00004C1.33325 11.6819 4.31802 14.6667 7.99992 14.6667Z" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.05048 2C5.52055 7.29512 9.23033 10.4722 14 9.94267" stroke="#9C9EAB" stroke-width="1.3"/>
|
||||
<path d="M6.5 5.5L10 2" stroke="#9C9EAB" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M8 8L12.5 3.5" stroke="#9C9EAB" stroke-width="1.3" stroke-linecap="square"/>
|
||||
<path d="M10.5 9.5L14 6" stroke="#9C9EAB" stroke-width="1.3" stroke-linecap="round"/>
|
||||
<path d="M7.99992 14.6667C11.6818 14.6667 14.6666 11.6819 14.6666 8.00004C14.6666 4.31814 11.6818 1.33337 7.99992 1.33337C4.31802 1.33337 1.33325 4.31814 1.33325 8.00004C1.33325 11.6819 4.31802 14.6667 7.99992 14.6667Z" stroke="#9C9EAB" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_704_2695">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 938 B |
431
pnpm-lock.yaml
generated
@@ -45,9 +45,6 @@ catalogs:
|
||||
'@playwright/test':
|
||||
specifier: ^1.52.0
|
||||
version: 1.52.0
|
||||
'@prettier/plugin-oxc':
|
||||
specifier: ^0.0.4
|
||||
version: 0.0.4
|
||||
'@primeuix/forms':
|
||||
specifier: 0.0.2
|
||||
version: 0.0.2
|
||||
@@ -186,18 +183,9 @@ catalogs:
|
||||
lint-staged:
|
||||
specifier: ^15.2.7
|
||||
version: 15.2.7
|
||||
markdown-table:
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4
|
||||
mixpanel-browser:
|
||||
specifier: ^2.71.0
|
||||
version: 2.71.0
|
||||
nx:
|
||||
specifier: 21.4.1
|
||||
version: 21.4.1
|
||||
picocolors:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1
|
||||
pinia:
|
||||
specifier: ^2.1.7
|
||||
version: 2.2.2
|
||||
@@ -205,20 +193,14 @@ catalogs:
|
||||
specifier: ^1.8.0
|
||||
version: 1.8.0
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
pretty-bytes:
|
||||
specifier: ^7.1.0
|
||||
version: 7.1.0
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
primeicons:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
primevue:
|
||||
specifier: ^4.2.5
|
||||
version: 4.2.5
|
||||
rollup-plugin-visualizer:
|
||||
specifier: ^6.0.4
|
||||
version: 6.0.4
|
||||
storybook:
|
||||
specifier: ^9.1.6
|
||||
version: 9.1.6
|
||||
@@ -269,7 +251,7 @@ catalogs:
|
||||
version: 3.5.13
|
||||
vue-component-type-helpers:
|
||||
specifier: ^3.0.7
|
||||
version: 3.1.1
|
||||
version: 3.1.0
|
||||
vue-eslint-parser:
|
||||
specifier: ^10.2.0
|
||||
version: 10.2.0
|
||||
@@ -491,7 +473,7 @@ importers:
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@playwright/test@1.52.0)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(typescript@5.9.2)
|
||||
'@nx/storybook':
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
'@nx/vite':
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(nx@21.4.1)(typescript@5.9.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vitest@3.2.4)
|
||||
@@ -501,24 +483,21 @@ importers:
|
||||
'@playwright/test':
|
||||
specifier: 'catalog:'
|
||||
version: 1.52.0
|
||||
'@prettier/plugin-oxc':
|
||||
specifier: 'catalog:'
|
||||
version: 0.0.4
|
||||
'@storybook/addon-docs':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
version: 9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/vue3':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
'@storybook/vue3-vite':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))
|
||||
'@tailwindcss/vite':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.12(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@trivago/prettier-plugin-sort-imports':
|
||||
specifier: 'catalog:'
|
||||
version: 5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.6.2)
|
||||
version: 5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.3.2)
|
||||
'@types/eslint-plugin-tailwindcss':
|
||||
specifier: 'catalog:'
|
||||
version: 3.17.0
|
||||
@@ -566,10 +545,10 @@ importers:
|
||||
version: 4.16.1(@typescript-eslint/utils@8.44.0(eslint@9.35.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.4.2))
|
||||
eslint-plugin-prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.6.2)
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.3.2)
|
||||
eslint-plugin-storybook:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
version: 9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
eslint-plugin-tailwindcss:
|
||||
specifier: 'catalog:'
|
||||
version: 4.0.0-beta.0(tailwindcss@4.1.12)
|
||||
@@ -603,33 +582,18 @@ importers:
|
||||
lint-staged:
|
||||
specifier: 'catalog:'
|
||||
version: 15.2.7
|
||||
markdown-table:
|
||||
specifier: 'catalog:'
|
||||
version: 3.0.4
|
||||
mixpanel-browser:
|
||||
specifier: 'catalog:'
|
||||
version: 2.71.0
|
||||
nx:
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1
|
||||
picocolors:
|
||||
specifier: 'catalog:'
|
||||
version: 1.1.1
|
||||
postcss-html:
|
||||
specifier: 'catalog:'
|
||||
version: 1.8.0
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
pretty-bytes:
|
||||
specifier: 'catalog:'
|
||||
version: 7.1.0
|
||||
rollup-plugin-visualizer:
|
||||
specifier: 'catalog:'
|
||||
version: 6.0.4(rollup@4.22.4)
|
||||
version: 3.3.2
|
||||
storybook:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
version: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
stylelint:
|
||||
specifier: 'catalog:'
|
||||
version: 16.24.0(typescript@5.9.2)
|
||||
@@ -677,7 +641,7 @@ importers:
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.14.10)(@vitest/ui@3.2.4)(happy-dom@15.11.0)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
vue-component-type-helpers:
|
||||
specifier: 'catalog:'
|
||||
version: 3.1.1
|
||||
version: 3.1.0
|
||||
vue-eslint-parser:
|
||||
specifier: 'catalog:'
|
||||
version: 10.2.0(eslint@9.35.0(jiti@2.4.2))
|
||||
@@ -2213,21 +2177,6 @@ packages:
|
||||
'@microsoft/tsdoc@0.15.1':
|
||||
resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==}
|
||||
|
||||
'@mixpanel/rrdom@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-vX/tbnS14ZzzatC7vOyvAm9tOLU8tof0BuppBlphzEx1YHTSw8DQiAmyAc0AmXidchLV0W+cUHV/WsehPLh2hQ==}
|
||||
|
||||
'@mixpanel/rrweb-snapshot@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-2kSnjZZ3QZ9zOz/isOt8s54mXUUDgXk/u0eEi/rE0xBWDeuA0NHrBcqiMc+w4F/yWWUpo5F5zcuPeYpc6ufAsw==}
|
||||
|
||||
'@mixpanel/rrweb-types@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-ucIYe1mfJ2UksvXW+d3bOySTB2/0yUSqQJlUydvbBz6OO2Bhq3nJHyLXV9ExkgUMZm1ZyDcvvmNUd1+5tAXlpA==}
|
||||
|
||||
'@mixpanel/rrweb-utils@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-OomKIB6GTx5xvCLJ7iic2khT/t/tnCJUex13aEqsbSqIT/UzUUsqf+LTrgUK5ex+f6odmkCNjre2y5jvpNqn+g==}
|
||||
|
||||
'@mixpanel/rrweb@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-J3dVTEu6Z4p8di7y9KKvUooNuBjX97DdG6XGWoPEPi07A9512h9M8MEtvlY3mK0PGfuC0Mz5Pv/Ws6gjGYfKQg==}
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||
|
||||
@@ -2354,98 +2303,6 @@ packages:
|
||||
'@one-ini/wasm@0.1.1':
|
||||
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
|
||||
|
||||
'@oxc-parser/binding-android-arm64@0.74.0':
|
||||
resolution: {integrity: sha512-lgq8TJq22eyfojfa2jBFy2m66ckAo7iNRYDdyn9reXYA3I6Wx7tgGWVx1JAp1lO+aUiqdqP/uPlDaETL9tqRcg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@oxc-parser/binding-darwin-arm64@0.74.0':
|
||||
resolution: {integrity: sha512-xbY/io/hkARggbpYEMFX6CwFzb7f4iS6WuBoBeZtdqRWfIEi7sm/uYWXfyVeB8uqOATvJ07WRFC2upI8PSI83g==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxc-parser/binding-darwin-x64@0.74.0':
|
||||
resolution: {integrity: sha512-FIj2gAGtFaW0Zk+TnGyenMUoRu1ju+kJ/h71D77xc1owOItbFZFGa+4WSVck1H8rTtceeJlK+kux+vCjGFCl9Q==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxc-parser/binding-freebsd-x64@0.74.0':
|
||||
resolution: {integrity: sha512-W1I+g5TJg0TRRMHgEWNWsTIfe782V3QuaPgZxnfPNmDMywYdtlzllzclBgaDq6qzvZCCQc/UhvNb37KWTCTj8A==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@oxc-parser/binding-linux-arm-gnueabihf@0.74.0':
|
||||
resolution: {integrity: sha512-gxqkyRGApeVI8dgvJ19SYe59XASW3uVxF1YUgkE7peW/XIg5QRAOVTFKyTjI9acYuK1MF6OJHqx30cmxmZLtiQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-arm-musleabihf@0.74.0':
|
||||
resolution: {integrity: sha512-jpnAUP4Fa93VdPPDzxxBguJmldj/Gpz7wTXKFzpAueqBMfZsy9KNC+0qT2uZ9HGUDMzNuKw0Se3bPCpL/gfD2Q==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-arm64-gnu@0.74.0':
|
||||
resolution: {integrity: sha512-fcWyM7BNfCkHqIf3kll8fJctbR/PseL4RnS2isD9Y3FFBhp4efGAzhDaxIUK5GK7kIcFh1P+puIRig8WJ6IMVQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-arm64-musl@0.74.0':
|
||||
resolution: {integrity: sha512-AMY30z/C77HgiRRJX7YtVUaelKq1ex0aaj28XoJu4SCezdS8i0IftUNTtGS1UzGjGZB8zQz5SFwVy4dRu4GLwg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-riscv64-gnu@0.74.0':
|
||||
resolution: {integrity: sha512-/RZAP24TgZo4vV/01TBlzRqs0R7E6xvatww4LnmZEBBulQBU/SkypDywfriFqWuFoa61WFXPV7sLcTjJGjim/w==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-s390x-gnu@0.74.0':
|
||||
resolution: {integrity: sha512-620J1beNAlGSPBD+Msb3ptvrwxu04B8iULCH03zlf0JSLy/5sqlD6qBs0XUVkUJv1vbakUw1gfVnUQqv0UTuEg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-x64-gnu@0.74.0':
|
||||
resolution: {integrity: sha512-WBFgQmGtFnPNzHyLKbC1wkYGaRIBxXGofO0+hz1xrrkPgbxbJS1Ukva1EB8sPaVBBQ52Bdc2GjLSp721NWRvww==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-linux-x64-musl@0.74.0':
|
||||
resolution: {integrity: sha512-y4mapxi0RGqlp3t6Sm+knJlAEqdKDYrEue2LlXOka/F2i4sRN0XhEMPiSOB3ppHmvK4I2zY2XBYTsX1Fel0fAg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@oxc-parser/binding-wasm32-wasi@0.74.0':
|
||||
resolution: {integrity: sha512-yDS9bRDh5ymobiS2xBmjlrGdUuU61IZoJBaJC5fELdYT5LJNBXlbr3Yc6m2PWfRJwkH6Aq5fRvxAZ4wCbkGa8w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@oxc-parser/binding-win32-arm64-msvc@0.74.0':
|
||||
resolution: {integrity: sha512-XFWY52Rfb4N5wEbMCTSBMxRkDLGbAI9CBSL24BIDywwDJMl31gHEVlmHdCDRoXAmanCI6gwbXYTrWe0HvXJ7Aw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@oxc-parser/binding-win32-x64-msvc@0.74.0':
|
||||
resolution: {integrity: sha512-1D3x6iU2apLyfTQHygbdaNbX3nZaHu4yaXpD7ilYpoLo7f0MX0tUuoDrqJyJrVGqvyXgc0uz4yXz9tH9ZZhvvg==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@oxc-project/types@0.74.0':
|
||||
resolution: {integrity: sha512-KOw/RZrVlHGhCXh1RufBFF7Nuo7HdY5w1lRJukM/igIl6x9qtz8QycDvZdzb4qnHO7znrPyo2sJrFJK2eKHgfQ==}
|
||||
|
||||
'@oxc-resolver/binding-android-arm-eabi@11.6.1':
|
||||
resolution: {integrity: sha512-Ma/kg29QJX1Jzelv0Q/j2iFuUad1WnjgPjpThvjqPjpOyLjCUaiFCCnshhmWjyS51Ki1Iol3fjf1qAzObf8GIA==}
|
||||
cpu: [arm]
|
||||
@@ -2579,10 +2436,6 @@ packages:
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@prettier/plugin-oxc@0.0.4':
|
||||
resolution: {integrity: sha512-UGXe+g/rSRbglL0FOJiar+a+nUrst7KaFmsg05wYbKiInGWP6eAj/f8A2Uobgo5KxEtb2X10zeflNH6RK2xeIQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@primeuix/forms@0.0.2':
|
||||
resolution: {integrity: sha512-DpecPQd/Qf/kav4LKCaIeGuT3AkwhJzuHCkLANTVlN/zBvo8KIj3OZHsCkm0zlIMVVnaJdtx1ULNlRQdudef+A==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
@@ -3143,9 +2996,6 @@ packages:
|
||||
'@types/chai@5.2.2':
|
||||
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
|
||||
|
||||
'@types/css-font-loading-module@0.0.7':
|
||||
resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
@@ -3644,9 +3494,6 @@ packages:
|
||||
'@webgpu/types@0.1.51':
|
||||
resolution: {integrity: sha512-ktR3u64NPjwIViNCck+z9QeyN0iPkQCUOQ07ZCV1RzlkfP+olLTeEZ95O1QHS+v4w9vJeY9xj/uJuSphsHy5rQ==}
|
||||
|
||||
'@xstate/fsm@1.6.5':
|
||||
resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==}
|
||||
|
||||
'@xterm/addon-fit@0.10.0':
|
||||
resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==}
|
||||
peerDependencies:
|
||||
@@ -3929,10 +3776,6 @@ packages:
|
||||
balanced-match@2.0.0:
|
||||
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
@@ -5043,6 +4886,9 @@ packages:
|
||||
get-tsconfig@4.10.1:
|
||||
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
||||
|
||||
get-tsconfig@4.7.5:
|
||||
resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -6120,9 +5966,6 @@ packages:
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mixpanel-browser@2.71.0:
|
||||
resolution: {integrity: sha512-jKmDXe68/oQFgk/9ns9Z36bA0CJ31PH8Y77XTLLGfJvhsUPbvu+7Se9e281NejZF6+OMqx7cE+zFxToozYyNrA==}
|
||||
|
||||
mkdirp@3.0.1:
|
||||
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -6150,6 +5993,11 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@3.3.8:
|
||||
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@5.1.5:
|
||||
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
|
||||
engines: {node: ^18 || >=20}
|
||||
@@ -6309,10 +6157,6 @@ packages:
|
||||
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
oxc-parser@0.74.0:
|
||||
resolution: {integrity: sha512-2tDN/ttU8WE6oFh8EzKNam7KE7ZXSG5uXmvX85iNzxdJfMssDWcj3gpYzZi1E04XuE7m3v1dVWl/8BE886vPGw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
oxc-resolver@11.6.1:
|
||||
resolution: {integrity: sha512-WQgmxevT4cM5MZ9ioQnEwJiHpPzbvntV5nInGAKo9NQZzegcOonHvcVcnkYqld7bTG35UFHEKeF7VwwsmA3cZg==}
|
||||
|
||||
@@ -6501,6 +6345,10 @@ packages:
|
||||
postcss-value-parser@4.2.0:
|
||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
||||
|
||||
postcss@8.5.1:
|
||||
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -6513,15 +6361,11 @@ packages:
|
||||
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
prettier@3.6.2:
|
||||
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
|
||||
prettier@3.3.2:
|
||||
resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
pretty-bytes@7.1.0:
|
||||
resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
@@ -6824,19 +6668,6 @@ packages:
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
rollup-plugin-visualizer@6.0.4:
|
||||
resolution: {integrity: sha512-q8Q7J/6YofkmaGW1sH/fPRAz37x/+pd7VBuaUU7lwvOS/YikuiiEU9jeb9PH8XHiq50XFrUsBbOxeAMYQ7KZkg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
rolldown: 1.x || ^1.0.0-beta
|
||||
rollup: 2.x || 3.x || 4.x
|
||||
peerDependenciesMeta:
|
||||
rolldown:
|
||||
optional: true
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
rollup@4.22.4:
|
||||
resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@@ -6991,10 +6822,6 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.7.6:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
speakingurl@14.0.1:
|
||||
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -7623,6 +7450,9 @@ packages:
|
||||
vue-component-type-helpers@2.2.12:
|
||||
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
|
||||
|
||||
vue-component-type-helpers@3.1.0:
|
||||
resolution: {integrity: sha512-cC1pYNRZkSS1iCvdlaMbbg2sjDwxX098FucEjtz9Yig73zYjWzQsnMe5M9H8dRNv55hAIDGUI29hF2BEUA4FMQ==}
|
||||
|
||||
vue-component-type-helpers@3.1.1:
|
||||
resolution: {integrity: sha512-B0kHv7qX6E7+kdc5nsaqjdGZ1KwNKSUQDWGy7XkTYT7wFsOpkEyaJ1Vq79TjwrrtuLRgizrTV7PPuC4rRQo+vw==}
|
||||
|
||||
@@ -9639,29 +9469,6 @@ snapshots:
|
||||
|
||||
'@microsoft/tsdoc@0.15.1': {}
|
||||
|
||||
'@mixpanel/rrdom@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
'@mixpanel/rrweb-snapshot': 2.0.0-alpha.18.2
|
||||
|
||||
'@mixpanel/rrweb-snapshot@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
|
||||
'@mixpanel/rrweb-types@2.0.0-alpha.18.2': {}
|
||||
|
||||
'@mixpanel/rrweb-utils@2.0.0-alpha.18.2': {}
|
||||
|
||||
'@mixpanel/rrweb@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
'@mixpanel/rrdom': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-snapshot': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-types': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-utils': 2.0.0-alpha.18.2
|
||||
'@types/css-font-loading-module': 0.0.7
|
||||
'@xstate/fsm': 1.6.5
|
||||
base64-arraybuffer: 1.0.2
|
||||
mitt: 3.0.1
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.4.5
|
||||
@@ -9838,7 +9645,7 @@ snapshots:
|
||||
- typescript
|
||||
- verdaccio
|
||||
|
||||
'@nx/storybook@21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)':
|
||||
'@nx/storybook@21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@nx/cypress': 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(typescript@5.9.2)
|
||||
'@nx/devkit': 21.4.1(nx@21.4.1)
|
||||
@@ -9846,7 +9653,7 @@ snapshots:
|
||||
'@nx/js': 21.4.1(@babel/traverse@7.28.3)(nx@21.4.1)
|
||||
'@phenomnomnominal/tsquery': 5.0.1(typescript@5.9.2)
|
||||
semver: 7.7.2
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- '@babel/traverse'
|
||||
@@ -9902,55 +9709,6 @@ snapshots:
|
||||
|
||||
'@one-ini/wasm@0.1.1': {}
|
||||
|
||||
'@oxc-parser/binding-android-arm64@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-darwin-arm64@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-darwin-x64@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-freebsd-x64@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-arm-gnueabihf@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-arm-musleabihf@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-arm64-gnu@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-arm64-musl@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-riscv64-gnu@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-s390x-gnu@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-x64-gnu@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-linux-x64-musl@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-wasm32-wasi@0.74.0':
|
||||
dependencies:
|
||||
'@napi-rs/wasm-runtime': 0.2.12
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-win32-arm64-msvc@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-parser/binding-win32-x64-msvc@0.74.0':
|
||||
optional: true
|
||||
|
||||
'@oxc-project/types@0.74.0': {}
|
||||
|
||||
'@oxc-resolver/binding-android-arm-eabi@11.6.1':
|
||||
optional: true
|
||||
|
||||
@@ -10046,10 +9804,6 @@ snapshots:
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@prettier/plugin-oxc@0.0.4':
|
||||
dependencies:
|
||||
oxc-parser: 0.74.0
|
||||
|
||||
'@primeuix/forms@0.0.2':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.3.2
|
||||
@@ -10250,29 +10004,29 @@ snapshots:
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
||||
'@storybook/addon-docs@9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/addon-docs@9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
'@mdx-js/react': 3.1.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@storybook/react-dom-shim': 9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/react-dom-shim': 9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
ts-dedent: 2.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
'@storybook/builder-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))':
|
||||
'@storybook/builder-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))':
|
||||
dependencies:
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
ts-dedent: 2.2.0
|
||||
vite: 5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
|
||||
'@storybook/csf-plugin@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/csf-plugin@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
unplugin: 1.16.1
|
||||
|
||||
'@storybook/global@5.0.0': {}
|
||||
@@ -10282,19 +10036,19 @@ snapshots:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
'@storybook/react-dom-shim@9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/react-dom-shim@9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
|
||||
'@storybook/vue3-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))':
|
||||
'@storybook/vue3-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))':
|
||||
dependencies:
|
||||
'@storybook/builder-vite': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/vue3': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
'@storybook/builder-vite': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/vue3': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
find-package-json: 1.2.0
|
||||
magic-string: 0.30.19
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
typescript: 5.9.2
|
||||
vite: 5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
vue-component-meta: 2.2.12(typescript@5.9.2)
|
||||
@@ -10302,10 +10056,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@storybook/vue3@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))':
|
||||
'@storybook/vue3@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))':
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
type-fest: 2.19.0
|
||||
vue: 3.5.13(typescript@5.9.2)
|
||||
vue-component-type-helpers: 3.1.1
|
||||
@@ -10570,7 +10324,7 @@ snapshots:
|
||||
'@tiptap/extension-text-style': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))
|
||||
'@tiptap/pm': 2.10.4
|
||||
|
||||
'@trivago/prettier-plugin-sort-imports@5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.6.2)':
|
||||
'@trivago/prettier-plugin-sort-imports@5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.3.2)':
|
||||
dependencies:
|
||||
'@babel/generator': 7.28.3
|
||||
'@babel/parser': 7.28.4
|
||||
@@ -10578,7 +10332,7 @@ snapshots:
|
||||
'@babel/types': 7.28.4
|
||||
javascript-natural-sort: 0.7.1
|
||||
lodash: 4.17.21
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
optionalDependencies:
|
||||
'@vue/compiler-sfc': 3.5.13
|
||||
transitivePeerDependencies:
|
||||
@@ -10603,8 +10357,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
|
||||
'@types/css-font-loading-module@0.0.7': {}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
@@ -11198,8 +10950,6 @@ snapshots:
|
||||
|
||||
'@webgpu/types@0.1.51': {}
|
||||
|
||||
'@xstate/fsm@1.6.5': {}
|
||||
|
||||
'@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)':
|
||||
dependencies:
|
||||
'@xterm/xterm': 5.5.0
|
||||
@@ -11516,8 +11266,6 @@ snapshots:
|
||||
|
||||
balanced-match@2.0.0: {}
|
||||
|
||||
base64-arraybuffer@1.0.2: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
better-opn@3.0.2:
|
||||
@@ -12362,20 +12110,20 @@ snapshots:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.6.2):
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.3.2):
|
||||
dependencies:
|
||||
eslint: 9.35.0(jiti@2.4.2)
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
prettier-linter-helpers: 1.0.0
|
||||
synckit: 0.11.11
|
||||
optionalDependencies:
|
||||
eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.4.2))
|
||||
|
||||
eslint-plugin-storybook@9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2):
|
||||
eslint-plugin-storybook@9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.44.0(eslint@9.35.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
eslint: 9.35.0(jiti@2.4.2)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@@ -12814,6 +12562,10 @@ snapshots:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
get-tsconfig@4.7.5:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
@@ -14086,10 +13838,6 @@ snapshots:
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mixpanel-browser@2.71.0:
|
||||
dependencies:
|
||||
'@mixpanel/rrweb': 2.0.0-alpha.18.2
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mlly@1.8.0:
|
||||
@@ -14112,6 +13860,8 @@ snapshots:
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
nanoid@3.3.8: {}
|
||||
|
||||
nanoid@5.1.5: {}
|
||||
|
||||
napi-postinstall@0.3.3: {}
|
||||
@@ -14332,26 +14082,6 @@ snapshots:
|
||||
safe-push-apply: 1.0.0
|
||||
optional: true
|
||||
|
||||
oxc-parser@0.74.0:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.74.0
|
||||
optionalDependencies:
|
||||
'@oxc-parser/binding-android-arm64': 0.74.0
|
||||
'@oxc-parser/binding-darwin-arm64': 0.74.0
|
||||
'@oxc-parser/binding-darwin-x64': 0.74.0
|
||||
'@oxc-parser/binding-freebsd-x64': 0.74.0
|
||||
'@oxc-parser/binding-linux-arm-gnueabihf': 0.74.0
|
||||
'@oxc-parser/binding-linux-arm-musleabihf': 0.74.0
|
||||
'@oxc-parser/binding-linux-arm64-gnu': 0.74.0
|
||||
'@oxc-parser/binding-linux-arm64-musl': 0.74.0
|
||||
'@oxc-parser/binding-linux-riscv64-gnu': 0.74.0
|
||||
'@oxc-parser/binding-linux-s390x-gnu': 0.74.0
|
||||
'@oxc-parser/binding-linux-x64-gnu': 0.74.0
|
||||
'@oxc-parser/binding-linux-x64-musl': 0.74.0
|
||||
'@oxc-parser/binding-wasm32-wasi': 0.74.0
|
||||
'@oxc-parser/binding-win32-arm64-msvc': 0.74.0
|
||||
'@oxc-parser/binding-win32-x64-msvc': 0.74.0
|
||||
|
||||
oxc-resolver@11.6.1:
|
||||
dependencies:
|
||||
napi-postinstall: 0.3.3
|
||||
@@ -14511,14 +14241,14 @@ snapshots:
|
||||
dependencies:
|
||||
htmlparser2: 8.0.2
|
||||
js-tokens: 9.0.1
|
||||
postcss: 8.5.6
|
||||
postcss-safe-parser: 6.0.0(postcss@8.5.6)
|
||||
postcss: 8.5.1
|
||||
postcss-safe-parser: 6.0.0(postcss@8.5.1)
|
||||
|
||||
postcss-resolve-nested-selector@0.1.6: {}
|
||||
|
||||
postcss-safe-parser@6.0.0(postcss@8.5.6):
|
||||
postcss-safe-parser@6.0.0(postcss@8.5.1):
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
postcss: 8.5.1
|
||||
|
||||
postcss-safe-parser@7.0.1(postcss@8.5.6):
|
||||
dependencies:
|
||||
@@ -14536,6 +14266,12 @@ snapshots:
|
||||
|
||||
postcss-value-parser@4.2.0: {}
|
||||
|
||||
postcss@8.5.1:
|
||||
dependencies:
|
||||
nanoid: 3.3.8
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
@@ -14548,9 +14284,7 @@ snapshots:
|
||||
dependencies:
|
||||
fast-diff: 1.3.0
|
||||
|
||||
prettier@3.6.2: {}
|
||||
|
||||
pretty-bytes@7.1.0: {}
|
||||
prettier@3.3.2: {}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
dependencies:
|
||||
@@ -14999,15 +14733,6 @@ snapshots:
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
rollup-plugin-visualizer@6.0.4(rollup@4.22.4):
|
||||
dependencies:
|
||||
open: 8.4.2
|
||||
picomatch: 4.0.3
|
||||
source-map: 0.7.6
|
||||
yargs: 17.7.2
|
||||
optionalDependencies:
|
||||
rollup: 4.22.4
|
||||
|
||||
rollup@4.22.4:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
@@ -15199,8 +14924,6 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
speakingurl@14.0.1: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
@@ -15227,7 +14950,7 @@ snapshots:
|
||||
internal-slot: 1.1.0
|
||||
optional: true
|
||||
|
||||
storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)):
|
||||
storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)):
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
'@testing-library/jest-dom': 6.6.4
|
||||
@@ -15242,7 +14965,7 @@ snapshots:
|
||||
semver: 7.7.2
|
||||
ws: 8.18.3
|
||||
optionalDependencies:
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
transitivePeerDependencies:
|
||||
- '@testing-library/dom'
|
||||
- bufferutil
|
||||
@@ -15564,7 +15287,7 @@ snapshots:
|
||||
tsx@4.19.4:
|
||||
dependencies:
|
||||
esbuild: 0.25.5
|
||||
get-tsconfig: 4.10.1
|
||||
get-tsconfig: 4.7.5
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
@@ -15916,7 +15639,7 @@ snapshots:
|
||||
vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.6
|
||||
postcss: 8.5.1
|
||||
rollup: 4.22.4
|
||||
optionalDependencies:
|
||||
'@types/node': 20.14.10
|
||||
@@ -15981,6 +15704,8 @@ snapshots:
|
||||
|
||||
vue-component-type-helpers@2.2.12: {}
|
||||
|
||||
vue-component-type-helpers@3.1.0: {}
|
||||
|
||||
vue-component-type-helpers@3.1.1: {}
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.13(typescript@5.9.2)):
|
||||
|
||||
@@ -16,7 +16,6 @@ catalog:
|
||||
'@nx/vite': 21.4.1
|
||||
'@pinia/testing': ^0.1.5
|
||||
'@playwright/test': ^1.52.0
|
||||
'@prettier/plugin-oxc': ^0.0.4
|
||||
'@primeuix/forms': 0.0.2
|
||||
'@primeuix/styled': 0.3.2
|
||||
'@primeuix/utils': ^0.3.2
|
||||
@@ -63,16 +62,12 @@ catalog:
|
||||
jsdom: ^26.1.0
|
||||
knip: ^5.62.0
|
||||
lint-staged: ^15.2.7
|
||||
markdown-table: ^3.0.4
|
||||
nx: 21.4.1
|
||||
picocolors: ^1.1.1
|
||||
pinia: ^2.1.7
|
||||
postcss-html: ^1.8.0
|
||||
prettier: ^3.6.2
|
||||
pretty-bytes: ^7.1.0
|
||||
prettier: ^3.3.2
|
||||
primeicons: ^7.0.0
|
||||
primevue: ^4.2.5
|
||||
rollup-plugin-visualizer: ^6.0.4
|
||||
storybook: ^9.1.6
|
||||
stylelint: ^16.24.0
|
||||
tailwindcss: ^4.1.12
|
||||
@@ -99,10 +94,12 @@ catalog:
|
||||
zod: ^3.23.8
|
||||
zod-to-json-schema: ^3.24.1
|
||||
zod-validation-error: ^3.3.0
|
||||
mixpanel-browser: ^2.71.0
|
||||
|
||||
cleanupUnusedCatalogs: true
|
||||
|
||||
overrides:
|
||||
'@types/eslint': '-'
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- '@firebase/util'
|
||||
- protobufjs
|
||||
@@ -117,6 +114,3 @@ onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- nx
|
||||
- oxc-resolver
|
||||
|
||||
overrides:
|
||||
'@types/eslint': '-'
|
||||
|
||||