Compare commits
23 Commits
drjkl/remo
...
5252-keybo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6606060802 | ||
|
|
06ec45c1c5 | ||
|
|
8c05ac8eb6 | ||
|
|
706ff953de | ||
|
|
9c97fb359d | ||
|
|
c662c77305 | ||
|
|
d0e81cdd33 | ||
|
|
01b3aeae68 | ||
|
|
20731fe3f0 | ||
|
|
bf9659fb2c | ||
|
|
e9352f613e | ||
|
|
d76b1abc46 | ||
|
|
fd757027a9 | ||
|
|
1efc2233c5 | ||
|
|
557b2fdb0e | ||
|
|
692666ff63 | ||
|
|
82a603ef03 | ||
|
|
14255b3512 | ||
|
|
e5adc840fe | ||
|
|
0f44b5ea58 | ||
|
|
7fd2dc304a | ||
|
|
e8de474d42 | ||
|
|
3f291672d4 |
31
.github/actions/setup-playwright/action.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Setup Playwright
|
||||
description: Cache and install Playwright browsers with dependencies
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Detect Playwright version
|
||||
id: detect-version
|
||||
shell: bash
|
||||
working-directory: ComfyUI_frontend
|
||||
run: |
|
||||
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq --raw-output '.[0].devDependencies["@playwright/test"].version')
|
||||
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
id: cache-playwright-browsers
|
||||
with:
|
||||
path: '~/.cache/ms-playwright'
|
||||
key: ${{ runner.os }}-playwright-browsers-${{ steps.detect-version.outputs.playwright-version }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Install Playwright Browsers (operating system dependencies)
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
||||
shell: bash
|
||||
run: pnpm exec playwright install-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
11
.github/workflows/claude-pr-review.yml
vendored
@@ -29,11 +29,9 @@ jobs:
|
||||
- name: Check if we should proceed
|
||||
id: check-status
|
||||
run: |
|
||||
# Get all check runs for this commit
|
||||
CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("lint-and-format|test|playwright-tests")) | {name, conclusion}')
|
||||
|
||||
# Check if any required checks failed
|
||||
if echo "$CHECK_RUNS" | grep -q '"conclusion": "failure"'; then
|
||||
CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("lint-and-format")) | {name, conclusion}')
|
||||
|
||||
if echo "$CHECK_RUNS" | grep -Eq '"conclusion": "(failure|cancelled|timed_out|action_required)"'; then
|
||||
echo "Some CI checks failed - skipping Claude review"
|
||||
echo "proceed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
@@ -53,6 +51,7 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/head
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -86,4 +85,4 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
|
||||
6
.github/workflows/lint-and-format.yaml
vendored
@@ -15,9 +15,7 @@ jobs:
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
@@ -102,4 +100,4 @@ jobs:
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '## ⚠️ Linting/Formatting Issues Found\n\nThis PR has linting or formatting issues that need to be fixed.\n\n**Since this PR is from a fork, auto-fix cannot be applied automatically.**\n\n### Option 1: Set up pre-commit hooks (recommended)\nRun this once to automatically format code on every commit:\n```bash\npnpm prepare\n```\n\n### Option 2: Fix manually\nRun these commands and push the changes:\n```bash\npnpm lint:fix\npnpm format\n```\n\nSee [CONTRIBUTING.md](https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/CONTRIBUTING.md#git-pre-commit-hooks) for more details.'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,7 +12,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||
playwright-version: ${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v5
|
||||
@@ -65,12 +64,6 @@ jobs:
|
||||
id: cache-key
|
||||
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Playwright Version
|
||||
id: playwright-version
|
||||
run: |
|
||||
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq --raw-output '.[0].devDependencies["@playwright/test"].version')
|
||||
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Save cache
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
@@ -123,22 +116,8 @@ jobs:
|
||||
working-directory: ComfyUI
|
||||
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
id: cache-playwright-browsers
|
||||
with:
|
||||
path: '~/.cache/ms-playwright'
|
||||
key: '${{ runner.os }}-playwright-browsers-${{ needs.setup.outputs.playwright-version }}'
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Install Playwright Browsers (operating system dependencies)
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
||||
run: pnpm exec playwright install-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
|
||||
- name: Start ComfyUI server
|
||||
run: |
|
||||
@@ -202,22 +181,8 @@ jobs:
|
||||
pip install wait-for-it
|
||||
working-directory: ComfyUI
|
||||
|
||||
- name: Cache Playwright Browsers
|
||||
uses: actions/cache@v4
|
||||
id: cache-playwright-browsers
|
||||
with:
|
||||
path: '~/.cache/ms-playwright'
|
||||
key: '${{ runner.os }}-playwright-browsers-${{ needs.setup.outputs.playwright-version }}'
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Install Playwright Browsers (operating system dependencies)
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
||||
run: pnpm exec playwright install-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
|
||||
- name: Start ComfyUI server
|
||||
run: |
|
||||
@@ -77,9 +77,8 @@ jobs:
|
||||
python main.py --cpu --multi-user &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
working-directory: ComfyUI
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
@@ -26,16 +26,8 @@ jobs:
|
||||
key: i18n-tools-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
i18n-tools-cache-${{ runner.os }}-
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
@@ -13,11 +13,9 @@ jobs:
|
||||
update-locales:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
@@ -14,16 +14,8 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
- name: Run Playwright tests and update snapshots
|
||||
id: playwright-tests
|
||||
run: pnpm exec playwright test --update-snapshots
|
||||
@@ -16,7 +16,7 @@ Without this flag, parallel tests will conflict and fail randomly.
|
||||
|
||||
### ComfyUI devtools
|
||||
|
||||
ComfyUI_devtools is now included in this repository under `tools/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
|
||||
ComfyUI_devtools is included in this repository under `tools/devtools/`. During CI/CD, these files are automatically copied to the `custom_nodes` directory.
|
||||
_ComfyUI_devtools adds additional API endpoints and nodes to ComfyUI for browser testing._
|
||||
|
||||
For local development, copy the devtools files to your ComfyUI installation:
|
||||
|
||||
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
1039
docs/keybinding.md
Normal file
@@ -90,6 +90,7 @@ export default defineConfig([
|
||||
}
|
||||
],
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
'vue/no-v-html': 'off',
|
||||
// Enforce dark-theme: instead of dark: prefix
|
||||
'vue/no-restricted-class': ['error', '/^dark:/'],
|
||||
@@ -207,5 +208,11 @@ export default defineConfig([
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.{test,spec,stories}.ts', '**/*.stories.vue'],
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -20,7 +20,6 @@ const config: KnipConfig = {
|
||||
project: ['src/**/*.{js,ts}', '*.{js,ts,mts}']
|
||||
},
|
||||
'packages/registry-types': {
|
||||
entry: ['src/comfyRegistryTypes.ts'],
|
||||
project: ['src/**/*.{js,ts}']
|
||||
}
|
||||
},
|
||||
|
||||
166
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.28.3",
|
||||
"version": "1.28.4",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
@@ -42,82 +42,82 @@
|
||||
"devtools:pycheck": "python3 -m compileall -q tools/devtools"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@intlify/eslint-plugin-vue-i18n": "^4.1.0",
|
||||
"@lobehub/i18n-cli": "^1.25.1",
|
||||
"@nx/eslint": "21.4.1",
|
||||
"@nx/playwright": "21.4.1",
|
||||
"@nx/storybook": "21.4.1",
|
||||
"@nx/vite": "21.4.1",
|
||||
"@pinia/testing": "^0.1.5",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@storybook/addon-docs": "^9.1.1",
|
||||
"@storybook/vue3": "^9.1.1",
|
||||
"@storybook/vue3-vite": "^9.1.1",
|
||||
"@tailwindcss/vite": "^4.1.12",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.0",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^20.14.8",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/three": "^0.169.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/ui": "^3.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-storybook": "^9.1.6",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"@eslint/js": "catalog:",
|
||||
"@intlify/eslint-plugin-vue-i18n": "catalog:",
|
||||
"@lobehub/i18n-cli": "catalog:",
|
||||
"@nx/eslint": "catalog:",
|
||||
"@nx/playwright": "catalog:",
|
||||
"@nx/storybook": "catalog:",
|
||||
"@nx/vite": "catalog:",
|
||||
"@pinia/testing": "catalog:",
|
||||
"@playwright/test": "catalog:",
|
||||
"@storybook/addon-docs": "catalog:",
|
||||
"@storybook/vue3": "catalog:",
|
||||
"@storybook/vue3-vite": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@trivago/prettier-plugin-sort-imports": "catalog:",
|
||||
"@types/fs-extra": "catalog:",
|
||||
"@types/jsdom": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/semver": "catalog:",
|
||||
"@types/three": "catalog:",
|
||||
"@vitejs/plugin-vue": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"@vitest/ui": "catalog:",
|
||||
"@vue/test-utils": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"eslint-config-prettier": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"eslint-plugin-storybook": "catalog:",
|
||||
"eslint-plugin-unused-imports": "catalog:",
|
||||
"eslint-plugin-vue": "catalog:",
|
||||
"fs-extra": "^11.2.0",
|
||||
"globals": "^15.9.0",
|
||||
"happy-dom": "^15.11.0",
|
||||
"husky": "^9.0.11",
|
||||
"jiti": "2.4.2",
|
||||
"jsdom": "^26.1.0",
|
||||
"knip": "^5.62.0",
|
||||
"lint-staged": "^15.2.7",
|
||||
"nx": "21.4.1",
|
||||
"prettier": "^3.3.2",
|
||||
"storybook": "^9.1.6",
|
||||
"tailwindcss": "^4.1.12",
|
||||
"tailwindcss-primeui": "^0.6.1",
|
||||
"tsx": "^4.15.6",
|
||||
"tw-animate-css": "^1.3.8",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript-eslint": "^8.44.0",
|
||||
"unplugin-icons": "^0.22.0",
|
||||
"unplugin-vue-components": "^0.28.0",
|
||||
"globals": "catalog:",
|
||||
"happy-dom": "catalog:",
|
||||
"husky": "catalog:",
|
||||
"jiti": "catalog:",
|
||||
"jsdom": "catalog:",
|
||||
"knip": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"nx": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"storybook": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"tailwindcss-primeui": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"tw-animate-css": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"typescript-eslint": "catalog:",
|
||||
"unplugin-icons": "catalog:",
|
||||
"unplugin-vue-components": "catalog:",
|
||||
"uuid": "^11.1.0",
|
||||
"vite": "^5.4.19",
|
||||
"vite-plugin-dts": "^4.5.4",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-vue-devtools": "^7.7.6",
|
||||
"vitest": "^3.2.4",
|
||||
"vue-component-type-helpers": "^3.0.7",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.0.7",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-dts": "catalog:",
|
||||
"vite-plugin-html": "catalog:",
|
||||
"vite-plugin-vue-devtools": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vue-component-type-helpers": "catalog:",
|
||||
"vue-eslint-parser": "catalog:",
|
||||
"vue-tsc": "catalog:",
|
||||
"zip-dir": "^2.0.0",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
"zod-to-json-schema": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@alloc/quick-lru": "catalog:",
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.3.1",
|
||||
"@comfyorg/comfyui-electron-types": "0.4.73-0",
|
||||
"@comfyorg/design-system": "workspace:*",
|
||||
"@comfyorg/registry-types": "workspace:*",
|
||||
"@comfyorg/tailwind-utils": "workspace:*",
|
||||
"@iconify/json": "^2.2.380",
|
||||
"@primeuix/forms": "0.0.2",
|
||||
"@primeuix/styled": "0.3.2",
|
||||
"@primeuix/utils": "^0.3.2",
|
||||
"@primevue/core": "^4.2.5",
|
||||
"@primevue/forms": "^4.2.5",
|
||||
"@primevue/icons": "4.2.5",
|
||||
"@primevue/themes": "^4.2.5",
|
||||
"@sentry/vue": "^8.48.0",
|
||||
"@iconify/json": "catalog:",
|
||||
"@primeuix/forms": "catalog:",
|
||||
"@primeuix/styled": "catalog:",
|
||||
"@primeuix/utils": "catalog:",
|
||||
"@primevue/core": "catalog:",
|
||||
"@primevue/forms": "catalog:",
|
||||
"@primevue/icons": "catalog:",
|
||||
"@primevue/themes": "catalog:",
|
||||
"@sentry/vue": "catalog:",
|
||||
"@tiptap/core": "^2.10.4",
|
||||
"@tiptap/extension-link": "^2.10.4",
|
||||
"@tiptap/extension-table": "^2.10.4",
|
||||
@@ -125,39 +125,39 @@
|
||||
"@tiptap/extension-table-header": "^2.10.4",
|
||||
"@tiptap/extension-table-row": "^2.10.4",
|
||||
"@tiptap/starter-kit": "^2.10.4",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"@vueuse/integrations": "^13.9.0",
|
||||
"@vueuse/core": "catalog:",
|
||||
"@vueuse/integrations": "catalog:",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-serialize": "^0.13.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"algoliasearch": "^5.21.0",
|
||||
"axios": "^1.8.2",
|
||||
"algoliasearch": "catalog:",
|
||||
"axios": "catalog:",
|
||||
"chart.js": "^4.5.0",
|
||||
"dompurify": "^3.2.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv": "catalog:",
|
||||
"es-toolkit": "^1.39.9",
|
||||
"extendable-media-recorder": "^9.2.27",
|
||||
"extendable-media-recorder-wav-encoder": "^7.0.129",
|
||||
"fast-glob": "^3.3.3",
|
||||
"firebase": "^11.6.0",
|
||||
"firebase": "catalog:",
|
||||
"fuse.js": "^7.0.0",
|
||||
"glob": "^11.0.3",
|
||||
"jsondiffpatch": "^0.6.0",
|
||||
"loglevel": "^1.9.2",
|
||||
"marked": "^15.0.11",
|
||||
"pinia": "^2.1.7",
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.2.5",
|
||||
"pinia": "catalog:",
|
||||
"primeicons": "catalog:",
|
||||
"primevue": "catalog:",
|
||||
"reka-ui": "^2.5.0",
|
||||
"semver": "^7.7.2",
|
||||
"three": "^0.170.0",
|
||||
"tiptap-markdown": "^0.8.10",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^9.14.3",
|
||||
"vue-router": "^4.4.3",
|
||||
"vuefire": "^3.2.1",
|
||||
"yjs": "^13.6.27",
|
||||
"zod": "^3.23.8",
|
||||
"zod-validation-error": "^3.3.0"
|
||||
"vue": "catalog:",
|
||||
"vue-i18n": "catalog:",
|
||||
"vue-router": "catalog:",
|
||||
"vuefire": "catalog:",
|
||||
"yjs": "catalog:",
|
||||
"zod": "catalog:",
|
||||
"zod-validation-error": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
"description": "Shared design system for ComfyUI Frontend",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./tailwind-config": {
|
||||
"import": "./tailwind.config.ts",
|
||||
"types": "./tailwind.config.ts"
|
||||
},
|
||||
"./tailwind-config": "./tailwind.config.ts",
|
||||
"./css/*": "./src/css/*"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -20,12 +17,12 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.1.178",
|
||||
"@iconify/tailwind": "^1.1.3"
|
||||
"@iconify-json/lucide": "catalog:",
|
||||
"@iconify/tailwind": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.4.5"
|
||||
"tailwindcss": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"packageManager": "pnpm@10.17.1"
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Everthing below here to be cleaned up over time. */
|
||||
/* Everything below here to be cleaned up over time. */
|
||||
|
||||
body {
|
||||
width: 100vw;
|
||||
|
||||
@@ -1125,7 +1125,7 @@ export interface paths {
|
||||
}
|
||||
get?: never
|
||||
put?: never
|
||||
/** Create a new custom node using admin priviledge */
|
||||
/** Create a new custom node using admin privilege */
|
||||
post: operations['adminCreateNode']
|
||||
delete?: never
|
||||
options?: never
|
||||
@@ -16383,7 +16383,7 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
/** @description Webhook processed succesfully */
|
||||
/** @description Webhook processed successfully */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
"./networkUtil": "./src/networkUtil.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.11.0"
|
||||
"axios": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.2"
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
@@ -25,6 +22,6 @@
|
||||
"tailwind-merge": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5"
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
887
pnpm-lock.yaml
generated
@@ -2,6 +2,115 @@ packages:
|
||||
- apps/**
|
||||
- packages/**
|
||||
|
||||
catalog:
|
||||
# Core frameworks
|
||||
typescript: ^5.9.2
|
||||
vue: ^3.5.13
|
||||
|
||||
# Build tools
|
||||
'@nx/eslint': 21.4.1
|
||||
'@nx/playwright': 21.4.1
|
||||
'@nx/storybook': 21.4.1
|
||||
'@nx/vite': 21.4.1
|
||||
nx: 21.4.1
|
||||
tsx: ^4.15.6
|
||||
vite: ^5.4.19
|
||||
'@vitejs/plugin-vue': ^5.1.4
|
||||
'vite-plugin-dts': ^4.5.4
|
||||
vue-tsc: ^3.0.7
|
||||
|
||||
# Testing
|
||||
'happy-dom': ^15.11.0
|
||||
jsdom: ^26.1.0
|
||||
'@pinia/testing': ^0.1.5
|
||||
'@playwright/test': ^1.52.0
|
||||
'@vitest/coverage-v8': ^3.2.4
|
||||
'@vitest/ui': ^3.0.0
|
||||
vitest: ^3.2.4
|
||||
'@vue/test-utils': ^2.4.6
|
||||
|
||||
# Linting & Formatting
|
||||
'@eslint/js': ^9.35.0
|
||||
eslint: ^9.34.0
|
||||
'eslint-config-prettier': ^10.1.8
|
||||
'eslint-plugin-prettier': ^5.5.4
|
||||
'eslint-plugin-storybook': ^9.1.6
|
||||
'eslint-plugin-unused-imports': ^4.2.0
|
||||
'eslint-plugin-vue': ^10.4.0
|
||||
globals: ^15.9.0
|
||||
'@intlify/eslint-plugin-vue-i18n': ^4.1.0
|
||||
prettier: ^3.3.2
|
||||
'typescript-eslint': ^8.44.0
|
||||
'vue-eslint-parser': ^10.2.0
|
||||
|
||||
# Vue ecosystem
|
||||
'@sentry/vue': ^8.48.0
|
||||
'@vueuse/core': ^11.0.0
|
||||
'@vueuse/integrations': ^13.9.0
|
||||
'vite-plugin-html': ^3.2.2
|
||||
'vite-plugin-vue-devtools': ^7.7.6
|
||||
pinia: ^2.1.7
|
||||
'vue-i18n': ^9.14.3
|
||||
'vue-router': ^4.4.3
|
||||
vuefire: ^3.2.1
|
||||
|
||||
# PrimeVue UI framework
|
||||
'@primeuix/forms': 0.0.2
|
||||
'@primeuix/styled': 0.3.2
|
||||
'@primeuix/utils': ^0.3.2
|
||||
'@primevue/core': ^4.2.5
|
||||
'@primevue/forms': ^4.2.5
|
||||
'@primevue/icons': 4.2.5
|
||||
'@primevue/themes': ^4.2.5
|
||||
primeicons: ^7.0.0
|
||||
primevue: ^4.2.5
|
||||
|
||||
# Tailwind CSS and design
|
||||
'@iconify/json': ^2.2.380
|
||||
'@iconify-json/lucide': ^1.1.178
|
||||
'@iconify/tailwind': ^1.1.3
|
||||
'@tailwindcss/vite': ^4.1.12
|
||||
tailwindcss: ^4.1.12
|
||||
'tailwindcss-primeui': ^0.6.1
|
||||
'tw-animate-css': ^1.3.8
|
||||
'unplugin-icons': ^0.22.0
|
||||
'unplugin-vue-components': ^0.28.0
|
||||
|
||||
# Storybook
|
||||
'@storybook/addon-docs': ^9.1.1
|
||||
storybook: ^9.1.6
|
||||
'@storybook/vue3': ^9.1.1
|
||||
'@storybook/vue3-vite': ^9.1.1
|
||||
|
||||
# Data and validation
|
||||
algoliasearch: ^5.21.0
|
||||
axios: ^1.8.2
|
||||
firebase: ^11.6.0
|
||||
yjs: ^13.6.27
|
||||
zod: ^3.23.8
|
||||
'zod-validation-error': ^3.3.0
|
||||
|
||||
# Dev tools
|
||||
dotenv: ^16.4.5
|
||||
husky: ^9.0.11
|
||||
jiti: 2.4.2
|
||||
knip: ^5.62.0
|
||||
'lint-staged': ^15.2.7
|
||||
|
||||
# Type definitions
|
||||
'@types/fs-extra': ^11.0.4
|
||||
'@types/jsdom': ^21.1.7
|
||||
'@types/node': ^20.14.8
|
||||
'@types/semver': ^7.7.0
|
||||
'@types/three': ^0.169.0
|
||||
'vue-component-type-helpers': ^3.0.7
|
||||
'zod-to-json-schema': ^3.24.1
|
||||
|
||||
# i18n
|
||||
'@alloc/quick-lru': ^5.2.0
|
||||
'@lobehub/i18n-cli': ^1.25.1
|
||||
'@trivago/prettier-plugin-sort-imports': ^5.2.0
|
||||
|
||||
ignoredBuiltDependencies:
|
||||
- '@firebase/util'
|
||||
- protobufjs
|
||||
|
||||
@@ -44,7 +44,6 @@ const showContextMenu = (event: MouseEvent) => {
|
||||
onMounted(() => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
window['__COMFYUI_FRONTEND_VERSION__'] = config.app_version
|
||||
console.log('ComfyUI Front-end version:', config.app_version)
|
||||
|
||||
if (isElectron()) {
|
||||
document.addEventListener('contextmenu', showContextMenu)
|
||||
|
||||
@@ -69,7 +69,7 @@ const terminalCreated = (
|
||||
await loadLogEntries()
|
||||
} catch (err) {
|
||||
console.error('Error loading logs', err)
|
||||
// On older backends the endpoints wont exist
|
||||
// On older backends the endpoints won't exist
|
||||
errorMessage.value =
|
||||
'Unable to load logs, please ensure you have updated your ComfyUI backend.'
|
||||
return
|
||||
|
||||
@@ -45,37 +45,39 @@
|
||||
<span class="text-muted">{{ t('auth.login.orContinueWith') }}</span>
|
||||
</Divider>
|
||||
|
||||
<!-- Social Login Buttons -->
|
||||
<!-- Social Login Buttons (hidden if host not whitelisted) -->
|
||||
<div class="flex flex-col gap-6">
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGoogle"
|
||||
>
|
||||
<i class="pi pi-google mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGoogle')
|
||||
: t('auth.signup.signUpWithGoogle')
|
||||
}}
|
||||
</Button>
|
||||
<template v-if="ssoAllowed">
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGoogle"
|
||||
>
|
||||
<i class="pi pi-google mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGoogle')
|
||||
: t('auth.signup.signUpWithGoogle')
|
||||
}}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGithub"
|
||||
>
|
||||
<i class="pi pi-github mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGithub')
|
||||
: t('auth.signup.signUpWithGithub')
|
||||
}}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
class="h-10"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="signInWithGithub"
|
||||
>
|
||||
<i class="pi pi-github mr-2"></i>
|
||||
{{
|
||||
isSignIn
|
||||
? t('auth.login.loginWithGithub')
|
||||
: t('auth.signup.signUpWithGithub')
|
||||
}}
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
@@ -149,6 +151,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { COMFY_PLATFORM_BASE_URL } from '@/config/comfyApi'
|
||||
import type { SignInData, SignUpData } from '@/schemas/signInSchema'
|
||||
import { isHostWhitelisted, normalizeHost } from '@/utils/hostWhitelist'
|
||||
import { isInChina } from '@/utils/networkUtil'
|
||||
|
||||
import ApiKeyForm from './signin/ApiKeyForm.vue'
|
||||
@@ -164,6 +167,7 @@ const authActions = useFirebaseAuthActions()
|
||||
const isSecureContext = window.isSecureContext
|
||||
const isSignIn = ref(true)
|
||||
const showApiKeyForm = ref(false)
|
||||
const ssoAllowed = isHostWhitelisted(normalizeHost(window.location.hostname))
|
||||
|
||||
const toggleState = () => {
|
||||
isSignIn.value = !isSignIn.value
|
||||
|
||||
@@ -237,7 +237,7 @@ async function removeKeybinding(commandData: ICommandData) {
|
||||
async function captureKeybinding(event: KeyboardEvent) {
|
||||
// Allow the use of keyboard shortcuts when adding keyboard shortcuts
|
||||
if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
||||
switch (event.key) {
|
||||
switch (event.code) {
|
||||
case 'Escape':
|
||||
cancelEdit()
|
||||
return
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, provide, ref, watch } from 'vue'
|
||||
import { computed, provide, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import IconButton from '@/components/button/IconButton.vue'
|
||||
@@ -202,12 +202,4 @@ const selectedSort = ref<string>('popular')
|
||||
const selectedNavItem = ref<string | null>('installed')
|
||||
|
||||
const gridStyle = computed(() => createGridStyle())
|
||||
|
||||
watch(searchText, (newQuery) => {
|
||||
console.log('searchText:', searchText.value, newQuery)
|
||||
})
|
||||
|
||||
watch(searchQuery, (newQuery) => {
|
||||
console.log('searchQuery:', searchQuery.value, newQuery)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -86,7 +86,7 @@ const createStoryTemplate = (args: StoryArgs) => ({
|
||||
const t = (k: string) => k
|
||||
|
||||
const onClose = () => {
|
||||
console.log('OnClose invoked')
|
||||
// OnClose handler for story
|
||||
}
|
||||
provide(OnCloseKey, onClose)
|
||||
|
||||
|
||||
@@ -300,9 +300,6 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
const modeValue = String(modeWidget.value)
|
||||
const durationValue = String(durationWidget.value)
|
||||
const modelValue = String(modelWidget.value)
|
||||
console.log('modelValue', modelValue)
|
||||
console.log('modeValue', modeValue)
|
||||
console.log('durationValue', durationValue)
|
||||
|
||||
// Same pricing matrix as KlingTextToVideoNode
|
||||
if (modelValue.includes('v1-6') || modelValue.includes('v1-5')) {
|
||||
@@ -356,9 +353,6 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
const modeValue = String(modeWidget.value)
|
||||
const durationValue = String(durationWidget.value)
|
||||
const modelValue = String(modelWidget.value)
|
||||
console.log('modelValue', modelValue)
|
||||
console.log('modeValue', modeValue)
|
||||
console.log('durationValue', durationValue)
|
||||
|
||||
// Same pricing matrix as KlingTextToVideoNode
|
||||
if (
|
||||
@@ -564,9 +558,6 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
const model = String(modelWidget.value)
|
||||
const resolution = String(resolutionWidget.value).toLowerCase()
|
||||
const duration = String(durationWidget.value)
|
||||
console.log('model', model)
|
||||
console.log('resolution', resolution)
|
||||
console.log('duration', duration)
|
||||
|
||||
if (model.includes('ray-flash-2')) {
|
||||
if (duration.includes('5s')) {
|
||||
|
||||
@@ -26,58 +26,58 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'r'
|
||||
key: 'KeyR'
|
||||
},
|
||||
commandId: 'Comfy.RefreshNodeDefinitions'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'q'
|
||||
key: 'KeyQ'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.queue'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'w'
|
||||
key: 'KeyW'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.workflows'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'n'
|
||||
key: 'KeyN'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.node-library'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm'
|
||||
key: 'KeyM'
|
||||
},
|
||||
commandId: 'Workspace.ToggleSidebarTab.model-library'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 's',
|
||||
key: 'KeyS',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.SaveWorkflow'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'o',
|
||||
key: 'KeyO',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.OpenWorkflow'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'g',
|
||||
key: 'KeyG',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Graph.GroupSelectedNodes'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: ',',
|
||||
key: 'Comma',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.ShowSettingsDialog'
|
||||
@@ -85,7 +85,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
// For '=' both holding shift and not holding shift
|
||||
{
|
||||
combo: {
|
||||
key: '=',
|
||||
key: 'Equal',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomIn',
|
||||
@@ -93,7 +93,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '+',
|
||||
key: 'Equal',
|
||||
alt: true,
|
||||
shift: true
|
||||
},
|
||||
@@ -103,7 +103,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
// For number pad '+'
|
||||
{
|
||||
combo: {
|
||||
key: '+',
|
||||
key: 'NumpadAdd',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomIn',
|
||||
@@ -111,7 +111,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '-',
|
||||
key: 'Minus',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ZoomOut',
|
||||
@@ -119,21 +119,21 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '.'
|
||||
key: 'Period'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.FitView',
|
||||
targetElementId: 'graph-canvas-container'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'p'
|
||||
key: 'KeyP'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelected.Pin',
|
||||
targetElementId: 'graph-canvas-container'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'c',
|
||||
key: 'KeyC',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Collapse',
|
||||
@@ -141,7 +141,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'b',
|
||||
key: 'KeyB',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Bypass',
|
||||
@@ -149,7 +149,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm',
|
||||
key: 'KeyM',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleSelectedNodes.Mute',
|
||||
@@ -157,20 +157,20 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: '`',
|
||||
key: 'Backquote',
|
||||
ctrl: true
|
||||
},
|
||||
commandId: 'Workspace.ToggleBottomPanelTab.logs-terminal'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'f'
|
||||
key: 'KeyF'
|
||||
},
|
||||
commandId: 'Workspace.ToggleFocusMode'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'e',
|
||||
key: 'KeyE',
|
||||
ctrl: true,
|
||||
shift: true
|
||||
},
|
||||
@@ -178,7 +178,7 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'm',
|
||||
key: 'KeyM',
|
||||
alt: true
|
||||
},
|
||||
commandId: 'Comfy.Canvas.ToggleMinimap'
|
||||
@@ -187,19 +187,19 @@ export const CORE_KEYBINDINGS: Keybinding[] = [
|
||||
combo: {
|
||||
ctrl: true,
|
||||
shift: true,
|
||||
key: 'k'
|
||||
key: 'KeyK'
|
||||
},
|
||||
commandId: 'Workspace.ToggleBottomPanel.Shortcuts'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'v'
|
||||
key: 'KeyV'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.Unlock'
|
||||
},
|
||||
{
|
||||
combo: {
|
||||
key: 'h'
|
||||
key: 'KeyH'
|
||||
},
|
||||
commandId: 'Comfy.Canvas.Lock'
|
||||
},
|
||||
|
||||
@@ -344,7 +344,7 @@ export const SERVER_CONFIG_ITEMS: ServerConfig<any>[] = [
|
||||
type: 'number',
|
||||
defaultValue: null,
|
||||
tooltip:
|
||||
'Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reverved depending on your OS.'
|
||||
'Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reserved depending on your OS.'
|
||||
},
|
||||
|
||||
// Misc settings
|
||||
|
||||
@@ -135,7 +135,7 @@ function addProxyFromOverlay(subgraphNode: SubgraphNode, overlay: Overlay) {
|
||||
* @param {string} property - The name of the accessed value.
|
||||
* Checked for conditional logic, but never changed
|
||||
* @param {object} receiver - The object the result is set to
|
||||
* and the vlaue used as 'this' if property is a get/set method
|
||||
* and the value used as 'this' if property is a get/set method
|
||||
* @param {unknown} value - only used on set calls. The thing being assigned
|
||||
*/
|
||||
const handler = {
|
||||
|
||||
@@ -147,7 +147,7 @@ app.registerExtension({
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node[WEBCAM_READY].then((v) => {
|
||||
video = v
|
||||
// If width isnt specified then use video output resolution
|
||||
// If width isn't specified then use video output resolution
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (!w.value) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
|
||||
@@ -149,7 +149,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
target_slot: number
|
||||
) {
|
||||
// Fires before the link is made allowing us to reject it if it isn't valid
|
||||
// No widget, we cant connect
|
||||
// No widget, we can't connect
|
||||
if (!input.widget && !(input.type in ComfyWidgets)) {
|
||||
return false
|
||||
}
|
||||
@@ -388,7 +388,7 @@ export class PrimitiveNode extends LGraphNode {
|
||||
}
|
||||
|
||||
onLastDisconnect() {
|
||||
// We cant remove + re-add the output here as if you drag a link over the same link
|
||||
// We can't remove + re-add the output here as if you drag a link over the same link
|
||||
// it removes, then re-adds, causing it to break
|
||||
this.outputs[0].type = '*'
|
||||
this.outputs[0].name = 'connect to widget input'
|
||||
@@ -595,7 +595,7 @@ app.registerExtension({
|
||||
|
||||
this.graph?.add(node)
|
||||
|
||||
// Calculate a position that wont directly overlap another node
|
||||
// Calculate a position that won't directly overlap another node
|
||||
const pos: [number, number] = [
|
||||
this.pos[0] - node.size[0] - 30,
|
||||
this.pos[1]
|
||||
|
||||
@@ -146,8 +146,8 @@ Litegraph has no runtime dependencies. The build tooling has been tested on Node
|
||||
|
||||
Use GitHub actions to release normal versions.
|
||||
|
||||
1. Run the `Release a New Version` action, selecting the version incrment type
|
||||
1. Merge the resultion PR
|
||||
1. Run the `Release a New Version` action, selecting the version increment type
|
||||
1. Merge the resolution PR
|
||||
1. A GitHub release is automatically published on merge
|
||||
|
||||
### Pre-release
|
||||
|
||||
@@ -274,8 +274,6 @@ export class LGraph
|
||||
* @param o data from previous serialization [optional]
|
||||
*/
|
||||
constructor(o?: ISerialisedGraph | SerialisableGraph) {
|
||||
if (LiteGraph.debug) console.log('Graph created')
|
||||
|
||||
/** @see MapProxyHandler */
|
||||
const links = this._links
|
||||
MapProxyHandler.bindAllMethods(links)
|
||||
@@ -532,7 +530,7 @@ export class LGraph
|
||||
this.errors_in_execution = true
|
||||
if (LiteGraph.throw_errors) throw error
|
||||
|
||||
if (LiteGraph.debug) console.log('Error during execution:', error)
|
||||
if (LiteGraph.debug) console.error('Error during execution:', error)
|
||||
this.stop()
|
||||
}
|
||||
}
|
||||
@@ -1128,7 +1126,7 @@ export class LGraph
|
||||
/**
|
||||
* Snaps the provided items to a grid.
|
||||
*
|
||||
* Item positions are reounded to the nearest multiple of {@link LiteGraph.CANVAS_GRID_SIZE}.
|
||||
* Item positions are rounded to the nearest multiple of {@link LiteGraph.CANVAS_GRID_SIZE}.
|
||||
*
|
||||
* When {@link LiteGraph.alwaysSnapToGrid} is enabled
|
||||
* and the grid size is falsy, a default of 1 is used.
|
||||
@@ -1167,7 +1165,7 @@ export class LGraph
|
||||
const ctor = LiteGraph.registered_node_types[node.type]
|
||||
if (node.constructor == ctor) continue
|
||||
|
||||
console.log('node being replaced by newer version:', node.type)
|
||||
console.warn('node being replaced by newer version:', node.type)
|
||||
const newnode = LiteGraph.createNode(node.type)
|
||||
if (!newnode) continue
|
||||
_nodes[i] = newnode
|
||||
@@ -1229,9 +1227,6 @@ export class LGraph
|
||||
|
||||
/* Called when something visually changed (not the graph!) */
|
||||
change(): void {
|
||||
if (LiteGraph.debug) {
|
||||
console.log('Graph changed')
|
||||
}
|
||||
this.canvasAction((c) => c.setDirty(true, true))
|
||||
this.on_change?.(this)
|
||||
}
|
||||
@@ -1626,12 +1621,6 @@ export class LGraph
|
||||
} else {
|
||||
throw new TypeError('Subgraph input node is not a SubgraphInput')
|
||||
}
|
||||
console.debug(
|
||||
'Reconnect input links in parent graph',
|
||||
{ ...link },
|
||||
this.links.get(link.id),
|
||||
this.links.get(link.id) === link
|
||||
)
|
||||
|
||||
for (const resolved of others) {
|
||||
resolved.link.disconnect(this)
|
||||
@@ -2233,7 +2222,7 @@ export class LGraph
|
||||
let node = LiteGraph.createNode(String(n_info.type), n_info.title)
|
||||
if (!node) {
|
||||
if (LiteGraph.debug)
|
||||
console.log('Node not found or has errors:', n_info.type)
|
||||
console.warn('Node not found or has errors:', n_info.type)
|
||||
|
||||
// in case of error we create a replacement node to avoid losing info
|
||||
node = new LGraphNode('')
|
||||
|
||||
@@ -461,7 +461,7 @@ export class LGraphCanvas
|
||||
}
|
||||
|
||||
const baseFontSize = LiteGraph.NODE_TEXT_SIZE // 14px
|
||||
const dprAdjustment = Math.sqrt(window.devicePixelRatio || 1) //Using sqrt here because higher DPR monitors do not linearily scale the readability of the font, instead they increase the font by some heurisitc, and to approximate we use sqrt to say bascially a DPR of 2 increases the readibility by 40%, 3 by 70%
|
||||
const dprAdjustment = Math.sqrt(window.devicePixelRatio || 1) //Using sqrt here because higher DPR monitors do not linearily scale the readability of the font, instead they increase the font by some heurisitc, and to approximate we use sqrt to say basically a DPR of 2 increases the readability by 40%, 3 by 70%
|
||||
|
||||
// Calculate the zoom level where text becomes unreadable
|
||||
this._lowQualityZoomThreshold =
|
||||
@@ -547,7 +547,7 @@ export class LGraphCanvas
|
||||
linkMarkerShape: LinkMarkerShape = LinkMarkerShape.Circle
|
||||
links_render_mode: number
|
||||
/** Minimum font size in pixels before switching to low quality rendering.
|
||||
* This intializes first and if we cant get the value from the settings we default to 8px
|
||||
* This initializes first and if we can't get the value from the settings we default to 8px
|
||||
*/
|
||||
private _min_font_size_for_lod: number = 8
|
||||
|
||||
@@ -1228,7 +1228,7 @@ export class LGraphCanvas
|
||||
className: 'event'
|
||||
})
|
||||
}
|
||||
// add callback for modifing the menu elements onMenuNodeOutputs
|
||||
// add callback for modifying the menu elements onMenuNodeOutputs
|
||||
const retEntries = node.onMenuNodeOutputs?.(entries)
|
||||
if (retEntries) entries = retEntries
|
||||
|
||||
@@ -3902,7 +3902,7 @@ export class LGraphCanvas
|
||||
for (const item of [...parsed.nodes, ...parsed.reroutes]) {
|
||||
if (item.pos == null)
|
||||
throw new TypeError(
|
||||
'Invalid node encounterd on paste. `pos` was null.'
|
||||
'Invalid node encountered on paste. `pos` was null.'
|
||||
)
|
||||
|
||||
if (item.pos[0] < offsetX) offsetX = item.pos[0]
|
||||
@@ -6406,7 +6406,7 @@ export class LGraphCanvas
|
||||
|
||||
return true
|
||||
}
|
||||
console.log(`failed creating ${nodeNewType}`)
|
||||
console.error(`failed creating ${nodeNewType}`)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -6818,7 +6818,7 @@ export class LGraphCanvas
|
||||
canvas.focus()
|
||||
root_document.body.style.overflow = ''
|
||||
|
||||
// important, if canvas loses focus keys wont be captured
|
||||
// important, if canvas loses focus keys won't be captured
|
||||
setTimeout(() => canvas.focus(), 20)
|
||||
dialog.remove()
|
||||
}
|
||||
@@ -7095,7 +7095,7 @@ export class LGraphCanvas
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// console.warn("cant find slot " + options.slot_from);
|
||||
// console.warn("can't find slot " + options.slot_from);
|
||||
}
|
||||
}
|
||||
if (options.node_to) {
|
||||
@@ -7140,7 +7140,7 @@ export class LGraphCanvas
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// console.warn("cant find slot_nodeTO " + options.slot_from);
|
||||
// console.warn("can't find slot_nodeTO " + options.slot_from);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7478,7 +7478,7 @@ export class LGraphCanvas
|
||||
return dialog
|
||||
}
|
||||
|
||||
// TODO refactor, theer are different dialog, some uses createDialog, some dont
|
||||
// TODO refactor, there are different dialog, some uses createDialog, some dont
|
||||
createDialog(html: string, options: IDialogOptions): IDialog {
|
||||
const def_options = {
|
||||
checkForInput: false,
|
||||
|
||||
@@ -167,8 +167,8 @@ input|output: every connection
|
||||
general properties:
|
||||
+ clip_area: if you render outside the node, it will be clipped
|
||||
+ unsafe_execution: not allowed for safe execution
|
||||
+ skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected
|
||||
+ resizable: if set to false it wont be resizable with the mouse
|
||||
+ skip_repeated_outputs: when adding new outputs, it won't show if there is one already connected
|
||||
+ resizable: if set to false it won't be resizable with the mouse
|
||||
+ widgets_start_y: widgets start at y distance from the top of the node
|
||||
|
||||
flags object:
|
||||
@@ -902,7 +902,7 @@ export class LGraphNode
|
||||
|
||||
if (this.onSerialize?.(o))
|
||||
console.warn(
|
||||
'node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter'
|
||||
"node onSerialize shouldn't return anything, data should be stored in the object pass in the first parameter"
|
||||
)
|
||||
|
||||
return o
|
||||
@@ -1950,7 +1950,7 @@ export class LGraphNode
|
||||
try {
|
||||
this.removeWidget(widget)
|
||||
} catch (error) {
|
||||
console.debug('Failed to remove widget', error)
|
||||
console.error('Failed to remove widget', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2351,7 +2351,7 @@ export class LGraphNode
|
||||
|
||||
/**
|
||||
* returns the output (or input) slot with a given type, -1 if not found
|
||||
* @param input uise inputs instead of outputs
|
||||
* @param input use inputs instead of outputs
|
||||
* @param type the type of the slot to find
|
||||
* @param returnObj if the obj itself wanted
|
||||
* @param preferFreeSlot if we want a free slot (if not found, will return the first of the type anyway)
|
||||
@@ -2583,12 +2583,7 @@ export class LGraphNode
|
||||
if (slotIndex !== undefined)
|
||||
return this.connect(slot, target_node, slotIndex, optsIn?.afterRerouteId)
|
||||
|
||||
console.debug(
|
||||
'[connectByType]: no way to connect type:',
|
||||
target_slotType,
|
||||
'to node:',
|
||||
target_node
|
||||
)
|
||||
// No compatible slot found - connection not possible
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2621,7 +2616,7 @@ export class LGraphNode
|
||||
if (slotIndex !== undefined)
|
||||
return source_node.connect(slotIndex, this, slot, optsIn?.afterRerouteId)
|
||||
|
||||
console.debug(
|
||||
console.error(
|
||||
'[connectByType]: no way to connect type:',
|
||||
source_slotType,
|
||||
'to node:',
|
||||
@@ -2661,7 +2656,7 @@ export class LGraphNode
|
||||
if (!graph) {
|
||||
// could be connected before adding it to a graph
|
||||
// due to link ids being associated with graphs
|
||||
console.log(
|
||||
console.error(
|
||||
"Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."
|
||||
)
|
||||
return null
|
||||
@@ -2672,11 +2667,12 @@ export class LGraphNode
|
||||
slot = this.findOutputSlot(slot)
|
||||
if (slot == -1) {
|
||||
if (LiteGraph.debug)
|
||||
console.log(`Connect: Error, no slot of name ${slot}`)
|
||||
console.error(`Connect: Error, no slot of name ${slot}`)
|
||||
return null
|
||||
}
|
||||
} else if (!outputs || slot >= outputs.length) {
|
||||
if (LiteGraph.debug) console.log('Connect: Error, slot number not found')
|
||||
if (LiteGraph.debug)
|
||||
console.error('Connect: Error, slot number not found')
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2696,7 +2692,7 @@ export class LGraphNode
|
||||
targetIndex = target_node.findInputSlot(target_slot)
|
||||
if (targetIndex == -1) {
|
||||
if (LiteGraph.debug)
|
||||
console.log(`Connect: Error, no slot of name ${targetIndex}`)
|
||||
console.error(`Connect: Error, no slot of name ${targetIndex}`)
|
||||
return null
|
||||
}
|
||||
} else if (target_slot === LiteGraph.EVENT) {
|
||||
@@ -2728,7 +2724,8 @@ export class LGraphNode
|
||||
!target_node.inputs ||
|
||||
targetIndex >= target_node.inputs.length
|
||||
) {
|
||||
if (LiteGraph.debug) console.log('Connect: Error, slot number not found')
|
||||
if (LiteGraph.debug)
|
||||
console.error('Connect: Error, slot number not found')
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2914,7 +2911,7 @@ export class LGraphNode
|
||||
const fromLastFloatingReroute =
|
||||
parentReroute?.floating?.slotType === 'output'
|
||||
|
||||
// Adding from an ouput, or a floating reroute that is NOT the tip of an existing floating chain
|
||||
// Adding from an output, or a floating reroute that is NOT the tip of an existing floating chain
|
||||
if (afterRerouteId == null || !fromLastFloatingReroute) {
|
||||
const link = new LLink(
|
||||
-1,
|
||||
@@ -2955,11 +2952,12 @@ export class LGraphNode
|
||||
slot = this.findOutputSlot(slot)
|
||||
if (slot == -1) {
|
||||
if (LiteGraph.debug)
|
||||
console.log(`Connect: Error, no slot of name ${slot}`)
|
||||
console.error(`Connect: Error, no slot of name ${slot}`)
|
||||
return false
|
||||
}
|
||||
} else if (!this.outputs || slot >= this.outputs.length) {
|
||||
if (LiteGraph.debug) console.log('Connect: Error, slot number not found')
|
||||
if (LiteGraph.debug)
|
||||
console.error('Connect: Error, slot number not found')
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3075,19 +3073,19 @@ export class LGraphNode
|
||||
slot = this.findInputSlot(slot)
|
||||
if (slot == -1) {
|
||||
if (LiteGraph.debug)
|
||||
console.log(`Connect: Error, no slot of name ${slot}`)
|
||||
console.error(`Connect: Error, no slot of name ${slot}`)
|
||||
return false
|
||||
}
|
||||
} else if (!this.inputs || slot >= this.inputs.length) {
|
||||
if (LiteGraph.debug) {
|
||||
console.log('Connect: Error, slot number not found')
|
||||
console.error('Connect: Error, slot number not found')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const input = this.inputs[slot]
|
||||
if (!input) {
|
||||
console.debug('disconnectInput: input not found', slot, this.inputs)
|
||||
console.error('disconnectInput: input not found', slot, this.inputs)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3116,19 +3114,16 @@ export class LGraphNode
|
||||
|
||||
const target_node = graph.getNodeById(link_info.origin_id)
|
||||
if (!target_node) {
|
||||
console.debug(
|
||||
'disconnectInput: target node not found',
|
||||
link_info.origin_id
|
||||
console.error(
|
||||
'disconnectInput: output not found',
|
||||
link_info.origin_slot
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const output = target_node.outputs[link_info.origin_slot]
|
||||
if (!output?.links?.length) {
|
||||
console.debug(
|
||||
'disconnectInput: output not found',
|
||||
link_info.origin_slot
|
||||
)
|
||||
// Output not found - may have been removed
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -241,10 +241,10 @@ export class LiteGraphGlobal {
|
||||
*/
|
||||
do_add_triggers_slots = false
|
||||
|
||||
/** [false!] being events, it is strongly reccomended to use them sequentially, one by one */
|
||||
/** [false!] being events, it is strongly recommended to use them sequentially, one by one */
|
||||
allow_multi_output_for_events = true
|
||||
|
||||
/** [true!] allows to create and connect a ndoe clicking with the third button (wheel) */
|
||||
/** [true!] allows to create and connect a node clicking with the third button (wheel) */
|
||||
middle_click_slot_add_default_node = false
|
||||
|
||||
/** [true!] dragging a link to empty space will open a menu, add from list, search or defaults */
|
||||
@@ -398,8 +398,6 @@ export class LiteGraphGlobal {
|
||||
throw 'Cannot register a simple object, it must be a class with a prototype'
|
||||
base_class.type = type
|
||||
|
||||
if (this.debug) console.log('Node registered:', type)
|
||||
|
||||
const classname = base_class.name
|
||||
|
||||
const pos = type.lastIndexOf('/')
|
||||
@@ -415,7 +413,7 @@ export class LiteGraphGlobal {
|
||||
|
||||
const prev = this.registered_node_types[type]
|
||||
if (prev && this.debug) {
|
||||
console.log('replacing node type:', type)
|
||||
console.warn('replacing node type:', type)
|
||||
}
|
||||
|
||||
this.registered_node_types[type] = base_class
|
||||
@@ -430,7 +428,7 @@ export class LiteGraphGlobal {
|
||||
`LiteGraph node class ${type} has onPropertyChange method, it must be called onPropertyChanged with d at the end`
|
||||
)
|
||||
|
||||
// TODO one would want to know input and ouput :: this would allow through registerNodeAndSlotType to get all the slots types
|
||||
// TODO one would want to know input and output :: this would allow through registerNodeAndSlotType to get all the slots types
|
||||
if (this.auto_load_slot_types) new base_class(base_class.title || 'tmpnode')
|
||||
}
|
||||
|
||||
@@ -524,7 +522,7 @@ export class LiteGraphGlobal {
|
||||
): LGraphNode | null {
|
||||
const base_class = this.registered_node_types[type]
|
||||
if (!base_class) {
|
||||
if (this.debug) console.log(`GraphNode type "${type}" not registered.`)
|
||||
if (this.debug) console.warn(`GraphNode type "${type}" not registered.`)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -637,7 +635,6 @@ export class LiteGraphGlobal {
|
||||
continue
|
||||
|
||||
try {
|
||||
if (this.debug) console.log('Reloading:', src)
|
||||
const dynamicScript = document.createElement('script')
|
||||
dynamicScript.type = 'text/javascript'
|
||||
dynamicScript.src = src
|
||||
@@ -645,11 +642,9 @@ export class LiteGraphGlobal {
|
||||
script_file.remove()
|
||||
} catch (error) {
|
||||
if (this.throw_errors) throw error
|
||||
if (this.debug) console.log('Error while reloading', src)
|
||||
if (this.debug) console.error('Error while reloading', src)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.debug) console.log('Nodes reloaded')
|
||||
}
|
||||
|
||||
// separated just to improve if it doesn't work
|
||||
@@ -749,7 +744,7 @@ export class LiteGraphGlobal {
|
||||
// convert pointerevents to touch event when not available
|
||||
if (sMethod == 'pointer' && !window.PointerEvent) {
|
||||
console.warn("sMethod=='pointer' && !window.PointerEvent")
|
||||
console.log(
|
||||
console.warn(
|
||||
`Converting pointer[${sEvent}] : down move up cancel enter TO touchstart touchmove touchend, etc ..`
|
||||
)
|
||||
switch (sEvent) {
|
||||
@@ -774,7 +769,7 @@ export class LiteGraphGlobal {
|
||||
break
|
||||
}
|
||||
case 'enter': {
|
||||
console.log('debug: Should I send a move event?') // ???
|
||||
// TODO: Determine if a move event should be sent
|
||||
break
|
||||
}
|
||||
// case "over": case "out": not used at now
|
||||
|
||||
@@ -906,7 +906,6 @@ export class LinkConnector {
|
||||
if (connectingTo === 'output') {
|
||||
// Dropping new output link
|
||||
const output = node.findOutputByType(firstLink.fromSlot.type)?.slot
|
||||
console.debug('out', node, output, firstLink.fromSlot)
|
||||
if (output === undefined) {
|
||||
console.warn(
|
||||
`Could not find slot for link type: [${firstLink.fromSlot.type}].`
|
||||
@@ -918,7 +917,6 @@ export class LinkConnector {
|
||||
} else if (connectingTo === 'input') {
|
||||
// Dropping new input link
|
||||
const input = node.findInputByType(firstLink.fromSlot.type)?.slot
|
||||
console.debug('in', node, input, firstLink.fromSlot)
|
||||
if (input === undefined) {
|
||||
console.warn(
|
||||
`Could not find slot for link type: [${firstLink.fromSlot.type}].`
|
||||
|
||||
@@ -57,7 +57,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
#id: ExecutionId
|
||||
|
||||
/**
|
||||
* The path to the acutal node through subgraph instances, represented as a list of all subgraph node IDs (instances),
|
||||
* The path to the actual node through subgraph instances, represented as a list of all subgraph node IDs (instances),
|
||||
* followed by the actual original node ID within the subgraph. Each segment is separated by `:`.
|
||||
*
|
||||
* e.g. `1:2:3`:
|
||||
@@ -104,7 +104,7 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
readonly subgraphNodePath: readonly NodeId[],
|
||||
/** A flattened map of all DTOs in this node network. Subgraph instances have been expanded into their inner nodes. */
|
||||
readonly nodesByExecutionId: Map<ExecutionId, ExecutableLGraphNode>,
|
||||
/** The actual subgraph instance that contains this node, otherise undefined. */
|
||||
/** The actual subgraph instance that contains this node, otherwise undefined. */
|
||||
readonly subgraphNode?: SubgraphNode
|
||||
) {
|
||||
if (!node.graph) throw new NullGraphError()
|
||||
@@ -271,9 +271,9 @@ export class ExecutableNodeDTO implements ExecutableLGraphNode {
|
||||
// Bypass nodes by finding first input with matching type
|
||||
const matchingIndex = this.#getBypassSlotIndex(slot, type)
|
||||
|
||||
// No input types match
|
||||
// No input types match - bypass not possible
|
||||
if (matchingIndex === -1) {
|
||||
console.debug(
|
||||
console.warn(
|
||||
`[ExecutableNodeDTO.resolveOutput] No input types match type [${type}] for id [${this.id}] slot [${slot}]`,
|
||||
this
|
||||
)
|
||||
|
||||
@@ -175,8 +175,6 @@ export class SubgraphInput extends SubgraphSlot {
|
||||
}
|
||||
|
||||
widgets.push(widget)
|
||||
} else {
|
||||
console.debug('No input found on link id', linkId, link)
|
||||
}
|
||||
}
|
||||
return widgets
|
||||
|
||||
@@ -188,7 +188,7 @@ export class SubgraphInputNode
|
||||
|
||||
const subgraphInput = this.slots.at(subgraphInputIndex)
|
||||
if (!subgraphInput) {
|
||||
console.debug(
|
||||
console.warn(
|
||||
'disconnectNodeInput: subgraphInput not found',
|
||||
this,
|
||||
subgraphInputIndex
|
||||
@@ -201,7 +201,7 @@ export class SubgraphInputNode
|
||||
if (index !== -1) {
|
||||
subgraphInput.linkIds.splice(index, 1)
|
||||
} else {
|
||||
console.debug(
|
||||
console.warn(
|
||||
'disconnectNodeInput: link ID not found in subgraphInput linkIds',
|
||||
link.id
|
||||
)
|
||||
|
||||
@@ -430,7 +430,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
const inputSlot = this.subgraph.inputNode.slots[slot]
|
||||
const innerLinks = inputSlot.getLinks()
|
||||
if (innerLinks.length === 0) {
|
||||
console.debug(
|
||||
console.warn(
|
||||
`[SubgraphNode.resolveSubgraphInputLinks] No inner links found for input slot [${slot}] ${inputSlot.name}`,
|
||||
this
|
||||
)
|
||||
@@ -447,9 +447,10 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
|
||||
resolveSubgraphOutputLink(slot: number): ResolvedConnection | undefined {
|
||||
const outputSlot = this.subgraph.outputNode.slots[slot]
|
||||
const innerLink = outputSlot.getLinks().at(0)
|
||||
if (innerLink) return innerLink.resolve(this.subgraph)
|
||||
|
||||
console.debug(
|
||||
if (innerLink) {
|
||||
return innerLink.resolve(this.subgraph)
|
||||
}
|
||||
console.warn(
|
||||
`[SubgraphNode.resolveSubgraphOutputLink] No inner link found for output slot [${slot}] ${outputSlot.name}`,
|
||||
this
|
||||
)
|
||||
|
||||
@@ -116,7 +116,7 @@ export function getBoundaryLinks(
|
||||
|
||||
const resolved = LLink.resolve(input.link, graph)
|
||||
if (!resolved) {
|
||||
console.debug(`Failed to resolve link ID [${input.link}]`)
|
||||
console.warn(`Failed to resolve link ID [${input.link}]`)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ export interface SubgraphIO extends SubgraphIOShared {
|
||||
id: UUID
|
||||
/** The data type this slot uses. Unlike nodes, this does not support legacy numeric types. */
|
||||
type: string
|
||||
/** Links connected to this slot, or `undefined` if not connected. An ouptut slot should only ever have one link. */
|
||||
/** Links connected to this slot, or `undefined` if not connected. An output slot should only ever have one link. */
|
||||
linkIds?: LinkId[]
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +426,6 @@ describe('ExecutableNodeDTO Integration', () => {
|
||||
describe('ExecutableNodeDTO Scale Testing', () => {
|
||||
it('should create DTOs at scale', () => {
|
||||
const graph = new LGraph()
|
||||
const startTime = performance.now()
|
||||
const dtos: ExecutableNodeDTO[] = []
|
||||
|
||||
// Create DTOs to test performance
|
||||
@@ -440,16 +439,11 @@ describe('ExecutableNodeDTO Scale Testing', () => {
|
||||
dtos.push(dto)
|
||||
}
|
||||
|
||||
const endTime = performance.now()
|
||||
const duration = endTime - startTime
|
||||
|
||||
expect(dtos).toHaveLength(1000)
|
||||
// Test deterministic properties instead of flaky timing
|
||||
expect(dtos[0].id).toBe('parent:0')
|
||||
expect(dtos[999].id).toBe('parent:999')
|
||||
expect(dtos.every((dto, i) => dto.id === `parent:${i}`)).toBe(true)
|
||||
|
||||
console.log(`Created 1000 DTOs in ${duration.toFixed(2)}ms`)
|
||||
})
|
||||
|
||||
it('should handle complex path generation correctly', () => {
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('SubgraphConversion', () => {
|
||||
expect(graph.nodes.length).toBe(2)
|
||||
expect(graph.links.size).toBe(1)
|
||||
})
|
||||
it('Should merge boundry links', () => {
|
||||
it('Should merge boundary links', () => {
|
||||
const subgraph = createTestSubgraph({
|
||||
inputs: [{ name: 'value', type: 'number' }],
|
||||
outputs: [{ name: 'value', type: 'number' }]
|
||||
|
||||
@@ -134,7 +134,7 @@ describe('SubgraphSlot visual feedback', () => {
|
||||
expect(globalAlphaValues).toContain(0.4)
|
||||
})
|
||||
|
||||
// "not implmeneted yet"
|
||||
// "not implemented yet"
|
||||
// it("should render slots with full opacity when dragging between compatible SubgraphInput and SubgraphOutput", () => {
|
||||
// const subgraph = createTestSubgraph()
|
||||
|
||||
|
||||
@@ -57,12 +57,10 @@ export const Default: Story = {
|
||||
render: (args) => ({
|
||||
components: { AssetBrowserModal },
|
||||
setup() {
|
||||
const onAssetSelect = (asset: AssetDisplayItem) => {
|
||||
console.log('Selected asset:', asset)
|
||||
}
|
||||
const onClose = () => {
|
||||
console.log('Modal closed')
|
||||
const onAssetSelect = (_asset: AssetDisplayItem) => {
|
||||
// Asset selection handler for story
|
||||
}
|
||||
const onClose = () => {}
|
||||
|
||||
return {
|
||||
...args,
|
||||
@@ -97,11 +95,11 @@ export const SingleAssetType: Story = {
|
||||
render: (args) => ({
|
||||
components: { AssetBrowserModal },
|
||||
setup() {
|
||||
const onAssetSelect = (asset: AssetDisplayItem) => {
|
||||
console.log('Selected asset:', asset)
|
||||
const onAssetSelect = (_asset: AssetDisplayItem) => {
|
||||
// Asset selection handler for story
|
||||
}
|
||||
const onClose = () => {
|
||||
console.log('Modal closed')
|
||||
// Modal close handler for story
|
||||
}
|
||||
|
||||
// Create assets with only one type (checkpoints)
|
||||
@@ -146,11 +144,11 @@ export const NoLeftPanel: Story = {
|
||||
render: (args) => ({
|
||||
components: { AssetBrowserModal },
|
||||
setup() {
|
||||
const onAssetSelect = (asset: AssetDisplayItem) => {
|
||||
console.log('Selected asset:', asset)
|
||||
const onAssetSelect = (_asset: AssetDisplayItem) => {
|
||||
// Asset selection handler for story
|
||||
}
|
||||
const onClose = () => {
|
||||
console.log('Modal closed')
|
||||
// Modal close handler for story
|
||||
}
|
||||
|
||||
return { ...args, onAssetSelect, onClose, assets: mockAssets }
|
||||
|
||||
@@ -198,10 +198,6 @@ export function useAssetBrowser(assets: AssetItem[] = []) {
|
||||
assetId: string,
|
||||
onSelect?: (filename: string) => void
|
||||
): Promise<void> {
|
||||
if (import.meta.env.DEV) {
|
||||
console.debug('Asset selected:', assetId)
|
||||
}
|
||||
|
||||
if (!onSelect) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ const DialogDemoComponent = {
|
||||
}
|
||||
|
||||
const handleAssetSelected = (assetPath: string) => {
|
||||
console.log('Asset selected:', assetPath)
|
||||
alert(`Selected asset: ${assetPath}`)
|
||||
isDialogOpen.value = false // Auto-close like the real composable
|
||||
}
|
||||
|
||||
@@ -986,7 +986,7 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
name: 'Auto Save',
|
||||
type: 'combo',
|
||||
options: ['off', 'after delay'], // Room for other options like on focus change, tab change, window change
|
||||
defaultValue: 'off', // Popular requst by users (https://github.com/Comfy-Org/ComfyUI_frontend/issues/1584#issuecomment-2536610154)
|
||||
defaultValue: 'off', // Popular request by users (https://github.com/Comfy-Org/ComfyUI_frontend/issues/1584#issuecomment-2536610154)
|
||||
versionAdded: '1.16.0'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -195,7 +195,7 @@ defineExpose({
|
||||
bottom: calc(
|
||||
var(--sidebar-width) * 2 + var(--sidebar-icon-size) / 2 -
|
||||
var(--whats-new-popup-bottom)
|
||||
); /* Position to center of help center icon (2 icons below + half icon height for center - whats new popup bottom position ) */
|
||||
); /* Position to center of help center icon (2 icons below + half icon height for center - what's new popup bottom position ) */
|
||||
}
|
||||
|
||||
/* Sidebar positioning classes applied by parent */
|
||||
|
||||
@@ -87,7 +87,6 @@ export class ComfyWorkflow extends UserFile {
|
||||
}
|
||||
|
||||
// Note: originalContent is populated by super.load()
|
||||
console.debug('load and start tracking of workflow', this.path)
|
||||
this.changeTracker = markRaw(
|
||||
new ChangeTracker(
|
||||
this,
|
||||
@@ -98,7 +97,6 @@ export class ComfyWorkflow extends UserFile {
|
||||
}
|
||||
|
||||
override unload(): void {
|
||||
console.debug('unload workflow', this.path)
|
||||
this.changeTracker = null
|
||||
super.unload()
|
||||
}
|
||||
@@ -302,7 +300,6 @@ export const useWorkflowStore = defineStore('workflow', () => {
|
||||
const loadedWorkflow = await workflow.load()
|
||||
activeWorkflow.value = loadedWorkflow
|
||||
comfyApp.canvas.bg_tint = loadedWorkflow.tintCanvasBg
|
||||
console.debug('[workflowStore] open workflow', workflow.path)
|
||||
return loadedWorkflow
|
||||
}
|
||||
|
||||
@@ -379,7 +376,6 @@ export const useWorkflowStore = defineStore('workflow', () => {
|
||||
} else {
|
||||
workflow.unload()
|
||||
}
|
||||
console.debug('[workflowStore] close workflow', workflow.path)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -219,7 +219,7 @@ const zSubgraphIO = zNodeInput.extend({
|
||||
id: z.string().uuid(),
|
||||
/** The data type this slot uses. Unlike nodes, this does not support legacy numeric types. */
|
||||
type: z.string(),
|
||||
/** Links connected to this slot, or `undefined` if not connected. An ouptut slot should only ever have one link. */
|
||||
/** Links connected to this slot, or `undefined` if not connected. An output slot should only ever have one link. */
|
||||
linkIds: z.array(z.number()).optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -211,7 +211,8 @@ const isSelected = computed(() => {
|
||||
})
|
||||
|
||||
// Use execution state composable
|
||||
const { executing, progress } = useNodeExecutionState(() => nodeData.id)
|
||||
const nodeLocatorId = computed(() => getLocatorIdFromNodeData(nodeData))
|
||||
const { executing, progress } = useNodeExecutionState(nodeLocatorId)
|
||||
|
||||
// Direct access to execution store for error state
|
||||
const executionStore = useExecutionStore()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:data-testid="`node-header-${nodeData?.id || ''}`"
|
||||
@dblclick="handleDoubleClick"
|
||||
>
|
||||
<div class="flex items-center justify-between relative">
|
||||
<div class="flex items-center justify-between gap-2.5 relative">
|
||||
<!-- Collapse/Expand Button -->
|
||||
<button
|
||||
v-show="!readonly"
|
||||
@@ -43,24 +43,22 @@
|
||||
data-testid="node-pin-indicator"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!readonly" class="flex items-center lod-toggle shrink-0">
|
||||
<IconButton
|
||||
v-if="isSubgraphNode"
|
||||
size="sm"
|
||||
type="transparent"
|
||||
class="text-stone-200 dark-theme:text-slate-300"
|
||||
data-testid="subgraph-enter-button"
|
||||
title="Enter Subgraph"
|
||||
@click.stop="handleEnterSubgraph"
|
||||
@dblclick.stop
|
||||
>
|
||||
<i class="pi pi-external-link"></i>
|
||||
</IconButton>
|
||||
</div>
|
||||
<LODFallback />
|
||||
</div>
|
||||
|
||||
<!-- Title Buttons -->
|
||||
<div v-if="!readonly" class="flex items-center lod-toggle">
|
||||
<IconButton
|
||||
v-if="isSubgraphNode"
|
||||
size="sm"
|
||||
type="transparent"
|
||||
class="text-stone-200 dark-theme:text-slate-300"
|
||||
data-testid="subgraph-enter-button"
|
||||
title="Enter Subgraph"
|
||||
@click.stop="handleEnterSubgraph"
|
||||
@dblclick.stop
|
||||
>
|
||||
<i class="pi pi-external-link"></i>
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
:widget="widget.simplified"
|
||||
:model-value="widget.value"
|
||||
:readonly="readonly"
|
||||
:node-id="nodeData?.id != null ? String(nodeData.id) : ''"
|
||||
class="flex-1"
|
||||
@update:model-value="widget.updateHandler"
|
||||
/>
|
||||
|
||||
@@ -13,8 +13,8 @@ import { cn } from '@/utils/tailwindUtil'
|
||||
*
|
||||
*
|
||||
* IMPORTANT: this escape is needed for many reason due to primevue's directive tooltip system.
|
||||
* We cannot use PT to conditonally render the tooltips because the entire PT object only run
|
||||
* once during the intialization of the directive not every mount/unmount.
|
||||
* We cannot use PT to conditionally render the tooltips because the entire PT object only run
|
||||
* once during the initialization of the directive not every mount/unmount.
|
||||
* Once the directive is constructed its no longer reactive in the traditional sense.
|
||||
* We have to use something non destructive like mouseevents to dismiss the tooltip.
|
||||
*
|
||||
|
||||
@@ -9,27 +9,27 @@ import { useExecutionStore } from '@/stores/executionStore'
|
||||
* Provides reactive access to execution state and progress for a specific node
|
||||
* by injecting execution data from the parent GraphCanvas provider.
|
||||
*
|
||||
* @param nodeIdMaybe - The ID of the node to track execution state for
|
||||
* @param nodeLocatorIdMaybe - Locator ID (root or subgraph scoped) of the node to track
|
||||
* @returns Object containing reactive execution state and progress
|
||||
*/
|
||||
export const useNodeExecutionState = (
|
||||
nodeIdMaybe: MaybeRefOrGetter<string>
|
||||
nodeLocatorIdMaybe: MaybeRefOrGetter<string | undefined>
|
||||
) => {
|
||||
const nodeId = toValue(nodeIdMaybe)
|
||||
const { uniqueExecutingNodeIdStrings, nodeProgressStates } =
|
||||
storeToRefs(useExecutionStore())
|
||||
const locatorId = computed(() => toValue(nodeLocatorIdMaybe) ?? '')
|
||||
const { nodeLocationProgressStates } = storeToRefs(useExecutionStore())
|
||||
|
||||
const executing = computed(() => {
|
||||
return uniqueExecutingNodeIdStrings.value.has(nodeId)
|
||||
const progressState = computed(() => {
|
||||
const id = locatorId.value
|
||||
return id ? nodeLocationProgressStates.value[id] : undefined
|
||||
})
|
||||
|
||||
const executing = computed(() => progressState.value?.state === 'running')
|
||||
|
||||
const progress = computed(() => {
|
||||
const state = nodeProgressStates.value[nodeId]
|
||||
return state?.max > 0 ? state.value / state.max : undefined
|
||||
const state = progressState.value
|
||||
return state && state.max > 0 ? state.value / state.max : undefined
|
||||
})
|
||||
|
||||
const progressState = computed(() => nodeProgressStates.value[nodeId])
|
||||
|
||||
const progressPercentage = computed(() => {
|
||||
const prog = progress.value
|
||||
return prog !== undefined ? Math.round(prog * 100) : undefined
|
||||
|
||||
@@ -86,7 +86,6 @@ describe('WidgetInputNumberSlider Value Binding', () => {
|
||||
it('renders input field', () => {
|
||||
const widget = createMockWidget(5)
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
console.log(wrapper.html())
|
||||
|
||||
expect(wrapper.find('input[inputmode="numeric"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ export const useImageUploadWidget = () => {
|
||||
}
|
||||
|
||||
// On load if we have a value then render the image
|
||||
// The value isnt set immediately so we need to wait a moment
|
||||
// The value isn't set immediately so we need to wait a moment
|
||||
// No change callbacks seem to be fired on initial setting of the value
|
||||
requestAnimationFrame(() => {
|
||||
nodeOutputStore.setNodeOutputs(node, fileComboWidget.value, {
|
||||
|
||||
@@ -524,7 +524,7 @@ export class ComfyApi extends EventTarget {
|
||||
if (msg.data.sid) {
|
||||
const clientId = msg.data.sid
|
||||
this.clientId = clientId
|
||||
window.name = clientId // use window name so it isnt reused when duplicating tabs
|
||||
window.name = clientId // use window name so it isn't reused when duplicating tabs
|
||||
sessionStorage.setItem('clientId', clientId) // store in session storage so duplicate tab can load correct workflow
|
||||
}
|
||||
this.dispatchCustomEvent('status', msg.data.status ?? null)
|
||||
|
||||
@@ -69,9 +69,6 @@ export const useRegistrySearchGateway = (): NodePackSearchProvider => {
|
||||
const timeSinceLastAttempt =
|
||||
Date.now() - providerState.lastAttempt.getTime()
|
||||
if (timeSinceLastAttempt > CIRCUIT_BREAKER_TIMEOUT) {
|
||||
console.info(
|
||||
`Retrying ${providerState.name} provider after circuit breaker timeout`
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
KeybindingImpl,
|
||||
useKeybindingStore
|
||||
} from '@/stores/keybindingStore'
|
||||
import { migrateKeybindings } from '@/utils/keybindingMigration'
|
||||
|
||||
export const useKeybindingService = () => {
|
||||
const keybindingStore = useKeybindingStore()
|
||||
@@ -51,7 +52,7 @@ export const useKeybindingService = () => {
|
||||
if (keybinding && keybinding.targetElementId !== 'graph-canvas') {
|
||||
// Special handling for Escape key - let dialogs handle it first
|
||||
if (
|
||||
event.key === 'Escape' &&
|
||||
event.code === 'Escape' &&
|
||||
!event.ctrlKey &&
|
||||
!event.altKey &&
|
||||
!event.metaKey
|
||||
@@ -88,7 +89,7 @@ export const useKeybindingService = () => {
|
||||
}
|
||||
|
||||
// Escape key: close the first open modal found, and all dialogs
|
||||
if (event.key === 'Escape') {
|
||||
if (event.code === 'Escape') {
|
||||
const modals = document.querySelectorAll<HTMLElement>('.comfy-modal')
|
||||
for (const modal of modals) {
|
||||
const modalDisplay = window
|
||||
@@ -111,14 +112,41 @@ export const useKeybindingService = () => {
|
||||
}
|
||||
}
|
||||
|
||||
function registerUserKeybindings() {
|
||||
// Unset bindings first as new bindings might conflict with default bindings.
|
||||
async function registerUserKeybindings() {
|
||||
// Load user keybindings from settings
|
||||
const unsetBindings = settingStore.get('Comfy.Keybinding.UnsetBindings')
|
||||
for (const keybinding of unsetBindings) {
|
||||
const newBindings = settingStore.get('Comfy.Keybinding.NewBindings')
|
||||
|
||||
// Migrate keybindings from old event.key format to new event.code format
|
||||
const migratedUnset = migrateKeybindings(unsetBindings)
|
||||
const migratedNew = migrateKeybindings(newBindings)
|
||||
|
||||
// Save migrated keybindings back to settings if any migration occurred
|
||||
if (migratedUnset.migrated) {
|
||||
await settingStore.set(
|
||||
'Comfy.Keybinding.UnsetBindings',
|
||||
migratedUnset.keybindings
|
||||
)
|
||||
console.warn(
|
||||
'[Keybindings] Migrated unset keybindings to event.code format'
|
||||
)
|
||||
}
|
||||
|
||||
if (migratedNew.migrated) {
|
||||
await settingStore.set(
|
||||
'Comfy.Keybinding.NewBindings',
|
||||
migratedNew.keybindings
|
||||
)
|
||||
console.warn(
|
||||
'[Keybindings] Migrated custom keybindings to event.code format'
|
||||
)
|
||||
}
|
||||
|
||||
// Unset bindings first as new bindings might conflict with default bindings.
|
||||
for (const keybinding of migratedUnset.keybindings) {
|
||||
keybindingStore.unsetKeybinding(new KeybindingImpl(keybinding))
|
||||
}
|
||||
const newBindings = settingStore.get('Comfy.Keybinding.NewBindings')
|
||||
for (const keybinding of newBindings) {
|
||||
for (const keybinding of migratedNew.keybindings) {
|
||||
keybindingStore.addUserKeybinding(new KeybindingImpl(keybinding))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface ElectronDownload
|
||||
status?: DownloadStatus
|
||||
}
|
||||
|
||||
/** Electron donwloads store handler */
|
||||
/** Electron downloads store handler */
|
||||
export const useElectronDownloadStore = defineStore('downloads', () => {
|
||||
const downloads = ref<ElectronDownload[]>([])
|
||||
const { DownloadManager } = electronAPI()
|
||||
|
||||
@@ -404,10 +404,6 @@ export const useExecutionStore = defineStore('execution', () => {
|
||||
...queuedPrompt.nodes
|
||||
}
|
||||
queuedPrompt.workflow = workflow
|
||||
|
||||
console.debug(
|
||||
`queued task ${id} with ${Object.values(queuedPrompt.nodes).length} nodes`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,7 +65,7 @@ export const useExtensionStore = defineStore('extension', () => {
|
||||
}
|
||||
|
||||
if (disabledExtensionNames.value.has(extension.name)) {
|
||||
console.log(`Extension ${extension.name} is disabled.`)
|
||||
console.warn(`Extension ${extension.name} is disabled.`)
|
||||
}
|
||||
|
||||
extensionByName.value[extension.name] = markRaw(extension)
|
||||
|
||||
@@ -44,7 +44,7 @@ export class KeyComboImpl implements KeyCombo {
|
||||
|
||||
static fromEvent(event: KeyboardEvent) {
|
||||
return new KeyComboImpl({
|
||||
key: event.key,
|
||||
key: event.code,
|
||||
ctrl: event.ctrlKey || event.metaKey,
|
||||
alt: event.altKey,
|
||||
shift: event.shiftKey
|
||||
@@ -75,7 +75,16 @@ export class KeyComboImpl implements KeyCombo {
|
||||
}
|
||||
|
||||
get isModifier(): boolean {
|
||||
return ['Control', 'Meta', 'Alt', 'Shift'].includes(this.key)
|
||||
return [
|
||||
'ControlLeft',
|
||||
'ControlRight',
|
||||
'MetaLeft',
|
||||
'MetaRight',
|
||||
'AltLeft',
|
||||
'AltRight',
|
||||
'ShiftLeft',
|
||||
'ShiftRight'
|
||||
].includes(this.key)
|
||||
}
|
||||
|
||||
get modifierCount(): number {
|
||||
@@ -106,9 +115,95 @@ export class KeyComboImpl implements KeyCombo {
|
||||
if (this.shift) {
|
||||
sequences.push('Shift')
|
||||
}
|
||||
sequences.push(this.key)
|
||||
sequences.push(this.getDisplayKey())
|
||||
return sequences
|
||||
}
|
||||
|
||||
getDisplayKey(): string {
|
||||
// Convert key codes to display names
|
||||
const keyMap: Record<string, string> = {
|
||||
// Letters
|
||||
KeyA: 'A',
|
||||
KeyB: 'B',
|
||||
KeyC: 'C',
|
||||
KeyD: 'D',
|
||||
KeyE: 'E',
|
||||
KeyF: 'F',
|
||||
KeyG: 'G',
|
||||
KeyH: 'H',
|
||||
KeyI: 'I',
|
||||
KeyJ: 'J',
|
||||
KeyK: 'K',
|
||||
KeyL: 'L',
|
||||
KeyM: 'M',
|
||||
KeyN: 'N',
|
||||
KeyO: 'O',
|
||||
KeyP: 'P',
|
||||
KeyQ: 'Q',
|
||||
KeyR: 'R',
|
||||
KeyS: 'S',
|
||||
KeyT: 'T',
|
||||
KeyU: 'U',
|
||||
KeyV: 'V',
|
||||
KeyW: 'W',
|
||||
KeyX: 'X',
|
||||
KeyY: 'Y',
|
||||
KeyZ: 'Z',
|
||||
// Numbers
|
||||
Digit0: '0',
|
||||
Digit1: '1',
|
||||
Digit2: '2',
|
||||
Digit3: '3',
|
||||
Digit4: '4',
|
||||
Digit5: '5',
|
||||
Digit6: '6',
|
||||
Digit7: '7',
|
||||
Digit8: '8',
|
||||
Digit9: '9',
|
||||
// Function keys
|
||||
F1: 'F1',
|
||||
F2: 'F2',
|
||||
F3: 'F3',
|
||||
F4: 'F4',
|
||||
F5: 'F5',
|
||||
F6: 'F6',
|
||||
F7: 'F7',
|
||||
F8: 'F8',
|
||||
F9: 'F9',
|
||||
F10: 'F10',
|
||||
F11: 'F11',
|
||||
F12: 'F12',
|
||||
// Special keys
|
||||
Space: 'Space',
|
||||
Enter: 'Enter',
|
||||
Tab: 'Tab',
|
||||
Escape: 'Escape',
|
||||
Backspace: 'Backspace',
|
||||
Delete: 'Delete',
|
||||
ArrowUp: '↑',
|
||||
ArrowDown: '↓',
|
||||
ArrowLeft: '←',
|
||||
ArrowRight: '→',
|
||||
Home: 'Home',
|
||||
End: 'End',
|
||||
PageUp: 'PageUp',
|
||||
PageDown: 'PageDown',
|
||||
Insert: 'Insert',
|
||||
// Punctuation
|
||||
Minus: '-',
|
||||
Equal: '=',
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Backslash: '\\',
|
||||
Semicolon: ';',
|
||||
Quote: "'",
|
||||
Backquote: '`',
|
||||
Comma: ',',
|
||||
Period: '.',
|
||||
Slash: '/'
|
||||
}
|
||||
return keyMap[this.key] || this.key
|
||||
}
|
||||
}
|
||||
|
||||
export const useKeybindingStore = defineStore('keybinding', () => {
|
||||
|
||||
@@ -151,7 +151,6 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
|
||||
/** @todo Refreshes Electron tasks only. */
|
||||
const refreshDesktopTasks = async () => {
|
||||
isRefreshing.value = true
|
||||
console.log('Refreshing desktop tasks')
|
||||
await electron.Validation.validateInstallation(processUpdate)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export class ExecutableGroupNodeChildDTO extends ExecutableNodeDTO {
|
||||
subgraphNodePath: readonly NodeId[],
|
||||
/** A flattened map of all DTOs in this node network. Subgraph instances have been expanded into their inner nodes. */
|
||||
nodesByExecutionId: Map<ExecutionId, ExecutableLGraphNode>,
|
||||
/** The actual subgraph instance that contains this node, otherise undefined. */
|
||||
/** The actual subgraph instance that contains this node, otherwise undefined. */
|
||||
subgraphNode?: SubgraphNode | undefined,
|
||||
groupNodeHandler?: GroupNodeHandler
|
||||
) {
|
||||
|
||||