diff --git a/.claude/skills/add-model-page/SKILL.md b/.claude/skills/add-model-page/SKILL.md new file mode 100644 index 0000000000..0b7fe4b81e --- /dev/null +++ b/.claude/skills/add-model-page/SKILL.md @@ -0,0 +1,173 @@ +--- +name: add-model-page +description: 'add, update, or remove a model page entry on the comfy org website. creates a PR to Comfy-Org/ComfyUI_frontend apps/website folder with the change and posts a Vercel preview link back to Slack.' +--- + +# add-model-page + +add, update, or remove model pages in the ComfyUI website. + +## Trigger phrases + +- `Add a model page for ` +- `Update the model page for ` +- `Remove from model pages` + +## Phase 1 — Parse the request + +Extract: + +- **action**: `add` | `update` | `remove` +- **model-name**: raw string (e.g. `flux1-schnell`, `flux1_dev.safetensors`) + +Normalize to a slug: lowercase, replace `_` and `.` with `-`, strip file extensions. +Example: `flux1_dev.safetensors` → `flux1-dev` + +## Architecture overview + +Models come from two sources merged at build time: + +| File | Purpose | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `apps/website/src/config/generated-models.json` | Auto-generated from workflow_templates (slug, name, directory, huggingFaceUrl, workflowCount, displayName, thumbnailUrl, docsUrl) | +| `apps/website/src/config/model-metadata.ts` | Hand-curated overrides (docsUrl, blogUrl, featured) — only add entries that need overrides | +| `apps/website/src/config/models.ts` | Merges the two above; exports typed `Model[]` | + +To regenerate the JSON from workflow_templates: + +```bash +pnpm tsx apps/website/scripts/generate-models.ts +``` + +This writes `apps/website/src/config/generated-models.json` directly. +Thumbnails are populated from local `.webp` files in `workflow_templates/templates/` — no network access needed. + +--- + +## Phase 2 — Gather model data (ADD / UPDATE) + +Run the generator to get fresh data, then find the model: + +```bash +pnpm tsx apps/website/scripts/generate-models.ts +jq '.[] | select(.slug | contains("MODEL_SLUG"))' \ + apps/website/src/config/generated-models.json +``` + +The JSON fields are: + +- `slug` — URL slug +- `name` — exact filename or display name for partner nodes +- `huggingFaceUrl` — download URL (empty for partner nodes) +- `directory` — `diffusion_models` | `loras` | … | `partner_nodes` +- `workflowCount` — integer +- `displayName` — human-readable name + +If no match and it is a known API/partner model, add it to `API_PROVIDER_MAP` in +`generate-models.ts` and re-run. Otherwise tell the user. + +--- + +## Phase 3 — Check for existing entry + +```bash +jq --arg slug "${SLUG}" '.[] | select(.slug == $slug)' \ + apps/website/src/config/generated-models.json +``` + +- Match found + action is `add` → switch to UPDATE flow automatically +- No match + action is `update` → stop and tell the user + +--- + +## Phase 4A — ADD: new partner/API model not in workflow_templates + +For partner nodes (no local file), add an entry to `API_PROVIDER_MAP` in +`apps/website/scripts/generate-models.ts`: + +```typescript +mymodel: { name: 'My Model', slug: 'my-model' }, +``` + +Then re-run `pnpm tsx apps/website/scripts/generate-models.ts` — it will appear +in `generated-models.json` automatically. + +If you also want a `docsUrl`, `blogUrl`, or a link to the hub model page, add an entry to `model-metadata.ts`: + +```typescript +'my-model': { + docsUrl: 'https://docs.comfy.org/tutorials/...', + blogUrl: 'https://blog.comfy.org/...', + hubSlug: 'my-model', // slug at comfy.org/workflows/model/{hubSlug} — only set if the page exists (returns 200) + featured: true +} +``` + +No changes to `models.ts` or `translations.ts` are needed. + +--- + +## Phase 4B — UPDATE: edit existing entry + +Only `model-metadata.ts` needs editing for most updates (docsUrl, blogUrl, +featured). For `displayName` or `directory` changes, edit the entry directly in +`generated-models.json` (until the next generator run would overwrite it — then +fix the source in `generate-models.ts`). + +--- + +## Phase 4C — REMOVE: delete entry + +Remove the entry from `generated-models.json` (or mark it with `canonicalSlug` +pointing to the replacement). No translation file changes needed. + +--- + +## Phase 5 — Verify TypeScript + +```bash +pnpm typecheck 2>&1 | grep -E "error|warning" | head -20 +``` + +Fix any type errors before proceeding. Common issues: + +- `ModelDirectory` type not matching a new `directory` value — add it to the union +- JSON import shape mismatch — `generated-models.json` must match `OutputModel` + +--- + +## Phase 6 — Create PR + +```bash +BRANCH="add-model-page-MODEL-SLUG" # or update- / remove- +git checkout -b $BRANCH +git add apps/website/src/config/generated-models.json \ + apps/website/scripts/generate-models.ts \ + apps/website/src/config/model-metadata.ts +git commit -m "feat(models): add model page for MODEL-SLUG" +git push -u origin $BRANCH +gh pr create \ + --title "Add model page: MODEL-SLUG" \ + --body "$(cat <<'EOF' +Adds a new model page entry for MODEL-SLUG. + +## Changes +- `generated-models.json`: regenerated with new entry (workflowCount N, directory DIRECTORY) +- `model-metadata.ts`: editorial overrides (docsUrl, featured) if needed +EOF +)" +``` + +For UPDATE use branch `update-model-page-MODEL-SLUG`. +For REMOVE use `remove-model-page-MODEL-SLUG`. + +--- + +## Error states + +| Situation | Response | +| ------------------------------- | ---------------------------------------------------------------- | +| Model not in workflow templates | Ask user to verify spelling or add it manually as a partner node | +| Slug already exists (add) | Switch to update flow automatically | +| Slug not found (update/remove) | Stop and ask user to confirm | +| Typecheck fails | Fix the error before pushing | diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 20af82bec6..35c5d31183 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -19,15 +19,26 @@ reviews: - name: End-to-end regression coverage for fixes mode: error instructions: | - Use only PR metadata already available in the review context: the PR title, commit subjects in this PR, the files changed in this PR relative to the PR base (equivalent to `base...head`), and the PR description. - Do not rely on shell commands. Do not inspect reverse diffs, files changed only on the base branch, or files outside this PR. If the changed-file list or commit subjects are unavailable, mark the check inconclusive instead of guessing. + Use only PR metadata already available in the review context: + - the PR title + - commit subjects in this PR + - The files changed in this PR relative to the PR base (equivalent to `base...head`) + - the PR description. + Do not rely on shell commands. + Do not inspect reverse diffs, files changed only on the base branch, or files outside this PR. + If the changed-file list or commit subjects are unavailable, mark the check inconclusive instead of guessing. - Pass if at least one of the following is true: - 1. Neither the PR title nor any commit subject in the PR uses bug-fix language such as `fix`, `fixed`, `fixes`, `fixing`, `bugfix`, or `hotfix`. - 2. The PR changes at least one file under `browser_tests/`. - 3. The PR description includes a concrete, non-placeholder explanation of why an end-to-end regression test was not added. + Fail if all of the following are true: + 1. The PR title and/or any commit subject in the PR uses bug-fix language such as `fix`, `fixed`, `fixes`, `fixing`, `bugfix`, or `hotfix`. + 2. The PR changes files under `src/` or `packages/` related to the main frontend application but the PR does not change at least one file under `browser_tests/`. + 3. The PR description lacks a concrete explanation of why an end-to-end regression test was not added. + + Do not fail if the changes are exclusively in `apps/website`, just documentation changes, or changes related to CI processes. + The goal is to make sure that fixes include End-to-End regression tests. Do not insist on tests when the PR is not fixing a bug. + + Pass otherwise. + When failing, mention which bug-fix signal you found and ask the author to either add or update a Playwright regression test under `browser_tests/` or add a concrete explanation in the PR description of why an end-to-end regression test is not practical. - Fail otherwise. When failing, mention which bug-fix signal you found and ask the author to either add or update a Playwright regression test under `browser_tests/` or add a concrete explanation in the PR description of why an end-to-end regression test is not practical. - name: ADR compliance for entity/litegraph changes mode: warning instructions: | diff --git a/.github/actions/cloud-nodes-pull/action.yaml b/.github/actions/cloud-nodes-pull/action.yaml new file mode 100644 index 0000000000..8c4dfd2702 --- /dev/null +++ b/.github/actions/cloud-nodes-pull/action.yaml @@ -0,0 +1,19 @@ +name: Cloud Nodes Pull +description: 'Refresh the apps/website cloud nodes snapshot from the Comfy Cloud /api/object_info endpoint' +inputs: + api_key: + description: 'Comfy Cloud API key (WEBSITE_CLOUD_API_KEY).' + required: true +runs: + using: 'composite' + steps: + # Note: this action assumes the frontend repo is checked out at the workspace root. + + - name: Setup frontend + uses: ./.github/actions/setup-frontend + + - name: Refresh cloud nodes snapshot + shell: bash + env: + WEBSITE_CLOUD_API_KEY: ${{ inputs.api_key }} + run: pnpm --filter @comfyorg/website cloud-nodes:refresh-snapshot diff --git a/.github/workflows/ci-perf-report.yaml b/.github/workflows/ci-perf-report.yaml index bd6c286f37..94dad8b916 100644 --- a/.github/workflows/ci-perf-report.yaml +++ b/.github/workflows/ci-perf-report.yaml @@ -54,10 +54,14 @@ jobs: - name: Start ComfyUI server uses: ./.github/actions/start-comfyui-server + # PRs run each test once to keep wall time bounded; main runs 3× so the + # baseline saved to perf-data has enough samples to median over noise. - name: Run performance tests id: perf continue-on-error: true - run: pnpm exec playwright test --project=performance --workers=1 --repeat-each=3 + env: + PERF_REPEAT: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && '3' || '2' }} + run: pnpm exec playwright test --project=performance --workers=1 --repeat-each=$PERF_REPEAT - name: Upload perf metrics if: always() diff --git a/.github/workflows/ci-tests-e2e-coverage.yaml b/.github/workflows/ci-tests-e2e-coverage.yaml index 2944bf0c82..246b551917 100644 --- a/.github/workflows/ci-tests-e2e-coverage.yaml +++ b/.github/workflows/ci-tests-e2e-coverage.yaml @@ -20,6 +20,8 @@ jobs: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest timeout-minutes: 10 + outputs: + has-coverage: ${{ steps.coverage-shards.outputs.has-coverage }} steps: - name: Checkout repository @@ -37,31 +39,33 @@ jobs: path: temp/coverage-shards if_no_artifact_found: warn + - name: Detect shard coverage data + id: coverage-shards + run: | + if [ -d temp/coverage-shards ] && find temp/coverage-shards -name 'coverage.lcov' -type f | grep -q .; then + echo "has-coverage=true" >> "$GITHUB_OUTPUT" + else + echo "has-coverage=false" >> "$GITHUB_OUTPUT" + echo "No E2E coverage shard artifacts found; treating this run as skipped." >> "$GITHUB_STEP_SUMMARY" + fi + - name: Install lcov + if: steps.coverage-shards.outputs.has-coverage == 'true' run: sudo apt-get install -y -qq lcov - name: Merge shard coverage into single LCOV + if: steps.coverage-shards.outputs.has-coverage == 'true' run: | mkdir -p coverage/playwright LCOV_FILES=$(find temp/coverage-shards -name 'coverage.lcov' -type f) - if [ -z "$LCOV_FILES" ]; then - echo "No coverage.lcov files found" - touch coverage/playwright/coverage.lcov - exit 0 - fi ADD_ARGS="" for f in $LCOV_FILES; do ADD_ARGS="$ADD_ARGS -a $f"; done lcov $ADD_ARGS -o coverage/playwright/coverage.lcov wc -l coverage/playwright/coverage.lcov - name: Validate merged coverage + if: steps.coverage-shards.outputs.has-coverage == 'true' run: | - SHARD_COUNT=$(find temp/coverage-shards -name 'coverage.lcov' -type f | wc -l | tr -d ' ') - if [ "$SHARD_COUNT" -eq 0 ]; then - echo "::notice::No shard coverage files; upstream E2E was likely skipped." - exit 0 - fi - MERGED_SF=$(grep -c '^SF:' coverage/playwright/coverage.lcov || echo 0) MERGED_LH=$(awk -F: '/^LH:/{s+=$2}END{print s+0}' coverage/playwright/coverage.lcov) MERGED_LF=$(awk -F: '/^LF:/{s+=$2}END{print s+0}' coverage/playwright/coverage.lcov) @@ -82,7 +86,7 @@ jobs: done - name: Upload merged coverage data - if: always() + if: steps.coverage-shards.outputs.has-coverage == 'true' uses: actions/upload-artifact@v6 with: name: e2e-coverage @@ -91,7 +95,7 @@ jobs: if-no-files-found: warn - name: Upload E2E coverage to Codecov - if: always() + if: steps.coverage-shards.outputs.has-coverage == 'true' uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3 with: files: coverage/playwright/coverage.lcov @@ -100,20 +104,17 @@ jobs: fail_ci_if_error: false - name: Generate HTML coverage report + if: steps.coverage-shards.outputs.has-coverage == 'true' run: | - if [ ! -s coverage/playwright/coverage.lcov ]; then - echo "No coverage data; generating placeholder report." - mkdir -p coverage/html - echo '

