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 @@
-
-
- {{ $t('g.firstTimeUIMessage') }}
-
-
-
-
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 @@
- * ```
- */
-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
})
],