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)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Ref } from 'vue'
|
||||
|
||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
||||
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
||||
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
|
||||
@@ -71,7 +71,7 @@ export function useSelectionToolboxPosition(
|
||||
visible.value = true
|
||||
|
||||
// Get bounds for all selected items
|
||||
const allBounds: Rect[] = []
|
||||
const allBounds: ReadOnlyRect[] = []
|
||||
for (const item of selectableItems) {
|
||||
// Skip items without valid IDs
|
||||
if (item.id == null) continue
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Point, Rect } from './interfaces'
|
||||
import type { Point, ReadOnlyRect, Rect } from './interfaces'
|
||||
import { EaseFunction, Rectangle } from './litegraph'
|
||||
|
||||
export interface DragAndScaleState {
|
||||
@@ -188,7 +188,10 @@ export class DragAndScale {
|
||||
* Fits the view to the specified bounds.
|
||||
* @param bounds The bounds to fit the view to, defined by a rectangle.
|
||||
*/
|
||||
fitToBounds(bounds: Rect, { zoom = 0.75 }: { zoom?: number } = {}): void {
|
||||
fitToBounds(
|
||||
bounds: ReadOnlyRect,
|
||||
{ zoom = 0.75 }: { zoom?: number } = {}
|
||||
): void {
|
||||
const cw = this.element.width / window.devicePixelRatio
|
||||
const ch = this.element.height / window.devicePixelRatio
|
||||
let targetScale = this.scale
|
||||
@@ -220,7 +223,7 @@ export class DragAndScale {
|
||||
* @param bounds The bounds to animate the view to, defined by a rectangle.
|
||||
*/
|
||||
animateToBounds(
|
||||
bounds: Readonly<Rect | Rectangle>,
|
||||
bounds: ReadOnlyRect,
|
||||
setDirty: () => void,
|
||||
{
|
||||
duration = 350,
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
SUBGRAPH_INPUT_ID,
|
||||
SUBGRAPH_OUTPUT_ID
|
||||
} from '@/lib/litegraph/src/constants'
|
||||
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||
import type { UUID } from '@/lib/litegraph/src/utils/uuid'
|
||||
import { createUuidv4, zeroUuid } from '@/lib/litegraph/src/utils/uuid'
|
||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||
@@ -275,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)
|
||||
@@ -533,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()
|
||||
}
|
||||
}
|
||||
@@ -1129,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.
|
||||
@@ -1168,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
|
||||
@@ -1230,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)
|
||||
}
|
||||
@@ -1627,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)
|
||||
@@ -1708,12 +1696,7 @@ export class LGraph
|
||||
...subgraphNode.subgraph.groups
|
||||
].map((p: { pos: Point; size?: Size }): HasBoundingRect => {
|
||||
return {
|
||||
boundingRect: new Rectangle(
|
||||
p.pos[0],
|
||||
p.pos[1],
|
||||
p.size?.[0] ?? 0,
|
||||
p.size?.[1] ?? 0
|
||||
)
|
||||
boundingRect: [p.pos[0], p.pos[1], p.size?.[0] ?? 0, p.size?.[1] ?? 0]
|
||||
}
|
||||
})
|
||||
const bounds = createBounds(positionables) ?? [0, 0, 0, 0]
|
||||
@@ -2239,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('')
|
||||
|
||||
@@ -47,6 +47,8 @@ import type {
|
||||
NullableProperties,
|
||||
Point,
|
||||
Positionable,
|
||||
ReadOnlyPoint,
|
||||
ReadOnlyRect,
|
||||
Rect,
|
||||
Size
|
||||
} from './interfaces'
|
||||
@@ -234,11 +236,11 @@ export class LGraphCanvas
|
||||
implements CustomEventDispatcher<LGraphCanvasEventMap>
|
||||
{
|
||||
// Optimised buffers used during rendering
|
||||
static #temp = [0, 0, 0, 0] satisfies Rect
|
||||
static #temp_vec2 = [0, 0] satisfies Point
|
||||
static #tmp_area = [0, 0, 0, 0] satisfies Rect
|
||||
static #margin_area = [0, 0, 0, 0] satisfies Rect
|
||||
static #link_bounding = [0, 0, 0, 0] satisfies Rect
|
||||
static #temp = new Float32Array(4)
|
||||
static #temp_vec2 = new Float32Array(2)
|
||||
static #tmp_area = new Float32Array(4)
|
||||
static #margin_area = new Float32Array(4)
|
||||
static #link_bounding = new Float32Array(4)
|
||||
|
||||
static DEFAULT_BACKGROUND_IMAGE =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII='
|
||||
@@ -459,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 =
|
||||
@@ -545,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
|
||||
|
||||
@@ -626,7 +628,7 @@ export class LGraphCanvas
|
||||
dirty_area?: Rect | null
|
||||
/** @deprecated Unused */
|
||||
node_in_panel?: LGraphNode | null
|
||||
last_mouse: Point = [0, 0]
|
||||
last_mouse: ReadOnlyPoint = [0, 0]
|
||||
last_mouseclick: number = 0
|
||||
graph: LGraph | Subgraph | null
|
||||
get _graph(): LGraph | Subgraph {
|
||||
@@ -1226,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
|
||||
|
||||
@@ -2632,7 +2634,7 @@ export class LGraphCanvas
|
||||
pointer: CanvasPointer,
|
||||
node?: LGraphNode | undefined
|
||||
): void {
|
||||
const dragRect: [number, number, number, number] = [0, 0, 0, 0]
|
||||
const dragRect = new Float32Array(4)
|
||||
|
||||
dragRect[0] = e.canvasX
|
||||
dragRect[1] = e.canvasY
|
||||
@@ -3172,7 +3174,7 @@ export class LGraphCanvas
|
||||
|
||||
LGraphCanvas.active_canvas = this
|
||||
this.adjustMouseEvent(e)
|
||||
const mouse: Point = [e.clientX, e.clientY]
|
||||
const mouse: ReadOnlyPoint = [e.clientX, e.clientY]
|
||||
this.mouse[0] = mouse[0]
|
||||
this.mouse[1] = mouse[1]
|
||||
const delta = [mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1]]
|
||||
@@ -3900,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]
|
||||
@@ -4075,10 +4077,7 @@ export class LGraphCanvas
|
||||
this.setDirty(true)
|
||||
}
|
||||
|
||||
#handleMultiSelect(
|
||||
e: CanvasPointerEvent,
|
||||
dragRect: [number, number, number, number]
|
||||
) {
|
||||
#handleMultiSelect(e: CanvasPointerEvent, dragRect: Float32Array) {
|
||||
// Process drag
|
||||
// Convert Point pair (pos, offset) to Rect
|
||||
const { graph, selectedItems, subgraph } = this
|
||||
@@ -4733,47 +4732,32 @@ export class LGraphCanvas
|
||||
for (const renderLink of renderLinks) {
|
||||
const {
|
||||
fromSlot,
|
||||
fromPos: pos
|
||||
// fromDirection,
|
||||
// dragDirection
|
||||
fromPos: pos,
|
||||
fromDirection,
|
||||
dragDirection
|
||||
} = renderLink
|
||||
const connShape = fromSlot.shape
|
||||
const connType = fromSlot.type
|
||||
|
||||
const color = resolveConnectingLinkColor(connType)
|
||||
const colour = resolveConnectingLinkColor(connType)
|
||||
|
||||
// the connection being dragged by the mouse
|
||||
if (
|
||||
this.linkRenderer &&
|
||||
renderLink.fromSlotIndex !== undefined &&
|
||||
renderLink.node !== undefined
|
||||
) {
|
||||
const { fromSlotIndex, node } = renderLink
|
||||
if (
|
||||
node instanceof LGraphNode &&
|
||||
('link' in fromSlot || 'links' in fromSlot)
|
||||
) {
|
||||
this.linkRenderer.renderDraggingLink(
|
||||
ctx,
|
||||
node,
|
||||
fromSlot,
|
||||
fromSlotIndex,
|
||||
highlightPos,
|
||||
this.buildLinkRenderContext(),
|
||||
{ fromInput: 'link' in fromSlot, color }
|
||||
// pos,
|
||||
// colour,
|
||||
// fromDirection,
|
||||
// dragDirection,
|
||||
// {
|
||||
// ...this.buildLinkRenderContext(),
|
||||
// linkMarkerShape: LinkMarkerShape.None
|
||||
// }
|
||||
)
|
||||
}
|
||||
if (this.linkRenderer) {
|
||||
this.linkRenderer.renderDraggingLink(
|
||||
ctx,
|
||||
pos,
|
||||
highlightPos,
|
||||
colour,
|
||||
fromDirection,
|
||||
dragDirection,
|
||||
{
|
||||
...this.buildLinkRenderContext(),
|
||||
linkMarkerShape: LinkMarkerShape.None
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ctx.fillStyle = color
|
||||
ctx.fillStyle = colour
|
||||
ctx.beginPath()
|
||||
if (connType === LiteGraph.EVENT || connShape === RenderShape.BOX) {
|
||||
ctx.rect(pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, 10)
|
||||
@@ -4864,7 +4848,7 @@ export class LGraphCanvas
|
||||
}
|
||||
|
||||
/** Get the target snap / highlight point in graph space */
|
||||
#getHighlightPosition(): Point {
|
||||
#getHighlightPosition(): ReadOnlyPoint {
|
||||
return LiteGraph.snaps_for_comfy
|
||||
? this.linkConnector.state.snapLinksPos ??
|
||||
this._highlight_pos ??
|
||||
@@ -4879,7 +4863,7 @@ export class LGraphCanvas
|
||||
*/
|
||||
#renderSnapHighlight(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
highlightPos: Point
|
||||
highlightPos: ReadOnlyPoint
|
||||
): void {
|
||||
const linkConnectorSnap = !!this.linkConnector.state.snapLinksPos
|
||||
if (!this._highlight_pos && !linkConnectorSnap) return
|
||||
@@ -5221,8 +5205,7 @@ export class LGraphCanvas
|
||||
// clip if required (mask)
|
||||
const shape = node._shape || RenderShape.BOX
|
||||
const size = LGraphCanvas.#temp_vec2
|
||||
size[0] = node.renderingSize[0]
|
||||
size[1] = node.renderingSize[1]
|
||||
size.set(node.renderingSize)
|
||||
|
||||
if (node.collapsed) {
|
||||
ctx.font = this.inner_text_font
|
||||
@@ -5417,10 +5400,7 @@ export class LGraphCanvas
|
||||
|
||||
// Normalised node dimensions
|
||||
const area = LGraphCanvas.#tmp_area
|
||||
area[0] = node.boundingRect[0]
|
||||
area[1] = node.boundingRect[1]
|
||||
area[2] = node.boundingRect[2]
|
||||
area[3] = node.boundingRect[3]
|
||||
area.set(node.boundingRect)
|
||||
area[0] -= node.pos[0]
|
||||
area[1] -= node.pos[1]
|
||||
|
||||
@@ -5522,10 +5502,7 @@ export class LGraphCanvas
|
||||
shape = RenderShape.ROUND
|
||||
) {
|
||||
const snapGuide = LGraphCanvas.#temp
|
||||
snapGuide[0] = item.boundingRect[0]
|
||||
snapGuide[1] = item.boundingRect[1]
|
||||
snapGuide[2] = item.boundingRect[2]
|
||||
snapGuide[3] = item.boundingRect[3]
|
||||
snapGuide.set(item.boundingRect)
|
||||
|
||||
// Not all items have pos equal to top-left of bounds
|
||||
const { pos } = item
|
||||
@@ -5965,8 +5942,8 @@ export class LGraphCanvas
|
||||
*/
|
||||
renderLink(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
a: Point,
|
||||
b: Point,
|
||||
a: ReadOnlyPoint,
|
||||
b: ReadOnlyPoint,
|
||||
link: LLink | null,
|
||||
skip_border: boolean,
|
||||
flow: number | null,
|
||||
@@ -5983,9 +5960,9 @@ export class LGraphCanvas
|
||||
/** When defined, render data will be saved to this reroute instead of the {@link link}. */
|
||||
reroute?: Reroute
|
||||
/** Offset of the bezier curve control point from {@link a point a} (output side) */
|
||||
startControl?: Point
|
||||
startControl?: ReadOnlyPoint
|
||||
/** Offset of the bezier curve control point from {@link b point b} (input side) */
|
||||
endControl?: Point
|
||||
endControl?: ReadOnlyPoint
|
||||
/** Number of sublines (useful to represent vec3 or rgb) @todo If implemented, refactor calculations out of the loop */
|
||||
num_sublines?: number
|
||||
/** Whether this is a floating link segment */
|
||||
@@ -6429,7 +6406,7 @@ export class LGraphCanvas
|
||||
|
||||
return true
|
||||
}
|
||||
console.log(`failed creating ${nodeNewType}`)
|
||||
console.error(`failed creating ${nodeNewType}`)
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -6841,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()
|
||||
}
|
||||
@@ -7118,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) {
|
||||
@@ -7163,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7501,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,
|
||||
@@ -8456,7 +8433,7 @@ export class LGraphCanvas
|
||||
* Starts an animation to fit the view around the specified selection of nodes.
|
||||
* @param bounds The bounds to animate the view to, defined by a rectangle.
|
||||
*/
|
||||
animateToBounds(bounds: Rect | Rectangle, options: AnimationOptions = {}) {
|
||||
animateToBounds(bounds: ReadOnlyRect, options: AnimationOptions = {}) {
|
||||
const setDirty = () => this.setDirty(true, true)
|
||||
this.ds.animateToBounds(bounds, setDirty, options)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { NullGraphError } from '@/lib/litegraph/src/infrastructure/NullGraphError'
|
||||
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||
|
||||
import type { LGraph } from './LGraph'
|
||||
import { LGraphCanvas } from './LGraphCanvas'
|
||||
@@ -41,15 +40,15 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
title: string
|
||||
font?: string
|
||||
font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24
|
||||
_bounding: [number, number, number, number] = [
|
||||
_bounding: Float32Array = new Float32Array([
|
||||
10,
|
||||
10,
|
||||
LGraphGroup.minWidth,
|
||||
LGraphGroup.minHeight
|
||||
]
|
||||
])
|
||||
|
||||
_pos: Point = [10, 10]
|
||||
_size: Size = [LGraphGroup.minWidth, LGraphGroup.minHeight]
|
||||
_pos: Point = this._bounding.subarray(0, 2)
|
||||
_size: Size = this._bounding.subarray(2, 4)
|
||||
/** @deprecated See {@link _children} */
|
||||
_nodes: LGraphNode[] = []
|
||||
_children: Set<Positionable> = new Set()
|
||||
@@ -108,13 +107,8 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
this._size[1] = Math.max(LGraphGroup.minHeight, v[1])
|
||||
}
|
||||
|
||||
get boundingRect(): Rectangle {
|
||||
return Rectangle.from([
|
||||
this._pos[0],
|
||||
this._pos[1],
|
||||
this._size[0],
|
||||
this._size[1]
|
||||
])
|
||||
get boundingRect() {
|
||||
return this._bounding
|
||||
}
|
||||
|
||||
get nodes() {
|
||||
@@ -151,17 +145,14 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
configure(o: ISerialisedGroup): void {
|
||||
this.id = o.id
|
||||
this.title = o.title
|
||||
this._pos[0] = o.bounding[0]
|
||||
this._pos[1] = o.bounding[1]
|
||||
this._size[0] = o.bounding[2]
|
||||
this._size[1] = o.bounding[3]
|
||||
this._bounding.set(o.bounding)
|
||||
this.color = o.color
|
||||
this.flags = o.flags || this.flags
|
||||
if (o.font_size) this.font_size = o.font_size
|
||||
}
|
||||
|
||||
serialize(): ISerialisedGroup {
|
||||
const b = [this._pos[0], this._pos[1], this._size[0], this._size[1]]
|
||||
const b = this._bounding
|
||||
return {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
@@ -219,7 +210,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
)
|
||||
|
||||
if (LiteGraph.highlight_selected_group && this.selected) {
|
||||
strokeShape(ctx, this.boundingRect, {
|
||||
strokeShape(ctx, this._bounding, {
|
||||
title_height: this.titleHeight,
|
||||
padding
|
||||
})
|
||||
@@ -260,7 +251,7 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
|
||||
// Move nodes we overlap the centre point of
|
||||
for (const node of nodes) {
|
||||
if (containsCentre(this.boundingRect, node.boundingRect)) {
|
||||
if (containsCentre(this._bounding, node.boundingRect)) {
|
||||
this._nodes.push(node)
|
||||
children.add(node)
|
||||
}
|
||||
@@ -268,13 +259,12 @@ export class LGraphGroup implements Positionable, IPinnable, IColorable {
|
||||
|
||||
// Move reroutes we overlap the centre point of
|
||||
for (const reroute of reroutes.values()) {
|
||||
if (isPointInRect(reroute.pos, this.boundingRect)) children.add(reroute)
|
||||
if (isPointInRect(reroute.pos, this._bounding)) children.add(reroute)
|
||||
}
|
||||
|
||||
// Move groups we wholly contain
|
||||
for (const group of groups) {
|
||||
if (containsRect(this.boundingRect, group.boundingRect))
|
||||
children.add(group)
|
||||
if (containsRect(this._bounding, group._bounding)) children.add(group)
|
||||
}
|
||||
|
||||
groups.sort((a, b) => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { Reroute, RerouteId } from './Reroute'
|
||||
import { getNodeInputOnPos, getNodeOutputOnPos } from './canvas/measureSlots'
|
||||
import type { IDrawBoundingOptions } from './draw'
|
||||
import { NullGraphError } from './infrastructure/NullGraphError'
|
||||
import type { ReadOnlyRectangle } from './infrastructure/Rectangle'
|
||||
import { Rectangle } from './infrastructure/Rectangle'
|
||||
import type {
|
||||
ColorOption,
|
||||
@@ -36,6 +37,8 @@ import type {
|
||||
ISlotType,
|
||||
Point,
|
||||
Positionable,
|
||||
ReadOnlyPoint,
|
||||
ReadOnlyRect,
|
||||
Rect,
|
||||
Size
|
||||
} from './interfaces'
|
||||
@@ -164,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:
|
||||
@@ -384,7 +387,7 @@ export class LGraphNode
|
||||
* Called once at the start of every frame. Caller may change the values in {@link out}, which will be reflected in {@link boundingRect}.
|
||||
* WARNING: Making changes to boundingRect via onBounding is poorly supported, and will likely result in strange behaviour.
|
||||
*/
|
||||
onBounding?(this: LGraphNode, out: Rectangle): void
|
||||
onBounding?(this: LGraphNode, out: Rect): void
|
||||
console?: string[]
|
||||
_level?: number
|
||||
_shape?: RenderShape
|
||||
@@ -410,12 +413,12 @@ export class LGraphNode
|
||||
}
|
||||
|
||||
/** @inheritdoc {@link renderArea} */
|
||||
#renderArea: [number, number, number, number] = [0, 0, 0, 0]
|
||||
#renderArea: Float32Array = new Float32Array(4)
|
||||
/**
|
||||
* Rect describing the node area, including shadows and any protrusions.
|
||||
* Determines if the node is visible. Calculated once at the start of every frame.
|
||||
*/
|
||||
get renderArea(): Rect {
|
||||
get renderArea(): ReadOnlyRect {
|
||||
return this.#renderArea
|
||||
}
|
||||
|
||||
@@ -426,12 +429,12 @@ export class LGraphNode
|
||||
*
|
||||
* Determines the node hitbox and other rendering effects. Calculated once at the start of every frame.
|
||||
*/
|
||||
get boundingRect(): Rectangle {
|
||||
get boundingRect(): ReadOnlyRectangle {
|
||||
return this.#boundingRect
|
||||
}
|
||||
|
||||
/** The offset from {@link pos} to the top-left of {@link boundingRect}. */
|
||||
get boundingOffset(): Point {
|
||||
get boundingOffset(): ReadOnlyPoint {
|
||||
const {
|
||||
pos: [posX, posY],
|
||||
boundingRect: [bX, bY]
|
||||
@@ -440,9 +443,9 @@ export class LGraphNode
|
||||
}
|
||||
|
||||
/** {@link pos} and {@link size} values are backed by this {@link Rect}. */
|
||||
_posSize: [number, number, number, number] = [0, 0, 0, 0]
|
||||
_pos: Point = [0, 0]
|
||||
_size: Size = [0, 0]
|
||||
_posSize: Float32Array = new Float32Array(4)
|
||||
_pos: Point = this._posSize.subarray(0, 2)
|
||||
_size: Size = this._posSize.subarray(2, 4)
|
||||
|
||||
public get pos() {
|
||||
return this._pos
|
||||
@@ -899,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
|
||||
@@ -1650,7 +1653,7 @@ export class LGraphNode
|
||||
inputs ? inputs.filter((input) => !isWidgetInputSlot(input)).length : 1,
|
||||
outputs ? outputs.length : 1
|
||||
)
|
||||
const size = out || [0, 0]
|
||||
const size = out || new Float32Array([0, 0])
|
||||
rows = Math.max(rows, 1)
|
||||
// although it should be graphcanvas.inner_text_font size
|
||||
const font_size = LiteGraph.NODE_TEXT_SIZE
|
||||
@@ -1947,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1975,7 +1978,7 @@ export class LGraphNode
|
||||
* @param out `x, y, width, height` are written to this array.
|
||||
* @param ctx The canvas context to use for measuring text.
|
||||
*/
|
||||
measure(out: Rectangle, ctx: CanvasRenderingContext2D): void {
|
||||
measure(out: Rect, ctx: CanvasRenderingContext2D): void {
|
||||
const titleMode = this.title_mode
|
||||
const renderTitle =
|
||||
titleMode != TitleMode.TRANSPARENT_TITLE &&
|
||||
@@ -2001,13 +2004,13 @@ export class LGraphNode
|
||||
|
||||
/**
|
||||
* returns the bounding of the object, used for rendering purposes
|
||||
* @param out {Rect?} [optional] a place to store the output, to free garbage
|
||||
* @param out {Float32Array[4]?} [optional] a place to store the output, to free garbage
|
||||
* @param includeExternal {boolean?} [optional] set to true to
|
||||
* include the shadow and connection points in the bounding calculation
|
||||
* @returns the bounding box in format of [topleft_cornerx, topleft_cornery, width, height]
|
||||
*/
|
||||
getBounding(out?: Rect, includeExternal?: boolean): Rect {
|
||||
out ||= [0, 0, 0, 0]
|
||||
out ||= new Float32Array(4)
|
||||
|
||||
const rect = includeExternal ? this.renderArea : this.boundingRect
|
||||
out[0] = rect[0]
|
||||
@@ -2028,10 +2031,7 @@ export class LGraphNode
|
||||
this.onBounding?.(bounds)
|
||||
|
||||
const renderArea = this.#renderArea
|
||||
renderArea[0] = bounds[0]
|
||||
renderArea[1] = bounds[1]
|
||||
renderArea[2] = bounds[2]
|
||||
renderArea[3] = bounds[3]
|
||||
renderArea.set(bounds)
|
||||
// 4 offset for collapsed node connection points
|
||||
renderArea[0] -= 4
|
||||
renderArea[1] -= 4
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -3174,7 +3169,7 @@ export class LGraphNode
|
||||
* @returns the position
|
||||
*/
|
||||
getConnectionPos(is_input: boolean, slot_number: number, out?: Point): Point {
|
||||
out ||= [0, 0]
|
||||
out ||= new Float32Array(2)
|
||||
|
||||
const {
|
||||
pos: [nodeX, nodeY],
|
||||
@@ -3839,7 +3834,7 @@ export class LGraphNode
|
||||
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
||||
}
|
||||
|
||||
#measureSlots(): Rect | null {
|
||||
#measureSlots(): ReadOnlyRect | null {
|
||||
const slots: (NodeInputSlot | NodeOutputSlot)[] = []
|
||||
|
||||
for (const [slotIndex, slot] of this.#concreteInputs.entries()) {
|
||||
|
||||
@@ -109,7 +109,7 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
||||
data?: number | string | boolean | { toToolTip?(): string }
|
||||
_data?: unknown
|
||||
/** Centre point of the link, calculated during render only - can be inaccurate */
|
||||
_pos: [number, number]
|
||||
_pos: Float32Array
|
||||
/** @todo Clean up - never implemented in comfy. */
|
||||
_last_time?: number
|
||||
/** The last canvas 2D path that was used to render this link */
|
||||
@@ -171,7 +171,7 @@ export class LLink implements LinkSegment, Serialisable<SerialisableLLink> {
|
||||
|
||||
this._data = null
|
||||
// center
|
||||
this._pos = [0, 0]
|
||||
this._pos = new Float32Array(2)
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link LLink.create} */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||
import { LayoutSource } from '@/renderer/core/layout/types'
|
||||
|
||||
@@ -13,8 +12,8 @@ import type {
|
||||
LinkSegment,
|
||||
Point,
|
||||
Positionable,
|
||||
ReadonlyLinkNetwork,
|
||||
Rect
|
||||
ReadOnlyRect,
|
||||
ReadonlyLinkNetwork
|
||||
} from './interfaces'
|
||||
import { distance, isPointInRect } from './measure'
|
||||
import type { Serialisable, SerialisableReroute } from './types/serialisation'
|
||||
@@ -50,6 +49,8 @@ export class Reroute
|
||||
return Reroute.radius + gap + Reroute.slotRadius
|
||||
}
|
||||
|
||||
#malloc = new Float32Array(8)
|
||||
|
||||
/** The network this reroute belongs to. Contains all valid links and reroutes. */
|
||||
#network: WeakRef<LinkNetwork>
|
||||
|
||||
@@ -72,7 +73,7 @@ export class Reroute
|
||||
/** This property is only defined on the last reroute of a floating reroute chain (closest to input end). */
|
||||
floating?: FloatingRerouteSlot
|
||||
|
||||
#pos: [number, number] = [0, 0]
|
||||
#pos = this.#malloc.subarray(0, 2)
|
||||
/** @inheritdoc */
|
||||
get pos(): Point {
|
||||
return this.#pos
|
||||
@@ -88,17 +89,17 @@ export class Reroute
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
get boundingRect(): Rectangle {
|
||||
get boundingRect(): ReadOnlyRect {
|
||||
const { radius } = Reroute
|
||||
const [x, y] = this.#pos
|
||||
return Rectangle.from([x - radius, y - radius, 2 * radius, 2 * radius])
|
||||
return [x - radius, y - radius, 2 * radius, 2 * radius]
|
||||
}
|
||||
|
||||
/**
|
||||
* Slightly over-sized rectangle, guaranteed to contain the entire surface area for hover detection.
|
||||
* Eliminates most hover positions using an extremely cheap check.
|
||||
*/
|
||||
get #hoverArea(): Rect {
|
||||
get #hoverArea(): ReadOnlyRect {
|
||||
const xOffset = 2 * Reroute.slotOffset
|
||||
const yOffset = 2 * Math.max(Reroute.radius, Reroute.slotRadius)
|
||||
|
||||
@@ -125,14 +126,14 @@ export class Reroute
|
||||
sin: number = 0
|
||||
|
||||
/** Bezier curve control point for the "target" (input) side of the link */
|
||||
controlPoint: [number, number] = [0, 0]
|
||||
controlPoint: Point = this.#malloc.subarray(4, 6)
|
||||
|
||||
/** @inheritdoc */
|
||||
path?: Path2D
|
||||
/** @inheritdoc */
|
||||
_centreAngle?: number
|
||||
/** @inheritdoc */
|
||||
_pos: [number, number] = [0, 0]
|
||||
_pos: Float32Array = this.#malloc.subarray(6, 8)
|
||||
|
||||
/** @inheritdoc */
|
||||
_dragging?: boolean
|
||||
|
||||
@@ -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}].`
|
||||
|
||||
@@ -67,7 +67,7 @@ interface IDrawTextInAreaOptions {
|
||||
*/
|
||||
export function strokeShape(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
area: Rect | Rectangle,
|
||||
area: Rect,
|
||||
{
|
||||
shape = RenderShape.BOX,
|
||||
round_radius,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { clamp } from 'es-toolkit/compat'
|
||||
|
||||
import type { Rect, Size } from '@/lib/litegraph/src/interfaces'
|
||||
import type {
|
||||
ReadOnlyRect,
|
||||
ReadOnlySize,
|
||||
Size
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
|
||||
/**
|
||||
* Basic width and height, with min/max constraints.
|
||||
@@ -51,15 +55,15 @@ export class ConstrainedSize {
|
||||
this.desiredHeight = height
|
||||
}
|
||||
|
||||
static fromSize(size: Size): ConstrainedSize {
|
||||
static fromSize(size: ReadOnlySize): ConstrainedSize {
|
||||
return new ConstrainedSize(size[0], size[1])
|
||||
}
|
||||
|
||||
static fromRect(rect: Rect): ConstrainedSize {
|
||||
static fromRect(rect: ReadOnlyRect): ConstrainedSize {
|
||||
return new ConstrainedSize(rect[2], rect[3])
|
||||
}
|
||||
|
||||
setSize(size: Size): void {
|
||||
setSize(size: ReadOnlySize): void {
|
||||
this.desiredWidth = size[0]
|
||||
this.desiredHeight = size[1]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LGraph } from '@/lib/litegraph/src/LGraph'
|
||||
import type { LLink, ResolvedConnection } from '@/lib/litegraph/src/LLink'
|
||||
import type { Rect } from '@/lib/litegraph/src/interfaces'
|
||||
import type { ReadOnlyRect } from '@/lib/litegraph/src/interfaces'
|
||||
import type { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'
|
||||
import type {
|
||||
ExportedSubgraph,
|
||||
@@ -29,7 +29,7 @@ export interface LGraphEventMap {
|
||||
/** The type of subgraph to create. */
|
||||
subgraph: Subgraph
|
||||
/** The boundary around every item that was moved into the subgraph. */
|
||||
bounds: Rect
|
||||
bounds: ReadOnlyRect
|
||||
/** The raw data that was used to create the subgraph. */
|
||||
exportedSubgraph: ExportedSubgraph
|
||||
/** The links that were used to create the subgraph. */
|
||||
|
||||
@@ -1,50 +1,47 @@
|
||||
import type {
|
||||
CompassCorners,
|
||||
Point,
|
||||
Rect,
|
||||
ReadOnlyPoint,
|
||||
ReadOnlyRect,
|
||||
ReadOnlySize,
|
||||
ReadOnlyTypedArray,
|
||||
Size
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { isInRectangle } from '@/lib/litegraph/src/measure'
|
||||
|
||||
/**
|
||||
* A rectangle, represented as an array of 4 numbers: [x, y, width, height].
|
||||
* A rectangle, represented as a float64 array of 4 numbers: [x, y, width, height].
|
||||
*
|
||||
* This class extends Array and provides both array access (rect[0], rect[1], etc.)
|
||||
* and convenient property access (rect.x, rect.y, rect.width, rect.height).
|
||||
* This class is a subclass of Float64Array, and so has all the methods of that class. Notably,
|
||||
* {@link Rectangle.from} can be used to convert a {@link ReadOnlyRect}. Typing of this however,
|
||||
* is broken due to the base TS lib returning Float64Array rather than `this`.
|
||||
*
|
||||
* Sub-array properties ({@link Float64Array.subarray}):
|
||||
* - {@link pos}: The position of the top-left corner of the rectangle.
|
||||
* - {@link size}: The size of the rectangle.
|
||||
*/
|
||||
export class Rectangle extends Array<number> {
|
||||
export class Rectangle extends Float64Array {
|
||||
#pos: Point | undefined
|
||||
#size: Size | undefined
|
||||
|
||||
constructor(
|
||||
x: number = 0,
|
||||
y: number = 0,
|
||||
width: number = 0,
|
||||
height: number = 0
|
||||
) {
|
||||
super()
|
||||
super(4)
|
||||
|
||||
this[0] = x
|
||||
this[1] = y
|
||||
this[2] = width
|
||||
this[3] = height
|
||||
this.length = 4
|
||||
}
|
||||
|
||||
static override from([x, y, width, height]: Rect): Rectangle {
|
||||
static override from([x, y, width, height]: ReadOnlyRect): Rectangle {
|
||||
return new Rectangle(x, y, width, height)
|
||||
}
|
||||
|
||||
/** Set all values from an array (for TypedArray compatibility) */
|
||||
set(values: ArrayLike<number>): void {
|
||||
this[0] = values[0] ?? 0
|
||||
this[1] = values[1] ?? 0
|
||||
this[2] = values[2] ?? 0
|
||||
this[3] = values[3] ?? 0
|
||||
}
|
||||
|
||||
/** Create a subarray (for TypedArray compatibility) */
|
||||
subarray(begin: number = 0, end?: number): number[] {
|
||||
const endIndex = end ?? this.length
|
||||
return this.slice(begin, endIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new rectangle positioned at the given centre, with the given width/height.
|
||||
* @param centre The centre of the rectangle, as an `[x, y]` point
|
||||
@@ -52,38 +49,57 @@ export class Rectangle extends Array<number> {
|
||||
* @param height The height of the rectangle. Default: {@link width}
|
||||
* @returns A new rectangle whose centre is at {@link x}
|
||||
*/
|
||||
static fromCentre([x, y]: Point, width: number, height = width): Rectangle {
|
||||
static fromCentre(
|
||||
[x, y]: ReadOnlyPoint,
|
||||
width: number,
|
||||
height = width
|
||||
): Rectangle {
|
||||
const left = x - width * 0.5
|
||||
const top = y - height * 0.5
|
||||
return new Rectangle(left, top, width, height)
|
||||
}
|
||||
|
||||
static ensureRect(rect: Rect | Rectangle): Rectangle {
|
||||
static ensureRect(rect: ReadOnlyRect): Rectangle {
|
||||
return rect instanceof Rectangle
|
||||
? rect
|
||||
: new Rectangle(rect[0], rect[1], rect[2], rect[3])
|
||||
}
|
||||
|
||||
/**
|
||||
* The position of the top-left corner of this rectangle.
|
||||
*/
|
||||
get pos(): Point {
|
||||
return [this[0], this[1]]
|
||||
override subarray(
|
||||
begin: number = 0,
|
||||
end?: number
|
||||
): Float64Array<ArrayBuffer> {
|
||||
const byteOffset = begin << 3
|
||||
const length = end === undefined ? end : end - begin
|
||||
return new Float64Array(this.buffer, byteOffset, length)
|
||||
}
|
||||
|
||||
set pos(value: Point) {
|
||||
/**
|
||||
* A reference to the position of the top-left corner of this rectangle.
|
||||
*
|
||||
* Updating the values of the returned object will update this rectangle.
|
||||
*/
|
||||
get pos(): Point {
|
||||
this.#pos ??= this.subarray(0, 2)
|
||||
return this.#pos!
|
||||
}
|
||||
|
||||
set pos(value: ReadOnlyPoint) {
|
||||
this[0] = value[0]
|
||||
this[1] = value[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of this rectangle.
|
||||
* A reference to the size of this rectangle.
|
||||
*
|
||||
* Updating the values of the returned object will update this rectangle.
|
||||
*/
|
||||
get size(): Size {
|
||||
return [this[2], this[3]]
|
||||
this.#size ??= this.subarray(2, 4)
|
||||
return this.#size!
|
||||
}
|
||||
|
||||
set size(value: Size) {
|
||||
set size(value: ReadOnlySize) {
|
||||
this[2] = value[0]
|
||||
this[3] = value[1]
|
||||
}
|
||||
@@ -176,7 +192,7 @@ export class Rectangle extends Array<number> {
|
||||
* Updates the rectangle to the values of {@link rect}.
|
||||
* @param rect The rectangle to update to.
|
||||
*/
|
||||
updateTo(rect: Rect) {
|
||||
updateTo(rect: ReadOnlyRect) {
|
||||
this[0] = rect[0]
|
||||
this[1] = rect[1]
|
||||
this[2] = rect[2]
|
||||
@@ -199,7 +215,7 @@ export class Rectangle extends Array<number> {
|
||||
* @param point The point to check
|
||||
* @returns `true` if {@link point} is inside this rectangle, otherwise `false`.
|
||||
*/
|
||||
containsPoint([x, y]: Point): boolean {
|
||||
containsPoint([x, y]: ReadOnlyPoint): boolean {
|
||||
const [left, top, width, height] = this
|
||||
return x >= left && x < left + width && y >= top && y < top + height
|
||||
}
|
||||
@@ -210,7 +226,7 @@ export class Rectangle extends Array<number> {
|
||||
* @param other The rectangle to check
|
||||
* @returns `true` if {@link other} is inside this rectangle, otherwise `false`.
|
||||
*/
|
||||
containsRect(other: Rect | Rectangle): boolean {
|
||||
containsRect(other: ReadOnlyRect): boolean {
|
||||
const { right, bottom } = this
|
||||
const otherRight = other[0] + other[2]
|
||||
const otherBottom = other[1] + other[3]
|
||||
@@ -235,7 +251,7 @@ export class Rectangle extends Array<number> {
|
||||
* @param rect The rectangle to check
|
||||
* @returns `true` if {@link rect} overlaps with this rectangle, otherwise `false`.
|
||||
*/
|
||||
overlaps(rect: Rect | Rectangle): boolean {
|
||||
overlaps(rect: ReadOnlyRect): boolean {
|
||||
return (
|
||||
this.x < rect[0] + rect[2] &&
|
||||
this.y < rect[1] + rect[3] &&
|
||||
@@ -368,12 +384,12 @@ export class Rectangle extends Array<number> {
|
||||
}
|
||||
|
||||
/** @returns The offset from the top-left of this rectangle to the point [{@link x}, {@link y}], as a new {@link Point}. */
|
||||
getOffsetTo([x, y]: Point): Point {
|
||||
getOffsetTo([x, y]: ReadOnlyPoint): Point {
|
||||
return [x - this[0], y - this[1]]
|
||||
}
|
||||
|
||||
/** @returns The offset from the point [{@link x}, {@link y}] to the top-left of this rectangle, as a new {@link Point}. */
|
||||
getOffsetFrom([x, y]: Point): Point {
|
||||
getOffsetFrom([x, y]: ReadOnlyPoint): Point {
|
||||
return [this[0] - x, this[1] - y]
|
||||
}
|
||||
|
||||
@@ -454,4 +470,14 @@ export class Rectangle extends Array<number> {
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOnlyRectangle is now just Rectangle since we unified the types
|
||||
export type ReadOnlyRectangle = Omit<
|
||||
ReadOnlyTypedArray<Rectangle>,
|
||||
| 'setHeightBottomAnchored'
|
||||
| 'setWidthRightAnchored'
|
||||
| 'resizeTopLeft'
|
||||
| 'resizeBottomLeft'
|
||||
| 'resizeTopRight'
|
||||
| 'resizeBottomRight'
|
||||
| 'resizeBottomRight'
|
||||
| 'updateTo'
|
||||
>
|
||||
|
||||
@@ -60,7 +60,7 @@ export interface HasBoundingRect {
|
||||
* @readonly
|
||||
* @see {@link move}
|
||||
*/
|
||||
readonly boundingRect: Rectangle
|
||||
readonly boundingRect: ReadOnlyRect
|
||||
}
|
||||
|
||||
/** An object containing a set of child objects */
|
||||
@@ -194,7 +194,7 @@ export interface LinkSegment {
|
||||
/** The last canvas 2D path that was used to render this segment */
|
||||
path?: Path2D
|
||||
/** Centre point of the {@link path}. Calculated during render only - can be inaccurate */
|
||||
readonly _pos: [number, number]
|
||||
readonly _pos: Float32Array
|
||||
/**
|
||||
* Y-forward along the {@link path} from its centre point, in radians.
|
||||
* `undefined` if using circles for link centres.
|
||||
@@ -226,13 +226,52 @@ export interface IFoundSlot extends IInputOrOutput {
|
||||
}
|
||||
|
||||
/** A point represented as `[x, y]` co-ordinates */
|
||||
export type Point = [x: number, y: number]
|
||||
export type Point = [x: number, y: number] | Float32Array | Float64Array
|
||||
|
||||
/** A size represented as `[width, height]` */
|
||||
export type Size = [width: number, height: number]
|
||||
export type Size = [width: number, height: number] | Float32Array | Float64Array
|
||||
|
||||
/** A very firm array */
|
||||
type ArRect = [x: number, y: number, width: number, height: number]
|
||||
|
||||
/** A rectangle starting at top-left coordinates `[x, y, width, height]` */
|
||||
export type Rect = [number, number, number, number]
|
||||
export type Rect = ArRect | Float32Array | Float64Array
|
||||
|
||||
/** A point represented as `[x, y]` co-ordinates that will not be modified */
|
||||
export type ReadOnlyPoint =
|
||||
| readonly [x: number, y: number]
|
||||
| ReadOnlyTypedArray<Float32Array>
|
||||
| ReadOnlyTypedArray<Float64Array>
|
||||
|
||||
/** A size represented as `[width, height]` that will not be modified */
|
||||
export type ReadOnlySize =
|
||||
| readonly [width: number, height: number]
|
||||
| ReadOnlyTypedArray<Float32Array>
|
||||
| ReadOnlyTypedArray<Float64Array>
|
||||
|
||||
/** A rectangle starting at top-left coordinates `[x, y, width, height]` that will not be modified */
|
||||
export type ReadOnlyRect =
|
||||
| readonly [x: number, y: number, width: number, height: number]
|
||||
| ReadOnlyTypedArray<Float32Array>
|
||||
| ReadOnlyTypedArray<Float64Array>
|
||||
|
||||
type TypedArrays =
|
||||
| Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
| Uint32Array
|
||||
| Float32Array
|
||||
| Float64Array
|
||||
|
||||
type TypedBigIntArrays = BigInt64Array | BigUint64Array
|
||||
export type ReadOnlyTypedArray<T extends TypedArrays | TypedBigIntArrays> =
|
||||
Omit<
|
||||
Readonly<T>,
|
||||
'fill' | 'copyWithin' | 'reverse' | 'set' | 'sort' | 'subarray'
|
||||
>
|
||||
|
||||
/** Union of property names that are of type Match */
|
||||
type KeysOfType<T, Match> = Exclude<
|
||||
@@ -291,7 +330,7 @@ export interface INodeSlot extends HasBoundingRect {
|
||||
nameLocked?: boolean
|
||||
pos?: Point
|
||||
/** @remarks Automatically calculated; not included in serialisation. */
|
||||
boundingRect: Rectangle
|
||||
boundingRect: Rect
|
||||
/**
|
||||
* A list of floating link IDs that are connected to this slot.
|
||||
* This is calculated at runtime; it is **not** serialized.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { Rectangle } from './infrastructure/Rectangle'
|
||||
import type { HasBoundingRect, Point, Rect } from './interfaces'
|
||||
import type {
|
||||
HasBoundingRect,
|
||||
Point,
|
||||
ReadOnlyPoint,
|
||||
ReadOnlyRect,
|
||||
Rect
|
||||
} from './interfaces'
|
||||
import { Alignment, LinkDirection, hasFlag } from './types/globalEnums'
|
||||
|
||||
/**
|
||||
@@ -8,7 +13,7 @@ import { Alignment, LinkDirection, hasFlag } from './types/globalEnums'
|
||||
* @param b Point b as `x, y`
|
||||
* @returns Distance between point {@link a} & {@link b}
|
||||
*/
|
||||
export function distance(a: Point, b: Point): number {
|
||||
export function distance(a: ReadOnlyPoint, b: ReadOnlyPoint): number {
|
||||
return Math.sqrt(
|
||||
(b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])
|
||||
)
|
||||
@@ -56,7 +61,10 @@ export function isInRectangle(
|
||||
* @param rect The rectangle, as `x, y, width, height`
|
||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||
*/
|
||||
export function isPointInRect(point: Point, rect: Rect | Rectangle): boolean {
|
||||
export function isPointInRect(
|
||||
point: ReadOnlyPoint,
|
||||
rect: ReadOnlyRect
|
||||
): boolean {
|
||||
return (
|
||||
point[0] >= rect[0] &&
|
||||
point[0] < rect[0] + rect[2] &&
|
||||
@@ -72,11 +80,7 @@ export function isPointInRect(point: Point, rect: Rect | Rectangle): boolean {
|
||||
* @param rect The rectangle, as `x, y, width, height`
|
||||
* @returns `true` if the point is inside the rect, otherwise `false`
|
||||
*/
|
||||
export function isInRect(
|
||||
x: number,
|
||||
y: number,
|
||||
rect: Rect | Rectangle
|
||||
): boolean {
|
||||
export function isInRect(x: number, y: number, rect: ReadOnlyRect): boolean {
|
||||
return (
|
||||
x >= rect[0] &&
|
||||
x < rect[0] + rect[2] &&
|
||||
@@ -117,10 +121,7 @@ export function isInsideRectangle(
|
||||
* @param b Rectangle B as `x, y, width, height`
|
||||
* @returns `true` if rectangles overlap, otherwise `false`
|
||||
*/
|
||||
export function overlapBounding(
|
||||
a: Rect | Rectangle,
|
||||
b: Rect | Rectangle
|
||||
): boolean {
|
||||
export function overlapBounding(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
||||
const aRight = a[0] + a[2]
|
||||
const aBottom = a[1] + a[3]
|
||||
const bRight = b[0] + b[2]
|
||||
@@ -136,7 +137,7 @@ export function overlapBounding(
|
||||
* @param rect The rectangle, as `x, y, width, height`
|
||||
* @returns The centre of the rectangle, as `x, y`
|
||||
*/
|
||||
export function getCentre(rect: Rect | Rectangle): Point {
|
||||
export function getCentre(rect: ReadOnlyRect): Point {
|
||||
return [rect[0] + rect[2] * 0.5, rect[1] + rect[3] * 0.5]
|
||||
}
|
||||
|
||||
@@ -146,10 +147,7 @@ export function getCentre(rect: Rect | Rectangle): Point {
|
||||
* @param b Sub-rectangle B as `x, y, width, height`
|
||||
* @returns `true` if {@link a} contains most of {@link b}, otherwise `false`
|
||||
*/
|
||||
export function containsCentre(
|
||||
a: Rect | Rectangle,
|
||||
b: Rect | Rectangle
|
||||
): boolean {
|
||||
export function containsCentre(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
||||
const centreX = b[0] + b[2] * 0.5
|
||||
const centreY = b[1] + b[3] * 0.5
|
||||
return isInRect(centreX, centreY, a)
|
||||
@@ -161,10 +159,7 @@ export function containsCentre(
|
||||
* @param b Sub-rectangle B as `x, y, width, height`
|
||||
* @returns `true` if {@link a} wholly contains {@link b}, otherwise `false`
|
||||
*/
|
||||
export function containsRect(
|
||||
a: Rect | Rectangle,
|
||||
b: Rect | Rectangle
|
||||
): boolean {
|
||||
export function containsRect(a: ReadOnlyRect, b: ReadOnlyRect): boolean {
|
||||
const aRight = a[0] + a[2]
|
||||
const aBottom = a[1] + a[3]
|
||||
const bRight = b[0] + b[2]
|
||||
@@ -294,8 +289,8 @@ export function rotateLink(
|
||||
* the right
|
||||
*/
|
||||
export function getOrientation(
|
||||
lineStart: Point,
|
||||
lineEnd: Point,
|
||||
lineStart: ReadOnlyPoint,
|
||||
lineEnd: ReadOnlyPoint,
|
||||
x: number,
|
||||
y: number
|
||||
): number {
|
||||
@@ -315,10 +310,10 @@ export function getOrientation(
|
||||
*/
|
||||
export function findPointOnCurve(
|
||||
out: Point,
|
||||
a: Point,
|
||||
b: Point,
|
||||
controlA: Point,
|
||||
controlB: Point,
|
||||
a: ReadOnlyPoint,
|
||||
b: ReadOnlyPoint,
|
||||
controlA: ReadOnlyPoint,
|
||||
controlB: ReadOnlyPoint,
|
||||
t: number = 0.5
|
||||
): void {
|
||||
const iT = 1 - t
|
||||
@@ -335,13 +330,8 @@ export function findPointOnCurve(
|
||||
export function createBounds(
|
||||
objects: Iterable<HasBoundingRect>,
|
||||
padding: number = 10
|
||||
): Rect | null {
|
||||
const bounds: [number, number, number, number] = [
|
||||
Infinity,
|
||||
Infinity,
|
||||
-Infinity,
|
||||
-Infinity
|
||||
]
|
||||
): ReadOnlyRect | null {
|
||||
const bounds = new Float32Array([Infinity, Infinity, -Infinity, -Infinity])
|
||||
|
||||
for (const obj of objects) {
|
||||
const rect = obj.boundingRect
|
||||
@@ -389,11 +379,11 @@ export function snapPoint(pos: Point | Rect, snapTo: number): boolean {
|
||||
* @returns The original {@link rect}, modified in place.
|
||||
*/
|
||||
export function alignToContainer(
|
||||
rect: Rect | Rectangle,
|
||||
rect: Rect,
|
||||
anchors: Alignment,
|
||||
[containerX, containerY, containerWidth, containerHeight]: Rect | Rectangle,
|
||||
[insetX, insetY]: Point = [0, 0]
|
||||
): Rect | Rectangle {
|
||||
[containerX, containerY, containerWidth, containerHeight]: ReadOnlyRect,
|
||||
[insetX, insetY]: ReadOnlyPoint = [0, 0]
|
||||
): Rect {
|
||||
if (hasFlag(anchors, Alignment.Left)) {
|
||||
// Left
|
||||
rect[0] = containerX + insetX
|
||||
@@ -432,11 +422,11 @@ export function alignToContainer(
|
||||
* @returns The original {@link rect}, modified in place.
|
||||
*/
|
||||
export function alignOutsideContainer(
|
||||
rect: Rect | Rectangle,
|
||||
rect: Rect,
|
||||
anchors: Alignment,
|
||||
[otherX, otherY, otherWidth, otherHeight]: Rect | Rectangle,
|
||||
[outsetX, outsetY]: Point = [0, 0]
|
||||
): Rect | Rectangle {
|
||||
[otherX, otherY, otherWidth, otherHeight]: ReadOnlyRect,
|
||||
[outsetX, outsetY]: ReadOnlyPoint = [0, 0]
|
||||
): Rect {
|
||||
if (hasFlag(anchors, Alignment.Left)) {
|
||||
// Left
|
||||
rect[0] = otherX - outsetX - rect[2]
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
OptionalProps,
|
||||
Point
|
||||
ReadOnlyPoint
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
||||
@@ -32,7 +32,7 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
||||
this.#widget = widget ? new WeakRef(widget) : undefined
|
||||
}
|
||||
|
||||
get collapsedPos(): Point {
|
||||
get collapsedPos(): ReadOnlyPoint {
|
||||
return [0, LiteGraph.NODE_TITLE_HEIGHT * -0.5]
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
OptionalProps,
|
||||
Point
|
||||
ReadOnlyPoint
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { type IDrawOptions, NodeSlot } from '@/lib/litegraph/src/node/NodeSlot'
|
||||
@@ -24,7 +24,7 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
return false
|
||||
}
|
||||
|
||||
get collapsedPos(): Point {
|
||||
get collapsedPos(): ReadOnlyPoint {
|
||||
return [
|
||||
this.#node._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH,
|
||||
LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
||||
|
||||
@@ -8,7 +8,8 @@ import type {
|
||||
INodeSlot,
|
||||
ISubgraphInput,
|
||||
OptionalProps,
|
||||
Point
|
||||
Point,
|
||||
ReadOnlyPoint
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph, Rectangle } from '@/lib/litegraph/src/litegraph'
|
||||
import { getCentre } from '@/lib/litegraph/src/measure'
|
||||
@@ -35,7 +36,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
pos?: Point
|
||||
|
||||
/** The offset from the parent node to the centre point of this slot. */
|
||||
get #centreOffset(): Point {
|
||||
get #centreOffset(): ReadOnlyPoint {
|
||||
const nodePos = this.node.pos
|
||||
const { boundingRect } = this
|
||||
|
||||
@@ -51,7 +52,7 @@ export abstract class NodeSlot extends SlotBase implements INodeSlot {
|
||||
}
|
||||
|
||||
/** The center point of this slot when the node is collapsed. */
|
||||
abstract get collapsedPos(): Point
|
||||
abstract get collapsedPos(): ReadOnlyPoint
|
||||
|
||||
#node: LGraphNode
|
||||
get node(): LGraphNode {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
Point,
|
||||
Rect
|
||||
ReadOnlyRect
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||
@@ -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
|
||||
@@ -213,7 +211,7 @@ export class SubgraphInput extends SubgraphSlot {
|
||||
}
|
||||
|
||||
/** For inputs, x is the right edge of the input node. */
|
||||
override arrange(rect: Rect): void {
|
||||
override arrange(rect: ReadOnlyRect): void {
|
||||
const [right, top, width, height] = rect
|
||||
const { boundingRect: b, pos } = this
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
Point,
|
||||
Rect
|
||||
ReadOnlyRect
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||
@@ -119,7 +119,7 @@ export class SubgraphOutput extends SubgraphSlot {
|
||||
return [x + height, y + height * 0.5]
|
||||
}
|
||||
|
||||
override arrange(rect: Rect): void {
|
||||
override arrange(rect: ReadOnlyRect): void {
|
||||
const [left, top, width, height] = rect
|
||||
const { boundingRect: b, pos } = this
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import type {
|
||||
INodeInputSlot,
|
||||
INodeOutputSlot,
|
||||
Point,
|
||||
Rect,
|
||||
Size
|
||||
ReadOnlyRect,
|
||||
ReadOnlySize
|
||||
} from '@/lib/litegraph/src/interfaces'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { SlotBase } from '@/lib/litegraph/src/node/SlotBase'
|
||||
@@ -45,7 +45,7 @@ export abstract class SubgraphSlot
|
||||
return LiteGraph.NODE_SLOT_HEIGHT
|
||||
}
|
||||
|
||||
readonly #pos: Point = [0, 0]
|
||||
readonly #pos: Point = new Float32Array(2)
|
||||
|
||||
readonly measurement: ConstrainedSize = new ConstrainedSize(
|
||||
SubgraphSlot.defaultHeight,
|
||||
@@ -133,7 +133,7 @@ export abstract class SubgraphSlot
|
||||
}
|
||||
}
|
||||
|
||||
measure(): Size {
|
||||
measure(): ReadOnlySize {
|
||||
const width = LGraphCanvas._measureText?.(this.displayName) ?? 0
|
||||
|
||||
const { defaultHeight } = SubgraphSlot
|
||||
@@ -141,7 +141,7 @@ export abstract class SubgraphSlot
|
||||
return this.measurement.toSize()
|
||||
}
|
||||
|
||||
abstract arrange(rect: Rect): void
|
||||
abstract arrange(rect: ReadOnlyRect): void
|
||||
|
||||
abstract connect(
|
||||
slot: INodeInputSlot | INodeOutputSlot,
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, vi } from 'vitest'
|
||||
|
||||
import { Rectangle } from '@/lib/litegraph/src/infrastructure/Rectangle'
|
||||
import type { INodeInputSlot, Point } from '@/lib/litegraph/src/interfaces'
|
||||
import { LGraphNode, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { LGraph } from '@/lib/litegraph/src/litegraph'
|
||||
@@ -85,8 +84,8 @@ describe('LGraphNode', () => {
|
||||
}))
|
||||
}
|
||||
node.configure(configureData)
|
||||
expect(node.pos).toEqual([50, 60])
|
||||
expect(node.size).toEqual([70, 80])
|
||||
expect(node.pos).toEqual(new Float32Array([50, 60]))
|
||||
expect(node.size).toEqual(new Float32Array([70, 80]))
|
||||
})
|
||||
|
||||
test('should configure inputs correctly', () => {
|
||||
@@ -572,7 +571,7 @@ describe('LGraphNode', () => {
|
||||
name: 'test_in',
|
||||
type: 'string',
|
||||
link: null,
|
||||
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
||||
}
|
||||
})
|
||||
test('should return position based on title height when collapsed', () => {
|
||||
@@ -595,7 +594,7 @@ describe('LGraphNode', () => {
|
||||
name: 'test_in_2',
|
||||
type: 'number',
|
||||
link: null,
|
||||
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
||||
}
|
||||
node.inputs = [inputSlot, inputSlot2]
|
||||
const slotIndex = 0
|
||||
@@ -630,13 +629,13 @@ describe('LGraphNode', () => {
|
||||
name: 'in0',
|
||||
type: 'string',
|
||||
link: null,
|
||||
boundingRect: new Rectangle(0, 0, 0, 0)
|
||||
boundingRect: new Float32Array([0, 0, 0, 0])
|
||||
}
|
||||
const input1: INodeInputSlot = {
|
||||
name: 'in1',
|
||||
type: 'number',
|
||||
link: null,
|
||||
boundingRect: new Rectangle(0, 0, 0, 0),
|
||||
boundingRect: new Float32Array([0, 0, 0, 0]),
|
||||
pos: [5, 45]
|
||||
}
|
||||
node.inputs = [input0, input1]
|
||||
|
||||
@@ -4,19 +4,19 @@ exports[`LGraph configure() > LGraph matches previous snapshot (normal configure
|
||||
LGraph {
|
||||
"_groups": [
|
||||
LGraphGroup {
|
||||
"_bounding": [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
80,
|
||||
"_bounding": Float32Array [
|
||||
20,
|
||||
20,
|
||||
1,
|
||||
3,
|
||||
],
|
||||
"_children": Set {},
|
||||
"_nodes": [],
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
20,
|
||||
20,
|
||||
],
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
1,
|
||||
3,
|
||||
],
|
||||
@@ -39,19 +39,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -98,7 +98,6 @@ LGraph {
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": "LGraphNode",
|
||||
"title_buttons": [],
|
||||
"type": "mustBeSet",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
@@ -109,19 +108,19 @@ LGraph {
|
||||
"1": LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -168,7 +167,6 @@ LGraph {
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": "LGraphNode",
|
||||
"title_buttons": [],
|
||||
"type": "mustBeSet",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
@@ -180,19 +178,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -239,7 +237,6 @@ LGraph {
|
||||
"selected": [Function],
|
||||
},
|
||||
"title": "LGraphNode",
|
||||
"title_buttons": [],
|
||||
"type": "mustBeSet",
|
||||
"widgets": undefined,
|
||||
"widgets_start_y": undefined,
|
||||
@@ -252,16 +249,7 @@ LGraph {
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"events": CustomEventTarget {},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
@@ -308,16 +296,7 @@ LGraph {
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"events": CustomEventTarget {},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
|
||||
@@ -4,19 +4,19 @@ exports[`LGraph > supports schema v0.4 graphs > oldSchemaGraph 1`] = `
|
||||
LGraph {
|
||||
"_groups": [
|
||||
LGraphGroup {
|
||||
"_bounding": [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
80,
|
||||
"_bounding": Float32Array [
|
||||
20,
|
||||
20,
|
||||
1,
|
||||
3,
|
||||
],
|
||||
"_children": Set {},
|
||||
"_nodes": [],
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
20,
|
||||
20,
|
||||
],
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
1,
|
||||
3,
|
||||
],
|
||||
@@ -39,19 +39,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -111,19 +111,19 @@ LGraph {
|
||||
"1": LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -184,19 +184,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -258,16 +258,7 @@ LGraph {
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"events": CustomEventTarget {},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
|
||||
@@ -4,19 +4,19 @@ exports[`LGraph (constructor only) > Matches previous snapshot > basicLGraph 1`]
|
||||
LGraph {
|
||||
"_groups": [
|
||||
LGraphGroup {
|
||||
"_bounding": [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
80,
|
||||
"_bounding": Float32Array [
|
||||
20,
|
||||
20,
|
||||
1,
|
||||
3,
|
||||
],
|
||||
"_children": Set {},
|
||||
"_nodes": [],
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
20,
|
||||
20,
|
||||
],
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
1,
|
||||
3,
|
||||
],
|
||||
@@ -39,19 +39,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -109,19 +109,19 @@ LGraph {
|
||||
"1": LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -180,19 +180,19 @@ LGraph {
|
||||
LGraphNode {
|
||||
"_collapsed_width": undefined,
|
||||
"_level": undefined,
|
||||
"_pos": [
|
||||
"_pos": Float32Array [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
"_posSize": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
"_posSize": Float32Array [
|
||||
10,
|
||||
10,
|
||||
140,
|
||||
60,
|
||||
],
|
||||
"_relative_id": undefined,
|
||||
"_shape": undefined,
|
||||
"_size": [
|
||||
"_size": Float32Array [
|
||||
140,
|
||||
60,
|
||||
],
|
||||
@@ -252,16 +252,7 @@ LGraph {
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"events": CustomEventTarget {},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
@@ -308,16 +299,7 @@ LGraph {
|
||||
"config": {},
|
||||
"elapsed_time": 0.01,
|
||||
"errors_in_execution": undefined,
|
||||
"events": CustomEventTarget {
|
||||
Symbol(listeners): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
Symbol(listenerOptions): {
|
||||
"bubbling": Map {},
|
||||
"capturing": Map {},
|
||||
},
|
||||
},
|
||||
"events": CustomEventTarget {},
|
||||
"execution_time": undefined,
|
||||
"execution_timer_id": undefined,
|
||||
"extra": {},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { test as baseTest } from 'vitest'
|
||||
|
||||
import { Rectangle } from '../src/infrastructure/Rectangle'
|
||||
import type { Point, Rect } from '../src/interfaces'
|
||||
import {
|
||||
addDirectionalOffset,
|
||||
@@ -132,8 +131,8 @@ test('snapPoint correctly snaps points to grid', ({ expect }) => {
|
||||
|
||||
test('createBounds correctly creates bounding box', ({ expect }) => {
|
||||
const objects = [
|
||||
{ boundingRect: new Rectangle(0, 0, 10, 10) },
|
||||
{ boundingRect: new Rectangle(5, 5, 10, 10) }
|
||||
{ boundingRect: [0, 0, 10, 10] as Rect },
|
||||
{ boundingRect: [5, 5, 10, 10] as Rect }
|
||||
]
|
||||
|
||||
const defaultBounds = createBounds(objects)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||