diff --git a/.gitattributes b/.gitattributes index af4b6adbc..749554ee1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,9 +2,13 @@ * text=auto # Force TS to LF to make the unixy scripts not break on Windows +*.cjs text eol=lf +*.js text eol=lf +*.json text eol=lf +*.mjs text eol=lf +*.mts text eol=lf *.ts text eol=lf *.vue text eol=lf -*.js text eol=lf # Generated files src/types/comfyRegistryTypes.ts linguist-generated=true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c25dc9674..8280246b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -265,7 +265,7 @@ The project supports three types of icons, all with automatic imports (no manual 2. **Iconify Icons** - 200,000+ icons from various libraries: ``, `` 3. **Custom Icons** - Your own SVG icons: `` -Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `src/assets/icons/custom/`. +Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `src/assets/icons/custom/` and processed by `build/customIconCollection.ts` with automatic validation. For detailed instructions and code examples, see [src/assets/icons/README.md](src/assets/icons/README.md). diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 738f5719c..f64ca5c94 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -124,6 +124,7 @@ export class ComfyPage { public readonly url: string // All canvas position operations are based on default view of canvas. public readonly canvas: Locator + public readonly selectionToolbox: Locator public readonly widgetTextBox: Locator // Buttons @@ -158,6 +159,7 @@ export class ComfyPage { ) { this.url = process.env.PLAYWRIGHT_TEST_URL || 'http://localhost:8188' this.canvas = page.locator('#graph-canvas') + this.selectionToolbox = page.locator('.selection-toolbox') this.widgetTextBox = page.getByPlaceholder('text').nth(1) this.resetViewButton = page.getByRole('button', { name: 'Reset View' }) this.queueButton = page.getByRole('button', { name: 'Queue Prompt' }) diff --git a/browser_tests/tests/customIcons.spec.ts b/browser_tests/tests/customIcons.spec.ts new file mode 100644 index 000000000..da640fd89 --- /dev/null +++ b/browser_tests/tests/customIcons.spec.ts @@ -0,0 +1,57 @@ +import { expect } from '@playwright/test' +import type { Locator } from '@playwright/test' + +import { comfyPageFixture as test } from '../fixtures/ComfyPage' + +async function verifyCustomIconSvg(iconElement: Locator) { + const svgVariable = await iconElement.evaluate((element) => { + const styles = getComputedStyle(element) + return styles.getPropertyValue('--svg') + }) + + expect(svgVariable).toBeTruthy() + const dataUrlMatch = svgVariable.match( + /url\("data:image\/svg\+xml,([^"]+)"\)/ + ) + expect(dataUrlMatch).toBeTruthy() + + const encodedSvg = dataUrlMatch![1] + const decodedSvg = decodeURIComponent(encodedSvg) + + // Check for SVG header to confirm it's a valid SVG + expect(decodedSvg).toContain(" { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + }) + + test('sidebar tab icons use custom SVGs', async ({ comfyPage }) => { + // Find the icon in the sidebar + const icon = comfyPage.page.locator( + '.icon-\\[comfy--ai-model\\].side-bar-button-icon' + ) + await expect(icon).toBeVisible() + + // Verify the custom SVG content + await verifyCustomIconSvg(icon) + }) + + test('Browse Templates menu item uses custom template icon', async ({ + comfyPage + }) => { + // Open the topbar menu + await comfyPage.menu.topbar.openTopbarMenu() + const menuItem = comfyPage.menu.topbar.getMenuItem('Browse Templates') + + // Find the icon as a previous sibling of the menu item label + const templateIcon = menuItem + .locator('..') + .locator('.icon-\\[comfy--template\\]') + await expect(templateIcon).toBeVisible() + + // Verify the custom SVG content + await verifyCustomIconSvg(templateIcon) + }) +}) diff --git a/browser_tests/tests/nodeHelp.spec.ts b/browser_tests/tests/nodeHelp.spec.ts index ced74c903..68ce7b8d5 100644 --- a/browser_tests/tests/nodeHelp.spec.ts +++ b/browser_tests/tests/nodeHelp.spec.ts @@ -41,15 +41,12 @@ test.describe('Node Help', () => { // Select the node with panning to ensure toolbox is visible await selectNodeWithPan(comfyPage, ksamplerNodes[0]) - // Wait for selection overlay container and toolbox to appear - await expect( - comfyPage.page.locator('.selection-overlay-container') - ).toBeVisible() - await expect(comfyPage.page.locator('.selection-toolbox')).toBeVisible() + // Wait for selection toolbox to appear + await expect(comfyPage.selectionToolbox).toBeVisible() // Click the help button in the selection toolbox - const helpButton = comfyPage.page.locator( - '.selection-toolbox button:has(.pi-question-circle)' + const helpButton = comfyPage.selectionToolbox.locator( + 'button:has(.pi-question-circle)' ) await expect(helpButton).toBeVisible() await helpButton.click() diff --git a/browser_tests/tests/selectionToolbox.spec.ts b/browser_tests/tests/selectionToolbox.spec.ts index 72264e8b3..90568e3aa 100644 --- a/browser_tests/tests/selectionToolbox.spec.ts +++ b/browser_tests/tests/selectionToolbox.spec.ts @@ -14,20 +14,17 @@ test.describe('Selection Toolbox', () => { test('shows selection toolbox', async ({ comfyPage }) => { // By default, selection toolbox should be enabled - expect( - await comfyPage.page.locator('.selection-overlay-container').isVisible() - ).toBe(false) + await expect(comfyPage.selectionToolbox).not.toBeVisible() // Select multiple nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) // Selection toolbox should be visible with multiple nodes selected - await expect( - comfyPage.page.locator('.selection-overlay-container') - ).toBeVisible() - await expect( - comfyPage.page.locator('.selection-overlay-container.show-border') - ).toBeVisible() + await expect(comfyPage.selectionToolbox).toBeVisible() + // Border is now drawn on canvas, check via screenshot + await expect(comfyPage.canvas).toHaveScreenshot( + 'selection-toolbox-multiple-nodes-border.png' + ) }) test('shows at correct position when node is pasted', async ({ @@ -39,18 +36,16 @@ test.describe('Selection Toolbox', () => { await comfyPage.page.mouse.move(100, 100) await comfyPage.ctrlV() - const overlayContainer = comfyPage.page.locator( - '.selection-overlay-container' - ) - await expect(overlayContainer).toBeVisible() + const toolboxContainer = comfyPage.selectionToolbox + await expect(toolboxContainer).toBeVisible() - // Verify the absolute position - const boundingBox = await overlayContainer.boundingBox() + // Verify toolbox is positioned (canvas-based positioning has different coordinates) + const boundingBox = await toolboxContainer.boundingBox() expect(boundingBox).not.toBeNull() - // 10px offset for the pasted node - expect(Math.round(boundingBox!.x)).toBeCloseTo(90, -1) // Allow ~10px tolerance - // 30px offset of node title height - expect(Math.round(boundingBox!.y)).toBeCloseTo(60, -1) + // Canvas-based positioning can vary, just verify toolbox appears in reasonable bounds + expect(boundingBox!.x).toBeGreaterThan(-100) // Not too far off-screen left + expect(boundingBox!.x).toBeLessThan(1000) // Not too far off-screen right + expect(boundingBox!.y).toBeGreaterThan(-100) // Not too far off-screen top }) test('hide when select and drag happen at the same time', async ({ @@ -65,38 +60,35 @@ test.describe('Selection Toolbox', () => { await comfyPage.page.mouse.down() await comfyPage.page.mouse.move(nodePos.x + 200, nodePos.y + 200) await comfyPage.nextFrame() - await expect( - comfyPage.page.locator('.selection-overlay-container') - ).not.toBeVisible() + await expect(comfyPage.selectionToolbox).not.toBeVisible() }) test('shows border only with multiple selections', async ({ comfyPage }) => { // Select single node await comfyPage.selectNodes(['KSampler']) - // Selection overlay should be visible but without border - await expect( - comfyPage.page.locator('.selection-overlay-container') - ).toBeVisible() - await expect( - comfyPage.page.locator('.selection-overlay-container.show-border') - ).not.toBeVisible() + // Selection toolbox should be visible but without border + await expect(comfyPage.selectionToolbox).toBeVisible() + // Border is now drawn on canvas, check via screenshot + await expect(comfyPage.canvas).toHaveScreenshot( + 'selection-toolbox-single-node-no-border.png' + ) // Select multiple nodes await comfyPage.selectNodes(['KSampler', 'CLIP Text Encode (Prompt)']) - // Selection overlay should show border with multiple selections - await expect( - comfyPage.page.locator('.selection-overlay-container.show-border') - ).toBeVisible() + // Selection border should show with multiple selections (canvas-based) + await expect(comfyPage.canvas).toHaveScreenshot( + 'selection-toolbox-multiple-selections-border.png' + ) // Deselect to single node await comfyPage.selectNodes(['CLIP Text Encode (Prompt)']) - // Border should be hidden again - await expect( - comfyPage.page.locator('.selection-overlay-container.show-border') - ).not.toBeVisible() + // Border should be hidden again (canvas-based) + await expect(comfyPage.canvas).toHaveScreenshot( + 'selection-toolbox-single-selection-no-border.png' + ) }) test('displays bypass button in toolbox when nodes are selected', async ({ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png new file mode 100644 index 000000000..12215637f Binary files /dev/null and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-nodes-border-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png new file mode 100644 index 000000000..a2d11d350 Binary files /dev/null and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-multiple-selections-border-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png new file mode 100644 index 000000000..93924ff73 Binary files /dev/null and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-node-no-border-chromium-linux.png differ diff --git a/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-selection-no-border-chromium-linux.png b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-selection-no-border-chromium-linux.png new file mode 100644 index 000000000..8017b8f49 Binary files /dev/null and b/browser_tests/tests/selectionToolbox.spec.ts-snapshots/selection-toolbox-single-selection-no-border-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png index 32edf383c..4b948f017 100644 Binary files a/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png and b/browser_tests/tests/widget.spec.ts-snapshots/animated-image-preview-saved-webp-chromium-linux.png differ diff --git a/build/customIconCollection.ts b/build/customIconCollection.ts new file mode 100644 index 000000000..f2d823ed5 --- /dev/null +++ b/build/customIconCollection.ts @@ -0,0 +1,100 @@ +import { existsSync, readFileSync, readdirSync } from 'fs' +import { join } from 'path' +import { dirname } from 'path' +import { fileURLToPath } from 'url' + +const fileName = fileURLToPath(import.meta.url) +const dirName = dirname(fileName) +const customIconsPath = join(dirName, '..', 'src', 'assets', 'icons', 'custom') + +// Iconify collection structure +interface IconifyIcon { + body: string + width?: number + height?: number +} + +interface IconifyCollection { + prefix: string + icons: Record + width?: number + height?: number +} + +// Create an Iconify collection for custom icons +export const iconCollection: IconifyCollection = { + prefix: 'comfy', + icons: {}, + width: 16, + height: 16 +} + +/** + * Validates that an SVG file contains valid SVG content + */ +function validateSvgContent(content: string, filename: string): void { + if (!content.trim()) { + throw new Error(`Empty SVG file: ${filename}`) + } + + if (!content.includes(' tag): ${filename}`) + } + + // Basic XML structure validation + const openTags = (content.match(/]*>/g) || []).length + const closeTags = (content.match(/<\/svg>/g) || []).length + + if (openTags !== closeTags) { + throw new Error(`Malformed SVG file (mismatched svg tags): ${filename}`) + } +} + +/** + * Loads custom SVG icons from the icons directory + */ +function loadCustomIcons(): void { + if (!existsSync(customIconsPath)) { + console.warn(`Custom icons directory not found: ${customIconsPath}`) + return + } + + try { + const files = readdirSync(customIconsPath) + const svgFiles = files.filter((file) => file.endsWith('.svg')) + + if (svgFiles.length === 0) { + console.warn('No SVG files found in custom icons directory') + return + } + + svgFiles.forEach((file) => { + const name = file.replace('.svg', '') + const filePath = join(customIconsPath, file) + + try { + const content = readFileSync(filePath, 'utf-8') + validateSvgContent(content, file) + + iconCollection.icons[name] = { + body: content + } + } catch (error) { + console.error( + `Failed to load custom icon ${file}:`, + error instanceof Error ? error.message : error + ) + // Continue loading other icons instead of failing the entire build + } + }) + } catch (error) { + console.error( + 'Failed to read custom icons directory:', + error instanceof Error ? error.message : error + ) + // Don't throw here - allow build to continue without custom icons + } +} + +// Load icons when this module is imported +loadCustomIcons() diff --git a/docs/adr/0002-monorepo-conversion.md b/docs/adr/0002-monorepo-conversion.md new file mode 100644 index 000000000..d1ada909e --- /dev/null +++ b/docs/adr/0002-monorepo-conversion.md @@ -0,0 +1,50 @@ +# 2. Restructure ComfyUI_frontend as a monorepo + +Date: 2025-08-25 + +## Status + +Proposed + + + +## Context + +[Most of the context is in here](https://github.com/Comfy-Org/ComfyUI_frontend/issues/4661) + +TL;DR: As we're merging more subprojects like litegraph, devtools, and soon a fork of PrimeVue, + a monorepo structure will help a lot with code sharing and organization. + +For more information on Monorepos, check out [monorepo.tools](https://monorepo.tools/) + +## Decision + +- Swap out NPM for PNPM +- Add a workspace for the PrimeVue fork +- Move the frontend code into its own app workspace +- Longer term: Extract and reorganize common infrastructure to take advantage of the new monorepo tooling + +### Tools proposed + +[PNPM](https://pnpm.io/) and [PNPM workspaces](https://pnpm.io/workspaces) + +For monorepo management, I'd probably go with [Nx](https://nx.dev/), but I could be conviced otherwise. +There's a [whole list here](https://monorepo.tools/#tools-review) if you're interested. + +## Consequences + +### Positive + +- Adding new projects with shared dependencies becomes really easy +- Makes the process of forking and customizing projects more structured, if not strictly easier +- It *could* speed up the build and development process (not guaranteed) +- It would let us cleanly organize and release packages like `comfyui-frontend-types` + +### Negative + +- Monorepos take some getting used to +- Reviews and code contribution management has to account for the different projects' situations and constraints + + \ No newline at end of file diff --git a/docs/adr/README.md b/docs/adr/README.md index 67ef529de..3ebf340d5 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -11,6 +11,7 @@ An Architecture Decision Record captures an important architectural decision mad | ADR | Title | Status | Date | |-----|-------|--------|------| | [0001](0001-merge-litegraph-into-frontend.md) | Merge LiteGraph.js into ComfyUI Frontend | Accepted | 2025-08-05 | +| [0002](0002-monorepo-conversion.md) | Restructure as a Monorepo | Proposed | 2025-08-25 | ## Creating a New ADR diff --git a/package-lock.json b/package-lock.json index 5fe5dd683..2193dd5fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,6 +55,7 @@ "@eslint/js": "^9.8.0", "@executeautomation/playwright-mcp-server": "^1.0.5", "@iconify/json": "^2.2.245", + "@iconify/tailwind": "^1.2.0", "@intlify/eslint-plugin-vue-i18n": "^3.2.0", "@lobehub/i18n-cli": "^1.20.0", "@pinia/testing": "^0.1.5", @@ -2338,119 +2339,24 @@ "pathe": "^1.1.2" } }, + "node_modules/@iconify/tailwind": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@iconify/tailwind/-/tailwind-1.2.0.tgz", + "integrity": "sha512-KgpIHWOTcRYw1XcoUqyNSrmYyfLLqZYu3AmP8zdfLk0F5TqRO8YerhlvlQmGfn7rJXgPeZN569xPAJnJ53zZxA==", + "dev": true, + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", "dev": true }, - "node_modules/@iconify/utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", - "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", - "dev": true, - "dependencies": { - "@antfu/install-pkg": "^1.0.0", - "@antfu/utils": "^8.1.0", - "@iconify/types": "^2.0.0", - "debug": "^4.4.0", - "globals": "^15.14.0", - "kolorist": "^1.8.0", - "local-pkg": "^1.0.0", - "mlly": "^1.7.4" - } - }, - "node_modules/@iconify/utils/node_modules/@antfu/install-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", - "dev": true, - "dependencies": { - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@iconify/utils/node_modules/@antfu/utils": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", - "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@iconify/utils/node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "dev": true - }, - "node_modules/@iconify/utils/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@iconify/utils/node_modules/local-pkg": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", - "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", - "dev": true, - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.0.1", - "quansync": "^0.2.8" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@iconify/utils/node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", - "dev": true - }, - "node_modules/@iconify/utils/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true - }, - "node_modules/@iconify/utils/node_modules/pkg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", - "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", - "dev": true, - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/@iconify/utils/node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "dev": true - }, "node_modules/@inkjs/ui": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-1.0.0.tgz", @@ -6580,13 +6486,13 @@ } }, "node_modules/axios": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", - "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -6983,7 +6889,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -7135,6 +7040,201 @@ "node": ">= 16" } }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio-select/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18.17" + } + }, + "node_modules/cheerio/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -8233,7 +8333,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -8346,6 +8445,37 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -8392,7 +8522,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -8401,7 +8530,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8411,7 +8539,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -8419,6 +8546,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-toolkit": { "version": "1.39.9", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.9.tgz", @@ -9751,12 +9893,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -9881,7 +10026,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9927,7 +10071,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -9951,7 +10094,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -10104,7 +10246,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -10194,7 +10335,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -10206,7 +10346,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -10229,7 +10368,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -10283,6 +10421,80 @@ "node": ">= 12" } }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/htmlparser2/node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/htmlparser2/node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -12311,7 +12523,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -14055,6 +14266,55 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -15009,9 +15269,9 @@ } }, "node_modules/quansync": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", - "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", "dev": true, "funding": [ { @@ -15022,7 +15282,8 @@ "type": "individual", "url": "https://github.com/sponsors/sxzz" } - ] + ], + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -17653,6 +17914,72 @@ } } }, + "node_modules/unplugin-icons/node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/unplugin-icons/node_modules/@iconify/utils/node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unplugin-icons/node_modules/@iconify/utils/node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unplugin-icons/node_modules/@iconify/utils/node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/unplugin-icons/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/unplugin-icons/node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -17670,6 +17997,39 @@ } } }, + "node_modules/unplugin-icons/node_modules/package-manager-detector": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin-icons/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin-icons/node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/unplugin-icons/node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, "node_modules/unplugin-vue-components": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.28.0.tgz", diff --git a/package.json b/package.json index 45d95b84e..5380f73a5 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@eslint/js": "^9.8.0", "@executeautomation/playwright-mcp-server": "^1.0.5", "@iconify/json": "^2.2.245", + "@iconify/tailwind": "^1.2.0", "@intlify/eslint-plugin-vue-i18n": "^3.2.0", "@lobehub/i18n-cli": "^1.20.0", "@pinia/testing": "^0.1.5", @@ -68,6 +69,7 @@ "identity-obj-proxy": "^3.0.0", "knip": "^5.62.0", "lint-staged": "^15.2.7", + "lucide-vue-next": "^0.540.0", "postcss": "^8.4.39", "prettier": "^3.3.2", "storybook": "^9.1.1", @@ -84,8 +86,7 @@ "vitest": "^2.0.0", "vue-tsc": "^2.1.10", "zip-dir": "^2.0.0", - "zod-to-json-schema": "^3.24.1", - "lucide-vue-next": "^0.540.0" + "zod-to-json-schema": "^3.24.1" }, "dependencies": { "@alloc/quick-lru": "^5.2.0", diff --git a/src/assets/icons/README.md b/src/assets/icons/README.md index cd92f1ddf..b01a3e3ef 100644 --- a/src/assets/icons/README.md +++ b/src/assets/icons/README.md @@ -247,9 +247,29 @@ Icons are automatically imported using `unplugin-icons` - no manual imports need ### Configuration -The icon system is configured in `vite.config.mts`: +The icon system has two layers: + +1. **Build-time Processing** (`build/customIconCollection.ts`): + - Scans `src/assets/icons/custom/` for SVG files + - Validates SVG content and structure + - Creates Iconify collection for Tailwind CSS + - Provides error handling for malformed files + +2. **Vite Runtime** (`vite.config.mts`): + - Enables direct SVG import as Vue components + - Supports dynamic icon loading ```typescript +// Build script creates Iconify collection +export const iconCollection: IconifyCollection = { + prefix: 'comfy', + icons: { + 'workflow': { body: '...' }, + 'node': { body: '...' } + } +} + +// Vite configuration for component-based usage Icons({ compiler: 'vue3', customCollections: { @@ -271,8 +291,9 @@ Icons are fully typed. If TypeScript doesn't recognize a new custom icon: ### Icon Not Showing 1. **Check filename**: Must be kebab-case without special characters 2. **Restart dev server**: Required after adding new icons -3. **Verify SVG**: Ensure it's valid SVG syntax +3. **Verify SVG**: Ensure it's valid SVG syntax (build script validates automatically) 4. **Check console**: Look for Vue component resolution errors +5. **Build script errors**: Check console during build - malformed SVGs are logged but don't break builds ### Icon Wrong Color - Replace hardcoded colors with `currentColor` diff --git a/src/assets/icons/custom/ai-model.svg b/src/assets/icons/custom/ai-model.svg index ede8e5c7e..a9f987088 100644 --- a/src/assets/icons/custom/ai-model.svg +++ b/src/assets/icons/custom/ai-model.svg @@ -1,6 +1 @@ - - - - - - \ No newline at end of file + diff --git a/src/assets/icons/custom/node.svg b/src/assets/icons/custom/node.svg index 3239b59bd..f31c03c6b 100644 --- a/src/assets/icons/custom/node.svg +++ b/src/assets/icons/custom/node.svg @@ -1,3 +1 @@ - - - \ No newline at end of file + diff --git a/src/assets/icons/custom/template.svg b/src/assets/icons/custom/template.svg index 2a2a75f8d..8c1a66caf 100644 --- a/src/assets/icons/custom/template.svg +++ b/src/assets/icons/custom/template.svg @@ -1,5 +1 @@ - - - - - \ No newline at end of file + diff --git a/src/assets/icons/custom/workflow.svg b/src/assets/icons/custom/workflow.svg index 043d24e7b..b91b58b69 100644 --- a/src/assets/icons/custom/workflow.svg +++ b/src/assets/icons/custom/workflow.svg @@ -1,3 +1 @@ - - - + diff --git a/src/components/dialog/content/SettingDialogContent.vue b/src/components/dialog/content/SettingDialogContent.vue index 9d676ed0c..c9c71f25a 100644 --- a/src/components/dialog/content/SettingDialogContent.vue +++ b/src/components/dialog/content/SettingDialogContent.vue @@ -41,7 +41,6 @@ > @@ -76,7 +75,6 @@ import { flattenTree } from '@/utils/treeUtil' import ColorPaletteMessage from './setting/ColorPaletteMessage.vue' import CurrentUserMessage from './setting/CurrentUserMessage.vue' -import FirstTimeUIMessage from './setting/FirstTimeUIMessage.vue' import PanelTemplate from './setting/PanelTemplate.vue' import SettingsPanel from './setting/SettingsPanel.vue' diff --git a/src/components/dialog/content/setting/FirstTimeUIMessage.vue b/src/components/dialog/content/setting/FirstTimeUIMessage.vue deleted file mode 100644 index 82794923f..000000000 --- a/src/components/dialog/content/setting/FirstTimeUIMessage.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/src/components/input/SearchBox.vue b/src/components/input/SearchBox.vue index 462668a12..0e60be550 100644 --- a/src/components/input/SearchBox.vue +++ b/src/components/input/SearchBox.vue @@ -13,7 +13,7 @@
- - +
+ +
+
+ +
@@ -32,14 +40,15 @@ const isVideoType = false const baseImageClass = computed(() => { - return `absolute inset-0 ${isVideoType ? 'w-full h-full object-cover' : 'max-w-full max-h-64 object-contain'}` + const sizeClasses = isVideoType + ? 'size-full object-cover' + : 'size-full object-contain' + return sizeClasses }) const overlayImageClass = computed(() => { - const baseClasses = 'absolute inset-0 transition-opacity duration-300' - const sizeClasses = isVideoType - ? 'w-full h-full object-cover' - : 'max-w-full max-h-64 object-contain' + const baseClasses = 'size-full transition-opacity duration-300' + const sizeClasses = isVideoType ? 'object-cover' : 'object-contain' const opacityClasses = isHovered ? 'opacity-100' : 'opacity-0' return `${baseClasses} ${sizeClasses} ${opacityClasses}` }) diff --git a/src/components/topbar/CommandMenubar.vue b/src/components/topbar/CommandMenubar.vue index f76af8b3e..d64cf92e5 100644 --- a/src/components/topbar/CommandMenubar.vue +++ b/src/components/topbar/CommandMenubar.vue @@ -187,7 +187,7 @@ const extraMenuItems = computed(() => [ { key: 'browse-templates', label: t('menuLabels.Browse Templates'), - icon: 'pi pi-folder-open', + icon: 'icon-[comfy--template]', command: () => commandStore.execute('Comfy.BrowseTemplates') }, { diff --git a/src/composables/element/useRetriggerableAnimation.ts b/src/composables/element/useRetriggerableAnimation.ts deleted file mode 100644 index e6bd2d566..000000000 --- a/src/composables/element/useRetriggerableAnimation.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { onMounted, ref, watch } from 'vue' -import type { Ref, WatchSource } from 'vue' - -/** - * A composable that manages retriggerable CSS animations. - * Provides a boolean ref that can be toggled to restart CSS animations. - * - * @param trigger - Optional reactive source that triggers the animation when it changes - * @param options - Configuration options - * @returns An object containing the animation state ref - * - * @example - * ```vue - * - * - * - * ``` - */ -export function useRetriggerableAnimation( - trigger?: WatchSource | Ref, - options: { - animateOnMount?: boolean - animationDelay?: number - } = {} -) { - const { animateOnMount = true, animationDelay = 0 } = options - - const shouldAnimate = ref(false) - - /** - * Retriggers the animation by removing and re-adding the animation class - */ - const retriggerAnimation = () => { - // Remove animation class - shouldAnimate.value = false - // Force browser reflow to ensure the class removal is processed - void document.body.offsetHeight - // Re-add animation class in the next frame - requestAnimationFrame(() => { - if (animationDelay > 0) { - setTimeout(() => { - shouldAnimate.value = true - }, animationDelay) - } else { - shouldAnimate.value = true - } - }) - } - - // Trigger animation on mount if requested - if (animateOnMount) { - onMounted(() => { - if (animationDelay > 0) { - setTimeout(() => { - shouldAnimate.value = true - }, animationDelay) - } else { - shouldAnimate.value = true - } - }) - } - - // Watch for trigger changes to retrigger animation - if (trigger) { - watch(trigger, () => { - retriggerAnimation() - }) - } - - return { - shouldAnimate, - retriggerAnimation - } -} diff --git a/src/composables/sidebarTabs/useModelLibrarySidebarTab.ts b/src/composables/sidebarTabs/useModelLibrarySidebarTab.ts index c5f5d9099..269955648 100644 --- a/src/composables/sidebarTabs/useModelLibrarySidebarTab.ts +++ b/src/composables/sidebarTabs/useModelLibrarySidebarTab.ts @@ -1,18 +1,14 @@ -import { defineAsyncComponent, markRaw } from 'vue' +import { markRaw } from 'vue' import ModelLibrarySidebarTab from '@/components/sidebar/tabs/ModelLibrarySidebarTab.vue' import { useElectronDownloadStore } from '@/stores/electronDownloadStore' import type { SidebarTabExtension } from '@/types/extensionTypes' import { isElectron } from '@/utils/envUtil' -const AiModelIcon = markRaw( - defineAsyncComponent(() => import('virtual:icons/comfy/ai-model')) -) - export const useModelLibrarySidebarTab = (): SidebarTabExtension => { return { id: 'model-library', - icon: AiModelIcon, + icon: 'icon-[comfy--ai-model]', title: 'sideToolbar.modelLibrary', tooltip: 'sideToolbar.modelLibrary', label: 'sideToolbar.labels.models', diff --git a/src/composables/sidebarTabs/useNodeLibrarySidebarTab.ts b/src/composables/sidebarTabs/useNodeLibrarySidebarTab.ts index 5da93a1c4..7b9d6b3b0 100644 --- a/src/composables/sidebarTabs/useNodeLibrarySidebarTab.ts +++ b/src/composables/sidebarTabs/useNodeLibrarySidebarTab.ts @@ -1,16 +1,12 @@ -import { defineAsyncComponent, markRaw } from 'vue' +import { markRaw } from 'vue' import NodeLibrarySidebarTab from '@/components/sidebar/tabs/NodeLibrarySidebarTab.vue' import type { SidebarTabExtension } from '@/types/extensionTypes' -const NodeIcon = markRaw( - defineAsyncComponent(() => import('virtual:icons/comfy/node')) -) - export const useNodeLibrarySidebarTab = (): SidebarTabExtension => { return { id: 'node-library', - icon: NodeIcon, + icon: 'icon-[comfy--node]', title: 'sideToolbar.nodeLibrary', tooltip: 'sideToolbar.nodeLibrary', label: 'sideToolbar.labels.nodes', diff --git a/src/composables/sidebarTabs/useWorkflowsSidebarTab.ts b/src/composables/sidebarTabs/useWorkflowsSidebarTab.ts index 772adf4de..0e3891f26 100644 --- a/src/composables/sidebarTabs/useWorkflowsSidebarTab.ts +++ b/src/composables/sidebarTabs/useWorkflowsSidebarTab.ts @@ -1,20 +1,16 @@ -import { defineAsyncComponent, markRaw } from 'vue' +import { markRaw } from 'vue' import WorkflowsSidebarTab from '@/components/sidebar/tabs/WorkflowsSidebarTab.vue' import { useSettingStore } from '@/stores/settingStore' import { useWorkflowStore } from '@/stores/workflowStore' import type { SidebarTabExtension } from '@/types/extensionTypes' -const WorkflowIcon = markRaw( - defineAsyncComponent(() => import('virtual:icons/comfy/workflow')) -) - export const useWorkflowsSidebarTab = (): SidebarTabExtension => { const settingStore = useSettingStore() const workflowStore = useWorkflowStore() return { id: 'workflows', - icon: WorkflowIcon, + icon: 'icon-[comfy--workflow]', iconBadge: () => { if ( settingStore.get('Comfy.Workflow.WorkflowTabsPosition') !== 'Sidebar' diff --git a/src/constants/coreSettings.ts b/src/constants/coreSettings.ts index d91c03cd5..7b398e090 100644 --- a/src/constants/coreSettings.ts +++ b/src/constants/coreSettings.ts @@ -791,11 +791,11 @@ export const CORE_SETTINGS: SettingParams[] = [ type: 'combo', options: [ { value: 'standard', text: 'Standard (New)' }, - { value: 'legacy', text: 'Left-Click Pan (Legacy)' } + { value: 'legacy', text: 'Drag Navigation' } ], versionAdded: '1.25.0', defaultsByInstallVersion: { - '1.25.0': 'standard' + '1.25.0': 'legacy' } }, { diff --git a/src/lib/litegraph/src/LGraphNode.ts b/src/lib/litegraph/src/LGraphNode.ts index 59d55002d..123afca51 100644 --- a/src/lib/litegraph/src/LGraphNode.ts +++ b/src/lib/litegraph/src/LGraphNode.ts @@ -218,6 +218,7 @@ export class LGraphNode static MAX_CONSOLE?: number static type?: string static category?: string + static description?: string static filter?: string static skip_list?: boolean diff --git a/src/locales/ar/main.json b/src/locales/ar/main.json index 9dc6c4cad..13a9d331c 100644 --- a/src/locales/ar/main.json +++ b/src/locales/ar/main.json @@ -313,7 +313,6 @@ "feedback": "ملاحظات", "filter": "تصفية", "findIssues": "العثور على مشاكل", - "firstTimeUIMessage": "هذه هي المرة الأولى التي تستخدم فيها واجهة المستخدم الجديدة. اختر \"القائمة > استخدام القائمة الجديدة > تعطيل\" لاستعادة الواجهة القديمة.", "frontendNewer": "إصدار الواجهة الأمامية {frontendVersion} قد لا يكون متوافقاً مع الإصدار الخلفي {backendVersion}.", "frontendOutdated": "إصدار الواجهة الأمامية {frontendVersion} قديم. يتطلب الإصدار الخلفي {requiredVersion} أو أحدث.", "goToNode": "الانتقال إلى العقدة", diff --git a/src/locales/ar/settings.json b/src/locales/ar/settings.json index 954b83de2..86a5263d5 100644 --- a/src/locales/ar/settings.json +++ b/src/locales/ar/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "وضع تنقل اللوحة", "options": { - "Left-Click Pan (Legacy)": "سحب بالنقر الأيسر (قديم)", + "Drag Navigation": "سحب للتنقل", "Standard (New)": "قياسي (جديد)" } }, diff --git a/src/locales/en/main.json b/src/locales/en/main.json index ecc17b17c..c2118a286 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -5,7 +5,6 @@ "empty": "Empty", "noWorkflowsFound": "No workflows found.", "comingSoon": "Coming Soon", - "firstTimeUIMessage": "This is the first time you use the new UI. Choose \"Menu > Use New Menu > Disabled\" to restore the old UI.", "download": "Download", "import": "Import", "loadAllFolders": "Load All Folders", diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 6f2a410e4..e2c759c5c 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -33,7 +33,7 @@ "name": "Canvas Navigation Mode", "options": { "Standard (New)": "Standard (New)", - "Left-Click Pan (Legacy)": "Left-Click Pan (Legacy)" + "Drag Navigation": "Drag Navigation" } }, "Comfy_Canvas_SelectionToolbox": { diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 817e1a4a0..0e80fc9a5 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -313,7 +313,6 @@ "feedback": "Retroalimentación", "filter": "Filtrar", "findIssues": "Encontrar problemas", - "firstTimeUIMessage": "Esta es la primera vez que usas la nueva interfaz. Elige \"Menú > Usar nuevo menú > Desactivado\" para restaurar la antigua interfaz.", "frontendNewer": "La versión del frontend {frontendVersion} puede no ser compatible con la versión del backend {backendVersion}.", "frontendOutdated": "La versión del frontend {frontendVersion} está desactualizada. El backend requiere la versión {requiredVersion} o superior.", "goToNode": "Ir al nodo", diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 0bc0af0a0..3444877ed 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "Modo de navegación del lienzo", "options": { - "Left-Click Pan (Legacy)": "Desplazamiento con clic izquierdo (Legado)", + "Drag Navigation": "Navegación por arrastre", "Standard (New)": "Estándar (Nuevo)" } }, diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index a60b78385..db878ecf8 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -313,7 +313,6 @@ "feedback": "Commentaires", "filter": "Filtrer", "findIssues": "Trouver des problèmes", - "firstTimeUIMessage": "C'est la première fois que vous utilisez la nouvelle interface utilisateur. Choisissez \"Menu > Utiliser le nouveau menu > Désactivé\" pour restaurer l'ancienne interface utilisateur.", "frontendNewer": "La version du frontend {frontendVersion} peut ne pas être compatible avec la version du backend {backendVersion}.", "frontendOutdated": "La version du frontend {frontendVersion} est obsolète. Le backend requiert la version {requiredVersion} ou supérieure.", "goToNode": "Aller au nœud", diff --git a/src/locales/fr/settings.json b/src/locales/fr/settings.json index fd734e9a1..b551b5267 100644 --- a/src/locales/fr/settings.json +++ b/src/locales/fr/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "Mode de navigation sur le canvas", "options": { - "Left-Click Pan (Legacy)": "Panoramique clic gauche (Hérité)", + "Drag Navigation": "Navigation par glisser-déposer", "Standard (New)": "Standard (Nouveau)" } }, diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 581b05e4b..c6a9608b7 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -313,7 +313,6 @@ "feedback": "フィードバック", "filter": "フィルタ", "findIssues": "問題を見つける", - "firstTimeUIMessage": "新しいUIを初めて使用しています。「メニュー > 新しいメニューを使用 > 無効」を選択することで古いUIに戻すことが可能です。", "frontendNewer": "フロントエンドのバージョン {frontendVersion} はバックエンドのバージョン {backendVersion} と互換性がない可能性があります。", "frontendOutdated": "フロントエンドのバージョン {frontendVersion} は古くなっています。バックエンドは {requiredVersion} 以上が必要です。", "goToNode": "ノードに移動", diff --git a/src/locales/ja/settings.json b/src/locales/ja/settings.json index accae3f8e..c31c323c8 100644 --- a/src/locales/ja/settings.json +++ b/src/locales/ja/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "キャンバスナビゲーションモード", "options": { - "Left-Click Pan (Legacy)": "左クリックパン(レガシー)", + "Drag Navigation": "ドラッグナビゲーション", "Standard (New)": "標準(新)" } }, diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index a45694561..9e1ba1857 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -313,7 +313,6 @@ "feedback": "피드백", "filter": "필터", "findIssues": "문제 찾기", - "firstTimeUIMessage": "새 UI를 처음 사용합니다. \"메뉴 > 새 메뉴 사용 > 비활성화\"를 선택하여 이전 UI로 복원하세요.", "frontendNewer": "프론트엔드 버전 {frontendVersion}이(가) 백엔드 버전 {backendVersion}과(와) 호환되지 않을 수 있습니다.", "frontendOutdated": "프론트엔드 버전 {frontendVersion}이(가) 오래되었습니다. 백엔드는 {requiredVersion} 이상이 필요합니다.", "goToNode": "노드로 이동", diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index 9e46299ea..45117dcb6 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "캔버스 내비게이션 모드", "options": { - "Left-Click Pan (Legacy)": "왼쪽 클릭 이동(레거시)", + "Drag Navigation": "드래그 내비게이션", "Standard (New)": "표준(신규)" } }, diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index 57cb8d5f2..c0c4df41e 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -313,7 +313,6 @@ "feedback": "Обратная связь", "filter": "Фильтр", "findIssues": "Найти проблемы", - "firstTimeUIMessage": "Вы впервые используете новый интерфейс. Выберите \"Меню > Использовать новое меню > Отключено\", чтобы восстановить старый интерфейс.", "frontendNewer": "Версия интерфейса {frontendVersion} может быть несовместима с версией сервера {backendVersion}.", "frontendOutdated": "Версия интерфейса {frontendVersion} устарела. Требуется версия не ниже {requiredVersion} для работы с сервером.", "goToNode": "Перейти к ноде", diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index 14836ab16..76e38da04 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "Режим навигации по холсту", "options": { - "Left-Click Pan (Legacy)": "Перемещение левой кнопкой (устаревший)", + "Drag Navigation": "Перетаскивание", "Standard (New)": "Стандартный (новый)" } }, diff --git a/src/locales/zh-TW/main.json b/src/locales/zh-TW/main.json index 28ad3d0ad..d8d3dc1e7 100644 --- a/src/locales/zh-TW/main.json +++ b/src/locales/zh-TW/main.json @@ -313,7 +313,6 @@ "feedback": "意見回饋", "filter": "篩選", "findIssues": "尋找問題", - "firstTimeUIMessage": "這是您第一次使用新介面。若要返回舊介面,請前往「選單」>「使用新介面」>「關閉」。", "frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。", "frontendOutdated": "前端版本 {frontendVersion} 已過時。後端需要 {requiredVersion} 或更高版本。", "goToNode": "前往節點", diff --git a/src/locales/zh-TW/settings.json b/src/locales/zh-TW/settings.json index 360e7ee74..375f751e6 100644 --- a/src/locales/zh-TW/settings.json +++ b/src/locales/zh-TW/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "畫布導航模式", "options": { - "Left-Click Pan (Legacy)": "左鍵拖曳平移(舊版)", + "Drag Navigation": "拖曳導覽", "Standard (New)": "標準(新)" } }, diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index f522fe87a..2a0482654 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -313,7 +313,6 @@ "feedback": "反馈", "filter": "过滤", "findIssues": "查找问题", - "firstTimeUIMessage": "这是您第一次使用新界面。选择 \"菜单 > 使用新菜单 > 禁用\" 来恢复旧界面。", "frontendNewer": "前端版本 {frontendVersion} 可能與後端版本 {backendVersion} 不相容。", "frontendOutdated": "前端版本 {frontendVersion} 已过时。后端需要 {requiredVersion} 或更高版本。", "goToNode": "转到节点", diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index 346113233..343454ce9 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -32,7 +32,7 @@ "Comfy_Canvas_NavigationMode": { "name": "画布导航模式", "options": { - "Left-Click Pan (Legacy)": "左键拖曳(旧版)", + "Drag Navigation": "拖动画布", "Standard (New)": "标准(新)" } }, diff --git a/src/scripts/app.ts b/src/scripts/app.ts index debcc2627..3db03cc39 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -918,7 +918,7 @@ export class ComfyApp { output_is_list: [], output_node: false, python_module: 'custom_nodes.frontend_only', - description: `Frontend only node for ${name}` + description: node.description ?? `Frontend only node for ${name}` } as ComfyNodeDefV1 } diff --git a/tailwind.config.js b/tailwind.config.js index 7008cd9d3..9c809eaa8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,8 @@ /** @type {import('tailwindcss').Config} */ +import { addDynamicIconSelectors } from '@iconify/tailwind' + +import { iconCollection } from './build/customIconCollection.ts' + export default { content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], @@ -219,6 +223,11 @@ export default { }, plugins: [ + addDynamicIconSelectors({ + iconSets: { + comfy: iconCollection + } + }), function ({ addVariant }) { addVariant('dark-theme', '.dark-theme &') }, diff --git a/vite.config.mts b/vite.config.mts index 3b1c81566..a52e4c81c 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -136,7 +136,8 @@ export default defineConfig({ ], dirs: ['src/components', 'src/layout', 'src/views'], deep: true, - extensions: ['vue'] + extensions: ['vue'], + directoryAsNamespace: true }) ],