diff --git a/.github/workflows/tests-ci.yaml b/.github/workflows/tests-ci.yaml index 4f4cc5f9c..ac49e551e 100644 --- a/.github/workflows/tests-ci.yaml +++ b/.github/workflows/tests-ci.yaml @@ -89,7 +89,7 @@ jobs: run: sleep 10 - name: Restore cached setup - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 + uses: actions/cache/restore@v4 with: fail-on-cache-miss: true path: | @@ -155,7 +155,7 @@ jobs: run: sleep 10 - name: Restore cached setup - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 + uses: actions/cache/restore@v4 with: fail-on-cache-miss: true path: | diff --git a/.github/workflows/update-locales.yaml b/.github/workflows/update-locales.yaml index 3bf939b23..6ee093312 100644 --- a/.github/workflows/update-locales.yaml +++ b/.github/workflows/update-locales.yaml @@ -14,6 +14,9 @@ jobs: if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'version-bump-')) runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@v5 + - name: Setup Frontend uses: ./.github/actions/setup-frontend diff --git a/.github/workflows/update-playwright-expectations.yaml b/.github/workflows/update-playwright-expectations.yaml index df6d004f9..5d08a3be0 100644 --- a/.github/workflows/update-playwright-expectations.yaml +++ b/.github/workflows/update-playwright-expectations.yaml @@ -3,114 +3,165 @@ name: Update Playwright Expectations on: pull_request: - types: [ labeled ] + types: [labeled] + issue_comment: + types: [created] jobs: test: runs-on: ubuntu-latest - if: github.event.label.name == 'New Browser Test Expectations' + 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: Checkout workflow repo - uses: actions/checkout@v5 - - name: Setup Frontend - uses: ./.github/actions/setup-frontend - - 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 - const headSha = context.payload.pull_request.head.sha + - name: Initial Checkout + uses: actions/checkout@v5 - 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] + - name: Pull Request Checkout + if: github.event.issue.pull_request && github.event_name == 'issue_comment' + run: gh pr checkout ${{ github.event.issue.number }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - let has = 'false' - let runId = '' - if (run) { - runId = String(run.id) - const { data: { artifacts = [] } } = await github.rest.actions.listWorkflowRunArtifacts({ + - name: Setup Frontend + uses: ./.github/actions/setup-frontend + + - 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, - run_id: run.id, - per_page: 100, + workflow_id: 'tests-ci.yaml', + head_sha: headSha, + event: 'pull_request', + per_page: 1, }) - if (artifacts.some(a => a.name === 'failed-screenshot-tests' && !a.expired)) has = 'true' - } - core.setOutput('run_id', runId) - core.setOutput('has_manifest', has) + const run = data.workflow_runs?.[0] - - 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 + 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: Re-run failed screenshot tests and update snapshots - id: playwright-tests - shell: bash - working-directory: ComfyUI_frontend - run: | - set -euo pipefail - if [ ! -d ci-rerun ]; then - echo "No manifest found; running full suite as fallback" - pnpm exec playwright test --update-snapshots - exit 0 - fi - shopt -s nullglob - files=(ci-rerun/*.txt) - if [ ${#files[@]} -eq 0 ]; then - echo "Manifest is empty; running full suite as fallback" - pnpm exec playwright test --update-snapshots - exit 0 - fi - for f in "${files[@]}"; do - project="$(basename "$f" .txt)" - mapfile -t lines < "$f" - # Filter out blank lines - filtered=( ) - for l in "${lines[@]}"; do - [ -n "$l" ] && filtered+=("$l") - done - if [ ${#filtered[@]} -eq 0 ]; then - continue + - 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 and update snapshots + id: playwright-tests + shell: bash + working-directory: ComfyUI_frontend + continue-on-error: true + run: | + set -euo pipefail + if [ ! -d ci-rerun ]; then + echo "No manifest found; running full suite as fallback" + PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \ + pnpm exec playwright test --update-snapshots \ + --reporter=line --reporter=html + exit 0 + fi + shopt -s nullglob + files=(ci-rerun/*.txt) + if [ ${#files[@]} -eq 0 ]; then + echo "Manifest is empty; running full suite as fallback" + PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \ + pnpm exec playwright test --update-snapshots \ + --reporter=line --reporter=html + exit 0 + fi + for f in "${files[@]}"; do + project="$(basename "$f" .txt)" + mapfile -t lines < "$f" + filtered=( ) + for l in "${lines[@]}"; do + [ -n "$l" ] && filtered+=("$l") + done + if [ ${#filtered[@]} -eq 0 ]; then + continue + fi + echo "Re-running ${#filtered[@]} tests for project $project" + PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \ + pnpm exec playwright test --project="$project" --update-snapshots \ + --reporter=line --reporter=html \ + "${filtered[@]}" + done + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: ComfyUI_frontend/playwright-report/ + retention-days: 30 + + - name: Debugging info + working-directory: ComfyUI_frontend + run: | + echo "Branch: ${{ github.head_ref }}" + git status + + - name: Commit updated expectations + working-directory: ComfyUI_frontend + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@github.com' + if [ "${{ github.event_name }}" = "issue_comment" ]; then + true + else + git fetch origin ${{ github.head_ref }} + git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }} + fi + git add browser_tests + if git diff --cached --quiet; then + echo "No expectation updates detected; skipping commit." + else + git commit -m "[automated] Update test expectations" + if [ "${{ github.event_name }}" = "issue_comment" ]; then + git push + else + git push origin HEAD:${{ github.head_ref }} + fi fi - echo "Re-running ${#filtered[@]} tests for project $project" - pnpm exec playwright test --project="$project" --update-snapshots "${filtered[@]}" - done - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: ComfyUI_frontend/playwright-report/ - retention-days: 30 - - name: Debugging info - working-directory: ComfyUI_frontend - run: | - echo "Branch: ${{ github.head_ref }}" - git status - - name: Commit updated expectations - working-directory: ComfyUI_frontend - run: | - git config --global user.name 'github-actions' - git config --global user.email 'github-actions@github.com' - git fetch origin ${{ github.head_ref }} - git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }} - git add browser_tests - if git diff --cached --quiet; then - echo "No expectation updates detected; skipping commit." - else - git commit -m "[automated] Update test expectations" - git push origin HEAD:${{ github.head_ref }} - fi diff --git a/.storybook/README.md b/.storybook/README.md index 0d34474ec..be5405e51 100644 --- a/.storybook/README.md +++ b/.storybook/README.md @@ -211,18 +211,17 @@ This Storybook setup includes: ## Icon Usage in Storybook -In this project, the `` syntax from unplugin-icons is not supported in Storybook. +In this project, only the `` syntax from unplugin-icons is supported in Storybook. **Example:** ```vue ``` diff --git a/.stylelintrc.json b/.stylelintrc.json index e53789123..276ac8156 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -7,15 +7,15 @@ } ], "rules": { - "import-notation": "url", + "import-notation": "string", "font-family-no-missing-generic-family-keyword": true, - "declaration-block-no-redundant-longhand-properties": true, "declaration-property-value-no-unknown": [ true, { "ignoreProperties": { "speak": ["none"], - "app-region": ["drag", "no-drag"] + "app-region": ["drag", "no-drag"], + "/^(width|height)$/": ["/^v-bind/"] } } ], @@ -35,7 +35,7 @@ "selector-max-type": 2, "declaration-block-no-duplicate-properties": true, "block-no-empty": true, - "no-descending-specificity": true, + "no-descending-specificity": null, "no-duplicate-at-import-rules": true, "at-rule-no-unknown": [ true, @@ -57,7 +57,8 @@ true, { "ignoreFunctions": [ - "theme" + "theme", + "v-bind" ] } ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 135a9db01..f1c9341d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -255,7 +255,7 @@ pnpm format The project supports three types of icons, all with automatic imports (no manual imports needed): 1. **PrimeIcons** - Built-in PrimeVue icons using CSS classes: `` -2. **Iconify Icons** - 200,000+ icons from various libraries: ``, `` +2. **Iconify Icons** - 200,000+ icons from various libraries: ``, `` 3. **Custom Icons** - Your own SVG icons: `` Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `packages/design-system/src/icons/` and processed by `packages/design-system/src/iconCollection.ts` with automatic validation. diff --git a/apps/desktop-ui/src/components/install/GpuPicker.vue b/apps/desktop-ui/src/components/install/GpuPicker.vue index e95d3204c..0a8cac0a9 100644 --- a/apps/desktop-ui/src/components/install/GpuPicker.vue +++ b/apps/desktop-ui/src/components/install/GpuPicker.vue @@ -53,7 +53,7 @@ :value="$t('install.gpuPicker.recommended')" class="bg-neutral-300 text-neutral-900 rounded-full text-sm font-bold px-2 py-[1px]" /> - + diff --git a/apps/desktop-ui/src/components/install/InstallLocationPicker.vue b/apps/desktop-ui/src/components/install/InstallLocationPicker.vue index 7d930d00e..6b139c1e9 100644 --- a/apps/desktop-ui/src/components/install/InstallLocationPicker.vue +++ b/apps/desktop-ui/src/components/install/InstallLocationPicker.vue @@ -286,6 +286,12 @@ const onFocus = async () => { .p-accordionheader { @apply rounded-t-xl rounded-b-none; } + + .p-accordionheader-toggle-icon { + &::before { + content: '\e902'; + } + } } .p-accordioncontent { @@ -302,13 +308,5 @@ const onFocus = async () => { content: '\e933'; } } - - .p-accordionpanel-active { - .p-accordionheader-toggle-icon { - &::before { - content: '\e902'; - } - } - } } diff --git a/apps/desktop-ui/src/views/DesktopUpdateView.vue b/apps/desktop-ui/src/views/DesktopUpdateView.vue index efaa6d1f4..1cb9135e1 100644 --- a/apps/desktop-ui/src/views/DesktopUpdateView.vue +++ b/apps/desktop-ui/src/views/DesktopUpdateView.vue @@ -65,12 +65,12 @@ onUnmounted(() => electron.Validation.dispose()) .download-bg::before { @apply m-0 absolute text-muted; - font-family: 'primeicons'; + font-family: 'primeicons', sans-serif; top: -2rem; right: 2rem; speak: none; font-style: normal; - font-weight: normal; + font-weight: 400; font-variant: normal; text-transform: none; line-height: 1; diff --git a/apps/desktop-ui/src/views/MaintenanceView.vue b/apps/desktop-ui/src/views/MaintenanceView.vue index facc08dfb..dbe1b269e 100644 --- a/apps/desktop-ui/src/views/MaintenanceView.vue +++ b/apps/desktop-ui/src/views/MaintenanceView.vue @@ -186,12 +186,12 @@ onUnmounted(() => electron.Validation.dispose()) .backspan::before { @apply m-0 absolute text-muted; - font-family: 'primeicons'; + font-family: 'primeicons', sans-serif; top: -2rem; right: -2rem; speak: none; font-style: normal; - font-weight: normal; + font-weight: 400; font-variant: normal; text-transform: none; line-height: 1; diff --git a/apps/desktop-ui/src/views/ServerStartView.vue b/apps/desktop-ui/src/views/ServerStartView.vue index dd6409991..b7d1f4eac 100644 --- a/apps/desktop-ui/src/views/ServerStartView.vue +++ b/apps/desktop-ui/src/views/ServerStartView.vue @@ -18,16 +18,16 @@ style=" background: radial-gradient( ellipse 800px 600px at center, - rgba(23, 23, 23, 0.95) 0%, - rgba(23, 23, 23, 0.93) 10%, - rgba(23, 23, 23, 0.9) 20%, - rgba(23, 23, 23, 0.85) 30%, - rgba(23, 23, 23, 0.75) 40%, - rgba(23, 23, 23, 0.6) 50%, - rgba(23, 23, 23, 0.4) 60%, - rgba(23, 23, 23, 0.2) 70%, - rgba(23, 23, 23, 0.1) 80%, - rgba(23, 23, 23, 0.05) 90%, + rgb(23 23 23 / 0.95) 0%, + rgb(23 23 23 / 0.93) 10%, + rgb(23 23 23 / 0.9) 20%, + rgb(23 23 23 / 0.85) 30%, + rgb(23 23 23 / 0.75) 40%, + rgb(23 23 23 / 0.6) 50%, + rgb(23 23 23 / 0.4) 60%, + rgb(23 23 23 / 0.2) 70%, + rgb(23 23 23 / 0.1) 80%, + rgb(23 23 23 / 0.05) 90%, transparent 100% ); " diff --git a/browser_tests/fixtures/VueNodeHelpers.ts b/browser_tests/fixtures/VueNodeHelpers.ts index e69b407af..bc4f32452 100644 --- a/browser_tests/fixtures/VueNodeHelpers.ts +++ b/browser_tests/fixtures/VueNodeHelpers.ts @@ -24,9 +24,7 @@ export class VueNodeHelpers { * Get locator for selected Vue node components (using visual selection indicators) */ get selectedNodes(): Locator { - return this.page.locator( - '[data-node-id].outline-black, [data-node-id].outline-white' - ) + return this.page.locator('[data-node-id].outline-node-component-outline') } /** diff --git a/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png b/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png index 1fa6c6d67..02c2e31a2 100644 Binary files a/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png and b/browser_tests/tests/domWidget.spec.ts-snapshots/focus-mode-on-chromium-linux.png differ diff --git a/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png b/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png index a9e9926bf..d66ea8927 100644 Binary files a/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png and b/browser_tests/tests/rerouteNode.spec.ts-snapshots/reroute-inserted-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts b/browser_tests/tests/vueNodes/groups/groups.spec.ts index a43e96a5d..fcfb0efad 100644 --- a/browser_tests/tests/vueNodes/groups/groups.spec.ts +++ b/browser_tests/tests/vueNodes/groups/groups.spec.ts @@ -8,6 +8,7 @@ const CREATE_GROUP_HOTKEY = 'Control+g' test.describe('Vue Node Groups', () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.setSetting('Comfy.VueNodes.Enabled', true) + await comfyPage.setSetting('Comfy.Minimap.ShowGroups', true) await comfyPage.vueNodes.waitForNodes() }) @@ -15,6 +16,7 @@ test.describe('Vue Node Groups', () => { await comfyPage.page.getByText('Load Checkpoint').click() await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] }) await comfyPage.page.keyboard.press(CREATE_GROUP_HOTKEY) + await comfyPage.nextFrame() await expect(comfyPage.canvas).toHaveScreenshot( 'vue-groups-create-group.png' ) diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png index 0d7f65fcf..6eb1e94f5 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-create-group-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png index 69e4e6be7..ec0efa7b5 100644 Binary files a/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png and b/browser_tests/tests/vueNodes/groups/groups.spec.ts-snapshots/vue-groups-fit-to-contents-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png index 7ec1d1aa0..02f7dfd5b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png index 4cc6eb1a9..528d65f51 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/zoom.spec.ts-snapshots/zoomed-in-ctrl-shift-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png index b26c2257a..eb3725e5c 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-dragging-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png index 8edcd177b..ff85a019b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-ctrl-alt-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png index 4c7295c8c..b69b3d399 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-input-drag-reuses-origin-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png index eaa94be43..023d075ad 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-input-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png index 0364bbe08..d26710216 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-reroute-output-shift-drag-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png index 2b2f7ebb6..a0271f86b 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-shift-output-multi-link-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png index 0b7ad8ed0..3f0d5f72a 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png index 01b0428b3..f1dbcf18f 100644 Binary files a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts-snapshots/vue-node-snap-to-slot-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png index edf8ca6a0..6cefdc2a9 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png index d37b587af..172c373fc 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/collapse.spec.ts b/browser_tests/tests/vueNodes/nodeStates/collapse.spec.ts index a339a0a25..fb5fc3c17 100644 --- a/browser_tests/tests/vueNodes/nodeStates/collapse.spec.ts +++ b/browser_tests/tests/vueNodes/nodeStates/collapse.spec.ts @@ -50,17 +50,17 @@ test.describe('Vue Node Collapse', () => { // Check initial expanded state icon let iconClass = await vueNode.getCollapseIconClass() - expect(iconClass).toContain('pi-chevron-down') + expect(iconClass).not.toContain('-rotate-90') // Collapse and check icon await vueNode.toggleCollapse() iconClass = await vueNode.getCollapseIconClass() - expect(iconClass).toContain('pi-chevron-right') + expect(iconClass).toContain('-rotate-90') // Expand and check icon await vueNode.toggleCollapse() iconClass = await vueNode.getCollapseIconClass() - expect(iconClass).toContain('pi-chevron-down') + expect(iconClass).not.toContain('-rotate-90') }) test('should preserve title when collapsing/expanding', async ({ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png index c9d144e8f..3ccb97b5a 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-color-blue-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png index bb1c64294..0873ebfbb 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-dark-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png index 95b7ad4df..4326b8f7e 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/colors.spec.ts-snapshots/vue-node-custom-colors-light-all-colors-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png index 9e110e536..89173c640 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-default-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png index 29dd9cea3..e6281d325 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-active-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png index cf77a50be..f05e4f68b 100644 Binary files a/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png and b/browser_tests/tests/vueNodes/nodeStates/lod.spec.ts-snapshots/vue-nodes-lod-inactive-chromium-linux.png differ diff --git a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png index 32e9c4bd9..6e960b8bb 100644 Binary files a/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png and b/browser_tests/tests/vueNodes/widgets/load/uploadWidgets.spec.ts-snapshots/vue-nodes-upload-widgets-chromium-linux.png differ diff --git a/knip.config.ts b/knip.config.ts index 5d975c8b4..0ed7361e2 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -44,9 +44,9 @@ const config: KnipConfig = { compilers: { // https://github.com/webpro-nl/knip/issues/1008#issuecomment-3207756199 css: (text: string) => - [ - ...text.replaceAll('plugin', 'import').matchAll(/(?<=@)import[^;]+/g) - ].join('\n') + [...text.replaceAll('plugin', 'import').matchAll(/(?<=@)import[^;]+/g)] + .map((match) => match[0].replace(/url\(['"]?([^'"()]+)['"]?\)/, '$1')) + .join('\n') }, vite: { config: ['vite?(.*).config.mts'] diff --git a/packages/design-system/src/css/style.css b/packages/design-system/src/css/style.css index 1cb74892f..af18f4e3e 100644 --- a/packages/design-system/src/css/style.css +++ b/packages/design-system/src/css/style.css @@ -9,35 +9,6 @@ @config '../../tailwind.config.ts'; -:root { - --fg-color: #000; - --bg-color: #fff; - --comfy-menu-bg: #353535; - --comfy-menu-secondary-bg: #292929; - --comfy-topbar-height: 2.5rem; - --comfy-input-bg: #222; - --input-text: #ddd; - --descrip-text: #999; - --drag-text: #ccc; - --error-text: #ff4444; - --border-color: #4e4e4e; - --tr-even-bg-color: #222; - --tr-odd-bg-color: #353535; - --primary-bg: #236692; - --primary-fg: #ffffff; - --primary-hover-bg: #3485bb; - --primary-hover-fg: #ffffff; - --content-bg: #e0e0e0; - --content-fg: #000; - --content-hover-bg: #adadad; - --content-hover-fg: #000; - - /* Code styling colors for help menu*/ - --code-text-color: rgba(0, 122, 255, 1); - --code-bg-color: rgba(96, 165, 250, 0.2); - --code-block-bg-color: rgba(60, 60, 60, 0.12); -} - @media (prefers-color-scheme: dark) { :root { --fg-color: #fff; @@ -128,12 +99,121 @@ --color-dark-elevation-2: rgba(from white r g b / 0.03); } +:root { + --fg-color: #000; + --bg-color: #fff; + --comfy-menu-bg: #353535; + --comfy-menu-secondary-bg: #292929; + --comfy-topbar-height: 2.5rem; + --comfy-input-bg: #222; + --input-text: #ddd; + --descrip-text: #999; + --drag-text: #ccc; + --error-text: #ff4444; + --border-color: #4e4e4e; + --tr-even-bg-color: #222; + --tr-odd-bg-color: #353535; + --primary-bg: #236692; + --primary-fg: #ffffff; + --primary-hover-bg: #3485bb; + --primary-hover-fg: #ffffff; + --content-bg: #e0e0e0; + --content-fg: #000; + --content-hover-bg: #adadad; + --content-hover-fg: #000; + + /* Code styling colors for help menu*/ + --code-text-color: rgb(0 122 255 / 1); + --code-bg-color: rgb(96 165 250 / 0.2); + --code-block-bg-color: rgb(60 60 60 / 0.12); + + /* --- */ + + --backdrop: var(--color-white); + --dialog-surface: var(--color-neutral-200); + --node-component-border: var(--color-gray-400); + --node-component-executing: var(--color-blue-500); + --node-component-header: var(--fg-color); + --node-component-header-icon: var(--color-stone-200); + --node-component-header-surface: var(--color-white); + --node-component-outline: var(--color-black); + --node-component-ring: rgb(from var(--color-gray-500) r g b / 50%); + --node-component-slot-dot-outline-opacity-mult: 1; + --node-component-slot-dot-outline-opacity: 5%; + --node-component-slot-dot-outline: var(--color-black); + --node-component-slot-text: var(--color-stone-200); + --node-component-surface-highlight: var(--color-stone-100); + --node-component-surface-hovered: var(--color-charcoal-400); + --node-component-surface-selected: var(--color-charcoal-200); + --node-component-surface: var(--color-white); + --node-component-tooltip: var(--color-charcoal-700); + --node-component-tooltip-border: var(--color-sand-100); + --node-component-tooltip-surface: var(--color-white); + --node-component-widget-input: var(--fg-color); + --node-component-widget-input-surface: rgb( + from var(--color-zinc-500) r g b / 10% + ); + --node-component-widget-skeleton-surface: var(--color-zinc-300); + --node-stroke: var(--color-stone-100); +} + +.dark-theme { + --backdrop: var(--color-neutral-900); + --dialog-surface: var(--color-neutral-700); + --node-component-border: var(--color-stone-200); + --node-component-header-icon: var(--color-slate-300); + --node-component-header-surface: var(--color-charcoal-800); + --node-component-outline: var(--color-white); + --node-component-ring: rgb(var(--color-gray-500) / 20%); + --node-component-slot-dot-outline-opacity: 10%; + --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-400); + --node-component-surface-selected: var(--color-charcoal-200); + --node-component-surface: var(--color-charcoal-800); + --node-component-tooltip: var(--color-white); + --node-component-tooltip-border: var(--color-slate-300); + --node-component-tooltip-surface: var(--color-charcoal-800); + --node-component-widget-skeleton-surface: var(--color-zinc-800); + --node-stroke: var(--color-slate-100); +} + @theme inline { - --color-node-component-surface: var(--color-charcoal-600); - --color-node-component-surface-highlight: var(--color-slate-100); - --color-node-component-surface-hovered: var(--color-charcoal-400); - --color-node-component-surface-selected: var(--color-charcoal-200); - --color-node-stroke: var(--color-stone-100); + --color-backdrop: var(--backdrop); + --color-dialog-surface: var(--dialog-surface); + --color-node-component-border: var(--node-component-border); + --color-node-component-executing: var(--node-component-executing); + --color-node-component-header: var(--node-component-header); + --color-node-component-header-icon: var(--node-component-header-icon); + --color-node-component-header-surface: var(--node-component-header-surface); + --color-node-component-outline: var(--node-component-outline); + --color-node-component-ring: var(--node-component-ring); + --color-node-component-slot-dot-outline: rgb( + from var(--node-component-slot-dot-outline) r g b / + calc( + var(--node-component-slot-dot-outline-opacity) * + var(--node-component-slot-dot-outline-opacity-mult) + ) + ); + --color-node-component-slot-text: var(--node-component-slot-text); + --color-node-component-surface-highlight: var( + --node-component-surface-highlight + ); + --color-node-component-surface-hovered: var(--node-component-surface-hovered); + --color-node-component-surface-selected: var(--component-surface-selected); + --color-node-component-surface: var(--node-component-surface); + --color-node-component-tooltip: var(--node-component-tooltip); + --color-node-component-tooltip-border: var(--node-component-tooltip-border); + --color-node-component-tooltip-surface: var(--node-component-tooltip-surface); + --color-node-component-widget-input: var(--node-component-widget-input); + --color-node-component-widget-input-surface: var( + --node-component-widget-input-surface + ); + --color-node-component-widget-skeleton-surface: var( + --node-component-widget-skeleton-surface + ); + --color-node-stroke: var(--node-stroke); } @custom-variant dark-theme { @@ -418,7 +498,7 @@ body { /* Strong and emphasis */ .comfy-markdown-content strong { - font-weight: bold; + font-weight: 700; } .comfy-markdown-content em { @@ -429,7 +509,7 @@ body { display: none; /* Hidden by default */ position: fixed; /* Stay in place */ z-index: 100; /* Sit on top */ - padding: 30px 30px 10px 30px; + padding: 30px 30px 10px; background-color: var(--comfy-menu-bg); /* Modal background */ color: var(--error-text); box-shadow: 0 0 20px #888888; @@ -477,8 +557,8 @@ body { background-color: var(--comfy-menu-bg); font-family: sans-serif; padding: 10px; - border-radius: 0 8px 8px 8px; - box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.4); + border-radius: 0 8px 8px; + box-shadow: 3px 3px 8px rgb(0 0 0 / 0.4); } .comfy-menu-header { @@ -496,7 +576,7 @@ body { } .comfy-menu .comfy-menu-actions button { - background-color: rgba(0, 0, 0, 0); + background-color: rgb(0 0 0 / 0); padding: 0; border: none; cursor: pointer; @@ -611,7 +691,7 @@ span.drag-handle::after { min-width: 160px; margin: 0; padding: 3px; - font-weight: normal; + font-weight: 400; } .comfy-list-items button { @@ -728,7 +808,7 @@ dialog { } dialog::backdrop { - background: rgba(0, 0, 0, 0.5); + background: rgb(0 0 0 / 0.5); } .comfy-dialog.comfyui-dialog.comfy-modal { @@ -934,9 +1014,6 @@ audio.comfy-audio.empty-audio-widget { .lg-node { /* Disable text selection on all nodes */ user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; } .lg-node .lg-slot, @@ -963,7 +1040,6 @@ audio.comfy-audio.empty-audio-widget { filter: none; backdrop-filter: none; text-shadow: none; - -webkit-mask-image: none; mask-image: none; clip-path: none; background-image: none; diff --git a/packages/design-system/src/icons/README.md b/packages/design-system/src/icons/README.md index ba7cdb3e4..7f9665a46 100644 --- a/packages/design-system/src/icons/README.md +++ b/packages/design-system/src/icons/README.md @@ -26,9 +26,9 @@ ComfyUI supports three types of icons that can be used throughout the interface. ```vue ``` @@ -77,7 +77,7 @@ ComfyUI supports three types of icons that can be used throughout the interface. @@ -88,8 +88,8 @@ ComfyUI supports three types of icons that can be used throughout the interface. ```vue diff --git a/src/components/graph/selectionToolbox/ExecuteButton.vue b/src/components/graph/selectionToolbox/ExecuteButton.vue index 802d95278..0f84fc041 100644 --- a/src/components/graph/selectionToolbox/ExecuteButton.vue +++ b/src/components/graph/selectionToolbox/ExecuteButton.vue @@ -10,7 +10,7 @@ @mouseleave="() => handleMouseLeave()" @click="handleClick" > - + diff --git a/src/components/graph/selectionToolbox/FrameNodes.vue b/src/components/graph/selectionToolbox/FrameNodes.vue index 6d800a16f..1d9baec77 100644 --- a/src/components/graph/selectionToolbox/FrameNodes.vue +++ b/src/components/graph/selectionToolbox/FrameNodes.vue @@ -9,7 +9,7 @@ severity="secondary" @click="frameNodes" > - + diff --git a/src/components/graph/selectionToolbox/InfoButton.vue b/src/components/graph/selectionToolbox/InfoButton.vue index 3fd159d89..3b50c016f 100644 --- a/src/components/graph/selectionToolbox/InfoButton.vue +++ b/src/components/graph/selectionToolbox/InfoButton.vue @@ -9,7 +9,7 @@ severity="secondary" @click="toggleHelp" > - + diff --git a/src/components/graph/selectionToolbox/MenuOptionItem.vue b/src/components/graph/selectionToolbox/MenuOptionItem.vue index ee88a47d9..3c059a4d1 100644 --- a/src/components/graph/selectionToolbox/MenuOptionItem.vue +++ b/src/components/graph/selectionToolbox/MenuOptionItem.vue @@ -14,10 +14,10 @@ {{ option.shortcut }} - - + diff --git a/src/components/graph/selectionToolbox/RefreshSelectionButton.vue b/src/components/graph/selectionToolbox/RefreshSelectionButton.vue index 0da7364a0..57d29d707 100644 --- a/src/components/graph/selectionToolbox/RefreshSelectionButton.vue +++ b/src/components/graph/selectionToolbox/RefreshSelectionButton.vue @@ -7,7 +7,7 @@ data-testid="refresh-button" @click="refreshSelected" > - + diff --git a/src/components/graph/selectionToolbox/SaveToSubgraphLibrary.vue b/src/components/graph/selectionToolbox/SaveToSubgraphLibrary.vue index 7f76d2eab..3fb876a02 100644 --- a/src/components/graph/selectionToolbox/SaveToSubgraphLibrary.vue +++ b/src/components/graph/selectionToolbox/SaveToSubgraphLibrary.vue @@ -10,7 +10,7 @@ @click="() => commandStore.execute('Comfy.PublishSubgraph')" > diff --git a/src/components/graph/selectionToolbox/SubmenuPopover.vue b/src/components/graph/selectionToolbox/SubmenuPopover.vue index 2f1e25229..653c950b8 100644 --- a/src/components/graph/selectionToolbox/SubmenuPopover.vue +++ b/src/components/graph/selectionToolbox/SubmenuPopover.vue @@ -32,9 +32,9 @@ :style="{ backgroundColor: subOption.color }" /> diff --git a/src/components/sidebar/tabs/modelLibrary/ModelPreview.vue b/src/components/sidebar/tabs/modelLibrary/ModelPreview.vue index 3d5032b98..9de3d236f 100644 --- a/src/components/sidebar/tabs/modelLibrary/ModelPreview.vue +++ b/src/components/sidebar/tabs/modelLibrary/ModelPreview.vue @@ -72,7 +72,7 @@ const modelDef = props.modelDef object-fit: contain; } .model_preview_title { - font-weight: bold; + font-weight: 700; text-align: center; font-size: 14px; } @@ -89,6 +89,6 @@ const modelDef = props.modelDef font-size: 10px; } .model_preview_prefix { - font-weight: bold; + font-weight: 700; } diff --git a/src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue b/src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue index b0f64f196..fb52d8c85 100644 --- a/src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue +++ b/src/components/sidebar/tabs/nodeLibrary/NodeTreeLeaf.vue @@ -32,7 +32,7 @@ @click.stop="editBlueprint" > diff --git a/src/components/sidebar/tabs/queue/TaskItem.vue b/src/components/sidebar/tabs/queue/TaskItem.vue index 6f4297d5e..4b51a25e3 100644 --- a/src/components/sidebar/tabs/queue/TaskItem.vue +++ b/src/components/sidebar/tabs/queue/TaskItem.vue @@ -66,7 +66,7 @@ outlined @click="handleOutputLengthClick" > - {{ flatOutputs.length }} + {{ flatOutputs.length }} diff --git a/src/components/topbar/WorkflowTab.vue b/src/components/topbar/WorkflowTab.vue index d427736b8..97195beaf 100644 --- a/src/components/topbar/WorkflowTab.vue +++ b/src/components/topbar/WorkflowTab.vue @@ -13,7 +13,7 @@
@@ -99,7 +99,7 @@ class="!bg-white !text-neutral-900" @click="() => {}" > - + diff --git a/src/components/widget/layout/BaseModalLayout.vue b/src/components/widget/layout/BaseModalLayout.vue index 023e3be43..f88535279 100644 --- a/src/components/widget/layout/BaseModalLayout.vue +++ b/src/components/widget/layout/BaseModalLayout.vue @@ -5,7 +5,7 @@ :class="rightPanelButtonClasses" @click="toggleRightPanel" > - + @@ -29,8 +29,11 @@
- - + +
@@ -40,7 +43,7 @@ v-if="isRightPanelOpen && hasRightPanel" @click="toggleRightPanel" > - +
diff --git a/src/components/widget/nav/NavItem.vue b/src/components/widget/nav/NavItem.vue index f5bd09431..d334260fa 100644 --- a/src/components/widget/nav/NavItem.vue +++ b/src/components/widget/nav/NavItem.vue @@ -10,7 +10,7 @@ @click="onClick" > - + diff --git a/src/components/widget/panel/PanelHeader.vue b/src/components/widget/panel/PanelHeader.vue index 9f00ed9e7..13df123f4 100644 --- a/src/components/widget/panel/PanelHeader.vue +++ b/src/components/widget/panel/PanelHeader.vue @@ -2,7 +2,7 @@
- +

diff --git a/src/composables/graph/useVueNodeLifecycle.ts b/src/composables/graph/useVueNodeLifecycle.ts index ff89dfc85..cfcee1d44 100644 --- a/src/composables/graph/useVueNodeLifecycle.ts +++ b/src/composables/graph/useVueNodeLifecycle.ts @@ -116,7 +116,7 @@ function useVueNodeLifecycleIndividual() { slotSyncManager.attemptStart(canvas as LGraphCanvas) } }, - { immediate: true } + { immediate: true, flush: 'sync' } ) // Handle case where Vue nodes are enabled but graph starts empty diff --git a/src/composables/node/useNodePricing.ts b/src/composables/node/useNodePricing.ts index 194e3c0a0..a2590101c 100644 --- a/src/composables/node/useNodePricing.ts +++ b/src/composables/node/useNodePricing.ts @@ -169,6 +169,74 @@ const byteDanceVideoPricingCalculator = (node: LGraphNode): string => { : `$${minCost.toFixed(2)}-$${maxCost.toFixed(2)}/Run` } +// ---- constants ---- +const SORA_SIZES = { + BASIC: new Set(['720x1280', '1280x720']), + PRO: new Set(['1024x1792', '1792x1024']) +} +const ALL_SIZES = new Set([...SORA_SIZES.BASIC, ...SORA_SIZES.PRO]) + +// ---- sora-2 pricing helpers ---- +function validateSora2Selection( + modelRaw: string, + duration: number, + sizeRaw: string +): string | undefined { + const model = modelRaw?.toLowerCase() ?? '' + const size = sizeRaw?.toLowerCase() ?? '' + + if (!duration || Number.isNaN(duration)) return 'Set duration (4s / 8s / 12s)' + if (!size) return 'Set size (720x1280, 1280x720, 1024x1792, 1792x1024)' + if (!ALL_SIZES.has(size)) + return 'Invalid size. Must be 720x1280, 1280x720, 1024x1792, or 1792x1024.' + + if (model.includes('sora-2-pro')) return undefined + + if (model.includes('sora-2') && !SORA_SIZES.BASIC.has(size)) + return 'sora-2 supports only 720x1280 or 1280x720' + + if (!model.includes('sora-2')) return 'Unsupported model' + + return undefined +} + +function perSecForSora2(modelRaw: string, sizeRaw: string): number { + const model = modelRaw?.toLowerCase() ?? '' + const size = sizeRaw?.toLowerCase() ?? '' + + if (model.includes('sora-2-pro')) { + return SORA_SIZES.PRO.has(size) ? 0.5 : 0.3 + } + if (model.includes('sora-2')) return 0.1 + + return SORA_SIZES.PRO.has(size) ? 0.5 : 0.1 +} + +function formatRunPrice(perSec: number, duration: number) { + return `$${(perSec * duration).toFixed(2)}/Run` +} + +// ---- pricing calculator ---- +const sora2PricingCalculator: PricingFunction = (node: LGraphNode): string => { + const getWidgetValue = (name: string) => + String(node.widgets?.find((w) => w.name === name)?.value ?? '') + + const model = getWidgetValue('model') + const size = getWidgetValue('size') + const duration = Number( + node.widgets?.find((w) => ['duration', 'duration_s'].includes(w.name)) + ?.value + ) + + if (!model || !size || !duration) return 'Set model, duration & size' + + const validationError = validateSora2Selection(model, duration, size) + if (validationError) return validationError + + const perSec = perSecForSora2(model, size) + return formatRunPrice(perSec, duration) +} + /** * Static pricing data for API nodes, now supporting both strings and functions */ @@ -195,6 +263,9 @@ const apiNodeCosts: Record = FluxProKontextMaxNode: { displayPrice: '$0.08/Run' }, + OpenAIVideoSora2: { + displayPrice: sora2PricingCalculator + }, IdeogramV1: { displayPrice: (node: LGraphNode): string => { const numImagesWidget = node.widgets?.find( @@ -1658,6 +1729,7 @@ export const useNodePricing = () => { MinimaxHailuoVideoNode: ['resolution', 'duration'], OpenAIDalle3: ['size', 'quality'], OpenAIDalle2: ['size', 'n'], + OpenAIVideoSora2: ['model', 'size', 'duration'], OpenAIGPTImage1: ['quality', 'n'], IdeogramV1: ['num_images', 'turbo'], IdeogramV2: ['num_images', 'turbo'], diff --git a/src/core/graph/subgraph/SubgraphNode.vue b/src/core/graph/subgraph/SubgraphNode.vue index f163df53f..18e57409e 100644 --- a/src/core/graph/subgraph/SubgraphNode.vue +++ b/src/core/graph/subgraph/SubgraphNode.vue @@ -246,7 +246,7 @@ onBeforeUnmount(() => { />
@@ -302,7 +302,7 @@ onBeforeUnmount(() => {