diff --git a/.env_example b/.env_example index 3ddf31fa2e..7bebc69367 100644 --- a/.env_example +++ b/.env_example @@ -12,6 +12,10 @@ DEV_SERVER_COMFYUI_URL=http://127.0.0.1:8188 # to ComfyUI launch script to serve the custom web version. DEPLOY_COMFYUI_DIR=/home/ComfyUI/web +# The directory containing the ComfyUI installation used to run Playwright tests. +# If you aren't using a separate install for testing, point this to your regular install. +TEST_COMFYUI_DIR=/home/ComfyUI + # The directory containing the ComfyUI_examples repo used to extract test workflows. EXAMPLE_REPO_PATH=tests-ui/ComfyUI_examples diff --git a/.prettierrc b/.prettierrc index 9d774965dc..f36bb1cd21 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "singleQuote": true, "tabWidth": 2, "semi": false, - "trailingComma": "none" + "trailingComma": "none", + "printWidth": 80 } \ No newline at end of file diff --git a/README.md b/README.md index 749af62074..b7f0b63127 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ ### Nightly Release -Nightly releases are published daily at [https://github.com/Comfy-Org/ComfyUI_frontend/releases](https://github.com/Comfy-Org/ComfyUI_frontend/releases). +Nightly releases are published daily at [https://github.com/Comfy-Org/ComfyUI_frontend/releases](https://github.com/Comfy-Org/ComfyUI_frontend/releases). To use the latest nightly release, add the following command line argument to your ComfyUI launch script: -``` +```bat --front-end-version Comfy-Org/ComfyUI_frontend@latest ``` @@ -62,6 +62,23 @@ There will be a 2-day feature freeze before each stable release. During this per ### Major features +
+ v1.3.7: Keybinding customization + +## Basic UI +![image](https://github.com/user-attachments/assets/c84a1609-3880-48e0-a746-011f36beda68) + +## Reset button +![image](https://github.com/user-attachments/assets/4d2922da-bb4f-4f90-8017-a8e4a0db07c7) + +## Edit Keybinding +![image](https://github.com/user-attachments/assets/77626b7a-cb46-48f8-9465-e03120aac66a) +![image](https://github.com/user-attachments/assets/79131a4e-75c6-4715-bd11-c6aaed887779) + +[rec.webm](https://github.com/user-attachments/assets/a3984ed9-eb28-4d47-86c0-7fc3efc2b5d0) + +
+
v1.2.4: Node library sidebar tab @@ -90,6 +107,32 @@ https://github.com/user-attachments/assets/4bbca3ee-318f-4cf0-be32-a5a5541066cf ### QoL changes +
+ v1.3.6: **Litegraph** Toggle link visibility + +[rec.webm](https://github.com/user-attachments/assets/34e460ac-fbbc-44ef-bfbb-99a84c2ae2be) + +
+ +
+ v1.3.4: **Litegraph** Auto widget to input conversion + +Dropping a link of correct type on node widget will automatically convert the widget to input. + +[rec.webm](https://github.com/user-attachments/assets/15cea0b0-b225-4bec-af50-2cdb16dc46bf) + +
+ +
+ v1.3.4: **Litegraph** Canvas pan mode + +The canvas becomes readonly in pan mode. Pan mode is activated by clicking the pan mode button on the canvas menu +or by holding the space key. + +[rec.webm](https://github.com/user-attachments/assets/c7872532-a2ac-44c1-9e7d-9e03b5d1a80b) + +
+
v1.3.1: **Litegraph** Shift drag link to create a new link @@ -97,6 +140,13 @@ https://github.com/user-attachments/assets/4bbca3ee-318f-4cf0-be32-a5a5541066cf
+
+ v1.2.62: **Litegraph** Show optional input slots as donuts + +![GYEIRidb0AYGO-v](https://github.com/user-attachments/assets/e6cde0b6-654b-4afd-a117-133657a410b1) + +
+
v1.2.44: **Litegraph** Double click group title to edit diff --git a/browser_tests/ComfyPage.ts b/browser_tests/ComfyPage.ts index 8bc2cca078..7c4d13644b 100644 --- a/browser_tests/ComfyPage.ts +++ b/browser_tests/ComfyPage.ts @@ -575,6 +575,10 @@ export class ComfyPage { await this.nextFrame() } + async getVisibleToastCount() { + return await this.page.locator('.p-toast:visible').count() + } + async clickTextEncodeNode1() { await this.canvas.click({ position: { diff --git a/browser_tests/README.md b/browser_tests/README.md index 1a58c89885..b63db0ddbd 100644 --- a/browser_tests/README.md +++ b/browser_tests/README.md @@ -5,7 +5,7 @@ This document outlines the setup and usage of Playwright for testing the ComfyUI ## WARNING The browser tests will change the ComfyUI backend state, such as user settings and saved workflows. -Please backup your ComfyUI data before running the tests locally. +If `TEST_COMFYUI_DIR` in `.env` isn't set to your `(Comfy Path)/ComfyUI` directory, these changes won't be automatically restored. ## Setup diff --git a/browser_tests/assets/old_workflow_converted_input.json b/browser_tests/assets/old_workflow_converted_input.json new file mode 100644 index 0000000000..8734433b1a --- /dev/null +++ b/browser_tests/assets/old_workflow_converted_input.json @@ -0,0 +1,128 @@ +{ + "last_node_id": 2, + "last_link_id": 1, + "nodes": [ + { + "id": 1, + "type": "ControlNetApplyAdvanced", + "pos": { + "0": 449, + "1": 204 + }, + "size": [ + 340.20001220703125, + 166 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "positive", + "type": "CONDITIONING", + "link": null + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": null + }, + { + "name": "control_net", + "type": "CONTROL_NET", + "link": null + }, + { + "name": "image", + "type": "IMAGE", + "link": null + }, + { + "name": "strength", + "type": "FLOAT", + "link": 1, + "widget": { + "name": "strength" + } + } + ], + "outputs": [ + { + "name": "positive", + "type": "CONDITIONING", + "links": null + }, + { + "name": "negative", + "type": "CONDITIONING", + "links": null + } + ], + "properties": { + "Node name for S&R": "ControlNetApplyAdvanced" + }, + "widgets_values": [ + 1, + 0, + 1 + ] + }, + { + "id": 2, + "type": "PrimitiveNode", + "pos": { + "0": 177, + "1": 265 + }, + "size": [ + 210, + 82 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "FLOAT", + "type": "FLOAT", + "links": [ + 1 + ], + "widget": { + "name": "strength" + } + } + ], + "properties": { + "Run widget replace on values": false + }, + "widgets_values": [ + 1, + "fixed" + ] + } + ], + "links": [ + [ + 1, + 2, + 0, + 1, + 4, + "FLOAT" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 1, + "offset": { + "0": 47.541666666666515, + "1": 186.9375 + } + } + }, + "version": 0.4 +} \ No newline at end of file diff --git a/browser_tests/dialog.spec.ts b/browser_tests/dialog.spec.ts index 7b46f95259..5e2d601f26 100644 --- a/browser_tests/dialog.spec.ts +++ b/browser_tests/dialog.spec.ts @@ -95,3 +95,34 @@ test.describe('Missing models warning', () => { await expect(folderSelect).not.toBeVisible() }) }) + +test.describe('Settings', () => { + test.afterEach(async ({ comfyPage }) => { + // Restore default setting value + await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.1) + }) + + test('@mobile Should be visible on mobile', async ({ comfyPage }) => { + await comfyPage.page.keyboard.press('Control+,') + const searchBox = comfyPage.page.locator('.settings-content') + await expect(searchBox).toBeVisible() + }) + + test('Can open settings with hotkey', async ({ comfyPage }) => { + await comfyPage.page.keyboard.down('ControlOrMeta') + await comfyPage.page.keyboard.press(',') + await comfyPage.page.keyboard.up('ControlOrMeta') + const settingsLocator = comfyPage.page.locator('.settings-container') + await expect(settingsLocator).toBeVisible() + await comfyPage.page.keyboard.press('Escape') + await expect(settingsLocator).not.toBeVisible() + }) + + test('Can change canvas zoom speed setting', async ({ comfyPage }) => { + const maxSpeed = 2.5 + await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', maxSpeed) + test.step('Setting should persist', async () => { + expect(await comfyPage.getSetting('Comfy.Graph.ZoomSpeed')).toBe(maxSpeed) + }) + }) +}) diff --git a/browser_tests/globalSetup.ts b/browser_tests/globalSetup.ts new file mode 100644 index 0000000000..86626480f2 --- /dev/null +++ b/browser_tests/globalSetup.ts @@ -0,0 +1,20 @@ +import { FullConfig } from '@playwright/test' +import { backupPath } from './utils/backupUtils' +import dotenv from 'dotenv' + +dotenv.config() + +export default function globalSetup(config: FullConfig) { + if (!process.env.CI) { + if (process.env.TEST_COMFYUI_DIR) { + backupPath([process.env.TEST_COMFYUI_DIR, 'user']) + backupPath([process.env.TEST_COMFYUI_DIR, 'models'], { + renameAndReplaceWithScaffolding: true + }) + } else { + console.warn( + 'Set TEST_COMFYUI_DIR in .env to prevent user data (settings, workflows, etc.) from being overwritten' + ) + } + } +} diff --git a/browser_tests/globalTeardown.ts b/browser_tests/globalTeardown.ts new file mode 100644 index 0000000000..8ebb713396 --- /dev/null +++ b/browser_tests/globalTeardown.ts @@ -0,0 +1,12 @@ +import { FullConfig } from '@playwright/test' +import { restorePath } from './utils/backupUtils' +import dotenv from 'dotenv' + +dotenv.config() + +export default function globalTeardown(config: FullConfig) { + if (!process.env.CI && process.env.TEST_COMFYUI_DIR) { + restorePath([process.env.TEST_COMFYUI_DIR, 'user']) + restorePath([process.env.TEST_COMFYUI_DIR, 'models']) + } +} diff --git a/browser_tests/groupNode.spec.ts b/browser_tests/groupNode.spec.ts index e24255ce04..c722454735 100644 --- a/browser_tests/groupNode.spec.ts +++ b/browser_tests/groupNode.spec.ts @@ -252,4 +252,20 @@ test.describe('Group Node', () => { }) }) }) + + test.describe('Keybindings', () => { + test('Convert to group node, no selection', async ({ comfyPage }) => { + expect(await comfyPage.getVisibleToastCount()).toBe(0) + await comfyPage.page.keyboard.press('Alt+g') + await comfyPage.page.waitForTimeout(300) + expect(await comfyPage.getVisibleToastCount()).toBe(1) + }) + test('Convert to group node, selected 1 node', async ({ comfyPage }) => { + expect(await comfyPage.getVisibleToastCount()).toBe(0) + await comfyPage.clickTextEncodeNode1() + await comfyPage.page.keyboard.press('Alt+g') + await comfyPage.page.waitForTimeout(300) + expect(await comfyPage.getVisibleToastCount()).toBe(1) + }) + }) }) diff --git a/browser_tests/interaction.spec.ts b/browser_tests/interaction.spec.ts index 743884e02d..d542e117ca 100644 --- a/browser_tests/interaction.spec.ts +++ b/browser_tests/interaction.spec.ts @@ -12,9 +12,19 @@ test.describe('Node Interaction', () => { }) test.describe('Node Selection', () => { - test.afterEach(async ({ comfyPage }) => { - // Deselect all nodes - await comfyPage.clickEmptySpace() + const multiSelectModifiers = ['Control', 'Shift', 'Meta'] + + multiSelectModifiers.forEach((modifier) => { + test(`Can add multiple nodes to selection using ${modifier}+Click`, async ({ + comfyPage + }) => { + const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode') + for (const node of clipNodes) { + await node.click('title', { modifiers: [modifier] }) + } + const selectedNodeCount = await comfyPage.getSelectedGraphNodesCount() + expect(selectedNodeCount).toBe(clipNodes.length) + }) }) test('Can highlight selected', async ({ comfyPage }) => { @@ -497,15 +507,3 @@ test.describe('Load duplicate workflow', () => { expect(await comfyPage.getGraphNodesCount()).toBe(1) }) }) - -test.describe('Menu interactions', () => { - test('Can open settings with hotkey', async ({ comfyPage }) => { - await comfyPage.page.keyboard.down('ControlOrMeta') - await comfyPage.page.keyboard.press(',') - await comfyPage.page.keyboard.up('ControlOrMeta') - const settingsLocator = comfyPage.page.locator('.settings-container') - await expect(settingsLocator).toBeVisible() - await comfyPage.page.keyboard.press('Escape') - await expect(settingsLocator).not.toBeVisible() - }) -}) diff --git a/browser_tests/menu.spec.ts b/browser_tests/menu.spec.ts index 9633002748..f55f16257a 100644 --- a/browser_tests/menu.spec.ts +++ b/browser_tests/menu.spec.ts @@ -416,10 +416,9 @@ test.describe('Menu', () => { const tab = comfyPage.menu.workflowsTab await tab.open() - expect(await tab.getTopLevelSavedWorkflowNames()).toEqual([ - 'workflow1.json', - 'workflow2.json' - ]) + expect(await tab.getTopLevelSavedWorkflowNames()).toEqual( + expect.arrayContaining(['workflow1.json', 'workflow2.json']) + ) }) test('Does not report warning when switching between opened workflows', async ({ @@ -525,17 +524,4 @@ test.describe('Menu', () => { expect(await comfyPage.getSetting('Comfy.UseNewMenu')).toBe('Top') }) }) - - test('Can change canvas zoom speed setting', async ({ comfyPage }) => { - const [defaultSpeed, maxSpeed] = [1.1, 2.5] - expect(await comfyPage.getSetting('Comfy.Graph.ZoomSpeed')).toBe( - defaultSpeed - ) - await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', maxSpeed) - expect(await comfyPage.getSetting('Comfy.Graph.ZoomSpeed')).toBe(maxSpeed) - await comfyPage.page.reload() - await comfyPage.setup() - expect(await comfyPage.getSetting('Comfy.Graph.ZoomSpeed')).toBe(maxSpeed) - await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', defaultSpeed) - }) }) diff --git a/browser_tests/nodeDisplay.spec.ts b/browser_tests/nodeDisplay.spec.ts index bbbf98ae59..05d0157ce3 100644 --- a/browser_tests/nodeDisplay.spec.ts +++ b/browser_tests/nodeDisplay.spec.ts @@ -32,4 +32,16 @@ test.describe('Optional input', () => { // If the node's multiline text widget is visible, then it was loaded successfully expect(comfyPage.page.locator('.comfy-multiline-input')).toHaveCount(1) }) + test('Old workflow with converted input', async ({ comfyPage }) => { + await comfyPage.loadWorkflow('old_workflow_converted_input') + const node = await comfyPage.getNodeRefById('1') + const inputs = await node.getProperty('inputs') + const vaeInput = inputs.find((w) => w.name === 'vae') + const convertedInput = inputs.find((w) => w.name === 'strength') + + expect(vaeInput).toBeDefined() + expect(convertedInput).toBeDefined() + expect(vaeInput.link).toBeNull() + expect(convertedInput.link).not.toBeNull() + }) }) diff --git a/browser_tests/utils/backupUtils.ts b/browser_tests/utils/backupUtils.ts new file mode 100644 index 0000000000..0d2ae1f4dc --- /dev/null +++ b/browser_tests/utils/backupUtils.ts @@ -0,0 +1,69 @@ +import path from 'path' +import fs from 'fs-extra' + +type PathParts = readonly [string, ...string[]] + +const getBackupPath = (originalPath: string): string => `${originalPath}.bak` + +const resolvePathIfExists = (pathParts: PathParts): string | null => { + const resolvedPath = path.resolve(...pathParts) + if (!fs.pathExistsSync(resolvedPath)) { + console.warn(`Path not found: ${resolvedPath}`) + return null + } + return resolvedPath +} + +const createScaffoldingCopy = (srcDir: string, destDir: string) => { + // Get all items (files and directories) in the source directory + const items = fs.readdirSync(srcDir, { withFileTypes: true }) + + for (const item of items) { + const srcPath = path.join(srcDir, item.name) + const destPath = path.join(destDir, item.name) + + if (item.isDirectory()) { + // Create the corresponding directory in the destination + fs.ensureDirSync(destPath) + + // Recursively copy the directory structure + createScaffoldingCopy(srcPath, destPath) + } + } +} + +export function backupPath( + pathParts: PathParts, + { renameAndReplaceWithScaffolding = false } = {} +) { + const originalPath = resolvePathIfExists(pathParts) + if (!originalPath) return + + const backupPath = getBackupPath(originalPath) + try { + if (renameAndReplaceWithScaffolding) { + // Rename the original path and create scaffolding in its place + fs.moveSync(originalPath, backupPath) + createScaffoldingCopy(backupPath, originalPath) + } else { + // Create a copy of the original path + fs.copySync(originalPath, backupPath) + } + } catch (error) { + console.error(`Failed to backup ${originalPath} from ${backupPath}`, error) + } +} + +export function restorePath(pathParts: PathParts) { + const originalPath = resolvePathIfExists(pathParts) + if (!originalPath) return + + const backupPath = getBackupPath(originalPath) + if (!fs.pathExistsSync(backupPath)) return + + try { + fs.moveSync(backupPath, originalPath, { overwrite: true }) + } catch (error) { + console.error(`Failed to restore ${originalPath} from ${backupPath}`, error) + } +} diff --git a/index.html b/index.html index 07f29df3e2..5e6f66d545 100644 --- a/index.html +++ b/index.html @@ -36,7 +36,6 @@ - diff --git a/package-lock.json b/package-lock.json index 6c74f41d65..6b4e4cd82a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,24 @@ { "name": "comfyui-frontend", - "version": "1.3.10", + "version": "1.3.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "comfyui-frontend", - "version": "1.3.10", + "version": "1.3.19", "dependencies": { - "@atlaskit/pragmatic-drag-and-drop": "^1.2.1", - "@comfyorg/litegraph": "^0.8.0", + "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", + "@comfyorg/litegraph": "^0.8.3", "@primevue/themes": "^4.0.5", - "@vitejs/plugin-vue": "^5.0.5", "@vueuse/core": "^11.0.0", "axios": "^1.7.4", - "class-transformer": "^0.5.1", "dotenv": "^16.4.5", "fuse.js": "^7.0.0", "lodash": "^4.17.21", "pinia": "^2.1.7", "primeicons": "^7.0.0", "primevue": "^4.0.5", - "reflect-metadata": "^0.2.2", "vue": "^3.4.31", "vue-i18n": "^9.13.1", "vue-router": "^4.4.3", @@ -38,6 +35,7 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.17.6", "@types/node": "^20.14.8", + "@vitejs/plugin-vue": "^5.1.4", "@vue/test-utils": "^2.4.6", "@vue/vue3-jest": "^29.2.6", "autoprefixer": "^10.4.19", @@ -63,6 +61,7 @@ "tsx": "^4.15.6", "typescript": "^5.4.5", "typescript-eslint": "^8.0.0", + "typescript-strict-plugin": "^2.4.4", "unplugin-icons": "^0.19.3", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.6", @@ -141,9 +140,9 @@ } }, "node_modules/@atlaskit/pragmatic-drag-and-drop": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.2.1.tgz", - "integrity": "sha512-gW2wJblFAeg94YXITHg0YdhFM2nmFAdDmX0LKYBIm79yEbIrOiuHHukgSjII07M4U5JpJ0Ff/4BaADjN23ix+A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.3.1.tgz", + "integrity": "sha512-MptcLppK78B2eplL5fHk93kfCbZ6uCpt33YauBPrOwI5zcHYJhZGeaGEaAXoVAHnSJOdQUhy6kGVVC9qggz2Fg==", "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.0.0", @@ -1911,9 +1910,9 @@ "dev": true }, "node_modules/@comfyorg/litegraph": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.8.0.tgz", - "integrity": "sha512-VUER05ldXpi+lcLidK3/tTOfw8c60dnSr2TxVwGULM/b0Zc34m8MoQhxmFcWka8wRplrsU0YqOxT8kfHB8zyMA==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.8.3.tgz", + "integrity": "sha512-BlKqsHhEAn1+ZXlS8Lv8oIVf9qjHyQSj7sJsnBqherqCl9RCksqiXZUXRu+/+GWlHapscQsZJcnPcoAHzGaOKA==", "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { @@ -1945,6 +1944,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "aix" @@ -1960,6 +1960,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -1975,6 +1976,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -1990,6 +1992,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "android" @@ -2005,6 +2008,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -2020,6 +2024,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -2035,6 +2040,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -2050,6 +2056,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "freebsd" @@ -2065,6 +2072,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2080,6 +2088,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2095,6 +2104,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2110,6 +2120,7 @@ "cpu": [ "loong64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2125,6 +2136,7 @@ "cpu": [ "mips64el" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2140,6 +2152,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2155,6 +2168,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2170,6 +2184,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2185,6 +2200,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -2200,6 +2216,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "netbsd" @@ -2215,6 +2232,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "openbsd" @@ -2230,6 +2248,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "sunos" @@ -2245,6 +2264,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -2260,6 +2280,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -2275,6 +2296,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3558,6 +3580,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "android" @@ -3570,6 +3593,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "android" @@ -3582,6 +3606,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -3594,6 +3619,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "darwin" @@ -3606,6 +3632,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3618,6 +3645,7 @@ "cpu": [ "arm" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3630,6 +3658,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3642,6 +3671,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3654,6 +3684,7 @@ "cpu": [ "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3666,6 +3697,7 @@ "cpu": [ "riscv64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3678,6 +3710,7 @@ "cpu": [ "s390x" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3690,6 +3723,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3702,6 +3736,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "linux" @@ -3714,6 +3749,7 @@ "cpu": [ "arm64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3726,6 +3762,7 @@ "cpu": [ "ia32" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3738,6 +3775,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" @@ -3844,7 +3882,8 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -3917,7 +3956,7 @@ "version": "20.14.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", - "devOptional": true, + "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -4212,9 +4251,11 @@ } }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", - "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", + "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", + "dev": true, + "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" }, @@ -5057,6 +5098,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5075,6 +5137,33 @@ "integrity": "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==", "license": "MIT" }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5157,6 +5246,31 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5313,11 +5427,6 @@ "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" - }, "node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -5333,6 +5442,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -5442,6 +5564,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -5790,6 +5922,19 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5972,6 +6117,16 @@ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -5996,6 +6151,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -6752,6 +6908,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -7066,6 +7223,27 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -7236,6 +7414,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7263,6 +7451,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9516,6 +9717,99 @@ "dev": true, "license": "MIT" }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", @@ -9959,6 +10253,133 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -10567,6 +10988,17 @@ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -10666,11 +11098,6 @@ "node": ">=8.10.0" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -10848,6 +11275,7 @@ "version": "4.22.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -11806,6 +12234,264 @@ } } }, + "node_modules/typescript-strict-plugin": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/typescript-strict-plugin/-/typescript-strict-plugin-2.4.4.tgz", + "integrity": "sha512-OXcWHQk+pW9gqEL/Mb1eTgj/Yiqk1oHBERr9v4VInPOYN++p+cXejmQK/h/VlUPGD++FXQ8pgiqVMyEtxU4T6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "minimatch": "^9.0.3", + "ora": "^5.4.1", + "yargs": "^16.2.0" + }, + "bin": { + "tsc-strict": "dist/cli/tsc-strict/index.js", + "update-strict-comments": "dist/cli/update-strict-comments/index.js" + } + }, + "node_modules/typescript-strict-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/typescript-strict-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript-strict-plugin/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript-strict-plugin/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/typescript-strict-plugin/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript-strict-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript-strict-plugin/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/typescript-strict-plugin/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript-strict-plugin/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", @@ -11816,7 +12502,7 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true + "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -12071,6 +12757,7 @@ "version": "5.4.6", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "dev": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -12542,6 +13229,16 @@ "makeerror": "1.0.12" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 6a95ada151..f04b3be83b 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "comfyui-frontend", "private": true, - "version": "1.3.10", + "version": "1.3.19", "type": "module", "scripts": { "dev": "vite", "build": "npm run typecheck && vite build", "deploy": "npm run build && node scripts/deploy.js", "zipdist": "node scripts/zipdist.js", - "typecheck": "tsc --noEmit", + "typecheck": "tsc --noEmit && tsc-strict", "format": "prettier --write './**/*.{js,ts,tsx,vue}'", "test": "npm run build && jest", "test:generate:examples": "npx tsx tests-ui/extractExamples", @@ -30,6 +30,7 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.17.6", "@types/node": "^20.14.8", + "@vitejs/plugin-vue": "^5.1.4", "@vue/test-utils": "^2.4.6", "@vue/vue3-jest": "^29.2.6", "autoprefixer": "^10.4.19", @@ -55,6 +56,7 @@ "tsx": "^4.15.6", "typescript": "^5.4.5", "typescript-eslint": "^8.0.0", + "typescript-strict-plugin": "^2.4.4", "unplugin-icons": "^0.19.3", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.6", @@ -63,20 +65,17 @@ "zip-dir": "^2.0.0" }, "dependencies": { - "@atlaskit/pragmatic-drag-and-drop": "^1.2.1", - "@comfyorg/litegraph": "^0.8.0", + "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", + "@comfyorg/litegraph": "^0.8.3", "@primevue/themes": "^4.0.5", - "@vitejs/plugin-vue": "^5.0.5", "@vueuse/core": "^11.0.0", "axios": "^1.7.4", - "class-transformer": "^0.5.1", "dotenv": "^16.4.5", "fuse.js": "^7.0.0", "lodash": "^4.17.21", "pinia": "^2.1.7", "primeicons": "^7.0.0", "primevue": "^4.0.5", - "reflect-metadata": "^0.2.2", "vue": "^3.4.31", "vue-i18n": "^9.13.1", "vue-router": "^4.4.3", diff --git a/playwright.config.ts b/playwright.config.ts index 45807ad824..a915a209d0 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -30,6 +30,10 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry' }, + /* Path to global setup file. Exported function runs once before all the tests */ + globalSetup: './browser_tests/globalSetup.ts', + /* Path to global teardown file. Exported function runs once after all the tests */ + globalTeardown: './browser_tests/globalTeardown.ts', /* Configure projects for major browsers */ projects: [ diff --git a/src/App.vue b/src/App.vue index 675f4fc870..a52f770266 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,8 +15,15 @@ import { useWorkspaceStore } from '@/stores/workspaceStateStore' import BlockUI from 'primevue/blockui' import ProgressSpinner from 'primevue/progressspinner' import GlobalDialog from '@/components/dialog/GlobalDialog.vue' +import { useEventListener } from '@vueuse/core' -const isLoading = computed(() => useWorkspaceStore().spinner) +const workspaceStore = useWorkspaceStore() +const isLoading = computed(() => workspaceStore.spinner) +const handleKey = (e: KeyboardEvent) => { + workspaceStore.shiftDown = e.shiftKey +} +useEventListener(window, 'keydown', handleKey) +useEventListener(window, 'keyup', handleKey) onMounted(() => { window['__COMFYUI_FRONTEND_VERSION__'] = config.app_version diff --git a/src/components/actionbar/ComfyActionbar.vue b/src/components/actionbar/ComfyActionbar.vue index c7007d2e23..978e7a697e 100644 --- a/src/components/actionbar/ComfyActionbar.vue +++ b/src/components/actionbar/ComfyActionbar.vue @@ -83,6 +83,7 @@ const setInitialPosition = () => { if (storedPosition.value.x !== 0 || storedPosition.value.y !== 0) { x.value = storedPosition.value.x y.value = storedPosition.value.y + captureLastDragState() return } if (panelRef.value) { @@ -97,6 +98,7 @@ const setInitialPosition = () => { x.value = (screenWidth - menuWidth) / 2 y.value = screenHeight - menuHeight - 10 // 10px margin from bottom + captureLastDragState() } } onMounted(setInitialPosition) @@ -106,6 +108,31 @@ watch(visible, (newVisible) => { } }) +const lastDragState = ref({ + x: x.value, + y: y.value, + windowWidth: window.innerWidth, + windowHeight: window.innerHeight +}) +const captureLastDragState = () => { + lastDragState.value = { + x: x.value, + y: y.value, + windowWidth: window.innerWidth, + windowHeight: window.innerHeight + } +} +watch( + isDragging, + (newIsDragging) => { + if (!newIsDragging) { + // Stop dragging + captureLastDragState() + } + }, + { immediate: true } +) + const adjustMenuPosition = () => { if (panelRef.value) { const screenWidth = window.innerWidth @@ -113,10 +140,34 @@ const adjustMenuPosition = () => { const menuWidth = panelRef.value.offsetWidth const menuHeight = panelRef.value.offsetHeight - // Adjust x position if menu is off-screen horizontally - x.value = clamp(x.value, 0, screenWidth - menuWidth) + // Calculate the distance from each edge + const distanceRight = + lastDragState.value.windowWidth - (lastDragState.value.x + menuWidth) + const distanceBottom = + lastDragState.value.windowHeight - (lastDragState.value.y + menuHeight) - // Adjust y position if menu is off-screen vertically + // Determine if the menu is closer to right/bottom or left/top + const anchorRight = distanceRight < lastDragState.value.x + const anchorBottom = distanceBottom < lastDragState.value.y + + // Calculate new position + if (anchorRight) { + x.value = + screenWidth - (lastDragState.value.windowWidth - lastDragState.value.x) + } else { + x.value = lastDragState.value.x + } + + if (anchorBottom) { + y.value = + screenHeight - + (lastDragState.value.windowHeight - lastDragState.value.y) + } else { + y.value = lastDragState.value.y + } + + // Ensure the menu stays within the screen bounds + x.value = clamp(x.value, 0, screenWidth - menuWidth) y.value = clamp(y.value, 0, screenHeight - menuHeight) } } diff --git a/src/components/actionbar/ComfyQueueButton.vue b/src/components/actionbar/ComfyQueueButton.vue index b816678491..854a61bb50 100644 --- a/src/components/actionbar/ComfyQueueButton.vue +++ b/src/components/actionbar/ComfyQueueButton.vue @@ -3,13 +3,20 @@ + - - diff --git a/src/components/sidebar/tabs/NodeLibrarySidebarTab.vue b/src/components/sidebar/tabs/NodeLibrarySidebarTab.vue index 30fca02c90..df887e1159 100644 --- a/src/components/sidebar/tabs/NodeLibrarySidebarTab.vue +++ b/src/components/sidebar/tabs/NodeLibrarySidebarTab.vue @@ -1,5 +1,8 @@