No E2E coverage data available for this run.

' > coverage/html/index.html - exit 0 - fi genhtml coverage/playwright/coverage.lcov \ -o coverage/html \ --title "ComfyUI E2E Coverage" \ --no-function-coverage \ - --precision 1 + --precision 1 \ + --ignore-errors source,unmapped - name: Upload HTML report artifact + if: steps.coverage-shards.outputs.has-coverage == 'true' uses: actions/upload-artifact@v6 with: name: e2e-coverage-html @@ -122,7 +123,10 @@ jobs: deploy: needs: merge - if: github.event.workflow_run.head_branch == 'main' + if: > + github.event.workflow_run.head_branch == 'main' && + needs.merge.outputs.has-coverage == 'true' && + github.event.workflow_run.event == 'push' runs-on: ubuntu-latest permissions: pages: write diff --git a/.github/workflows/ci-vercel-website-preview.yaml b/.github/workflows/ci-vercel-website-preview.yaml index 3588cfc2bf..7e298abb81 100644 --- a/.github/workflows/ci-vercel-website-preview.yaml +++ b/.github/workflows/ci-vercel-website-preview.yaml @@ -4,6 +4,9 @@ name: 'CI: Vercel Website Preview' on: pull_request: types: [opened, synchronize, reopened] + branches-ignore: + - 'core/**' + - 'cloud/**' paths: - 'apps/website/**' - 'packages/design-system/**' @@ -55,6 +58,7 @@ jobs: env: WEBSITE_ASHBY_API_KEY: ${{ secrets.WEBSITE_ASHBY_API_KEY }} WEBSITE_ASHBY_JOB_BOARD_NAME: ${{ secrets.WEBSITE_ASHBY_JOB_BOARD_NAME }} + WEBSITE_CLOUD_API_KEY: ${{ secrets.WEBSITE_CLOUD_API_KEY }} run: vercel build - name: Fetch head commit metadata @@ -148,10 +152,20 @@ jobs: - name: Pull Vercel environment information run: vercel pull --yes --environment=production + - name: Verify WEBSITE_CLOUD_API_KEY is present for production build + env: + WEBSITE_CLOUD_API_KEY: ${{ secrets.WEBSITE_CLOUD_API_KEY }} + run: | + if [ -z "${WEBSITE_CLOUD_API_KEY:-}" ]; then + echo "::error title=Missing WEBSITE_CLOUD_API_KEY::Production builds require WEBSITE_CLOUD_API_KEY so /cloud/supported-nodes is generated from fresh Cloud API data. Add it as a GitHub Actions repo secret and to the Vercel project environment. See apps/website/README.md." + exit 1 + fi + - name: Build project artifacts env: WEBSITE_ASHBY_API_KEY: ${{ secrets.WEBSITE_ASHBY_API_KEY }} WEBSITE_ASHBY_JOB_BOARD_NAME: ${{ secrets.WEBSITE_ASHBY_JOB_BOARD_NAME }} + WEBSITE_CLOUD_API_KEY: ${{ secrets.WEBSITE_CLOUD_API_KEY }} run: vercel build --prod - name: Deploy project artifacts to Vercel diff --git a/.github/workflows/model-page-discovery.yaml b/.github/workflows/model-page-discovery.yaml new file mode 100644 index 0000000000..457c7546aa --- /dev/null +++ b/.github/workflows/model-page-discovery.yaml @@ -0,0 +1,123 @@ +name: Model Page Discovery + +on: + schedule: + - cron: '0 9 * * 1' + workflow_dispatch: + +jobs: + discover: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + + steps: + - name: Fetch model labels from hub API + id: hub + shell: bash + run: | + set -euo pipefail + curl -fsSL 'https://comfy.org/api/hub/labels?type=model' -o hub-labels.json + echo "Fetched $(jq '.labels | length' hub-labels.json) model labels from hub" + + - name: Checkout ComfyUI_frontend + uses: actions/checkout@v6 + with: + sparse-checkout: apps/website/src/config/generated-models.json + + - name: Compare against existing models + id: compare + shell: bash + run: | + set -euo pipefail + + HUB_SLUGS=$(jq -r '[.labels[].name]' hub-labels.json) + + EXISTING_SLUGS=$(node -e " + const fs = require('fs'); + const models = JSON.parse( + fs.readFileSync( + 'apps/website/src/config/generated-models.json', + 'utf8' + ) + ); + console.log(JSON.stringify(models.map(m => m.slug))); + " 2>/dev/null || echo '[]') + + ADDED_SLUGS=$(node -e " + const hub = $HUB_SLUGS; + const existing = new Set($EXISTING_SLUGS); + console.log(JSON.stringify(hub.filter(s => !existing.has(s)))); + ") + + COUNT=$(node -e "console.log($ADDED_SLUGS.length)") + echo "new_count=$COUNT" >> \$GITHUB_OUTPUT + echo "new_slugs=$ADDED_SLUGS" >> \$GITHUB_OUTPUT + + if [ "\$COUNT" -eq 0 ]; then + echo "No new models found." + else + echo "Found \$COUNT new model(s)" + fi + + - name: Check for existing open discovery issue + id: existing_issue + if: steps.compare.outputs.new_count != '0' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + COUNT=$(gh issue list \ + --repo "$GITHUB_REPOSITORY" \ + --state open \ + --search 'in:title "New models detected"' \ + --json number \ + --jq 'length') + echo "open_count=$COUNT" >> $GITHUB_OUTPUT + + - name: Open GitHub issue for new models + if: | + steps.compare.outputs.new_count != '0' && + steps.existing_issue.outputs.open_count == '0' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NEW_SLUGS: ${{ steps.compare.outputs.new_slugs }} + NEW_COUNT: ${{ steps.compare.outputs.new_count }} + shell: bash + run: | + SLUG_LIST=$(node -e " + const slugs = $NEW_SLUGS; + console.log(slugs.map(s => '- \`' + s + '\`').join('\n')); + ") + + gh issue create \ + --repo "$GITHUB_REPOSITORY" \ + --title "New models detected — add to model pages" \ + --body "## $NEW_COUNT new model(s) found in hub + + The weekly model discovery scan found model labels on the hub not yet in + \`apps/website/src/config/generated-models.json\`. + + ### New slugs ($NEW_COUNT) + + $SLUG_LIST + + ### Next steps + + 1. Review which of these warrant an SEO model page + 2. For local models: run \`SKIP_THUMBNAILS=1 pnpm generate:models\` and commit the result + 3. For partner/API models: add to \`API_PROVIDER_MAP\` in \`generate-models.ts\`, regenerate, commit + + --- + *Generated by the [model-page-discovery workflow](https://github.com/$GITHUB_REPOSITORY/actions/workflows/model-page-discovery.yaml)*" + + - name: Skip — open issue already exists + if: | + steps.compare.outputs.new_count != '0' && + steps.existing_issue.outputs.open_count != '0' + run: echo "An open discovery issue already exists — skipping creation." + + - name: No new models found + if: steps.compare.outputs.new_count == '0' + run: echo "No new models found — nothing to do." diff --git a/.github/workflows/release-website.yaml b/.github/workflows/release-website.yaml index 8ec080bddd..2814d75315 100644 --- a/.github/workflows/release-website.yaml +++ b/.github/workflows/release-website.yaml @@ -1,6 +1,6 @@ -# Description: Manual workflow to refresh the apps/website Ashby roles snapshot -# and open a PR. Merging the PR triggers the existing Vercel website production -# deploy via ci-vercel-website-preview.yaml. +# Description: Manual workflow to refresh the apps/website Ashby roles and +# cloud nodes snapshots and open a PR. Merging the PR triggers the existing +# Vercel website production deploy via ci-vercel-website-preview.yaml. name: 'Release: Website' on: @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: true jobs: - refresh-snapshot: + refresh-snapshots: if: github.repository == 'Comfy-Org/ComfyUI_frontend' runs-on: ubuntu-latest permissions: @@ -31,28 +31,39 @@ jobs: api_key: ${{ secrets.WEBSITE_ASHBY_API_KEY }} job_board_name: ${{ secrets.WEBSITE_ASHBY_JOB_BOARD_NAME }} + - name: Refresh cloud nodes snapshot + uses: ./.github/actions/cloud-nodes-pull + with: + api_key: ${{ secrets.WEBSITE_CLOUD_API_KEY }} + - name: Create Pull Request uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ secrets.PR_GH_TOKEN }} - commit-message: 'chore(website): refresh Ashby roles snapshot' - title: 'chore(website): refresh Ashby roles snapshot' + commit-message: 'chore(website): refresh Ashby and cloud nodes snapshots' + title: 'chore(website): refresh Ashby and cloud nodes snapshots' body: | - Automated refresh of `apps/website/src/data/ashby-roles.snapshot.json` - from the Ashby job board API. + Automated refresh of remote-data snapshots used by the website + build: + + - `apps/website/src/data/ashby-roles.snapshot.json` — Ashby job + board API + - `apps/website/src/data/cloud-nodes.snapshot.json` — Comfy Cloud + `/api/object_info` **Flow:** 1. `Release: Website` workflow ran (manual trigger). - 2. This PR opens with the regenerated snapshot. + 2. This PR opens with the regenerated snapshots. 3. `CI: Vercel Website Preview` deploys a preview for review. 4. Merging to `main` triggers the production Vercel deploy. - The snapshot fallback in `apps/website/src/utils/ashby.ts` remains - intact: builds without `WEBSITE_ASHBY_API_KEY` continue to use the - committed snapshot. + The snapshot fallback in `apps/website/src/utils/ashby.ts` and + `apps/website/src/utils/cloudNodes.ts` remains intact: builds + without the respective API keys continue to use the committed + snapshot (with a warning annotation in CI). Triggered by workflow run `${{ github.run_id }}`. - branch: chore/refresh-ashby-snapshot-${{ github.run_id }} + branch: chore/refresh-website-snapshots-${{ github.run_id }} base: main labels: | Release:Website diff --git a/.oxlintrc.json b/.oxlintrc.json index 19700f80c2..66142ab5ca 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -85,6 +85,15 @@ "typescript/no-unused-vars": "off", "unicorn/no-empty-file": "off", "vitest/require-mock-type-parameters": "off", + "vitest/consistent-each-for": [ + "error", + { + "test": "for", + "it": "for", + "describe": "for", + "suite": "for" + } + ], "unicorn/no-new-array": "off", "unicorn/no-single-promise-in-promise-methods": "off", "unicorn/no-useless-fallback-in-spread": "off", diff --git a/apps/desktop-ui/src/components/maintenance/TaskListItem.vue b/apps/desktop-ui/src/components/maintenance/TaskListItem.vue index 0762bbd057..6f893d0496 100644 --- a/apps/desktop-ui/src/components/maintenance/TaskListItem.vue +++ b/apps/desktop-ui/src/components/maintenance/TaskListItem.vue @@ -1,12 +1,12 @@